bootstack 0.2.0__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.2.0/src/bootstack.egg-info → bootstack-0.2.1}/PKG-INFO +1 -1
  2. {bootstack-0.2.0 → bootstack-0.2.1}/pyproject.toml +2 -2
  3. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_runtime/base_window.py +17 -2
  4. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_runtime/events.py +46 -12
  5. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_runtime/shortcuts.py +10 -2
  6. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_runtime/utility.py +22 -0
  7. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/colorchooser.py +19 -23
  8. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/datedialog.py +15 -27
  9. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/dialog.py +52 -0
  10. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/message.py +18 -22
  11. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/query.py +15 -22
  12. bootstack-0.2.1/src/bootstack/events/_subscription.py +60 -0
  13. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/base.py +51 -11
  14. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_parts/numberentry_part.py +7 -2
  15. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/shell.py +5 -1
  16. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/codeeditor.py +7 -33
  17. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/datefield.py +7 -34
  18. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/numberfield.py +7 -33
  19. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/passwordfield.py +7 -33
  20. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/pathfield.py +7 -33
  21. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/select.py +8 -34
  22. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/spinnerfield.py +7 -33
  23. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/tabs.py +2 -21
  24. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/textarea.py +7 -33
  25. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/textfield.py +7 -33
  26. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/timefield.py +7 -34
  27. {bootstack-0.2.0 → bootstack-0.2.1/src/bootstack.egg-info}/PKG-INFO +1 -1
  28. bootstack-0.2.0/src/bootstack/events/_subscription.py +0 -38
  29. {bootstack-0.2.0 → bootstack-0.2.1}/LICENSE +0 -0
  30. {bootstack-0.2.0 → bootstack-0.2.1}/MANIFEST.in +0 -0
  31. {bootstack-0.2.0 → bootstack-0.2.1}/NOTICE +0 -0
  32. {bootstack-0.2.0 → bootstack-0.2.1}/README.md +0 -0
  33. {bootstack-0.2.0 → bootstack-0.2.1}/setup.cfg +0 -0
  34. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/__init__.py +0 -0
  35. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/__main__.py +0 -0
  36. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/__init__.py +0 -0
  37. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/__init__.py +0 -0
  38. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/after.py +0 -0
  39. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/bind.py +0 -0
  40. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/bindtags.py +0 -0
  41. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/busy.py +0 -0
  42. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/clipboard.py +0 -0
  43. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/focus.py +0 -0
  44. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/grab.py +0 -0
  45. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/grid.py +0 -0
  46. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/localization.py +0 -0
  47. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/pack.py +0 -0
  48. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/place.py +0 -0
  49. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/selection.py +0 -0
  50. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/signals.py +0 -0
  51. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/capabilities/winfo.py +0 -0
  52. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/colorutils.py +0 -0
  53. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/exceptions.py +0 -0
  54. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/images.py +0 -0
  55. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/mixins/__init__.py +0 -0
  56. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/mixins/ttk_state.py +0 -0
  57. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/mixins/widget.py +0 -0
  58. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/paths.py +0 -0
  59. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/publisher.py +0 -0
  60. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_core/variables.py +0 -0
  61. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_runtime/__init__.py +0 -0
  62. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_runtime/app.py +0 -0
  63. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_runtime/tk_patch.py +0 -0
  64. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_runtime/toplevel.py +0 -0
  65. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_runtime/visual_focus.py +0 -0
  66. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_runtime/wheel.py +0 -0
  67. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/_runtime/window_utilities.py +0 -0
  68. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/__init__.py +0 -0
  69. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/bootstack.ico +0 -0
  70. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/bootstack.png +0 -0
  71. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/__init__.py +0 -0
  72. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/badge-pill.png +0 -0
  73. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/badge-square.png +0 -0
  74. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/button-compact.png +0 -0
  75. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/button-default.png +0 -0
  76. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
  77. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
  78. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
  79. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
  80. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
  81. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
  82. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
  83. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
  84. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
  85. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
  86. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
  87. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
  88. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/card.png +0 -0
  89. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/checkbox-checked.png +0 -0
  90. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  91. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/checkbox-unchecked.png +0 -0
  92. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/field.png +0 -0
  93. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/input-addon-compact.png +0 -0
  94. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/input-addon-default.png +0 -0
  95. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/input-compact.png +0 -0
  96. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/input-default.png +0 -0
  97. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/list-item-separated.png +0 -0
  98. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/list-item.png +0 -0
  99. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/listrow-compact.png +0 -0
  100. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/listrow-default.png +0 -0
  101. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/manifest.toml +0 -0
  102. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/menu-item.png +0 -0
  103. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/navitem-compact.png +0 -0
  104. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/navitem-default.png +0 -0
  105. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/progressbar-h-compact.png +0 -0
  106. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/progressbar-h-default.png +0 -0
  107. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/progressbar-v-compact.png +0 -0
  108. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/progressbar-v-default.png +0 -0
  109. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/radiobutton.png +0 -0
  110. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  111. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/scrollbar-vertical.png +0 -0
  112. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/slider-handle.png +0 -0
  113. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/slider-track-h.png +0 -0
  114. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/slider-track-v.png +0 -0
  115. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/switch-off.png +0 -0
  116. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/switch-on.png +0 -0
  117. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/tab-h.png +0 -0
  118. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/elements/tab-v.png +0 -0
  119. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/icons/bootstrap.ttf +0 -0
  120. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/icons/glyphmap.json +0 -0
  121. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/icons/icon_metrics.json +0 -0
  122. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  123. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +0 -0
  124. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
  125. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +0 -0
  126. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  127. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +0 -0
  128. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  129. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +0 -0
  130. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  131. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +0 -0
  132. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  133. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +0 -0
  134. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  135. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +0 -0
  136. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  137. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +0 -0
  138. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  139. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +0 -0
  140. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  141. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +0 -0
  142. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  143. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +0 -0
  144. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  145. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +0 -0
  146. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  147. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +0 -0
  148. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  149. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +0 -0
  150. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  151. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +0 -0
  152. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  153. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +0 -0
  154. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  155. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +0 -0
  156. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  157. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +0 -0
  158. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  159. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +0 -0
  160. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  161. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +0 -0
  162. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  163. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +0 -0
  164. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  165. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +0 -0
  166. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  167. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +0 -0
  168. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/__init__.py +0 -0
  169. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/__main__.py +0 -0
  170. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/add.py +0 -0
  171. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/appicon.py +0 -0
  172. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/build.py +0 -0
  173. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/config.py +0 -0
  174. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/demo.py +0 -0
  175. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/dev.py +0 -0
  176. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/doctor.py +0 -0
  177. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/icons.py +0 -0
  178. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/promote.py +0 -0
  179. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/pyinstaller.py +0 -0
  180. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/run.py +0 -0
  181. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/start.py +0 -0
  182. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/cli/templates/__init__.py +0 -0
  183. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/clipboard.py +0 -0
  184. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/constants.py +0 -0
  185. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/data/README.md +0 -0
  186. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/data/__init__.py +0 -0
  187. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/data/_observable.py +0 -0
  188. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/data/base.py +0 -0
  189. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/data/file_source.py +0 -0
  190. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/data/memory_source.py +0 -0
  191. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/data/query.py +0 -0
  192. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/data/readers.py +0 -0
  193. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/data/sqlite_source.py +0 -0
  194. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/data/types.py +0 -0
  195. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/data/writers.py +0 -0
  196. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dev/__init__.py +0 -0
  197. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dev/_capture.py +0 -0
  198. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dev/_env.py +0 -0
  199. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dev/_registry.py +0 -0
  200. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dev/_reloader.py +0 -0
  201. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dev/_reset.py +0 -0
  202. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dev/_watcher.py +0 -0
  203. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dialogs/__init__.py +0 -0
  204. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/__init__.py +0 -0
  205. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/filterdialog.py +0 -0
  206. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/fontdialog.py +0 -0
  207. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/formdialog.py +0 -0
  208. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/errors.py +0 -0
  209. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/events/__init__.py +0 -0
  210. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/events/_event.py +0 -0
  211. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/events/_payloads.py +0 -0
  212. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/i18n/README.md +0 -0
  213. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/i18n/__init__.py +0 -0
  214. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/i18n/catalog.py +0 -0
  215. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/i18n/intl_format.py +0 -0
  216. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/i18n/msgcat.py +0 -0
  217. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/i18n/specs.py +0 -0
  218. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/images.py +0 -0
  219. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/py.typed +0 -0
  220. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/scheduling/__init__.py +0 -0
  221. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/scheduling/_schedule.py +0 -0
  222. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/shortcuts.py +0 -0
  223. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/signals/README.md +0 -0
  224. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/signals/__init__.py +0 -0
  225. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/signals/integration.py +0 -0
  226. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/signals/signal.py +0 -0
  227. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/signals/types.py +0 -0
  228. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/store.py +0 -0
  229. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/streams/__init__.py +0 -0
  230. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/streams/_stream.py +0 -0
  231. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/__init__.py +0 -0
  232. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/__init__.py +0 -0
  233. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/badge.py +0 -0
  234. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/button.py +0 -0
  235. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/buttongroup.py +0 -0
  236. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/calendar.py +0 -0
  237. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/checkbutton.py +0 -0
  238. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/combobox.py +0 -0
  239. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/contextmenu.py +0 -0
  240. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/entry.py +0 -0
  241. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/expander.py +0 -0
  242. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/field.py +0 -0
  243. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/frame.py +0 -0
  244. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/label.py +0 -0
  245. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/labelframe.py +0 -0
  246. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/listview.py +0 -0
  247. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/menubar.py +0 -0
  248. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/menubutton.py +0 -0
  249. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/panedwindow.py +0 -0
  250. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/progressbar.py +0 -0
  251. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/radiobutton.py +0 -0
  252. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/scale.py +0 -0
  253. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/scrollbar.py +0 -0
  254. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/separator.py +0 -0
  255. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/sidenav.py +0 -0
  256. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/sizegrip.py +0 -0
  257. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/spinbox.py +0 -0
  258. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/switch.py +0 -0
  259. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/tabitem.py +0 -0
  260. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/togglegroup.py +0 -0
  261. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/toolbutton.py +0 -0
  262. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/tooltip.py +0 -0
  263. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/treeview.py +0 -0
  264. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders/utils.py +0 -0
  265. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders_tk/__init__.py +0 -0
  266. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/builders_tk/defaults.py +0 -0
  267. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/element.py +0 -0
  268. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/fonts.py +0 -0
  269. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/style.py +0 -0
  270. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/style_builder_base.py +0 -0
  271. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/style_builder_mixed.py +0 -0
  272. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/style_builder_tk.py +0 -0
  273. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/style_builder_ttk.py +0 -0
  274. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/style_resolver.py +0 -0
  275. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/theme.py +0 -0
  276. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/theme_provider.py +0 -0
  277. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/themes/__init__.py +0 -0
  278. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/tk_patch.py +0 -0
  279. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/token_maps.py +0 -0
  280. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/types.py +0 -0
  281. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/typography.py +0 -0
  282. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/style/utility.py +0 -0
  283. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/types.py +0 -0
  284. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/validation/__init__.py +0 -0
  285. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/validation/types.py +0 -0
  286. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/validation/validation_result.py +0 -0
  287. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/validation/validation_rules.py +0 -0
  288. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/__init__.py +0 -0
  289. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/__init__.py +0 -0
  290. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/app_config.py +0 -0
  291. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/choices.py +0 -0
  292. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/container.py +0 -0
  293. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/context.py +0 -0
  294. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/events.py +0 -0
  295. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/field_mixin.py +0 -0
  296. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/icon_image_props.py +0 -0
  297. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/image_binding.py +0 -0
  298. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/kwargs.py +0 -0
  299. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/navmodel.py +0 -0
  300. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/options.py +0 -0
  301. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/selection_group.py +0 -0
  302. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/window_controls.py +0 -0
  303. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_core/window_menu.py +0 -0
  304. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/__init__.py +0 -0
  305. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_internal/__init__.py +0 -0
  306. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_internal/wrapper_base.py +0 -0
  307. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_parts/__init__.py +0 -0
  308. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_parts/spinnerentry_part.py +0 -0
  309. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_parts/textentry_part.py +0 -0
  310. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/__init__.py +0 -0
  311. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/_dateutils.py +0 -0
  312. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/_image_fit.py +0 -0
  313. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/accordion.py +0 -0
  314. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/avatar.py +0 -0
  315. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/buttongroup.py +0 -0
  316. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/calendar.py +0 -0
  317. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/carousel.py +0 -0
  318. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/chart.py +0 -0
  319. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/compositeframe.py +0 -0
  320. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/contextmenu.py +0 -0
  321. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/dateentry.py +0 -0
  322. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/dropdownbutton.py +0 -0
  323. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/expander.py +0 -0
  324. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/field.py +0 -0
  325. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/form.py +0 -0
  326. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/gallery.py +0 -0
  327. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/list/__init__.py +0 -0
  328. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/list/listitem.py +0 -0
  329. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/list/listview.py +0 -0
  330. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/menu/__init__.py +0 -0
  331. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/menu/model.py +0 -0
  332. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/menu/render_native.py +0 -0
  333. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/menu/render_themed.py +0 -0
  334. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/meter.py +0 -0
  335. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/numericentry.py +0 -0
  336. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/pagestack.py +0 -0
  337. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/passwordentry.py +0 -0
  338. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/pathentry.py +0 -0
  339. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/picture.py +0 -0
  340. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/radiogroup.py +0 -0
  341. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/scrolledtext.py +0 -0
  342. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/scrolledtext.pyi +0 -0
  343. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/scrollview.py +0 -0
  344. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/selectbox.py +0 -0
  345. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/__init__.py +0 -0
  346. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/content_host.py +0 -0
  347. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/layout.py +0 -0
  348. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/nav_panel.py +0 -0
  349. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/providers.py +0 -0
  350. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/rail.py +0 -0
  351. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/workspace.py +0 -0
  352. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/sidenav/__init__.py +0 -0
  353. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/sidenav/header.py +0 -0
  354. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/sidenav/separator.py +0 -0
  355. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/slider/__init__.py +0 -0
  356. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/slider/_shared.py +0 -0
  357. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/slider/rangeslider.py +0 -0
  358. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/slider/slider.py +0 -0
  359. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/spinnerentry.py +0 -0
  360. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tableview/__init__.py +0 -0
  361. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tableview/tableview.py +0 -0
  362. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tableview/types.py +0 -0
  363. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tabs/__init__.py +0 -0
  364. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tabs/tabitem.py +0 -0
  365. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tabs/tabs.py +0 -0
  366. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tabs/tabview.py +0 -0
  367. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/__init__.py +0 -0
  368. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/change.py +0 -0
  369. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/codeeditor.py +0 -0
  370. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/core.py +0 -0
  371. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/decoration.py +0 -0
  372. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/diff.py +0 -0
  373. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +0 -0
  374. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +0 -0
  375. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +0 -0
  376. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +0 -0
  377. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +0 -0
  378. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +0 -0
  379. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/filter.py +0 -0
  380. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/search_overlay.py +0 -0
  381. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/sidebar.py +0 -0
  382. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/style_registry.py +0 -0
  383. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/textarea.py +0 -0
  384. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/undo.py +0 -0
  385. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textentry.py +0 -0
  386. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/timeentry.py +0 -0
  387. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/toast.py +0 -0
  388. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/toast_stack.py +0 -0
  389. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/togglegroup.py +0 -0
  390. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/toolbar.py +0 -0
  391. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tooltip.py +0 -0
  392. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/__init__.py +0 -0
  393. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/source_binding.py +0 -0
  394. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/treeitem.py +0 -0
  395. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/treenode.py +0 -0
  396. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/treeview.py +0 -0
  397. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/__init__.py +0 -0
  398. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/configure_mixin.py +0 -0
  399. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/entry_mixin.py +0 -0
  400. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/font_mixin.py +0 -0
  401. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/icon_mixin.py +0 -0
  402. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/localization_mixin.py +0 -0
  403. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/signal_mixin.py +0 -0
  404. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/validation_mixin.py +0 -0
  405. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/__init__.py +0 -0
  406. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/_menubutton.py +0 -0
  407. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/badge.py +0 -0
  408. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/button.py +0 -0
  409. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/card.py +0 -0
  410. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/checkbutton.py +0 -0
  411. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/checktoggle.py +0 -0
  412. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/combobox.py +0 -0
  413. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/entry.py +0 -0
  414. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/flexframe.py +0 -0
  415. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/frame.py +0 -0
  416. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/gridframe.py +0 -0
  417. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/label.py +0 -0
  418. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/labelframe.py +0 -0
  419. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/optionmenu.py +0 -0
  420. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/packframe.py +0 -0
  421. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/panedwindow.py +0 -0
  422. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/progressbar.py +0 -0
  423. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/radiobutton.py +0 -0
  424. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/radiotoggle.py +0 -0
  425. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/scrollbar.py +0 -0
  426. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/separator.py +0 -0
  427. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/sizegrip.py +0 -0
  428. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/spinbox.py +0 -0
  429. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/switch.py +0 -0
  430. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/treeview.py +0 -0
  431. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/app.py +0 -0
  432. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/appshell.py +0 -0
  433. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/avatar.py +0 -0
  434. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/boolean_controls.py +0 -0
  435. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/button.py +0 -0
  436. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/buttongroup.py +0 -0
  437. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/calendar.py +0 -0
  438. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/card.py +0 -0
  439. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/carousel.py +0 -0
  440. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/chart.py +0 -0
  441. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/contextmenu.py +0 -0
  442. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/datatable.py +0 -0
  443. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/divider.py +0 -0
  444. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/expander.py +0 -0
  445. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/form.py +0 -0
  446. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/gallery.py +0 -0
  447. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/gauge.py +0 -0
  448. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/grid.py +0 -0
  449. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/groupbox.py +0 -0
  450. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/label.py +0 -0
  451. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/listview.py +0 -0
  452. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/menubutton.py +0 -0
  453. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/pagestack.py +0 -0
  454. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/picture.py +0 -0
  455. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/progressbar.py +0 -0
  456. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/radio_variants.py +0 -0
  457. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/radiogroup.py +0 -0
  458. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/scrollbar.py +0 -0
  459. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/scrollview.py +0 -0
  460. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/selectbutton.py +0 -0
  461. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/sidebar_toggle.py +0 -0
  462. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/sizegrip.py +0 -0
  463. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/slider.py +0 -0
  464. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/spinbox.py +0 -0
  465. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/splash.py +0 -0
  466. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/splitview.py +0 -0
  467. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/stacks.py +0 -0
  468. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/statusbar.py +0 -0
  469. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/theme_toggle.py +0 -0
  470. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/toast.py +0 -0
  471. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/togglegroup.py +0 -0
  472. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/toolbar.py +0 -0
  473. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/tooltip.py +0 -0
  474. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/tree.py +0 -0
  475. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/types.py +0 -0
  476. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack/widgets/window.py +0 -0
  477. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack.egg-info/SOURCES.txt +0 -0
  478. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack.egg-info/dependency_links.txt +0 -0
  479. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack.egg-info/entry_points.txt +0 -0
  480. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack.egg-info/requires.txt +0 -0
  481. {bootstack-0.2.0 → bootstack-0.2.1}/src/bootstack.egg-info/top_level.txt +0 -0
  482. {bootstack-0.2.0 → bootstack-0.2.1}/tests/test_dialog_anchor.py +0 -0
  483. {bootstack-0.2.0 → bootstack-0.2.1}/tests/test_events_doc_coverage.py +0 -0
  484. {bootstack-0.2.0 → bootstack-0.2.1}/tests/test_hot_reload.py +0 -0
  485. {bootstack-0.2.0 → bootstack-0.2.1}/tests/test_images.py +0 -0
  486. {bootstack-0.2.0 → bootstack-0.2.1}/tests/test_images_gui.py +0 -0
  487. {bootstack-0.2.0 → bootstack-0.2.1}/tests/test_intl_format.py +0 -0
  488. {bootstack-0.2.0 → bootstack-0.2.1}/tests/test_navmodel.py +0 -0
  489. {bootstack-0.2.0 → bootstack-0.2.1}/tests/test_on_color_contrast.py +0 -0
  490. {bootstack-0.2.0 → bootstack-0.2.1}/tests/test_public_surface.py +0 -0
  491. {bootstack-0.2.0 → bootstack-0.2.1}/tests/test_store.py +0 -0
  492. {bootstack-0.2.0 → bootstack-0.2.1}/tests/test_theme_from_existing.py +0 -0
  493. {bootstack-0.2.0 → 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.2.0
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.2.0"
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.2.0"
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:
@@ -354,22 +354,56 @@ def _patched_unbind(
354
354
  what = ('bind', self._w, sequence)
355
355
  try:
356
356
  script = self.tk.call(what)
357
- lines = str(script).split('\n')
358
- # Must match what `_patched_bind` emits (and stock `Misc._bind`).
359
- prefix = 'if {"[%s ' % funcid
360
- keep = [line for line in lines if not line.startswith(prefix)]
361
- if len(keep) == len(lines):
362
- # This funcid is not bound to this widget and sequence, so it is
363
- # not ours to delete — the live binding may be somewhere else, and
364
- # deleting its command would orphan it. Leave everything alone.
365
- return
366
- remaining = '\n'.join(keep)
367
- self.tk.call(*what, remaining if remaining.strip() else '')
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:
368
398
  # Replace the command rather than deleting it, so an in-flight copy of
369
399
  # the concatenated script has something harmless to call.
370
400
  self.tk.createcommand(funcid, lambda *args: '')
371
401
  except tk.TclError:
372
- return
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
373
407
 
374
408
  def _delete_command() -> None:
375
409
  try:
@@ -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:
@@ -16,7 +16,10 @@ ttk = SimpleNamespace(
16
16
  )
17
17
  from bootstack.i18n import MessageCatalog
18
18
  from bootstack.dialogs._impl.datedialog import DateDialog
19
- from bootstack.dialogs._impl.dialog import Dialog, DialogButton
19
+ from bootstack.events import Subscription
20
+ from bootstack.dialogs._impl.dialog import (
21
+ DIALOG_RESULT, Dialog, DialogButton, emit_dialog_result, result_target,
22
+ )
20
23
  from bootstack.dialogs._impl.fontdialog import FontDialog
21
24
  from bootstack.dialogs._impl.message import MessageBox
22
25
  from bootstack.widgets._impl.composites.textentry import TextEntry
@@ -264,23 +267,17 @@ class QueryDialog:
264
267
  position: x and y coordinates to position the dialog. If None, centers on parent.
265
268
  """
266
269
  self._dialog.show(position=position, modal=True)
267
- target = self._dialog.toplevel or self._master
268
- if target:
269
- payload = {"result": self._dialog.result, "confirmed": self._dialog.result is not None}
270
- try:
271
- target.event_generate("<<DialogResult>>", data=payload)
272
- except Exception:
273
- try:
274
- target.event_generate("<<DialogResult>>")
275
- except Exception:
276
- pass
270
+ emit_dialog_result(
271
+ result_target(self._dialog, self._master),
272
+ {"result": self._dialog.result, "confirmed": self._dialog.result is not None},
273
+ )
277
274
 
278
275
  @property
279
276
  def result(self) -> Any:
280
277
  """The dialog result value."""
281
278
  return self._dialog.result
282
279
 
283
- def on_dialog_result(self, callback: Callable[[Any], None]) -> Optional[str]:
280
+ def on_dialog_result(self, callback: Callable[[Any], None]) -> "Optional[Subscription]":
284
281
  """Bind a callback fired when the dialog produces a result.
285
282
 
286
283
  The callback receives `event.data["result"]` when available.
@@ -289,23 +286,19 @@ class QueryDialog:
289
286
  callback: Callable that receives the result payload.
290
287
 
291
288
  Returns:
292
- Binding identifier for use with `off_dialog_result`.
289
+ A `Subscription`, or `None` if there is no widget to bind to.
290
+ Cancel it with `.cancel()`.
293
291
  """
294
- target = self._dialog.toplevel or self._master
292
+ target = result_target(self._dialog, self._master)
295
293
  if target is None:
296
294
  return None
297
295
 
298
296
  def handler(event):
299
297
  callback(getattr(event, "data", None))
300
298
 
301
- return target.bind("<<DialogResult>>", handler, add="+")
302
-
303
- def off_dialog_result(self, funcid: str) -> None:
304
- """Unbind a previously bound dialog result callback."""
305
- target = self._dialog.toplevel or self._master
306
- if target is None:
307
- return
308
- target.unbind("<<DialogResult>>", funcid)
299
+ # The Subscription remembers the widget it bound to — see #397.
300
+ bind_id = target.bind(DIALOG_RESULT, handler, add="+")
301
+ return Subscription(target, DIALOG_RESULT, bind_id)
309
302
 
310
303
 
311
304
  class QueryBox:
@@ -0,0 +1,60 @@
1
+ from __future__ import annotations
2
+
3
+ import tkinter
4
+
5
+
6
+ class Subscription:
7
+ """Handle returned by `widget.on(...)`. Cancels the binding when `.cancel()`
8
+ is called or the context manager exits.
9
+
10
+ Idempotent: calling `cancel()` more than once is a no-op.
11
+ """
12
+
13
+ __slots__ = ("_widget", "_sequence", "_bind_id", "_cancelled")
14
+
15
+ def __init__(self, widget: tkinter.Misc, sequence: str, bind_id: str) -> None:
16
+ self._widget = widget
17
+ self._sequence = sequence
18
+ self._bind_id = bind_id
19
+ self._cancelled = False
20
+
21
+ def cancel(self) -> None:
22
+ """Remove the binding. Safe to call more than once, and on a dead widget.
23
+
24
+ `cancelled` afterwards means the removal was carried out as far as this
25
+ handle can carry it — the handler will not be delivered to again through
26
+ this subscription.
27
+ """
28
+ if self._cancelled:
29
+ return
30
+ try:
31
+ self._widget.unbind(self._sequence, self._bind_id)
32
+ except tkinter.TclError:
33
+ # The removal failed partway, which leaves the handler bound and
34
+ # still firing. Swallowed so teardown never fails, but deliberately
35
+ # NOT marked cancelled: claiming otherwise made `cancelled` read
36
+ # True for a live handler (#400). A later cancel() may still succeed.
37
+ #
38
+ # A widget that is merely gone does not reach here — unbind absorbs
39
+ # that case itself, and rightly reports success, since a destroyed
40
+ # widget takes its bindings with it.
41
+ return
42
+ # Includes the case where unbind found nothing to remove: the handler is
43
+ # already unreachable through this widget and sequence, and no retry can
44
+ # change that, so reporting anything but cancelled would be both wrong
45
+ # and permanent. If it is unreachable here because it was bound
46
+ # somewhere else, that is drift rather than a completed cancel, and
47
+ # unbind reports it under BOOTSTACK_DEBUG (#399) — this handle has no
48
+ # way to tell the two apart.
49
+ self._cancelled = True
50
+
51
+ def __enter__(self) -> "Subscription":
52
+ return self
53
+
54
+ def __exit__(self, exc_type, exc, tb) -> None:
55
+ self.cancel()
56
+
57
+ @property
58
+ def cancelled(self) -> bool:
59
+ """Whether `cancel()` has completed for this subscription."""
60
+ return self._cancelled