bootstack 0.1.0__py3-none-any.whl

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 (471) hide show
  1. bootstack/__init__.py +157 -0
  2. bootstack/__main__.py +5 -0
  3. bootstack/_core/__init__.py +21 -0
  4. bootstack/_core/capabilities/__init__.py +45 -0
  5. bootstack/_core/capabilities/after.py +103 -0
  6. bootstack/_core/capabilities/bind.py +192 -0
  7. bootstack/_core/capabilities/bindtags.py +112 -0
  8. bootstack/_core/capabilities/busy.py +72 -0
  9. bootstack/_core/capabilities/clipboard.py +89 -0
  10. bootstack/_core/capabilities/focus.py +118 -0
  11. bootstack/_core/capabilities/grab.py +65 -0
  12. bootstack/_core/capabilities/grid.py +211 -0
  13. bootstack/_core/capabilities/localization.py +231 -0
  14. bootstack/_core/capabilities/pack.py +140 -0
  15. bootstack/_core/capabilities/place.py +113 -0
  16. bootstack/_core/capabilities/selection.py +136 -0
  17. bootstack/_core/capabilities/signals.py +244 -0
  18. bootstack/_core/capabilities/winfo.py +315 -0
  19. bootstack/_core/colorutils.py +234 -0
  20. bootstack/_core/exceptions.py +25 -0
  21. bootstack/_core/images.py +463 -0
  22. bootstack/_core/mixins/__init__.py +1 -0
  23. bootstack/_core/mixins/ttk_state.py +35 -0
  24. bootstack/_core/mixins/widget.py +132 -0
  25. bootstack/_core/paths.py +49 -0
  26. bootstack/_core/publisher.py +149 -0
  27. bootstack/_core/variables.py +62 -0
  28. bootstack/_runtime/__init__.py +3 -0
  29. bootstack/_runtime/app.py +930 -0
  30. bootstack/_runtime/base_window.py +945 -0
  31. bootstack/_runtime/events.py +399 -0
  32. bootstack/_runtime/shortcuts.py +496 -0
  33. bootstack/_runtime/tk_patch.py +43 -0
  34. bootstack/_runtime/toplevel.py +276 -0
  35. bootstack/_runtime/utility.py +457 -0
  36. bootstack/_runtime/visual_focus.py +240 -0
  37. bootstack/_runtime/window_utilities.py +1123 -0
  38. bootstack/assets/__init__.py +21 -0
  39. bootstack/assets/bootstack.ico +0 -0
  40. bootstack/assets/bootstack.png +0 -0
  41. bootstack/assets/elements/__init__.py +0 -0
  42. bootstack/assets/elements/badge-pill.png +0 -0
  43. bootstack/assets/elements/badge-square.png +0 -0
  44. bootstack/assets/elements/button-compact.png +0 -0
  45. bootstack/assets/elements/button-default.png +0 -0
  46. bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
  47. bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
  48. bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
  49. bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
  50. bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
  51. bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
  52. bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
  53. bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
  54. bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
  55. bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
  56. bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
  57. bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
  58. bootstack/assets/elements/card.png +0 -0
  59. bootstack/assets/elements/checkbox-checked.png +0 -0
  60. bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  61. bootstack/assets/elements/checkbox-unchecked.png +0 -0
  62. bootstack/assets/elements/field.png +0 -0
  63. bootstack/assets/elements/input-addon-compact.png +0 -0
  64. bootstack/assets/elements/input-addon-default.png +0 -0
  65. bootstack/assets/elements/input-compact.png +0 -0
  66. bootstack/assets/elements/input-default.png +0 -0
  67. bootstack/assets/elements/list-item-separated.png +0 -0
  68. bootstack/assets/elements/list-item.png +0 -0
  69. bootstack/assets/elements/listrow-compact.png +0 -0
  70. bootstack/assets/elements/listrow-default.png +0 -0
  71. bootstack/assets/elements/manifest.toml +361 -0
  72. bootstack/assets/elements/menu-item.png +0 -0
  73. bootstack/assets/elements/navitem-compact.png +0 -0
  74. bootstack/assets/elements/navitem-default.png +0 -0
  75. bootstack/assets/elements/progressbar-h-compact.png +0 -0
  76. bootstack/assets/elements/progressbar-h-default.png +0 -0
  77. bootstack/assets/elements/progressbar-v-compact.png +0 -0
  78. bootstack/assets/elements/progressbar-v-default.png +0 -0
  79. bootstack/assets/elements/radiobutton.png +0 -0
  80. bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  81. bootstack/assets/elements/scrollbar-vertical.png +0 -0
  82. bootstack/assets/elements/slider-handle.png +0 -0
  83. bootstack/assets/elements/slider-track-h.png +0 -0
  84. bootstack/assets/elements/slider-track-v.png +0 -0
  85. bootstack/assets/elements/switch-off.png +0 -0
  86. bootstack/assets/elements/switch-on.png +0 -0
  87. bootstack/assets/elements/tab-h.png +0 -0
  88. bootstack/assets/elements/tab-v.png +0 -0
  89. bootstack/assets/icons/bootstrap.ttf +0 -0
  90. bootstack/assets/icons/glyphmap.json +2080 -0
  91. bootstack/assets/icons/icon_metrics.json +12470 -0
  92. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  93. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +856 -0
  94. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
  95. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +878 -0
  96. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  97. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +856 -0
  98. bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  99. bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +856 -0
  100. bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  101. bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +856 -0
  102. bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  103. bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +878 -0
  104. bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  105. bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +856 -0
  106. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  107. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +856 -0
  108. bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  109. bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +854 -0
  110. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  111. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +845 -0
  112. bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  113. bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +844 -0
  114. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  115. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +917 -0
  116. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  117. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +845 -0
  118. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  119. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +844 -0
  120. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  121. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +844 -0
  122. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  123. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +845 -0
  124. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  125. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +845 -0
  126. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  127. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +845 -0
  128. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  129. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +845 -0
  130. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  131. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +845 -0
  132. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  133. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +845 -0
  134. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  135. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +845 -0
  136. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  137. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +845 -0
  138. bootstack/cli/__init__.py +133 -0
  139. bootstack/cli/__main__.py +6 -0
  140. bootstack/cli/add.py +395 -0
  141. bootstack/cli/appicon.py +285 -0
  142. bootstack/cli/build.py +115 -0
  143. bootstack/cli/config.py +313 -0
  144. bootstack/cli/demo.py +564 -0
  145. bootstack/cli/dev.py +153 -0
  146. bootstack/cli/doctor.py +195 -0
  147. bootstack/cli/icons.py +98 -0
  148. bootstack/cli/promote.py +120 -0
  149. bootstack/cli/pyinstaller.py +268 -0
  150. bootstack/cli/run.py +95 -0
  151. bootstack/cli/start.py +117 -0
  152. bootstack/cli/templates/__init__.py +931 -0
  153. bootstack/clipboard.py +48 -0
  154. bootstack/constants.py +318 -0
  155. bootstack/data/README.md +615 -0
  156. bootstack/data/__init__.py +78 -0
  157. bootstack/data/_observable.py +276 -0
  158. bootstack/data/base.py +780 -0
  159. bootstack/data/file_source.py +367 -0
  160. bootstack/data/memory_source.py +450 -0
  161. bootstack/data/query.py +367 -0
  162. bootstack/data/readers.py +289 -0
  163. bootstack/data/sqlite_source.py +869 -0
  164. bootstack/data/types.py +354 -0
  165. bootstack/data/writers.py +232 -0
  166. bootstack/dev/__init__.py +36 -0
  167. bootstack/dev/_capture.py +141 -0
  168. bootstack/dev/_env.py +43 -0
  169. bootstack/dev/_registry.py +140 -0
  170. bootstack/dev/_reloader.py +351 -0
  171. bootstack/dev/_reset.py +50 -0
  172. bootstack/dev/_watcher.py +91 -0
  173. bootstack/dialogs/__init__.py +948 -0
  174. bootstack/dialogs/_impl/__init__.py +47 -0
  175. bootstack/dialogs/_impl/colorchooser.py +588 -0
  176. bootstack/dialogs/_impl/datedialog.py +450 -0
  177. bootstack/dialogs/_impl/dialog.py +594 -0
  178. bootstack/dialogs/_impl/filterdialog.py +358 -0
  179. bootstack/dialogs/_impl/fontdialog.py +364 -0
  180. bootstack/dialogs/_impl/formdialog.py +564 -0
  181. bootstack/dialogs/_impl/message.py +486 -0
  182. bootstack/dialogs/_impl/query.py +570 -0
  183. bootstack/errors.py +67 -0
  184. bootstack/events/__init__.py +111 -0
  185. bootstack/events/_event.py +135 -0
  186. bootstack/events/_payloads.py +539 -0
  187. bootstack/events/_subscription.py +38 -0
  188. bootstack/i18n/README.md +77 -0
  189. bootstack/i18n/__init__.py +23 -0
  190. bootstack/i18n/catalog.py +121 -0
  191. bootstack/i18n/intl_format.py +584 -0
  192. bootstack/i18n/msgcat.py +425 -0
  193. bootstack/i18n/specs.py +156 -0
  194. bootstack/images.py +563 -0
  195. bootstack/py.typed +1 -0
  196. bootstack/scheduling/__init__.py +11 -0
  197. bootstack/scheduling/_schedule.py +218 -0
  198. bootstack/shortcuts.py +21 -0
  199. bootstack/signals/README.md +98 -0
  200. bootstack/signals/__init__.py +10 -0
  201. bootstack/signals/integration.py +100 -0
  202. bootstack/signals/signal.py +353 -0
  203. bootstack/signals/types.py +6 -0
  204. bootstack/store.py +286 -0
  205. bootstack/streams/__init__.py +12 -0
  206. bootstack/streams/_stream.py +321 -0
  207. bootstack/style/__init__.py +38 -0
  208. bootstack/style/builders/__init__.py +51 -0
  209. bootstack/style/builders/badge.py +46 -0
  210. bootstack/style/builders/button.py +339 -0
  211. bootstack/style/builders/buttongroup.py +311 -0
  212. bootstack/style/builders/calendar.py +271 -0
  213. bootstack/style/builders/checkbutton.py +110 -0
  214. bootstack/style/builders/combobox.py +113 -0
  215. bootstack/style/builders/contextmenu.py +268 -0
  216. bootstack/style/builders/entry.py +82 -0
  217. bootstack/style/builders/expander.py +148 -0
  218. bootstack/style/builders/field.py +335 -0
  219. bootstack/style/builders/frame.py +50 -0
  220. bootstack/style/builders/label.py +28 -0
  221. bootstack/style/builders/labelframe.py +34 -0
  222. bootstack/style/builders/listview.py +369 -0
  223. bootstack/style/builders/menubar.py +91 -0
  224. bootstack/style/builders/menubutton.py +359 -0
  225. bootstack/style/builders/panedwindow.py +25 -0
  226. bootstack/style/builders/progressbar.py +67 -0
  227. bootstack/style/builders/radiobutton.py +99 -0
  228. bootstack/style/builders/scale.py +64 -0
  229. bootstack/style/builders/scrollbar.py +225 -0
  230. bootstack/style/builders/separator.py +49 -0
  231. bootstack/style/builders/sidenav.py +643 -0
  232. bootstack/style/builders/sizegrip.py +15 -0
  233. bootstack/style/builders/spinbox.py +119 -0
  234. bootstack/style/builders/switch.py +70 -0
  235. bootstack/style/builders/tabitem.py +204 -0
  236. bootstack/style/builders/togglegroup.py +294 -0
  237. bootstack/style/builders/toolbutton.py +275 -0
  238. bootstack/style/builders/tooltip.py +26 -0
  239. bootstack/style/builders/treeview.py +193 -0
  240. bootstack/style/builders/utils.py +455 -0
  241. bootstack/style/builders_tk/__init__.py +16 -0
  242. bootstack/style/builders_tk/defaults.py +229 -0
  243. bootstack/style/element.py +173 -0
  244. bootstack/style/fonts.py +123 -0
  245. bootstack/style/style.py +609 -0
  246. bootstack/style/style_builder_base.py +716 -0
  247. bootstack/style/style_builder_mixed.py +93 -0
  248. bootstack/style/style_builder_tk.py +109 -0
  249. bootstack/style/style_builder_ttk.py +353 -0
  250. bootstack/style/style_resolver.py +447 -0
  251. bootstack/style/theme.py +245 -0
  252. bootstack/style/theme_provider.py +471 -0
  253. bootstack/style/themes/__init__.py +128 -0
  254. bootstack/style/tk_patch.py +5 -0
  255. bootstack/style/token_maps.py +41 -0
  256. bootstack/style/types.py +32 -0
  257. bootstack/style/typography.py +523 -0
  258. bootstack/style/utility.py +746 -0
  259. bootstack/types.py +39 -0
  260. bootstack/validation/__init__.py +6 -0
  261. bootstack/validation/types.py +15 -0
  262. bootstack/validation/validation_result.py +17 -0
  263. bootstack/validation/validation_rules.py +205 -0
  264. bootstack/widgets/__init__.py +74 -0
  265. bootstack/widgets/_core/__init__.py +31 -0
  266. bootstack/widgets/_core/app_config.py +266 -0
  267. bootstack/widgets/_core/base.py +461 -0
  268. bootstack/widgets/_core/container.py +334 -0
  269. bootstack/widgets/_core/context.py +35 -0
  270. bootstack/widgets/_core/events.py +130 -0
  271. bootstack/widgets/_core/field_mixin.py +353 -0
  272. bootstack/widgets/_core/icon_image_props.py +72 -0
  273. bootstack/widgets/_core/image_binding.py +93 -0
  274. bootstack/widgets/_core/navmodel.py +345 -0
  275. bootstack/widgets/_core/options.py +210 -0
  276. bootstack/widgets/_core/selection_group.py +130 -0
  277. bootstack/widgets/_core/window_controls.py +96 -0
  278. bootstack/widgets/_core/window_menu.py +246 -0
  279. bootstack/widgets/_impl/__init__.py +1 -0
  280. bootstack/widgets/_impl/_internal/__init__.py +0 -0
  281. bootstack/widgets/_impl/_internal/wrapper_base.py +307 -0
  282. bootstack/widgets/_impl/_parts/__init__.py +11 -0
  283. bootstack/widgets/_impl/_parts/numberentry_part.py +385 -0
  284. bootstack/widgets/_impl/_parts/spinnerentry_part.py +434 -0
  285. bootstack/widgets/_impl/_parts/textentry_part.py +406 -0
  286. bootstack/widgets/_impl/composites/__init__.py +33 -0
  287. bootstack/widgets/_impl/composites/_dateutils.py +33 -0
  288. bootstack/widgets/_impl/composites/_image_fit.py +105 -0
  289. bootstack/widgets/_impl/composites/accordion.py +381 -0
  290. bootstack/widgets/_impl/composites/avatar.py +191 -0
  291. bootstack/widgets/_impl/composites/buttongroup.py +371 -0
  292. bootstack/widgets/_impl/composites/calendar.py +972 -0
  293. bootstack/widgets/_impl/composites/carousel.py +567 -0
  294. bootstack/widgets/_impl/composites/chart.py +882 -0
  295. bootstack/widgets/_impl/composites/compositeframe.py +298 -0
  296. bootstack/widgets/_impl/composites/contextmenu.py +1951 -0
  297. bootstack/widgets/_impl/composites/dateentry.py +404 -0
  298. bootstack/widgets/_impl/composites/dropdownbutton.py +325 -0
  299. bootstack/widgets/_impl/composites/expander.py +515 -0
  300. bootstack/widgets/_impl/composites/field.py +670 -0
  301. bootstack/widgets/_impl/composites/form.py +1066 -0
  302. bootstack/widgets/_impl/composites/gallery.py +551 -0
  303. bootstack/widgets/_impl/composites/list/__init__.py +15 -0
  304. bootstack/widgets/_impl/composites/list/listitem.py +802 -0
  305. bootstack/widgets/_impl/composites/list/listview.py +1433 -0
  306. bootstack/widgets/_impl/composites/menu/__init__.py +18 -0
  307. bootstack/widgets/_impl/composites/menu/model.py +358 -0
  308. bootstack/widgets/_impl/composites/menu/render_native.py +134 -0
  309. bootstack/widgets/_impl/composites/menu/render_themed.py +134 -0
  310. bootstack/widgets/_impl/composites/meter.py +860 -0
  311. bootstack/widgets/_impl/composites/numericentry.py +201 -0
  312. bootstack/widgets/_impl/composites/pagestack.py +395 -0
  313. bootstack/widgets/_impl/composites/passwordentry.py +142 -0
  314. bootstack/widgets/_impl/composites/pathentry.py +168 -0
  315. bootstack/widgets/_impl/composites/picture.py +289 -0
  316. bootstack/widgets/_impl/composites/radiogroup.py +511 -0
  317. bootstack/widgets/_impl/composites/scrolledtext.py +375 -0
  318. bootstack/widgets/_impl/composites/scrolledtext.pyi +186 -0
  319. bootstack/widgets/_impl/composites/scrollview.py +764 -0
  320. bootstack/widgets/_impl/composites/selectbox.py +1026 -0
  321. bootstack/widgets/_impl/composites/shell/__init__.py +40 -0
  322. bootstack/widgets/_impl/composites/shell/content_host.py +52 -0
  323. bootstack/widgets/_impl/composites/shell/layout.py +335 -0
  324. bootstack/widgets/_impl/composites/shell/nav_panel.py +345 -0
  325. bootstack/widgets/_impl/composites/shell/providers.py +558 -0
  326. bootstack/widgets/_impl/composites/shell/rail.py +117 -0
  327. bootstack/widgets/_impl/composites/shell/shell.py +581 -0
  328. bootstack/widgets/_impl/composites/shell/workspace.py +273 -0
  329. bootstack/widgets/_impl/composites/sidenav/__init__.py +16 -0
  330. bootstack/widgets/_impl/composites/sidenav/header.py +81 -0
  331. bootstack/widgets/_impl/composites/sidenav/separator.py +44 -0
  332. bootstack/widgets/_impl/composites/slider/__init__.py +7 -0
  333. bootstack/widgets/_impl/composites/slider/_shared.py +195 -0
  334. bootstack/widgets/_impl/composites/slider/rangeslider.py +982 -0
  335. bootstack/widgets/_impl/composites/slider/slider.py +851 -0
  336. bootstack/widgets/_impl/composites/spinnerentry.py +185 -0
  337. bootstack/widgets/_impl/composites/tableview/__init__.py +5 -0
  338. bootstack/widgets/_impl/composites/tableview/tableview.py +3388 -0
  339. bootstack/widgets/_impl/composites/tableview/types.py +169 -0
  340. bootstack/widgets/_impl/composites/tabs/__init__.py +23 -0
  341. bootstack/widgets/_impl/composites/tabs/tabitem.py +389 -0
  342. bootstack/widgets/_impl/composites/tabs/tabs.py +974 -0
  343. bootstack/widgets/_impl/composites/tabs/tabview.py +650 -0
  344. bootstack/widgets/_impl/composites/textarea/__init__.py +26 -0
  345. bootstack/widgets/_impl/composites/textarea/change.py +46 -0
  346. bootstack/widgets/_impl/composites/textarea/codeeditor.py +526 -0
  347. bootstack/widgets/_impl/composites/textarea/core.py +495 -0
  348. bootstack/widgets/_impl/composites/textarea/decoration.py +42 -0
  349. bootstack/widgets/_impl/composites/textarea/diff.py +127 -0
  350. bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +1 -0
  351. bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +138 -0
  352. bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +158 -0
  353. bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +138 -0
  354. bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +312 -0
  355. bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +177 -0
  356. bootstack/widgets/_impl/composites/textarea/filter.py +171 -0
  357. bootstack/widgets/_impl/composites/textarea/search_overlay.py +459 -0
  358. bootstack/widgets/_impl/composites/textarea/sidebar.py +88 -0
  359. bootstack/widgets/_impl/composites/textarea/style_registry.py +178 -0
  360. bootstack/widgets/_impl/composites/textarea/textarea.py +606 -0
  361. bootstack/widgets/_impl/composites/textarea/undo.py +217 -0
  362. bootstack/widgets/_impl/composites/textentry.py +57 -0
  363. bootstack/widgets/_impl/composites/timeentry.py +176 -0
  364. bootstack/widgets/_impl/composites/toast.py +390 -0
  365. bootstack/widgets/_impl/composites/toast_stack.py +156 -0
  366. bootstack/widgets/_impl/composites/togglegroup.py +418 -0
  367. bootstack/widgets/_impl/composites/toolbar.py +608 -0
  368. bootstack/widgets/_impl/composites/tooltip.py +491 -0
  369. bootstack/widgets/_impl/composites/tree/__init__.py +7 -0
  370. bootstack/widgets/_impl/composites/tree/source_binding.py +136 -0
  371. bootstack/widgets/_impl/composites/tree/treeitem.py +393 -0
  372. bootstack/widgets/_impl/composites/tree/treenode.py +174 -0
  373. bootstack/widgets/_impl/composites/tree/treeview.py +841 -0
  374. bootstack/widgets/_impl/mixins/__init__.py +24 -0
  375. bootstack/widgets/_impl/mixins/configure_mixin.py +216 -0
  376. bootstack/widgets/_impl/mixins/entry_mixin.py +134 -0
  377. bootstack/widgets/_impl/mixins/font_mixin.py +368 -0
  378. bootstack/widgets/_impl/mixins/icon_mixin.py +61 -0
  379. bootstack/widgets/_impl/mixins/localization_mixin.py +253 -0
  380. bootstack/widgets/_impl/mixins/signal_mixin.py +268 -0
  381. bootstack/widgets/_impl/mixins/validation_mixin.py +226 -0
  382. bootstack/widgets/_impl/primitives/__init__.py +49 -0
  383. bootstack/widgets/_impl/primitives/_menubutton.py +107 -0
  384. bootstack/widgets/_impl/primitives/badge.py +45 -0
  385. bootstack/widgets/_impl/primitives/button.py +76 -0
  386. bootstack/widgets/_impl/primitives/card.py +45 -0
  387. bootstack/widgets/_impl/primitives/checkbutton.py +124 -0
  388. bootstack/widgets/_impl/primitives/checktoggle.py +62 -0
  389. bootstack/widgets/_impl/primitives/combobox.py +156 -0
  390. bootstack/widgets/_impl/primitives/entry.py +87 -0
  391. bootstack/widgets/_impl/primitives/flexframe.py +448 -0
  392. bootstack/widgets/_impl/primitives/frame.py +185 -0
  393. bootstack/widgets/_impl/primitives/gridframe.py +546 -0
  394. bootstack/widgets/_impl/primitives/label.py +84 -0
  395. bootstack/widgets/_impl/primitives/labelframe.py +54 -0
  396. bootstack/widgets/_impl/primitives/optionmenu.py +387 -0
  397. bootstack/widgets/_impl/primitives/packframe.py +227 -0
  398. bootstack/widgets/_impl/primitives/panedwindow.py +45 -0
  399. bootstack/widgets/_impl/primitives/progressbar.py +83 -0
  400. bootstack/widgets/_impl/primitives/radiobutton.py +115 -0
  401. bootstack/widgets/_impl/primitives/radiotoggle.py +54 -0
  402. bootstack/widgets/_impl/primitives/scrollbar.py +42 -0
  403. bootstack/widgets/_impl/primitives/separator.py +43 -0
  404. bootstack/widgets/_impl/primitives/sizegrip.py +33 -0
  405. bootstack/widgets/_impl/primitives/spinbox.py +95 -0
  406. bootstack/widgets/_impl/primitives/switch.py +44 -0
  407. bootstack/widgets/_impl/primitives/treeview.py +69 -0
  408. bootstack/widgets/app.py +371 -0
  409. bootstack/widgets/appshell.py +1179 -0
  410. bootstack/widgets/avatar.py +140 -0
  411. bootstack/widgets/boolean_controls.py +455 -0
  412. bootstack/widgets/button.py +224 -0
  413. bootstack/widgets/buttongroup.py +239 -0
  414. bootstack/widgets/calendar.py +195 -0
  415. bootstack/widgets/card.py +159 -0
  416. bootstack/widgets/carousel.py +241 -0
  417. bootstack/widgets/chart.py +302 -0
  418. bootstack/widgets/codeeditor.py +675 -0
  419. bootstack/widgets/contextmenu.py +371 -0
  420. bootstack/widgets/datatable.py +688 -0
  421. bootstack/widgets/datefield.py +395 -0
  422. bootstack/widgets/divider.py +60 -0
  423. bootstack/widgets/expander.py +579 -0
  424. bootstack/widgets/form.py +200 -0
  425. bootstack/widgets/gallery.py +250 -0
  426. bootstack/widgets/gauge.py +168 -0
  427. bootstack/widgets/grid.py +121 -0
  428. bootstack/widgets/groupbox.py +162 -0
  429. bootstack/widgets/label.py +229 -0
  430. bootstack/widgets/listview.py +354 -0
  431. bootstack/widgets/menubutton.py +419 -0
  432. bootstack/widgets/numberfield.py +431 -0
  433. bootstack/widgets/pagestack.py +345 -0
  434. bootstack/widgets/passwordfield.py +383 -0
  435. bootstack/widgets/pathfield.py +454 -0
  436. bootstack/widgets/picture.py +251 -0
  437. bootstack/widgets/progressbar.py +106 -0
  438. bootstack/widgets/radio_variants.py +271 -0
  439. bootstack/widgets/radiogroup.py +228 -0
  440. bootstack/widgets/scrollbar.py +64 -0
  441. bootstack/widgets/scrollview.py +186 -0
  442. bootstack/widgets/select.py +302 -0
  443. bootstack/widgets/selectbutton.py +182 -0
  444. bootstack/widgets/sidebar_toggle.py +130 -0
  445. bootstack/widgets/sizegrip.py +42 -0
  446. bootstack/widgets/slider.py +408 -0
  447. bootstack/widgets/spinbox.py +147 -0
  448. bootstack/widgets/spinnerfield.py +413 -0
  449. bootstack/widgets/splash.py +367 -0
  450. bootstack/widgets/splitview.py +482 -0
  451. bootstack/widgets/stacks.py +249 -0
  452. bootstack/widgets/statusbar.py +189 -0
  453. bootstack/widgets/tabs.py +425 -0
  454. bootstack/widgets/textarea.py +459 -0
  455. bootstack/widgets/textfield.py +410 -0
  456. bootstack/widgets/theme_toggle.py +92 -0
  457. bootstack/widgets/timefield.py +340 -0
  458. bootstack/widgets/toast.py +314 -0
  459. bootstack/widgets/togglegroup.py +231 -0
  460. bootstack/widgets/toolbar.py +347 -0
  461. bootstack/widgets/tooltip.py +94 -0
  462. bootstack/widgets/tree.py +648 -0
  463. bootstack/widgets/types.py +305 -0
  464. bootstack/widgets/window.py +307 -0
  465. bootstack-0.1.0.dist-info/METADATA +301 -0
  466. bootstack-0.1.0.dist-info/RECORD +471 -0
  467. bootstack-0.1.0.dist-info/WHEEL +5 -0
  468. bootstack-0.1.0.dist-info/entry_points.txt +2 -0
  469. bootstack-0.1.0.dist-info/licenses/LICENSE +21 -0
  470. bootstack-0.1.0.dist-info/licenses/NOTICE +10 -0
  471. bootstack-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,450 @@
1
+ """Dialog wrapper around the Calendar widget.
2
+
3
+ Exposes a chrome-less, popover-capable date picker dialog that can close on
4
+ outside clicks and forwards Calendar options (disabled dates, bounds, etc.).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import tkinter
10
+ from datetime import date, datetime
11
+ from types import SimpleNamespace
12
+ from typing import Any, Callable, Iterable, Literal, Optional, Tuple, Union
13
+ from tkinter import Widget
14
+
15
+ from bootstack.widgets._impl.primitives import Frame
16
+ from bootstack.constants import BOTH, YES
17
+ from bootstack.dialogs._impl.dialog import Dialog, DialogButton
18
+ from bootstack._runtime.window_utilities import AnchorPoint
19
+ from bootstack.widgets._impl.composites.calendar import Calendar
20
+
21
+ ttk = SimpleNamespace(Frame=Frame)
22
+
23
+ __all__ = ["DateDialog"]
24
+
25
+
26
+ class _ChromeDialog(Dialog):
27
+ """Dialog that can optionally hide window chrome via override-redirect."""
28
+
29
+ def __init__(self, *args: Any, hide_window_chrome: bool = False, **kwargs: Any) -> None:
30
+ self._hide_window_chrome = hide_window_chrome
31
+ self._suppress_focus_out = False
32
+ self._outside_click_binding: str | None = None
33
+ super().__init__(*args, **kwargs)
34
+
35
+ def _create_toplevel(self):
36
+ super()._create_toplevel()
37
+ if self._hide_window_chrome and self._toplevel:
38
+ # Note: overrideredirect is automatically disabled on macOS
39
+ # in BaseWindow.overrideredirect() due to Tk/Cocoa issues
40
+ try:
41
+ self._toplevel.overrideredirect(True)
42
+ except Exception:
43
+ pass
44
+
45
+ def show(
46
+ self,
47
+ *,
48
+ position: Optional[Tuple[int, int]] = None,
49
+ modal: Optional[bool] = None,
50
+ anchor_to: Optional[Union[Widget, Literal["screen", "cursor", "parent"]]] = None,
51
+ anchor_point: AnchorPoint = 'center',
52
+ window_point: AnchorPoint = 'center',
53
+ offset: Tuple[int, int] = (0, 0),
54
+ auto_flip: Union[bool, Literal['vertical', 'horizontal']] = False
55
+ ):
56
+ """Override show to position before deiconify, avoiding placement flash.
57
+
58
+ Args:
59
+ position: Optional (x, y) coordinates to position the dialog.
60
+ modal: Override the mode's default modality.
61
+ anchor_to: Positioning target (Widget, "screen", "cursor", "parent", or None).
62
+ anchor_point: Point on the anchor target (n, s, e, w, ne, nw, se, sw, center).
63
+ window_point: Point on the dialog window (n, s, e, w, ne, nw, se, sw, center).
64
+ offset: Additional (x, y) offset in pixels from the anchor position.
65
+ auto_flip: Smart positioning to keep window on screen.
66
+ """
67
+ if modal is None:
68
+ modal = (self._mode == "modal")
69
+
70
+ self.result = None
71
+ self._create_toplevel()
72
+ if self._hide_window_chrome and self._toplevel:
73
+ self._toplevel.withdraw()
74
+
75
+ self._build_content()
76
+ self._build_footer()
77
+
78
+ self._position_dialog(
79
+ position=position,
80
+ anchor_to=anchor_to,
81
+ anchor_point=anchor_point,
82
+ window_point=window_point,
83
+ offset=offset,
84
+ auto_flip=auto_flip
85
+ )
86
+
87
+ if self._hide_window_chrome and self._toplevel:
88
+ self._toplevel.deiconify()
89
+ try:
90
+ self._toplevel.lift()
91
+ self._toplevel.focus_force()
92
+ except Exception:
93
+ pass
94
+
95
+ if self._alert:
96
+ self._toplevel.bell()
97
+
98
+ if self._mode == "popover":
99
+ self._suppress_focus_out = True
100
+ self._toplevel.bind("<FocusOut>", self._on_focus_out, add="+")
101
+ try:
102
+ self._toplevel.after(50, lambda: setattr(self, "_suppress_focus_out", False))
103
+ except Exception:
104
+ self._suppress_focus_out = False
105
+ self._bind_outside_click()
106
+
107
+ if modal:
108
+ # transient() is already called in Dialog._create_toplevel()
109
+ # Don't call it again here as it breaks mica effect on Windows
110
+ if self._mode == "modal":
111
+ self._toplevel.grab_set()
112
+ self._master.wait_window(self._toplevel)
113
+
114
+ def _on_focus_out(self, event: tkinter.Event):
115
+ if self._suppress_focus_out:
116
+ return
117
+ return super()._on_focus_out(event)
118
+
119
+ def _bind_outside_click(self) -> None:
120
+ """Close popover when clicking outside the toplevel."""
121
+ if not self._toplevel:
122
+ return
123
+
124
+ def handler(event: tkinter.Event) -> None:
125
+ widget = getattr(event, "widget", None)
126
+ if widget is None:
127
+ return
128
+
129
+ # Walk up the widget hierarchy to check if any parent is the toplevel
130
+ current = widget
131
+ toplevel_str = str(self._toplevel)
132
+ while current:
133
+ current_str = str(current)
134
+ if current_str == toplevel_str or current_str.startswith(toplevel_str + "."):
135
+ return
136
+ try:
137
+ current = current.master
138
+ except AttributeError:
139
+ break
140
+
141
+ # Schedule destroy for after current event processing completes
142
+ # This allows button commands inside the dialog to execute first
143
+ try:
144
+ if self._toplevel.winfo_exists():
145
+ self._toplevel.after_idle(lambda: self._destroy_if_exists())
146
+ except Exception:
147
+ pass
148
+
149
+ try:
150
+ self._outside_click_binding = self._toplevel.bind_all("<ButtonPress-1>", handler, add="+")
151
+ self._toplevel.bind("<Destroy>", lambda e: self._unbind_outside_click(), add="+")
152
+ except Exception:
153
+ self._outside_click_binding = None
154
+
155
+ def _destroy_if_exists(self) -> None:
156
+ """Destroy the toplevel if it still exists."""
157
+ try:
158
+ if self._toplevel and self._toplevel.winfo_exists():
159
+ self._toplevel.destroy()
160
+ except Exception:
161
+ pass
162
+
163
+ def _unbind_outside_click(self) -> None:
164
+ if self._toplevel and self._outside_click_binding:
165
+ try:
166
+ self._toplevel.unbind_all("<ButtonPress-1>", self._outside_click_binding)
167
+ except Exception:
168
+ pass
169
+ self._outside_click_binding = None
170
+
171
+
172
+ class _DialogCalendar(Calendar):
173
+ """Calendar variant that records why a selection event fired."""
174
+
175
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
176
+ super().__init__(*args, **kwargs)
177
+ self._last_trigger_reason: str | None = None
178
+
179
+ def _on_reset_date(self, *args: Any) -> None:
180
+ self._last_trigger_reason = "reset"
181
+ super()._on_reset_date(*args)
182
+
183
+ def _on_date_selected_by_date(self, target: date) -> None:
184
+ self._last_trigger_reason = "select"
185
+ super()._on_date_selected_by_date(target)
186
+
187
+
188
+ class DateDialog:
189
+ """Modal dialog that displays a Calendar"""
190
+
191
+ def __init__(
192
+ self,
193
+ master: Optional[tkinter.Misc] = None,
194
+ title: str = " ",
195
+ initial_date: Optional[date] = None,
196
+ first_weekday: int = 6,
197
+ accent: str = None,
198
+ disabled_dates: Optional[Iterable[date | datetime | str]] = None,
199
+ min_date: Optional[date | datetime | str] = None,
200
+ max_date: Optional[date | datetime | str] = None,
201
+ show_outside_days: Optional[bool] = None,
202
+ show_week_numbers: bool = False,
203
+ hide_window_chrome: bool = False,
204
+ close_on_click_outside: bool = False,
205
+ selection_mode: Literal["single", "range"] = "single",
206
+ start_date: Optional[date | datetime | str] = None,
207
+ end_date: Optional[date | datetime | str] = None,
208
+ ) -> None:
209
+ """Create a date selection dialog.
210
+
211
+ Args:
212
+ master: Parent widget; positions dialog relative to it when set.
213
+ title: Dialog window title text.
214
+ initial_date: Initial date shown in single mode; defaults to `date.today()`.
215
+ first_weekday: First weekday index (0=Monday, 6=Sunday).
216
+ accent: Calendar accent token (e.g., `primary`, `secondary`).
217
+ disabled_dates: Iterable of dates to disable selection.
218
+ min_date: Lower bound for selectable dates.
219
+ max_date: Upper bound for selectable dates.
220
+ show_outside_days: Whether to show outside-month days. Defaults to
221
+ the Calendar behavior (True for single month).
222
+ show_week_numbers: Display ISO week numbers beside each row.
223
+ hide_window_chrome: When True, displays the dialog with no window
224
+ decorations using override-redirect.
225
+ close_on_click_outside: When True, closes the dialog when focus
226
+ moves outside (popover mode).
227
+ selection_mode: `'single'` for a single date (default) or `'range'`
228
+ for a start/end date range. In range mode `result` is a
229
+ `tuple[date, date]` rather than a `date`.
230
+ start_date: Initial range start date (range mode only).
231
+ end_date: Initial range end date (range mode only).
232
+ """
233
+ self._master = master
234
+ self._first_weekday = first_weekday
235
+ self._initial_date = initial_date or datetime.today().date()
236
+ self._accent = accent or 'primary'
237
+ self._disabled_dates = disabled_dates
238
+ self._min_date = min_date
239
+ self._max_date = max_date
240
+ self._show_outside_days = show_outside_days
241
+ self._show_week_numbers = show_week_numbers
242
+ self._hide_window_chrome = hide_window_chrome
243
+ self._close_on_click_outside = close_on_click_outside
244
+ self._selection_mode = selection_mode
245
+ self._start_date = start_date
246
+ self._end_date = end_date
247
+
248
+ self._picker: Optional[_DialogCalendar] = None
249
+
250
+ # Range mode uses an explicit OK/Cancel footer instead of committing the
251
+ # moment the second date is clicked; single mode keeps commit-on-select.
252
+ buttons: list[DialogButton] = []
253
+ if self._selection_mode == "range":
254
+ buttons = [
255
+ DialogButton(text="button.cancel", role="cancel", result=None),
256
+ DialogButton(
257
+ text="button.ok",
258
+ role="primary",
259
+ default=True,
260
+ closes=False,
261
+ command=lambda dlg: self._on_confirm_range(),
262
+ ),
263
+ ]
264
+
265
+ self._dialog = _ChromeDialog(
266
+ title=title,
267
+ content_builder=self._create_content,
268
+ _raw_content=True,
269
+ buttons=buttons,
270
+ footer_builder=None,
271
+ hide_window_chrome=self._hide_window_chrome,
272
+ mode="popover" if self._close_on_click_outside else "modal",
273
+ parent=master,
274
+ )
275
+
276
+ def _create_content(self, master: tkinter.Widget) -> None:
277
+ """Build the Calendar content inside the dialog."""
278
+ container = ttk.Frame(master, padding=2, show_border=True)
279
+ container.pack(fill=BOTH, expand=YES)
280
+
281
+ self._picker = _DialogCalendar(
282
+ master=container,
283
+ selection_mode=self._selection_mode,
284
+ start_date=self._start_date if self._selection_mode == "range" else self._initial_date,
285
+ end_date=self._end_date if self._selection_mode == "range" else None,
286
+ first_weekday=self._first_weekday,
287
+ accent=self._accent,
288
+ disabled_dates=self._disabled_dates,
289
+ min_date=self._min_date,
290
+ max_date=self._max_date,
291
+ show_outside_days=self._show_outside_days,
292
+ show_week_numbers=self._show_week_numbers,
293
+ padding=0,
294
+ )
295
+ self._picker.pack(fill=BOTH, expand=YES)
296
+ self._picker.on_date_selected(self._on_date_selected)
297
+
298
+ def _on_date_selected(self, event: tkinter.Event) -> None:
299
+ """Handle <<DateSelect>> from the embedded Calendar (single mode only).
300
+
301
+ Range mode does not commit on selection — the user confirms the range
302
+ with the OK button (see `_on_confirm_range`).
303
+ """
304
+ if not self._picker or self._selection_mode == "range":
305
+ return
306
+ trigger_reason = getattr(self._picker, "_last_trigger_reason", None)
307
+ if trigger_reason != "select":
308
+ return
309
+
310
+ payload = getattr(event, "data", None)
311
+ selected = getattr(payload, "date", None)
312
+ selected = selected or self._picker.get()
313
+ if selected is None:
314
+ return
315
+
316
+ self._confirm(selected)
317
+
318
+ def _on_confirm_range(self) -> None:
319
+ """Confirm the selected range when OK is clicked.
320
+
321
+ No-op (dialog stays open) until both endpoints are selected.
322
+ """
323
+ if not self._picker:
324
+ return
325
+ start, end = self._picker.get_range()
326
+ if start is None or end is None:
327
+ return
328
+ self._confirm((start, end))
329
+
330
+ def _confirm(self, result: date | Tuple[date, date]) -> None:
331
+ """Record the result, emit, and close the dialog."""
332
+ self._dialog.result = result
333
+ self._emit_result(result, confirmed=True)
334
+ if self._dialog.toplevel:
335
+ top = self._dialog.toplevel
336
+ try:
337
+ top.grab_release()
338
+ except Exception:
339
+ pass
340
+ top.destroy()
341
+
342
+ def show(
343
+ self,
344
+ *,
345
+ position: Optional[Tuple[int, int]] = None,
346
+ modal: Optional[bool] = None,
347
+ anchor_to: Optional[Union[Widget, Literal["screen", "cursor", "parent"]]] = None,
348
+ anchor_point: AnchorPoint = 'center',
349
+ window_point: AnchorPoint = 'center',
350
+ offset: Tuple[int, int] = (0, 0),
351
+ auto_flip: Union[bool, Literal['vertical', 'horizontal']] = False
352
+ ) -> None:
353
+ """Display the dialog and block until closed.
354
+
355
+ Args:
356
+ position: Optional (x, y) coordinates to position the dialog.
357
+ If provided, takes precedence over anchor-based positioning.
358
+ If omitted and anchor_to is not provided, positions at the parent's
359
+ bottom-right when available, otherwise centers.
360
+ modal: Override the mode's default modality.
361
+ If None, uses True for modal mode dialogs.
362
+ anchor_to: Positioning target. Can be:
363
+ - Widget: Anchor to a specific widget
364
+ - "screen": Anchor to screen edges/corners
365
+ - "cursor": Anchor to mouse cursor location
366
+ - "parent": Anchor to parent window (same as widget)
367
+ - None: Uses default positioning behavior
368
+ anchor_point: Point on the anchor target (n, s, e, w, ne, nw, se, sw, center).
369
+ Default 'center'.
370
+ window_point: Point on the dialog window (n, s, e, w, ne, nw, se, sw, center).
371
+ Default 'center'.
372
+ offset: Additional (x, y) offset in pixels from the anchor position.
373
+ auto_flip: Smart positioning to keep window on screen.
374
+ - False: No flipping (default)
375
+ - True: Flip both vertically and horizontally as needed
376
+ - 'vertical': Only flip up/down
377
+ - 'horizontal': Only flip left/right
378
+ """
379
+ # Default positioning: bottom-right of parent if no positioning options provided
380
+ if position is None and anchor_to is None and self._master:
381
+ try:
382
+ x = self._master.winfo_rootx() + self._master.winfo_width()
383
+ y = self._master.winfo_rooty() + self._master.winfo_height()
384
+ position = (x, y)
385
+ except Exception:
386
+ pass
387
+
388
+ # Default modal to True if not specified
389
+ if modal is None:
390
+ modal = True
391
+
392
+ self._dialog.show(
393
+ position=position,
394
+ modal=modal,
395
+ anchor_to=anchor_to,
396
+ anchor_point=anchor_point,
397
+ window_point=window_point,
398
+ offset=offset,
399
+ auto_flip=auto_flip
400
+ )
401
+
402
+ @property
403
+ def result(self) -> Optional[Union[date, Tuple[date, date]]]:
404
+ """The selected date (single mode) or `(start, end)` tuple (range mode), or None if cancelled."""
405
+ return self._dialog.result
406
+
407
+ def on_result(self, callback: Callable[[date], None]) -> Optional[str]:
408
+ """Bind a callback fired when a result is produced.
409
+
410
+ The callback receives `event.data["result"]` (a `datetime.date`).
411
+
412
+ Args:
413
+ callback: Callable that receives the selected `datetime.date`.
414
+
415
+ Returns:
416
+ The Tk binding identifier, which can be used with `off_result`.
417
+ """
418
+ target = self._dialog.toplevel or self._master
419
+ if target is None:
420
+ return None
421
+
422
+ def handler(event: tkinter.Event) -> None:
423
+ callback(getattr(event, "data", None))
424
+
425
+ return target.bind("<<DialogResult>>", handler, add="+")
426
+
427
+ def off_result(self, funcid: str) -> None:
428
+ """Unbind a previously bound `on_result` callback.
429
+
430
+ Args:
431
+ funcid: Binding identifier returned by `on_result`.
432
+ """
433
+ target = self._dialog.toplevel or self._master
434
+ if target is None:
435
+ return
436
+ target.unbind("<<DialogResult>>", funcid)
437
+
438
+ def _emit_result(self, value: date, confirmed: bool) -> None:
439
+ """Emit a virtual Tk event with the dialog result."""
440
+ target = self._dialog.toplevel or self._master
441
+ if not target:
442
+ return
443
+ payload = {"result": value, "confirmed": confirmed}
444
+ try:
445
+ target.event_generate("<<DialogResult>>", data=payload)
446
+ except Exception:
447
+ try:
448
+ target.event_generate("<<DialogResult>>")
449
+ except Exception:
450
+ pass