bootstack 0.4.6__tar.gz → 0.4.8__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 (480) hide show
  1. {bootstack-0.4.6/src/bootstack.egg-info → bootstack-0.4.8}/PKG-INFO +1 -1
  2. {bootstack-0.4.6 → bootstack-0.4.8}/pyproject.toml +2 -2
  3. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_core/localization.py +90 -1
  4. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/style_builder_ttk.py +30 -0
  5. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/utility.py +68 -0
  6. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/base.py +12 -3
  7. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/container.py +34 -8
  8. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/_parts/spinnerentry_part.py +2 -1
  9. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/_parts/textentry_part.py +2 -1
  10. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/contextmenu.py +21 -0
  11. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/selectbox.py +49 -6
  12. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tableview/tableview.py +2 -1
  13. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tabs/tabview.py +2 -3
  14. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/core.py +1 -12
  15. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tree/treeitem.py +7 -0
  16. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/mixins/localization_mixin.py +2 -2
  17. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/flexframe.py +47 -5
  18. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/optionmenu.py +3 -3
  19. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/label.py +9 -13
  20. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/select.py +5 -1
  21. {bootstack-0.4.6 → bootstack-0.4.8/src/bootstack.egg-info}/PKG-INFO +1 -1
  22. {bootstack-0.4.6 → bootstack-0.4.8}/LICENSE +0 -0
  23. {bootstack-0.4.6 → bootstack-0.4.8}/MANIFEST.in +0 -0
  24. {bootstack-0.4.6 → bootstack-0.4.8}/NOTICE +0 -0
  25. {bootstack-0.4.6 → bootstack-0.4.8}/README.md +0 -0
  26. {bootstack-0.4.6 → bootstack-0.4.8}/setup.cfg +0 -0
  27. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/__init__.py +0 -0
  28. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/__main__.py +0 -0
  29. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_core/__init__.py +0 -0
  30. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_core/capture.py +0 -0
  31. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_core/exceptions.py +0 -0
  32. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_core/images.py +0 -0
  33. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_core/mixins/__init__.py +0 -0
  34. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_core/mixins/ttk_state.py +0 -0
  35. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_core/mixins/widget.py +0 -0
  36. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_core/paths.py +0 -0
  37. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_core/signal_binding.py +0 -0
  38. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_core/variables.py +0 -0
  39. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_runtime/__init__.py +0 -0
  40. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_runtime/app.py +0 -0
  41. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_runtime/base_window.py +0 -0
  42. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_runtime/events.py +0 -0
  43. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_runtime/grab.py +0 -0
  44. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_runtime/shortcuts.py +0 -0
  45. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_runtime/tk_patch.py +0 -0
  46. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_runtime/toplevel.py +0 -0
  47. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_runtime/utility.py +0 -0
  48. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_runtime/visual_focus.py +0 -0
  49. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_runtime/wheel.py +0 -0
  50. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/_runtime/window_utilities.py +0 -0
  51. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/__init__.py +0 -0
  52. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/bootstack.ico +0 -0
  53. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/bootstack.png +0 -0
  54. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/__init__.py +0 -0
  55. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/badge-pill.png +0 -0
  56. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/badge-square.png +0 -0
  57. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/button-compact.png +0 -0
  58. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/button-default.png +0 -0
  59. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
  60. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
  61. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
  62. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
  63. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
  64. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
  65. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
  66. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
  67. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
  68. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
  69. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
  70. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
  71. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/card.png +0 -0
  72. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/checkbox-checked.png +0 -0
  73. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  74. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/checkbox-unchecked.png +0 -0
  75. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/field.png +0 -0
  76. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/input-addon-compact.png +0 -0
  77. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/input-addon-default.png +0 -0
  78. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/input-compact.png +0 -0
  79. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/input-default.png +0 -0
  80. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/list-item-separated.png +0 -0
  81. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/list-item.png +0 -0
  82. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/listrow-compact.png +0 -0
  83. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/listrow-default.png +0 -0
  84. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/manifest.toml +0 -0
  85. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/menu-item.png +0 -0
  86. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/navitem-compact.png +0 -0
  87. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/navitem-default.png +0 -0
  88. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/progressbar-h-compact.png +0 -0
  89. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/progressbar-h-default.png +0 -0
  90. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/progressbar-v-compact.png +0 -0
  91. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/progressbar-v-default.png +0 -0
  92. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/radiobutton.png +0 -0
  93. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  94. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/scrollbar-vertical.png +0 -0
  95. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/slider-handle.png +0 -0
  96. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/slider-track-h.png +0 -0
  97. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/slider-track-v.png +0 -0
  98. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/switch-off.png +0 -0
  99. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/switch-on.png +0 -0
  100. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/tab-h.png +0 -0
  101. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/elements/tab-v.png +0 -0
  102. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/icons/bootstrap.ttf +0 -0
  103. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/icons/glyphmap.json +0 -0
  104. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/icons/icon_metrics.json +0 -0
  105. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  106. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +0 -0
  107. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
  108. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +0 -0
  109. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  110. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +0 -0
  111. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  112. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +0 -0
  113. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  114. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +0 -0
  115. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  116. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +0 -0
  117. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  118. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +0 -0
  119. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  120. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +0 -0
  121. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  122. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +0 -0
  123. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  124. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +0 -0
  125. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  126. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +0 -0
  127. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  128. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +0 -0
  129. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  130. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +0 -0
  131. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  132. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +0 -0
  133. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  134. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +0 -0
  135. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  136. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +0 -0
  137. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  138. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +0 -0
  139. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  140. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +0 -0
  141. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  142. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +0 -0
  143. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  144. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +0 -0
  145. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  146. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +0 -0
  147. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  148. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +0 -0
  149. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  150. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +0 -0
  151. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/__init__.py +0 -0
  152. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/__main__.py +0 -0
  153. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/add.py +0 -0
  154. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/appicon.py +0 -0
  155. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/build.py +0 -0
  156. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/config.py +0 -0
  157. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/demo.py +0 -0
  158. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/dev.py +0 -0
  159. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/doctor.py +0 -0
  160. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/icons.py +0 -0
  161. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/promote.py +0 -0
  162. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/pyinstaller.py +0 -0
  163. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/run.py +0 -0
  164. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/start.py +0 -0
  165. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/cli/templates/__init__.py +0 -0
  166. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/clipboard.py +0 -0
  167. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/constants.py +0 -0
  168. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/data/README.md +0 -0
  169. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/data/__init__.py +0 -0
  170. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/data/_observable.py +0 -0
  171. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/data/base.py +0 -0
  172. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/data/file_source.py +0 -0
  173. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/data/memory_source.py +0 -0
  174. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/data/query.py +0 -0
  175. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/data/readers.py +0 -0
  176. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/data/sqlite_source.py +0 -0
  177. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/data/types.py +0 -0
  178. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/data/writers.py +0 -0
  179. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dev/__init__.py +0 -0
  180. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dev/_capture.py +0 -0
  181. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dev/_env.py +0 -0
  182. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dev/_registry.py +0 -0
  183. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dev/_reloader.py +0 -0
  184. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dev/_reset.py +0 -0
  185. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dev/_watcher.py +0 -0
  186. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dialogs/__init__.py +0 -0
  187. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dialogs/_impl/__init__.py +0 -0
  188. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dialogs/_impl/colorchooser.py +0 -0
  189. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dialogs/_impl/datedialog.py +0 -0
  190. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dialogs/_impl/dialog.py +0 -0
  191. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dialogs/_impl/filterdialog.py +0 -0
  192. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dialogs/_impl/fontdialog.py +0 -0
  193. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dialogs/_impl/formdialog.py +0 -0
  194. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dialogs/_impl/message.py +0 -0
  195. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/dialogs/_impl/query.py +0 -0
  196. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/errors.py +0 -0
  197. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/events/__init__.py +0 -0
  198. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/events/_event.py +0 -0
  199. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/events/_payloads.py +0 -0
  200. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/events/_subscription.py +0 -0
  201. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/i18n/README.md +0 -0
  202. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/i18n/__init__.py +0 -0
  203. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/i18n/catalog.py +0 -0
  204. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/i18n/intl_format.py +0 -0
  205. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/i18n/msgcat.py +0 -0
  206. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/i18n/specs.py +0 -0
  207. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/images.py +0 -0
  208. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/py.typed +0 -0
  209. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/scheduling/__init__.py +0 -0
  210. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/scheduling/_schedule.py +0 -0
  211. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/shortcuts.py +0 -0
  212. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/signals/README.md +0 -0
  213. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/signals/__init__.py +0 -0
  214. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/signals/integration.py +0 -0
  215. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/signals/signal.py +0 -0
  216. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/signals/types.py +0 -0
  217. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/store.py +0 -0
  218. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/streams/__init__.py +0 -0
  219. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/streams/_stream.py +0 -0
  220. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/__init__.py +0 -0
  221. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/__init__.py +0 -0
  222. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/badge.py +0 -0
  223. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/button.py +0 -0
  224. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/buttongroup.py +0 -0
  225. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/calendar.py +0 -0
  226. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/checkbutton.py +0 -0
  227. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/combobox.py +0 -0
  228. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/contextmenu.py +0 -0
  229. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/entry.py +0 -0
  230. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/expander.py +0 -0
  231. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/field.py +0 -0
  232. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/frame.py +0 -0
  233. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/label.py +0 -0
  234. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/labelframe.py +0 -0
  235. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/listview.py +0 -0
  236. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/menubar.py +0 -0
  237. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/menubutton.py +0 -0
  238. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/panedwindow.py +0 -0
  239. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/progressbar.py +0 -0
  240. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/radiobutton.py +0 -0
  241. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/scale.py +0 -0
  242. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/scrollbar.py +0 -0
  243. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/separator.py +0 -0
  244. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/sidenav.py +0 -0
  245. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/sizegrip.py +0 -0
  246. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/spinbox.py +0 -0
  247. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/switch.py +0 -0
  248. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/tabitem.py +0 -0
  249. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/togglegroup.py +0 -0
  250. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/toolbutton.py +0 -0
  251. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/tooltip.py +0 -0
  252. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/treeview.py +0 -0
  253. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders/utils.py +0 -0
  254. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders_tk/__init__.py +0 -0
  255. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/builders_tk/defaults.py +0 -0
  256. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/element.py +0 -0
  257. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/fonts.py +0 -0
  258. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/style.py +0 -0
  259. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/style_builder_base.py +0 -0
  260. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/style_builder_mixed.py +0 -0
  261. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/style_builder_tk.py +0 -0
  262. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/style_resolver.py +0 -0
  263. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/theme.py +0 -0
  264. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/theme_provider.py +0 -0
  265. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/themes/__init__.py +0 -0
  266. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/tk_patch.py +0 -0
  267. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/token_maps.py +0 -0
  268. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/types.py +0 -0
  269. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/style/typography.py +0 -0
  270. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/types.py +0 -0
  271. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/validation/__init__.py +0 -0
  272. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/validation/types.py +0 -0
  273. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/validation/validation_result.py +0 -0
  274. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/validation/validation_rules.py +0 -0
  275. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/__init__.py +0 -0
  276. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/__init__.py +0 -0
  277. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/app_config.py +0 -0
  278. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/choices.py +0 -0
  279. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/context.py +0 -0
  280. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/events.py +0 -0
  281. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/field_mixin.py +0 -0
  282. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/icon_image_props.py +0 -0
  283. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/image_binding.py +0 -0
  284. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/kwargs.py +0 -0
  285. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/navmodel.py +0 -0
  286. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/options.py +0 -0
  287. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/selection_group.py +0 -0
  288. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/window_controls.py +0 -0
  289. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_core/window_menu.py +0 -0
  290. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/__init__.py +0 -0
  291. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/_internal/__init__.py +0 -0
  292. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/_internal/wrapper_base.py +0 -0
  293. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/_parts/__init__.py +0 -0
  294. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/_parts/numberentry_part.py +0 -0
  295. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/__init__.py +0 -0
  296. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/_dateutils.py +0 -0
  297. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/_image_fit.py +0 -0
  298. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/accordion.py +0 -0
  299. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/avatar.py +0 -0
  300. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/buttongroup.py +0 -0
  301. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/calendar.py +0 -0
  302. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/carousel.py +0 -0
  303. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/chart.py +0 -0
  304. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/compositeframe.py +0 -0
  305. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/dateentry.py +0 -0
  306. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/dropdownbutton.py +0 -0
  307. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/expander.py +0 -0
  308. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/field.py +0 -0
  309. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/form.py +0 -0
  310. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/gallery.py +0 -0
  311. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/list/__init__.py +0 -0
  312. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/list/listitem.py +0 -0
  313. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/list/listview.py +0 -0
  314. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/menu/__init__.py +0 -0
  315. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/menu/model.py +0 -0
  316. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/menu/render_native.py +0 -0
  317. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/menu/render_themed.py +0 -0
  318. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/meter.py +0 -0
  319. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/numericentry.py +0 -0
  320. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/pagestack.py +0 -0
  321. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/passwordentry.py +0 -0
  322. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/pathentry.py +0 -0
  323. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/picture.py +0 -0
  324. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/radiogroup.py +0 -0
  325. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/scrolledtext.py +0 -0
  326. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/scrolledtext.pyi +0 -0
  327. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/scrollview.py +0 -0
  328. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/shell/__init__.py +0 -0
  329. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/shell/content_host.py +0 -0
  330. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/shell/layout.py +0 -0
  331. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/shell/nav_panel.py +0 -0
  332. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/shell/providers.py +0 -0
  333. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/shell/rail.py +0 -0
  334. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/shell/shell.py +0 -0
  335. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/shell/workspace.py +0 -0
  336. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/sidenav/__init__.py +0 -0
  337. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/sidenav/header.py +0 -0
  338. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/sidenav/separator.py +0 -0
  339. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/slider/__init__.py +0 -0
  340. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/slider/_shared.py +0 -0
  341. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/slider/rangeslider.py +0 -0
  342. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/slider/slider.py +0 -0
  343. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/spinnerentry.py +0 -0
  344. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tableview/__init__.py +0 -0
  345. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tableview/types.py +0 -0
  346. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tabs/__init__.py +0 -0
  347. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tabs/tabitem.py +0 -0
  348. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tabs/tabs.py +0 -0
  349. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/__init__.py +0 -0
  350. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/change.py +0 -0
  351. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/codeeditor.py +0 -0
  352. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/decoration.py +0 -0
  353. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/diff.py +0 -0
  354. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +0 -0
  355. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +0 -0
  356. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +0 -0
  357. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +0 -0
  358. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +0 -0
  359. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +0 -0
  360. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/filter.py +0 -0
  361. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/redirector.py +0 -0
  362. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/search_overlay.py +0 -0
  363. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/sidebar.py +0 -0
  364. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/style_registry.py +0 -0
  365. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/textarea.py +0 -0
  366. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textarea/undo.py +0 -0
  367. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/textentry.py +0 -0
  368. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/timeentry.py +0 -0
  369. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/toast.py +0 -0
  370. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/toast_stack.py +0 -0
  371. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/togglegroup.py +0 -0
  372. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/toolbar.py +0 -0
  373. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tooltip.py +0 -0
  374. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tree/__init__.py +0 -0
  375. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tree/source_binding.py +0 -0
  376. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tree/treenode.py +0 -0
  377. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/composites/tree/treeview.py +0 -0
  378. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/mixins/__init__.py +0 -0
  379. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/mixins/configure_mixin.py +0 -0
  380. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/mixins/entry_mixin.py +0 -0
  381. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/mixins/font_mixin.py +0 -0
  382. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/mixins/icon_mixin.py +0 -0
  383. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/mixins/signal_mixin.py +0 -0
  384. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/mixins/validation_mixin.py +0 -0
  385. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/__init__.py +0 -0
  386. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/_menubutton.py +0 -0
  387. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/badge.py +0 -0
  388. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/button.py +0 -0
  389. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/card.py +0 -0
  390. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/checkbutton.py +0 -0
  391. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/checktoggle.py +0 -0
  392. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/combobox.py +0 -0
  393. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/entry.py +0 -0
  394. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/frame.py +0 -0
  395. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/gridframe.py +0 -0
  396. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/label.py +0 -0
  397. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/labelframe.py +0 -0
  398. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/packframe.py +0 -0
  399. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/panedwindow.py +0 -0
  400. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/progressbar.py +0 -0
  401. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/radiobutton.py +0 -0
  402. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/radiotoggle.py +0 -0
  403. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/scrollbar.py +0 -0
  404. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/separator.py +0 -0
  405. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/sizegrip.py +0 -0
  406. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/spinbox.py +0 -0
  407. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/switch.py +0 -0
  408. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/_impl/primitives/treeview.py +0 -0
  409. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/app.py +0 -0
  410. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/appshell.py +0 -0
  411. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/avatar.py +0 -0
  412. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/boolean_controls.py +0 -0
  413. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/button.py +0 -0
  414. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/buttongroup.py +0 -0
  415. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/calendar.py +0 -0
  416. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/card.py +0 -0
  417. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/carousel.py +0 -0
  418. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/chart.py +0 -0
  419. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/codeeditor.py +0 -0
  420. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/contextmenu.py +0 -0
  421. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/datatable.py +0 -0
  422. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/datefield.py +0 -0
  423. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/divider.py +0 -0
  424. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/expander.py +0 -0
  425. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/form.py +0 -0
  426. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/gallery.py +0 -0
  427. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/gauge.py +0 -0
  428. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/grid.py +0 -0
  429. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/groupbox.py +0 -0
  430. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/listview.py +0 -0
  431. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/menubutton.py +0 -0
  432. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/numberfield.py +0 -0
  433. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/pagestack.py +0 -0
  434. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/passwordfield.py +0 -0
  435. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/pathfield.py +0 -0
  436. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/picture.py +0 -0
  437. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/progressbar.py +0 -0
  438. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/radio_variants.py +0 -0
  439. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/radiogroup.py +0 -0
  440. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/scrollbar.py +0 -0
  441. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/scrollview.py +0 -0
  442. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/selectbutton.py +0 -0
  443. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/sidebar_toggle.py +0 -0
  444. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/sizegrip.py +0 -0
  445. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/slider.py +0 -0
  446. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/spinbox.py +0 -0
  447. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/spinnerfield.py +0 -0
  448. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/splash.py +0 -0
  449. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/splitview.py +0 -0
  450. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/stacks.py +0 -0
  451. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/statusbar.py +0 -0
  452. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/tabs.py +0 -0
  453. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/textarea.py +0 -0
  454. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/textfield.py +0 -0
  455. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/theme_toggle.py +0 -0
  456. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/timefield.py +0 -0
  457. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/toast.py +0 -0
  458. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/togglegroup.py +0 -0
  459. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/toolbar.py +0 -0
  460. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/tooltip.py +0 -0
  461. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/tree.py +0 -0
  462. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/types.py +0 -0
  463. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack/widgets/window.py +0 -0
  464. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack.egg-info/SOURCES.txt +0 -0
  465. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack.egg-info/dependency_links.txt +0 -0
  466. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack.egg-info/entry_points.txt +0 -0
  467. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack.egg-info/requires.txt +0 -0
  468. {bootstack-0.4.6 → bootstack-0.4.8}/src/bootstack.egg-info/top_level.txt +0 -0
  469. {bootstack-0.4.6 → bootstack-0.4.8}/tests/test_dialog_anchor.py +0 -0
  470. {bootstack-0.4.6 → bootstack-0.4.8}/tests/test_events_doc_coverage.py +0 -0
  471. {bootstack-0.4.6 → bootstack-0.4.8}/tests/test_hot_reload.py +0 -0
  472. {bootstack-0.4.6 → bootstack-0.4.8}/tests/test_images.py +0 -0
  473. {bootstack-0.4.6 → bootstack-0.4.8}/tests/test_images_gui.py +0 -0
  474. {bootstack-0.4.6 → bootstack-0.4.8}/tests/test_intl_format.py +0 -0
  475. {bootstack-0.4.6 → bootstack-0.4.8}/tests/test_navmodel.py +0 -0
  476. {bootstack-0.4.6 → bootstack-0.4.8}/tests/test_on_color_contrast.py +0 -0
  477. {bootstack-0.4.6 → bootstack-0.4.8}/tests/test_public_surface.py +0 -0
  478. {bootstack-0.4.6 → bootstack-0.4.8}/tests/test_store.py +0 -0
  479. {bootstack-0.4.6 → bootstack-0.4.8}/tests/test_theme_from_existing.py +0 -0
  480. {bootstack-0.4.6 → bootstack-0.4.8}/tests/test_validation_rules.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bootstack
3
- Version: 0.4.6
3
+ Version: 0.4.8
4
4
  Summary: Build desktop GUI apps in pure Python and ship them as a standalone executable — built on Tk, no Tkinter boilerplate.
5
5
  Author-email: Israel Dryer <israel.dryer@gmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "bootstack"
7
- version = "0.4.6"
7
+ version = "0.4.8"
8
8
  description = "Build desktop GUI apps in pure Python and ship them as a standalone executable — built on Tk, no Tkinter boilerplate."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -114,7 +114,7 @@ where = ["src"]
114
114
  ]
115
115
 
116
116
  [tool.bumpversion]
117
- current_version = "0.4.6"
117
+ current_version = "0.4.8"
118
118
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)((?P<pre>a|b|rc)(?P<pre_n>\\d+))?"
119
119
  serialize = [
120
120
  "{major}.{minor}.{patch}{pre}{pre_n}",
@@ -10,7 +10,10 @@ a thin glue layer.
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
- from typing import Any, TYPE_CHECKING
13
+ import itertools
14
+ import weakref
15
+ from tkinter import TclError
16
+ from typing import Any, Callable, TYPE_CHECKING
14
17
 
15
18
  from bootstack.i18n.msgcat import MessageCatalog
16
19
  from bootstack.i18n.specs import (
@@ -177,6 +180,91 @@ def get_current_locale() -> str:
177
180
  return MessageCatalog.locale()
178
181
 
179
182
 
183
+ # =============================================================================
184
+ # Locale Change Propagation
185
+ # =============================================================================
186
+
187
+ _LOCALE_EVENT = "<<LocaleChanged>>"
188
+
189
+
190
+ # Don't bind a widget's refresh directly on the window: nothing removes it
191
+ # when the widget is destroyed.
192
+ class _LocaleRefreshers:
193
+ """The refresh methods one toplevel calls on a locale change."""
194
+
195
+ def __init__(self, toplevel: Any) -> None:
196
+ self._toplevel = toplevel
197
+ self._refreshers: dict[int, weakref.WeakMethod] = {}
198
+ self._keys = itertools.count()
199
+ self._funcid: str | None = None
200
+
201
+ def add(self, refresh: Callable[[], Any]) -> None:
202
+ """Register `refresh`, a bound method of a widget on this toplevel."""
203
+ self._ensure_bound()
204
+ key = next(self._keys)
205
+ refreshers = self._refreshers
206
+ # Drop the entry when the widget is collected, so a window whose
207
+ # locale never changes does not accumulate dead references either.
208
+ refreshers[key] = weakref.WeakMethod(
209
+ refresh, lambda _ref: refreshers.pop(key, None)
210
+ )
211
+
212
+ def _ensure_bound(self) -> None:
213
+ """Bind the dispatcher, or bind it again if something replaced it.
214
+
215
+ A `bind` of the event without `add` on this toplevel replaces every
216
+ binding for it, the dispatcher included, which would silently stop
217
+ every widget on the window from refreshing.
218
+ """
219
+ toplevel = self._toplevel
220
+ if self._funcid is not None:
221
+ script = str(toplevel.tk.call("bind", toplevel._w, _LOCALE_EVENT))
222
+ # The line prefix `_patched_bind` emits, as `_patched_unbind` matches it.
223
+ if ('if {"[%s ' % self._funcid) in script:
224
+ return
225
+ self._funcid = toplevel.bind(_LOCALE_EVENT, self._dispatch, add="+")
226
+
227
+ def _dispatch(self, _event: Any = None) -> None:
228
+ """Call each live widget's refresh, in the order they registered."""
229
+ for key, ref in list(self._refreshers.items()):
230
+ refresh = ref()
231
+ if refresh is None:
232
+ continue
233
+ widget = refresh.__self__
234
+ try:
235
+ alive = widget.winfo_exists()
236
+ except TclError:
237
+ alive = False
238
+ if not alive:
239
+ # Destroyed, but still referenced from somewhere else.
240
+ self._refreshers.pop(key, None)
241
+ continue
242
+ try:
243
+ refresh()
244
+ except Exception:
245
+ # Report and carry on, as a handler bound on its own would:
246
+ # one failing widget must not stop the rest from refreshing.
247
+ widget._report_exception()
248
+
249
+
250
+ def bind_locale_refresh(refresh: Callable[[], Any]) -> None:
251
+ """Call `refresh` on every locale change for as long as its widget exists.
252
+
253
+ The locale-change event is generated on the root; `refresh` is called,
254
+ with no arguments, when the event reaches the toplevel of the widget it is
255
+ bound to, after the widgets registered before it. A destroyed widget is
256
+ never called, and registering holds no strong reference to it.
257
+
258
+ Args:
259
+ refresh: A bound method of the widget to refresh.
260
+ """
261
+ toplevel = refresh.__self__.winfo_toplevel()
262
+ refreshers = toplevel.__dict__.get("_bs_locale_refreshers")
263
+ if refreshers is None:
264
+ refreshers = toplevel._bs_locale_refreshers = _LocaleRefreshers(toplevel)
265
+ refreshers.add(refresh)
266
+
267
+
180
268
  # =============================================================================
181
269
  # Signal Formatting
182
270
  # =============================================================================
@@ -227,5 +315,6 @@ __all__ = [
227
315
  "resolve_variable_text",
228
316
  "apply_spec",
229
317
  "get_current_locale",
318
+ "bind_locale_refresh",
230
319
  "create_formatted_signal",
231
320
  ]
@@ -9,6 +9,7 @@ from bootstack._core.exceptions import StyleBuilderError
9
9
  from bootstack.style.style_builder_base import StyleBuilderBase
10
10
  from bootstack.style.element import Element, ElementImage
11
11
  from bootstack.style.theme_provider import ThemeProvider
12
+ from bootstack.style.utility import pretile_element_image
12
13
 
13
14
 
14
15
  class BuilderCallable(Protocol):
@@ -344,8 +345,37 @@ class StyleBuilderTtk(StyleBuilderBase):
344
345
  if name in existing:
345
346
  # Element already exists; avoid duplicate creation error on theme rebuilds
346
347
  return
348
+ try:
349
+ args, kwargs = self._pretile_element_images(args, kwargs)
350
+ except Exception:
351
+ # Pre-tiling only speeds painting up; the element draws from its own images.
352
+ from bootstack._runtime.utility import debug_log_exception
353
+ debug_log_exception(f"could not pre-tile the images of element {name!r}")
347
354
  self.style.element_create(name, "image", *args, **kwargs)
348
355
 
356
+ def _pretile_element_images(self, args: list, kwargs: dict) -> tuple[list, dict]:
357
+ """Swap an element's images for copies that paint a wide parcel in few repeats.
358
+
359
+ Only an element that stretches horizontally is pre-tiled; one that does
360
+ not is drawn at its image's own width, so a wider image would draw wider.
361
+ No `sticky` means `nsew`.
362
+ """
363
+ sticky = kwargs.get("sticky")
364
+ if sticky is not None and not ("e" in sticky and "w" in sticky):
365
+ return args, kwargs
366
+
367
+ master = self.style.master
368
+ border = kwargs.get("border", 0)
369
+ default, *specs = args
370
+ tiled = [pretile_element_image(default, border, master)]
371
+ tiled += [(state, pretile_element_image(image, border, master)) for state, image in specs]
372
+
373
+ if tiled[0] != default and "width" not in kwargs:
374
+ # The element's minimum width defaults to its image's width, so pin
375
+ # it to the original image's.
376
+ kwargs = {**kwargs, "width": int(self.style.tk.call("image", "width", default))}
377
+ return tiled, kwargs
378
+
349
379
  def create_style_layout(self, ttk_style: str, element: Element | list[Element]):
350
380
  if isinstance(element, list):
351
381
  self.style.layout(ttk_style, [e.spec() for e in element])
@@ -1,3 +1,4 @@
1
+ import tkinter
1
2
  from colorsys import hls_to_rgb, rgb_to_hls
2
3
  from dataclasses import dataclass
3
4
  from pathlib import Path
@@ -553,6 +554,73 @@ def recolor_element_image(
553
554
  return ElementImageResult(image=photo_image, meta=meta)
554
555
 
555
556
 
557
+ PRETILE_SPAN = 256
558
+ """The width, in pixels, an element image's middle column is repeated to."""
559
+
560
+
561
+ def pretile_element_image(
562
+ image: str,
563
+ border: int | tuple[int, ...] | list[int],
564
+ master: tkinter.Misc,
565
+ ) -> str:
566
+ """Return a copy of an element image with its middle column repeated.
567
+
568
+ A stretched image element is drawn by repeating its middle column across the
569
+ parcel, one draw per repeat, so an element image only a few pixels wide makes
570
+ a wide widget slow to paint. The copy repeats the column to at least
571
+ `PRETILE_SPAN` pixels, in whole repeats starting at the column's left edge as
572
+ the element's own drawing does, so it draws the same pixels in fewer repeats.
573
+ The left and right columns are copied unchanged.
574
+
575
+ Copies are cached by source image and border, so elements that share an image
576
+ share its copy.
577
+
578
+ Args:
579
+ image: Name of the source photo image.
580
+ border: The element's border: one value, or up to four in the order left,
581
+ top, right, bottom, where right defaults to left.
582
+ master: Widget whose interpreter owns `image`.
583
+
584
+ Returns:
585
+ The name of the copy, or `image` itself when its middle column is already
586
+ at least `PRETILE_SPAN` pixels wide or has no width.
587
+ """
588
+ values = [border] if isinstance(border, int) else list(border)
589
+ left = int(values[0]) if values else 0
590
+ right = int(values[2]) if len(values) > 2 else left
591
+
592
+ cache_key = ("element-pretile", str(image), left, right)
593
+ cached = ImageService.get_cached(cache_key)
594
+ if cached is not None:
595
+ return str(cached)
596
+
597
+ tk = master.tk
598
+ width = int(tk.call("image", "width", image))
599
+ height = int(tk.call("image", "height", image))
600
+ middle = width - left - right
601
+ if middle <= 0 or middle >= PRETILE_SPAN:
602
+ return str(image)
603
+
604
+ span = middle * -(-PRETILE_SPAN // middle)
605
+ tiled = tkinter.PhotoImage(master=master, width=left + span + right, height=height)
606
+
607
+ def copy(x0: int, x1: int, *to: int) -> None:
608
+ tk.call(
609
+ tiled, "copy", image, "-from", x0, 0, x1, height,
610
+ "-to", *to, "-compositingrule", "set",
611
+ )
612
+
613
+ if left:
614
+ copy(0, left, 0, 0)
615
+ # A -to rectangle larger than the -from region repeats the region across it.
616
+ copy(left, left + middle, left, 0, left + span, height)
617
+ if right:
618
+ copy(width - right, width, left + span, 0)
619
+
620
+ ImageService.set_cached(cache_key, tiled)
621
+ return str(tiled)
622
+
623
+
556
624
  def should_darken(bg_hex: str) -> bool:
557
625
  """Determine whether to darken or lighten based on luminance and saturation."""
558
626
  r, g, b = [v / 255.0 for v in ImageColor.getrgb(bg_hex)]
@@ -446,8 +446,14 @@ class PublicWidgetBase:
446
446
  if placement.method == "flex":
447
447
  # The FlexFrame's managed list IS the attached-sibling order, so the
448
448
  # current managed index is the slot a plain attach() should restore.
449
+ from bootstack.widgets._core.container import snapshot_flex_slot
450
+
449
451
  idx = placement.master.index_of(self._internal)
450
- placement.index = idx if idx >= 0 else None
452
+ if idx >= 0:
453
+ snapshot_flex_slot(placement, placement.master, idx)
454
+ else:
455
+ placement.index = None
456
+ placement.preceding = ()
451
457
  placement.master.remove_child(self._internal)
452
458
  elif placement.method == "pack":
453
459
  slaves = list(placement.master.pack_slaves())
@@ -485,7 +491,7 @@ class PublicWidgetBase:
485
491
  """
486
492
  from bootstack.widgets._core.container import (
487
493
  PACK_KEYS, GRID_KEYS, PLACE_KEYS, normalize_fill, _expand_margin, _flex_child_opts,
488
- _reject_legacy_child_kwargs, resolve_pack_order
494
+ _reject_legacy_child_kwargs, resolve_pack_order, restored_flex_index
489
495
  )
490
496
 
491
497
  placement = getattr(self, "_placement", None)
@@ -503,7 +509,10 @@ class PublicWidgetBase:
503
509
  options = dict(placement.options)
504
510
 
505
511
  if placement.method == "flex":
506
- index = kwargs.pop("index", placement.index)
512
+ if "index" in kwargs:
513
+ index = kwargs.pop("index")
514
+ else:
515
+ index = restored_flex_index(placement)
507
516
  if kwargs:
508
517
  _reject_legacy_child_kwargs(kwargs, type(self).__name__, "flex child")
509
518
  _expand_margin(kwargs)
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import tkinter
4
+ import weakref
4
5
  from typing import Any, Literal
5
6
 
6
7
  PACK_KEYS = frozenset({
@@ -101,11 +102,12 @@ class Placement:
101
102
  Recorded on the child at layout time by `guide_layout`; consumed by
102
103
  `PublicWidgetBase.detach`/`attach` to remove and re-insert the widget
103
104
  without re-running its constructor. `options` holds the resolved Tk
104
- geometry-manager options (no ordering refs); `index` is the pack position
105
- among siblings, snapshotted afresh on each `detach`.
105
+ geometry-manager options (no ordering refs); `index` is the position among
106
+ siblings, snapshotted afresh on each `detach`. For a flex child, set it
107
+ with `snapshot_flex_slot`, which keeps `preceding` in step with it.
106
108
  """
107
109
 
108
- __slots__ = ("method", "master", "options", "index")
110
+ __slots__ = ("method", "master", "options", "index", "preceding")
109
111
 
110
112
  def __init__(
111
113
  self,
@@ -114,10 +116,31 @@ class Placement:
114
116
  options: dict,
115
117
  index: int | None = None,
116
118
  ) -> None:
117
- self.method = method # "pack" | "grid" | "place"
119
+ self.method = method # "pack" | "grid" | "place" | "flex"
118
120
  self.master = master # the Tk master the child is placed in
119
121
  self.options = options # resolved Tk options (no before/after/index)
120
122
  self.index = index # pack order among siblings (snapshot at detach)
123
+ self.preceding: tuple = () # weakrefs to the flex siblings before `index`
124
+
125
+
126
+ def snapshot_flex_slot(placement: Placement, frame: Any, index: int) -> None:
127
+ """Record `index` in `frame` as the slot a plain `attach()` restores.
128
+
129
+ The siblings before the slot are kept by weak reference so that one
130
+ destroyed before the attach no longer counts toward the index.
131
+ """
132
+ placement.index = index
133
+ placement.preceding = tuple(weakref.ref(w) for w, _ in frame._managed[:index])
134
+
135
+
136
+ def restored_flex_index(placement: Placement) -> int | None:
137
+ """The snapshotted slot, less the preceding siblings destroyed since."""
138
+ from bootstack.widgets._impl.primitives.flexframe import _alive
139
+
140
+ if placement.index is None:
141
+ return None
142
+ gone = sum(1 for ref in placement.preceding if not ((w := ref()) and _alive(w)))
143
+ return max(placement.index - gone, 0)
121
144
 
122
145
 
123
146
  def resolve_pack_order(order: dict, master: tkinter.Misc) -> dict:
@@ -441,8 +464,11 @@ def place_flex_child(
441
464
  frame.add_child(child._internal, opts, index=index)
442
465
  child._placement = Placement("flex", frame, opts)
443
466
  else:
444
- # Record the slot a later attach() restores: an explicit index, else the
445
- # natural append position (current child count).
446
- if index is None:
447
- index = len(frame._managed)
467
+ # Record the slot a later attach() restores: an explicit index as given,
468
+ # else the natural append position (current child count).
448
469
  child._placement = Placement("flex", frame, opts, index=index)
470
+ if index is None:
471
+ # Prune first: a dead sibling left in `_managed` would be counted
472
+ # twice, once in the count and again when attach() discounts it.
473
+ frame._prune_destroyed()
474
+ snapshot_flex_slot(child._placement, frame, len(frame._managed))
@@ -8,6 +8,7 @@ both text and numeric values.
8
8
  from tkinter import Event, TclError
9
9
  from typing import Any, Callable
10
10
 
11
+ from bootstack._core.localization import bind_locale_refresh
11
12
  from bootstack.events import ChangeEvent, InputEvent
12
13
  from bootstack.i18n import MessageCatalog, IntlFormatter
13
14
  from bootstack.widgets._impl.primitives.spinbox import Spinbox
@@ -131,7 +132,7 @@ class SpinnerEntryPart(ValidationMixin, Spinbox):
131
132
  self.bind('<FocusIn>', self._store_prev_value, add=True)
132
133
  self.bind('<FocusOut>', self._handle_focus_out, add=True)
133
134
  self.bind('<Return>', self._handle_return, add=True)
134
- self.winfo_toplevel().bind('<<LocaleChanged>>', self._on_locale_changed, add='+')
135
+ bind_locale_refresh(self._on_locale_changed)
135
136
 
136
137
  # Set initial focus
137
138
  if initial_focus:
@@ -1,6 +1,7 @@
1
1
  from tkinter import Event, TclError
2
2
  from typing import Any, Callable
3
3
 
4
+ from bootstack._core.localization import bind_locale_refresh
4
5
  from bootstack.events import ChangeEvent, InputEvent
5
6
  from bootstack.i18n import MessageCatalog, IntlFormatter
6
7
  from bootstack.widgets._impl.primitives.entry import Entry
@@ -102,7 +103,7 @@ class TextEntryPart(ValidationMixin, Entry):
102
103
  self.bind('<FocusIn>', self._store_prev_value, add=True)
103
104
  self.bind('<FocusOut>', self._handle_focus_out, add=True)
104
105
  self.bind('<Return>', self._handle_return, add=True)
105
- self.winfo_toplevel().bind('<<LocaleChanged>>', self._on_locale_changed, add='+')
106
+ bind_locale_refresh(self._on_locale_changed)
106
107
 
107
108
  # set initial focus
108
109
  if initial_focus:
@@ -4,6 +4,7 @@ Provides a customizable context menu with support for commands, checkbuttons,
4
4
  radiobuttons, and separators.
5
5
  """
6
6
 
7
+ import weakref
7
8
  from tkinter import BooleanVar, IntVar, Misc, StringVar, TclError, Toplevel, Widget
8
9
  from typing_extensions import Unpack
9
10
  from typing import Any, Callable, Literal
@@ -42,6 +43,23 @@ On Windows and Linux the result is the created widget. On macOS (native
42
43
  # explicitly" (no target — no positioning, no auto-trigger).
43
44
  _TARGET_DEFAULT: Any = object()
44
45
 
46
+ # Themed menus currently shown. Weak, so a new menu torn down with its parent
47
+ # is not pinned here (it's destroy()) is never called.
48
+ _OPEN_MENUS: "weakref.WeakSet[_ToplevelContextMenu]" = weakref.WeakSet()
49
+
50
+
51
+ def hide_open_menus() -> None:
52
+ """Hide every open themed context menu.
53
+
54
+ Call this from a click handler that returns 'break': the break stops the
55
+ click before the window-level outside-click handler can close the menu.
56
+ """
57
+ for menu in list(_OPEN_MENUS):
58
+ try:
59
+ menu.hide()
60
+ except TclError:
61
+ _OPEN_MENUS.discard(menu)
62
+
45
63
 
46
64
  class _CommandItemFrame(CompositeFrame):
47
65
  """Container frame for command items that delegates to the inner button.
@@ -648,6 +666,7 @@ class _ToplevelContextMenu(CustomConfigMixin):
648
666
  self._toplevel.attributes('-topmost', True)
649
667
  self._toplevel.lift()
650
668
  self._toplevel.focus_force()
669
+ _OPEN_MENUS.add(self)
651
670
 
652
671
  # Start with no item highlighted (keyboard nav will highlight on first arrow key)
653
672
  self._highlighted_index = -1
@@ -737,6 +756,7 @@ class _ToplevelContextMenu(CustomConfigMixin):
737
756
 
738
757
  def hide(self) -> None:
739
758
  """Hide the context menu."""
759
+ _OPEN_MENUS.discard(self)
740
760
  # Unbind click handler first
741
761
  self._cancel_click_outside_after()
742
762
  self._unbind_click_outside_handler()
@@ -756,6 +776,7 @@ class _ToplevelContextMenu(CustomConfigMixin):
756
776
 
757
777
  def destroy(self) -> None:
758
778
  """Destroy the context menu and cleanup resources."""
779
+ _OPEN_MENUS.discard(self)
759
780
  # Unbind click handler
760
781
  self._cancel_click_outside_after()
761
782
  self._unbind_click_outside_handler()
@@ -4,6 +4,7 @@ from typing_extensions import Unpack
4
4
 
5
5
  from typing import Any
6
6
 
7
+ from bootstack._core.localization import bind_locale_refresh
7
8
  from bootstack.events import ChangeEvent
8
9
  from bootstack.widgets._core.options import (
9
10
  cluster_records,
@@ -139,7 +140,7 @@ class SelectBox(Field):
139
140
  self._entry._get_validation_value = self._validation_value
140
141
 
141
142
  # Re-translate the displayed text + option maps on a live locale switch.
142
- self.winfo_toplevel().bind('<<LocaleChanged>>', self._on_locale_changed_refresh, add='+')
143
+ bind_locale_refresh(self._on_locale_changed_refresh)
143
144
 
144
145
  self._group_by = group_by
145
146
  self._max_visible_items = max_visible_items
@@ -391,7 +392,48 @@ class SelectBox(Field):
391
392
  text = self.entry_widget.get()
392
393
  if text in self._value_by_text and self._value_by_text[text] != text:
393
394
  return self._value_by_text[text]
394
- return self._entry_live_validation_value()
395
+ return self._decode_custom_text(text, self._entry_live_validation_value)
396
+
397
+ def _numeric_option_kind(self):
398
+ """`int` or `float` when every option value shares that type, else None."""
399
+ # type(), not isinstance: bool never reads as int here, so a bool option
400
+ # list falls out as "not numeric" without a special case.
401
+ kinds = {type(rec.value) for rec in self._records}
402
+ if len(kinds) != 1:
403
+ return None
404
+ kind = kinds.pop()
405
+ return kind if kind in (int, float) else None
406
+
407
+ def _decode_custom_text(self, text, fallback):
408
+ """Decode text that names no option: the options' own type when they share one.
409
+
410
+ A value typed by the user arrives as text with nothing to parse it,
411
+ while one assigned from code arrives already typed. When every option
412
+ value is an `int`, or every one a `float`, numeric text is parsed to
413
+ that type so both routes agree. Anything else — a mixed or empty
414
+ list, `str` or `bool` options, text that is not a number, an entry
415
+ that parses through its own `value_format` — is handed to `fallback`,
416
+ which is what the entry reported before.
417
+
418
+ A whole-number `float` narrows to `int` for `int` options; `'6.5'`
419
+ against `int` options stays `6.5` rather than being truncated.
420
+ """
421
+ if self.entry_widget._value_format is not None:
422
+ return fallback()
423
+ if getattr(self.entry_widget, "_showing_placeholder", False):
424
+ return fallback()
425
+ kind = self._numeric_option_kind()
426
+ if kind is None:
427
+ return fallback()
428
+ try:
429
+ parsed = self.entry_widget._fmt.parse(text, "decimal")
430
+ except ValueError:
431
+ return fallback()
432
+ if parsed is None:
433
+ return fallback()
434
+ if kind is int and not parsed.is_integer():
435
+ return parsed
436
+ return kind(parsed)
395
437
 
396
438
  def _register_retired_value(self, value: Any) -> None:
397
439
  """Make a value that is not in the option list decodable.
@@ -1185,16 +1227,17 @@ class SelectBox(Field):
1185
1227
  """The selected value, or None when the field is empty.
1186
1228
 
1187
1229
  For a decoupled option (its display text differs from its value) this
1188
- returns the option's value. Otherwise it returns the entry's own raw
1189
- value, so `value_format` parsing — e.g. TimeField's `datetime.time` —
1190
- is preserved.
1230
+ returns the option's value. A custom value typed by the user takes the
1231
+ type the options share when that is `int` or `float`. Otherwise it
1232
+ returns the entry's own raw value, so `value_format` parsing — e.g.
1233
+ TimeField's `datetime.time` — is preserved.
1191
1234
  """
1192
1235
  text = self.entry_widget.get()
1193
1236
  if text == "":
1194
1237
  return None
1195
1238
  if text in self._value_by_text and self._value_by_text[text] != text:
1196
1239
  return self._value_by_text[text]
1197
- return Field.value.fget(self)
1240
+ return self._decode_custom_text(text, lambda: Field.value.fget(self))
1198
1241
 
1199
1242
  @value.setter
1200
1243
  def value(self, value):
@@ -30,7 +30,7 @@ from bootstack.data.sqlite_source import SqliteDataSource
30
30
  from bootstack.data.query import col, any_of, all_of
31
31
  from bootstack.widgets._impl.primitives.button import Button
32
32
  from bootstack._runtime.utility import bind_right_click, debug_log_exception
33
- from bootstack.widgets._impl.composites.contextmenu import ContextMenu
33
+ from bootstack.widgets._impl.composites.contextmenu import ContextMenu, hide_open_menus
34
34
  from bootstack.widgets._impl.composites.tooltip import ToolTip
35
35
  from bootstack.widgets._impl.composites.dropdownbutton import DropdownButton
36
36
  from bootstack.widgets._impl.primitives.entry import Entry
@@ -1644,6 +1644,7 @@ class TableView(Frame):
1644
1644
  # ------------------------------------------------------------------ Row context menu
1645
1645
  def _dismiss_context_menus(self) -> None:
1646
1646
  """Hide any open built-in row/header context menu (idempotent)."""
1647
+ hide_open_menus()
1647
1648
  for menu in (self._row_menu, self._header_menu):
1648
1649
  if menu is not None:
1649
1650
  try:
@@ -17,6 +17,7 @@ from bootstack.events import (
17
17
  from bootstack.widgets._impl.composites.pagestack import PageStack
18
18
  from bootstack.widgets.types import Master
19
19
  from bootstack._core import NavigationError
20
+ from bootstack._core.localization import bind_locale_refresh
20
21
 
21
22
 
22
23
  class TabView(Frame):
@@ -121,9 +122,7 @@ class TabView(Frame):
121
122
  self._trace_id = self._tab_variable.trace_add('write', self._on_tab_selected)
122
123
 
123
124
  # Locale retranslation
124
- self.winfo_toplevel().bind(
125
- "<<LocaleChanged>>", self._on_locale_changed, add='+'
126
- )
125
+ bind_locale_refresh(self._on_locale_changed)
127
126
 
128
127
  # Clean up trace on destroy
129
128
  self.bind('<Destroy>', self._on_destroy, add='+')
@@ -524,26 +524,15 @@ class _MultilineCore(tk.Frame):
524
524
 
525
525
  # ── convenience: forward common Text API to self ──────────────────────
526
526
 
527
- def bind(self, sequence=None, func=None, add=None):
528
- return self.text.bind(sequence, func, add)
529
-
530
- def unbind(self, sequence, funcid=None):
531
- return self.text.unbind(sequence, funcid)
532
-
533
527
  def focus_set(self):
534
528
  self.text.focus_set()
535
529
 
536
530
  # ── cleanup ───────────────────────────────────────────────────────────
537
531
 
538
532
  def _on_destroy(self, event: tk.Event) -> None:
539
- # Released for any Destroy in this subtree, not just our own: the event
540
- # this handler receives names the inner Text, never the core, so gating
541
- # the release on `is self` releases nothing and the subscription outlives
542
- # the widget. The rest of the block stays behind the guard -- it has
543
- # never run, and starting it here is not this change's business.
544
- self._unbind_signal()
545
533
  if event.widget is not self:
546
534
  return
535
+ self._unbind_signal()
547
536
  self._chain.destroy()
548
537
  try:
549
538
  for seq in wheel.wheel_sequences(self):
@@ -14,6 +14,7 @@ from tkinter import TclError
14
14
  from typing import Any, Optional
15
15
 
16
16
  from bootstack.widgets._impl.composites.compositeframe import CompositeFrame
17
+ from bootstack.widgets._impl.composites.contextmenu import hide_open_menus
17
18
  from bootstack.widgets._impl.primitives.button import Button
18
19
  from bootstack.widgets._impl.primitives.frame import Frame
19
20
  from bootstack.widgets._impl.primitives.label import Label
@@ -193,6 +194,9 @@ class TreeItem(CompositeFrame):
193
194
  self._emit("<<TreeItemSelect>>", self._node)
194
195
 
195
196
  def _on_ctrl_click(self, event=None) -> str:
197
+ # The 'break' below stops the click before an open menu's outside-click
198
+ # handler sees it, so close the open menus here
199
+ hide_open_menus()
196
200
  if self._node is not None:
197
201
  if self._focusable:
198
202
  self.focus()
@@ -209,6 +213,9 @@ class TreeItem(CompositeFrame):
209
213
  return "break"
210
214
 
211
215
  def _on_right_click(self, event) -> str:
216
+ # The 'break' below stops the click before an open menu's outside-click
217
+ # handler sees it, so close open menus before this row opens its own
218
+ hide_open_menus()
212
219
  if self._node is not None:
213
220
  self._emit(
214
221
  "<<TreeItemRightClick>>",
@@ -18,6 +18,7 @@ from bootstack._core.localization import (
18
18
  resolve_text,
19
19
  resolve_variable_text,
20
20
  apply_spec,
21
+ bind_locale_refresh,
21
22
  create_formatted_signal,
22
23
  )
23
24
  from bootstack.i18n.specs import (
@@ -67,8 +68,7 @@ class LocalizationMixin(Misc):
67
68
  self._localize_mode = localize
68
69
  self._default_value_format = value_format
69
70
 
70
- root = self.winfo_toplevel() # event is generated on root
71
- root.bind("<<LocaleChanged>>", self._on_locale_changed, add="+")
71
+ bind_locale_refresh(self._on_locale_changed)
72
72
 
73
73
  # Check if widget has a textsignal or textvariable with value_format
74
74
  if value_format and self._has_signal_or_variable():