bootstack 0.3.0__tar.gz → 0.3.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 (494) hide show
  1. {bootstack-0.3.0/src/bootstack.egg-info → bootstack-0.3.1}/PKG-INFO +1 -1
  2. {bootstack-0.3.0 → bootstack-0.3.1}/pyproject.toml +2 -2
  3. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/datedialog.py +27 -2
  4. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/dialog.py +293 -7
  5. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/query.py +12 -10
  6. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/base.py +1 -1
  7. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/container.py +63 -6
  8. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/appshell.py +1 -1
  9. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/card.py +1 -1
  10. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/expander.py +2 -2
  11. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/grid.py +1 -1
  12. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/groupbox.py +1 -1
  13. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/pagestack.py +1 -1
  14. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/splitview.py +1 -1
  15. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/tabs.py +1 -1
  16. {bootstack-0.3.0 → bootstack-0.3.1/src/bootstack.egg-info}/PKG-INFO +1 -1
  17. {bootstack-0.3.0 → bootstack-0.3.1}/LICENSE +0 -0
  18. {bootstack-0.3.0 → bootstack-0.3.1}/MANIFEST.in +0 -0
  19. {bootstack-0.3.0 → bootstack-0.3.1}/NOTICE +0 -0
  20. {bootstack-0.3.0 → bootstack-0.3.1}/README.md +0 -0
  21. {bootstack-0.3.0 → bootstack-0.3.1}/setup.cfg +0 -0
  22. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/__init__.py +0 -0
  23. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/__main__.py +0 -0
  24. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/__init__.py +0 -0
  25. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/__init__.py +0 -0
  26. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/after.py +0 -0
  27. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/bind.py +0 -0
  28. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/bindtags.py +0 -0
  29. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/busy.py +0 -0
  30. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/clipboard.py +0 -0
  31. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/focus.py +0 -0
  32. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/grab.py +0 -0
  33. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/grid.py +0 -0
  34. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/localization.py +0 -0
  35. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/pack.py +0 -0
  36. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/place.py +0 -0
  37. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/selection.py +0 -0
  38. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/signals.py +0 -0
  39. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capabilities/winfo.py +0 -0
  40. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/capture.py +0 -0
  41. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/colorutils.py +0 -0
  42. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/exceptions.py +0 -0
  43. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/images.py +0 -0
  44. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/mixins/__init__.py +0 -0
  45. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/mixins/ttk_state.py +0 -0
  46. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/mixins/widget.py +0 -0
  47. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/paths.py +0 -0
  48. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/publisher.py +0 -0
  49. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_core/variables.py +0 -0
  50. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_runtime/__init__.py +0 -0
  51. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_runtime/app.py +0 -0
  52. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_runtime/base_window.py +0 -0
  53. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_runtime/events.py +0 -0
  54. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_runtime/shortcuts.py +0 -0
  55. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_runtime/tk_patch.py +0 -0
  56. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_runtime/toplevel.py +0 -0
  57. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_runtime/utility.py +0 -0
  58. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_runtime/visual_focus.py +0 -0
  59. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_runtime/wheel.py +0 -0
  60. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/_runtime/window_utilities.py +0 -0
  61. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/__init__.py +0 -0
  62. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/bootstack.ico +0 -0
  63. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/bootstack.png +0 -0
  64. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/__init__.py +0 -0
  65. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/badge-pill.png +0 -0
  66. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/badge-square.png +0 -0
  67. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/button-compact.png +0 -0
  68. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/button-default.png +0 -0
  69. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
  70. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
  71. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
  72. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
  73. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
  74. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
  75. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
  76. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
  77. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
  78. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
  79. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
  80. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
  81. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/card.png +0 -0
  82. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/checkbox-checked.png +0 -0
  83. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  84. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/checkbox-unchecked.png +0 -0
  85. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/field.png +0 -0
  86. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/input-addon-compact.png +0 -0
  87. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/input-addon-default.png +0 -0
  88. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/input-compact.png +0 -0
  89. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/input-default.png +0 -0
  90. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/list-item-separated.png +0 -0
  91. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/list-item.png +0 -0
  92. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/listrow-compact.png +0 -0
  93. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/listrow-default.png +0 -0
  94. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/manifest.toml +0 -0
  95. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/menu-item.png +0 -0
  96. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/navitem-compact.png +0 -0
  97. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/navitem-default.png +0 -0
  98. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/progressbar-h-compact.png +0 -0
  99. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/progressbar-h-default.png +0 -0
  100. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/progressbar-v-compact.png +0 -0
  101. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/progressbar-v-default.png +0 -0
  102. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/radiobutton.png +0 -0
  103. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  104. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/scrollbar-vertical.png +0 -0
  105. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/slider-handle.png +0 -0
  106. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/slider-track-h.png +0 -0
  107. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/slider-track-v.png +0 -0
  108. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/switch-off.png +0 -0
  109. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/switch-on.png +0 -0
  110. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/tab-h.png +0 -0
  111. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/elements/tab-v.png +0 -0
  112. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/icons/bootstrap.ttf +0 -0
  113. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/icons/glyphmap.json +0 -0
  114. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/icons/icon_metrics.json +0 -0
  115. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  116. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +0 -0
  117. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
  118. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +0 -0
  119. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  120. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +0 -0
  121. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  122. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +0 -0
  123. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  124. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +0 -0
  125. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  126. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +0 -0
  127. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  128. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +0 -0
  129. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  130. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +0 -0
  131. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  132. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +0 -0
  133. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  134. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +0 -0
  135. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  136. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +0 -0
  137. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  138. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +0 -0
  139. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  140. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +0 -0
  141. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  142. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +0 -0
  143. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  144. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +0 -0
  145. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  146. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +0 -0
  147. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  148. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +0 -0
  149. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  150. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +0 -0
  151. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  152. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +0 -0
  153. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  154. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +0 -0
  155. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  156. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +0 -0
  157. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  158. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +0 -0
  159. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  160. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +0 -0
  161. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/__init__.py +0 -0
  162. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/__main__.py +0 -0
  163. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/add.py +0 -0
  164. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/appicon.py +0 -0
  165. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/build.py +0 -0
  166. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/config.py +0 -0
  167. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/demo.py +0 -0
  168. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/dev.py +0 -0
  169. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/doctor.py +0 -0
  170. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/icons.py +0 -0
  171. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/promote.py +0 -0
  172. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/pyinstaller.py +0 -0
  173. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/run.py +0 -0
  174. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/start.py +0 -0
  175. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/cli/templates/__init__.py +0 -0
  176. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/clipboard.py +0 -0
  177. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/constants.py +0 -0
  178. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/data/README.md +0 -0
  179. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/data/__init__.py +0 -0
  180. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/data/_observable.py +0 -0
  181. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/data/base.py +0 -0
  182. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/data/file_source.py +0 -0
  183. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/data/memory_source.py +0 -0
  184. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/data/query.py +0 -0
  185. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/data/readers.py +0 -0
  186. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/data/sqlite_source.py +0 -0
  187. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/data/types.py +0 -0
  188. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/data/writers.py +0 -0
  189. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dev/__init__.py +0 -0
  190. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dev/_capture.py +0 -0
  191. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dev/_env.py +0 -0
  192. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dev/_registry.py +0 -0
  193. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dev/_reloader.py +0 -0
  194. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dev/_reset.py +0 -0
  195. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dev/_watcher.py +0 -0
  196. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dialogs/__init__.py +0 -0
  197. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/__init__.py +0 -0
  198. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/colorchooser.py +0 -0
  199. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/filterdialog.py +0 -0
  200. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/fontdialog.py +0 -0
  201. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/formdialog.py +0 -0
  202. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/message.py +0 -0
  203. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/errors.py +0 -0
  204. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/events/__init__.py +0 -0
  205. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/events/_event.py +0 -0
  206. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/events/_payloads.py +0 -0
  207. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/events/_subscription.py +0 -0
  208. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/i18n/README.md +0 -0
  209. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/i18n/__init__.py +0 -0
  210. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/i18n/catalog.py +0 -0
  211. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/i18n/intl_format.py +0 -0
  212. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/i18n/msgcat.py +0 -0
  213. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/i18n/specs.py +0 -0
  214. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/images.py +0 -0
  215. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/py.typed +0 -0
  216. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/scheduling/__init__.py +0 -0
  217. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/scheduling/_schedule.py +0 -0
  218. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/shortcuts.py +0 -0
  219. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/signals/README.md +0 -0
  220. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/signals/__init__.py +0 -0
  221. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/signals/integration.py +0 -0
  222. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/signals/signal.py +0 -0
  223. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/signals/types.py +0 -0
  224. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/store.py +0 -0
  225. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/streams/__init__.py +0 -0
  226. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/streams/_stream.py +0 -0
  227. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/__init__.py +0 -0
  228. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/__init__.py +0 -0
  229. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/badge.py +0 -0
  230. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/button.py +0 -0
  231. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/buttongroup.py +0 -0
  232. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/calendar.py +0 -0
  233. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/checkbutton.py +0 -0
  234. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/combobox.py +0 -0
  235. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/contextmenu.py +0 -0
  236. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/entry.py +0 -0
  237. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/expander.py +0 -0
  238. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/field.py +0 -0
  239. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/frame.py +0 -0
  240. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/label.py +0 -0
  241. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/labelframe.py +0 -0
  242. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/listview.py +0 -0
  243. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/menubar.py +0 -0
  244. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/menubutton.py +0 -0
  245. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/panedwindow.py +0 -0
  246. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/progressbar.py +0 -0
  247. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/radiobutton.py +0 -0
  248. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/scale.py +0 -0
  249. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/scrollbar.py +0 -0
  250. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/separator.py +0 -0
  251. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/sidenav.py +0 -0
  252. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/sizegrip.py +0 -0
  253. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/spinbox.py +0 -0
  254. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/switch.py +0 -0
  255. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/tabitem.py +0 -0
  256. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/togglegroup.py +0 -0
  257. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/toolbutton.py +0 -0
  258. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/tooltip.py +0 -0
  259. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/treeview.py +0 -0
  260. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders/utils.py +0 -0
  261. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders_tk/__init__.py +0 -0
  262. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/builders_tk/defaults.py +0 -0
  263. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/element.py +0 -0
  264. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/fonts.py +0 -0
  265. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/style.py +0 -0
  266. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/style_builder_base.py +0 -0
  267. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/style_builder_mixed.py +0 -0
  268. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/style_builder_tk.py +0 -0
  269. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/style_builder_ttk.py +0 -0
  270. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/style_resolver.py +0 -0
  271. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/theme.py +0 -0
  272. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/theme_provider.py +0 -0
  273. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/themes/__init__.py +0 -0
  274. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/tk_patch.py +0 -0
  275. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/token_maps.py +0 -0
  276. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/types.py +0 -0
  277. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/typography.py +0 -0
  278. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/style/utility.py +0 -0
  279. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/types.py +0 -0
  280. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/validation/__init__.py +0 -0
  281. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/validation/types.py +0 -0
  282. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/validation/validation_result.py +0 -0
  283. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/validation/validation_rules.py +0 -0
  284. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/__init__.py +0 -0
  285. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/__init__.py +0 -0
  286. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/app_config.py +0 -0
  287. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/choices.py +0 -0
  288. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/context.py +0 -0
  289. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/events.py +0 -0
  290. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/field_mixin.py +0 -0
  291. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/icon_image_props.py +0 -0
  292. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/image_binding.py +0 -0
  293. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/kwargs.py +0 -0
  294. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/navmodel.py +0 -0
  295. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/options.py +0 -0
  296. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/selection_group.py +0 -0
  297. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/window_controls.py +0 -0
  298. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_core/window_menu.py +0 -0
  299. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/__init__.py +0 -0
  300. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/_internal/__init__.py +0 -0
  301. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/_internal/wrapper_base.py +0 -0
  302. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/_parts/__init__.py +0 -0
  303. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/_parts/numberentry_part.py +0 -0
  304. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/_parts/spinnerentry_part.py +0 -0
  305. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/_parts/textentry_part.py +0 -0
  306. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/__init__.py +0 -0
  307. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/_dateutils.py +0 -0
  308. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/_image_fit.py +0 -0
  309. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/accordion.py +0 -0
  310. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/avatar.py +0 -0
  311. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/buttongroup.py +0 -0
  312. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/calendar.py +0 -0
  313. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/carousel.py +0 -0
  314. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/chart.py +0 -0
  315. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/compositeframe.py +0 -0
  316. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/contextmenu.py +0 -0
  317. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/dateentry.py +0 -0
  318. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/dropdownbutton.py +0 -0
  319. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/expander.py +0 -0
  320. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/field.py +0 -0
  321. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/form.py +0 -0
  322. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/gallery.py +0 -0
  323. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/list/__init__.py +0 -0
  324. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/list/listitem.py +0 -0
  325. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/list/listview.py +0 -0
  326. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/menu/__init__.py +0 -0
  327. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/menu/model.py +0 -0
  328. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/menu/render_native.py +0 -0
  329. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/menu/render_themed.py +0 -0
  330. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/meter.py +0 -0
  331. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/numericentry.py +0 -0
  332. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/pagestack.py +0 -0
  333. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/passwordentry.py +0 -0
  334. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/pathentry.py +0 -0
  335. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/picture.py +0 -0
  336. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/radiogroup.py +0 -0
  337. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/scrolledtext.py +0 -0
  338. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/scrolledtext.pyi +0 -0
  339. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/scrollview.py +0 -0
  340. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/selectbox.py +0 -0
  341. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/__init__.py +0 -0
  342. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/content_host.py +0 -0
  343. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/layout.py +0 -0
  344. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/nav_panel.py +0 -0
  345. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/providers.py +0 -0
  346. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/rail.py +0 -0
  347. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/shell.py +0 -0
  348. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/workspace.py +0 -0
  349. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/sidenav/__init__.py +0 -0
  350. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/sidenav/header.py +0 -0
  351. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/sidenav/separator.py +0 -0
  352. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/slider/__init__.py +0 -0
  353. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/slider/_shared.py +0 -0
  354. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/slider/rangeslider.py +0 -0
  355. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/slider/slider.py +0 -0
  356. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/spinnerentry.py +0 -0
  357. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tableview/__init__.py +0 -0
  358. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tableview/tableview.py +0 -0
  359. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tableview/types.py +0 -0
  360. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tabs/__init__.py +0 -0
  361. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tabs/tabitem.py +0 -0
  362. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tabs/tabs.py +0 -0
  363. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tabs/tabview.py +0 -0
  364. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/__init__.py +0 -0
  365. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/change.py +0 -0
  366. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/codeeditor.py +0 -0
  367. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/core.py +0 -0
  368. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/decoration.py +0 -0
  369. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/diff.py +0 -0
  370. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +0 -0
  371. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +0 -0
  372. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +0 -0
  373. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +0 -0
  374. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +0 -0
  375. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +0 -0
  376. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/filter.py +0 -0
  377. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/redirector.py +0 -0
  378. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/search_overlay.py +0 -0
  379. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/sidebar.py +0 -0
  380. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/style_registry.py +0 -0
  381. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/textarea.py +0 -0
  382. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/undo.py +0 -0
  383. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textentry.py +0 -0
  384. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/timeentry.py +0 -0
  385. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/toast.py +0 -0
  386. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/toast_stack.py +0 -0
  387. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/togglegroup.py +0 -0
  388. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/toolbar.py +0 -0
  389. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tooltip.py +0 -0
  390. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tree/__init__.py +0 -0
  391. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tree/source_binding.py +0 -0
  392. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tree/treeitem.py +0 -0
  393. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tree/treenode.py +0 -0
  394. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tree/treeview.py +0 -0
  395. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/__init__.py +0 -0
  396. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/configure_mixin.py +0 -0
  397. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/entry_mixin.py +0 -0
  398. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/font_mixin.py +0 -0
  399. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/icon_mixin.py +0 -0
  400. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/localization_mixin.py +0 -0
  401. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/signal_mixin.py +0 -0
  402. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/validation_mixin.py +0 -0
  403. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/__init__.py +0 -0
  404. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/_menubutton.py +0 -0
  405. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/badge.py +0 -0
  406. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/button.py +0 -0
  407. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/card.py +0 -0
  408. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/checkbutton.py +0 -0
  409. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/checktoggle.py +0 -0
  410. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/combobox.py +0 -0
  411. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/entry.py +0 -0
  412. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/flexframe.py +0 -0
  413. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/frame.py +0 -0
  414. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/gridframe.py +0 -0
  415. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/label.py +0 -0
  416. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/labelframe.py +0 -0
  417. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/optionmenu.py +0 -0
  418. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/packframe.py +0 -0
  419. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/panedwindow.py +0 -0
  420. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/progressbar.py +0 -0
  421. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/radiobutton.py +0 -0
  422. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/radiotoggle.py +0 -0
  423. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/scrollbar.py +0 -0
  424. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/separator.py +0 -0
  425. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/sizegrip.py +0 -0
  426. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/spinbox.py +0 -0
  427. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/switch.py +0 -0
  428. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/treeview.py +0 -0
  429. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/app.py +0 -0
  430. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/avatar.py +0 -0
  431. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/boolean_controls.py +0 -0
  432. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/button.py +0 -0
  433. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/buttongroup.py +0 -0
  434. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/calendar.py +0 -0
  435. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/carousel.py +0 -0
  436. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/chart.py +0 -0
  437. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/codeeditor.py +0 -0
  438. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/contextmenu.py +0 -0
  439. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/datatable.py +0 -0
  440. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/datefield.py +0 -0
  441. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/divider.py +0 -0
  442. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/form.py +0 -0
  443. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/gallery.py +0 -0
  444. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/gauge.py +0 -0
  445. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/label.py +0 -0
  446. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/listview.py +0 -0
  447. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/menubutton.py +0 -0
  448. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/numberfield.py +0 -0
  449. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/passwordfield.py +0 -0
  450. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/pathfield.py +0 -0
  451. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/picture.py +0 -0
  452. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/progressbar.py +0 -0
  453. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/radio_variants.py +0 -0
  454. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/radiogroup.py +0 -0
  455. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/scrollbar.py +0 -0
  456. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/scrollview.py +0 -0
  457. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/select.py +0 -0
  458. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/selectbutton.py +0 -0
  459. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/sidebar_toggle.py +0 -0
  460. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/sizegrip.py +0 -0
  461. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/slider.py +0 -0
  462. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/spinbox.py +0 -0
  463. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/spinnerfield.py +0 -0
  464. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/splash.py +0 -0
  465. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/stacks.py +0 -0
  466. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/statusbar.py +0 -0
  467. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/textarea.py +0 -0
  468. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/textfield.py +0 -0
  469. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/theme_toggle.py +0 -0
  470. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/timefield.py +0 -0
  471. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/toast.py +0 -0
  472. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/togglegroup.py +0 -0
  473. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/toolbar.py +0 -0
  474. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/tooltip.py +0 -0
  475. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/tree.py +0 -0
  476. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/types.py +0 -0
  477. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack/widgets/window.py +0 -0
  478. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack.egg-info/SOURCES.txt +0 -0
  479. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack.egg-info/dependency_links.txt +0 -0
  480. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack.egg-info/entry_points.txt +0 -0
  481. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack.egg-info/requires.txt +0 -0
  482. {bootstack-0.3.0 → bootstack-0.3.1}/src/bootstack.egg-info/top_level.txt +0 -0
  483. {bootstack-0.3.0 → bootstack-0.3.1}/tests/test_dialog_anchor.py +0 -0
  484. {bootstack-0.3.0 → bootstack-0.3.1}/tests/test_events_doc_coverage.py +0 -0
  485. {bootstack-0.3.0 → bootstack-0.3.1}/tests/test_hot_reload.py +0 -0
  486. {bootstack-0.3.0 → bootstack-0.3.1}/tests/test_images.py +0 -0
  487. {bootstack-0.3.0 → bootstack-0.3.1}/tests/test_images_gui.py +0 -0
  488. {bootstack-0.3.0 → bootstack-0.3.1}/tests/test_intl_format.py +0 -0
  489. {bootstack-0.3.0 → bootstack-0.3.1}/tests/test_navmodel.py +0 -0
  490. {bootstack-0.3.0 → bootstack-0.3.1}/tests/test_on_color_contrast.py +0 -0
  491. {bootstack-0.3.0 → bootstack-0.3.1}/tests/test_public_surface.py +0 -0
  492. {bootstack-0.3.0 → bootstack-0.3.1}/tests/test_store.py +0 -0
  493. {bootstack-0.3.0 → bootstack-0.3.1}/tests/test_theme_from_existing.py +0 -0
  494. {bootstack-0.3.0 → bootstack-0.3.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.3.0
3
+ Version: 0.3.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.3.0"
7
+ version = "0.3.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.3.0"
117
+ current_version = "0.3.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}",
@@ -16,7 +16,8 @@ from bootstack.widgets._impl.primitives import Frame
16
16
  from bootstack.constants import BOTH, YES
17
17
  from bootstack.events import Subscription
18
18
  from bootstack.dialogs._impl.dialog import (
19
- DIALOG_RESULT, Dialog, DialogButton, emit_dialog_result, result_target,
19
+ DIALOG_RESULT, Dialog, DialogButton, capture_grab, emit_dialog_result, restore_grab,
20
+ result_target,
20
21
  )
21
22
  from bootstack._runtime.window_utilities import AnchorPoint
22
23
  from bootstack.widgets._impl.composites.calendar import Calendar
@@ -78,6 +79,22 @@ class _ChromeDialog(Dialog):
78
79
  self._build_content()
79
80
  self._build_footer()
80
81
 
82
+ # This override duplicates `Dialog.show()` rather than extending it, so
83
+ # the initial-focus resolution has to be repeated here — see
84
+ # `Dialog._focus_when_mapped` (issue #439).
85
+ #
86
+ # This reaches only RANGE mode. Single-date mode commits the moment a
87
+ # date is clicked and so builds no footer buttons (see `__init__`),
88
+ # leaving `_default_button` None; nothing sets `_focus_target` for the
89
+ # raw calendar content either. So `ask_date()` still opens with focus on
90
+ # the toplevel itself, exactly as before — measured, not assumed. Giving
91
+ # the single-date calendar a real focus target is a separate change,
92
+ # since it would put a focus ring on a day cell and make the arrow keys
93
+ # move the selection.
94
+ focus_target = self._focus_target or self._default_button
95
+ if focus_target is not None:
96
+ self._focus_when_mapped(focus_target)
97
+
81
98
  self._position_dialog(
82
99
  position=position,
83
100
  anchor_to=anchor_to,
@@ -110,9 +127,17 @@ class _ChromeDialog(Dialog):
110
127
  if modal:
111
128
  # transient() is already called in Dialog._create_toplevel()
112
129
  # Don't call it again here as it breaks mica effect on Windows
130
+ previous_grab = None
113
131
  if self._mode == "modal":
132
+ # Remember who held the grab, and how, so it can be handed back
133
+ # — a nested modal must not leave its opener non-modal (#440),
134
+ # nor narrow an app-modal window to a local grab.
135
+ previous_grab = capture_grab(self._toplevel)
114
136
  self._toplevel.grab_set()
115
- self._master.wait_window(self._toplevel)
137
+ try:
138
+ self._master.wait_window(self._toplevel)
139
+ finally:
140
+ restore_grab(previous_grab)
116
141
 
117
142
  def _on_focus_out(self, event: tkinter.Event):
118
143
  if self._suppress_focus_out:
@@ -182,6 +182,187 @@ def emit_dialog_result(target: Optional[tkinter.Misc], payload: dict) -> None:
182
182
 
183
183
  # --- Dialog ----------------------------------------------------------------
184
184
 
185
+ def capture_grab(widget: Any) -> tuple[Any, str | None] | None:
186
+ """Record who holds the modal grab and HOW, for `restore_grab` to hand back.
187
+
188
+ Returns `None` when nothing holds the grab — the outermost case, which needs
189
+ no restore. Otherwise returns the holder paired with its grab KIND.
190
+
191
+ ⚠ Call this BEFORE taking the grab. Once another window grabs, the previous
192
+ holder's `grab_status()` reads `None`, so a kind read afterwards is always
193
+ wrong — measured, and the reason this pairing exists as one function rather
194
+ than two steps a caller has to sequence correctly.
195
+
196
+ The kind matters because Tk has two: `bs.Window(modal="app")` takes a GLOBAL
197
+ grab, and restoring that as a local one silently narrows the window's
198
+ modality (it would block only this application). Reading the kind back from
199
+ Tk rather than assuming it keeps this correct on every window system without
200
+ a platform branch — whatever Tk reported, we hand back.
201
+ """
202
+ holder = widget.grab_current()
203
+ if holder is None:
204
+ return None
205
+ try:
206
+ kind = holder.grab_status()
207
+ except (AttributeError, tkinter.TclError):
208
+ # Fall back to the narrower grab rather than guessing global.
209
+ kind = "local"
210
+ return (holder, kind)
211
+
212
+
213
+ def restore_grab(previous: tuple[Any, str | None] | None) -> None:
214
+ """Hand the modal grab back to whatever held it before this dialog took it.
215
+
216
+ Tk releases a grab when the window holding it is destroyed, but it does NOT
217
+ restore the grab that window displaced. So a modal opened from inside
218
+ another modal — `bs.alert()` from a dialog button command, or
219
+ `QueryDialog._on_submit` — took the grab over and then dropped it on the
220
+ floor when it closed. The OUTER dialog was left on screen and still
221
+ blocking its caller inside `show()`, yet holding no grab at all: the user
222
+ could click straight back into the main window and drive the app
223
+ underneath it, against a dialog that was modal in appearance only
224
+ (issue #440).
225
+
226
+ Pass the token `capture_grab()` returned. `None` means nothing held the
227
+ grab, which is the outermost case and needs no restore.
228
+
229
+ A failure here is deliberately swallowed. This runs on a teardown path,
230
+ where the previous holder may itself have been destroyed while the inner
231
+ dialog was up, or the whole interpreter may be going down — and a dialog
232
+ that has already closed must not raise on its way out. It is LOGGED rather
233
+ than passed over in silence, because a failed restore is the very defect
234
+ this function exists to prevent (#440): the outer dialog stays on screen
235
+ holding nothing.
236
+
237
+ ⚠ A global restore can fail where a local one cannot — `grab set -global`
238
+ is the call Tk's viewability rule guards, and on X11 it can also lose to
239
+ another client. That is the one way this is riskier than always restoring
240
+ a local grab, so a failed global restore DEGRADES TO LOCAL rather than to
241
+ nothing: modal within the application is imperfect, but it is not the #440
242
+ symptom.
243
+ """
244
+ if previous is None:
245
+ return
246
+ holder, kind = previous
247
+ try:
248
+ if not holder.winfo_exists():
249
+ return
250
+ if kind == "global":
251
+ try:
252
+ holder.grab_set_global()
253
+ except tkinter.TclError:
254
+ _log_grab_failure("could not restore a global grab; falling back to local")
255
+ holder.grab_set()
256
+ else:
257
+ holder.grab_set()
258
+ except (AttributeError, tkinter.TclError):
259
+ _log_grab_failure("could not restore the previous dialog's grab")
260
+
261
+
262
+ def _log_grab_failure(message: str) -> None:
263
+ """Report a grab failure without ever raising from a teardown path."""
264
+ from bootstack._runtime.utility import debug_log_exception
265
+
266
+ debug_log_exception(message)
267
+
268
+
269
+ # Bindtags whose widgets treat Enter as CONTENT rather than as a command.
270
+ # `Text` is Tk's multi-line text class, which `TextArea` and `CodeEditor` are
271
+ # both built on — naming the class covers them, and anything else Text-backed,
272
+ # without listing widgets.
273
+ _ENTER_IS_CONTENT = frozenset({"Text"})
274
+
275
+
276
+ def _key_was_consumed(widget: Any, keysym: str) -> bool:
277
+ """Did `widget` already answer this Enter key by the time it reached us?
278
+
279
+ A dialog binds Return on its TOPLEVEL so the default button can be pressed
280
+ from an input field. The toplevel is last in the bindtag walk, so by the
281
+ time that binding runs a widget-level or class-level handler has already
282
+ had the key — and firing the default button on top of it either invokes the
283
+ same button twice or runs the default button's command over the one the
284
+ user actually pressed.
285
+
286
+ Two kinds of widget answer Enter, and the rule names the intent rather than
287
+ inferring it:
288
+
289
+ * a **button** invokes. bootstack installs `TButton <Key-Return>` ->
290
+ `button_default_binding` at app construction (`_runtime/app.py:151`).
291
+ * a **multi-line text widget** inserts a newline. That is issue #441: the
292
+ newline went in and the dialog then closed on top of it, because the
293
+ guard recognized only buttons.
294
+
295
+ In both cases a DISABLED widget is the exception: its class binding runs
296
+ but does nothing — `invoke` is a no-op on a disabled button, and
297
+ `tk::TextInsert` returns early on a disabled text (both measured,
298
+ `development/probe_441_key_already_handled.py`). Nothing answered the key,
299
+ so the default button should still get it. Standing down there would leave
300
+ the keyboard dead for the whole dialog.
301
+
302
+ ⚠ THE KEY MATTERS, NOT ONLY THE WIDGET — which is why `keysym` is required
303
+ rather than defaulted. The two Enter keys are equivalent to a button and are
304
+ NOT equivalent to a text widget. Measured against the live binding table:
305
+
306
+ TButton <Key-Return> -> button_default_binding
307
+ TButton <Key-KP_Enter> -> button_default_binding (both, app.py:150)
308
+ Text <Key-Return> -> tk::TextInsert
309
+ Text <Key-KP_Enter> -> '# nothing'
310
+
311
+ Tk binds `Text <KP_Enter>` to the literal script `# nothing`
312
+ (`tk8.6/text.tcl:308`, unconditional — it is in the block that stops the
313
+ generic `<Key>` binding inserting `%A`, which for that key is `\r` rather
314
+ than `\n`). So wherever the keypad key arrives as its own keysym a text
315
+ widget inserts NOTHING, and reporting it consumed would leave that key dead
316
+ for the whole dialog. Windows never reaches this: it FOLDS the keypad key
317
+ into `Return` (see the binding site below), so `keysym` is `Return` there
318
+ and the text branch behaves as it always has. X11 reports `KP_Enter`
319
+ separately and is the case this scoping exists for. Aqua is unmeasured and
320
+ needs no measurement — both of its possible answers are handled.
321
+
322
+ ⚠ The text branch tests `keysym != "KP_Enter"` rather than
323
+ `keysym == "Return"`, deliberately. An unrecognized keysym then reads as
324
+ CONSUMED, which is the conservative answer for a text widget: standing down
325
+ wrongly costs a dead key, while firing wrongly costs #441 itself — the
326
+ dialog closing on top of a newline the user just typed. The button branch
327
+ has no such asymmetry, both keys invoking there.
328
+
329
+ ⚠ INTERROGATING THE BINDINGS INSTEAD WAS TRIED AND IS WRONG — do not
330
+ re-propose it as the "general" rule. Asking whether any bindtag carries a
331
+ real binding for the key looks principled and misclassifies the control:
332
+ bootstack's own `TextField` binds `<Return>` as an instance binding to emit
333
+ its `submit` event, so a plain entry reads as "already handled" and
334
+ `ask_string()` stops submitting. Tk also binds `TEntry <Return>` to the
335
+ literal script `# nothing`, so a non-empty script does not mean the key was
336
+ handled either. Binding inspection cannot separate binds-to-notify from
337
+ binds-to-consume; the class name is where that intent is recorded.
338
+ """
339
+ try:
340
+ tags = {str(tag) for tag in widget.bindtags()}
341
+ except (AttributeError, tkinter.TclError):
342
+ # Nothing identifiable answered the key, so let the default button run
343
+ # rather than silently swallowing Enter for the whole dialog.
344
+ return False
345
+
346
+ if "TButton" in tags:
347
+ try:
348
+ return not widget.instate(["disabled"])
349
+ except (AttributeError, tkinter.TclError):
350
+ return True
351
+
352
+ if tags & _ENTER_IS_CONTENT:
353
+ if keysym == "KP_Enter":
354
+ # `Text <KP_Enter>` is the literal script `# nothing`, so nothing
355
+ # was inserted whatever the widget's state — the default button
356
+ # should still get the key rather than it dying here.
357
+ return False
358
+ try:
359
+ return str(widget.cget("state")) == "normal"
360
+ except (AttributeError, tkinter.TclError):
361
+ return True
362
+
363
+ return False
364
+
365
+
185
366
  class Dialog:
186
367
  """A flexible dialog window using the builder pattern.
187
368
 
@@ -283,6 +464,13 @@ class Dialog:
283
464
  self._footer: _Frame | None = None
284
465
  self._border_frame: _Frame | None = None
285
466
 
467
+ # Who gets focus when the window comes up. `_focus_target` is the
468
+ # override a content builder can claim (QueryDialog's entry); the
469
+ # default button is the fallback. Resolved once in `show()`, after
470
+ # content is built, so the two cannot race — see `_focus_when_mapped`.
471
+ self._default_button: _Button | None = None
472
+ self._focus_target: Widget | None = None
473
+
286
474
  self.result: Any = None
287
475
 
288
476
  # --------------------------------------------------------------- API
@@ -333,6 +521,16 @@ class Dialog:
333
521
  self._create_toplevel(modal=modal)
334
522
  self._build_footer()
335
523
  self._build_content()
524
+
525
+ # Resolve initial focus once, now that both halves exist. A content
526
+ # widget that claimed `_focus_target` wins; otherwise the default
527
+ # button takes it, which is what `DialogButton.default` promises.
528
+ # Deferred to <Map> because the window is still withdrawn here and
529
+ # `focus_set()` is a silent no-op until it is not (issue #439).
530
+ focus_target = self._focus_target or self._default_button
531
+ if focus_target is not None:
532
+ self._focus_when_mapped(focus_target)
533
+
336
534
  self._position_dialog(
337
535
  position=position,
338
536
  anchor_to=_resolve_anchor_target(anchor_to),
@@ -353,9 +551,18 @@ class Dialog:
353
551
  # sheet window class; calling grab_set on top of that is fine
354
552
  # but unnecessary. Plain modal mode still uses grab to block
355
553
  # interaction with the parent on platforms without a sheet.
554
+ previous_grab = None
356
555
  if self._mode in ("modal", "sheet"):
556
+ # Remember who held the grab, and how, so it can be handed back
557
+ # — a nested modal must not leave its opener non-modal (#440),
558
+ # nor narrow an app-modal window to a local grab. Captured
559
+ # before grab_set(), which is the only order that works.
560
+ previous_grab = capture_grab(self._toplevel)
357
561
  self._toplevel.grab_set()
358
- self._master.wait_window(self._toplevel)
562
+ try:
563
+ self._master.wait_window(self._toplevel)
564
+ finally:
565
+ restore_grab(previous_grab)
359
566
 
360
567
  @property
361
568
  def toplevel(self) -> Toplevel | None:
@@ -541,7 +748,10 @@ class Dialog:
541
748
  return
542
749
 
543
750
  if default_button is not None:
544
- default_button.focus_set()
751
+ # Recorded, not focused here: `show()` resolves focus after the
752
+ # content is built, so a content widget asking for initial focus
753
+ # is not overruled by build order (issue #439).
754
+ self._default_button = default_button
545
755
  top = self._toplevel
546
756
 
547
757
  def press_default(event: Any, b: Any = default_button) -> None:
@@ -563,18 +773,46 @@ class Dialog:
563
773
  leave the keyboard dead for the whole dialog — which is what a
564
774
  content button that greys itself out on click would cause.
565
775
 
776
+ A multi-line text widget answers Enter too, by inserting a
777
+ newline, and standing down for it is issue #441 — the newline
778
+ went in and the dialog closed on top of it. `_key_was_consumed`
779
+ owns that decision for both kinds of widget.
780
+
781
+ It is handed the KEYSYM as well as the widget because a text
782
+ widget answers only the main Enter key: Tk binds its keypad
783
+ twin to a no-op script, so standing down for that one would
784
+ leave it dead. A button answers both.
785
+
566
786
  The remaining case is the one this binding exists for — focus
567
787
  in an entry, as in `QueryDialog`, where nothing else would
568
788
  answer the key.
569
789
  """
570
- pressed = event.widget
571
- if "TButton" in pressed.bindtags() and not pressed.instate(["disabled"]):
790
+ if _key_was_consumed(event.widget, event.keysym):
572
791
  return
573
792
  b.invoke()
574
793
 
575
- # The keypad Enter key reports `KP_Enter`, a separate keysym from
576
- # `Return` on Windows, X11 and Aqua alike, so it needs its own
577
- # binding to reach the default button from an input field.
794
+ # The keypad Enter key needs its own binding to reach the default
795
+ # button from an input field -- but only where the window system
796
+ # reports it separately, which is NOT everywhere. Measured with a
797
+ # real keypress, alternating against the main Enter key as a
798
+ # control (`development/probe_441_kp_enter_platform.py`):
799
+ #
800
+ # * Windows FOLDS it into `Return` -- keysym `Return`, keycode 13,
801
+ # char `\r`, byte-identical to the main Enter key -- so this
802
+ # binding never fires there. Synthesis is no help either: win32
803
+ # cannot map the keysym back to a keycode and `event_generate`
804
+ # delivers keysym `??` with keycode 0, matching no binding at
805
+ # all. Both routes to this path are closed on Windows, so no
806
+ # test there can cover it.
807
+ # * X11 reports `KP_Enter` as a distinct keysym. That is the case
808
+ # this binding exists for.
809
+ # * Aqua is unmeasured.
810
+ #
811
+ # Binding both is harmless where the two coincide. Where they do
812
+ # NOT, the keysym is threaded into `_key_was_consumed` so a text
813
+ # widget stands down only for the key it actually inserts for --
814
+ # Tk's `Text <KP_Enter>` is a no-op script, so treating the two
815
+ # alike would kill the keypad key inside a dialog TextArea.
578
816
  for key in ("<Return>", "<KP_Enter>"):
579
817
  self._toplevel.bind(key, press_default)
580
818
 
@@ -583,6 +821,54 @@ class Dialog:
583
821
  else:
584
822
  self._toplevel.bind("<Escape>", lambda e: self._toplevel.destroy())
585
823
 
824
+ @staticmethod
825
+ def _focus_when_mapped(widget: Widget) -> None:
826
+ """Focus `widget` once it is actually mapped.
827
+
828
+ `focus_set()` is a SILENT no-op while the widget's toplevel is
829
+ withdrawn: `TkSetFocusWin` walks the ancestry and returns without
830
+ setting anything, reporting nothing. Footer buttons are built from
831
+ `_build_footer`, which runs while `_create_toplevel` still has the
832
+ window withdrawn, so focusing the default button there never took —
833
+ `focus_lastfor()` kept naming the toplevel, leaving keyboard users
834
+ with no focus ring and a Tab order starting from nowhere, in a dialog
835
+ documented as focusing its default button (issue #439).
836
+
837
+ Waiting for the widget's own `<Map>` states that precondition instead
838
+ of guessing a delay. Measured on Windows
839
+ (`development/probe_439_focus_timing.py`): the button is still
840
+ unmapped once `deiconify()` AND `update_idletasks()` have both
841
+ returned, so neither is a usable barrier. Asking after the toplevel is
842
+ deiconified happens to work — Tk defers a request made against a
843
+ mapped toplevel — but that deferral is a Tk implementation detail and
844
+ window managers map asynchronously, so the explicit wait is what
845
+ travels off this box.
846
+
847
+ Binding on the widget rather than scheduling on the root is
848
+ deliberate: a `<Map>` binding is torn down with the widget it is on,
849
+ so a dialog closed before it ever maps leaves nothing pending. An
850
+ `after` timer would outlive the widget and fire as an orphan.
851
+ """
852
+ if widget.winfo_ismapped():
853
+ widget.focus_set()
854
+ return
855
+
856
+ def on_map(_event: Any = None) -> None:
857
+ # One-shot: the default button should not reclaim focus every time
858
+ # the dialog is unmapped and remapped (minimize/restore), which
859
+ # would yank focus off whatever the user had tabbed to.
860
+ try:
861
+ widget.unbind("<Map>", bind_id)
862
+ except tkinter.TclError:
863
+ pass
864
+ try:
865
+ if widget.winfo_exists():
866
+ widget.focus_set()
867
+ except tkinter.TclError:
868
+ pass
869
+
870
+ bind_id = widget.bind("<Map>", on_map, add="+")
871
+
586
872
  def _position_dialog(
587
873
  self,
588
874
  position: Optional[Tuple[int, int]] = None,
@@ -161,16 +161,18 @@ class QueryDialog:
161
161
  # anyway: focus goes to `entry.entry_widget`, and the composite frame is
162
162
  # not in that widget's bindtags, so Tk would never reach it.
163
163
 
164
- # Focus the entry field
165
- def _focus():
166
- if hasattr(entry, 'entry_widget'):
167
- entry.entry_widget.focus_set()
168
- else:
169
- entry.focus_set()
170
-
171
- _focus()
172
- # Ensure focus after dialog buttons set their own focus
173
- entry.after_idle(_focus)
164
+ # Claim initial focus for the entry, so the user can type straight
165
+ # away. `Dialog` applies this once the window is mapped, in preference
166
+ # to the default button.
167
+ #
168
+ # This used to be a bare `focus_set()` plus an `after_idle` repeat to
169
+ # "ensure focus after dialog buttons set their own focus". Both ran
170
+ # while the toplevel was still withdrawn, where `focus_set()` is a
171
+ # silent no-op, so neither ever took and the entry was never actually
172
+ # focused measured in `development/probe_439_default_button_focus.py`
173
+ # against pre-fix code, where focus sat on the toplevel. Handing the
174
+ # target to `Dialog` replaces the race with an ordering (issue #439).
175
+ self._dialog._focus_target = getattr(entry, "entry_widget", entry)
174
176
 
175
177
  frame.pack(fill=X, expand=True)
176
178
  self._entry_widget = entry
@@ -492,7 +492,7 @@ class PublicWidgetBase:
492
492
  if placement.method == "flex":
493
493
  index = kwargs.pop("index", placement.index)
494
494
  if kwargs:
495
- _reject_legacy_child_kwargs(kwargs, type(self).__name__)
495
+ _reject_legacy_child_kwargs(kwargs, type(self).__name__, "flex child")
496
496
  _expand_margin(kwargs)
497
497
  options.update(_flex_child_opts(self, kwargs))
498
498
  master.add_child(self._internal, options, index=index)
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import tkinter
4
- from typing import Any
4
+ from typing import Any, Literal
5
5
 
6
6
  PACK_KEYS = frozenset({
7
7
  "side", "fill", "expand", "anchor",
@@ -218,16 +218,73 @@ class PublicContainer(PublicWidgetBase):
218
218
  # --- Flex engine (Row / Column) --------------------------------------------
219
219
 
220
220
 
221
- def _reject_legacy_child_kwargs(layout_kw: dict, where: str) -> None:
222
- """Raise on legacy pack/grid placement kwargs passed to a flex child."""
221
+ # Per-child keys a GRID CELL honors. `grow=` is deliberately ABSENT: Grid and
222
+ # the page/pane containers filter their child options to GRID_KEYS, so a `grow=`
223
+ # written on one of their children is silently dropped. `sticky`/`in_` are not
224
+ # user-facing — `sticky` is a legacy key the guard below rejects, and the cell
225
+ # derives it from `horizontal`/`vertical` instead.
226
+ GRID_CHILD_KEYS = (GRID_KEYS | {"horizontal", "vertical"}) - {"sticky", "in_"}
227
+
228
+ _ALIGN_ADVICE = (
229
+ "horizontal= takes left/center/right/stretch and vertical= takes "
230
+ "top/center/bottom/stretch (for example horizontal=\"stretch\")"
231
+ )
232
+
233
+ # The remedy depends on HOW the container places its children, and naming the
234
+ # wrong one is the defect this message exists to fix (issue #426). A flex
235
+ # container honors `grow=` on the child; a grid cell drops it, so recommending
236
+ # it there would send the user to a kwarg that does nothing — the same silent
237
+ # no-op #426 was filed about, just one step further along.
238
+ FLEX_CHILD_ADVICE = (
239
+ "Use grow= to claim leftover space along the stacking axis, or "
240
+ "horizontal=/vertical= to align or stretch across it — " + _ALIGN_ADVICE
241
+ )
242
+
243
+ # Container-level options are named WITHOUT a trailing `=` on purpose: the `X=`
244
+ # spelling is reserved for per-child remedies, which is what the regression test
245
+ # checks must be real keys.
246
+ GRID_CHILD_ADVICE = (
247
+ "Use horizontal=/vertical= to align or stretch the child inside its cell — "
248
+ + _ALIGN_ADVICE
249
+ + ". Leftover space is claimed by weighting the row or column on the "
250
+ "container (its columns/rows arguments), not by a kwarg on the child"
251
+ )
252
+
253
+ # The kind names the child's PLACEMENT, not its parent class: the dual-mode
254
+ # containers (Card/GroupBox/Expander/AccordionSection) are one or the other
255
+ # depending on their `layout=`, and the page/pane containers are grid cells
256
+ # without a `Grid` anywhere in sight.
257
+ ChildKind = Literal["flex child", "grid cell"]
258
+
259
+ _CHILD_ADVICE: dict[str, str] = {
260
+ "flex child": FLEX_CHILD_ADVICE,
261
+ "grid cell": GRID_CHILD_ADVICE,
262
+ }
263
+
264
+
265
+ def _reject_legacy_child_kwargs(layout_kw: dict, where: str, kind: ChildKind) -> None:
266
+ """Raise on legacy pack/grid placement kwargs passed to a laid-out child.
267
+
268
+ Args:
269
+ layout_kw: the split-out layout kwargs to inspect.
270
+ where: the call site named in the message.
271
+ kind: how this container places the child — `'flex child'` or
272
+ `'grid cell'`. It selects the remedy, and the two differ: a grid
273
+ cell drops `grow=`, so recommending it there is the silent no-op
274
+ issue #426 exists to remove. Required rather than defaulted,
275
+ because a default hands the wrong advice to any caller who forgets
276
+ — which is how four call sites kept the defect through its own fix.
277
+ """
223
278
  bad = _LEGACY_CHILD_KEYS & layout_kw.keys()
224
279
  if bad:
225
280
  from bootstack.errors import BootstackError
226
281
 
282
+ # KeyError on an unknown kind is deliberate: a mistyped kind must fail
283
+ # loudly here rather than quietly pick a remedy for the other engine.
284
+ advice = _CHILD_ADVICE[kind]
227
285
  raise BootstackError(
228
286
  f"{where}: {', '.join(sorted(bad))} is not a valid layout option for "
229
- f"a Row/Column/Grid child. Use grow= / align_self= (and justify_self= "
230
- f"in a Grid) instead — see the layout guide."
287
+ f"a {kind}. {advice} see the layout guide."
231
288
  )
232
289
 
233
290
 
@@ -376,7 +433,7 @@ def place_flex_child(
376
433
  child._placement = Placement("place", frame, options)
377
434
  return
378
435
 
379
- _reject_legacy_child_kwargs(layout_kw, where)
436
+ _reject_legacy_child_kwargs(layout_kw, where, "flex child")
380
437
  _expand_margin(layout_kw)
381
438
  index = layout_kw.get("index")
382
439
  opts = _flex_child_opts(child, layout_kw)
@@ -106,7 +106,7 @@ class Page:
106
106
 
107
107
  def guide_layout(self, child: Any, **layout_kw: Any) -> None:
108
108
  if self._layout == "grid":
109
- _reject_legacy_child_kwargs(layout_kw, "AppShell page")
109
+ _reject_legacy_child_kwargs(layout_kw, "AppShell page", "grid cell")
110
110
  _expand_margin(layout_kw)
111
111
  options = {k: v for k, v in layout_kw.items() if k in GRID_KEYS}
112
112
  h = layout_kw.get("horizontal") or self._horizontal_items
@@ -144,7 +144,7 @@ class Card(PublicContainer):
144
144
 
145
145
  def _merge_layout_options(self, child: Any, layout_kw: dict) -> tuple[str, dict]:
146
146
  # Only reached for grid layout (column/row use the flex path above).
147
- _reject_legacy_child_kwargs(layout_kw, "Card")
147
+ _reject_legacy_child_kwargs(layout_kw, "Card", "grid cell")
148
148
  options = {k: v for k, v in layout_kw.items() if k in GRID_KEYS}
149
149
  h = layout_kw.get("horizontal") or self._horizontal_items
150
150
  v = layout_kw.get("vertical") or self._vertical_items
@@ -166,7 +166,7 @@ class Expander(PublicContainer):
166
166
 
167
167
  def guide_layout(self, child: PublicWidgetBase, **layout_kw: Any) -> None:
168
168
  if self._layout == "grid":
169
- _reject_legacy_child_kwargs(layout_kw, "Expander")
169
+ _reject_legacy_child_kwargs(layout_kw, "Expander", "grid cell")
170
170
  _expand_margin(layout_kw)
171
171
  options = {k: v for k, v in layout_kw.items() if k in GRID_KEYS}
172
172
  h = layout_kw.get("horizontal") or self._horizontal_items
@@ -284,7 +284,7 @@ class AccordionSection:
284
284
 
285
285
  def guide_layout(self, child: PublicWidgetBase, **layout_kw: Any) -> None:
286
286
  if self._layout == "grid":
287
- _reject_legacy_child_kwargs(layout_kw, "AccordionSection")
287
+ _reject_legacy_child_kwargs(layout_kw, "AccordionSection", "grid cell")
288
288
  _expand_margin(layout_kw)
289
289
  options = {k: v for k, v in layout_kw.items() if k in GRID_KEYS}
290
290
  h = layout_kw.get("horizontal") or self._horizontal_items
@@ -111,7 +111,7 @@ class Grid(PublicContainer):
111
111
  return "grid"
112
112
 
113
113
  def _merge_layout_options(self, child: Any, layout_kw: dict) -> tuple[str, dict]:
114
- _reject_legacy_child_kwargs(layout_kw, "Grid")
114
+ _reject_legacy_child_kwargs(layout_kw, "Grid", "grid cell")
115
115
  options = {k: v for k, v in layout_kw.items() if k in GRID_KEYS}
116
116
  # Derive the cell sticky from per-child horizontal/vertical, each falling
117
117
  # back to the container default for its axis.
@@ -136,7 +136,7 @@ class GroupBox(PublicContainer):
136
136
 
137
137
  def _merge_layout_options(self, child: Any, layout_kw: dict) -> tuple[str, dict]:
138
138
  # Only reached for grid layout (column/row use the flex path above).
139
- _reject_legacy_child_kwargs(layout_kw, "GroupBox")
139
+ _reject_legacy_child_kwargs(layout_kw, "GroupBox", "grid cell")
140
140
  options = {k: v for k, v in layout_kw.items() if k in GRID_KEYS}
141
141
  h = layout_kw.get("horizontal") or self._horizontal_items
142
142
  v = layout_kw.get("vertical") or self._vertical_items
@@ -96,7 +96,7 @@ class StackPage:
96
96
 
97
97
  def guide_layout(self, child: PublicWidgetBase, **layout_kw: Any) -> None:
98
98
  if self._layout == "grid":
99
- _reject_legacy_child_kwargs(layout_kw, "StackPage")
99
+ _reject_legacy_child_kwargs(layout_kw, "StackPage", "grid cell")
100
100
  _expand_margin(layout_kw)
101
101
  options = {k: v for k, v in layout_kw.items() if k in GRID_KEYS}
102
102
  h = layout_kw.get("horizontal") or self._horizontal_items
@@ -92,7 +92,7 @@ class SplitPane:
92
92
 
93
93
  def guide_layout(self, child: PublicWidgetBase, **layout_kw: Any) -> None:
94
94
  if self._layout == "grid":
95
- _reject_legacy_child_kwargs(layout_kw, "SplitPane")
95
+ _reject_legacy_child_kwargs(layout_kw, "SplitPane", "grid cell")
96
96
  _expand_margin(layout_kw)
97
97
  options = {k: v for k, v in layout_kw.items() if k in GRID_KEYS}
98
98
  h = layout_kw.get("horizontal") or self._horizontal_items