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,404 @@
1
+ """Date entry field widget with calendar picker button.
2
+
3
+ Provides a specialized entry field for date input with locale-aware formatting
4
+ and an optional calendar picker button.
5
+ """
6
+
7
+ from datetime import date, datetime
8
+ from typing import TYPE_CHECKING, Iterable, Literal
9
+ from bootstack.i18n.intl_format import DateFormatSpec
10
+
11
+ from typing_extensions import Unpack
12
+
13
+ from bootstack.events import ChangeEvent
14
+ from bootstack.widgets._impl.primitives.button import Button
15
+ from bootstack.widgets._impl.composites._dateutils import coerce_date
16
+ from bootstack.widgets._impl.composites.field import Field, FieldOptions
17
+ from bootstack.widgets._impl.mixins import configure_delegate
18
+ from bootstack.widgets.types import Master
19
+
20
+ if TYPE_CHECKING:
21
+ pass
22
+
23
+
24
+ class DateEntry(Field):
25
+ """A date entry field widget with calendar picker button.
26
+
27
+ DateEntry extends the Field widget to provide specialized date input with
28
+ locale-aware formatting and an optional calendar picker button. The widget
29
+ supports various date format presets and custom ICU date format patterns,
30
+ and can accept input as strings, date objects, or datetime objects.
31
+
32
+ The available date format presets are: `longDate` (January 15, 2025),
33
+ `shortDate` (1/15/25), `monthAndDate` (January 15), `monthAndYear`
34
+ (January 2025), `quarterAndYear` (Q1 2025), `day` (15), `dayOfWeek`
35
+ (Wednesday), `month` (January), `quarter` (Q1), `year` (2025),
36
+ `longTime` (3:30:45 PM PST), `shortTime` (3:30 PM), `longDateLongTime`,
37
+ `shortDateShortTime`, or any custom ICU date format pattern (e.g., "yyyy-MM-dd").
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ master: Master = None,
43
+ value: str | date | datetime = None,
44
+ value_format: DateFormatSpec = "longDate",
45
+ label: str = None,
46
+ message: str = None,
47
+ show_picker_button=True,
48
+ picker_title: str = None,
49
+ picker_first_weekday: int = 6,
50
+ selection_mode: Literal["single", "range"] = "single",
51
+ start_date: date | datetime | str | None = None,
52
+ end_date: date | datetime | str | None = None,
53
+ min_date: date | datetime | str | None = None,
54
+ max_date: date | datetime | str | None = None,
55
+ disabled_dates: Iterable[date | datetime | str] | None = None,
56
+ **kwargs: Unpack[FieldOptions]
57
+ ):
58
+ """Args:
59
+ master: Parent widget. If None, uses the default root window.
60
+ value: Initial date value to display (single
61
+ mode). Ignored when `selection_mode='range'`.
62
+ value_format: Date format pattern to use for parsing and displaying
63
+ dates. See class docstring for complete list of presets.
64
+ label: Optional label text to display above the entry field.
65
+ If required=True, an asterisk (*) is automatically appended.
66
+ message: Optional message text to display below the entry field.
67
+ Used for hints or help text. Replaced by validation errors when
68
+ validation fails.
69
+ show_picker_button: If True, displays the calendar picker button
70
+ to the right of the entry. If False, hides the button.
71
+ picker_title: Title text for the calendar picker dialog. Defaults
72
+ to "Select date range" in range mode and "Select new date" otherwise.
73
+ picker_first_weekday: First day of the week to display in the
74
+ calendar picker. 0=Monday, 6=Sunday.
75
+ selection_mode: `'single'` (default) for a single date or
76
+ `'range'` for a start/end date range. In range mode the entry
77
+ is readonly and the user selects dates exclusively via the picker.
78
+ `value` returns `tuple[date, date] | None` in range mode.
79
+ start_date: Initial range start date (range
80
+ mode only).
81
+ end_date: Initial range end date (range mode
82
+ only).
83
+ min_date: Lower bound for selectable dates.
84
+ Dates before this are disabled in the picker.
85
+ max_date: Upper bound for selectable dates.
86
+ Dates after this are disabled in the picker.
87
+ disabled_dates: Specific dates to disable in the picker.
88
+
89
+ Other Parameters:
90
+ locale: Locale identifier for date formatting (e.g., 'en_US').
91
+ required: If True, field cannot be empty.
92
+ allow_blank: Allow empty input.
93
+ cursor: Cursor style when hovering.
94
+ exportselection: Export selection to clipboard.
95
+ font: Font for text display.
96
+ foreground: Text color.
97
+ initial_focus: If True, widget receives focus on creation.
98
+ justify: Text alignment ('left', 'center', 'right').
99
+ show_message: If True, displays message area.
100
+ padding: Padding around entry widget.
101
+ takefocus: If True, widget accepts Tab focus.
102
+ textvariable: Tkinter Variable to link with text.
103
+ See [tkinter Variables](https://docs.python.org/3/library/tkinter.html#tkinter-variables).
104
+ textsignal: Signal object for reactive updates.
105
+ width: Width in characters.
106
+ xscrollcommand: Callback for horizontal scrolling.
107
+ """
108
+ # Store before super().__init__ so property override is safe if called during init
109
+ self._selection_mode = selection_mode
110
+ self._range_value: tuple[date, date] | None = None
111
+ self._value_format = value_format
112
+
113
+ if picker_title is None:
114
+ picker_title = "Select date range" if selection_mode == "range" else "Select new date"
115
+
116
+ kwargs.setdefault('accent', 'primary')
117
+ # In range mode pass value=None so the entry starts empty; display text
118
+ # is set manually below after range initialisation.
119
+ super().__init__(
120
+ master=master,
121
+ value=None if selection_mode == "range" else value,
122
+ value_format=value_format,
123
+ label=label,
124
+ message=message,
125
+ **kwargs,
126
+ )
127
+
128
+ # configuration
129
+ self._show_picker_button = show_picker_button
130
+ self._picker_title = picker_title
131
+ self._picker_first_weekday = picker_first_weekday
132
+ self._min_date = min_date
133
+ self._max_date = max_date
134
+ self._disabled_dates = disabled_dates
135
+
136
+ self._button_pack = {}
137
+
138
+ self.insert_addon(
139
+ Button,
140
+ position="after",
141
+ name="date-picker",
142
+ icon="calendar-week",
143
+ icon_only=True,
144
+ command=self._show_date_picker
145
+ )
146
+
147
+ self._delegate_show_picker_button(self._show_picker_button)
148
+
149
+ if selection_mode == "range":
150
+ # Make the entry readonly — user must use the picker
151
+ self._entry.state(['readonly'])
152
+ self._field.state(['!disabled'])
153
+
154
+ # Seed initial range
155
+ s = self._coerce_date(start_date)
156
+ e = self._coerce_date(end_date)
157
+ if s is not None and e is not None:
158
+ self._set_range((s, e))
159
+
160
+ # --- value property override for range mode -------------------------
161
+
162
+ @property
163
+ def value(self) -> date | tuple[date, date] | None:
164
+ """Selected date (single mode) or `(start, end)` tuple (range mode)."""
165
+ if self._selection_mode == 'range':
166
+ return self._range_value
167
+ return Field.value.fget(self)
168
+
169
+ @value.setter
170
+ def value(self, val):
171
+ if self._selection_mode == 'range':
172
+ self._set_range(val)
173
+ else:
174
+ Field.value.fset(self, val)
175
+
176
+ # --- addon state override for range mode ----------------------------
177
+
178
+ def _sync_addon_state(self, event=None):
179
+ if getattr(self, '_selection_mode', 'single') == 'range':
180
+ # Entry is intentionally readonly in range mode; keep addons active
181
+ self._set_addons_state(False)
182
+ else:
183
+ super()._sync_addon_state(event)
184
+
185
+ # --- range helpers --------------------------------------------------
186
+
187
+ def _set_range(self, val) -> None:
188
+ """Set the range value, update display text, and fire <<Change>>."""
189
+ if val is None:
190
+ prev = self._range_value
191
+ self._range_value = None
192
+ self.variable.set("")
193
+ if prev is not None:
194
+ try:
195
+ self._entry.event_generate("<<Change>>", data=ChangeEvent(
196
+ value=None, prev_value=prev, text="",
197
+ ))
198
+ except Exception:
199
+ pass
200
+ return
201
+
202
+ if not (isinstance(val, (tuple, list)) and len(val) == 2):
203
+ return
204
+
205
+ s = self._coerce_date(val[0])
206
+ e = self._coerce_date(val[1])
207
+ if s is None or e is None:
208
+ return
209
+ if e < s:
210
+ s, e = e, s
211
+
212
+ prev = self._range_value
213
+ self._range_value = (s, e)
214
+ self.variable.set(self._format_range_display(s, e))
215
+
216
+ if prev != self._range_value:
217
+ try:
218
+ self._entry.event_generate("<<Change>>", data=ChangeEvent(
219
+ value=self._range_value,
220
+ prev_value=prev,
221
+ text=self.variable.get(),
222
+ ))
223
+ except Exception:
224
+ pass
225
+
226
+ def _format_range_display(self, start: date, end: date) -> str:
227
+ """Format a date range as 'Jan 1 – Jan 31, 2025' using value_format."""
228
+ from bootstack.i18n import IntlFormatter, MessageCatalog
229
+ fmt = IntlFormatter(locale=MessageCatalog.locale())
230
+ start_str = fmt.format(start, self._value_format)
231
+ end_str = fmt.format(end, self._value_format)
232
+ return f"{start_str} – {end_str}"
233
+
234
+ _coerce_date = staticmethod(coerce_date)
235
+
236
+ def set_min_date(self, value: date | datetime | str | None) -> None:
237
+ """Set the earliest selectable date for the picker. None clears the bound."""
238
+ self._min_date = self._coerce_date(value)
239
+
240
+ def set_max_date(self, value: date | datetime | str | None) -> None:
241
+ """Set the latest selectable date for the picker. None clears the bound."""
242
+ self._max_date = self._coerce_date(value)
243
+
244
+ def set_disabled_dates(self, dates: Iterable[date | datetime | str] | None) -> None:
245
+ """Replace the dates disabled in the picker. The iterable is coerced and
246
+ materialized, so a one-shot generator is consumed exactly once here."""
247
+ self._disabled_dates = [
248
+ d for d in (self._coerce_date(x) for x in (dates or [])) if d is not None
249
+ ]
250
+
251
+ # --- public properties ----------------------------------------------
252
+
253
+ @property
254
+ def date_picker_button(self) -> Button | None:
255
+ """Get the calendar picker button widget."""
256
+ return self.addons.get('date-picker')
257
+
258
+ @configure_delegate('show_picker_button')
259
+ def _delegate_show_picker_button(self, value: bool = None):
260
+ """Get or set the visibility of the calendar picker button."""
261
+ if value is None:
262
+ return self._show_picker_button
263
+ else:
264
+ if value:
265
+ if not self.date_picker_button.winfo_ismapped():
266
+ self.date_picker_button.pack(**self._button_pack)
267
+ else:
268
+ self._button_pack = self.date_picker_button.pack_info()
269
+ self.date_picker_button.pack_forget()
270
+ return None
271
+
272
+ def _show_date_picker(self):
273
+ """Open the calendar picker dialog.
274
+
275
+ Opens a DateDialog seeded with the current value (or range), updates
276
+ the entry when a date is picked, and leaves the field unchanged on
277
+ cancel.
278
+ """
279
+ from bootstack.dialogs._impl.datedialog import DateDialog
280
+
281
+ # Prevent multiple dialogs: reuse existing if still open
282
+ existing = getattr(self, "_active_date_dialog", None)
283
+ top = getattr(getattr(existing, "_dialog", None), "toplevel", None) if existing else None
284
+ try:
285
+ exists = bool(top and top.winfo_exists())
286
+ except Exception:
287
+ exists = False
288
+
289
+ if exists:
290
+ try:
291
+ top.lift()
292
+ top.focus_force()
293
+ except Exception:
294
+ pass
295
+ return
296
+ self._active_date_dialog = None
297
+
298
+ if self._selection_mode == "range":
299
+ cur = self._range_value
300
+ dialog = DateDialog(
301
+ master=self.winfo_toplevel(),
302
+ title=self._picker_title,
303
+ first_weekday=self._picker_first_weekday,
304
+ selection_mode="range",
305
+ start_date=cur[0] if cur else None,
306
+ end_date=cur[1] if cur else None,
307
+ accent=self._accent,
308
+ disabled_dates=self._disabled_dates,
309
+ min_date=self._min_date,
310
+ max_date=self._max_date,
311
+ hide_window_chrome=True,
312
+ close_on_click_outside=False,
313
+ )
314
+ else:
315
+ current_value = self.value
316
+ if isinstance(current_value, datetime):
317
+ current_value = current_value.date()
318
+ if not isinstance(current_value, date):
319
+ current_value = date.today()
320
+ dialog = DateDialog(
321
+ master=self.winfo_toplevel(),
322
+ title=self._picker_title,
323
+ first_weekday=self._picker_first_weekday,
324
+ initial_date=current_value,
325
+ accent=self._accent,
326
+ disabled_dates=self._disabled_dates,
327
+ min_date=self._min_date,
328
+ max_date=self._max_date,
329
+ hide_window_chrome=True,
330
+ close_on_click_outside=False,
331
+ )
332
+
333
+ self._active_date_dialog = dialog
334
+
335
+ def _on_result(payload):
336
+ result = payload.get('result') if isinstance(payload, dict) else payload
337
+ if self._selection_mode == "range":
338
+ if isinstance(result, tuple) and len(result) == 2:
339
+ self.value = result
340
+ else:
341
+ if isinstance(result, (date, datetime)):
342
+ self.value = result
343
+
344
+ dialog.on_result(lambda x: _on_result(x))
345
+ dialog.show(
346
+ anchor_to=self._field,
347
+ anchor_point='se',
348
+ window_point='ne',
349
+ offset=(-4, 2),
350
+ )
351
+
352
+ top = getattr(getattr(dialog, "_dialog", None), "toplevel", None)
353
+ cleared = False
354
+ try:
355
+ if top and top.winfo_exists():
356
+ def _clear(_=None):
357
+ self._active_date_dialog = None
358
+ top.bind("<Destroy>", _clear, add="+")
359
+ cleared = True
360
+ except Exception:
361
+ cleared = False
362
+ if not cleared:
363
+ self._active_date_dialog = None
364
+
365
+ # Fallback: ensure value is applied after modal dialog closes.
366
+ selected = dialog.result
367
+ if self._selection_mode == "range":
368
+ if isinstance(selected, tuple) and len(selected) == 2:
369
+ self.value = selected
370
+ else:
371
+ if isinstance(selected, datetime):
372
+ selected = selected.date()
373
+ if isinstance(selected, date):
374
+ self.value = selected
375
+
376
+ # Return focus to the entry after dialog closes
377
+ # Note: focus_force() is needed on Windows after override-redirect dialogs
378
+ try:
379
+ self._entry.focus_force()
380
+ except Exception:
381
+ pass
382
+
383
+ def _picker_position(self):
384
+ """Choose a dialog position beneath the entry mirroring SelectBox spacing."""
385
+ try:
386
+ # The field is already rendered when the user clicks, so no layout
387
+ # flush is needed — update_idletasks() here would drain unrelated
388
+ # idle callbacks (e.g. Calendar._lock_size from a previous dialog)
389
+ # and cause noticeable lag on repeated opens.
390
+ field_widget = self._field
391
+ x = field_widget.winfo_rootx() + 4
392
+ y = field_widget.winfo_rooty() + field_widget.winfo_height() + 2
393
+ return x, y
394
+ except Exception:
395
+ pass
396
+
397
+ try:
398
+ top = self.winfo_toplevel()
399
+ x = top.winfo_rootx() + top.winfo_width() // 2
400
+ y = top.winfo_rooty() + top.winfo_height() // 2
401
+ return x, y
402
+ except Exception:
403
+ return None
404
+