tkblend 0.4.0b1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. tkblend-0.4.0b1/.agents/rules/platform-build-invariants.md +66 -0
  2. tkblend-0.4.0b1/.agents/rules/tooling.md +6 -0
  3. tkblend-0.4.0b1/.agents/skills/blend2d-vector-widgets/SKILL.md +134 -0
  4. tkblend-0.4.0b1/.github/workflows/ci.yml +263 -0
  5. tkblend-0.4.0b1/.github/workflows/release.yml +139 -0
  6. tkblend-0.4.0b1/.gitignore +49 -0
  7. tkblend-0.4.0b1/CMakeLists.txt +518 -0
  8. tkblend-0.4.0b1/CMakeLocalConfig.cmake.example +62 -0
  9. tkblend-0.4.0b1/LICENSE +21 -0
  10. tkblend-0.4.0b1/PKG-INFO +325 -0
  11. tkblend-0.4.0b1/README.md +286 -0
  12. tkblend-0.4.0b1/docs/README.md +37 -0
  13. tkblend-0.4.0b1/docs/architecture.md +150 -0
  14. tkblend-0.4.0b1/docs/assets/canvas_studio_custom.gif +0 -0
  15. tkblend-0.4.0b1/docs/assets/canvas_studio_overview.png +0 -0
  16. tkblend-0.4.0b1/docs/assets/custom_widgets_overview.png +0 -0
  17. tkblend-0.4.0b1/docs/assets/hero_analytics_cinematic.png +0 -0
  18. tkblend-0.4.0b1/docs/assets/hero_analytics_dark.png +0 -0
  19. tkblend-0.4.0b1/docs/assets/hero_analytics_light.png +0 -0
  20. tkblend-0.4.0b1/docs/assets/hero_showcase.gif +0 -0
  21. tkblend-0.4.0b1/docs/assets/realtime_particles.png +0 -0
  22. tkblend-0.4.0b1/docs/assets/realtime_spectrum.png +0 -0
  23. tkblend-0.4.0b1/docs/assets/realtime_visualizer.gif +0 -0
  24. tkblend-0.4.0b1/docs/assets/widget_catalog_overview.png +0 -0
  25. tkblend-0.4.0b1/docs/assets/widget_matrix_theming.gif +0 -0
  26. tkblend-0.4.0b1/docs/canvas_and_vector.md +240 -0
  27. tkblend-0.4.0b1/docs/caveats_and_edge_cases.md +330 -0
  28. tkblend-0.4.0b1/docs/examples.md +81 -0
  29. tkblend-0.4.0b1/docs/getting_started.md +163 -0
  30. tkblend-0.4.0b1/docs/theming.md +169 -0
  31. tkblend-0.4.0b1/docs/widgets.md +403 -0
  32. tkblend-0.4.0b1/examples/README.md +147 -0
  33. tkblend-0.4.0b1/examples/analytics_dashboard.py +473 -0
  34. tkblend-0.4.0b1/examples/api_inspector.py +1376 -0
  35. tkblend-0.4.0b1/examples/canvas_studio.py +682 -0
  36. tkblend-0.4.0b1/examples/cinematic_theme_demo.py +353 -0
  37. tkblend-0.4.0b1/examples/custom_widget_cookbook.py +508 -0
  38. tkblend-0.4.0b1/examples/file_explorer.py +373 -0
  39. tkblend-0.4.0b1/examples/gradient_widgets.py +1352 -0
  40. tkblend-0.4.0b1/examples/hub.py +453 -0
  41. tkblend-0.4.0b1/examples/icons_and_typography_demo.py +226 -0
  42. tkblend-0.4.0b1/examples/multimedia_dashboard.py +415 -0
  43. tkblend-0.4.0b1/examples/realtime_visualizer.py +569 -0
  44. tkblend-0.4.0b1/examples/showcase.py +561 -0
  45. tkblend-0.4.0b1/examples/table_demo.py +346 -0
  46. tkblend-0.4.0b1/merge.py +99 -0
  47. tkblend-0.4.0b1/pyproject.toml +132 -0
  48. tkblend-0.4.0b1/sanitizers.log +840 -0
  49. tkblend-0.4.0b1/scratch/test_all_examples.py +72 -0
  50. tkblend-0.4.0b1/scratch/test_playground_interaction.py +69 -0
  51. tkblend-0.4.0b1/scratch/test_screenshots.py +117 -0
  52. tkblend-0.4.0b1/scratch/test_showcase_all_pages.py +57 -0
  53. tkblend-0.4.0b1/scratch/test_showcase_steps.py +56 -0
  54. tkblend-0.4.0b1/scratch/verify_all_showcase_examples.py +58 -0
  55. tkblend-0.4.0b1/scripts/benchmark_render.py +98 -0
  56. tkblend-0.4.0b1/scripts/benchmark_vars.py +116 -0
  57. tkblend-0.4.0b1/scripts/benchmark_window_shape.py +185 -0
  58. tkblend-0.4.0b1/scripts/build.py +98 -0
  59. tkblend-0.4.0b1/scripts/build.sh +18 -0
  60. tkblend-0.4.0b1/scripts/build_dist.py +99 -0
  61. tkblend-0.4.0b1/scripts/build_dist.sh +18 -0
  62. tkblend-0.4.0b1/scripts/capture_showcase.py +147 -0
  63. tkblend-0.4.0b1/scripts/export_demo_screenshots.py +114 -0
  64. tkblend-0.4.0b1/scripts/generate_embedded_fonts.py +367 -0
  65. tkblend-0.4.0b1/scripts/generate_showcase_assets.py +555 -0
  66. tkblend-0.4.0b1/scripts/render_all_widgets_to_png.py +146 -0
  67. tkblend-0.4.0b1/scripts/run_coverage.sh +81 -0
  68. tkblend-0.4.0b1/scripts/run_sanitizers.sh +108 -0
  69. tkblend-0.4.0b1/scripts/run_tests.py +62 -0
  70. tkblend-0.4.0b1/scripts/standardize_table_themes.py +543 -0
  71. tkblend-0.4.0b1/scripts/test.sh +18 -0
  72. tkblend-0.4.0b1/scripts/validate_granular_appearance.py +686 -0
  73. tkblend-0.4.0b1/scripts/validate_theme_appearance.py +407 -0
  74. tkblend-0.4.0b1/scripts/verify_inputs_visuals.py +74 -0
  75. tkblend-0.4.0b1/scripts/verify_snapshots.py +167 -0
  76. tkblend-0.4.0b1/scripts/verify_table_visuals.py +112 -0
  77. tkblend-0.4.0b1/src/animation.cpp +98 -0
  78. tkblend-0.4.0b1/src/animation.hpp +22 -0
  79. tkblend-0.4.0b1/src/bindings.cpp +1477 -0
  80. tkblend-0.4.0b1/src/blit/blit_backend.h +43 -0
  81. tkblend-0.4.0b1/src/blit/blit_common.cpp +69 -0
  82. tkblend-0.4.0b1/src/blit/blit_macos.mm +112 -0
  83. tkblend-0.4.0b1/src/blit/blit_win32.cpp +185 -0
  84. tkblend-0.4.0b1/src/blit/blit_x11.cpp +106 -0
  85. tkblend-0.4.0b1/src/color.cpp +71 -0
  86. tkblend-0.4.0b1/src/color.hpp +74 -0
  87. tkblend-0.4.0b1/src/compiled_draw_batch.cpp +203 -0
  88. tkblend-0.4.0b1/src/compiled_draw_batch.hpp +114 -0
  89. tkblend-0.4.0b1/src/default_styles.hpp +781 -0
  90. tkblend-0.4.0b1/src/draw_batch.cpp +167 -0
  91. tkblend-0.4.0b1/src/draw_batch.hpp +113 -0
  92. tkblend-0.4.0b1/src/draw_engine.cpp +1350 -0
  93. tkblend-0.4.0b1/src/draw_engine.hpp +80 -0
  94. tkblend-0.4.0b1/src/emoji_engine.cpp +293 -0
  95. tkblend-0.4.0b1/src/emoji_engine.hpp +65 -0
  96. tkblend-0.4.0b1/src/fnv1a.hpp +62 -0
  97. tkblend-0.4.0b1/src/font/embedded_fonts.cpp +45898 -0
  98. tkblend-0.4.0b1/src/font/embedded_fonts.hpp +24 -0
  99. tkblend-0.4.0b1/src/font/font_resolver.hpp +28 -0
  100. tkblend-0.4.0b1/src/font/font_resolver_linux.cpp +403 -0
  101. tkblend-0.4.0b1/src/font/font_resolver_macos.mm +236 -0
  102. tkblend-0.4.0b1/src/font/font_resolver_win32.cpp +372 -0
  103. tkblend-0.4.0b1/src/font_manager.cpp +471 -0
  104. tkblend-0.4.0b1/src/font_manager.hpp +60 -0
  105. tkblend-0.4.0b1/src/gradient.cpp +44 -0
  106. tkblend-0.4.0b1/src/gradient.hpp +33 -0
  107. tkblend-0.4.0b1/src/native_controller.cpp +1476 -0
  108. tkblend-0.4.0b1/src/native_controller.hpp +284 -0
  109. tkblend-0.4.0b1/src/path.cpp +65 -0
  110. tkblend-0.4.0b1/src/path.hpp +28 -0
  111. tkblend-0.4.0b1/src/platform_compat.h +438 -0
  112. tkblend-0.4.0b1/src/shadow_engine.cpp +220 -0
  113. tkblend-0.4.0b1/src/shadow_engine.hpp +71 -0
  114. tkblend-0.4.0b1/src/string_utils.hpp +144 -0
  115. tkblend-0.4.0b1/src/style_engine.cpp +1015 -0
  116. tkblend-0.4.0b1/src/style_engine.hpp +222 -0
  117. tkblend-0.4.0b1/src/surface.cpp +1935 -0
  118. tkblend-0.4.0b1/src/surface.hpp +449 -0
  119. tkblend-0.4.0b1/src/surface_registry.hpp +609 -0
  120. tkblend-0.4.0b1/src/tkblend.hpp +13 -0
  121. tkblend-0.4.0b1/src/window_shape.cpp +233 -0
  122. tkblend-0.4.0b1/src/window_shape.hpp +32 -0
  123. tkblend-0.4.0b1/tests/test_batch_native_render.py +136 -0
  124. tkblend-0.4.0b1/tests/test_blitting.py +57 -0
  125. tkblend-0.4.0b1/tests/test_c_optimizations.py +220 -0
  126. tkblend-0.4.0b1/tests/test_class_auto_redraw.py +90 -0
  127. tkblend-0.4.0b1/tests/test_color.py +107 -0
  128. tkblend-0.4.0b1/tests/test_controller.py +254 -0
  129. tkblend-0.4.0b1/tests/test_css_variables.py +88 -0
  130. tkblend-0.4.0b1/tests/test_draw_engine.py +161 -0
  131. tkblend-0.4.0b1/tests/test_edge_cases_and_errors.py +73 -0
  132. tkblend-0.4.0b1/tests/test_embedded_fonts_and_icons.py +128 -0
  133. tkblend-0.4.0b1/tests/test_gradient.py +60 -0
  134. tkblend-0.4.0b1/tests/test_leaks_and_cleanup.py +376 -0
  135. tkblend-0.4.0b1/tests/test_logging_and_errors.py +98 -0
  136. tkblend-0.4.0b1/tests/test_nb_memory_leaks.py +1030 -0
  137. tkblend-0.4.0b1/tests/test_path.py +54 -0
  138. tkblend-0.4.0b1/tests/test_resilience_and_chaos.py +331 -0
  139. tkblend-0.4.0b1/tests/test_shadow_engine.py +49 -0
  140. tkblend-0.4.0b1/tests/test_surface.py +195 -0
  141. tkblend-0.4.0b1/tests/test_text_and_fonts.py +225 -0
  142. tkblend-0.4.0b1/tests/test_theme.py +553 -0
  143. tkblend-0.4.0b1/tests/test_theme_autodetect.py +192 -0
  144. tkblend-0.4.0b1/tests/test_theme_enforced_hierarchy.py +125 -0
  145. tkblend-0.4.0b1/tests/test_theme_files.py +180 -0
  146. tkblend-0.4.0b1/tests/test_theme_switching.py +93 -0
  147. tkblend-0.4.0b1/tests/test_thread_safety.py +865 -0
  148. tkblend-0.4.0b1/tests/test_tk_font_sync.py +148 -0
  149. tkblend-0.4.0b1/tests/test_tkblend.py +152 -0
  150. tkblend-0.4.0b1/tests/test_widgets.py +1026 -0
  151. tkblend-0.4.0b1/tkblend/__init__.py +340 -0
  152. tkblend-0.4.0b1/tkblend/controller.py +567 -0
  153. tkblend-0.4.0b1/tkblend/font.py +648 -0
  154. tkblend-0.4.0b1/tkblend/icons.py +4131 -0
  155. tkblend-0.4.0b1/tkblend/surface.py +964 -0
  156. tkblend-0.4.0b1/tkblend/theme.py +1011 -0
  157. tkblend-0.4.0b1/tkblend/themes/catppuccin_latte.css +282 -0
  158. tkblend-0.4.0b1/tkblend/themes/catppuccin_mocha.css +282 -0
  159. tkblend-0.4.0b1/tkblend/themes/cinematic.css +551 -0
  160. tkblend-0.4.0b1/tkblend/themes/cyberpunk.css +282 -0
  161. tkblend-0.4.0b1/tkblend/themes/dark.css +411 -0
  162. tkblend-0.4.0b1/tkblend/themes/debug.css +465 -0
  163. tkblend-0.4.0b1/tkblend/themes/dracula.css +282 -0
  164. tkblend-0.4.0b1/tkblend/themes/emerald.css +282 -0
  165. tkblend-0.4.0b1/tkblend/themes/emerald_forest.css +282 -0
  166. tkblend-0.4.0b1/tkblend/themes/light.css +410 -0
  167. tkblend-0.4.0b1/tkblend/themes/monokai.css +282 -0
  168. tkblend-0.4.0b1/tkblend/themes/monokai_pro.css +282 -0
  169. tkblend-0.4.0b1/tkblend/themes/nord.css +282 -0
  170. tkblend-0.4.0b1/tkblend/themes/ocean.css +282 -0
  171. tkblend-0.4.0b1/tkblend/themes/slate.css +281 -0
  172. tkblend-0.4.0b1/tkblend/themes/solarized_dark.css +282 -0
  173. tkblend-0.4.0b1/tkblend/themes/solarized_light.css +282 -0
  174. tkblend-0.4.0b1/tkblend/themes/sunset.css +282 -0
  175. tkblend-0.4.0b1/tkblend/themes/sunset_amber.css +282 -0
  176. tkblend-0.4.0b1/tkblend/themes/tokyo_night.css +282 -0
  177. tkblend-0.4.0b1/tkblend/utils/__init__.py +13 -0
  178. tkblend-0.4.0b1/tkblend/utils/tcl_interp.py +66 -0
  179. tkblend-0.4.0b1/tkblend/utils/window_shape.py +89 -0
  180. tkblend-0.4.0b1/tkblend/widgets/__init__.py +59 -0
  181. tkblend-0.4.0b1/tkblend/widgets/badge.py +147 -0
  182. tkblend-0.4.0b1/tkblend/widgets/base.py +668 -0
  183. tkblend-0.4.0b1/tkblend/widgets/button.py +112 -0
  184. tkblend-0.4.0b1/tkblend/widgets/card.py +101 -0
  185. tkblend-0.4.0b1/tkblend/widgets/checkbox.py +76 -0
  186. tkblend-0.4.0b1/tkblend/widgets/circular_progress.py +143 -0
  187. tkblend-0.4.0b1/tkblend/widgets/combobox.py +540 -0
  188. tkblend-0.4.0b1/tkblend/widgets/constants.py +194 -0
  189. tkblend-0.4.0b1/tkblend/widgets/frame.py +79 -0
  190. tkblend-0.4.0b1/tkblend/widgets/label.py +39 -0
  191. tkblend-0.4.0b1/tkblend/widgets/progressbar.py +77 -0
  192. tkblend-0.4.0b1/tkblend/widgets/radio.py +108 -0
  193. tkblend-0.4.0b1/tkblend/widgets/range_slider.py +111 -0
  194. tkblend-0.4.0b1/tkblend/widgets/scrollable_frame.py +295 -0
  195. tkblend-0.4.0b1/tkblend/widgets/segmented_button.py +135 -0
  196. tkblend-0.4.0b1/tkblend/widgets/slider.py +106 -0
  197. tkblend-0.4.0b1/tkblend/widgets/sparkline.py +47 -0
  198. tkblend-0.4.0b1/tkblend/widgets/switch.py +81 -0
  199. tkblend-0.4.0b1/tkblend/widgets/table.py +1483 -0
  200. tkblend-0.4.0b1/tkblend/widgets/tabview.py +151 -0
  201. tkblend-0.4.0b1/tkblend/widgets/textbox.py +315 -0
  202. tkblend-0.4.0b1/tkblend/widgets/utils.py +280 -0
  203. tkblend-0.4.0b1/uv.lock +2230 -0
@@ -0,0 +1,66 @@
1
+ # Multiplatform Build & Packaging Invariants
2
+
3
+ This rule documents critical invariants, hard-won fixes, and regression prevention guidelines across Linux, macOS, and Windows CI/CD, cibuildwheel, CMake, and native C++ extension workflows for `tkblend`.
4
+
5
+ ---
6
+
7
+ ## 1. 🍎 macOS (`macos-14` ARM64 / `macos-15-intel` x86_64 / delocate)
8
+
9
+ 1. **Static FreeType Compilation**:
10
+ - **Rule**: FreeType must always be built from source statically via `FetchContent` on `APPLE` with `BUILD_SHARED_LIBS=OFF`.
11
+ - **Why**: Homebrew's `freetype` dylibs are compiled with `MACOSX_DEPLOYMENT_TARGET >= 14.0`. Linking dynamic Homebrew FreeType causes `delocate-wheel` to reject the wheel for deployment targets `10.15` (Intel) and `11.0` (ARM64).
12
+
13
+ 2. **Static Tcl/Tk Stubs**:
14
+ - **Rule**: Link only against static stub libraries (`libtkstub8.6.a`, `libtclstub8.6.a`) via `brew install tcl-tk` and `CMAKE_PREFIX_PATH=$(brew --prefix tcl-tk)`.
15
+ - **Why**: Never dynamically link `Tk.framework` or `Tcl.framework` at build time to ensure wheels load across different Python distributions and Tk versions.
16
+
17
+ 3. **Dynamic Symbol Resolution for Tk Cocoa Internals**:
18
+ - **Rule**: Resolve Cocoa Tk symbols like `TkMacOSXGetCGContextForDrawable` dynamically at runtime via `dlsym(RTLD_DEFAULT, "TkMacOSXGetCGContextForDrawable")`.
19
+ - **Why**: Direct linker references or `__attribute__((weak_import))` still require build-time framework linking on older runner images, causing build failures.
20
+
21
+ 4. **No Unsafe ObjC Window Shaping**:
22
+ - **Rule**: `is_window_shaping_supported()`, `apply_round_rect_shape()`, and `clear_window_shape()` must return `false` on macOS in `src/window_shape.cpp`.
23
+ - **Why**: In Aqua Cocoa Tk, `widget.winfo_id()` returns a pointer to Tk's internal C struct `MacDrawable`, NOT an Objective-C `NSView*`. Casting this pointer to `NSView*` and calling ObjC selectors (`[view isKindOfClass:...]`) causes an immediate segmentation fault (`EXC_BAD_ACCESS`). Child Tk widgets share the top-level `TKContentView` and rely on Blend2D canvas clipping.
24
+
25
+ ---
26
+
27
+ ## 2. 🪟 Windows (`windows-latest` / MSVC / delvewheel)
28
+
29
+ 1. **CMake Prefix Propagation to cibuildwheel**:
30
+ - **Rule**: Always pass `CMAKE_PREFIX_PATH` in both the GitHub Actions step `env:` and via `CIBW_ENVIRONMENT_WINDOWS: "CMAKE_PREFIX_PATH='${{ steps.tcltk.outputs.environment-path }}/Library'"`.
31
+ - **Why**: `cibuildwheel` spawns isolated build subshells that do not inherit `$GITHUB_ENV`.
32
+
33
+ 2. **Excluding Tcl/Tk DLLs in delvewheel**:
34
+ - **Rule**: Maintain `--exclude tcl86t.dll --exclude tk86t.dll` in `tool.cibuildwheel.windows.repair-wheel-command`.
35
+ - **Why**: Prevents delvewheel from vendoring host Tcl/Tk DLLs into the wheel, allowing host Python's Tkinter to provide them at runtime.
36
+
37
+ 3. **Headless & Standalone Test Virtualenv Isolation**:
38
+ - **Rule**: All Tkinter imports in `tkblend/__init__.py` must be wrapped in `try/except ImportError`. The C++ extension `_tkblend` is always imported directly.
39
+ - **Why**: cibuildwheel's clean Windows test virtualenvs do not bundle `_tkinter.pyd`. Guarding imports allows the smoke test `python -c "from tkblend import _tkblend; print('C++ extension loaded successfully')"` to verify binary integrity without crashing with `ModuleNotFoundError: No module named 'tkinter'`.
40
+
41
+ 4. **Target Installation Destination**:
42
+ - **Rule**: Configure `install(TARGETS _tkblend LIBRARY DESTINATION tkblend RUNTIME DESTINATION tkblend ARCHIVE DESTINATION tkblend)` in `CMakeLists.txt`.
43
+ - **Why**: Ensures Windows `.pyd` files are placed inside the `tkblend/` package folder.
44
+
45
+ ---
46
+
47
+ ## 3. 🐧 Linux (`manylinux_2_28` / x86_64 / aarch64 / auditwheel)
48
+
49
+ 1. **Docker Container Dependencies**:
50
+ - **Rule**: Declare `yum install -y tcl-devel tk-devel xorg-x11-server-Xvfb xorg-x11-xinit` under `tool.cibuildwheel.linux.before-all`.
51
+ - **Why**: manylinux container environments run in clean isolation from the host runner.
52
+
53
+ 2. **Virtual Display Server for Tests**:
54
+ - **Rule**: Run test suites using `xvfb-run -a pytest {project}/tests`.
55
+ - **Why**: Tkinter requires a running X11 display server to initialize widgets in headless CI runners.
56
+
57
+ ---
58
+
59
+ ## 4. 🛠️ Local Development & Testing
60
+
61
+ - Always run Python commands and test suites using `uv`:
62
+ ```bash
63
+ uv run pytest
64
+ uv run python <script>
65
+ ```
66
+ - When native C++ sources in `src/` are modified, rebuild the native extension with `cmake --build build` and copy the compiled `_tkblend*.so` / `_tkblend*.pyd` into `tkblend/` and the active `.venv` environment.
@@ -0,0 +1,6 @@
1
+ # Tooling & Environment Invariants
2
+
3
+ - Always run Python commands, scripts, and test suites using `uv`:
4
+ - `uv run pytest`
5
+ - `uv run python <script>`
6
+ - When native C++ sources in `src/` are modified, rebuild with `cmake --build build` and copy the resulting `_tkblend*.so` into `.venv/lib/python3.12/site-packages/tkblend/` and `tkblend/`.
@@ -0,0 +1,134 @@
1
+ ---
2
+ name: blend2d-vector-widgets
3
+ description: Architecture guide and reference patterns for developing pure Blend2D surface-based vector widgets in tkblend with zero TTK dependencies.
4
+ ---
5
+
6
+ # Blend2D Vector Widgets Architecture & Development Guide
7
+
8
+ This skill provides the architectural foundation, development workflow, and widget design patterns for `tkblend`'s pure surface-based vector UI components.
9
+
10
+ ## Core Architectural Principles
11
+
12
+ 1. **Zero TTK Dependencies**:
13
+ - `tkblend` does not use the Tcl/Tk TTK theme engine (`Ttk_RegisterElement`, `Ttk_RegisterTheme`, `ttk.Style`).
14
+ - All interactive components are self-contained vector widgets powered by Blend2D C++ rendering directly into Tk window drawables via zero-copy blitting without intermediate `PhotoImage` allocations.
15
+
16
+ 2. **DPI-Aware Coordinate Scaling**:
17
+ - Always route dimensions and layout coordinates through `ScalingTracker`:
18
+ ```python
19
+ scale = ScalingTracker.get_scaling_factor(master)
20
+ widget_w = int(logical_w * scale)
21
+ widget_h = int(logical_h * scale)
22
+ ```
23
+ - Auto-activates process-level high-DPI awareness on Windows and detects scaling factors on macOS / Linux.
24
+
25
+ 3. **BaseControl & NativeController Lifecycle (`tkblend.widgets.BaseControl`)**:
26
+ - Subclass `BaseControl`, which wraps `tk.Frame` with an integrated `NativeWidgetController` for zero-copy direct drawable blitting.
27
+ - **Transparent Frame Protocol**: Always initialize `super().__init__(..., background="")` so Tk's internal `DisplayFrame` has `framePtr->border == NULL` and does not erase Blend2D vector surfaces on Expose/Configure events.
28
+ - Implement `render(self, surf: Surface, pal: Palette, width: int, height: int, scale: float) -> None`:
29
+ 1. Vector shapes, paths, text, or drop shadows are drawn into `surf`.
30
+ 2. Background clearing is handled via `surf.clear(self._resolved_parent_bg)`.
31
+ - Request redraws via `self.request_redraw()`, which schedules non-blocking idle execution through `self.after_idle(self._execute_idle_redraw)`.
32
+ - **Direct C++ Hardware Blits on Expose / MapNotify**: `NativeWidgetController` handles X11 `Expose`, `MapNotify`, and `VisibilityNotify` events by immediately executing `blit_active_surface()` in C++ from the backing surface buffer.
33
+
34
+ 4. **Dynamic Theming with `tkblend.theme`**:
35
+ - Query `get_theme()` for semantic colors: `bg`, `fg`, `primary`, `secondary`, `accent`, `card_bg`, `card_border`, `track_bg`, `thumb_color`.
36
+ - `BaseControl` automatically registers with `add_theme_listener` and re-renders on `set_theme("light" | "dark")`.
37
+ - **Avoid Static Color Locking**: Do not pass explicit theme colors in constructors unless a permanent user override is intended. Let child widgets dynamically inherit background via `_resolve_default_bg(master, palette)`.
38
+ - **Compound Container Protocol (`.bg_color`)**: Any compound container (`ScrollableFrame`, `Tabview`, `Table`, `TextBox`, `Frame`, `Card`) must expose a `.bg_color` property returning its active inner surface fill color so `cascade_bg_to_children()` can properly recurse.
39
+ - **Subcomponent Cascade in `_on_theme_changed`**:
40
+ Compound widgets must update their internal frames, canvas viewports, and vector scrollbars with `set_parent_bg()` and call `cascade_bg_to_children(self, inner_bg)`.
41
+ - **Startup UI Palette Injection**:
42
+ Call `self._on_theme_changed(pal)` or `tb.cascade_bg_to_children(self, pal.bg)` right after `_build_ui()` so standard helper `tk.Frame`s adapt immediately to theme card colors on first launch.
43
+
44
+ ## Developing a Native Vector Widget
45
+
46
+ Follow this template for creating custom vector components with `BaseControl`:
47
+
48
+ ```python
49
+ import tkinter as tk
50
+ from typing import Optional
51
+ from tkblend.widgets import BaseControl
52
+ from tkblend.theme import Palette, get_theme
53
+ from tkblend.surface import Surface, ColorLike
54
+
55
+ class CustomGauge(BaseControl):
56
+ def __init__(
57
+ self,
58
+ master: Optional[tk.Misc] = None,
59
+ size: int = 100,
60
+ value: float = 50.0,
61
+ color: Optional[ColorLike] = None,
62
+ parent_bg: Optional[str] = None,
63
+ **kwargs,
64
+ ):
65
+ self._value = value
66
+ self._color = color
67
+ super().__init__(master=master, width=size, height=size, parent_bg=parent_bg, **kwargs)
68
+
69
+ def set_value(self, val: float) -> None:
70
+ self._value = max(0.0, min(100.0, float(val)))
71
+ self.request_redraw()
72
+
73
+ def render(self, surf: Surface, pal: Palette, width: int, height: int, scale: float) -> None:
74
+ s = scale
75
+ cx = width / 2.0
76
+ cy = height / 2.0
77
+ r = min(cx, cy) - 6.0 * s
78
+
79
+ # Draw track and progress
80
+ active_color = self._color or pal.primary
81
+ surf.stroke_circle(cx, cy, r, pal.track_bg, stroke_width=6.0 * s)
82
+ surf.fill_circle(cx, cy, r * (self._value / 100.0), active_color)
83
+ ```
84
+
85
+ ## C++ Native Extension Concurrency & Memory Safety
86
+
87
+ 1. **GIL Management & Event Loop Thread Safety**:
88
+ - Never invoke raw `PyGILState_Ensure()` / `PyGILState_Release()` during `_tkinter` `Tcl_Eval` execution; use `nb::gil_scoped_acquire` in C++ event callbacks.
89
+ - Python-initiated redraws must be scheduled via `widget.after_idle()`.
90
+ - `Surface::resize()` runs under `nb::call_guard<nb::gil_scoped_release>()`.
91
+ - All buffer acquisitions (`SurfaceBufferObject`, `Surface::get_buffer()`) must register via `Surface::acquire_buffer_view()` / `Surface::release_buffer_view()` under `Surface::mutex_`.
92
+ - `active_buffers_` acts as a guard preventing resize and subsequent use-after-free while active memoryviews exist.
93
+
94
+ 2. **Display List & Direct Drawing Parity**:
95
+ - Every operation in `DrawBatch` (`DrawOp`) must maintain 1:1 parity with direct `Surface` calls (including all shadow blur, spread, offset-x, offset-y, and color arguments).
96
+ - Any new high-level drawing primitive added to `Surface` must also be added with identical arguments to `DrawBatch`.
97
+
98
+ 3. **Cross-Platform Native Hygiene**:
99
+ - Objective-C++ sources (macOS window shaping) must be pinned to `-fobjc-arc` and treat native view handles as borrowed references.
100
+ - Dynamic asset and font resolution must never use hardcoded user paths; dynamic `$HOME` / `$WINDIR` lookups must be used.
101
+ - Global native types (e.g. `SurfaceBufferType`) must be initialized via `std::call_once` or module init.
102
+
103
+ ## Embedded Sub-widget State Bridging & Controller Parity
104
+
105
+ When developing compound vector widgets that embed native Tk elements (e.g., `Entry`, `TextBox` embedding `tk.Entry` or `tk.Text`):
106
+ 1. **Property & Method Dual Parity**: `NativeController` and widget controllers must provide both property syntax (`controller.is_focused = True`) and method syntax (`controller.set_focused(True)`) for state mutators (`hovered`, `pressed`, `focused`, `disabled`, `checked`).
107
+ 2. **Focus Event Delegation**: Child Tk widgets must delegate `<FocusIn>` and `<FocusOut>` events to update the controller state and immediately trigger `self.request_redraw()` so focus rings and border colors render responsively.
108
+ 3. **Headless & CI Test Verification**: When testing focus state transitions in pytest fixtures, invoke the widget's focus callback hooks directly (`widget._on_entry_focus_in()`) to ensure reliable assertion across headless and unmapped X11 windows.
109
+
110
+ ## Compound Vector Widget Event Isolation & GIL Safety Protocol
111
+
112
+ When developing compound, virtualized, or multi-element vector widgets subclassing `BaseControl` (such as `Table`, `CanvasContainer`, or composite charts) that define custom mouse event bindings (`self.bind("<Button-1>", ...)`, `self.bind("<ButtonRelease-1>", ...)`):
113
+
114
+ 1. **Decouple `command` from `BaseControl`**:
115
+ - Always pass `command=None` to `super().__init__(..., command=None)` so `BaseControl` does not register a generic button-click callback.
116
+ - Store the user's callback in a widget-specific attribute (e.g., `self._command = command`) and expose it via `@property def command`.
117
+
118
+ 2. **Neutralize C++ Controller Click Interception**:
119
+ - Explicitly disable button press state tracking and clear native click handlers during `__init__`:
120
+ ```python
121
+ self.controller.auto_press = False
122
+ self.controller.clear_on_click()
123
+ ```
124
+ - Override `_on_controller_click` as a no-op:
125
+ ```python
126
+ def _on_controller_click(self, x: int, y: int, button: int) -> None:
127
+ """No-op: Compound widgets manage clicks and interactions internally."""
128
+ pass
129
+ ```
130
+
131
+ 3. **Prevent `_tkinter` `tsdPtr->tstate` Desynchronization**:
132
+ - Never allow C++ `on_tk_event` callbacks to execute arbitrary user callbacks synchronously if the widget also binds Tk events for the same event type.
133
+ - A nested `Tcl_Eval` executed from a C++ event handler clears `_tkinter`'s saved thread state to `NULL`, triggering a fatal `PyEval_RestoreThread` crash when Tk dispatches downstream Python event bindings for that event.
134
+
@@ -0,0 +1,263 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+ branches: [main, master]
8
+
9
+ jobs:
10
+ test:
11
+ name: Test (${{ matrix.os }} - Py ${{ matrix.python-version }})
12
+ runs-on: ${{ matrix.os }}
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ os: [ubuntu-latest, macos-14, windows-latest]
17
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
18
+ exclude:
19
+ # macos-14 runner is ARM64 and only supports Python >= 3.10
20
+ - os: macos-14
21
+ python-version: "3.9"
22
+
23
+ steps:
24
+ - name: Checkout code
25
+ uses: actions/checkout@v4
26
+ with:
27
+ submodules: recursive
28
+
29
+ - name: Set up Python ${{ matrix.python-version }}
30
+ if: runner.os != 'Windows'
31
+ uses: actions/setup-python@v5
32
+ with:
33
+ python-version: ${{ matrix.python-version }}
34
+ cache: "pip"
35
+
36
+ - name: Install Linux build & display dependencies
37
+ if: runner.os == 'Linux'
38
+ run: |
39
+ sudo apt-get update
40
+ sudo apt-get install -y tcl8.6-dev tk8.6-dev xvfb libx11-dev
41
+
42
+ - name: Install macOS Tcl/Tk development files
43
+ if: runner.os == 'macOS'
44
+ run: |
45
+ brew install tcl-tk
46
+ echo "CMAKE_PREFIX_PATH=$(brew --prefix tcl-tk)" >> $GITHUB_ENV
47
+
48
+ - name: Install Windows Tcl/Tk development files
49
+ if: runner.os == 'Windows'
50
+ id: tcltk
51
+ uses: mamba-org/setup-micromamba@v3
52
+ with:
53
+ environment-name: tkblend-tcltk
54
+ create-args: >-
55
+ --channel conda-forge
56
+ python=${{ matrix.python-version }}
57
+ tk=8.6.13
58
+ init-shell: none
59
+
60
+ - name: Install build tools & dependencies
61
+ if: runner.os != 'Windows'
62
+ run: |
63
+ python -m pip install --upgrade pip setuptools wheel
64
+ python -m pip install cmake ninja nanobind scikit-build-core pytest Pillow darkdetect
65
+
66
+ - name: Install Windows build tools & dependencies
67
+ if: runner.os == 'Windows'
68
+ run: |
69
+ micromamba run -n tkblend-tcltk python -m pip install --upgrade pip setuptools wheel
70
+ micromamba run -n tkblend-tcltk python -m pip install cmake ninja nanobind scikit-build-core pytest Pillow darkdetect
71
+
72
+ - name: Verify Windows tkinter
73
+ if: runner.os == 'Windows'
74
+ run: micromamba run -n tkblend-tcltk python -c "import tkinter; print(tkinter.TkVersion)"
75
+
76
+ - name: Build and install tkblend (Windows)
77
+ if: runner.os == 'Windows'
78
+ run: micromamba run -n tkblend-tcltk python -m pip install -e . --no-build-isolation -v
79
+ env:
80
+ CMAKE_PREFIX_PATH: ${{ steps.tcltk.outputs.environment-path }}/Library
81
+
82
+ - name: Build and install tkblend (macOS / Linux)
83
+ if: runner.os != 'Windows'
84
+ run: |
85
+ python -m pip install -e . --no-build-isolation -v
86
+
87
+ - name: Run tests (Linux with Xvfb)
88
+ if: runner.os == 'Linux'
89
+ run: |
90
+ xvfb-run -a pytest -v
91
+
92
+ - name: Run tests (macOS / Windows)
93
+ if: runner.os == 'macOS'
94
+ run: pytest -v
95
+
96
+ - name: Run tests (Windows)
97
+ if: runner.os == 'Windows'
98
+ run: |
99
+ micromamba run -n tkblend-tcltk python -m pytest -v
100
+
101
+ sanitizers:
102
+ name: ASan & UBSan (Linux - Py 3.12)
103
+ runs-on: ubuntu-latest
104
+ steps:
105
+ - name: Checkout code
106
+ uses: actions/checkout@v4
107
+ with:
108
+ submodules: recursive
109
+
110
+ - name: Set up Python 3.12
111
+ uses: actions/setup-python@v5
112
+ with:
113
+ python-version: "3.12"
114
+ cache: "pip"
115
+
116
+ - name: Install Linux build & display dependencies
117
+ run: |
118
+ sudo apt-get update
119
+ sudo apt-get install -y tcl8.6-dev tk8.6-dev xvfb libx11-dev libasan8
120
+
121
+ - name: Install build tools & test dependencies
122
+ run: |
123
+ python -m pip install --upgrade pip setuptools wheel
124
+ python -m pip install cmake ninja nanobind scikit-build-core pytest Pillow darkdetect
125
+
126
+ - name: Run ASan & UBSan Sanitizer Test Suite
127
+ run: |
128
+ ./scripts/run_sanitizers.sh
129
+
130
+ coverage:
131
+ name: Dual Code Coverage (Python + C++)
132
+ runs-on: ubuntu-latest
133
+ steps:
134
+ - name: Checkout code
135
+ uses: actions/checkout@v4
136
+ with:
137
+ submodules: recursive
138
+
139
+ - name: Set up Python 3.12
140
+ uses: actions/setup-python@v5
141
+ with:
142
+ python-version: "3.12"
143
+ cache: "pip"
144
+
145
+ - name: Install Linux build & display dependencies
146
+ run: |
147
+ sudo apt-get update
148
+ sudo apt-get install -y tcl8.6-dev tk8.6-dev xvfb libx11-dev gcovr
149
+
150
+ - name: Install build tools & coverage dependencies
151
+ run: |
152
+ python -m pip install --upgrade pip setuptools wheel
153
+ python -m pip install cmake ninja nanobind scikit-build-core pytest pytest-cov coverage gcovr Pillow darkdetect
154
+
155
+ - name: Run Dual Coverage Suite (Python + C++)
156
+ run: |
157
+ ./scripts/run_coverage.sh
158
+
159
+ - name: Upload Python Coverage HTML Report
160
+ uses: actions/upload-artifact@v4
161
+ if: always()
162
+ with:
163
+ name: python-coverage-report
164
+ path: build/coverage/python_html/
165
+
166
+ - name: Upload C++ Coverage HTML Report
167
+ uses: actions/upload-artifact@v4
168
+ if: always()
169
+ with:
170
+ name: cpp-coverage-report
171
+ path: build/coverage/cpp_html/
172
+
173
+ windows_direct_link:
174
+ name: Windows Direct-Link Validation (Py 3.12)
175
+ runs-on: windows-latest
176
+ steps:
177
+ - name: Checkout code
178
+ uses: actions/checkout@v4
179
+ with:
180
+ submodules: recursive
181
+
182
+ - name: Install Windows Tcl/Tk development files
183
+ id: tcltk
184
+ uses: mamba-org/setup-micromamba@v3
185
+ with:
186
+ environment-name: tkblend-tcltk-direct
187
+ create-args: >-
188
+ --channel conda-forge
189
+ python=3.12
190
+ tk=8.6.13
191
+ init-shell: none
192
+
193
+ - name: Install Windows build tools & dependencies
194
+ run: |
195
+ micromamba run -n tkblend-tcltk-direct python -m pip install --upgrade pip setuptools wheel
196
+ micromamba run -n tkblend-tcltk-direct python -m pip install cmake ninja nanobind scikit-build-core pytest Pillow darkdetect
197
+
198
+ - name: Verify Windows tkinter
199
+ run: micromamba run -n tkblend-tcltk-direct python -c "import tkinter; print(tkinter.TkVersion)"
200
+
201
+ - name: Build and install tkblend (Direct-Link mode)
202
+ run: micromamba run -n tkblend-tcltk-direct python -m pip install -e . --no-build-isolation -v -Ccmake.define.TKBLEND_PURE_STUBS=OFF
203
+ env:
204
+ CMAKE_PREFIX_PATH: ${{ steps.tcltk.outputs.environment-path }}/Library
205
+ CMAKE_ARGS: "-DTKBLEND_PURE_STUBS=OFF"
206
+
207
+ - name: Verify PE imports for direct Tk linking
208
+ run: |
209
+ micromamba run -n tkblend-tcltk-direct python -c "
210
+ import glob, struct, sys
211
+
212
+ pyd_files = glob.glob('tkblend/_tkblend*.pyd')
213
+ if not pyd_files:
214
+ print('Error: Could not find _tkblend*.pyd in tkblend/', file=sys.stderr)
215
+ sys.exit(1)
216
+
217
+ pyd_path = pyd_files[0]
218
+ print(f'Inspecting PE imports for: {pyd_path}')
219
+
220
+ with open(pyd_path, 'rb') as f:
221
+ data = f.read()
222
+
223
+ e_lfanew = struct.unpack_from('<I', data, 0x3C)[0]
224
+ magic = struct.unpack_from('<H', data, e_lfanew + 24)[0]
225
+
226
+ if magic == 0x20B:
227
+ import_rva, import_size = struct.unpack_from('<II', data, e_lfanew + 24 + 120)
228
+ else:
229
+ import_rva, import_size = struct.unpack_from('<II', data, e_lfanew + 24 + 104)
230
+
231
+ num_sections = struct.unpack_from('<H', data, e_lfanew + 6)[0]
232
+ opt_header_size = struct.unpack_from('<H', data, e_lfanew + 20)[0]
233
+ section_offset = e_lfanew + 24 + opt_header_size
234
+
235
+ def rva_to_offset(rva):
236
+ for i in range(num_sections):
237
+ sec = section_offset + i * 40
238
+ sec_vsize, sec_va, sec_raw_size, sec_raw_offset = struct.unpack_from('<IIII', data, sec + 8)
239
+ if sec_va <= rva < sec_va + max(sec_vsize, sec_raw_size):
240
+ return sec_raw_offset + (rva - sec_va)
241
+ return None
242
+
243
+ imports = []
244
+ curr = rva_to_offset(import_rva)
245
+ while curr is not None and curr + 20 <= len(data):
246
+ orig_first_thunk, ts, fchain, name_rva, first_thunk = struct.unpack_from('<IIIII', data, curr)
247
+ if name_rva == 0:
248
+ break
249
+ name_offset = rva_to_offset(name_rva)
250
+ if name_offset:
251
+ name = data[name_offset:data.find(b'\x00', name_offset)].decode('ascii', errors='ignore')
252
+ imports.append(name.lower())
253
+ curr += 20
254
+
255
+ print(f'Imported DLLs: {imports}')
256
+ assert any('tk86' in imp or 'tk' in imp for imp in imports), f'Expected direct link to Tk DLL in PE imports, found: {imports}'
257
+ print('Verified: Direct link to Tk is active in PE imports.')
258
+ "
259
+
260
+ - name: Run tests (Windows Direct-Link)
261
+ run: |
262
+ micromamba run -n tkblend-tcltk-direct python -m pytest -v -m "not slow"
263
+
@@ -0,0 +1,139 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: write
11
+
12
+ jobs:
13
+ build_sdist:
14
+ name: Build Source Distribution (sdist)
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - name: Checkout code
18
+ uses: actions/checkout@v4
19
+ with:
20
+ submodules: recursive
21
+
22
+ - name: Set up Python
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: "3.12"
26
+
27
+ - name: Install build
28
+ run: python -m pip install --upgrade build
29
+
30
+ - name: Build sdist
31
+ run: python -m build --sdist
32
+
33
+ - name: Upload sdist artifact
34
+ uses: actions/upload-artifact@v4
35
+ with:
36
+ name: dist-sdist
37
+ path: dist/*.tar.gz
38
+
39
+ build_wheels:
40
+ name: Build Wheels (${{ matrix.os }} - ${{ matrix.cibw_archs }})
41
+ runs-on: ${{ matrix.os }}
42
+ strategy:
43
+ fail-fast: false
44
+ matrix:
45
+ include:
46
+ - os: ubuntu-latest
47
+ cibw_archs: "x86_64"
48
+ - os: ubuntu-latest
49
+ cibw_archs: "aarch64"
50
+ use_qemu: true
51
+ - os: macos-15-intel
52
+ cibw_archs: "x86_64"
53
+ - os: macos-14
54
+ cibw_archs: "arm64"
55
+ - os: windows-latest
56
+ cibw_archs: "AMD64"
57
+
58
+ steps:
59
+ - name: Checkout code
60
+ uses: actions/checkout@v4
61
+ with:
62
+ submodules: recursive
63
+
64
+ - name: Set up QEMU
65
+ if: matrix.use_qemu
66
+ uses: docker/setup-qemu-action@v3
67
+ with:
68
+ platforms: arm64
69
+
70
+ - name: Set up Python
71
+ uses: actions/setup-python@v5
72
+ with:
73
+ python-version: "3.12"
74
+
75
+ - name: Install macOS Tcl/Tk development files
76
+ if: runner.os == 'macOS'
77
+ run: |
78
+ brew install tcl-tk
79
+ echo "CMAKE_PREFIX_PATH=$(brew --prefix tcl-tk)" >> $GITHUB_ENV
80
+
81
+ - name: Install Windows Tcl/Tk development files
82
+ if: runner.os == 'Windows'
83
+ id: tcltk
84
+ uses: mamba-org/setup-micromamba@v3
85
+ with:
86
+ environment-name: tkblend-tcltk
87
+ create-args: >-
88
+ --channel conda-forge
89
+ tk=8.6.13
90
+ init-shell: none
91
+
92
+ - name: Set Windows CMake Prefix Path
93
+ if: runner.os == 'Windows'
94
+ run: |
95
+ echo "CMAKE_PREFIX_PATH=${{ steps.tcltk.outputs.environment-path }}/Library" >> $GITHUB_ENV
96
+
97
+ - name: Install cibuildwheel
98
+ run: python -m pip install cibuildwheel==2.22.0 delvewheel
99
+
100
+ - name: Build wheels via cibuildwheel
101
+ run: python -m cibuildwheel --output-dir wheelhouse
102
+ env:
103
+ CIBW_ARCHS: ${{ matrix.cibw_archs }}
104
+ CMAKE_PREFIX_PATH: ${{ steps.tcltk.outputs.environment-path && format('{0}/Library', steps.tcltk.outputs.environment-path) || '' }}
105
+ CIBW_ENVIRONMENT_WINDOWS: "CMAKE_PREFIX_PATH='${{ steps.tcltk.outputs.environment-path }}/Library'"
106
+
107
+ - name: Upload wheel artifacts
108
+ uses: actions/upload-artifact@v4
109
+ with:
110
+ name: dist-wheels-${{ matrix.os }}-${{ matrix.cibw_archs }}
111
+ path: wheelhouse/*.whl
112
+
113
+ github_release:
114
+ name: Create GitHub Release
115
+ needs: [build_sdist, build_wheels]
116
+ runs-on: ubuntu-latest
117
+ if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
118
+ steps:
119
+ - name: Checkout code
120
+ uses: actions/checkout@v4
121
+
122
+ - name: Download all artifacts
123
+ uses: actions/download-artifact@v4
124
+ with:
125
+ path: dist
126
+ pattern: dist-*
127
+ merge-multiple: true
128
+
129
+ - name: List distribution files
130
+ run: |
131
+ ls -lh dist/
132
+
133
+ - name: Create GitHub Release
134
+ uses: softprops/action-gh-release@v2
135
+ with:
136
+ files: dist/*
137
+ generate_release_notes: true
138
+ draft: false
139
+ prerelease: ${{ contains(github.ref, 'b') || contains(github.ref, 'beta') || contains(github.ref, 'rc') || contains(github.ref, 'alpha') }}
@@ -0,0 +1,49 @@
1
+ # Python bytecode and caches
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.pyd
7
+ *.dll
8
+ *.dylib
9
+
10
+ # Build directories and eggs
11
+ build/
12
+ dist/
13
+ *.egg-info/
14
+ .eggs/
15
+ _skbuild/
16
+ CMakeLocalConfig.cmake
17
+
18
+ # Virtual environments
19
+ .venv/
20
+ env/
21
+ venv/
22
+ ENV/
23
+
24
+ # Testing and coverage
25
+ .pytest_cache/
26
+ .coverage
27
+ .coverage.*
28
+ htmlcov/
29
+ coverage.xml
30
+ *.gcda
31
+ *.gcno
32
+ *.gcov
33
+
34
+ # IDE and OS files
35
+ .vscode/
36
+ .idea/
37
+ .DS_Store
38
+ Thumbs.db
39
+ REFERENCE ONLY/
40
+ tests/visual_baselines/
41
+ CustomTkinter-6.0.0/
42
+ snapshots/
43
+
44
+
45
+ *.whl
46
+ *.tar.*
47
+ log/
48
+ artifacts/
49
+ screenshots/