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,471 @@
1
+ from __future__ import annotations
2
+
3
+ from bootstack._runtime.app import get_app_settings
4
+ from bootstack._core.exceptions import ThemeError
5
+ from bootstack.style.utility import (
6
+ shade_color, tint_color, color_to_hsl, hsl_to_hex, best_foreground
7
+ )
8
+
9
+ _registered_themes = {}
10
+ _current_theme = None
11
+
12
+ # Weights for generating color spectrum tints (toward white) and shades (toward black)
13
+ # Each weight represents how much of the original color to retain when mixing
14
+ # Full 50-step increments from 50-450 (tints) and 550-950 (shades)
15
+ # Tint (toward white) / shade (toward black) weights per 50-step stop. The named
16
+ # 100-step stops match Bootstrap 5.3's scale exactly — 100/200/300 = tint
17
+ # 80/60/40, 400 = tint 20, 600/700/800/900 = shade 20/40/60/80 — so `[100]`/
18
+ # `[200]`/`[800]` equal Bootstrap's bg-subtle/border-subtle/text-emphasis. The
19
+ # half-steps (150/250/…) interpolate linearly between the Bootstrap anchors.
20
+ TINT_WEIGHTS = {
21
+ 50: 0.90,
22
+ 100: 0.80,
23
+ 150: 0.70,
24
+ 200: 0.60,
25
+ 250: 0.50,
26
+ 300: 0.40,
27
+ 350: 0.30,
28
+ 400: 0.20,
29
+ 450: 0.10,
30
+ }
31
+ SHADE_WEIGHTS = {
32
+ 550: 0.10,
33
+ 600: 0.20,
34
+ 650: 0.30,
35
+ 700: 0.40,
36
+ 750: 0.50,
37
+ 800: 0.60,
38
+ 850: 0.70,
39
+ 900: 0.80,
40
+ 950: 0.90,
41
+ }
42
+
43
+
44
+ def register_theme(name, data):
45
+ """Register a resolved theme schema dict under `name`.
46
+
47
+ Args:
48
+ name: Canonical theme name used by `set_theme`.
49
+ data: Resolved theme dict (name, display_name, mode, foreground,
50
+ background, white, black, shades, semantic).
51
+ """
52
+ _registered_themes[name] = data
53
+
54
+
55
+ def get_theme(name):
56
+ """Return a registered theme by name.
57
+
58
+ If the theme is not currently registered this will re-run
59
+ `load_system_themes` once to pick up any newly added themes before
60
+ failing.
61
+ """
62
+ if name in _registered_themes:
63
+ return _registered_themes[name]
64
+
65
+ # Lazy fallback: configuration (e.g., load_all_themes/include_legacy_themes)
66
+ # may have changed after the provider singleton was first initialized.
67
+ # Re-run the loader once to pick up any additional themes.
68
+ try:
69
+ load_system_themes()
70
+ if name in _registered_themes:
71
+ return _registered_themes[name]
72
+ except Exception:
73
+ # If anything goes wrong here, fall through to the ThemeError.
74
+ pass
75
+
76
+ # Build a helpful error message listing available themes
77
+ available = sorted(
78
+ {
79
+ data.get("name")
80
+ for data in _registered_themes.values()
81
+ if isinstance(data, dict) and data.get("name")
82
+ }
83
+ )
84
+ available_str = ", ".join(available) if available else "<none>"
85
+ raise ThemeError(
86
+ f"Theme '{name}' is not registered. "
87
+ f"Registered themes: {available_str}"
88
+ )
89
+
90
+
91
+ def load_system_themes():
92
+ """Register the built-in themes and the `light`/`dark` aliases.
93
+
94
+ Built-in themes are plain `Theme` instances (no JSON assets). The themes
95
+ matching the app's configured `light_theme`/`dark_theme` are also registered
96
+ under the `'light'` and `'dark'` aliases. Themes may be registered before an
97
+ App exists (e.g. installing a custom theme with `base=` at module level), so
98
+ this falls back to the default light/dark theme names when there is no app.
99
+ """
100
+ from bootstack.style.themes import install_builtin_themes
101
+
102
+ install_builtin_themes()
103
+
104
+ try:
105
+ app_settings = get_app_settings()
106
+ dark_theme_name = app_settings.dark_theme
107
+ light_theme_name = app_settings.light_theme
108
+ except Exception:
109
+ dark_theme_name = "bootstrap-dark"
110
+ light_theme_name = "bootstrap-light"
111
+
112
+ if dark_theme_name in _registered_themes:
113
+ _registered_themes['dark'] = _registered_themes[dark_theme_name]
114
+ if light_theme_name in _registered_themes:
115
+ _registered_themes['light'] = _registered_themes[light_theme_name]
116
+
117
+
118
+ def color_spectrum(token, value):
119
+ """Generate a color spectrum with 50-step increments from 50-950.
120
+
121
+ Creates tints (lighter) and shades (darker) of the base color:
122
+ - 50-450: Tints toward white (50 is lightest)
123
+ - 500: Base color
124
+ - 550-950: Shades toward black (950 is darkest)
125
+
126
+ Args:
127
+ token: The color token name (e.g., 'gray', 'blue')
128
+ value: The base hex color value
129
+
130
+ Returns:
131
+ Dict mapping spectrum names to hex colors
132
+ """
133
+ result = {}
134
+
135
+ # Generate tints (50-450)
136
+ for stop, weight in TINT_WEIGHTS.items():
137
+ result[f'{token}[{stop}]'] = tint_color(value, weight)
138
+
139
+ # Base color (500)
140
+ result[f'{token}[500]'] = value
141
+
142
+ # Generate shades (550-950)
143
+ for stop, weight in SHADE_WEIGHTS.items():
144
+ result[f'{token}[{stop}]'] = shade_color(value, weight)
145
+
146
+ return result
147
+
148
+
149
+ class ThemeProvider:
150
+ """Theme data provider with singleton access and helpers.
151
+
152
+ Mirrors the pattern used by Style/use_style():
153
+ - `ThemeProvider()` returns the global instance (singleton)
154
+ - `use_theme(name)` returns/initializes the singleton with optional theme
155
+ - `ThemeProvider.instance(name)` remains for backward compatibility
156
+ """
157
+
158
+ # Class-level global singleton instance
159
+ _instance: ThemeProvider | None = None
160
+
161
+ def __new__(cls, *args, **kwargs):
162
+ """Ensure ThemeProvider() always returns the global singleton instance."""
163
+ if cls._instance is None:
164
+ cls._instance = super().__new__(cls)
165
+ return cls._instance
166
+
167
+ def __init__(self, name: str = "dark"):
168
+ # Prevent reinitialization on subsequent ThemeProvider() calls
169
+ if getattr(self, "_initialized", False):
170
+ if name and name != self.name:
171
+ self.use(name)
172
+ return
173
+
174
+ self._theme = {}
175
+ self._colors = {}
176
+ load_system_themes()
177
+
178
+ from bootstack.style.typography import Typography
179
+ Typography.initialize()
180
+
181
+ self.use(name)
182
+ self._initialized = True
183
+
184
+ # ----- Theme metadata helpers -------------------------------------------------
185
+
186
+ def list_themes(self) -> list[dict[str, str]]:
187
+ """Return a list of available themes with names and display names.
188
+
189
+ The result is a list of dictionaries in the form:
190
+
191
+ ```python
192
+ {"name": "bootstrap-light", "display_name": "Bootstrap Light"}
193
+ ```
194
+
195
+ Aliases such as `\"light\"` and `\"dark\"` are not included; only the
196
+ canonical theme entries loaded into the provider are returned.
197
+ """
198
+ themes: list[dict[str, str]] = []
199
+ seen: set[str] = set()
200
+
201
+ for key, data in _registered_themes.items():
202
+ # Skip alias keys that point at an existing theme object
203
+ name = data.get("name")
204
+ if not name:
205
+ continue
206
+ if key != name and name in _registered_themes:
207
+ # This is an alias like 'light' or 'dark'
208
+ continue
209
+ if name in seen:
210
+ continue
211
+ seen.add(name)
212
+ themes.append(
213
+ {
214
+ "name": name,
215
+ "display_name": data.get("display_name", name),
216
+ }
217
+ )
218
+
219
+ # If the application has declared a specific set/order of themes
220
+ # to expose, filter and order by that list.
221
+ select_themes = get_app_settings().available_themes
222
+ if select_themes:
223
+ by_name = {t["name"]: t for t in themes}
224
+ ordered: list[dict[str, str]] = []
225
+ for name in select_themes:
226
+ t = by_name.get(name)
227
+ if t is not None:
228
+ ordered.append(t)
229
+ return ordered
230
+
231
+ # Otherwise, sort for stable UI ordering (by display name, then name)
232
+ themes.sort(key=lambda t: (t["display_name"].lower(), t["name"].lower()))
233
+ return themes
234
+
235
+ def use(self, name):
236
+ self._theme = get_theme(name)
237
+ self.build_theme_colors()
238
+
239
+ @property
240
+ def raw(self):
241
+ """Return the raw source dictionary"""
242
+ return self._theme
243
+
244
+ def build_theme_colors(self):
245
+ colors = {}
246
+ colors.update(
247
+ foreground=self.raw.get('foreground'),
248
+ background=self.raw.get('background'),
249
+ white=self.raw.get('white'),
250
+ black=self.raw.get('black'),
251
+ **self._shades,
252
+ )
253
+ # add shaded spectrum
254
+ for color, value in self._shades.items():
255
+ colors.update(**color_spectrum(color, value))
256
+
257
+ # semantic tokens
258
+ neutral_tokens = {
259
+ "foreground",
260
+ "muted",
261
+ "muted_alt",
262
+ "subtle",
263
+ "border",
264
+ "border_subtle",
265
+ }
266
+ for token, value in self._semantic.items():
267
+ # Neutral roles are derived elsewhere from the top-level
268
+ # foreground/background and should not be overridden here.
269
+ if token in neutral_tokens:
270
+ continue
271
+ colors[token] = colors[value]
272
+
273
+ # Per-semantic subtle / border / emphasis tokens (Bootstrap 5.3 model),
274
+ # sourced from the accent's hue-FAMILY scale stops so they stay vibrant
275
+ # regardless of how dark the semantic solid is. Light: bg-subtle=[100],
276
+ # border-subtle=[200], text-emphasis=[800]; dark uses the dark end.
277
+ # border-subtle + text-emphasis are fixed accent shades (light: [200]/
278
+ # [800]; dark: [700]/[300]). The bg-subtle WASH is NOT precomputed here —
279
+ # it is surface-relative (a blend of the accent anchor into whatever
280
+ # surface it sits on), computed in `StyleBuilder.subtle()`.
281
+ import re as _re
282
+ _stop_re = _re.compile(r'^([a-z]+)\[\d+\]$')
283
+ _is_dark = self.mode == 'dark'
284
+ _stops = ('700', '300') if _is_dark else ('200', '800')
285
+ for token, value in self._semantic.items():
286
+ if token in neutral_tokens:
287
+ continue
288
+ m = _stop_re.match(str(value))
289
+ if m is None:
290
+ continue
291
+ family = m.group(1)
292
+ for suffix, stop in zip(('border_subtle', 'emphasis'), _stops):
293
+ colors[f'{token}_{suffix}'] = colors.get(f'{family}[{stop}]')
294
+
295
+ # Surface tokens - semantic ramps for container backgrounds
296
+ self._build_surface_tokens(colors)
297
+
298
+ # Neutral text tokens — pre-computed for direct use as accent tokens
299
+ from bootstack.style.utility import muted_foreground
300
+ colors['muted'] = muted_foreground(colors['background'])
301
+
302
+ self._colors.clear()
303
+ self._colors.update(**colors)
304
+
305
+ def _build_surface_tokens(self, colors: dict):
306
+ """Build semantic surface tokens for container backgrounds.
307
+
308
+ Surface tokens provide deterministic, theme-defined backgrounds
309
+ that don't rely on "background +1 math" for elevation. The hue
310
+ and saturation are derived from the theme's background color to
311
+ ensure consistent tinting across all surfaces.
312
+
313
+ Token families:
314
+ - chrome: UI shell (sidebars, toolbars, navigation)
315
+ - content: Main content area background (= theme background)
316
+ - card: Elevated content (cards, panels)
317
+ - overlay: Floating elements (menus, dialogs, tooltips)
318
+ - input: Form control backgrounds
319
+ """
320
+ from bootstack.style.utility import darken_color, lighten_color
321
+ is_dark = self.mode == 'dark'
322
+ bg = colors['background']
323
+ fg = colors['foreground']
324
+
325
+ # Derive surfaces by darkening/lightening the BACKGROUND — this preserves
326
+ # the theme's hue AND saturation at every level (rebuilding at a target
327
+ # lightness with capped saturation washed the tint out and could invert
328
+ # the content/sidebar hierarchy). `content` is always the background;
329
+ # `chrome` is the recessed shell tier (darkest in both modes).
330
+ def dk(amount: float) -> str:
331
+ return darken_color(bg, amount)
332
+
333
+ def lt(amount: float) -> str:
334
+ return lighten_color(bg, amount)
335
+
336
+ # Helpers for the muted-foreground + hover tokens below (a tinted gray at
337
+ # a target lightness; the main surfaces use dk/lt above).
338
+ hue, sat, bg_lightness = color_to_hsl(bg, model='hex')
339
+ tint_sat = min(sat, 25) if sat >= 5 else 0
340
+
341
+ def tinted_surface(lightness: float) -> str:
342
+ if tint_sat == 0:
343
+ return hsl_to_hex(0, 0, lightness)
344
+ return hsl_to_hex(hue, tint_sat, lightness)
345
+
346
+ if is_dark:
347
+ # Elevation lightens off the dark bg; chrome darkens (recessed).
348
+ surfaces = {
349
+ 'chrome': dk(0.40),
350
+ 'content': bg,
351
+ 'raised': lt(0.05),
352
+ 'card': lt(0.09),
353
+ 'card_raised': lt(0.15),
354
+ 'overlay': lt(0.12),
355
+ 'input': lt(0.03),
356
+ }
357
+ colors['stroke'] = lt(0.22)
358
+ colors['stroke_subtle'] = lt(0.10)
359
+ else:
360
+ # Surfaces darken off the light bg; content stays the lightest.
361
+ surfaces = {
362
+ 'chrome': dk(0.10),
363
+ 'content': bg,
364
+ 'raised': dk(0.025),
365
+ 'card': dk(0.05),
366
+ 'card_raised': dk(0.085),
367
+ 'overlay': dk(0.025),
368
+ 'input': bg,
369
+ }
370
+ colors['stroke'] = dk(0.14)
371
+ colors['stroke_subtle'] = dk(0.06)
372
+
373
+ # Per-theme surface overrides (escape hatch for cases the derivation
374
+ # can't produce well, e.g. a very-dark or very-light background).
375
+ override = self.raw.get('surfaces') if isinstance(self.raw, dict) else None
376
+ if override:
377
+ for name, value in override.items():
378
+ if name == 'stroke' or name == 'stroke_subtle':
379
+ colors[name] = value
380
+ else:
381
+ surfaces[name] = value
382
+
383
+ # Add surfaces to colors
384
+ for name, value in surfaces.items():
385
+ colors[name] = value
386
+
387
+ # Pre-compute foreground colors for each surface
388
+ candidates = [fg, '#ffffff', '#000000']
389
+ for name, value in surfaces.items():
390
+ colors[f'on_{name}'] = best_foreground(value, candidates)
391
+
392
+ # Secondary/muted foreground for each surface (reduced contrast)
393
+ for name, value in surfaces.items():
394
+ # Generate a muted foreground by mixing fg with the surface
395
+ on_color = colors[f'on_{name}']
396
+ if on_color in ('#ffffff', '#FFFFFF'):
397
+ colors[f'on_{name}_secondary'] = tinted_surface(65) if is_dark else tinted_surface(45)
398
+ else:
399
+ colors[f'on_{name}_secondary'] = tinted_surface(35) if is_dark else tinted_surface(55)
400
+
401
+ # Hover states for each surface (subtle highlight)
402
+ for name in surfaces.keys():
403
+ if name == 'content':
404
+ # Content hover: slightly elevated
405
+ hover_l = min(bg_lightness + 5, 25) if is_dark else max(bg_lightness - 5, 90)
406
+ else:
407
+ # Other surfaces: shift toward content
408
+ surface_h, surface_s, surface_l = color_to_hsl(colors[name], model='hex')
409
+ hover_l = min(surface_l + 5, 30) if is_dark else max(surface_l - 5, 85)
410
+ colors[f'{name}_hover'] = tinted_surface(hover_l)
411
+
412
+ @property
413
+ def name(self):
414
+ """The name of the theme"""
415
+ return self.raw.get('name')
416
+
417
+ @property
418
+ def display_name(self):
419
+ """The display name of the theme"""
420
+ return self.raw.get('display_name')
421
+
422
+ @property
423
+ def mode(self):
424
+ """Returns the color mode 'light' or 'dark'"""
425
+ return self.raw.get('mode')
426
+
427
+ @property
428
+ def colors(self):
429
+ return self._colors
430
+
431
+ @property
432
+ def typography(self):
433
+ """Returns the current typography configuration as FontTokens"""
434
+ from bootstack.style.typography import Typography
435
+ return Typography.all()
436
+
437
+ @property
438
+ def _shades(self):
439
+ return self.raw.get('shades')
440
+
441
+ @property
442
+ def _semantic(self):
443
+ return self.raw.get('semantic')
444
+
445
+ def __repr__(self):
446
+ """Return a string representation of the current theme"""
447
+ return f"<Theme name={self.name} mode={self.mode}>"
448
+
449
+
450
+ def use_theme(name: str = None) -> ThemeProvider:
451
+ """Return the global ThemeProvider singleton instance.
452
+
453
+ Convenience helper that mirrors `use_style()` so callers can obtain
454
+ the current ThemeProvider without handling singleton state.
455
+
456
+ Args:
457
+ name: Optional theme name to switch to, or None to return current instance.
458
+
459
+ Returns:
460
+ Global ThemeProvider instance.
461
+ """
462
+ # If instance doesn't exist yet, create it with default theme
463
+ if ThemeProvider._instance is None:
464
+ return ThemeProvider(name or "dark")
465
+
466
+ # If name is provided and different, switch themes
467
+ if name is not None and name != ThemeProvider._instance.name:
468
+ ThemeProvider._instance.use(name)
469
+
470
+ # Return the singleton
471
+ return ThemeProvider._instance
@@ -0,0 +1,128 @@
1
+ """Built-in color theme families, defined as `Theme` instances.
2
+
3
+ Each family declares its semantic accent colors (the `[500]` ramp anchors) plus a
4
+ light and dark background block; `install()` generates and registers both
5
+ `<name>-light` and `<name>-dark` variants. The framework picks the per-mode ramp
6
+ step for solids, washes, borders, and emphasis — there is no per-mode shade
7
+ boilerplate. Installed into the registry by `install_builtin_themes`, called from
8
+ the theme provider.
9
+
10
+ The non-bootstrap families adapt well-known, open-licensed designer palettes
11
+ (Nord, Solarized, Catppuccin, Gruvbox, Dracula, Tokyo Night, One, Everforest) to
12
+ the framework's semantic roles.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ from bootstack.style.theme import Theme
17
+
18
+ # --- Bootstrap (the neutral reference) -------------------------------------
19
+ BOOTSTRAP = Theme(
20
+ name="bootstrap",
21
+ primary="#0d6efd", success="#198754", info="#0dcaf0", warning="#ffc107", danger="#dc3545",
22
+ light=dict(background="#ffffff", foreground="#212529"),
23
+ dark=dict(background="#212529", foreground="#f8f9fa"),
24
+ )
25
+
26
+ # --- PyData (professional teal) --------------------------------------------
27
+ PYDATA = Theme(
28
+ name="pydata",
29
+ display_name="PyData",
30
+ primary="#0a7d91", success="#198754", info="#0dcaf0", warning="#ffc107", danger="#dc3545",
31
+ secondary="#8045e5",
32
+ neutral="#677384",
33
+ light=dict(background="#ffffff", foreground="#222832"),
34
+ dark=dict(background="#14181e", foreground="#ced6dd"),
35
+ )
36
+
37
+ # --- Nord (cool arctic) -----------------------------------------------------
38
+ NORD = Theme(
39
+ name="nord",
40
+ primary="#5e81ac", success="#a3be8c", info="#88c0d0", warning="#ebcb8b", danger="#bf616a",
41
+ secondary="#b48ead",
42
+ neutral="#4c566a",
43
+ light=dict(background="#eceff4", foreground="#2e3440"),
44
+ dark=dict(background="#2e3440", foreground="#eceff4"),
45
+ )
46
+
47
+ # --- Solarized (precision classic) -----------------------------------------
48
+ SOLARIZED = Theme(
49
+ name="solarized",
50
+ primary="#268bd2", success="#859900", info="#2aa198", warning="#b58900", danger="#dc322f",
51
+ secondary="#6c71c4",
52
+ neutral="#839496",
53
+ light=dict(background="#f6f1e9", foreground="#586e75"),
54
+ dark=dict(background="#002b36", foreground="#93a1a1"),
55
+ )
56
+
57
+ # --- Catppuccin (soft pastel) ----------------------------------------------
58
+ CATPPUCCIN = Theme(
59
+ name="catppuccin",
60
+ primary="#8839ef", success="#40a02b", info="#179299", warning="#df8e1d", danger="#d20f39",
61
+ secondary="#ea76cb",
62
+ neutral="#8c8fa1",
63
+ light=dict(background="#eff1f5", foreground="#4c4f69"),
64
+ dark=dict(background="#1e1e2e", foreground="#cdd6f4"),
65
+ )
66
+
67
+ # --- Gruvbox (warm retro) ---------------------------------------------------
68
+ GRUVBOX = Theme(
69
+ name="gruvbox",
70
+ primary="#458588", success="#98971a", info="#689d6a", warning="#d79921", danger="#cc241d",
71
+ secondary="#d65d0e",
72
+ neutral="#928374",
73
+ light=dict(background="#f2ede9", foreground="#3c3836"),
74
+ dark=dict(background="#282828", foreground="#ebdbb2"),
75
+ )
76
+
77
+ # --- Dracula (vibrant) ------------------------------------------------------
78
+ DRACULA = Theme(
79
+ name="dracula",
80
+ primary="#bd93f9", success="#50fa7b", info="#8be9fd", warning="#ffb86c", danger="#ff5555",
81
+ secondary="#ff79c6",
82
+ neutral="#6272a4",
83
+ light=dict(background="#f8f8f2", foreground="#282a36"),
84
+ dark=dict(background="#282a36", foreground="#f8f8f2"),
85
+ )
86
+
87
+ # --- Tokyo Night (deep blue/purple) ----------------------------------------
88
+ TOKYO_NIGHT = Theme(
89
+ name="tokyo-night",
90
+ display_name="Tokyo Night",
91
+ primary="#7aa2f7", success="#9ece6a", info="#7dcfff", warning="#e0af68", danger="#f7768e",
92
+ secondary="#bb9af7",
93
+ neutral="#565f89",
94
+ light=dict(background="#e1e2e7", foreground="#343b58"),
95
+ dark=dict(background="#1a1b26", foreground="#c0caf5"),
96
+ )
97
+
98
+ # --- One (Atom, clean neutral) ---------------------------------------------
99
+ ONE = Theme(
100
+ name="one",
101
+ primary="#4078f2", success="#50a14f", info="#0184bc", warning="#c18401", danger="#e45649",
102
+ secondary="#a626a4",
103
+ neutral="#a0a1a7",
104
+ light=dict(background="#fafafa", foreground="#383a42"),
105
+ dark=dict(background="#282c34", foreground="#abb2bf"),
106
+ )
107
+
108
+ # --- Everforest (green-leaning, soft) --------------------------------------
109
+ EVERFOREST = Theme(
110
+ name="everforest",
111
+ primary="#3a94c5", success="#8da101", info="#35a77c", warning="#dfa000", danger="#f85552",
112
+ secondary="#df69ba",
113
+ neutral="#939f91",
114
+ light=dict(background="#edf3ed", foreground="#5c6a72"),
115
+ dark=dict(background="#2d353b", foreground="#d3c6aa"),
116
+ )
117
+
118
+ #: All built-in theme families, in display order.
119
+ BUILTIN_THEMES = [
120
+ BOOTSTRAP, PYDATA, NORD, SOLARIZED, CATPPUCCIN,
121
+ GRUVBOX, DRACULA, TOKYO_NIGHT, ONE, EVERFOREST,
122
+ ]
123
+
124
+
125
+ def install_builtin_themes() -> None:
126
+ """Register every built-in theme variant into the theme registry (idempotent)."""
127
+ for theme in BUILTIN_THEMES:
128
+ theme.install()
@@ -0,0 +1,5 @@
1
+ """Shim so style imports keep working while runtime owns tk_patch."""
2
+
3
+ from bootstack._runtime.tk_patch import install_tk_autostyle
4
+
5
+ __all__ = ["install_tk_autostyle"]
@@ -0,0 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ COLOR_TOKENS = {
4
+ 'primary', 'secondary', 'info', 'success', 'warning', 'danger',
5
+ 'foreground', 'background', 'white', 'black',
6
+ 'muted',
7
+ 'blue', 'indigo', 'purple', 'red', 'orange',
8
+ 'yellow', 'green', 'teal', 'cyan', 'gray',
9
+ 'border'
10
+ }
11
+
12
+ WIDGET_CLASS_MAP = {
13
+ 'badge': 'TBadge',
14
+ 'button': 'TButton',
15
+ 'label': 'TLabel',
16
+ 'entry': 'TEntry',
17
+ 'frame': 'TFrame',
18
+ 'labelframe': 'TLabelframe',
19
+ 'progressbar': 'TProgressbar',
20
+ 'scale': 'TScale',
21
+ 'scrollbar': 'TScrollbar',
22
+ 'checkbutton': 'TCheckbutton',
23
+ 'radiobutton': 'TRadiobutton',
24
+ 'combobox': 'TCombobox',
25
+ 'treeview': 'Treeview',
26
+ 'separator': 'TSeparator',
27
+ 'sizegrip': 'TSizegrip',
28
+ 'panedwindow': 'TPanedwindow',
29
+ 'spinbox': 'TSpinbox',
30
+ 'menubutton': 'TMenubutton',
31
+ 'field': 'TField',
32
+ 'toolbutton': 'Toolbutton',
33
+ 'tooltip': 'Tooltip',
34
+ 'calendar': 'TCalendar',
35
+ 'buttongroup': 'ButtonGroup'
36
+ }
37
+
38
+ WIDGET_NAME_MAP = {v: k for k, v in WIDGET_CLASS_MAP.items()}
39
+ CONTAINER_CLASSES = {'TFrame'}
40
+ ORIENT_CLASSES = {'TProgressbar', 'TScale', 'TScrollbar', 'TPanedwindow', 'TSeparator'}
41
+ ICON_CLASSES = {'TLabel', 'TButton', 'TCheckbutton', 'TRadiobutton', 'TMenubutton'}
@@ -0,0 +1,32 @@
1
+ from tkinter.font import Font
2
+ from typing import Literal, Union
3
+
4
+ ColorShade = Literal[100, 200, 300, 400, 500, 600, 700, 800, 900]
5
+ ColorMode = Union[Literal['light', 'dark'], str]
6
+ ColorModel = Literal['hex', 'hsl', 'rgb']
7
+
8
+ # === Color Types ===
9
+
10
+ # supports subtle variants, e.g., 'primary[subtle]'
11
+ SemanticColor = Literal['primary', 'success', 'warning', 'danger']
12
+ UtilityColor = Literal['foreground', 'background']
13
+
14
+ # supports shade variants 100-900, e.g. `blue[300]`
15
+ ShadeColor = Literal['blue', 'indigo', 'purple', 'red', 'orange', 'yellow', 'green', 'teal', 'cyan', 'gray']
16
+
17
+ # === Color Tokens ===
18
+
19
+ SurfaceColor = Union[SemanticColor, str]
20
+ ForegroundColor = Union[SemanticColor, ShadeColor, UtilityColor, str]
21
+ ThemeColor = Union[SemanticColor, ShadeColor, UtilityColor, str]
22
+ SeparatorColor = Union[Literal['border'], SemanticColor]
23
+ BorderColor = Union[Literal['border'], SemanticColor, ShadeColor, str]
24
+
25
+ # === Font Tokens ====
26
+
27
+ BootstrapFontType = Literal[
28
+ 'label', 'body', 'body-sm', 'body-lg', 'body-xl', 'caption',
29
+ 'display-xl', 'display-lg', 'heading-xl', 'heading-lg', 'heading-md', 'code'
30
+ ]
31
+
32
+ TypographyToken = Union[BootstrapFontType, str, Font]