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,539 @@
1
+ """Typed event payloads — the catalog of every data-carrying framework event.
2
+
3
+ When a widget event carries application data (a new value, the typed text, a
4
+ validation result, a selected row), the handler bound with `on_*()` receives
5
+ one of the frozen dataclasses defined here, *unpacked* — the handler argument
6
+ IS the payload::
7
+
8
+ field.on_change(lambda e: print(e.value, e.prev_value))
9
+ field.on_input(lambda e: print(e.text))
10
+
11
+ Events that carry no payload (click, hover, focus, resize, key, scroll) instead
12
+ hand the handler a curated `Event`.
13
+
14
+ Every payload is `@dataclass(frozen=True, slots=True)` so attributes are
15
+ discoverable by editors and immutable in handlers.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from dataclasses import dataclass, field
21
+ from datetime import date as _date
22
+ from typing import Any, Literal, Mapping
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # Field / input family
26
+ # ---------------------------------------------------------------------------
27
+
28
+
29
+ @dataclass(frozen=True, slots=True)
30
+ class InputEvent:
31
+ """Fires on every keystroke, before the value is committed.
32
+
33
+ Use it for live feedback — character counts, as-you-type filtering. To react
34
+ only once editing settles, use `on_change` instead.
35
+ """
36
+
37
+ text: str = ""
38
+ """The current raw text in the field."""
39
+
40
+
41
+ @dataclass(frozen=True, slots=True)
42
+ class ChangeEvent:
43
+ """Fires when a field's value is committed (on blur or Enter)."""
44
+
45
+ value: Any = None
46
+ """The committed, parsed value."""
47
+
48
+ prev_value: Any = None
49
+ """The value before this change."""
50
+
51
+ text: str = ""
52
+ """The raw display text behind the value."""
53
+
54
+
55
+ @dataclass(frozen=True, slots=True)
56
+ class ValidationEvent:
57
+ """Fires after validation runs — `valid`, `invalid`, and `validate`."""
58
+
59
+ value: Any = None
60
+ """The value that was validated."""
61
+
62
+ is_valid: bool = True
63
+ """Whether validation passed."""
64
+
65
+ message: str = ""
66
+ """The failure message, or an empty string when valid."""
67
+
68
+
69
+ # ---------------------------------------------------------------------------
70
+ # Sliders & meters
71
+ # ---------------------------------------------------------------------------
72
+
73
+
74
+ @dataclass(frozen=True, slots=True)
75
+ class SliderEvent:
76
+ """Fires while a slider (or gauge/meter) value moves."""
77
+
78
+ value: float = 0.0
79
+ """The new value."""
80
+
81
+ prev_value: float = 0.0
82
+ """The value before this move."""
83
+
84
+
85
+ @dataclass(frozen=True, slots=True)
86
+ class SliderCommitEvent:
87
+ """Fires when a slider value is committed (drag released or stepped)."""
88
+
89
+ value: float = 0.0
90
+ """The committed value."""
91
+
92
+
93
+ @dataclass(frozen=True, slots=True)
94
+ class RangeSliderEvent:
95
+ """Fires while either handle of a range slider moves."""
96
+
97
+ low_value: float = 0.0
98
+ """The new lower-bound value."""
99
+
100
+ high_value: float = 0.0
101
+ """The new upper-bound value."""
102
+
103
+ prev_low_value: float = 0.0
104
+ """The lower-bound value before this move."""
105
+
106
+ prev_high_value: float = 0.0
107
+ """The upper-bound value before this move."""
108
+
109
+
110
+ @dataclass(frozen=True, slots=True)
111
+ class RangeSliderCommitEvent:
112
+ """Fires when a range slider's values are committed."""
113
+
114
+ low_value: float = 0.0
115
+ """The committed lower-bound value."""
116
+
117
+ high_value: float = 0.0
118
+ """The committed upper-bound value."""
119
+
120
+
121
+ # ---------------------------------------------------------------------------
122
+ # Calendar
123
+ # ---------------------------------------------------------------------------
124
+
125
+
126
+ @dataclass(frozen=True, slots=True)
127
+ class DateSelectEvent:
128
+ """Fires when a date (or date range) is chosen in a calendar."""
129
+
130
+ date: "_date | None" = None
131
+ """The selected date."""
132
+
133
+ range: "tuple[_date, _date] | None" = None
134
+ """The selected `(start, end)` range, or `None` in single-date mode."""
135
+
136
+
137
+ # ---------------------------------------------------------------------------
138
+ # Expander / Accordion
139
+ # ---------------------------------------------------------------------------
140
+
141
+
142
+ @dataclass(frozen=True, slots=True)
143
+ class ToggleEvent:
144
+ """Fires when an expander is expanded or collapsed."""
145
+
146
+ expanded: bool = False
147
+ """Whether the expander is now open."""
148
+
149
+
150
+ @dataclass(frozen=True, slots=True)
151
+ class AccordionChangeEvent:
152
+ """Fires when an accordion's set of open panels changes."""
153
+
154
+ expanded: tuple[Any, ...] = ()
155
+ """The currently expanded panels."""
156
+
157
+
158
+ # ---------------------------------------------------------------------------
159
+ # Picture / media
160
+ # ---------------------------------------------------------------------------
161
+
162
+
163
+ @dataclass(frozen=True, slots=True)
164
+ class ImageLoadEvent:
165
+ """Fires when a `Picture` has decoded its source and displayed it."""
166
+
167
+ width: int = 0
168
+ """The natural pixel width of the loaded image."""
169
+
170
+ height: int = 0
171
+ """The natural pixel height of the loaded image."""
172
+
173
+ frames: int = 1
174
+ """The number of frames — greater than one for an animated source."""
175
+
176
+
177
+ @dataclass(frozen=True, slots=True)
178
+ class ImageErrorEvent:
179
+ """Fires when a `Picture` fails to load or decode its source."""
180
+
181
+ message: str = ""
182
+ """A human-readable description of the failure."""
183
+
184
+
185
+ # ---------------------------------------------------------------------------
186
+ # Navigation — PageStack, SideNav
187
+ # ---------------------------------------------------------------------------
188
+
189
+
190
+ @dataclass(frozen=True, slots=True)
191
+ class PageChangeEvent:
192
+ """Fires when the active page changes (mount, will-mount, change)."""
193
+
194
+ page: str = ""
195
+ """The key of the page now active."""
196
+
197
+ prev_page: str | None = None
198
+ """The key of the previously active page, or `None`."""
199
+
200
+ nav: Any = None
201
+ """Navigation direction/context for this transition, if any."""
202
+
203
+ data: dict[str, Any] = field(default_factory=dict)
204
+ """Arbitrary data passed to `navigate(key, data=...)`."""
205
+
206
+ prev_data: Any = None
207
+ """The data the previous page was navigated with."""
208
+
209
+ index: int = 0
210
+ """Position of this page in the navigation history."""
211
+
212
+ length: int = 0
213
+ """Total length of the navigation history."""
214
+
215
+ can_back: bool = False
216
+ """Whether there is a previous page to go back to."""
217
+
218
+ can_forward: bool = False
219
+ """Whether there is a next page to go forward to."""
220
+
221
+
222
+ @dataclass(frozen=True, slots=True)
223
+ class NavEvent:
224
+ """Fires when a navigation item is selected."""
225
+
226
+ key: str = ""
227
+ """The key of the selected item."""
228
+
229
+
230
+ @dataclass(frozen=True, slots=True)
231
+ class PaneToggleEvent:
232
+ """Fires when a navigation pane opens or closes."""
233
+
234
+ is_open: bool = False
235
+ """Whether the pane is now open."""
236
+
237
+
238
+ @dataclass(frozen=True, slots=True)
239
+ class DisplayModeEvent:
240
+ """Fires when a navigation widget's display mode changes."""
241
+
242
+ mode: Any = None
243
+ """The new display mode."""
244
+
245
+
246
+ @dataclass(frozen=True, slots=True)
247
+ class WorkspaceChangeEvent:
248
+ """Fires when the shell's active workspace changes (a rail switch)."""
249
+
250
+ workspace: str = ""
251
+ """The key of the workspace now active."""
252
+
253
+ prev_workspace: str | None = None
254
+ """The key of the previously active workspace, or `None`."""
255
+
256
+
257
+ # ---------------------------------------------------------------------------
258
+ # Tabs
259
+ # ---------------------------------------------------------------------------
260
+
261
+ ChangeReason = Literal["user", "api", "hide", "forget", "unknown"]
262
+ """Why a change event fired — a direct `user` edit, an `api` call, a `hide` or
263
+ `forget` of carried data, or `unknown`."""
264
+
265
+ ChangeMethod = Literal["click", "key", "programmatic", "unknown"]
266
+ """How a change was made — by mouse `click`, by `key`, `programmatic`ally, or
267
+ `unknown`."""
268
+
269
+
270
+ @dataclass(frozen=True, slots=True)
271
+ class TabRef:
272
+ """Identifies a tab by key and display text."""
273
+
274
+ key: str = ""
275
+ """The tab's stable key."""
276
+
277
+ text: str = ""
278
+ """The tab's display text."""
279
+
280
+
281
+ @dataclass(frozen=True, slots=True)
282
+ class TabChangeEvent:
283
+ """Fires when the active tab changes."""
284
+
285
+ current: TabRef | None = None
286
+ """The tab now active."""
287
+
288
+ previous: TabRef | None = None
289
+ """The tab that was active before, or `None`."""
290
+
291
+ reason: ChangeReason = "unknown"
292
+ """Why the change happened."""
293
+
294
+ via: ChangeMethod = "unknown"
295
+ """How the change was triggered."""
296
+
297
+
298
+ @dataclass(frozen=True, slots=True)
299
+ class TabActivateEvent:
300
+ """Fires when a tab becomes active."""
301
+
302
+ key: str = ""
303
+ """The activated tab's key."""
304
+
305
+ text: str = ""
306
+ """The activated tab's display text."""
307
+
308
+
309
+ @dataclass(frozen=True, slots=True)
310
+ class TabDeactivateEvent:
311
+ """Fires when a tab stops being active."""
312
+
313
+ key: str = ""
314
+ """The deactivated tab's key."""
315
+
316
+ text: str = ""
317
+ """The deactivated tab's display text."""
318
+
319
+
320
+ @dataclass(frozen=True, slots=True)
321
+ class TabCloseEvent:
322
+ """Fires when a tab's close button is used."""
323
+
324
+ value: Any = None
325
+ """The closed tab's value/key."""
326
+
327
+
328
+ # ---------------------------------------------------------------------------
329
+ # Table — row-oriented
330
+ # ---------------------------------------------------------------------------
331
+
332
+
333
+ @dataclass(frozen=True, slots=True)
334
+ class RowEvent:
335
+ """Fires for a single-row action (click, double-click, right-click)."""
336
+
337
+ record: dict[str, Any] = field(default_factory=dict)
338
+ """The row's record."""
339
+
340
+ id: Any = None
341
+ """The row's id."""
342
+
343
+
344
+ @dataclass(frozen=True, slots=True)
345
+ class RowsEvent:
346
+ """Fires for a multi-row action (insert, update, delete, move)."""
347
+
348
+ records: list[dict[str, Any]] = field(default_factory=list)
349
+ """The affected records."""
350
+
351
+
352
+ @dataclass(frozen=True, slots=True)
353
+ class SelectionEvent:
354
+ """Fires when the set of selected rows changes."""
355
+
356
+ records: list[dict[str, Any]] = field(default_factory=list)
357
+ """The selected records."""
358
+
359
+ ids: list[Any] = field(default_factory=list)
360
+ """The selected rows' ids."""
361
+
362
+
363
+ @dataclass(frozen=True, slots=True)
364
+ class ExportEvent:
365
+ """Fires after the table's data is exported (copied or saved)."""
366
+
367
+ count: int = 0
368
+ """The number of rows exported."""
369
+
370
+ target: str = ""
371
+ """Where the data went — `'clipboard'` or `'file'`."""
372
+
373
+ format: str = ""
374
+ """The format written — `'csv'`, `'tsv'`, or `'xlsx'`."""
375
+
376
+ path: str | None = None
377
+ """The destination file path when `target` is `'file'`, else `None`."""
378
+
379
+ records: list[dict[str, Any]] = field(default_factory=list)
380
+ """The exported records. Populated for in-memory targets (clipboard);
381
+ empty for streamed file exports, which never materialize all rows."""
382
+
383
+
384
+ # ---------------------------------------------------------------------------
385
+ # Tree — node-oriented
386
+ # ---------------------------------------------------------------------------
387
+
388
+
389
+ @dataclass(frozen=True, slots=True)
390
+ class TreeSelectionEvent:
391
+ """Fires when a tree's set of selected nodes changes."""
392
+
393
+ nodes: list[Any] = field(default_factory=list)
394
+ """The selected `TreeNode` handles, in tree order."""
395
+
396
+
397
+ # ---------------------------------------------------------------------------
398
+ # TextArea / CodeEditor
399
+ # ---------------------------------------------------------------------------
400
+
401
+
402
+ @dataclass(frozen=True, slots=True)
403
+ class TextModifiedEvent:
404
+ """Fires when a text area's modified (dirty) flag changes."""
405
+
406
+ is_dirty: bool = False
407
+ """Whether the text differs from the last saved/baseline content."""
408
+
409
+
410
+ # ---------------------------------------------------------------------------
411
+ # ButtonGroup
412
+ # ---------------------------------------------------------------------------
413
+
414
+
415
+ @dataclass(frozen=True, slots=True)
416
+ class ButtonGroupClickEvent:
417
+ """Fires when a button in a button group is clicked."""
418
+
419
+ key: str = ""
420
+ """The clicked button's key."""
421
+
422
+ text: str = ""
423
+ """The clicked button's label text."""
424
+
425
+ icon: Any = None
426
+ """The clicked button's icon, if any."""
427
+
428
+
429
+ @dataclass(frozen=True, slots=True)
430
+ class MenuSelectEvent:
431
+ """Fires when a menu item is activated (`MenuButton` / `ContextMenu`)."""
432
+
433
+ type: str = ""
434
+ """The activated item's type — `'command'`, `'check'`, or `'radio'`."""
435
+
436
+ text: str = ""
437
+ """The item's label text."""
438
+
439
+ value: Any = None
440
+ """The item's value (a radio/check value, or `None` for a command)."""
441
+
442
+
443
+ # ---------------------------------------------------------------------------
444
+ # Data source — change broadcasting
445
+ # ---------------------------------------------------------------------------
446
+
447
+
448
+ ChangeKind = Literal[
449
+ "load",
450
+ "insert",
451
+ "update",
452
+ "delete",
453
+ "move",
454
+ "filter",
455
+ "sort",
456
+ "reload",
457
+ "select",
458
+ ]
459
+ """What kind of change a `DataChangeEvent` describes.
460
+
461
+ A row mutation (`insert`/`update`/`delete`/`move`), a view change
462
+ (`filter`/`sort`), a bulk replace (`load`/`reload`), or a selection toggle
463
+ (`select`).
464
+ """
465
+
466
+
467
+ @dataclass(frozen=True, slots=True)
468
+ class DataChangeEvent:
469
+ """Broadcast by a data source when its data or view changes.
470
+
471
+ Delivered to handlers registered with `DataSource.on_change`. Because the
472
+ source coalesces rapid mutations into one notification per event-loop turn,
473
+ a single event may stand in for several underlying changes; in that case
474
+ `kind` is `reload` and the per-row fields are empty. Treat it as a coarse
475
+ "something changed" signal and re-read the source for current data.
476
+ """
477
+
478
+ kind: ChangeKind = "reload"
479
+ """What changed."""
480
+
481
+ id: Any = None
482
+ """Affected record id for single-row kinds; `None` otherwise."""
483
+
484
+ ids: tuple[Any, ...] = ()
485
+ """Affected record ids for bulk kinds (e.g. `select_all`); empty otherwise."""
486
+
487
+ record: Mapping[str, Any] | None = None
488
+ """The affected record (or the applied updates) where cheaply available on
489
+ `insert`/`update`; `None` otherwise."""
490
+
491
+
492
+ # ---------------------------------------------------------------------------
493
+ # SplitView
494
+ # ---------------------------------------------------------------------------
495
+
496
+
497
+ @dataclass(frozen=True, slots=True)
498
+ class SashMoveEvent:
499
+ """Fires when a `SplitView` sash is released after being dragged."""
500
+
501
+ index: int = 0
502
+ """Zero-based index of the sash that moved."""
503
+
504
+ position: int = 0
505
+ """New pixel position of the sash in pixels from the leading edge."""
506
+
507
+
508
+ # ---------------------------------------------------------------------------
509
+ # ScrollView
510
+ # ---------------------------------------------------------------------------
511
+
512
+
513
+ @dataclass(frozen=True, slots=True)
514
+ class ScrollEvent:
515
+ """Fires when a `ScrollView`'s viewport position changes."""
516
+
517
+ y: float = 0.0
518
+ """Vertical scroll position — 0.0 (top) to 1.0 (bottom)."""
519
+
520
+ x: float = 0.0
521
+ """Horizontal scroll position — 0.0 (left) to 1.0 (right)."""
522
+
523
+
524
+ # ---------------------------------------------------------------------------
525
+ # Splash
526
+ # ---------------------------------------------------------------------------
527
+
528
+ SplashDismissReason = Literal["ready", "timer", "manual", "skip"]
529
+ """Why a splash screen dismissed — `'ready'` (the app finished building),
530
+ `'timer'` (its timed duration elapsed), `'manual'` (an explicit `dismiss()`),
531
+ or `'skip'` (the user clicked or pressed Escape)."""
532
+
533
+
534
+ @dataclass(frozen=True, slots=True)
535
+ class SplashDismissEvent:
536
+ """Fires when a splash screen begins dismissing."""
537
+
538
+ reason: SplashDismissReason = "ready"
539
+ """Why the splash dismissed."""
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ import tkinter
4
+
5
+
6
+ class Subscription:
7
+ """Handle returned by `widget.on(...)`. Cancels the binding when `.cancel()`
8
+ is called or the context manager exits.
9
+
10
+ Idempotent: calling `cancel()` more than once is a no-op.
11
+ """
12
+
13
+ __slots__ = ("_widget", "_sequence", "_bind_id", "_cancelled")
14
+
15
+ def __init__(self, widget: tkinter.Misc, sequence: str, bind_id: str) -> None:
16
+ self._widget = widget
17
+ self._sequence = sequence
18
+ self._bind_id = bind_id
19
+ self._cancelled = False
20
+
21
+ def cancel(self) -> None:
22
+ if self._cancelled:
23
+ return
24
+ self._cancelled = True
25
+ try:
26
+ self._widget.unbind(self._sequence, self._bind_id)
27
+ except tkinter.TclError:
28
+ pass
29
+
30
+ def __enter__(self) -> "Subscription":
31
+ return self
32
+
33
+ def __exit__(self, exc_type, exc, tb) -> None:
34
+ self.cancel()
35
+
36
+ @property
37
+ def cancelled(self) -> bool:
38
+ return self._cancelled
@@ -0,0 +1,77 @@
1
+ # Internationalization (i18n)
2
+
3
+ bootstack uses a unified message catalog that bridges Python gettext (compiled
4
+ with Babel) and Tcl/Tk msgcat. Catalogs are distributed with the package under
5
+ `bootstack/assets/locales`.
6
+
7
+ Highlights
8
+ - Use `_ = MessageCatalog.translate` in code to mark strings.
9
+ - The framework auto-initializes the catalog and auto-discovers shipped catalogs.
10
+ - Switch language at runtime with `MessageCatalog.locale('de')`.
11
+ - A virtual event `<<LocaleChanged>>` is emitted on language changes so UIs can refresh.
12
+ - Runtime overrides (`set`, `set_many`) let you add translations not yet in catalogs.
13
+
14
+ Directory structure
15
+ - `src/bootstack/assets/locales/<lang>/LC_MESSAGES/bootstack.po`
16
+ - `src/bootstack/assets/locales/<lang>/LC_MESSAGES/bootstack.mo`
17
+
18
+ Marking strings for translation
19
+ - In modules that render UI:
20
+ - `from bootstack.i18n import MessageCatalog`
21
+ - `_ = MessageCatalog.translate`
22
+ - Example: `bs.Label(root, text=_('Cancel'))`
23
+ - Formatting:
24
+ - `_("File: %s", name)` uses Python `%` formatting.
25
+ - Legacy Tcl printf like `%1$s` are supported via a formatting fallback.
26
+
27
+ Language switching
28
+ - Change language at runtime:
29
+ - `MessageCatalog.locale('fr')`
30
+ - Bind a refresh: `root.bind('<<LocaleChanged>>', lambda e: refresh())`
31
+
32
+ Runtime overrides (non-compiled messages)
33
+ - Add translations for a locale during runtime:
34
+ - Single: `MessageCatalog.set('fr', 'Hello', 'Bonjour')`
35
+ - Many: `MessageCatalog.set_many('de', 'Open','Oeffnen', 'Cancel','Abbrechen')`
36
+ - Emit refresh if you're already in that locale: `root.event_generate('<<LocaleChanged>>')`
37
+
38
+ Developer workflow (Babel)
39
+ - Config lives at project root: `babel.cfg` (extracts from `src/**/*.py` and `_()`/gettext keywords).
40
+ - Use the helper to manage catalogs (defaults to `src/bootstack/assets/locales` and domain `bootstack`):
41
+ - Extract template: `python tools/make_i18n.py extract`
42
+ - Init locales: `python tools/make_i18n.py init -l de fr`
43
+ - Update catalogs: `python tools/make_i18n.py update`
44
+ - Compile catalogs: `python tools/make_i18n.py compile`
45
+ - The compiled `.mo` files are shipped in the wheel from `assets/locales`.
46
+
47
+ Import path
48
+ - Public API is available at the top-level namespace:
49
+ - `import bootstack as bs; bs.MessageCatalog, bs.L, bs.LV, bs.IntlFormatter`
50
+ - `from bootstack.i18n import MessageCatalog, L, LV, IntlFormatter`
51
+
52
+ Contribution notes
53
+ - Prefer base locales (`de`, `fr`, `nl`) unless region-specific differences are
54
+ required (for example `pt_BR`).
55
+ - Avoid embedding mnemonics `&` in messages; MessageCatalog strips them when rendering.
56
+ - Keep message ids consistent (case and punctuation) to avoid duplicates.
57
+
58
+ Minimum keys to translate for a new language (baseline UI):
59
+ - OK, Ok, Retry, Delete, Next, Prev, Previous
60
+ - Yes, No, Open, Close, Add, Remove, Submit, Cancel
61
+ - Family, Weight, Slant, Effects, Preview, Size
62
+ - Should be of data type, Invalid data type
63
+ - Number cannot be greater than, Number cannot be less than, Out of range
64
+ - The quick brown fox jumps over the lazy dog.
65
+ - Font Selector, Color Chooser, Advanced, Themed, Standard
66
+ - Current, New, Hue, Sat, Lum, Hex, Red, Green, Blue
67
+ - color dropper, Search, Page, of
68
+ - Reset table, Columns, Move, Align
69
+ - Hide column, Delete column, Show All
70
+ - Move to left, Move to right, Move to first, Move to last
71
+ - Align left, Align center, Align right
72
+ - Sort, Filter, Export, Delete selected rows
73
+ - Sort Ascending, Sort Descending, Clear filters
74
+ - Filter by cell's value, Hide select rows, Show only select rows
75
+ - Export all records, Export current page, Export current selection, Export records in filter
76
+ - Move up, Move down, Move to top, Move to bottom
77
+ - Mo, Tu, We, Th, Fr, Sa, Su
@@ -0,0 +1,23 @@
1
+ """Localization for bootstack.
2
+
3
+ Public API: `L` (lazy translated text) and `LV` (lazy locale-formatted
4
+ value), used wherever a widget takes text. `MessageCatalog` (the gettext /
5
+ Tcl-msgcat bridge) and `IntlFormatter` (the Babel value formatter) are the
6
+ internal engines behind them — importable for internal use but not part of the
7
+ public API.
8
+ """
9
+ from .msgcat import MessageCatalog
10
+ from .intl_format import IntlFormatter
11
+ from .specs import L, LV
12
+ from .catalog import add_translation, add_translations, load_po, load_translations
13
+
14
+ # Public surface: L / LV and the catalog-extension helpers. MessageCatalog and
15
+ # IntlFormatter stay importable (internal engines) but are excluded from __all__.
16
+ __all__ = [
17
+ "L",
18
+ "LV",
19
+ "add_translation",
20
+ "add_translations",
21
+ "load_po",
22
+ "load_translations",
23
+ ]