bootstack 0.1.8__tar.gz → 0.2.1__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 (493) hide show
  1. {bootstack-0.1.8/src/bootstack.egg-info → bootstack-0.2.1}/PKG-INFO +1 -1
  2. {bootstack-0.1.8 → bootstack-0.2.1}/pyproject.toml +2 -2
  3. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/base_window.py +17 -2
  4. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/events.py +190 -9
  5. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/shortcuts.py +10 -2
  6. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/utility.py +22 -0
  7. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/colorchooser.py +19 -23
  8. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/datedialog.py +15 -27
  9. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/dialog.py +52 -0
  10. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/message.py +18 -22
  11. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/query.py +15 -22
  12. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/errors.py +12 -0
  13. bootstack-0.2.1/src/bootstack/events/_subscription.py +60 -0
  14. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/base.py +65 -15
  15. bootstack-0.2.1/src/bootstack/widgets/_core/choices.py +56 -0
  16. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/container.py +57 -0
  17. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/field_mixin.py +7 -0
  18. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_parts/numberentry_part.py +7 -2
  19. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_parts/spinnerentry_part.py +5 -3
  20. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_parts/textentry_part.py +9 -3
  21. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/dateentry.py +30 -7
  22. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/field.py +7 -1
  23. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/form.py +16 -4
  24. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/layout.py +33 -28
  25. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/shell.py +8 -4
  26. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/flexframe.py +74 -15
  27. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/appshell.py +12 -9
  28. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/calendar.py +2 -0
  29. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/card.py +4 -11
  30. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/codeeditor.py +9 -33
  31. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/datatable.py +4 -0
  32. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/datefield.py +9 -34
  33. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/expander.py +7 -21
  34. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/form.py +5 -1
  35. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/gallery.py +2 -0
  36. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/groupbox.py +4 -11
  37. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/listview.py +2 -0
  38. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/numberfield.py +7 -33
  39. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/pagestack.py +4 -11
  40. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/passwordfield.py +7 -33
  41. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/pathfield.py +9 -33
  42. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/scrollview.py +3 -0
  43. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/select.py +15 -34
  44. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/sidebar_toggle.py +2 -0
  45. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/spinnerfield.py +7 -33
  46. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/splitview.py +4 -11
  47. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/stacks.py +16 -7
  48. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/tabs.py +6 -32
  49. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/textarea.py +9 -33
  50. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/textfield.py +7 -33
  51. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/timefield.py +7 -34
  52. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/togglegroup.py +2 -0
  53. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/tree.py +2 -0
  54. {bootstack-0.1.8 → bootstack-0.2.1/src/bootstack.egg-info}/PKG-INFO +1 -1
  55. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack.egg-info/SOURCES.txt +1 -0
  56. {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_public_surface.py +1 -1
  57. bootstack-0.1.8/src/bootstack/events/_subscription.py +0 -38
  58. {bootstack-0.1.8 → bootstack-0.2.1}/LICENSE +0 -0
  59. {bootstack-0.1.8 → bootstack-0.2.1}/MANIFEST.in +0 -0
  60. {bootstack-0.1.8 → bootstack-0.2.1}/NOTICE +0 -0
  61. {bootstack-0.1.8 → bootstack-0.2.1}/README.md +0 -0
  62. {bootstack-0.1.8 → bootstack-0.2.1}/setup.cfg +0 -0
  63. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/__init__.py +0 -0
  64. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/__main__.py +0 -0
  65. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/__init__.py +0 -0
  66. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/__init__.py +0 -0
  67. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/after.py +0 -0
  68. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/bind.py +0 -0
  69. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/bindtags.py +0 -0
  70. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/busy.py +0 -0
  71. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/clipboard.py +0 -0
  72. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/focus.py +0 -0
  73. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/grab.py +0 -0
  74. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/grid.py +0 -0
  75. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/localization.py +0 -0
  76. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/pack.py +0 -0
  77. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/place.py +0 -0
  78. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/selection.py +0 -0
  79. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/signals.py +0 -0
  80. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/winfo.py +0 -0
  81. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/colorutils.py +0 -0
  82. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/exceptions.py +0 -0
  83. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/images.py +0 -0
  84. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/mixins/__init__.py +0 -0
  85. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/mixins/ttk_state.py +0 -0
  86. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/mixins/widget.py +0 -0
  87. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/paths.py +0 -0
  88. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/publisher.py +0 -0
  89. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/variables.py +0 -0
  90. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/__init__.py +0 -0
  91. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/app.py +0 -0
  92. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/tk_patch.py +0 -0
  93. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/toplevel.py +0 -0
  94. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/visual_focus.py +0 -0
  95. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/wheel.py +0 -0
  96. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/window_utilities.py +0 -0
  97. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/__init__.py +0 -0
  98. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/bootstack.ico +0 -0
  99. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/bootstack.png +0 -0
  100. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/__init__.py +0 -0
  101. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/badge-pill.png +0 -0
  102. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/badge-square.png +0 -0
  103. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/button-compact.png +0 -0
  104. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/button-default.png +0 -0
  105. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
  106. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
  107. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
  108. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
  109. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
  110. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
  111. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
  112. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
  113. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
  114. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
  115. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
  116. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
  117. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/card.png +0 -0
  118. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/checkbox-checked.png +0 -0
  119. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  120. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/checkbox-unchecked.png +0 -0
  121. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/field.png +0 -0
  122. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/input-addon-compact.png +0 -0
  123. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/input-addon-default.png +0 -0
  124. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/input-compact.png +0 -0
  125. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/input-default.png +0 -0
  126. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/list-item-separated.png +0 -0
  127. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/list-item.png +0 -0
  128. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/listrow-compact.png +0 -0
  129. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/listrow-default.png +0 -0
  130. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/manifest.toml +0 -0
  131. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/menu-item.png +0 -0
  132. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/navitem-compact.png +0 -0
  133. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/navitem-default.png +0 -0
  134. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/progressbar-h-compact.png +0 -0
  135. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/progressbar-h-default.png +0 -0
  136. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/progressbar-v-compact.png +0 -0
  137. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/progressbar-v-default.png +0 -0
  138. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/radiobutton.png +0 -0
  139. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  140. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/scrollbar-vertical.png +0 -0
  141. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/slider-handle.png +0 -0
  142. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/slider-track-h.png +0 -0
  143. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/slider-track-v.png +0 -0
  144. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/switch-off.png +0 -0
  145. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/switch-on.png +0 -0
  146. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/tab-h.png +0 -0
  147. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/tab-v.png +0 -0
  148. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/icons/bootstrap.ttf +0 -0
  149. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/icons/glyphmap.json +0 -0
  150. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/icons/icon_metrics.json +0 -0
  151. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  152. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +0 -0
  153. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
  154. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +0 -0
  155. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  156. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +0 -0
  157. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  158. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +0 -0
  159. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  160. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +0 -0
  161. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  162. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +0 -0
  163. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  164. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +0 -0
  165. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  166. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +0 -0
  167. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  168. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +0 -0
  169. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  170. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +0 -0
  171. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  172. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +0 -0
  173. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  174. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +0 -0
  175. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  176. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +0 -0
  177. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  178. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +0 -0
  179. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  180. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +0 -0
  181. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  182. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +0 -0
  183. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  184. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +0 -0
  185. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  186. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +0 -0
  187. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  188. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +0 -0
  189. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  190. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +0 -0
  191. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  192. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +0 -0
  193. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  194. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +0 -0
  195. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  196. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +0 -0
  197. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/__init__.py +0 -0
  198. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/__main__.py +0 -0
  199. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/add.py +0 -0
  200. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/appicon.py +0 -0
  201. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/build.py +0 -0
  202. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/config.py +0 -0
  203. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/demo.py +0 -0
  204. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/dev.py +0 -0
  205. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/doctor.py +0 -0
  206. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/icons.py +0 -0
  207. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/promote.py +0 -0
  208. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/pyinstaller.py +0 -0
  209. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/run.py +0 -0
  210. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/start.py +0 -0
  211. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/templates/__init__.py +0 -0
  212. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/clipboard.py +0 -0
  213. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/constants.py +0 -0
  214. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/README.md +0 -0
  215. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/__init__.py +0 -0
  216. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/_observable.py +0 -0
  217. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/base.py +0 -0
  218. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/file_source.py +0 -0
  219. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/memory_source.py +0 -0
  220. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/query.py +0 -0
  221. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/readers.py +0 -0
  222. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/sqlite_source.py +0 -0
  223. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/types.py +0 -0
  224. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/writers.py +0 -0
  225. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/__init__.py +0 -0
  226. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/_capture.py +0 -0
  227. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/_env.py +0 -0
  228. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/_registry.py +0 -0
  229. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/_reloader.py +0 -0
  230. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/_reset.py +0 -0
  231. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/_watcher.py +0 -0
  232. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/__init__.py +0 -0
  233. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/__init__.py +0 -0
  234. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/filterdialog.py +0 -0
  235. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/fontdialog.py +0 -0
  236. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/formdialog.py +0 -0
  237. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/events/__init__.py +0 -0
  238. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/events/_event.py +0 -0
  239. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/events/_payloads.py +0 -0
  240. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/i18n/README.md +0 -0
  241. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/i18n/__init__.py +0 -0
  242. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/i18n/catalog.py +0 -0
  243. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/i18n/intl_format.py +0 -0
  244. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/i18n/msgcat.py +0 -0
  245. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/i18n/specs.py +0 -0
  246. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/images.py +0 -0
  247. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/py.typed +0 -0
  248. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/scheduling/__init__.py +0 -0
  249. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/scheduling/_schedule.py +0 -0
  250. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/shortcuts.py +0 -0
  251. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/signals/README.md +0 -0
  252. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/signals/__init__.py +0 -0
  253. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/signals/integration.py +0 -0
  254. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/signals/signal.py +0 -0
  255. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/signals/types.py +0 -0
  256. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/store.py +0 -0
  257. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/streams/__init__.py +0 -0
  258. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/streams/_stream.py +0 -0
  259. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/__init__.py +0 -0
  260. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/__init__.py +0 -0
  261. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/badge.py +0 -0
  262. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/button.py +0 -0
  263. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/buttongroup.py +0 -0
  264. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/calendar.py +0 -0
  265. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/checkbutton.py +0 -0
  266. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/combobox.py +0 -0
  267. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/contextmenu.py +0 -0
  268. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/entry.py +0 -0
  269. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/expander.py +0 -0
  270. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/field.py +0 -0
  271. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/frame.py +0 -0
  272. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/label.py +0 -0
  273. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/labelframe.py +0 -0
  274. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/listview.py +0 -0
  275. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/menubar.py +0 -0
  276. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/menubutton.py +0 -0
  277. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/panedwindow.py +0 -0
  278. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/progressbar.py +0 -0
  279. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/radiobutton.py +0 -0
  280. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/scale.py +0 -0
  281. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/scrollbar.py +0 -0
  282. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/separator.py +0 -0
  283. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/sidenav.py +0 -0
  284. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/sizegrip.py +0 -0
  285. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/spinbox.py +0 -0
  286. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/switch.py +0 -0
  287. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/tabitem.py +0 -0
  288. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/togglegroup.py +0 -0
  289. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/toolbutton.py +0 -0
  290. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/tooltip.py +0 -0
  291. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/treeview.py +0 -0
  292. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/utils.py +0 -0
  293. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders_tk/__init__.py +0 -0
  294. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders_tk/defaults.py +0 -0
  295. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/element.py +0 -0
  296. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/fonts.py +0 -0
  297. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/style.py +0 -0
  298. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/style_builder_base.py +0 -0
  299. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/style_builder_mixed.py +0 -0
  300. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/style_builder_tk.py +0 -0
  301. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/style_builder_ttk.py +0 -0
  302. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/style_resolver.py +0 -0
  303. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/theme.py +0 -0
  304. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/theme_provider.py +0 -0
  305. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/themes/__init__.py +0 -0
  306. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/tk_patch.py +0 -0
  307. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/token_maps.py +0 -0
  308. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/types.py +0 -0
  309. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/typography.py +0 -0
  310. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/utility.py +0 -0
  311. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/types.py +0 -0
  312. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/validation/__init__.py +0 -0
  313. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/validation/types.py +0 -0
  314. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/validation/validation_result.py +0 -0
  315. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/validation/validation_rules.py +0 -0
  316. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/__init__.py +0 -0
  317. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/__init__.py +0 -0
  318. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/app_config.py +0 -0
  319. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/context.py +0 -0
  320. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/events.py +0 -0
  321. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/icon_image_props.py +0 -0
  322. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/image_binding.py +0 -0
  323. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/kwargs.py +0 -0
  324. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/navmodel.py +0 -0
  325. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/options.py +0 -0
  326. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/selection_group.py +0 -0
  327. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/window_controls.py +0 -0
  328. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/window_menu.py +0 -0
  329. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/__init__.py +0 -0
  330. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_internal/__init__.py +0 -0
  331. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_internal/wrapper_base.py +0 -0
  332. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_parts/__init__.py +0 -0
  333. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/__init__.py +0 -0
  334. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/_dateutils.py +0 -0
  335. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/_image_fit.py +0 -0
  336. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/accordion.py +0 -0
  337. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/avatar.py +0 -0
  338. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/buttongroup.py +0 -0
  339. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/calendar.py +0 -0
  340. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/carousel.py +0 -0
  341. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/chart.py +0 -0
  342. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/compositeframe.py +0 -0
  343. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/contextmenu.py +0 -0
  344. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/dropdownbutton.py +0 -0
  345. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/expander.py +0 -0
  346. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/gallery.py +0 -0
  347. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/list/__init__.py +0 -0
  348. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/list/listitem.py +0 -0
  349. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/list/listview.py +0 -0
  350. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/menu/__init__.py +0 -0
  351. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/menu/model.py +0 -0
  352. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/menu/render_native.py +0 -0
  353. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/menu/render_themed.py +0 -0
  354. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/meter.py +0 -0
  355. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/numericentry.py +0 -0
  356. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/pagestack.py +0 -0
  357. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/passwordentry.py +0 -0
  358. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/pathentry.py +0 -0
  359. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/picture.py +0 -0
  360. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/radiogroup.py +0 -0
  361. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/scrolledtext.py +0 -0
  362. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/scrolledtext.pyi +0 -0
  363. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/scrollview.py +0 -0
  364. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/selectbox.py +0 -0
  365. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/__init__.py +0 -0
  366. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/content_host.py +0 -0
  367. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/nav_panel.py +0 -0
  368. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/providers.py +0 -0
  369. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/rail.py +0 -0
  370. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/workspace.py +0 -0
  371. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/sidenav/__init__.py +0 -0
  372. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/sidenav/header.py +0 -0
  373. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/sidenav/separator.py +0 -0
  374. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/slider/__init__.py +0 -0
  375. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/slider/_shared.py +0 -0
  376. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/slider/rangeslider.py +0 -0
  377. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/slider/slider.py +0 -0
  378. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/spinnerentry.py +0 -0
  379. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tableview/__init__.py +0 -0
  380. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tableview/tableview.py +0 -0
  381. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tableview/types.py +0 -0
  382. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tabs/__init__.py +0 -0
  383. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tabs/tabitem.py +0 -0
  384. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tabs/tabs.py +0 -0
  385. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tabs/tabview.py +0 -0
  386. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/__init__.py +0 -0
  387. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/change.py +0 -0
  388. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/codeeditor.py +0 -0
  389. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/core.py +0 -0
  390. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/decoration.py +0 -0
  391. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/diff.py +0 -0
  392. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +0 -0
  393. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +0 -0
  394. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +0 -0
  395. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +0 -0
  396. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +0 -0
  397. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +0 -0
  398. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/filter.py +0 -0
  399. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/search_overlay.py +0 -0
  400. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/sidebar.py +0 -0
  401. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/style_registry.py +0 -0
  402. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/textarea.py +0 -0
  403. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/undo.py +0 -0
  404. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textentry.py +0 -0
  405. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/timeentry.py +0 -0
  406. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/toast.py +0 -0
  407. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/toast_stack.py +0 -0
  408. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/togglegroup.py +0 -0
  409. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/toolbar.py +0 -0
  410. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tooltip.py +0 -0
  411. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/__init__.py +0 -0
  412. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/source_binding.py +0 -0
  413. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/treeitem.py +0 -0
  414. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/treenode.py +0 -0
  415. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/treeview.py +0 -0
  416. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/__init__.py +0 -0
  417. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/configure_mixin.py +0 -0
  418. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/entry_mixin.py +0 -0
  419. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/font_mixin.py +0 -0
  420. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/icon_mixin.py +0 -0
  421. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/localization_mixin.py +0 -0
  422. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/signal_mixin.py +0 -0
  423. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/validation_mixin.py +0 -0
  424. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/__init__.py +0 -0
  425. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/_menubutton.py +0 -0
  426. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/badge.py +0 -0
  427. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/button.py +0 -0
  428. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/card.py +0 -0
  429. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/checkbutton.py +0 -0
  430. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/checktoggle.py +0 -0
  431. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/combobox.py +0 -0
  432. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/entry.py +0 -0
  433. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/frame.py +0 -0
  434. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/gridframe.py +0 -0
  435. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/label.py +0 -0
  436. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/labelframe.py +0 -0
  437. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/optionmenu.py +0 -0
  438. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/packframe.py +0 -0
  439. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/panedwindow.py +0 -0
  440. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/progressbar.py +0 -0
  441. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/radiobutton.py +0 -0
  442. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/radiotoggle.py +0 -0
  443. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/scrollbar.py +0 -0
  444. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/separator.py +0 -0
  445. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/sizegrip.py +0 -0
  446. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/spinbox.py +0 -0
  447. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/switch.py +0 -0
  448. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/treeview.py +0 -0
  449. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/app.py +0 -0
  450. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/avatar.py +0 -0
  451. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/boolean_controls.py +0 -0
  452. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/button.py +0 -0
  453. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/buttongroup.py +0 -0
  454. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/carousel.py +0 -0
  455. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/chart.py +0 -0
  456. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/contextmenu.py +0 -0
  457. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/divider.py +0 -0
  458. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/gauge.py +0 -0
  459. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/grid.py +0 -0
  460. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/label.py +0 -0
  461. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/menubutton.py +0 -0
  462. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/picture.py +0 -0
  463. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/progressbar.py +0 -0
  464. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/radio_variants.py +0 -0
  465. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/radiogroup.py +0 -0
  466. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/scrollbar.py +0 -0
  467. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/selectbutton.py +0 -0
  468. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/sizegrip.py +0 -0
  469. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/slider.py +0 -0
  470. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/spinbox.py +0 -0
  471. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/splash.py +0 -0
  472. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/statusbar.py +0 -0
  473. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/theme_toggle.py +0 -0
  474. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/toast.py +0 -0
  475. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/toolbar.py +0 -0
  476. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/tooltip.py +0 -0
  477. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/types.py +0 -0
  478. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/window.py +0 -0
  479. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack.egg-info/dependency_links.txt +0 -0
  480. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack.egg-info/entry_points.txt +0 -0
  481. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack.egg-info/requires.txt +0 -0
  482. {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack.egg-info/top_level.txt +0 -0
  483. {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_dialog_anchor.py +0 -0
  484. {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_events_doc_coverage.py +0 -0
  485. {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_hot_reload.py +0 -0
  486. {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_images.py +0 -0
  487. {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_images_gui.py +0 -0
  488. {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_intl_format.py +0 -0
  489. {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_navmodel.py +0 -0
  490. {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_on_color_contrast.py +0 -0
  491. {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_store.py +0 -0
  492. {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_theme_from_existing.py +0 -0
  493. {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_validation_rules.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bootstack
3
- Version: 0.1.8
3
+ Version: 0.2.1
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.1.8"
7
+ version = "0.2.1"
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.1.8"
117
+ current_version = "0.2.1"
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}",
@@ -34,14 +34,29 @@ def on_visibility_alpha(event: tkinter.Event) -> None:
34
34
  X11 requires alpha to be set after the window is visible, so we bind
35
35
  to the <Visibility> event and set alpha then, unbinding after first use.
36
36
 
37
+ Being woken by this event at all is what proves the precondition: X11
38
+ reports visibility only for a window that is already on screen, so the very
39
+ first delivery is a valid moment to apply alpha. Whether that first report
40
+ says obscured or unobscured makes no difference — opacity is a property of
41
+ the window, not of how much of it happens to be uncovered.
42
+
43
+ Alpha is therefore applied before the binding is released, so first use is
44
+ genuinely the last one needed. Applying first also means that if the request
45
+ itself fails, the binding is still in place and the next visibility change
46
+ tries again — the one failure where a retry could help (#398).
47
+
37
48
  Args:
38
49
  event: The visibility event containing the widget.
39
50
  """
40
51
  widget = event.widget
41
52
  if hasattr(widget, 'alpha') and hasattr(widget, 'alpha_bind'):
42
- if widget.alpha_bind:
43
- widget.unbind(widget.alpha_bind)
44
53
  widget.attributes("-alpha", widget.alpha)
54
+ if widget.alpha_bind:
55
+ # `alpha_bind` is the id `bind` returned, so it is the *funcid*
56
+ # argument — passing it as the sequence removes nothing at all and
57
+ # raises nothing either, which is how this stayed unnoticed (#398).
58
+ widget.unbind("<Visibility>", widget.alpha_bind)
59
+ widget.alpha_bind = None
45
60
 
46
61
 
47
62
  class BaseWindow:
@@ -32,6 +32,7 @@ Note:
32
32
  applied globally and affect all tkinter widgets in the application.
33
33
  """
34
34
 
35
+ import itertools
35
36
  import tkinter as tk
36
37
  import uuid
37
38
  import weakref
@@ -43,9 +44,35 @@ from typing import Any, Callable, Optional, Union
43
44
  _event_data_cache: OrderedDict[str, Any] = OrderedDict()
44
45
  _MAX_CACHE_SIZE = 100 # Prevent unbounded growth
45
46
 
47
+ # Serial number used to give every registered handler a unique name. See
48
+ # `_unique_name` for why the stock naming scheme is not safe here.
49
+ _binding_seq = itertools.count()
50
+
51
+
52
+ def _unique_name(prefix: str) -> str:
53
+ """Return a handler name that can never collide with a previous one.
54
+
55
+ Tkinter names the Tcl command behind a binding `repr(id(f)) + f.__name__`,
56
+ where `f` is a bound method created for that registration alone. Nothing
57
+ else refers to it, so releasing the command frees the method and CPython is
58
+ free to hand the very same address to the next one — measured at 498 out of
59
+ 499 consecutive bind/cancel/bind cycles. Every wrapper registered here has
60
+ the same `__name__`, so the whole name would repeat with it.
61
+
62
+ That is harmless while removals delete the command immediately, but
63
+ `_patched_unbind` defers the deletion to the next idle point. A repeated
64
+ name means a binding created in the meantime inherits the name a deletion is
65
+ already pending on, and that deletion then takes out a live handler — the
66
+ #392 failure again, on a new trigger. A serial number in the name keeps the
67
+ two apart no matter what the allocator does.
68
+ """
69
+ return '%s%d' % (prefix, next(_binding_seq))
70
+
71
+
46
72
  # Store original tkinter methods before patching
47
73
  _original_event_generate: Callable = tk.Misc.event_generate
48
74
  _original_bind: Callable = tk.Misc.bind
75
+ _original_unbind: Callable = tk.Misc.unbind
49
76
 
50
77
 
51
78
  def _patched_event_generate(
@@ -219,23 +246,60 @@ def _patched_bind(
219
246
  # Attach custom data (don't pop yet - multiple handlers may need it)
220
247
  if data_guid and data_guid in _event_data_cache:
221
248
  event.data = _event_data_cache[data_guid]
222
- # Schedule cleanup after all handlers have run
223
- widget.after_idle(lambda: _event_data_cache.pop(data_guid, None))
249
+ # Schedule cleanup after all handlers have run. On the root,
250
+ # not on `widget`: deferred work owned by a widget is torn
251
+ # down with it, so a handler that destroys its own widget
252
+ # would leave this callback pointing at a command destroy()
253
+ # had already swept — a silent background error. The root
254
+ # outlives every widget. (Same rule as `_patched_unbind`.)
255
+ try:
256
+ widget._root().after_idle(
257
+ lambda: _event_data_cache.pop(data_guid, None)
258
+ )
259
+ except (tk.TclError, AttributeError):
260
+ # No event loop left to defer onto; the LRU cap reclaims
261
+ # the entry instead.
262
+ pass
224
263
  else:
225
264
  # Data might have been evicted from cache already
226
265
  event.data = None
227
266
 
228
267
  return func(event)
229
268
 
230
- # Register wrapper function with tkinter
269
+ # Register wrapper function with tkinter. The name tkinter builds
270
+ # ends with the function's `__name__`, so making that unique is
271
+ # enough to keep a deferred deletion off a later binding.
272
+ wrapper.__name__ = _unique_name('bsvirtual')
231
273
  name = self._register(wrapper)
232
274
 
233
- # Build command with all substitutions including %d for data GUID
275
+ # Build the binding script. Its exact shape is load-bearing, and
276
+ # this must stay byte-for-byte what stock `Misc._bind` emits:
277
+ #
278
+ # * Removing a single handler works by matching the line prefix
279
+ # `if {"[<funcid> ` — note the TRAILING SPACE, which means the
280
+ # substitution list below must never be empty. A script in any
281
+ # other shape is invisible to the matcher, so the handler
282
+ # survives while its command is deleted, leaving an orphan that
283
+ # aborts every handler after it. All handlers for an event share
284
+ # ONE concatenated script, so that took out every handler
285
+ # registered after the cancelled one (#392).
286
+ # * The `if {...} == "break"` test is what lets a handler return
287
+ # 'break' to stop the remaining handlers for that event.
288
+ #
289
+ # (The trailing newline only separates this line from the next
290
+ # handler's; Tk inserts its own separator when appending a `+`
291
+ # script. It is kept to match stock exactly.)
292
+ #
293
+ # The substitution list carries %d (our data token) ahead of the
294
+ # standard values; its order must match `wrapper`'s parameters.
234
295
  # Substitution codes: https://tcl.tk/man/tcl8.6/TkCmd/bind.htm
235
- cmd = f'{name} %d %# %b %h %w %k %s %t %x %y %X %Y %A %K %N %T %E %D'
296
+ subst = '%d %# %b %h %w %k %s %t %x %y %X %Y %A %K %N %T %E %D'
297
+ cmd = '%sif {"[%s %s]" == "break"} break\n' % (
298
+ '+' if add else '', name, subst
299
+ )
236
300
 
237
301
  # Bind to widget
238
- self.tk.call('bind', self._w, sequence, cmd if not add else '+' + cmd)
302
+ self.tk.call('bind', self._w, sequence, cmd)
239
303
 
240
304
  return name
241
305
  else:
@@ -246,12 +310,126 @@ def _patched_bind(
246
310
  event.data = None
247
311
  return func(event)
248
312
 
313
+ # Real events are removed by the same patched `unbind`, so they need
314
+ # the same protection from a recycled name. See `_unique_name`.
315
+ regular_wrapper.__name__ = _unique_name('bsregular')
249
316
  return _original_bind(self, sequence, regular_wrapper, add)
250
317
 
251
318
  # No function or sequence provided, pass through to original
252
319
  return _original_bind(self, sequence, func, add)
253
320
 
254
321
 
322
+ def _patched_unbind(
323
+ self: tk.Misc,
324
+ sequence: str,
325
+ funcid: Optional[str] = None
326
+ ) -> None:
327
+ """Enhanced unbind that cannot disturb the other handlers for an event.
328
+
329
+ Stock tkinter removes one handler in two steps: rewrite the binding script
330
+ without that handler's line, then delete the Tcl command behind it. The
331
+ rewrite is correct, but the deletion is immediate — and all handlers for an
332
+ event share ONE script, so if the removal happens *while that script is
333
+ running* (a handler cancelling another handler's subscription), execution
334
+ later reaches the deleted command and the whole script aborts. Every handler
335
+ after the cancelled one is skipped for that dispatch, and the error surfaces
336
+ nowhere Python can see it.
337
+
338
+ This version neutralizes the command in place instead, then deletes it once
339
+ the current dispatch has finished. An already-running script then evaluates
340
+ a harmless no-op where the cancelled handler used to be and carries on to
341
+ the handlers after it.
342
+
343
+ Args:
344
+ self: The widget instance (automatically provided)
345
+ sequence: Event sequence the handler was bound to
346
+ funcid: Identifier returned by `bind`. When omitted, every handler for
347
+ the sequence is removed and the original implementation is used.
348
+ """
349
+ if funcid is None:
350
+ # Removing everything replaces the whole script, so there is no
351
+ # surviving line that could reference a deleted command.
352
+ return _original_unbind(self, sequence)
353
+
354
+ what = ('bind', self._w, sequence)
355
+ try:
356
+ script = self.tk.call(what)
357
+ except tk.TclError:
358
+ # Reading the script is the one step whose failure is expected: the
359
+ # widget is gone, and its bindings and commands went with it. Nothing
360
+ # to remove and nothing to leak.
361
+ return
362
+
363
+ lines = str(script).split('\n')
364
+ # Must match what `_patched_bind` emits (and stock `Misc._bind`).
365
+ prefix = 'if {"[%s ' % funcid
366
+ keep = [line for line in lines if not line.startswith(prefix)]
367
+ if len(keep) == len(lines):
368
+ # This funcid is not bound to this widget and sequence, so it is
369
+ # not ours to delete — the live binding may be somewhere else, and
370
+ # deleting its command would orphan it. Leave everything alone.
371
+ #
372
+ # Correct, but it means the command and its closure survive for the
373
+ # life of the interpreter, invisibly. Surface it in development so the
374
+ # drift that produced it can be found (#399).
375
+ #
376
+ # Reported, not warned. This runs inside event dispatch and on the
377
+ # teardown path behind `Subscription.cancel()` and its `__exit__`, both
378
+ # documented as safe to call on a dead or already-removed handler. A
379
+ # `warnings.warn` here becomes an exception out of both under
380
+ # `-W error`, which is a worse outcome than the leak it describes.
381
+ from bootstack._runtime.utility import debug_log
382
+ debug_log(
383
+ f"unbind({sequence!r}, {funcid!r}) on {self._w!r} matched no "
384
+ "binding, so its Tcl command cannot safely be released and will "
385
+ "leak. The usual cause is an unbind that targeted a different "
386
+ "widget than the bind did; hold the Subscription that bind() "
387
+ "returned instead of re-deriving the target."
388
+ )
389
+ return
390
+
391
+ remaining = '\n'.join(keep)
392
+ # Deliberately NOT guarded: if the rewrite fails the handler is still bound
393
+ # and still firing, and swallowing that would report a successful
394
+ # cancellation for a live handler — a silent wrong answer (#400).
395
+ self.tk.call(*what, remaining if remaining.strip() else '')
396
+
397
+ try:
398
+ # Replace the command rather than deleting it, so an in-flight copy of
399
+ # the concatenated script has something harmless to call.
400
+ self.tk.createcommand(funcid, lambda *args: '')
401
+ except tk.TclError:
402
+ # The binding line is already gone, so the handler is genuinely removed
403
+ # and an in-flight script would find the original command rather than a
404
+ # deleted one. Fall through to the deferred delete instead of returning,
405
+ # which would strand the command forever (#400).
406
+ pass
407
+
408
+ def _delete_command() -> None:
409
+ try:
410
+ self.deletecommand(funcid)
411
+ except (tk.TclError, AttributeError):
412
+ # Already swept, most likely because the widget was destroyed
413
+ # before the interpreter went idle. `AttributeError` is that same
414
+ # case seen from the other side: destroy() clears the widget's
415
+ # command bookkeeping, which `deletecommand` then cannot update.
416
+ pass
417
+
418
+ try:
419
+ # Deliberately scheduled on the root, not on `self`. Deferred work owned
420
+ # by a widget is torn down with it, and destroying a widget between the
421
+ # cancellation and the next idle point would leave the pending callback
422
+ # referring to a command that destroy() had already deleted — a silent
423
+ # error of exactly the kind this function exists to avoid. The root
424
+ # outlives every widget, and when the root itself goes the pending
425
+ # callback goes with it, which is equally fine: there is nothing left to
426
+ # clean up.
427
+ self._root().after_idle(_delete_command)
428
+ except (tk.TclError, AttributeError):
429
+ # No event loop left to defer onto; nothing can be mid-dispatch either.
430
+ _delete_command()
431
+
432
+
255
433
  def cleanup_event_cache() -> None:
256
434
  """Manually clear the entire event data cache.
257
435
 
@@ -342,9 +520,10 @@ def get_cache_size() -> int:
342
520
  def install_enhanced_events() -> None:
343
521
  """Install the enhanced event system by patching tkinter methods.
344
522
 
345
- This function applies monkey patches to tkinter's Misc class,
346
- replacing event_generate and bind with enhanced versions that
347
- support passing custom data through virtual events.
523
+ This function applies monkey patches to tkinter's Misc class, replacing
524
+ event_generate and bind with enhanced versions that support passing custom
525
+ data through virtual events, and unbind with a version that cannot disturb
526
+ the other handlers for an event while one is being removed.
348
527
 
349
528
  The patches are applied globally and affect all tkinter widgets
350
529
  in the application. This function is called automatically when
@@ -364,6 +543,7 @@ def install_enhanced_events() -> None:
364
543
  """
365
544
  tk.Misc.event_generate = _patched_event_generate
366
545
  tk.Misc.bind = _patched_bind
546
+ tk.Misc.unbind = _patched_unbind
367
547
 
368
548
 
369
549
  def uninstall_enhanced_events() -> None:
@@ -387,6 +567,7 @@ def uninstall_enhanced_events() -> None:
387
567
  """
388
568
  tk.Misc.event_generate = _original_event_generate
389
569
  tk.Misc.bind = _original_bind
570
+ tk.Misc.unbind = _original_unbind
390
571
  cleanup_event_cache()
391
572
 
392
573
 
@@ -57,13 +57,21 @@ _WIN_NAMES = {
57
57
  }
58
58
 
59
59
  # Tkinter binding modifiers
60
+ #
61
+ # `command` and `option` are platform-mapped for the same reason `mod` and `alt`
62
+ # are. Off macOS the toolkit resolves the words "Command" and "Option" to its
63
+ # generic Mod1 and Mod2 slots, and Windows reports NumLock as Mod1 — so a
64
+ # `Command+S` binding fired on a bare `s` for anyone with NumLock switched on,
65
+ # while the menu beside it read "Ctrl+S". Naming the modifier the display has
66
+ # always claimed keeps the two from disagreeing (#405, and the same trap as
67
+ # #403).
60
68
  _BINDING_MODIFIERS = {
61
69
  'mod': 'Command' if IS_MAC else 'Control',
62
- 'command': 'Command',
70
+ 'command': 'Command' if IS_MAC else 'Control',
63
71
  'ctrl': 'Control',
64
72
  'control': 'Control',
65
73
  'alt': 'Option' if IS_MAC else 'Alt',
66
- 'option': 'Option',
74
+ 'option': 'Option' if IS_MAC else 'Alt',
67
75
  'shift': 'Shift',
68
76
  }
69
77
 
@@ -344,6 +344,28 @@ def _debug_enabled() -> bool:
344
344
  return str(os.environ.get("BOOTSTACK_DEBUG", "")).lower() in {"1", "true", "yes"}
345
345
 
346
346
 
347
+ def debug_log(message: str) -> None:
348
+ """Print a diagnostic message if debug is enabled.
349
+
350
+ For reporting a condition that has no exception behind it. Use
351
+ `debug_log_exception` when there is one.
352
+
353
+ Never raises, and deliberately does not use `warnings.warn`: these fire from
354
+ inside event dispatch and teardown, where a warning filter set to `error`
355
+ would turn a diagnostic into a failure on a path documented as safe.
356
+
357
+ Args:
358
+ message: The text to print.
359
+ """
360
+ if not _debug_enabled():
361
+ return
362
+ try:
363
+ print(f"bootstack DEBUG: {message}")
364
+ except Exception:
365
+ # Never raise from debug logging
366
+ pass
367
+
368
+
347
369
  def debug_log_exception(message: str = "") -> None:
348
370
  """Print the current exception traceback if debug is enabled.
349
371
 
@@ -15,7 +15,10 @@ from PIL import ImageColor
15
15
  from bootstack.constants import *
16
16
  from bootstack._core import colorutils
17
17
  from bootstack._core.colorutils import HEX, HSL, HUE, LUM, RGB, SAT
18
- from bootstack.dialogs._impl.dialog import Dialog
18
+ from bootstack.events import Subscription
19
+ from bootstack.dialogs._impl.dialog import (
20
+ DIALOG_RESULT, Dialog, emit_dialog_result, result_target,
21
+ )
19
22
  from bootstack.i18n import MessageCatalog
20
23
  from bootstack._runtime import utility
21
24
  from bootstack.style.style import get_style, get_theme_color
@@ -554,35 +557,28 @@ class ColorChooserDialog:
554
557
  if not self._emitted_result:
555
558
  self._emit_result(confirmed=self.result is not None)
556
559
 
557
- def on_dialog_result(self, callback: Callable[[Any], None]) -> Optional[str]:
558
- """Bind a callback fired when the dialog produces a result."""
559
- target = self._master or self._dialog.toplevel
560
+ def on_dialog_result(self, callback: Callable[[Any], None]) -> "Optional[Subscription]":
561
+ """Bind a callback fired when the dialog produces a result.
562
+
563
+ Returns:
564
+ A `Subscription`, or `None` if there is no widget to bind to.
565
+ Cancel it with `.cancel()`.
566
+ """
567
+ target = result_target(self._dialog, self._master)
560
568
  if target is None:
561
569
  return None
562
570
 
563
571
  def handler(event):
564
572
  callback(getattr(event, "data", None))
565
573
 
566
- return target.bind("<<DialogResult>>", handler, add="+")
567
-
568
- def off_dialog_result(self, funcid: str) -> None:
569
- """Unbind a previously bound dialog result callback."""
570
- target = self._master or self._dialog.toplevel
571
- if target is None:
572
- return
573
- target.unbind("<<DialogResult>>", funcid)
574
+ # The Subscription remembers the widget it bound to — see #397.
575
+ bind_id = target.bind(DIALOG_RESULT, handler, add="+")
576
+ return Subscription(target, DIALOG_RESULT, bind_id)
574
577
 
575
578
  # helpers ------------------------------------------------------------------
576
579
  def _emit_result(self, confirmed: bool) -> None:
577
- payload = {"result": self.result, "confirmed": confirmed}
578
- target = self._master or self._dialog.toplevel
579
- if not target:
580
- return
581
- try:
582
- target.event_generate("<<DialogResult>>", data=payload)
583
- except Exception:
584
- try:
585
- target.event_generate("<<DialogResult>>")
586
- except Exception:
587
- pass
580
+ emit_dialog_result(
581
+ result_target(self._dialog, self._master),
582
+ {"result": self.result, "confirmed": confirmed},
583
+ )
588
584
  self._emitted_result = True
@@ -14,7 +14,10 @@ from tkinter import Widget
14
14
 
15
15
  from bootstack.widgets._impl.primitives import Frame
16
16
  from bootstack.constants import BOTH, YES
17
- from bootstack.dialogs._impl.dialog import Dialog, DialogButton
17
+ from bootstack.events import Subscription
18
+ from bootstack.dialogs._impl.dialog import (
19
+ DIALOG_RESULT, Dialog, DialogButton, emit_dialog_result, result_target,
20
+ )
18
21
  from bootstack._runtime.window_utilities import AnchorPoint
19
22
  from bootstack.widgets._impl.composites.calendar import Calendar
20
23
 
@@ -404,7 +407,7 @@ class DateDialog:
404
407
  """The selected date (single mode) or `(start, end)` tuple (range mode), or None if cancelled."""
405
408
  return self._dialog.result
406
409
 
407
- def on_result(self, callback: Callable[[date], None]) -> Optional[str]:
410
+ def on_result(self, callback: Callable[[date], None]) -> Optional[Subscription]:
408
411
  """Bind a callback fired when a result is produced.
409
412
 
410
413
  The callback receives `event.data["result"]` (a `datetime.date`).
@@ -413,38 +416,23 @@ class DateDialog:
413
416
  callback: Callable that receives the selected `datetime.date`.
414
417
 
415
418
  Returns:
416
- The Tk binding identifier, which can be used with `off_result`.
419
+ A `Subscription`, or `None` if there is no widget to bind to.
420
+ Cancel it with `.cancel()`.
417
421
  """
418
- target = self._dialog.toplevel or self._master
422
+ target = result_target(self._dialog, self._master)
419
423
  if target is None:
420
424
  return None
421
425
 
422
426
  def handler(event: tkinter.Event) -> None:
423
427
  callback(getattr(event, "data", None))
424
428
 
425
- return target.bind("<<DialogResult>>", handler, add="+")
426
-
427
- def off_result(self, funcid: str) -> None:
428
- """Unbind a previously bound `on_result` callback.
429
-
430
- Args:
431
- funcid: Binding identifier returned by `on_result`.
432
- """
433
- target = self._dialog.toplevel or self._master
434
- if target is None:
435
- return
436
- target.unbind("<<DialogResult>>", funcid)
429
+ # The Subscription remembers the widget it bound to — see #397.
430
+ bind_id = target.bind(DIALOG_RESULT, handler, add="+")
431
+ return Subscription(target, DIALOG_RESULT, bind_id)
437
432
 
438
433
  def _emit_result(self, value: date, confirmed: bool) -> None:
439
434
  """Emit a virtual Tk event with the dialog result."""
440
- target = self._dialog.toplevel or self._master
441
- if not target:
442
- return
443
- payload = {"result": value, "confirmed": confirmed}
444
- try:
445
- target.event_generate("<<DialogResult>>", data=payload)
446
- except Exception:
447
- try:
448
- target.event_generate("<<DialogResult>>")
449
- except Exception:
450
- pass
435
+ emit_dialog_result(
436
+ result_target(self._dialog, self._master),
437
+ {"result": value, "confirmed": confirmed},
438
+ )
@@ -127,6 +127,58 @@ class ShowOptions(TypedDict, total=False):
127
127
  auto_flip: Union[bool, Literal['vertical', 'horizontal']]
128
128
 
129
129
 
130
+ # --- Dialog result delivery -------------------------------------------------
131
+
132
+ DIALOG_RESULT = "<<DialogResult>>"
133
+
134
+
135
+ def result_target(dialog: "Dialog", master: Optional[tkinter.Misc]) -> Optional[tkinter.Misc]:
136
+ """The widget `<<DialogResult>>` is bound on and fired from.
137
+
138
+ Binding and firing both route through here so the two cannot resolve
139
+ differently. That mattered: `Dialog.toplevel` is `None` before `show()`
140
+ and a *destroyed* widget after it, because nothing resets `_toplevel`
141
+ when the modal wait ends. A target derived from it is therefore never
142
+ live at both ends — subscribers bound to the master while the result
143
+ fired at a dead toplevel, where `event_generate` raised and the error
144
+ was swallowed (#397).
145
+
146
+ `master` comes first for that reason: it outlives the dialog.
147
+
148
+ Args:
149
+ dialog: The dialog producing the result.
150
+ master: The dialog's master widget, if it has one.
151
+
152
+ Returns:
153
+ A live widget, or `None` when there is nothing left to fire on.
154
+ """
155
+ if master is not None:
156
+ return master
157
+ top = dialog.toplevel
158
+ if top is not None and top.winfo_exists():
159
+ return top
160
+ return None
161
+
162
+
163
+ def emit_dialog_result(target: Optional[tkinter.Misc], payload: dict) -> None:
164
+ """Fire `<<DialogResult>>` on `target` with `payload` attached.
165
+
166
+ Args:
167
+ target: The widget from `result_target`, or `None` to do nothing.
168
+ payload: Mapping with `result` and `confirmed` keys.
169
+ """
170
+ if target is None:
171
+ return
172
+ try:
173
+ target.event_generate(DIALOG_RESULT, data=payload)
174
+ except tkinter.TclError:
175
+ # `result_target` hands back a live widget, so reaching here means it
176
+ # was destroyed in between. Nothing can receive the result; surface it
177
+ # in development rather than letting it vanish the way it used to.
178
+ from bootstack._runtime.utility import debug_log_exception
179
+ debug_log_exception(f"could not deliver {DIALOG_RESULT}")
180
+
181
+
130
182
  # --- Dialog ----------------------------------------------------------------
131
183
 
132
184
  class Dialog:
@@ -10,7 +10,10 @@ from bootstack.widgets._impl.primitives import Frame, Label
10
10
  from bootstack._core.images import _ImageService
11
11
  from bootstack.constants import *
12
12
  from bootstack.i18n import MessageCatalog
13
- from .dialog import ButtonRole, Dialog, DialogButton
13
+ from bootstack.events import Subscription
14
+ from .dialog import (
15
+ DIALOG_RESULT, ButtonRole, Dialog, DialogButton, emit_dialog_result, result_target,
16
+ )
14
17
 
15
18
  ttk = SimpleNamespace(Frame=Frame, Label=Label)
16
19
 
@@ -198,23 +201,17 @@ class MessageDialog:
198
201
  position: x and y coordinates to position the dialog. If None, centers on parent.
199
202
  """
200
203
  self._dialog.show(position=position, modal=True)
201
- target = self._dialog.toplevel or self._master
202
- if target:
203
- payload = {"result": self._dialog.result, "confirmed": self._dialog.result is not None}
204
- try:
205
- target.event_generate("<<DialogResult>>", data=payload)
206
- except Exception:
207
- try:
208
- target.event_generate("<<DialogResult>>")
209
- except Exception:
210
- pass
204
+ emit_dialog_result(
205
+ result_target(self._dialog, self._master),
206
+ {"result": self._dialog.result, "confirmed": self._dialog.result is not None},
207
+ )
211
208
 
212
209
  @property
213
210
  def result(self) -> Any:
214
211
  """The dialog result value (the text of the button pressed)."""
215
212
  return self._dialog.result
216
213
 
217
- def on_dialog_result(self, callback: Callable[[Any], None]) -> Optional[str]:
214
+ def on_dialog_result(self, callback: Callable[[Any], None]) -> "Optional[Subscription]":
218
215
  """Bind a callback fired when the dialog produces a result.
219
216
 
220
217
  The callback receives `event.data["result"]` when available.
@@ -223,23 +220,22 @@ class MessageDialog:
223
220
  callback: Callable that receives the result payload.
224
221
 
225
222
  Returns:
226
- Binding identifier for use with `off_dialog_result`.
223
+ A `Subscription`, or `None` if there is no widget to bind to.
224
+ Cancel it with `.cancel()`.
227
225
  """
228
- target = self._dialog.toplevel or self._master
226
+ target = result_target(self._dialog, self._master)
229
227
  if target is None:
230
228
  return None
231
229
 
232
230
  def handler(event):
233
231
  callback(getattr(event, "data", None))
234
232
 
235
- return target.bind("<<DialogResult>>", handler, add="+")
236
-
237
- def off_dialog_result(self, funcid: str) -> None:
238
- """Unbind a previously bound dialog result callback."""
239
- target = self._dialog.toplevel or self._master
240
- if target is None:
241
- return
242
- target.unbind("<<DialogResult>>", funcid)
233
+ # The Subscription remembers the widget it bound to. Resolving the
234
+ # target a second time to unbind would not: `_dialog.toplevel` is None
235
+ # until show(), so subscribing before and cancelling after resolved to
236
+ # two different widgets and the removal silently matched nothing (#397).
237
+ bind_id = target.bind(DIALOG_RESULT, handler, add="+")
238
+ return Subscription(target, DIALOG_RESULT, bind_id)
243
239
 
244
240
 
245
241
  class MessageBox: