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,609 @@
1
+ """Enhanced TTK Style class with builder registry and theme management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import tkinter
6
+ from tkinter.ttk import Style as ttkStyle
7
+ from typing import Dict, Optional, Set
8
+
9
+ from bootstack._runtime.app import get_app_settings
10
+ from bootstack.style.style_builder_ttk import StyleBuilderTtk
11
+ from bootstack.style.theme_provider import ThemeProvider, use_theme
12
+ from bootstack.widgets.types import Master
13
+
14
+ _style_instance: Style | None = None
15
+
16
+
17
+ class Style(ttkStyle):
18
+ """Enhanced TTK Style with builder registry and theme management.
19
+
20
+ This class extends ttk.Style to provide:
21
+
22
+ - Singleton pattern (one instance per Tkinter master)
23
+ - Integration with StyleBuilder registry
24
+ - Theme management via ThemeProvider
25
+ - Automatic style rebuilding on theme changes
26
+ - Custom style options support
27
+
28
+ The Style class maintains registries to track:
29
+
30
+ - All created styles (for theme change rebuilds)
31
+ - Which styles exist in each theme (for caching)
32
+ - Custom options per style (for recreating with same options)
33
+ """
34
+
35
+ def __new__(cls, *args, **kwargs):
36
+ """Ensure Style() always returns the global singleton instance.
37
+
38
+ If an instance already exists, return it. Otherwise, create it.
39
+ """
40
+ global _style_instance
41
+ if _style_instance is None:
42
+ _style_instance = super().__new__(cls)
43
+ return _style_instance
44
+
45
+ def __init__(self, master: Master = None, theme: str = "light"):
46
+ """Initialize the Style instance.
47
+
48
+ Args:
49
+ master: Tkinter master widget (None for default)
50
+ theme: Initial theme name (default: "light")
51
+ """
52
+ # Prevent reinitialization on subsequent Style() calls
53
+ if getattr(self, "_initialized", False):
54
+ # Optionally honor a theme argument after initialization
55
+ try:
56
+ if theme and theme != self._current_theme:
57
+ self.theme_use(theme)
58
+ except Exception:
59
+ pass
60
+ return
61
+
62
+ super().__init__(master)
63
+
64
+ self._theme_provider = use_theme(theme)
65
+ self._style_builder = StyleBuilderTtk(theme_provider=self._theme_provider, style_instance=None)
66
+ self._style_builder.set_style_instance(self)
67
+
68
+ # Style registries
69
+ self._style_registry: Set[str] = set()
70
+ self._style_accents: Dict[str, Optional[str]] = {}
71
+ self._style_variants: Dict[str, str] = {}
72
+ self._style_options: Dict[str, dict] = {}
73
+ self._style_widget_classes: Dict[str, str] = {}
74
+
75
+ # Current theme tracking
76
+ self._current_theme: Optional[str] = theme
77
+
78
+ # Incremented on every theme change so a widget can detect a restyle it
79
+ # missed while off-screen (stamped on `_bs_theme_version`; see
80
+ # apply_theme_walk).
81
+ self._theme_version: int = 0
82
+
83
+ # Cached Tk builder — lazily initialized, reused across all widget creations
84
+ self._cached_tk_builder = None
85
+
86
+ self.theme_use(theme)
87
+ self._initialized = True
88
+
89
+ @property
90
+ def style_builder(self) -> StyleBuilderTtk:
91
+ """Get the builder manager instance.
92
+
93
+ Returns:
94
+ StyleBuilder instance
95
+ """
96
+ return self._style_builder
97
+
98
+ @property
99
+ def theme_provider(self) -> ThemeProvider:
100
+ """Get the theme provider instance.
101
+
102
+ Returns:
103
+ ThemeProvider instance
104
+ """
105
+ return self._theme_provider
106
+
107
+ # ----- Theme metadata helpers -------------------------------------------------
108
+
109
+ def list_themes(self) -> list[dict[str, str]]:
110
+ """Return available themes as [{'name', 'display_name'}, ...].
111
+
112
+ This delegates to the underlying ThemeProvider. Themes are always
113
+ loaded from both the v2 and legacy theme packages.
114
+
115
+ Returns:
116
+ List of theme dictionaries with 'name' and 'display_name' keys.
117
+ """
118
+ return self._theme_provider.list_themes()
119
+
120
+ @property
121
+ def current_theme(self) -> Optional[str]:
122
+ """Get the current theme name.
123
+
124
+ Returns:
125
+ Current theme name or None
126
+ """
127
+ return self._current_theme
128
+
129
+ @property
130
+ def colors(self) -> dict[str, str]:
131
+ """Get colors for the current theme.
132
+
133
+ Returns:
134
+ Colors dictionary from ThemeProvider
135
+ """
136
+ return self._theme_provider.colors
137
+
138
+ def style_exists(self, style: str) -> bool:
139
+ """Check if a style exists (basic check).
140
+
141
+ Args:
142
+ style: TTK style name
143
+
144
+ Returns:
145
+ True if style has any configuration
146
+ """
147
+ return bool(self.configure(style))
148
+
149
+ def register_style(self, ttk_style: str, options: Optional[dict] = None):
150
+ """Register a style in the current theme.
151
+
152
+ This adds the style to registries so it can be:
153
+
154
+ - Cached (not recreated if already exists)
155
+ - Rebuilt when theme changes
156
+ - Recreated with same options
157
+
158
+ Args:
159
+ ttk_style: Full TTK style name
160
+ options: Optional custom style options
161
+ """
162
+ # Add to global registry
163
+ self._style_registry.add(ttk_style)
164
+
165
+ # Store a copy of custom options if provided (to prevent mutation issues)
166
+ if options:
167
+ self._style_options[ttk_style] = options.copy()
168
+
169
+ def create_style(
170
+ self,
171
+ widget_class: str,
172
+ variant: str,
173
+ ttk_style: str,
174
+ accent: Optional[str] = None,
175
+ options: Optional[dict] = None) -> None:
176
+ """Create a new style if it doesn't exist in current theme.
177
+
178
+ Args:
179
+ widget_class: TTK widget class (e.g., "TButton")
180
+ variant: Variant name (e.g., "outline")
181
+ ttk_style: Full TTK style name (e.g., "success.Outline.TButton")
182
+ accent: Optional accent token (e.g., "success", "blue[100]")
183
+ options: Optional custom style options
184
+ """
185
+ # Fast path: Python-side set lookup before crossing into Tcl
186
+ if ttk_style in self._style_registry:
187
+ return
188
+ # Fallback for styles known to Tcl but not our registry (e.g. external theme changes)
189
+ if self.style_exists(ttk_style):
190
+ self.register_style(ttk_style, options)
191
+ return
192
+
193
+ # Call builder with widget class, variant, and parsed accent
194
+ self._style_builder.call_builder(
195
+ widget_class=widget_class,
196
+ variant=variant,
197
+ ttk_style=ttk_style,
198
+ accent=accent,
199
+ **(options or {})
200
+ )
201
+
202
+ # Store the accent, variant, and widget_class so we can rebuild with the same values
203
+ self._style_accents[ttk_style] = accent
204
+ self._style_variants[ttk_style] = variant
205
+ self._style_widget_classes[ttk_style] = widget_class
206
+
207
+ # Register it
208
+ self.register_style(ttk_style, options)
209
+
210
+ def theme_use(self, name: str = None) -> str | None:
211
+ """Switch to a different theme and rebuild all styles.
212
+
213
+ Applies theme change to the global Style instance, rebuilds all
214
+ TTK styles and registered Tk widgets, and publishes a legacy
215
+ theme-change event for subscribers.
216
+
217
+ Args:
218
+ name: Theme name to switch to. If None, returns the current theme.
219
+
220
+ Returns:
221
+ The current theme name, or None if no theme is set.
222
+ """
223
+ if name is None:
224
+ return super().theme_use()
225
+
226
+ # Only call use() if theme is changing to avoid redundant build_theme_colors()
227
+ if name != self._theme_provider.name:
228
+ self._theme_provider.use(name)
229
+ self._current_theme = name
230
+
231
+ if name not in self.theme_names():
232
+ self.theme_create(name, 'clam', {})
233
+
234
+ super().theme_use(name)
235
+
236
+ # Initialize all default widget styles before rebuilding custom styles
237
+ # self._style_builder.initialize_all_default_styles()
238
+
239
+ self._rebuild_all_styles()
240
+
241
+ # Bump version so off-screen widgets know they missed this theme change
242
+ # (they recolor when their container next shows them — see apply_theme_walk).
243
+ self._theme_version += 1
244
+
245
+ # Fire the bootstack theme-change event after full rebuild so handlers
246
+ # can safely read new colors. Uses get_default_root lazily to avoid
247
+ # importing app at module level.
248
+ try:
249
+ from bootstack._runtime.app import get_default_root
250
+ root = get_default_root()
251
+ # Unified theme walk: recolor every visible tree widget now; off-screen
252
+ # ones are left stale and recovered when their page/tab/section is next
253
+ # shown. Non-tree reactors (the Image handle, OS window chrome, the
254
+ # app-level on_theme_change callback) listen on <<BsThemeChanged>> below.
255
+ if root is not None:
256
+ self.apply_theme_walk(root, only_stale=False)
257
+ root.event_generate(
258
+ "<<BsThemeChanged>>",
259
+ data={"theme": name, "mode": self._theme_provider.mode},
260
+ when="tail",
261
+ )
262
+ except Exception:
263
+ pass
264
+
265
+ return self._current_theme
266
+
267
+ def _rebuild_all_styles(self):
268
+ """Recreate all registered styles when theme changes.
269
+
270
+ This iterates through all styles that have been created and
271
+ rebuilds them using the new theme's colors, preserving any
272
+ custom options.
273
+ """
274
+ for style in self._style_registry:
275
+ # Get stored options, accent, and variant
276
+ options = self._style_options.get(style, {})
277
+ accent = self._style_accents.get(style)
278
+ variant = self._style_variants.get(style)
279
+
280
+ # Use stored widget_class if available (preserves composite classes like 'Menubar.TField')
281
+ # Fall back to parsing from style name for backwards compatibility
282
+ widget_class = self._style_widget_classes.get(style)
283
+ if not widget_class:
284
+ parsed = self._parse_style_name(style)
285
+ if not parsed:
286
+ continue
287
+ widget_class = parsed['widget_class']
288
+ # Only use parsed variant if we don't have a stored one
289
+ if variant is None:
290
+ variant = parsed['variant']
291
+
292
+ if variant is None:
293
+ from bootstack.style.style_builder_ttk import StyleBuilderTtk
294
+ variant = StyleBuilderTtk.get_default_variant(widget_class)
295
+
296
+ self._style_builder.call_builder(
297
+ widget_class=widget_class,
298
+ variant=variant,
299
+ ttk_style=style,
300
+ accent=accent,
301
+ **options
302
+ )
303
+
304
+ def _get_tk_builder(self):
305
+ """Return the cached Tk builder, creating it on first call."""
306
+ if self._cached_tk_builder is None:
307
+ from bootstack.style.style_builder_tk import StyleBuilderTk
308
+ self._cached_tk_builder = StyleBuilderTk(
309
+ theme_provider=self._theme_provider,
310
+ style_instance=self,
311
+ )
312
+ return self._cached_tk_builder
313
+
314
+ # ------------------------------------------------------------------ Unified theme walk
315
+ #
316
+ # The single mechanism for recoloring *tree widgets* on a theme change. A
317
+ # widget that needs theming exposes one method, ``_bs_apply_theme()``, doing
318
+ # all of its work (background + any canvas repaint). Two triggers drive it,
319
+ # and neither relies on ``<Map>``:
320
+ # * a theme change walks from the root and applies every *visible* widget;
321
+ # * a container showing a page/tab walks that subtree and applies the
322
+ # *stale* ones (those that missed the change while hidden).
323
+ # Visibility is resolved at apply time, so nothing is deferred to a hoped-for
324
+ # event. Non-tree reactors (the Image handle, OS window chrome, the app-level
325
+ # on_theme_change callback) stay on the root ``<<BsThemeChanged>>`` binding.
326
+
327
+ def _apply_theme_to_widget(self, widget) -> None:
328
+ """Apply the current theme to one widget and stamp its version.
329
+
330
+ Re-applies the surface background for an autostyled Tk widget, then runs
331
+ the widget's ``_bs_apply_theme`` hook if it defines one (canvas painters
332
+ redraw there). Both are best-effort; a failure on one widget never aborts
333
+ the walk.
334
+ """
335
+ surface = getattr(widget, '_surface', None)
336
+ if surface is not None:
337
+ try:
338
+ self._get_tk_builder().call_builder(widget, surface=surface)
339
+ except Exception:
340
+ pass
341
+ hook = getattr(widget, '_bs_apply_theme', None)
342
+ if hook is not None:
343
+ try:
344
+ hook()
345
+ except Exception:
346
+ pass
347
+ widget._bs_theme_version = self._theme_version
348
+
349
+ def apply_theme_walk(self, root_widget, *, only_stale: bool) -> None:
350
+ """Walk ``root_widget``'s subtree and theme each on-screen widget.
351
+
352
+ Args:
353
+ root_widget: Subtree root — the application root on a theme change, or
354
+ a freshly-shown page/tab on a container-show.
355
+ only_stale: When True (container-show), apply every widget in the
356
+ subtree that missed the current theme version — regardless of
357
+ ``winfo_viewable()``, which is unreliable for a widget embedded in
358
+ a scrollable page's canvas; the container is showing it now. When
359
+ False (theme change), apply every *visible* widget and leave the
360
+ off-screen ones stale for their next container-show.
361
+ """
362
+ version = self._theme_version
363
+ stack = [root_widget]
364
+ while stack:
365
+ w = stack.pop()
366
+ # A PageStack keeps inactive pages mapped (so re-showing one doesn't
367
+ # blank-flash on macOS) but flags them hidden; prune the whole subtree
368
+ # so a theme change repaints only the visible page. A hidden page's
369
+ # widgets stay stale and are repainted on its next show (only_stale).
370
+ if getattr(w, '_bs_nav_hidden', False):
371
+ continue
372
+ try:
373
+ stack.extend(w.winfo_children())
374
+ except Exception:
375
+ pass
376
+ themed = hasattr(w, '_bs_apply_theme') or getattr(w, '_surface', None) is not None
377
+ if not themed:
378
+ continue
379
+ if only_stale:
380
+ if getattr(w, '_bs_theme_version', -1) == version:
381
+ continue
382
+ else:
383
+ try:
384
+ if not w.winfo_viewable():
385
+ continue
386
+ except Exception:
387
+ continue
388
+ self._apply_theme_to_widget(w)
389
+
390
+ @staticmethod
391
+ def _parse_style_name(ttk_style: str) -> Optional[dict]:
392
+ """Parse TTK style name to extract widget class and variant.
393
+
394
+ Args:
395
+ ttk_style: Full TTK style name
396
+
397
+ Returns:
398
+ Dict with 'widget_class' and 'variant', or None
399
+
400
+ Examples:
401
+ >>> Style._parse_style_name("success.TButton")
402
+ {'widget_class': 'TButton', 'variant': 'solid'}
403
+ >>> Style._parse_style_name("info.Thin.TProgressbar")
404
+ {'widget_class': 'TProgressbar', 'variant': 'thin'}
405
+ """
406
+ # Split into parts
407
+ parts = ttk_style.split('.')
408
+
409
+ if not parts:
410
+ return None
411
+
412
+ # Extract widget class (e.g., "TButton")
413
+ # Widget class is always the LAST part starting with 'T'
414
+ widget_class = None
415
+ for part in parts:
416
+ if part.startswith('T'):
417
+ widget_class = part
418
+
419
+ if not widget_class:
420
+ return None
421
+
422
+ # Determine variant using registered builders for this widget
423
+ from bootstack.style.style_builder_ttk import StyleBuilderTtk
424
+ builder_variants = set(v.lower() for v in StyleBuilderTtk.get_registered_builders(widget_class))
425
+
426
+ variant = None
427
+ accent = None
428
+ for part in parts:
429
+ # Skip the widget class itself (e.g., 'TCheckbutton')
430
+ if part == widget_class:
431
+ continue
432
+ token = part.lower()
433
+ # Identify variant by registry match
434
+ if token in builder_variants and variant is None:
435
+ variant = token
436
+ continue
437
+ # First non-variant token becomes accent
438
+ if accent is None:
439
+ accent = part
440
+
441
+ if variant is None:
442
+ variant = StyleBuilderTtk.get_default_variant(widget_class)
443
+
444
+ return {
445
+ 'widget_class': widget_class,
446
+ 'variant': variant
447
+ }
448
+
449
+ def get_style_builder(self) -> StyleBuilderTtk:
450
+ """Get the style builder instance.
451
+
452
+ Returns:
453
+ StyleBuilder instance
454
+ """
455
+ return self._style_builder
456
+
457
+ def __repr__(self) -> str:
458
+ """String representation of Style instance."""
459
+ return f"<Style theme={self._current_theme} styles={len(self._style_registry)}>"
460
+
461
+
462
+ def get_style(master: Master = None) -> Style:
463
+ """Return the global Style singleton instance.
464
+
465
+ Convenience helper function that ensures a single Style instance
466
+ is used across the application.
467
+
468
+ Args:
469
+ master: Optional master for initial construction; ignored thereafter.
470
+
471
+ Returns:
472
+ Global Style instance.
473
+
474
+ Examples:
475
+ >>> style = get_style()
476
+ >>> style.theme_use("nord-dark")
477
+ """
478
+ if _style_instance:
479
+ return _style_instance
480
+ else:
481
+ return Style(master)
482
+
483
+
484
+ def reset_style() -> None:
485
+ """Drop the process-wide Style singleton.
486
+
487
+ The Style singleton is a `ttk.Style` bound to the Tk root that created it.
488
+ Once that root is destroyed its interpreter is gone, so the singleton can no
489
+ longer be used — a fresh App must rebuild Style against the new root. Call
490
+ this after destroying the root (App.destroy does) so the next
491
+ `get_style()`/`Style()` constructs cleanly instead of reusing a dead
492
+ instance (whose `theme_use` would fail, e.g. "named font body does not
493
+ already exist").
494
+ """
495
+ global _style_instance
496
+ _style_instance = None
497
+
498
+
499
+ def get_style_builder() -> StyleBuilderTtk:
500
+ """Return the style builder for the currently active theme.
501
+
502
+ Returns:
503
+ The StyleBuilderTtk instance for the active theme.
504
+
505
+ Examples:
506
+ >>> builder = get_style_builder()
507
+ >>> primary_color = builder.color("primary")
508
+ """
509
+ style = get_style()
510
+ return style.style_builder
511
+
512
+
513
+ # --- Global Utilities ---
514
+
515
+ def set_theme(name: str) -> None:
516
+ """Set the active application theme.
517
+
518
+ Args:
519
+ name: Theme name to activate (e.g., "bootstrap-light", "nord-dark").
520
+
521
+ Examples:
522
+ >>> set_theme("nord-dark")
523
+ """
524
+ style = get_style()
525
+ style.theme_use(name)
526
+
527
+
528
+ def toggle_theme() -> None:
529
+ """Toggle the active application theme between light and dark mode.
530
+
531
+ Uses the light and dark themes specified in app settings, or defaults
532
+ to bootstrap-light and bootstrap-dark.
533
+
534
+ Examples:
535
+ >>> toggle_theme()
536
+ """
537
+ settings = get_app_settings()
538
+ light = settings.light_theme
539
+ dark = settings.dark_theme
540
+ theme = get_theme()
541
+ if theme == light or theme == "light":
542
+ set_theme(dark)
543
+ else:
544
+ set_theme(light)
545
+
546
+
547
+ def get_theme() -> str:
548
+ """Return the name of the currently active theme.
549
+
550
+ Returns:
551
+ Name of the active theme.
552
+
553
+ Examples:
554
+ >>> theme = get_theme()
555
+ >>> print(theme) # "bootstrap-dark"
556
+ """
557
+ style = get_style()
558
+ return style.theme_use()
559
+
560
+
561
+ def get_themes() -> list[dict[str, str]]:
562
+ """Return the list of all registered themes.
563
+
564
+ Returns:
565
+ List of dictionaries containing `name` and `display_name` for each theme.
566
+
567
+ Examples:
568
+ >>> themes = get_themes()
569
+ >>> print([theme["name"] for theme in themes])
570
+ """
571
+ style = get_style()
572
+ return style.list_themes()
573
+
574
+
575
+ def get_theme_provider() -> ThemeProvider:
576
+ """Get the theme provider instance for the active theme.
577
+
578
+ Returns:
579
+ ThemeProvider instance.
580
+
581
+ Examples:
582
+ >>> provider = get_theme_provider()
583
+ >>> colors = provider.get_colors()
584
+ """
585
+ style = get_style()
586
+ return style.theme_provider
587
+
588
+
589
+ def get_theme_color(token: str) -> str:
590
+ """Get a hex color value from a color token based on the active theme.
591
+
592
+ Args:
593
+ token: Color token name (e.g., "primary", "background").
594
+
595
+ Returns:
596
+ Hex color string (e.g., "#007bff").
597
+
598
+ Raises:
599
+ ValueError: If the color token is invalid.
600
+
601
+ Examples:
602
+ >>> primary = get_theme_color("primary")
603
+ >>> print(primary) # "#007bff"
604
+ """
605
+ builder = get_style_builder()
606
+ try:
607
+ return builder.color(token)
608
+ except Exception:
609
+ raise ValueError(f"Invalid color token: {token}")