bootstack 0.2.3__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.2.3/src/bootstack.egg-info → bootstack-0.3.1}/PKG-INFO +1 -1
  2. {bootstack-0.2.3 → bootstack-0.3.1}/pyproject.toml +2 -2
  3. bootstack-0.3.1/src/bootstack/_core/capture.py +449 -0
  4. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dialogs/__init__.py +6 -1
  5. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/datedialog.py +51 -10
  6. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/dialog.py +347 -16
  7. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/formdialog.py +122 -30
  8. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/query.py +41 -29
  9. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/base.py +91 -1
  10. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/container.py +63 -6
  11. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/form.py +17 -3
  12. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/appshell.py +1 -1
  13. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/card.py +1 -1
  14. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/expander.py +2 -2
  15. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/form.py +6 -1
  16. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/grid.py +1 -1
  17. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/groupbox.py +1 -1
  18. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/pagestack.py +1 -1
  19. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/splitview.py +1 -1
  20. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/tabs.py +1 -1
  21. {bootstack-0.2.3 → bootstack-0.3.1/src/bootstack.egg-info}/PKG-INFO +1 -1
  22. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack.egg-info/SOURCES.txt +1 -0
  23. {bootstack-0.2.3 → bootstack-0.3.1}/LICENSE +0 -0
  24. {bootstack-0.2.3 → bootstack-0.3.1}/MANIFEST.in +0 -0
  25. {bootstack-0.2.3 → bootstack-0.3.1}/NOTICE +0 -0
  26. {bootstack-0.2.3 → bootstack-0.3.1}/README.md +0 -0
  27. {bootstack-0.2.3 → bootstack-0.3.1}/setup.cfg +0 -0
  28. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/__init__.py +0 -0
  29. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/__main__.py +0 -0
  30. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/__init__.py +0 -0
  31. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/__init__.py +0 -0
  32. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/after.py +0 -0
  33. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/bind.py +0 -0
  34. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/bindtags.py +0 -0
  35. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/busy.py +0 -0
  36. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/clipboard.py +0 -0
  37. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/focus.py +0 -0
  38. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/grab.py +0 -0
  39. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/grid.py +0 -0
  40. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/localization.py +0 -0
  41. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/pack.py +0 -0
  42. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/place.py +0 -0
  43. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/selection.py +0 -0
  44. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/signals.py +0 -0
  45. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/capabilities/winfo.py +0 -0
  46. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/colorutils.py +0 -0
  47. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/exceptions.py +0 -0
  48. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/images.py +0 -0
  49. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/mixins/__init__.py +0 -0
  50. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/mixins/ttk_state.py +0 -0
  51. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/mixins/widget.py +0 -0
  52. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/paths.py +0 -0
  53. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/publisher.py +0 -0
  54. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_core/variables.py +0 -0
  55. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_runtime/__init__.py +0 -0
  56. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_runtime/app.py +0 -0
  57. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_runtime/base_window.py +0 -0
  58. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_runtime/events.py +0 -0
  59. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_runtime/shortcuts.py +0 -0
  60. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_runtime/tk_patch.py +0 -0
  61. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_runtime/toplevel.py +0 -0
  62. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_runtime/utility.py +0 -0
  63. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_runtime/visual_focus.py +0 -0
  64. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_runtime/wheel.py +0 -0
  65. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/_runtime/window_utilities.py +0 -0
  66. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/__init__.py +0 -0
  67. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/bootstack.ico +0 -0
  68. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/bootstack.png +0 -0
  69. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/__init__.py +0 -0
  70. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/badge-pill.png +0 -0
  71. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/badge-square.png +0 -0
  72. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/button-compact.png +0 -0
  73. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/button-default.png +0 -0
  74. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
  75. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
  76. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
  77. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
  78. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
  79. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
  80. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
  81. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
  82. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
  83. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
  84. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
  85. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
  86. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/card.png +0 -0
  87. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/checkbox-checked.png +0 -0
  88. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  89. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/checkbox-unchecked.png +0 -0
  90. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/field.png +0 -0
  91. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/input-addon-compact.png +0 -0
  92. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/input-addon-default.png +0 -0
  93. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/input-compact.png +0 -0
  94. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/input-default.png +0 -0
  95. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/list-item-separated.png +0 -0
  96. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/list-item.png +0 -0
  97. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/listrow-compact.png +0 -0
  98. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/listrow-default.png +0 -0
  99. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/manifest.toml +0 -0
  100. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/menu-item.png +0 -0
  101. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/navitem-compact.png +0 -0
  102. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/navitem-default.png +0 -0
  103. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/progressbar-h-compact.png +0 -0
  104. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/progressbar-h-default.png +0 -0
  105. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/progressbar-v-compact.png +0 -0
  106. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/progressbar-v-default.png +0 -0
  107. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/radiobutton.png +0 -0
  108. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  109. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/scrollbar-vertical.png +0 -0
  110. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/slider-handle.png +0 -0
  111. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/slider-track-h.png +0 -0
  112. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/slider-track-v.png +0 -0
  113. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/switch-off.png +0 -0
  114. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/switch-on.png +0 -0
  115. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/tab-h.png +0 -0
  116. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/elements/tab-v.png +0 -0
  117. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/icons/bootstrap.ttf +0 -0
  118. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/icons/glyphmap.json +0 -0
  119. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/icons/icon_metrics.json +0 -0
  120. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  121. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +0 -0
  122. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
  123. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +0 -0
  124. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  125. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +0 -0
  126. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  127. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +0 -0
  128. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  129. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +0 -0
  130. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  131. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +0 -0
  132. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  133. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +0 -0
  134. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  135. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +0 -0
  136. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  137. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +0 -0
  138. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  139. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +0 -0
  140. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  141. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +0 -0
  142. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  143. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +0 -0
  144. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  145. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +0 -0
  146. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  147. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +0 -0
  148. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  149. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +0 -0
  150. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  151. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +0 -0
  152. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  153. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +0 -0
  154. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  155. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +0 -0
  156. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  157. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +0 -0
  158. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  159. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +0 -0
  160. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  161. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +0 -0
  162. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  163. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +0 -0
  164. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  165. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +0 -0
  166. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/__init__.py +0 -0
  167. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/__main__.py +0 -0
  168. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/add.py +0 -0
  169. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/appicon.py +0 -0
  170. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/build.py +0 -0
  171. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/config.py +0 -0
  172. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/demo.py +0 -0
  173. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/dev.py +0 -0
  174. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/doctor.py +0 -0
  175. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/icons.py +0 -0
  176. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/promote.py +0 -0
  177. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/pyinstaller.py +0 -0
  178. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/run.py +0 -0
  179. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/start.py +0 -0
  180. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/cli/templates/__init__.py +0 -0
  181. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/clipboard.py +0 -0
  182. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/constants.py +0 -0
  183. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/data/README.md +0 -0
  184. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/data/__init__.py +0 -0
  185. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/data/_observable.py +0 -0
  186. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/data/base.py +0 -0
  187. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/data/file_source.py +0 -0
  188. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/data/memory_source.py +0 -0
  189. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/data/query.py +0 -0
  190. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/data/readers.py +0 -0
  191. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/data/sqlite_source.py +0 -0
  192. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/data/types.py +0 -0
  193. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/data/writers.py +0 -0
  194. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dev/__init__.py +0 -0
  195. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dev/_capture.py +0 -0
  196. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dev/_env.py +0 -0
  197. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dev/_registry.py +0 -0
  198. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dev/_reloader.py +0 -0
  199. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dev/_reset.py +0 -0
  200. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dev/_watcher.py +0 -0
  201. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/__init__.py +0 -0
  202. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/colorchooser.py +0 -0
  203. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/filterdialog.py +0 -0
  204. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/fontdialog.py +0 -0
  205. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/dialogs/_impl/message.py +0 -0
  206. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/errors.py +0 -0
  207. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/events/__init__.py +0 -0
  208. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/events/_event.py +0 -0
  209. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/events/_payloads.py +0 -0
  210. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/events/_subscription.py +0 -0
  211. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/i18n/README.md +0 -0
  212. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/i18n/__init__.py +0 -0
  213. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/i18n/catalog.py +0 -0
  214. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/i18n/intl_format.py +0 -0
  215. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/i18n/msgcat.py +0 -0
  216. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/i18n/specs.py +0 -0
  217. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/images.py +0 -0
  218. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/py.typed +0 -0
  219. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/scheduling/__init__.py +0 -0
  220. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/scheduling/_schedule.py +0 -0
  221. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/shortcuts.py +0 -0
  222. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/signals/README.md +0 -0
  223. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/signals/__init__.py +0 -0
  224. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/signals/integration.py +0 -0
  225. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/signals/signal.py +0 -0
  226. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/signals/types.py +0 -0
  227. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/store.py +0 -0
  228. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/streams/__init__.py +0 -0
  229. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/streams/_stream.py +0 -0
  230. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/__init__.py +0 -0
  231. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/__init__.py +0 -0
  232. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/badge.py +0 -0
  233. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/button.py +0 -0
  234. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/buttongroup.py +0 -0
  235. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/calendar.py +0 -0
  236. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/checkbutton.py +0 -0
  237. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/combobox.py +0 -0
  238. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/contextmenu.py +0 -0
  239. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/entry.py +0 -0
  240. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/expander.py +0 -0
  241. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/field.py +0 -0
  242. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/frame.py +0 -0
  243. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/label.py +0 -0
  244. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/labelframe.py +0 -0
  245. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/listview.py +0 -0
  246. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/menubar.py +0 -0
  247. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/menubutton.py +0 -0
  248. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/panedwindow.py +0 -0
  249. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/progressbar.py +0 -0
  250. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/radiobutton.py +0 -0
  251. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/scale.py +0 -0
  252. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/scrollbar.py +0 -0
  253. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/separator.py +0 -0
  254. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/sidenav.py +0 -0
  255. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/sizegrip.py +0 -0
  256. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/spinbox.py +0 -0
  257. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/switch.py +0 -0
  258. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/tabitem.py +0 -0
  259. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/togglegroup.py +0 -0
  260. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/toolbutton.py +0 -0
  261. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/tooltip.py +0 -0
  262. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/treeview.py +0 -0
  263. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders/utils.py +0 -0
  264. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders_tk/__init__.py +0 -0
  265. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/builders_tk/defaults.py +0 -0
  266. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/element.py +0 -0
  267. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/fonts.py +0 -0
  268. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/style.py +0 -0
  269. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/style_builder_base.py +0 -0
  270. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/style_builder_mixed.py +0 -0
  271. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/style_builder_tk.py +0 -0
  272. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/style_builder_ttk.py +0 -0
  273. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/style_resolver.py +0 -0
  274. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/theme.py +0 -0
  275. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/theme_provider.py +0 -0
  276. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/themes/__init__.py +0 -0
  277. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/tk_patch.py +0 -0
  278. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/token_maps.py +0 -0
  279. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/types.py +0 -0
  280. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/typography.py +0 -0
  281. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/style/utility.py +0 -0
  282. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/types.py +0 -0
  283. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/validation/__init__.py +0 -0
  284. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/validation/types.py +0 -0
  285. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/validation/validation_result.py +0 -0
  286. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/validation/validation_rules.py +0 -0
  287. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/__init__.py +0 -0
  288. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/__init__.py +0 -0
  289. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/app_config.py +0 -0
  290. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/choices.py +0 -0
  291. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/context.py +0 -0
  292. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/events.py +0 -0
  293. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/field_mixin.py +0 -0
  294. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/icon_image_props.py +0 -0
  295. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/image_binding.py +0 -0
  296. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/kwargs.py +0 -0
  297. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/navmodel.py +0 -0
  298. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/options.py +0 -0
  299. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/selection_group.py +0 -0
  300. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/window_controls.py +0 -0
  301. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_core/window_menu.py +0 -0
  302. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/__init__.py +0 -0
  303. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/_internal/__init__.py +0 -0
  304. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/_internal/wrapper_base.py +0 -0
  305. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/_parts/__init__.py +0 -0
  306. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/_parts/numberentry_part.py +0 -0
  307. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/_parts/spinnerentry_part.py +0 -0
  308. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/_parts/textentry_part.py +0 -0
  309. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/__init__.py +0 -0
  310. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/_dateutils.py +0 -0
  311. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/_image_fit.py +0 -0
  312. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/accordion.py +0 -0
  313. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/avatar.py +0 -0
  314. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/buttongroup.py +0 -0
  315. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/calendar.py +0 -0
  316. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/carousel.py +0 -0
  317. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/chart.py +0 -0
  318. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/compositeframe.py +0 -0
  319. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/contextmenu.py +0 -0
  320. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/dateentry.py +0 -0
  321. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/dropdownbutton.py +0 -0
  322. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/expander.py +0 -0
  323. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/field.py +0 -0
  324. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/gallery.py +0 -0
  325. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/list/__init__.py +0 -0
  326. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/list/listitem.py +0 -0
  327. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/list/listview.py +0 -0
  328. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/menu/__init__.py +0 -0
  329. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/menu/model.py +0 -0
  330. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/menu/render_native.py +0 -0
  331. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/menu/render_themed.py +0 -0
  332. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/meter.py +0 -0
  333. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/numericentry.py +0 -0
  334. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/pagestack.py +0 -0
  335. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/passwordentry.py +0 -0
  336. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/pathentry.py +0 -0
  337. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/picture.py +0 -0
  338. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/radiogroup.py +0 -0
  339. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/scrolledtext.py +0 -0
  340. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/scrolledtext.pyi +0 -0
  341. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/scrollview.py +0 -0
  342. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/selectbox.py +0 -0
  343. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/__init__.py +0 -0
  344. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/content_host.py +0 -0
  345. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/layout.py +0 -0
  346. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/nav_panel.py +0 -0
  347. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/providers.py +0 -0
  348. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/rail.py +0 -0
  349. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/shell.py +0 -0
  350. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/shell/workspace.py +0 -0
  351. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/sidenav/__init__.py +0 -0
  352. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/sidenav/header.py +0 -0
  353. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/sidenav/separator.py +0 -0
  354. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/slider/__init__.py +0 -0
  355. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/slider/_shared.py +0 -0
  356. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/slider/rangeslider.py +0 -0
  357. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/slider/slider.py +0 -0
  358. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/spinnerentry.py +0 -0
  359. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tableview/__init__.py +0 -0
  360. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tableview/tableview.py +0 -0
  361. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tableview/types.py +0 -0
  362. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tabs/__init__.py +0 -0
  363. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tabs/tabitem.py +0 -0
  364. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tabs/tabs.py +0 -0
  365. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tabs/tabview.py +0 -0
  366. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/__init__.py +0 -0
  367. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/change.py +0 -0
  368. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/codeeditor.py +0 -0
  369. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/core.py +0 -0
  370. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/decoration.py +0 -0
  371. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/diff.py +0 -0
  372. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +0 -0
  373. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +0 -0
  374. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +0 -0
  375. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +0 -0
  376. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +0 -0
  377. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +0 -0
  378. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/filter.py +0 -0
  379. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/redirector.py +0 -0
  380. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/search_overlay.py +0 -0
  381. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/sidebar.py +0 -0
  382. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/style_registry.py +0 -0
  383. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/textarea.py +0 -0
  384. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textarea/undo.py +0 -0
  385. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/textentry.py +0 -0
  386. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/timeentry.py +0 -0
  387. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/toast.py +0 -0
  388. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/toast_stack.py +0 -0
  389. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/togglegroup.py +0 -0
  390. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/toolbar.py +0 -0
  391. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tooltip.py +0 -0
  392. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tree/__init__.py +0 -0
  393. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tree/source_binding.py +0 -0
  394. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tree/treeitem.py +0 -0
  395. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tree/treenode.py +0 -0
  396. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/composites/tree/treeview.py +0 -0
  397. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/__init__.py +0 -0
  398. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/configure_mixin.py +0 -0
  399. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/entry_mixin.py +0 -0
  400. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/font_mixin.py +0 -0
  401. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/icon_mixin.py +0 -0
  402. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/localization_mixin.py +0 -0
  403. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/signal_mixin.py +0 -0
  404. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/mixins/validation_mixin.py +0 -0
  405. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/__init__.py +0 -0
  406. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/_menubutton.py +0 -0
  407. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/badge.py +0 -0
  408. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/button.py +0 -0
  409. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/card.py +0 -0
  410. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/checkbutton.py +0 -0
  411. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/checktoggle.py +0 -0
  412. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/combobox.py +0 -0
  413. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/entry.py +0 -0
  414. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/flexframe.py +0 -0
  415. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/frame.py +0 -0
  416. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/gridframe.py +0 -0
  417. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/label.py +0 -0
  418. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/labelframe.py +0 -0
  419. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/optionmenu.py +0 -0
  420. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/packframe.py +0 -0
  421. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/panedwindow.py +0 -0
  422. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/progressbar.py +0 -0
  423. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/radiobutton.py +0 -0
  424. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/radiotoggle.py +0 -0
  425. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/scrollbar.py +0 -0
  426. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/separator.py +0 -0
  427. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/sizegrip.py +0 -0
  428. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/spinbox.py +0 -0
  429. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/switch.py +0 -0
  430. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/_impl/primitives/treeview.py +0 -0
  431. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/app.py +0 -0
  432. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/avatar.py +0 -0
  433. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/boolean_controls.py +0 -0
  434. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/button.py +0 -0
  435. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/buttongroup.py +0 -0
  436. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/calendar.py +0 -0
  437. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/carousel.py +0 -0
  438. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/chart.py +0 -0
  439. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/codeeditor.py +0 -0
  440. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/contextmenu.py +0 -0
  441. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/datatable.py +0 -0
  442. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/datefield.py +0 -0
  443. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/divider.py +0 -0
  444. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/gallery.py +0 -0
  445. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/gauge.py +0 -0
  446. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/label.py +0 -0
  447. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/listview.py +0 -0
  448. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/menubutton.py +0 -0
  449. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/numberfield.py +0 -0
  450. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/passwordfield.py +0 -0
  451. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/pathfield.py +0 -0
  452. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/picture.py +0 -0
  453. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/progressbar.py +0 -0
  454. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/radio_variants.py +0 -0
  455. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/radiogroup.py +0 -0
  456. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/scrollbar.py +0 -0
  457. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/scrollview.py +0 -0
  458. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/select.py +0 -0
  459. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/selectbutton.py +0 -0
  460. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/sidebar_toggle.py +0 -0
  461. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/sizegrip.py +0 -0
  462. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/slider.py +0 -0
  463. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/spinbox.py +0 -0
  464. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/spinnerfield.py +0 -0
  465. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/splash.py +0 -0
  466. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/stacks.py +0 -0
  467. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/statusbar.py +0 -0
  468. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/textarea.py +0 -0
  469. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/textfield.py +0 -0
  470. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/theme_toggle.py +0 -0
  471. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/timefield.py +0 -0
  472. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/toast.py +0 -0
  473. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/togglegroup.py +0 -0
  474. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/toolbar.py +0 -0
  475. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/tooltip.py +0 -0
  476. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/tree.py +0 -0
  477. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/types.py +0 -0
  478. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack/widgets/window.py +0 -0
  479. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack.egg-info/dependency_links.txt +0 -0
  480. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack.egg-info/entry_points.txt +0 -0
  481. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack.egg-info/requires.txt +0 -0
  482. {bootstack-0.2.3 → bootstack-0.3.1}/src/bootstack.egg-info/top_level.txt +0 -0
  483. {bootstack-0.2.3 → bootstack-0.3.1}/tests/test_dialog_anchor.py +0 -0
  484. {bootstack-0.2.3 → bootstack-0.3.1}/tests/test_events_doc_coverage.py +0 -0
  485. {bootstack-0.2.3 → bootstack-0.3.1}/tests/test_hot_reload.py +0 -0
  486. {bootstack-0.2.3 → bootstack-0.3.1}/tests/test_images.py +0 -0
  487. {bootstack-0.2.3 → bootstack-0.3.1}/tests/test_images_gui.py +0 -0
  488. {bootstack-0.2.3 → bootstack-0.3.1}/tests/test_intl_format.py +0 -0
  489. {bootstack-0.2.3 → bootstack-0.3.1}/tests/test_navmodel.py +0 -0
  490. {bootstack-0.2.3 → bootstack-0.3.1}/tests/test_on_color_contrast.py +0 -0
  491. {bootstack-0.2.3 → bootstack-0.3.1}/tests/test_public_surface.py +0 -0
  492. {bootstack-0.2.3 → bootstack-0.3.1}/tests/test_store.py +0 -0
  493. {bootstack-0.2.3 → bootstack-0.3.1}/tests/test_theme_from_existing.py +0 -0
  494. {bootstack-0.2.3 → 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.2.3
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.2.3"
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.2.3"
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}",
@@ -0,0 +1,449 @@
1
+ """Screen-region capture backing the public `widget.capture()`.
2
+
3
+ A capture reads pixels from the display, so the region has to be visible:
4
+ the widget must be on screen, and anything drawn over it is captured along
5
+ with it. There is no offscreen rendering path — the toolkit does not offer
6
+ one — which is why the public method requires a visible widget.
7
+
8
+ Capture uses Pillow's `ImageGrab`, falling back to a desktop screenshot tool
9
+ on Linux sessions where `ImageGrab` is unavailable (Wayland, or a Pillow
10
+ built without XCB support).
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import contextlib
16
+ import os
17
+ import shutil
18
+ import subprocess
19
+ import sys
20
+ import tempfile
21
+ import time
22
+ import tkinter
23
+ from pathlib import Path
24
+
25
+ from PIL import Image, ImageGrab
26
+
27
+ from bootstack.errors import BootstackError
28
+
29
+ try:
30
+ from screeninfo import get_monitors
31
+ HAS_SCREENINFO = True
32
+ except ImportError:
33
+ HAS_SCREENINFO = False
34
+
35
+ # Platforms whose capture backend ships with the operating system. There is
36
+ # nothing to install and nothing to fall back to, so a failure here is a real
37
+ # failure rather than a missing-backend one.
38
+ _BUILTIN_BACKEND_PLATFORMS = ("win32", "darwin")
39
+
40
+ # How long to pause between event-loop turns while settling. Short enough to
41
+ # stay responsive to the repaint we are waiting for, long enough not to spin.
42
+ _SETTLE_TICK = 0.01
43
+
44
+ # Formats that store plain color and nothing else. A capture bound for one of
45
+ # these is converted first, because saving an image that carries transparency
46
+ # — or a color palette — as JPEG fails outright.
47
+ _NO_ALPHA_FORMATS = {".jpg", ".jpeg", ".pdf", ".bmp"}
48
+
49
+ # Image modes those formats accept as they are.
50
+ _PLAIN_COLOR_MODES = ("RGB", "L")
51
+
52
+ # Tried in order, first one installed wins.
53
+ _SUBPROCESS_BACKENDS = (
54
+ ("grim", ["grim", "{out}"]), # wlroots, Sway
55
+ ("gnome-screenshot", ["gnome-screenshot", "-f", "{out}"]),
56
+ # `-f` asks for the full screen. Without it spectacle repeats whatever
57
+ # capture mode it was last used with, which is remembered in its own
58
+ # config — so the same call photographs the desktop on one machine and a
59
+ # single window on the next.
60
+ ("spectacle", ["spectacle", "-b", "-n", "-f", "-o", "{out}"]), # KDE
61
+ ("import", ["import", "-window", "root", "{out}"]), # ImageMagick
62
+ )
63
+
64
+ # Platforms where cutting the region has to stay `ImageGrab`'s job, because it
65
+ # needs detail this module does not have. Windows crops against the origin its
66
+ # own grab reports, which cannot be reconstructed from a rectangle alone; macOS
67
+ # asks `screencapture` for the region and rescales the answer, which is what
68
+ # keeps the result aligned on a Retina display. Cutting the region here would
69
+ # discard both.
70
+ #
71
+ # This says which code owns the crop, not that the crop is checked — neither of
72
+ # those is. Windows pads a region reaching past the grabbed desktop with black,
73
+ # and macOS stretches a region the system clamped. Extending the bounds check to
74
+ # them is a separate change, and one no box available here can verify.
75
+ #
76
+ # Everywhere else — Linux — `ImageGrab` grabs the whole desktop and crops it
77
+ # with no check that it covers the region, padding the difference with black
78
+ # and raising nothing. That is the failure `_crop_desktop` refuses, so there
79
+ # the region is cut here rather than by the library.
80
+ _LIBRARY_REGION_PLATFORMS = ("win32", "darwin")
81
+ _LIBRARY_HANDLES_REGION = sys.platform in _LIBRARY_REGION_PLATFORMS
82
+
83
+
84
+ def widget_region(tk_widget, inset: int = 0) -> tuple[int, int, int, int]:
85
+ """Return a widget's on-screen rectangle as `(left, top, right, bottom)`.
86
+
87
+ Coordinates span the whole virtual desktop, so a widget on a monitor
88
+ placed to the left of, or above, the primary one has a negative origin.
89
+
90
+ Args:
91
+ tk_widget: The toolkit widget to measure.
92
+ inset: Pixels to trim from every edge. Zero or more.
93
+ """
94
+ if inset < 0:
95
+ raise BootstackError(
96
+ f"inset={inset} is negative. An inset trims pixels from every "
97
+ f"edge, so it cannot reach past the widget onto its neighbors."
98
+ )
99
+ x, y = tk_widget.winfo_rootx(), tk_widget.winfo_rooty()
100
+ width, height = tk_widget.winfo_width(), tk_widget.winfo_height()
101
+ left, top = x + inset, y + inset
102
+ right, bottom = x + width - inset, y + height - inset
103
+ if right <= left or bottom <= top:
104
+ raise BootstackError(
105
+ f"inset={inset} trims away the whole capture — the widget is only "
106
+ f"{width}x{height} pixels."
107
+ )
108
+ return (left, top, right, bottom)
109
+
110
+
111
+ def still_exists(tk_widget) -> bool:
112
+ """Whether a widget is still alive, without leaking a toolkit error.
113
+
114
+ `winfo_exists()` reports 0 for a destroyed child but RAISES for a destroyed
115
+ root — the toolkit has no interpreter left to ask by then. Both mean the
116
+ same thing to a caller, so both answer False here.
117
+ """
118
+ try:
119
+ return bool(tk_widget.winfo_exists())
120
+ except tkinter.TclError:
121
+ return False
122
+
123
+
124
+ def clipped_out_of_view(tk_widget) -> bool:
125
+ """Whether a widget's rectangle has left its own window entirely.
126
+
127
+ A widget scrolled out of a viewport stays MAPPED — the toolkit reports
128
+ `winfo_ismapped()` and `winfo_viewable()` as 1 for a row that is nowhere
129
+ near the visible area — while `winfo_rooty()` keeps reporting where the row
130
+ would be if the window were tall enough to show it. Measured: a row in a
131
+ scrolled list reported y=273 for a window spanning 390 to 630.
132
+
133
+ Capturing that rectangle photographs whatever else is on screen there and
134
+ saves it without complaint, which is the one outcome this module exists to
135
+ prevent. Only a rectangle that has left the window completely is refused;
136
+ a row half out of view still captures, matching the rule that a capture
137
+ shows what the screen shows.
138
+ """
139
+ top = tk_widget.winfo_toplevel()
140
+ if tk_widget is top:
141
+ return False
142
+
143
+ left, top_y = tk_widget.winfo_rootx(), tk_widget.winfo_rooty()
144
+ right = left + tk_widget.winfo_width()
145
+ bottom = top_y + tk_widget.winfo_height()
146
+
147
+ win_left, win_top = top.winfo_rootx(), top.winfo_rooty()
148
+ win_right = win_left + top.winfo_width()
149
+ win_bottom = win_top + top.winfo_height()
150
+
151
+ return not (
152
+ left < win_right and right > win_left
153
+ and top_y < win_bottom and bottom > win_top
154
+ )
155
+
156
+
157
+ @contextlib.contextmanager
158
+ def raised(tk_widget):
159
+ """Hold a widget's window at the front for the duration of a capture.
160
+
161
+ A capture reads whatever is on the display, so a window sitting over the
162
+ target is captured instead of it — silently, and with a plausible-looking
163
+ image to show for it. Raising the window first is what makes the result
164
+ mean what the caller asked for.
165
+
166
+ The previous always-on-top setting is restored on the way out, and left
167
+ alone entirely if it was already set, so a window the application
168
+ deliberately pinned on top does not get un-pinned by taking a picture.
169
+ """
170
+ top = tk_widget.winfo_toplevel()
171
+ try:
172
+ top.lift()
173
+ except tkinter.TclError:
174
+ pass
175
+
176
+ # None means "left alone" — either the setting could not be read, or the
177
+ # window manager does not support it. Some Linux window managers do not,
178
+ # and a capture must not fail just because it cannot pin a window.
179
+ was_topmost = None
180
+ try:
181
+ was_topmost = bool(top.attributes("-topmost"))
182
+ if not was_topmost:
183
+ top.attributes("-topmost", True)
184
+ except tkinter.TclError:
185
+ was_topmost = None
186
+
187
+ try:
188
+ yield
189
+ finally:
190
+ # Restore only what this function actually changed.
191
+ if was_topmost is False:
192
+ try:
193
+ top.attributes("-topmost", False)
194
+ except tkinter.TclError:
195
+ pass
196
+
197
+
198
+ def settle(tk_widget, seconds: float) -> None:
199
+ """Let pending redraws finish before the pixels are read.
200
+
201
+ Waits `seconds` while the event loop turns, so the desktop can repaint
202
+ the area behind any window that has just closed — a save dialog, most
203
+ commonly.
204
+
205
+ The wait has to dispatch events. Flushing our own drawing is not enough:
206
+ the area a closing window uncovers is repainted by the desktop, and on
207
+ macOS that repaint does not happen at all unless the loop is turning. A
208
+ capture taken without it photographs the dialog that was just dismissed
209
+ rather than the widget underneath.
210
+
211
+ Dispatching brings its own problem, which is why the window is held busy
212
+ for the duration. Turning the loop runs everything queued, including a
213
+ second click on the button that started the capture: that click would
214
+ re-enter the caller's handler, opening a second save dialog on top of the
215
+ first and starting an overlapping capture, none of which the person
216
+ clicking asked for. `tk busy` routes that click to a blocking window
217
+ instead.
218
+
219
+ ⚠ THE HOLD IS BEST-EFFORT, AND ON macOS IT DOES NOTHING. Aqua reports the
220
+ hold as successful and never maps the window it created: `tk busy status`
221
+ returns 1 while the busy window sits at `winfo_ismapped() == 0`, and Tk's
222
+ own hit test at the button's position still resolves to the button. So a
223
+ real click re-enters the handler there — confirmed by hand, since a
224
+ synthesized click cannot test this. Measured on Tk 8.6.17 in plain
225
+ tkinter, so it is the toolkit rather than anything above it. X11 and Win32
226
+ map the window and are expected to honor it;
227
+ `development/probe_429_busy_during_settle.py` reports which a given box
228
+ does.
229
+
230
+ The wait dispatches either way. A correct picture is worth more than a
231
+ stray click: without dispatching, the capture reads pixels that are simply
232
+ wrong, and that is the failure this function exists to prevent.
233
+
234
+ Args:
235
+ tk_widget: Any widget; its root window is the one flushed.
236
+ seconds: How long to wait for. Zero flushes without waiting.
237
+ """
238
+ root = tk_widget._root()
239
+ root.update_idletasks()
240
+ with _input_blocked(tk_widget):
241
+ deadline = time.perf_counter() + max(seconds, 0.0)
242
+ while time.perf_counter() < deadline:
243
+ root.update()
244
+ # Yield the processor rather than spinning on update() alone.
245
+ time.sleep(_SETTLE_TICK)
246
+ # Once more after the wait, so anything the pause allowed to arrive
247
+ # is drawn before the pixels are read.
248
+ root.update()
249
+ root.update_idletasks()
250
+
251
+
252
+ @contextlib.contextmanager
253
+ def _input_blocked(tk_widget):
254
+ """Route input away from a window while the event loop is being turned.
255
+
256
+ Yields either way: a window that cannot be held busy is still captured,
257
+ just without the protection from a stray click.
258
+
259
+ ⚠ Yielding `True` means the hold was ACCEPTED, not that input is actually
260
+ blocked — macOS accepts it and does not map the busy window. See `settle`.
261
+
262
+ ⚠ The target may already be DEAD by the time this runs — settling flushes
263
+ pending drawing first, and idle work queued before the capture can destroy
264
+ it. Asking a destroyed widget for its toplevel raises a raw `TclError`,
265
+ which would escape a method documented to raise `BootstackError` and
266
+ pre-empt the guard that reports the closure properly. So the lookup is
267
+ guarded too, not just the `tk busy` calls.
268
+ """
269
+ top = None
270
+ held = False
271
+ try:
272
+ top = tk_widget.winfo_toplevel()
273
+ top.tk.call("tk", "busy", "hold", top._w)
274
+ held = True
275
+ except tkinter.TclError:
276
+ held = False
277
+ try:
278
+ yield held
279
+ finally:
280
+ if held and top is not None:
281
+ try:
282
+ top.tk.call("tk", "busy", "forget", top._w)
283
+ except tkinter.TclError:
284
+ pass
285
+
286
+
287
+ def capture_region(bbox: tuple[int, int, int, int], path) -> Path:
288
+ """Grab one screen region and write it to `path`, returning the path."""
289
+ return save(grab(bbox), path)
290
+
291
+
292
+ def grab(bbox: tuple[int, int, int, int] | None = None) -> Image.Image:
293
+ """Grab a screen region, or the whole primary display when `bbox` is None."""
294
+ try:
295
+ if _LIBRARY_HANDLES_REGION:
296
+ # `all_screens` widens the grab to the whole virtual desktop.
297
+ # Without it, a region on a monitor left of or above the primary
298
+ # one is cropped out of a primary-only grab and comes back
299
+ # silently BLACK — no exception raised and nothing to debug.
300
+ return ImageGrab.grab(bbox=bbox, all_screens=bbox is not None)
301
+ # Grab the whole desktop and cut the region out below, under the
302
+ # bounds check. This platform grabs everything and crops either way,
303
+ # so what changes is where the crop happens, not how much is read.
304
+ image = ImageGrab.grab()
305
+ except (OSError, NotImplementedError) as exc:
306
+ # Diagnosis happens only AFTER a failed grab, never before one, so
307
+ # nothing here can reject a capture that would have worked.
308
+ #
309
+ # The catch is deliberately wide because the failures it has to cover
310
+ # are: no ImageGrab on this build at all, and a grab that ran and came
311
+ # back with nothing usable. Those need opposite answers, and the
312
+ # exception type cannot tell them apart — Pillow reports a region no
313
+ # display covers as `UnidentifiedImageError`, which IS an `OSError`.
314
+ if bbox is not None and _covered_by_a_display(bbox) is False:
315
+ raise BootstackError(
316
+ f"The area being captured — ({bbox[0]}, {bbox[1]}) to "
317
+ f"({bbox[2]}, {bbox[3]}) — is not on any display, so there are "
318
+ f"no pixels to read. A window moved off the edge of the screen, "
319
+ f"or a widget scrolled out of view, is the usual cause."
320
+ ) from exc
321
+
322
+ if sys.platform in _BUILTIN_BACKEND_PLATFORMS:
323
+ # Falling through to the subprocess chain here would end in advice
324
+ # to install Linux screenshot tools on a machine that already has a
325
+ # working backend — wrong on its face and no help in fixing this.
326
+ raise BootstackError(
327
+ f"The screen capture failed — {exc}. The window may have been "
328
+ f"hidden or moved while the picture was being taken."
329
+ ) from exc
330
+
331
+ # Wayland, or a Pillow built without XCB.
332
+ return _grab_via_subprocess(bbox)
333
+ # Outside the `except` on purpose: a region the grab missed is a capture
334
+ # failure to report, not a reason to try another backend.
335
+ return _crop_desktop(image, bbox) if bbox else image
336
+
337
+
338
+ def _covered_by_a_display(bbox: tuple[int, int, int, int]) -> bool | None:
339
+ """Whether any monitor covers part of `bbox`.
340
+
341
+ Returns None when the question cannot be answered — no `screeninfo`, or it
342
+ could not read the display layout — so a caller can tell "definitely not on
343
+ a display" apart from "could not tell", and only act on the former.
344
+
345
+ Partial overlap counts as covered. A window hanging off the bottom edge of
346
+ the screen captures perfectly well; only a region no monitor touches at all
347
+ has nothing to read.
348
+ """
349
+ if not HAS_SCREENINFO:
350
+ return None
351
+
352
+ left, top, right, bottom = bbox
353
+ try:
354
+ monitors = get_monitors()
355
+ except Exception: # noqa: BLE001 - a diagnostic must not raise its own error
356
+ return None
357
+ if not monitors:
358
+ return None
359
+
360
+ return any(
361
+ left < m.x + m.width and right > m.x
362
+ and top < m.y + m.height and bottom > m.y
363
+ for m in monitors
364
+ )
365
+
366
+
367
+ def save(image: Image.Image, path) -> Path:
368
+ """Write an image to `path`, taking the format from its extension."""
369
+ path = Path(path)
370
+ # Decided by the target format rather than one source mode: a desktop
371
+ # screenshot tool can hand back a palette or grayscale-with-alpha image,
372
+ # and those fail on the same formats RGBA does.
373
+ if (
374
+ path.suffix.lower() in _NO_ALPHA_FORMATS
375
+ and image.mode not in _PLAIN_COLOR_MODES
376
+ ):
377
+ image = image.convert("RGB")
378
+ try:
379
+ path.parent.mkdir(parents=True, exist_ok=True)
380
+ image.save(path)
381
+ except (OSError, ValueError, KeyError) as exc:
382
+ raise BootstackError(
383
+ f"Could not save the capture to '{path}' — {exc}. The file "
384
+ f"extension selects the format; '.png', '.jpg', and '.pdf' are "
385
+ f"the usual choices."
386
+ ) from exc
387
+ return path
388
+
389
+
390
+ def _grab_via_subprocess(
391
+ bbox: tuple[int, int, int, int] | None = None,
392
+ ) -> Image.Image:
393
+ """Capture through a desktop screenshot tool, for Linux without ImageGrab."""
394
+ for name, argv in _SUBPROCESS_BACKENDS:
395
+ if not shutil.which(name):
396
+ continue
397
+ # A unique name per call — a fixed one collides when two captures
398
+ # overlap, and the loser silently reads the other's pixels.
399
+ handle, tmp_name = tempfile.mkstemp(
400
+ prefix="bootstack-capture-", suffix=".png"
401
+ )
402
+ os.close(handle)
403
+ tmp = Path(tmp_name)
404
+ try:
405
+ subprocess.run(
406
+ [arg.format(out=tmp) for arg in argv],
407
+ check=True,
408
+ capture_output=True,
409
+ timeout=30,
410
+ )
411
+ with Image.open(tmp) as opened:
412
+ opened.load()
413
+ image = opened.copy()
414
+ except (subprocess.SubprocessError, OSError):
415
+ continue
416
+ finally:
417
+ tmp.unlink(missing_ok=True)
418
+ return _crop_desktop(image, bbox) if bbox else image
419
+
420
+ raise BootstackError(
421
+ "No screen capture backend is available. Install a Pillow build with "
422
+ "XCB support, or one of: grim, gnome-screenshot, spectacle, import."
423
+ )
424
+
425
+
426
+ def _crop_desktop(
427
+ image: Image.Image, bbox: tuple[int, int, int, int]
428
+ ) -> Image.Image:
429
+ """Cut a region out of a whole-desktop grab, refusing a region it misses.
430
+
431
+ A whole-desktop grab returns an image measured from whatever its source
432
+ treats as the origin, while the region is measured across the whole virtual
433
+ desktop. Those agree on an ordinary single-monitor session and can disagree
434
+ otherwise — most often when the window sits on a monitor the grab did not
435
+ cover. Cropping past the edge of an image pads the result with black rather
436
+ than failing, so the caller would get a plausible-looking all-black picture
437
+ and no way to tell why. Raising is the better answer.
438
+ """
439
+ left, top, right, bottom = bbox
440
+ if left < 0 or top < 0 or right > image.width or bottom > image.height:
441
+ raise BootstackError(
442
+ f"The screen capture returned a {image.width}x{image.height} "
443
+ f"picture, which does not cover the area being captured — "
444
+ f"({left}, {top}) to ({right}, {bottom}). Either the window is "
445
+ f"hanging off the edge of the screen, or it is on a display the "
446
+ f"capture did not reach. Moving it fully onto the main display "
447
+ f"resolves both."
448
+ )
449
+ return image.crop(bbox)
@@ -436,7 +436,12 @@ class FormDialog:
436
436
  on_close: Callback fired when the dialog closes by any means.
437
437
  width: Explicit form width in pixels.
438
438
  height: Explicit form height in pixels.
439
- buttons: Footer button specs. Defaults to Cancel + OK.
439
+ buttons: Footer button specs. Defaults to Cancel + OK. A button's
440
+ `result=` decides whether it submits the form: `'ok'`, `'submit'`
441
+ and `'save'` return the entered data, and so does a button with no
442
+ `result=` at all. Give an action button — a Delete, say — a result
443
+ token of its own, and it runs without the form having to validate
444
+ and hands that token back instead of the data.
440
445
  min_size: Minimum dialog window size `(width, height)`.
441
446
  max_size: Maximum dialog window size `(width, height)`.
442
447
  resizable: Allow window resizing. Default `False`.
@@ -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:
@@ -260,7 +285,6 @@ class DateDialog:
260
285
  text="button.ok",
261
286
  role="primary",
262
287
  default=True,
263
- closes=False,
264
288
  command=lambda dlg: self._on_confirm_range(),
265
289
  ),
266
290
  ]
@@ -318,22 +342,39 @@ class DateDialog:
318
342
 
319
343
  self._confirm(selected)
320
344
 
321
- def _on_confirm_range(self) -> None:
345
+ def _on_confirm_range(self) -> bool:
322
346
  """Confirm the selected range when OK is clicked.
323
347
 
324
- No-op (dialog stays open) until both endpoints are selected.
348
+ Refuses the press — leaving the dialog open until both endpoints are
349
+ selected. The OK button hands that answer back to `Dialog`, which owns
350
+ the close.
351
+
352
+ Unlike `_confirm`, this releases no grab of its own. It does not need
353
+ to: `Dialog` destroys the toplevel, and Tk releases a grab held by a
354
+ window when that window is destroyed (measured — see
355
+ `development/probe_437_review2_fixes.py`). `_confirm` keeps its
356
+ `grab_release()` because it destroys the window itself, where the call
357
+ is at least self-documenting.
325
358
  """
326
359
  if not self._picker:
327
- return
360
+ return False
328
361
  start, end = self._picker.get_range()
329
362
  if start is None or end is None:
330
- return
331
- self._confirm((start, end))
363
+ return False
364
+ self._record((start, end))
365
+ return True
332
366
 
333
- def _confirm(self, result: date | Tuple[date, date]) -> None:
334
- """Record the result, emit, and close the dialog."""
367
+ def _record(self, result: date | Tuple[date, date]) -> None:
368
+ """Record the result and notify listeners, without closing."""
335
369
  self._dialog.result = result
336
370
  self._emit_result(result, confirmed=True)
371
+
372
+ def _confirm(self, result: date | Tuple[date, date]) -> None:
373
+ """Record the result, emit, and close the dialog.
374
+
375
+ Used by the commit-on-select path, which has no button behind it.
376
+ """
377
+ self._record(result)
337
378
  if self._dialog.toplevel:
338
379
  top = self._dialog.toplevel
339
380
  try: