bootstack 0.2.3__tar.gz → 0.3.0__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.0}/PKG-INFO +1 -1
  2. {bootstack-0.2.3 → bootstack-0.3.0}/pyproject.toml +2 -2
  3. bootstack-0.3.0/src/bootstack/_core/capture.py +449 -0
  4. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dialogs/__init__.py +6 -1
  5. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dialogs/_impl/datedialog.py +24 -8
  6. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dialogs/_impl/dialog.py +59 -14
  7. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dialogs/_impl/formdialog.py +122 -30
  8. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dialogs/_impl/query.py +28 -18
  9. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/base.py +90 -0
  10. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/form.py +17 -3
  11. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/form.py +6 -1
  12. {bootstack-0.2.3 → bootstack-0.3.0/src/bootstack.egg-info}/PKG-INFO +1 -1
  13. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack.egg-info/SOURCES.txt +1 -0
  14. {bootstack-0.2.3 → bootstack-0.3.0}/LICENSE +0 -0
  15. {bootstack-0.2.3 → bootstack-0.3.0}/MANIFEST.in +0 -0
  16. {bootstack-0.2.3 → bootstack-0.3.0}/NOTICE +0 -0
  17. {bootstack-0.2.3 → bootstack-0.3.0}/README.md +0 -0
  18. {bootstack-0.2.3 → bootstack-0.3.0}/setup.cfg +0 -0
  19. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/__init__.py +0 -0
  20. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/__main__.py +0 -0
  21. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/__init__.py +0 -0
  22. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/__init__.py +0 -0
  23. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/after.py +0 -0
  24. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/bind.py +0 -0
  25. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/bindtags.py +0 -0
  26. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/busy.py +0 -0
  27. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/clipboard.py +0 -0
  28. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/focus.py +0 -0
  29. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/grab.py +0 -0
  30. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/grid.py +0 -0
  31. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/localization.py +0 -0
  32. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/pack.py +0 -0
  33. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/place.py +0 -0
  34. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/selection.py +0 -0
  35. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/signals.py +0 -0
  36. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/capabilities/winfo.py +0 -0
  37. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/colorutils.py +0 -0
  38. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/exceptions.py +0 -0
  39. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/images.py +0 -0
  40. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/mixins/__init__.py +0 -0
  41. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/mixins/ttk_state.py +0 -0
  42. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/mixins/widget.py +0 -0
  43. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/paths.py +0 -0
  44. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/publisher.py +0 -0
  45. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_core/variables.py +0 -0
  46. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_runtime/__init__.py +0 -0
  47. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_runtime/app.py +0 -0
  48. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_runtime/base_window.py +0 -0
  49. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_runtime/events.py +0 -0
  50. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_runtime/shortcuts.py +0 -0
  51. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_runtime/tk_patch.py +0 -0
  52. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_runtime/toplevel.py +0 -0
  53. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_runtime/utility.py +0 -0
  54. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_runtime/visual_focus.py +0 -0
  55. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_runtime/wheel.py +0 -0
  56. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/_runtime/window_utilities.py +0 -0
  57. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/__init__.py +0 -0
  58. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/bootstack.ico +0 -0
  59. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/bootstack.png +0 -0
  60. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/__init__.py +0 -0
  61. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/badge-pill.png +0 -0
  62. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/badge-square.png +0 -0
  63. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/button-compact.png +0 -0
  64. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/button-default.png +0 -0
  65. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
  66. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
  67. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
  68. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
  69. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
  70. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
  71. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
  72. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
  73. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
  74. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
  75. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
  76. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
  77. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/card.png +0 -0
  78. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/checkbox-checked.png +0 -0
  79. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  80. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/checkbox-unchecked.png +0 -0
  81. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/field.png +0 -0
  82. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/input-addon-compact.png +0 -0
  83. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/input-addon-default.png +0 -0
  84. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/input-compact.png +0 -0
  85. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/input-default.png +0 -0
  86. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/list-item-separated.png +0 -0
  87. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/list-item.png +0 -0
  88. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/listrow-compact.png +0 -0
  89. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/listrow-default.png +0 -0
  90. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/manifest.toml +0 -0
  91. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/menu-item.png +0 -0
  92. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/navitem-compact.png +0 -0
  93. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/navitem-default.png +0 -0
  94. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/progressbar-h-compact.png +0 -0
  95. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/progressbar-h-default.png +0 -0
  96. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/progressbar-v-compact.png +0 -0
  97. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/progressbar-v-default.png +0 -0
  98. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/radiobutton.png +0 -0
  99. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  100. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/scrollbar-vertical.png +0 -0
  101. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/slider-handle.png +0 -0
  102. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/slider-track-h.png +0 -0
  103. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/slider-track-v.png +0 -0
  104. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/switch-off.png +0 -0
  105. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/switch-on.png +0 -0
  106. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/tab-h.png +0 -0
  107. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/elements/tab-v.png +0 -0
  108. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/icons/bootstrap.ttf +0 -0
  109. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/icons/glyphmap.json +0 -0
  110. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/icons/icon_metrics.json +0 -0
  111. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  112. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +0 -0
  113. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
  114. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +0 -0
  115. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  116. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +0 -0
  117. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  118. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +0 -0
  119. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  120. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +0 -0
  121. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  122. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +0 -0
  123. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  124. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +0 -0
  125. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  126. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +0 -0
  127. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  128. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +0 -0
  129. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  130. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +0 -0
  131. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  132. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +0 -0
  133. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  134. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +0 -0
  135. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  136. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +0 -0
  137. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  138. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +0 -0
  139. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  140. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +0 -0
  141. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  142. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +0 -0
  143. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  144. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +0 -0
  145. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  146. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +0 -0
  147. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  148. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +0 -0
  149. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  150. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +0 -0
  151. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  152. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +0 -0
  153. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  154. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +0 -0
  155. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  156. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +0 -0
  157. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/__init__.py +0 -0
  158. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/__main__.py +0 -0
  159. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/add.py +0 -0
  160. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/appicon.py +0 -0
  161. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/build.py +0 -0
  162. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/config.py +0 -0
  163. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/demo.py +0 -0
  164. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/dev.py +0 -0
  165. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/doctor.py +0 -0
  166. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/icons.py +0 -0
  167. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/promote.py +0 -0
  168. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/pyinstaller.py +0 -0
  169. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/run.py +0 -0
  170. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/start.py +0 -0
  171. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/cli/templates/__init__.py +0 -0
  172. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/clipboard.py +0 -0
  173. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/constants.py +0 -0
  174. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/data/README.md +0 -0
  175. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/data/__init__.py +0 -0
  176. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/data/_observable.py +0 -0
  177. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/data/base.py +0 -0
  178. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/data/file_source.py +0 -0
  179. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/data/memory_source.py +0 -0
  180. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/data/query.py +0 -0
  181. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/data/readers.py +0 -0
  182. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/data/sqlite_source.py +0 -0
  183. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/data/types.py +0 -0
  184. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/data/writers.py +0 -0
  185. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dev/__init__.py +0 -0
  186. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dev/_capture.py +0 -0
  187. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dev/_env.py +0 -0
  188. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dev/_registry.py +0 -0
  189. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dev/_reloader.py +0 -0
  190. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dev/_reset.py +0 -0
  191. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dev/_watcher.py +0 -0
  192. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dialogs/_impl/__init__.py +0 -0
  193. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dialogs/_impl/colorchooser.py +0 -0
  194. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dialogs/_impl/filterdialog.py +0 -0
  195. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dialogs/_impl/fontdialog.py +0 -0
  196. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/dialogs/_impl/message.py +0 -0
  197. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/errors.py +0 -0
  198. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/events/__init__.py +0 -0
  199. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/events/_event.py +0 -0
  200. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/events/_payloads.py +0 -0
  201. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/events/_subscription.py +0 -0
  202. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/i18n/README.md +0 -0
  203. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/i18n/__init__.py +0 -0
  204. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/i18n/catalog.py +0 -0
  205. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/i18n/intl_format.py +0 -0
  206. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/i18n/msgcat.py +0 -0
  207. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/i18n/specs.py +0 -0
  208. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/images.py +0 -0
  209. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/py.typed +0 -0
  210. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/scheduling/__init__.py +0 -0
  211. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/scheduling/_schedule.py +0 -0
  212. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/shortcuts.py +0 -0
  213. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/signals/README.md +0 -0
  214. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/signals/__init__.py +0 -0
  215. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/signals/integration.py +0 -0
  216. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/signals/signal.py +0 -0
  217. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/signals/types.py +0 -0
  218. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/store.py +0 -0
  219. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/streams/__init__.py +0 -0
  220. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/streams/_stream.py +0 -0
  221. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/__init__.py +0 -0
  222. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/__init__.py +0 -0
  223. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/badge.py +0 -0
  224. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/button.py +0 -0
  225. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/buttongroup.py +0 -0
  226. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/calendar.py +0 -0
  227. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/checkbutton.py +0 -0
  228. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/combobox.py +0 -0
  229. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/contextmenu.py +0 -0
  230. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/entry.py +0 -0
  231. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/expander.py +0 -0
  232. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/field.py +0 -0
  233. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/frame.py +0 -0
  234. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/label.py +0 -0
  235. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/labelframe.py +0 -0
  236. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/listview.py +0 -0
  237. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/menubar.py +0 -0
  238. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/menubutton.py +0 -0
  239. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/panedwindow.py +0 -0
  240. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/progressbar.py +0 -0
  241. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/radiobutton.py +0 -0
  242. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/scale.py +0 -0
  243. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/scrollbar.py +0 -0
  244. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/separator.py +0 -0
  245. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/sidenav.py +0 -0
  246. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/sizegrip.py +0 -0
  247. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/spinbox.py +0 -0
  248. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/switch.py +0 -0
  249. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/tabitem.py +0 -0
  250. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/togglegroup.py +0 -0
  251. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/toolbutton.py +0 -0
  252. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/tooltip.py +0 -0
  253. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/treeview.py +0 -0
  254. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders/utils.py +0 -0
  255. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders_tk/__init__.py +0 -0
  256. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/builders_tk/defaults.py +0 -0
  257. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/element.py +0 -0
  258. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/fonts.py +0 -0
  259. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/style.py +0 -0
  260. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/style_builder_base.py +0 -0
  261. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/style_builder_mixed.py +0 -0
  262. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/style_builder_tk.py +0 -0
  263. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/style_builder_ttk.py +0 -0
  264. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/style_resolver.py +0 -0
  265. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/theme.py +0 -0
  266. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/theme_provider.py +0 -0
  267. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/themes/__init__.py +0 -0
  268. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/tk_patch.py +0 -0
  269. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/token_maps.py +0 -0
  270. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/types.py +0 -0
  271. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/typography.py +0 -0
  272. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/style/utility.py +0 -0
  273. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/types.py +0 -0
  274. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/validation/__init__.py +0 -0
  275. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/validation/types.py +0 -0
  276. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/validation/validation_result.py +0 -0
  277. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/validation/validation_rules.py +0 -0
  278. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/__init__.py +0 -0
  279. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/__init__.py +0 -0
  280. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/app_config.py +0 -0
  281. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/choices.py +0 -0
  282. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/container.py +0 -0
  283. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/context.py +0 -0
  284. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/events.py +0 -0
  285. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/field_mixin.py +0 -0
  286. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/icon_image_props.py +0 -0
  287. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/image_binding.py +0 -0
  288. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/kwargs.py +0 -0
  289. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/navmodel.py +0 -0
  290. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/options.py +0 -0
  291. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/selection_group.py +0 -0
  292. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/window_controls.py +0 -0
  293. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_core/window_menu.py +0 -0
  294. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/__init__.py +0 -0
  295. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/_internal/__init__.py +0 -0
  296. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/_internal/wrapper_base.py +0 -0
  297. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/_parts/__init__.py +0 -0
  298. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/_parts/numberentry_part.py +0 -0
  299. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/_parts/spinnerentry_part.py +0 -0
  300. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/_parts/textentry_part.py +0 -0
  301. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/__init__.py +0 -0
  302. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/_dateutils.py +0 -0
  303. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/_image_fit.py +0 -0
  304. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/accordion.py +0 -0
  305. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/avatar.py +0 -0
  306. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/buttongroup.py +0 -0
  307. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/calendar.py +0 -0
  308. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/carousel.py +0 -0
  309. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/chart.py +0 -0
  310. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/compositeframe.py +0 -0
  311. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/contextmenu.py +0 -0
  312. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/dateentry.py +0 -0
  313. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/dropdownbutton.py +0 -0
  314. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/expander.py +0 -0
  315. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/field.py +0 -0
  316. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/gallery.py +0 -0
  317. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/list/__init__.py +0 -0
  318. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/list/listitem.py +0 -0
  319. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/list/listview.py +0 -0
  320. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/menu/__init__.py +0 -0
  321. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/menu/model.py +0 -0
  322. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/menu/render_native.py +0 -0
  323. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/menu/render_themed.py +0 -0
  324. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/meter.py +0 -0
  325. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/numericentry.py +0 -0
  326. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/pagestack.py +0 -0
  327. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/passwordentry.py +0 -0
  328. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/pathentry.py +0 -0
  329. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/picture.py +0 -0
  330. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/radiogroup.py +0 -0
  331. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/scrolledtext.py +0 -0
  332. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/scrolledtext.pyi +0 -0
  333. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/scrollview.py +0 -0
  334. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/selectbox.py +0 -0
  335. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/shell/__init__.py +0 -0
  336. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/shell/content_host.py +0 -0
  337. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/shell/layout.py +0 -0
  338. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/shell/nav_panel.py +0 -0
  339. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/shell/providers.py +0 -0
  340. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/shell/rail.py +0 -0
  341. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/shell/shell.py +0 -0
  342. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/shell/workspace.py +0 -0
  343. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/sidenav/__init__.py +0 -0
  344. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/sidenav/header.py +0 -0
  345. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/sidenav/separator.py +0 -0
  346. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/slider/__init__.py +0 -0
  347. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/slider/_shared.py +0 -0
  348. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/slider/rangeslider.py +0 -0
  349. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/slider/slider.py +0 -0
  350. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/spinnerentry.py +0 -0
  351. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tableview/__init__.py +0 -0
  352. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tableview/tableview.py +0 -0
  353. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tableview/types.py +0 -0
  354. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tabs/__init__.py +0 -0
  355. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tabs/tabitem.py +0 -0
  356. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tabs/tabs.py +0 -0
  357. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tabs/tabview.py +0 -0
  358. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/__init__.py +0 -0
  359. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/change.py +0 -0
  360. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/codeeditor.py +0 -0
  361. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/core.py +0 -0
  362. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/decoration.py +0 -0
  363. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/diff.py +0 -0
  364. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +0 -0
  365. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +0 -0
  366. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +0 -0
  367. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +0 -0
  368. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +0 -0
  369. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +0 -0
  370. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/filter.py +0 -0
  371. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/redirector.py +0 -0
  372. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/search_overlay.py +0 -0
  373. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/sidebar.py +0 -0
  374. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/style_registry.py +0 -0
  375. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/textarea.py +0 -0
  376. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textarea/undo.py +0 -0
  377. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/textentry.py +0 -0
  378. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/timeentry.py +0 -0
  379. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/toast.py +0 -0
  380. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/toast_stack.py +0 -0
  381. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/togglegroup.py +0 -0
  382. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/toolbar.py +0 -0
  383. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tooltip.py +0 -0
  384. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tree/__init__.py +0 -0
  385. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tree/source_binding.py +0 -0
  386. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tree/treeitem.py +0 -0
  387. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tree/treenode.py +0 -0
  388. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/composites/tree/treeview.py +0 -0
  389. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/mixins/__init__.py +0 -0
  390. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/mixins/configure_mixin.py +0 -0
  391. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/mixins/entry_mixin.py +0 -0
  392. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/mixins/font_mixin.py +0 -0
  393. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/mixins/icon_mixin.py +0 -0
  394. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/mixins/localization_mixin.py +0 -0
  395. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/mixins/signal_mixin.py +0 -0
  396. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/mixins/validation_mixin.py +0 -0
  397. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/__init__.py +0 -0
  398. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/_menubutton.py +0 -0
  399. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/badge.py +0 -0
  400. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/button.py +0 -0
  401. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/card.py +0 -0
  402. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/checkbutton.py +0 -0
  403. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/checktoggle.py +0 -0
  404. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/combobox.py +0 -0
  405. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/entry.py +0 -0
  406. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/flexframe.py +0 -0
  407. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/frame.py +0 -0
  408. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/gridframe.py +0 -0
  409. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/label.py +0 -0
  410. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/labelframe.py +0 -0
  411. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/optionmenu.py +0 -0
  412. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/packframe.py +0 -0
  413. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/panedwindow.py +0 -0
  414. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/progressbar.py +0 -0
  415. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/radiobutton.py +0 -0
  416. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/radiotoggle.py +0 -0
  417. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/scrollbar.py +0 -0
  418. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/separator.py +0 -0
  419. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/sizegrip.py +0 -0
  420. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/spinbox.py +0 -0
  421. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/switch.py +0 -0
  422. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/_impl/primitives/treeview.py +0 -0
  423. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/app.py +0 -0
  424. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/appshell.py +0 -0
  425. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/avatar.py +0 -0
  426. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/boolean_controls.py +0 -0
  427. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/button.py +0 -0
  428. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/buttongroup.py +0 -0
  429. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/calendar.py +0 -0
  430. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/card.py +0 -0
  431. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/carousel.py +0 -0
  432. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/chart.py +0 -0
  433. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/codeeditor.py +0 -0
  434. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/contextmenu.py +0 -0
  435. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/datatable.py +0 -0
  436. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/datefield.py +0 -0
  437. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/divider.py +0 -0
  438. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/expander.py +0 -0
  439. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/gallery.py +0 -0
  440. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/gauge.py +0 -0
  441. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/grid.py +0 -0
  442. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/groupbox.py +0 -0
  443. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/label.py +0 -0
  444. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/listview.py +0 -0
  445. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/menubutton.py +0 -0
  446. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/numberfield.py +0 -0
  447. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/pagestack.py +0 -0
  448. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/passwordfield.py +0 -0
  449. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/pathfield.py +0 -0
  450. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/picture.py +0 -0
  451. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/progressbar.py +0 -0
  452. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/radio_variants.py +0 -0
  453. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/radiogroup.py +0 -0
  454. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/scrollbar.py +0 -0
  455. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/scrollview.py +0 -0
  456. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/select.py +0 -0
  457. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/selectbutton.py +0 -0
  458. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/sidebar_toggle.py +0 -0
  459. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/sizegrip.py +0 -0
  460. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/slider.py +0 -0
  461. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/spinbox.py +0 -0
  462. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/spinnerfield.py +0 -0
  463. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/splash.py +0 -0
  464. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/splitview.py +0 -0
  465. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/stacks.py +0 -0
  466. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/statusbar.py +0 -0
  467. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/tabs.py +0 -0
  468. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/textarea.py +0 -0
  469. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/textfield.py +0 -0
  470. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/theme_toggle.py +0 -0
  471. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/timefield.py +0 -0
  472. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/toast.py +0 -0
  473. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/togglegroup.py +0 -0
  474. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/toolbar.py +0 -0
  475. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/tooltip.py +0 -0
  476. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/tree.py +0 -0
  477. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/types.py +0 -0
  478. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack/widgets/window.py +0 -0
  479. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack.egg-info/dependency_links.txt +0 -0
  480. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack.egg-info/entry_points.txt +0 -0
  481. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack.egg-info/requires.txt +0 -0
  482. {bootstack-0.2.3 → bootstack-0.3.0}/src/bootstack.egg-info/top_level.txt +0 -0
  483. {bootstack-0.2.3 → bootstack-0.3.0}/tests/test_dialog_anchor.py +0 -0
  484. {bootstack-0.2.3 → bootstack-0.3.0}/tests/test_events_doc_coverage.py +0 -0
  485. {bootstack-0.2.3 → bootstack-0.3.0}/tests/test_hot_reload.py +0 -0
  486. {bootstack-0.2.3 → bootstack-0.3.0}/tests/test_images.py +0 -0
  487. {bootstack-0.2.3 → bootstack-0.3.0}/tests/test_images_gui.py +0 -0
  488. {bootstack-0.2.3 → bootstack-0.3.0}/tests/test_intl_format.py +0 -0
  489. {bootstack-0.2.3 → bootstack-0.3.0}/tests/test_navmodel.py +0 -0
  490. {bootstack-0.2.3 → bootstack-0.3.0}/tests/test_on_color_contrast.py +0 -0
  491. {bootstack-0.2.3 → bootstack-0.3.0}/tests/test_public_surface.py +0 -0
  492. {bootstack-0.2.3 → bootstack-0.3.0}/tests/test_store.py +0 -0
  493. {bootstack-0.2.3 → bootstack-0.3.0}/tests/test_theme_from_existing.py +0 -0
  494. {bootstack-0.2.3 → bootstack-0.3.0}/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.0
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.0"
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.0"
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`.
@@ -260,7 +260,6 @@ class DateDialog:
260
260
  text="button.ok",
261
261
  role="primary",
262
262
  default=True,
263
- closes=False,
264
263
  command=lambda dlg: self._on_confirm_range(),
265
264
  ),
266
265
  ]
@@ -318,22 +317,39 @@ class DateDialog:
318
317
 
319
318
  self._confirm(selected)
320
319
 
321
- def _on_confirm_range(self) -> None:
320
+ def _on_confirm_range(self) -> bool:
322
321
  """Confirm the selected range when OK is clicked.
323
322
 
324
- No-op (dialog stays open) until both endpoints are selected.
323
+ Refuses the press — leaving the dialog open until both endpoints are
324
+ selected. The OK button hands that answer back to `Dialog`, which owns
325
+ the close.
326
+
327
+ Unlike `_confirm`, this releases no grab of its own. It does not need
328
+ to: `Dialog` destroys the toplevel, and Tk releases a grab held by a
329
+ window when that window is destroyed (measured — see
330
+ `development/probe_437_review2_fixes.py`). `_confirm` keeps its
331
+ `grab_release()` because it destroys the window itself, where the call
332
+ is at least self-documenting.
325
333
  """
326
334
  if not self._picker:
327
- return
335
+ return False
328
336
  start, end = self._picker.get_range()
329
337
  if start is None or end is None:
330
- return
331
- self._confirm((start, end))
338
+ return False
339
+ self._record((start, end))
340
+ return True
332
341
 
333
- def _confirm(self, result: date | Tuple[date, date]) -> None:
334
- """Record the result, emit, and close the dialog."""
342
+ def _record(self, result: date | Tuple[date, date]) -> None:
343
+ """Record the result and notify listeners, without closing."""
335
344
  self._dialog.result = result
336
345
  self._emit_result(result, confirmed=True)
346
+
347
+ def _confirm(self, result: date | Tuple[date, date]) -> None:
348
+ """Record the result, emit, and close the dialog.
349
+
350
+ Used by the commit-on-select path, which has no button behind it.
351
+ """
352
+ self._record(result)
337
353
  if self._dialog.toplevel:
338
354
  top = self._dialog.toplevel
339
355
  try:
@@ -84,14 +84,15 @@ class DialogButton:
84
84
  result: Any | None = None # value assigned to dialog.result
85
85
  """Value assigned to `dialog.result` when clicked."""
86
86
 
87
- closes: bool = True # close dialog after click
88
- """Whether the button closes the dialog when clicked."""
89
-
90
87
  default: bool = False # default button (Enter)
91
88
  """Whether this is the default button (focused, triggered by Enter)."""
92
89
 
93
- command: Callable[[Dialog], None] | None = None
94
- """Callback invoked when clicked."""
90
+ command: Callable[[Dialog], Any] | None = None
91
+ """Callback invoked when clicked. Returning `False` refuses the press — no
92
+ result is recorded, and a dialog stays open — which is how a button rejects
93
+ the input it was given. Any other return value, including `None`, accepts
94
+ it. The same applies wherever these specifications are used, including the
95
+ button row of a `Form`."""
95
96
 
96
97
  accent: AccentToken | str | None = None
97
98
  """Accent token for styling (e.g. `'primary'`, `'danger'`)."""
@@ -501,11 +502,21 @@ class Dialog:
501
502
 
502
503
  def make_command(s: DialogButton):
503
504
  def cmd():
504
- if s.command:
505
- s.command(self)
505
+ # A command returning False refuses the press: the dialog
506
+ # neither records a result nor closes. Without this, a
507
+ # command that declines to act (a form failing validation,
508
+ # say) still stamps its button's result, and that value
509
+ # outlives the press — a later cancel cannot clear it,
510
+ # because a cancel button's own result is None and the write
511
+ # below is skipped for None.
512
+ if s.command and s.command(self) is False:
513
+ return
506
514
  if s.result is not None:
507
515
  self.result = s.result
508
- if s.closes and self._toplevel:
516
+ # `winfo_exists` rather than a bare truth test: a command is
517
+ # free to close the dialog itself, and destroying an already
518
+ # destroyed toplevel raises.
519
+ if self._toplevel and self._toplevel.winfo_exists():
509
520
  self._toplevel.destroy()
510
521
 
511
522
  return cmd
@@ -531,7 +542,41 @@ class Dialog:
531
542
 
532
543
  if default_button is not None:
533
544
  default_button.focus_set()
534
- self._toplevel.bind("<Return>", lambda e, b=default_button: b.invoke())
545
+ top = self._toplevel
546
+
547
+ def press_default(event: Any, b: Any = default_button) -> None:
548
+ """Press the default button, unless a button already has.
549
+
550
+ This binding lives on the toplevel, so it runs for every key
551
+ press anywhere in the dialog. A press delivered to a button has
552
+ already invoked that button by the time the bindtag walk
553
+ reaches here — buttons carry a class binding for Return and
554
+ KP_Enter — so firing again would either invoke the same button
555
+ twice or, with focus on some OTHER button, run the default
556
+ button's command on top of the one the user actually pressed.
557
+ Standing down is also what makes keyboard traversal mean
558
+ something: Enter presses the button you tabbed to.
559
+
560
+ A DISABLED button is the exception: its class binding runs but
561
+ `invoke` does nothing, so nothing has answered the key and the
562
+ default button should still get it. Standing down there would
563
+ leave the keyboard dead for the whole dialog — which is what a
564
+ content button that greys itself out on click would cause.
565
+
566
+ The remaining case is the one this binding exists for — focus
567
+ in an entry, as in `QueryDialog`, where nothing else would
568
+ answer the key.
569
+ """
570
+ pressed = event.widget
571
+ if "TButton" in pressed.bindtags() and not pressed.instate(["disabled"]):
572
+ return
573
+ b.invoke()
574
+
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.
578
+ for key in ("<Return>", "<KP_Enter>"):
579
+ self._toplevel.bind(key, press_default)
535
580
 
536
581
  if cancel_button is not None:
537
582
  self._toplevel.bind("<Escape>", lambda e, b=cancel_button: b.invoke())
@@ -636,11 +681,11 @@ class Dialog:
636
681
  def _style_for_role(self, role: ButtonRole) -> tuple[str | None, str | None]:
637
682
  """Return (accent, variant) tuple for a button role."""
638
683
  if role == "primary":
639
- return ("primary", None)
684
+ return "primary", None
640
685
  if role == "secondary":
641
- return ("default", None)
686
+ return "default", None
642
687
  if role == "danger":
643
- return ("danger", None)
688
+ return "danger", None
644
689
  if role == "cancel":
645
- return ("default", "outline")
646
- return ("default", None)
690
+ return "default", "outline"
691
+ return "default", None