bootstack 0.1.0a1__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 (419) hide show
  1. bootstack/__init__.py +249 -0
  2. bootstack/__main__.py +5 -0
  3. bootstack/api/__init__.py +127 -0
  4. bootstack/api/app.py +30 -0
  5. bootstack/api/constants.py +3 -0
  6. bootstack/api/data.py +23 -0
  7. bootstack/api/dialogs.py +44 -0
  8. bootstack/api/i18n.py +17 -0
  9. bootstack/api/localization.py +16 -0
  10. bootstack/api/menu.py +7 -0
  11. bootstack/api/style.py +23 -0
  12. bootstack/api/utils.py +24 -0
  13. bootstack/api/widgets.py +137 -0
  14. bootstack/assets/__init__.py +24 -0
  15. bootstack/assets/bootstack-transparent.png +0 -0
  16. bootstack/assets/bootstack.ico +0 -0
  17. bootstack/assets/bootstack.png +0 -0
  18. bootstack/assets/elements/__init__.py +0 -0
  19. bootstack/assets/elements/badge-pill.png +0 -0
  20. bootstack/assets/elements/badge-square.png +0 -0
  21. bootstack/assets/elements/border.png +0 -0
  22. bootstack/assets/elements/button-compact.png +0 -0
  23. bootstack/assets/elements/button-default.png +0 -0
  24. bootstack/assets/elements/button-group-horizontal-after-compact.png +0 -0
  25. bootstack/assets/elements/button-group-horizontal-after-default.png +0 -0
  26. bootstack/assets/elements/button-group-horizontal-before-compact.png +0 -0
  27. bootstack/assets/elements/button-group-horizontal-before-default.png +0 -0
  28. bootstack/assets/elements/button-group-horizontal-center-compact.png +0 -0
  29. bootstack/assets/elements/button-group-horizontal-center-default.png +0 -0
  30. bootstack/assets/elements/button-group-vertical-after-compact.png +0 -0
  31. bootstack/assets/elements/button-group-vertical-after-default.png +0 -0
  32. bootstack/assets/elements/button-group-vertical-before-compact.png +0 -0
  33. bootstack/assets/elements/button-group-vertical-before-default.png +0 -0
  34. bootstack/assets/elements/button-group-vertical-center-compact.png +0 -0
  35. bootstack/assets/elements/button-group-vertical-center-default.png +0 -0
  36. bootstack/assets/elements/checkbox-checked.png +0 -0
  37. bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  38. bootstack/assets/elements/checkbox-unchecked.png +0 -0
  39. bootstack/assets/elements/field.png +0 -0
  40. bootstack/assets/elements/input-after-compact.png +0 -0
  41. bootstack/assets/elements/input-after-default.png +0 -0
  42. bootstack/assets/elements/input-before-compact.png +0 -0
  43. bootstack/assets/elements/input-before-default.png +0 -0
  44. bootstack/assets/elements/input-compact.png +0 -0
  45. bootstack/assets/elements/input-default.png +0 -0
  46. bootstack/assets/elements/list-item-separated.png +0 -0
  47. bootstack/assets/elements/list-item.png +0 -0
  48. bootstack/assets/elements/manifest.toml +480 -0
  49. bootstack/assets/elements/menu-item.png +0 -0
  50. bootstack/assets/elements/nav-button-compact.png +0 -0
  51. bootstack/assets/elements/nav-button-default.png +0 -0
  52. bootstack/assets/elements/nav-icon-button-compact.png +0 -0
  53. bootstack/assets/elements/nav-icon-button-default.png +0 -0
  54. bootstack/assets/elements/notebook-client-border.png +0 -0
  55. bootstack/assets/elements/notebook-tab-active.png +0 -0
  56. bootstack/assets/elements/notebook-tab-bar.png +0 -0
  57. bootstack/assets/elements/notebook-tab-normal.png +0 -0
  58. bootstack/assets/elements/notebook-tab-pill.png +0 -0
  59. bootstack/assets/elements/progress-bar-horizontal-striped.png +0 -0
  60. bootstack/assets/elements/progress-bar-solid.png +0 -0
  61. bootstack/assets/elements/progress-bar-thin.png +0 -0
  62. bootstack/assets/elements/progress-bar-vertical-striped.png +0 -0
  63. bootstack/assets/elements/radio-selected.png +0 -0
  64. bootstack/assets/elements/radio-unselected.png +0 -0
  65. bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  66. bootstack/assets/elements/scrollbar-vertical.png +0 -0
  67. bootstack/assets/elements/slider-handle-focus.png +0 -0
  68. bootstack/assets/elements/slider-handle.png +0 -0
  69. bootstack/assets/elements/slider-track-horizontal.png +0 -0
  70. bootstack/assets/elements/slider-track-vertical.png +0 -0
  71. bootstack/assets/elements/switch-off.png +0 -0
  72. bootstack/assets/elements/switch-on.png +0 -0
  73. bootstack/assets/elements/tabs-bar-horizontal.png +0 -0
  74. bootstack/assets/elements/tabs-bar-vertical.png +0 -0
  75. bootstack/assets/elements/tabs-pill.png +0 -0
  76. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  77. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +853 -0
  78. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +875 -0
  79. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  80. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +853 -0
  81. bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  82. bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +853 -0
  83. bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  84. bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +853 -0
  85. bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  86. bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +875 -0
  87. bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  88. bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +853 -0
  89. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  90. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +853 -0
  91. bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  92. bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +851 -0
  93. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  94. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +842 -0
  95. bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  96. bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +841 -0
  97. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  98. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +914 -0
  99. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  100. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +842 -0
  101. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  102. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +841 -0
  103. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  104. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +841 -0
  105. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  106. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +842 -0
  107. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  108. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +842 -0
  109. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  110. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +842 -0
  111. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  112. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +842 -0
  113. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  114. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +842 -0
  115. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  116. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +842 -0
  117. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  118. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +842 -0
  119. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  120. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +842 -0
  121. bootstack/assets/themes/__init__.py +0 -0
  122. bootstack/assets/themes/amber-dark.json +32 -0
  123. bootstack/assets/themes/amber-light.json +32 -0
  124. bootstack/assets/themes/aurora-dark.json +32 -0
  125. bootstack/assets/themes/aurora-light.json +32 -0
  126. bootstack/assets/themes/bootstrap-dark.json +32 -0
  127. bootstack/assets/themes/bootstrap-light.json +32 -0
  128. bootstack/assets/themes/classic-dark.json +32 -0
  129. bootstack/assets/themes/classic-light.json +32 -0
  130. bootstack/assets/themes/docs-dark.json +32 -0
  131. bootstack/assets/themes/docs-light.json +32 -0
  132. bootstack/assets/themes/forest-dark.json +32 -0
  133. bootstack/assets/themes/forest-light.json +32 -0
  134. bootstack/assets/themes/ocean-dark.json +32 -0
  135. bootstack/assets/themes/ocean-light.json +32 -0
  136. bootstack/assets/themes/rose-dark.json +32 -0
  137. bootstack/assets/themes/rose-light.json +32 -0
  138. bootstack/assets/widgets/__init__.py +0 -0
  139. bootstack/assets/widgets/badge-default.png +0 -0
  140. bootstack/assets/widgets/badge-pill.png +0 -0
  141. bootstack/assets/widgets/border.png +0 -0
  142. bootstack/assets/widgets/button-group-horizontal-after.png +0 -0
  143. bootstack/assets/widgets/button-group-horizontal-before.png +0 -0
  144. bootstack/assets/widgets/button-group-horizontal-center.png +0 -0
  145. bootstack/assets/widgets/button-group-vertical-after.png +0 -0
  146. bootstack/assets/widgets/button-group-vertical-before.png +0 -0
  147. bootstack/assets/widgets/button-group-vertical-center.png +0 -0
  148. bootstack/assets/widgets/button.png +0 -0
  149. bootstack/assets/widgets/checkbox-checked.png +0 -0
  150. bootstack/assets/widgets/checkbox-indeterminate.png +0 -0
  151. bootstack/assets/widgets/checkbox-unchecked.png +0 -0
  152. bootstack/assets/widgets/field.png +0 -0
  153. bootstack/assets/widgets/icon-button.png +0 -0
  154. bootstack/assets/widgets/input-inner.png +0 -0
  155. bootstack/assets/widgets/input-prefix.png +0 -0
  156. bootstack/assets/widgets/input-suffix.png +0 -0
  157. bootstack/assets/widgets/input.png +0 -0
  158. bootstack/assets/widgets/list-item-focus.png +0 -0
  159. bootstack/assets/widgets/list-item-separated.png +0 -0
  160. bootstack/assets/widgets/menu-item-separated.png +0 -0
  161. bootstack/assets/widgets/notebook-client-border.png +0 -0
  162. bootstack/assets/widgets/notebook-pill-active.png +0 -0
  163. bootstack/assets/widgets/notebook-pill-inactive.png +0 -0
  164. bootstack/assets/widgets/notebook-tab-active.png +0 -0
  165. bootstack/assets/widgets/notebook-tab-border.png +0 -0
  166. bootstack/assets/widgets/notebook-tab-normal.png +0 -0
  167. bootstack/assets/widgets/notebook-underline.png +0 -0
  168. bootstack/assets/widgets/progress-bar-horizontal-default.png +0 -0
  169. bootstack/assets/widgets/progress-bar-horizontal-striped.png +0 -0
  170. bootstack/assets/widgets/progress-bar-vertical-default.png +0 -0
  171. bootstack/assets/widgets/progress-bar-vertical-striped.png +0 -0
  172. bootstack/assets/widgets/progress-trough-horizontal.png +0 -0
  173. bootstack/assets/widgets/progress-trough-vertical.png +0 -0
  174. bootstack/assets/widgets/radio-selected.png +0 -0
  175. bootstack/assets/widgets/radio-unselected.png +0 -0
  176. bootstack/assets/widgets/scrollbar-horizontal-rounded.png +0 -0
  177. bootstack/assets/widgets/scrollbar-vertical-rounded.png +0 -0
  178. bootstack/assets/widgets/separator-horizontal.png +0 -0
  179. bootstack/assets/widgets/separator-vertical.png +0 -0
  180. bootstack/assets/widgets/slider-handle-focus.png +0 -0
  181. bootstack/assets/widgets/slider-handle.png +0 -0
  182. bootstack/assets/widgets/slider-track-horizontal.png +0 -0
  183. bootstack/assets/widgets/slider-track-vertical.png +0 -0
  184. bootstack/assets/widgets/switch-off.png +0 -0
  185. bootstack/assets/widgets/switch-on.png +0 -0
  186. bootstack/assets/widgets/tabs-bar-horizontal.png +0 -0
  187. bootstack/assets/widgets/tabs-bar-vertical.png +0 -0
  188. bootstack/assets/widgets/tabs-pill.png +0 -0
  189. bootstack/cli/__init__.py +124 -0
  190. bootstack/cli/__main__.py +6 -0
  191. bootstack/cli/add.py +439 -0
  192. bootstack/cli/build.py +115 -0
  193. bootstack/cli/config.py +287 -0
  194. bootstack/cli/demo.py +1267 -0
  195. bootstack/cli/doctor.py +195 -0
  196. bootstack/cli/list_cmd.py +71 -0
  197. bootstack/cli/promote.py +120 -0
  198. bootstack/cli/pyinstaller.py +246 -0
  199. bootstack/cli/run.py +99 -0
  200. bootstack/cli/start.py +105 -0
  201. bootstack/cli/templates/__init__.py +861 -0
  202. bootstack/constants.py +325 -0
  203. bootstack/core/__init__.py +34 -0
  204. bootstack/core/capabilities/__init__.py +45 -0
  205. bootstack/core/capabilities/after.py +103 -0
  206. bootstack/core/capabilities/bind.py +154 -0
  207. bootstack/core/capabilities/bindtags.py +112 -0
  208. bootstack/core/capabilities/busy.py +61 -0
  209. bootstack/core/capabilities/clipboard.py +88 -0
  210. bootstack/core/capabilities/focus.py +118 -0
  211. bootstack/core/capabilities/grab.py +65 -0
  212. bootstack/core/capabilities/grid.py +188 -0
  213. bootstack/core/capabilities/localization.py +231 -0
  214. bootstack/core/capabilities/pack.py +119 -0
  215. bootstack/core/capabilities/place.py +92 -0
  216. bootstack/core/capabilities/selection.py +136 -0
  217. bootstack/core/capabilities/signals.py +242 -0
  218. bootstack/core/capabilities/winfo.py +315 -0
  219. bootstack/core/colorutils.py +234 -0
  220. bootstack/core/exceptions.py +95 -0
  221. bootstack/core/images.py +283 -0
  222. bootstack/core/localization/README.md +90 -0
  223. bootstack/core/localization/__init__.py +13 -0
  224. bootstack/core/localization/intl_format.py +580 -0
  225. bootstack/core/localization/msgcat.py +425 -0
  226. bootstack/core/localization/specs.py +143 -0
  227. bootstack/core/mixins/__init__.py +1 -0
  228. bootstack/core/mixins/ttk_state.py +35 -0
  229. bootstack/core/mixins/widget.py +132 -0
  230. bootstack/core/publisher.py +147 -0
  231. bootstack/core/signals/README.md +112 -0
  232. bootstack/core/signals/__init__.py +8 -0
  233. bootstack/core/signals/integration.py +100 -0
  234. bootstack/core/signals/signal.py +317 -0
  235. bootstack/core/signals/types.py +4 -0
  236. bootstack/core/validation/__init__.py +5 -0
  237. bootstack/core/validation/types.py +13 -0
  238. bootstack/core/validation/validation_result.py +17 -0
  239. bootstack/core/validation/validation_rules.py +112 -0
  240. bootstack/core/variables.py +62 -0
  241. bootstack/datasource/README.md +607 -0
  242. bootstack/datasource/__init__.py +51 -0
  243. bootstack/datasource/base.py +474 -0
  244. bootstack/datasource/file_source.py +541 -0
  245. bootstack/datasource/memory_source.py +482 -0
  246. bootstack/datasource/sqlite_source.py +453 -0
  247. bootstack/datasource/types.py +259 -0
  248. bootstack/dialogs/__init__.py +56 -0
  249. bootstack/dialogs/colorchooser.py +674 -0
  250. bootstack/dialogs/colordropper.py +257 -0
  251. bootstack/dialogs/datedialog.py +404 -0
  252. bootstack/dialogs/dialog.py +514 -0
  253. bootstack/dialogs/filterdialog.py +358 -0
  254. bootstack/dialogs/fontdialog.py +339 -0
  255. bootstack/dialogs/formdialog.py +541 -0
  256. bootstack/dialogs/message.py +489 -0
  257. bootstack/dialogs/query.py +561 -0
  258. bootstack/py.typed +1 -0
  259. bootstack/runtime/__init__.py +3 -0
  260. bootstack/runtime/app.py +879 -0
  261. bootstack/runtime/base_window.py +786 -0
  262. bootstack/runtime/events.py +399 -0
  263. bootstack/runtime/menu.py +510 -0
  264. bootstack/runtime/shortcuts.py +423 -0
  265. bootstack/runtime/tk_patch.py +31 -0
  266. bootstack/runtime/toplevel.py +131 -0
  267. bootstack/runtime/utility.py +371 -0
  268. bootstack/runtime/visual_focus.py +228 -0
  269. bootstack/runtime/window_utilities.py +1043 -0
  270. bootstack/style/__init__.py +5498 -0
  271. bootstack/style/bootstyle.py +507 -0
  272. bootstack/style/bootstyle_builder_base.py +752 -0
  273. bootstack/style/bootstyle_builder_mixed.py +93 -0
  274. bootstack/style/bootstyle_builder_tk.py +109 -0
  275. bootstack/style/bootstyle_builder_ttk.py +354 -0
  276. bootstack/style/builders/__init__.py +51 -0
  277. bootstack/style/builders/badge.py +44 -0
  278. bootstack/style/builders/button.py +453 -0
  279. bootstack/style/builders/buttongroup.py +344 -0
  280. bootstack/style/builders/calendar.py +271 -0
  281. bootstack/style/builders/checkbutton.py +95 -0
  282. bootstack/style/builders/combobox.py +112 -0
  283. bootstack/style/builders/contextmenu.py +268 -0
  284. bootstack/style/builders/entry.py +83 -0
  285. bootstack/style/builders/expander.py +171 -0
  286. bootstack/style/builders/field.py +312 -0
  287. bootstack/style/builders/frame.py +27 -0
  288. bootstack/style/builders/label.py +28 -0
  289. bootstack/style/builders/labelframe.py +41 -0
  290. bootstack/style/builders/listview.py +267 -0
  291. bootstack/style/builders/menubar.py +74 -0
  292. bootstack/style/builders/menubutton.py +408 -0
  293. bootstack/style/builders/notebook.py +316 -0
  294. bootstack/style/builders/panedwindow.py +25 -0
  295. bootstack/style/builders/progressbar.py +71 -0
  296. bootstack/style/builders/radiobutton.py +68 -0
  297. bootstack/style/builders/scale.py +66 -0
  298. bootstack/style/builders/scrollbar.py +360 -0
  299. bootstack/style/builders/separator.py +45 -0
  300. bootstack/style/builders/sidenav.py +313 -0
  301. bootstack/style/builders/sizegrip.py +15 -0
  302. bootstack/style/builders/spinbox.py +119 -0
  303. bootstack/style/builders/switch.py +67 -0
  304. bootstack/style/builders/tabitem.py +205 -0
  305. bootstack/style/builders/toolbutton.py +260 -0
  306. bootstack/style/builders/tooltip.py +26 -0
  307. bootstack/style/builders/treeview.py +269 -0
  308. bootstack/style/builders/utils.py +404 -0
  309. bootstack/style/builders_tk/__init__.py +16 -0
  310. bootstack/style/builders_tk/defaults.py +229 -0
  311. bootstack/style/element.py +173 -0
  312. bootstack/style/style.py +499 -0
  313. bootstack/style/theme_provider.py +449 -0
  314. bootstack/style/tk_patch.py +5 -0
  315. bootstack/style/token_maps.py +42 -0
  316. bootstack/style/types.py +32 -0
  317. bootstack/style/typography.py +527 -0
  318. bootstack/style/utility.py +696 -0
  319. bootstack/themes/__init__.py +12 -0
  320. bootstack/themes/standard.py +415 -0
  321. bootstack/themes/user.py +45 -0
  322. bootstack/widgets/__init__.py +53 -0
  323. bootstack/widgets/composites/__init__.py +38 -0
  324. bootstack/widgets/composites/accordion.py +385 -0
  325. bootstack/widgets/composites/appshell.py +445 -0
  326. bootstack/widgets/composites/buttongroup.py +391 -0
  327. bootstack/widgets/composites/calendar.py +914 -0
  328. bootstack/widgets/composites/compositeframe.py +282 -0
  329. bootstack/widgets/composites/contextmenu.py +1754 -0
  330. bootstack/widgets/composites/dateentry.py +261 -0
  331. bootstack/widgets/composites/dropdownbutton.py +190 -0
  332. bootstack/widgets/composites/expander.py +508 -0
  333. bootstack/widgets/composites/field.py +448 -0
  334. bootstack/widgets/composites/floodgauge.py +434 -0
  335. bootstack/widgets/composites/form.py +983 -0
  336. bootstack/widgets/composites/labeledscale.py +209 -0
  337. bootstack/widgets/composites/list/__init__.py +15 -0
  338. bootstack/widgets/composites/list/listitem.py +733 -0
  339. bootstack/widgets/composites/list/listview.py +1507 -0
  340. bootstack/widgets/composites/menubar.py +303 -0
  341. bootstack/widgets/composites/meter.py +882 -0
  342. bootstack/widgets/composites/numericentry.py +183 -0
  343. bootstack/widgets/composites/pagestack.py +330 -0
  344. bootstack/widgets/composites/passwordentry.py +149 -0
  345. bootstack/widgets/composites/pathentry.py +223 -0
  346. bootstack/widgets/composites/radiogroup.py +466 -0
  347. bootstack/widgets/composites/scrolledtext.py +388 -0
  348. bootstack/widgets/composites/scrolledtext.pyi +186 -0
  349. bootstack/widgets/composites/scrollview.py +675 -0
  350. bootstack/widgets/composites/selectbox.py +544 -0
  351. bootstack/widgets/composites/sidenav/__init__.py +24 -0
  352. bootstack/widgets/composites/sidenav/group.py +485 -0
  353. bootstack/widgets/composites/sidenav/header.py +83 -0
  354. bootstack/widgets/composites/sidenav/item.py +413 -0
  355. bootstack/widgets/composites/sidenav/separator.py +51 -0
  356. bootstack/widgets/composites/sidenav/view.py +919 -0
  357. bootstack/widgets/composites/spinnerentry.py +232 -0
  358. bootstack/widgets/composites/tableview/__init__.py +5 -0
  359. bootstack/widgets/composites/tableview/tableview.py +2254 -0
  360. bootstack/widgets/composites/tableview/types.py +169 -0
  361. bootstack/widgets/composites/tabs/__init__.py +6 -0
  362. bootstack/widgets/composites/tabs/tabitem.py +372 -0
  363. bootstack/widgets/composites/tabs/tabs.py +478 -0
  364. bootstack/widgets/composites/tabs/tabview.py +352 -0
  365. bootstack/widgets/composites/textentry.py +90 -0
  366. bootstack/widgets/composites/timeentry.py +189 -0
  367. bootstack/widgets/composites/toast.py +364 -0
  368. bootstack/widgets/composites/togglegroup.py +382 -0
  369. bootstack/widgets/composites/toolbar.py +393 -0
  370. bootstack/widgets/composites/tooltip.py +404 -0
  371. bootstack/widgets/internal/__init__.py +0 -0
  372. bootstack/widgets/internal/wrapper_base.py +304 -0
  373. bootstack/widgets/mixins/__init__.py +25 -0
  374. bootstack/widgets/mixins/configure_mixin.py +186 -0
  375. bootstack/widgets/mixins/entry_mixin.py +70 -0
  376. bootstack/widgets/mixins/font_mixin.py +346 -0
  377. bootstack/widgets/mixins/icon_mixin.py +38 -0
  378. bootstack/widgets/mixins/localization_mixin.py +255 -0
  379. bootstack/widgets/mixins/signal_mixin.py +272 -0
  380. bootstack/widgets/mixins/validation_mixin.py +204 -0
  381. bootstack/widgets/parts/__init__.py +11 -0
  382. bootstack/widgets/parts/numberentry_part.py +345 -0
  383. bootstack/widgets/parts/spinnerentry_part.py +394 -0
  384. bootstack/widgets/parts/textentry_part.py +344 -0
  385. bootstack/widgets/primitives/__init__.py +55 -0
  386. bootstack/widgets/primitives/badge.py +44 -0
  387. bootstack/widgets/primitives/button.py +89 -0
  388. bootstack/widgets/primitives/card.py +66 -0
  389. bootstack/widgets/primitives/checkbutton.py +124 -0
  390. bootstack/widgets/primitives/checktoggle.py +53 -0
  391. bootstack/widgets/primitives/combobox.py +165 -0
  392. bootstack/widgets/primitives/entry.py +98 -0
  393. bootstack/widgets/primitives/frame.py +206 -0
  394. bootstack/widgets/primitives/gridframe.py +479 -0
  395. bootstack/widgets/primitives/label.py +95 -0
  396. bootstack/widgets/primitives/labelframe.py +63 -0
  397. bootstack/widgets/primitives/menubutton.py +118 -0
  398. bootstack/widgets/primitives/notebook.py +551 -0
  399. bootstack/widgets/primitives/optionmenu.py +248 -0
  400. bootstack/widgets/primitives/packframe.py +228 -0
  401. bootstack/widgets/primitives/panedwindow.py +58 -0
  402. bootstack/widgets/primitives/progressbar.py +95 -0
  403. bootstack/widgets/primitives/radiobutton.py +115 -0
  404. bootstack/widgets/primitives/radiotoggle.py +50 -0
  405. bootstack/widgets/primitives/scale.py +85 -0
  406. bootstack/widgets/primitives/scrollbar.py +56 -0
  407. bootstack/widgets/primitives/separator.py +56 -0
  408. bootstack/widgets/primitives/sizegrip.py +47 -0
  409. bootstack/widgets/primitives/spinbox.py +91 -0
  410. bootstack/widgets/primitives/switch.py +41 -0
  411. bootstack/widgets/primitives/treeview.py +77 -0
  412. bootstack/widgets/types.py +20 -0
  413. bootstack-0.1.0a1.dist-info/METADATA +196 -0
  414. bootstack-0.1.0a1.dist-info/RECORD +419 -0
  415. bootstack-0.1.0a1.dist-info/WHEEL +5 -0
  416. bootstack-0.1.0a1.dist-info/entry_points.txt +2 -0
  417. bootstack-0.1.0a1.dist-info/licenses/LICENSE +22 -0
  418. bootstack-0.1.0a1.dist-info/licenses/NOTICE +10 -0
  419. bootstack-0.1.0a1.dist-info/top_level.txt +1 -0
bootstack/cli/demo.py ADDED
@@ -0,0 +1,1267 @@
1
+ """
2
+ bootstack Widget Gallery
3
+
4
+ An AppShell-based showcase of bootstack widgets, organized by category
5
+ to mirror the documentation structure. Each sidebar item opens a page
6
+ demonstrating a widget group.
7
+ """
8
+
9
+ import bootstack as bs
10
+ from bootstack.constants import *
11
+ from bootstack.core.signals import Signal
12
+ from bootstack.widgets.composites.tabs.tabview import TabView
13
+
14
+
15
+ # =============================================================================
16
+ # Page builders — one per sidebar item
17
+ # =============================================================================
18
+
19
+
20
+ def _build_home_page(page):
21
+ """Welcome / overview page."""
22
+ bs.Label(
23
+ page, text="bootstack", font="heading-xl[bold]",
24
+ ).pack(anchor=W, padx=20, pady=(20, 4))
25
+
26
+ bs.Label(
27
+ page,
28
+ text="Modern UI framework for Python",
29
+ font="body",
30
+ accent="secondary",
31
+ ).pack(anchor=W, padx=20, pady=(0, 20))
32
+
33
+ content = bs.LabelFrame(page, text="About This Gallery", padding=20)
34
+ content.pack(fill=BOTH, expand=YES, padx=20, pady=(0, 20))
35
+
36
+ bs.Label(
37
+ content,
38
+ text=(
39
+ "Browse the sidebar to explore bootstack widgets by category.\n\n"
40
+ "Each page demonstrates a group of related widgets with\n"
41
+ "color variants, style options, and interactive examples.\n\n"
42
+ "Use the theme toggle in the toolbar or visit the Theme\n"
43
+ "page to try different themes."
44
+ ),
45
+ ).pack(expand=YES)
46
+
47
+
48
+ # -- Typography ---------------------------------------------------------------
49
+
50
+
51
+ def _build_typography_page(page):
52
+ """Font tokens and modifiers showcase."""
53
+ bs.Label(
54
+ page, text="Typography", font="heading-xl",
55
+ ).pack(anchor=W, padx=20, pady=(20, 10))
56
+
57
+ bs.Label(
58
+ page,
59
+ text="Semantic font tokens for consistent text styling.",
60
+ accent="secondary",
61
+ ).pack(anchor=W, padx=20, pady=(0, 15))
62
+
63
+ # Font tokens
64
+ group1 = bs.LabelFrame(page, text="Font Tokens", padding=15)
65
+ group1.pack(fill=X, padx=20, pady=(0, 10))
66
+
67
+ fonts = [
68
+ ("display-xl", "Display XL"),
69
+ ("display-lg", "Display LG"),
70
+ ("heading-xl", "Heading XL"),
71
+ ("heading-lg", "Heading LG"),
72
+ ("heading-md", "Heading MD"),
73
+ ("heading-sm", "Heading SM"),
74
+ ("body-xl", "Body XL"),
75
+ ("body-lg", "Body LG"),
76
+ ("body", "Body (default)"),
77
+ ("body-sm", "Body SM"),
78
+ ("label", "Label"),
79
+ ("caption", "Caption"),
80
+ ("code", "Code"),
81
+ ]
82
+ for i, (token, desc) in enumerate(fonts):
83
+ group1.columnconfigure(0, weight=0, minsize=200)
84
+ group1.columnconfigure(1, weight=1)
85
+ bs.Label(group1, text=desc, font=token).grid(row=i, column=0, sticky=W, pady=2)
86
+ bs.Label(group1, text=f"font=\"{token}\"", font="code", accent="secondary").grid(row=i, column=1, sticky=W, pady=2)
87
+
88
+ # Font modifiers
89
+ group2 = bs.LabelFrame(page, text="Font Modifiers", padding=15)
90
+ group2.pack(fill=X, padx=20, pady=(0, 10))
91
+
92
+ modifiers = [
93
+ ("body[bold]", "Bold"),
94
+ ("body[italic]", "Italic"),
95
+ ("body[bold][italic]", "Bold Italic"),
96
+ ("body[underline]", "Underline"),
97
+ ("heading-md[bold]", "Heading Bold"),
98
+ ]
99
+ group2.columnconfigure(0, weight=0, minsize=200)
100
+ group2.columnconfigure(1, weight=1)
101
+ for i, (token, desc) in enumerate(modifiers):
102
+ bs.Label(group2, text=desc, font=token).grid(row=i, column=0, sticky=W, pady=2)
103
+ bs.Label(group2, text=f"font=\"{token}\"", font="code", accent="secondary").grid(row=i, column=1, sticky=W, pady=2)
104
+
105
+
106
+ # -- Icons --------------------------------------------------------------------
107
+
108
+
109
+ def _build_icons_page(page):
110
+ """Bootstrap Icons showcase."""
111
+ bs.Label(
112
+ page, text="Icons", font="heading-xl",
113
+ ).pack(anchor=W, padx=20, pady=(20, 10))
114
+
115
+ bs.Label(
116
+ page,
117
+ text="Bootstrap Icons available via the icon parameter.",
118
+ accent="secondary",
119
+ ).pack(anchor=W, padx=20, pady=(0, 15))
120
+
121
+ # Icon gallery
122
+ group1 = bs.LabelFrame(page, text="Common Icons", padding=15)
123
+ group1.pack(fill=X, padx=20, pady=(0, 10))
124
+
125
+ icon_names = [
126
+ "house", "gear", "person", "search", "bell",
127
+ "envelope", "heart", "star", "trash", "pencil",
128
+ "folder", "file-earmark-text", "download", "upload", "check-circle",
129
+ "exclamation-triangle", "info-circle", "x-circle", "arrow-left", "arrow-right",
130
+ ]
131
+ icon_row = bs.Frame(group1)
132
+ icon_row.pack(fill=X, pady=(0, 8))
133
+ for i, name in enumerate(icon_names):
134
+ if i > 0 and i % 10 == 0:
135
+ icon_row = bs.Frame(group1)
136
+ icon_row.pack(fill=X, pady=(0, 8))
137
+ f = bs.Frame(icon_row)
138
+ f.pack(side=LEFT, padx=6)
139
+ bs.Label(f, icon=name, icon_only=True).pack()
140
+ bs.Label(f, text=name, font="caption").pack()
141
+
142
+ # Icon sizes
143
+ group2 = bs.LabelFrame(page, text="Icon Sizes", padding=15)
144
+ group2.pack(fill=X, padx=20, pady=(0, 10))
145
+
146
+ size_row = bs.Frame(group2)
147
+ size_row.pack(fill=X)
148
+ for size in (12, 16, 20, 24, 32, 48):
149
+ f = bs.Frame(size_row)
150
+ f.pack(side=LEFT, padx=10)
151
+ bs.Label(f, icon={"name": "star-fill", "size": size}, icon_only=True).pack()
152
+ bs.Label(f, text=f"{size}px", font="caption").pack()
153
+
154
+ # Icons in context
155
+ group3 = bs.LabelFrame(page, text="Icons in Context", padding=15)
156
+ group3.pack(fill=X, padx=20, pady=(0, 10))
157
+
158
+ ctx_row = bs.Frame(group3)
159
+ ctx_row.pack(fill=X, pady=(0, 8))
160
+ bs.Button(ctx_row, text="Save", icon="save").pack(side=LEFT, padx=(0, 8))
161
+ bs.Button(ctx_row, text="Delete", icon="trash", accent="danger").pack(side=LEFT, padx=(0, 8))
162
+ bs.Button(ctx_row, text="Settings", icon="gear", accent="secondary").pack(side=LEFT, padx=(0, 8))
163
+ bs.Button(ctx_row, icon="plus-lg", icon_only=True, accent="success").pack(side=LEFT, padx=(0, 8))
164
+ bs.Button(ctx_row, icon="x-lg", icon_only=True, accent="danger").pack(side=LEFT)
165
+
166
+ # Accent-colored icons
167
+ group4 = bs.LabelFrame(page, text="Accent Colors on Icons", padding=15)
168
+ group4.pack(fill=X, padx=20, pady=(0, 10))
169
+
170
+ color_row = bs.Frame(group4)
171
+ color_row.pack(fill=X)
172
+ for color in ("primary", "secondary", "success", "info", "warning", "danger"):
173
+ f = bs.Frame(color_row)
174
+ f.pack(side=LEFT, padx=8)
175
+ bs.Label(f, icon="heart-fill", icon_only=True, accent=color).pack()
176
+ bs.Label(f, text=color, font="caption").pack()
177
+
178
+
179
+ # -- Actions ------------------------------------------------------------------
180
+
181
+
182
+ def _build_buttons_page(page):
183
+ """Buttons, dropdown buttons, button groups."""
184
+ bs.Label(
185
+ page, text="Actions", font="heading-xl",
186
+ ).pack(anchor=W, padx=20, pady=(20, 10))
187
+
188
+ bs.Label(
189
+ page,
190
+ text="Buttons and button-like widgets for triggering actions.",
191
+ accent="secondary",
192
+ ).pack(anchor=W, padx=20, pady=(0, 15))
193
+
194
+ # Color variants
195
+ group = bs.LabelFrame(page, text="Button — Color Variants", padding=15)
196
+ group.pack(fill=X, padx=20, pady=(0, 10))
197
+
198
+ row = bs.Frame(group)
199
+ row.pack(fill=X)
200
+ for color in ("primary", "secondary", "success", "info", "warning", "danger"):
201
+ bs.Button(row, text=color.title(), accent=color).pack(
202
+ side=LEFT, padx=2, expand=YES, fill=X,
203
+ )
204
+
205
+ # Style variants
206
+ group2 = bs.LabelFrame(page, text="Button — Style Variants", padding=15)
207
+ group2.pack(fill=X, padx=20, pady=(0, 10))
208
+
209
+ row2 = bs.Frame(group2)
210
+ row2.pack(fill=X, pady=(0, 8))
211
+ for variant_name in ("solid", "outline", "link", "ghost"):
212
+ bs.Button(
213
+ row2, text=variant_name.title(), accent="primary", variant=variant_name,
214
+ ).pack(side=LEFT, padx=2, expand=YES, fill=X)
215
+
216
+ row3 = bs.Frame(group2)
217
+ row3.pack(fill=X)
218
+ bs.Button(row3, text="Disabled Solid", accent="primary", state=DISABLED).pack(
219
+ side=LEFT, padx=2, expand=YES, fill=X,
220
+ )
221
+ bs.Button(
222
+ row3, text="Disabled Outline", accent="secondary",
223
+ variant="outline", state=DISABLED,
224
+ ).pack(side=LEFT, padx=2, expand=YES, fill=X)
225
+
226
+ # DropdownButton
227
+ group3 = bs.LabelFrame(page, text="DropdownButton", padding=15)
228
+ group3.pack(fill=X, padx=20, pady=(0, 10))
229
+
230
+ dd = bs.DropdownButton(group3, text="File", accent="primary")
231
+ dd.pack(side=LEFT, padx=(0, 8))
232
+ dd.add_command(text="New")
233
+ dd.add_command(text="Open")
234
+ dd.add_separator()
235
+ dd.add_command(text="Exit")
236
+
237
+ dd2 = bs.DropdownButton(
238
+ group3, text="Edit", accent="secondary", variant="outline",
239
+ )
240
+ dd2.pack(side=LEFT, padx=(0, 8))
241
+ dd2.add_command(text="Cut")
242
+ dd2.add_command(text="Copy")
243
+ dd2.add_command(text="Paste")
244
+
245
+ # MenuButton
246
+ mb = bs.MenuButton(group3, text="Options", accent="info")
247
+ mb.pack(side=LEFT)
248
+ menu = bs.Menu(mb, tearoff=0)
249
+ menu.add_command(label="Settings")
250
+ menu.add_command(label="Preferences")
251
+ mb["menu"] = menu
252
+
253
+ # ButtonGroup
254
+ group4 = bs.LabelFrame(page, text="ButtonGroup", padding=15)
255
+ group4.pack(fill=X, padx=20, pady=(0, 10))
256
+
257
+ for accent, variant in [
258
+ ("primary", "solid"), ("secondary", "outline"), ("success", "ghost"),
259
+ ]:
260
+ bg = bs.ButtonGroup(group4, accent=accent, variant=variant)
261
+ bg.pack(side=LEFT, padx=(0, 12))
262
+ bg.add(text="Cut", icon="scissors")
263
+ bg.add(text="Copy", icon="copy")
264
+ bg.add(text="Paste", icon="clipboard")
265
+
266
+
267
+ # -- Text Inputs -------------------------------------------------------------
268
+
269
+
270
+ def _build_text_inputs_page(page):
271
+ """TextEntry, PasswordEntry, PathEntry, ScrolledText."""
272
+ bs.Label(
273
+ page, text="Text Inputs", font="heading-xl",
274
+ ).pack(anchor=W, padx=20, pady=(20, 10))
275
+
276
+ bs.Label(
277
+ page,
278
+ text="Specialized entry widgets for text, passwords, and file paths.",
279
+ accent="secondary",
280
+ ).pack(anchor=W, padx=20, pady=(0, 15))
281
+
282
+ # TextEntry
283
+ group = bs.LabelFrame(page, text="TextEntry", padding=15)
284
+ group.pack(fill=X, padx=20, pady=(0, 10))
285
+
286
+ bs.TextEntry(
287
+ group, label="Name", message="Enter your full name",
288
+ ).pack(fill=X, pady=(0, 8))
289
+
290
+ bs.TextEntry(
291
+ group, label="Email", message="example@email.com",
292
+ ).pack(fill=X, pady=(0, 8))
293
+
294
+ bs.TextEntry(
295
+ group, label="Disabled", value="Read only", state=DISABLED,
296
+ ).pack(fill=X)
297
+
298
+ # PasswordEntry
299
+ group2 = bs.LabelFrame(page, text="PasswordEntry", padding=15)
300
+ group2.pack(fill=X, padx=20, pady=(0, 10))
301
+
302
+ bs.PasswordEntry(
303
+ group2, label="Password", message="Click the eye to toggle visibility",
304
+ ).pack(fill=X)
305
+
306
+ # PathEntry
307
+ group3 = bs.LabelFrame(page, text="PathEntry", padding=15)
308
+ group3.pack(fill=X, padx=20, pady=(0, 10))
309
+
310
+ bs.PathEntry(
311
+ group3, label="File", dialog="openfilename",
312
+ message="Select a file to open",
313
+ ).pack(fill=X, pady=(0, 8))
314
+
315
+ bs.PathEntry(
316
+ group3, label="Folder", dialog="directory",
317
+ message="Select a directory",
318
+ ).pack(fill=X)
319
+
320
+ # ScrolledText
321
+ group4 = bs.LabelFrame(page, text="ScrolledText", padding=15)
322
+ group4.pack(fill=BOTH, expand=YES, padx=20, pady=(0, 10))
323
+
324
+ text = bs.ScrolledText(group4, height=5, autohide=True)
325
+ text.pack(fill=BOTH, expand=YES)
326
+ text.insert(END, "ScrolledText provides a multi-line text area with\n")
327
+ text.insert(END, "automatic scrollbars that hide when not needed.\n\n")
328
+ text.insert(END, "Try typing more text to see scrollbars appear!")
329
+
330
+
331
+ # -- Numeric & Date -----------------------------------------------------------
332
+
333
+
334
+ def _build_numeric_page(page):
335
+ """NumericEntry, SpinnerEntry, Scale, LabeledScale, DateEntry, TimeEntry."""
336
+ bs.Label(
337
+ page, text="Numeric & Date", font="heading-xl",
338
+ ).pack(anchor=W, padx=20, pady=(20, 10))
339
+
340
+ bs.Label(
341
+ page,
342
+ text="Numeric entries, sliders, and date/time pickers.",
343
+ accent="secondary",
344
+ ).pack(anchor=W, padx=20, pady=(0, 15))
345
+
346
+ # NumericEntry
347
+ group = bs.LabelFrame(page, text="NumericEntry", padding=15)
348
+ group.pack(fill=X, padx=20, pady=(0, 10))
349
+
350
+ bs.NumericEntry(
351
+ group, label="Quantity", value=42, minvalue=0, maxvalue=100,
352
+ ).pack(fill=X, pady=(0, 8))
353
+
354
+ bs.NumericEntry(
355
+ group, label="Price", value=19.99, increment=0.01,
356
+ value_format="currency",
357
+ ).pack(fill=X)
358
+
359
+ # SpinnerEntry
360
+ group2 = bs.LabelFrame(page, text="SpinnerEntry", padding=15)
361
+ group2.pack(fill=X, padx=20, pady=(0, 10))
362
+
363
+ bs.SpinnerEntry(
364
+ group2, label="Month", values=["Jan", "Feb", "Mar", "Apr", "May",
365
+ "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], value="Jan",
366
+ ).pack(fill=X)
367
+
368
+ # Scale + LabeledScale
369
+ group3 = bs.LabelFrame(page, text="Scale & LabeledScale", padding=15)
370
+ group3.pack(fill=X, padx=20, pady=(0, 10))
371
+
372
+ bs.Label(group3, text="Scale:").pack(anchor=W)
373
+ bs.Scale(group3, from_=0, to=100, value=50).pack(fill=X, pady=(0, 12))
374
+
375
+ bs.Label(group3, text="LabeledScale (with value display):").pack(anchor=W)
376
+ bs.LabeledScale(
377
+ group3, minvalue=0, maxvalue=100, value=65,
378
+ ).pack(fill=X, pady=(0, 4))
379
+
380
+ # DateEntry + TimeEntry
381
+ group4 = bs.LabelFrame(page, text="DateEntry & TimeEntry", padding=15)
382
+ group4.pack(fill=X, padx=20, pady=(0, 10))
383
+
384
+ row = bs.Frame(group4)
385
+ row.pack(fill=X, pady=(0, 8))
386
+ bs.DateEntry(row, label="Date").pack(side=LEFT, fill=X, expand=YES, padx=(0, 8))
387
+ bs.TimeEntry(row, label="Time").pack(side=LEFT, fill=X, expand=YES)
388
+
389
+
390
+ # -- Selection ----------------------------------------------------------------
391
+
392
+
393
+ def _build_selection_page(page):
394
+ """CheckButton, Switch, RadioButton, RadioGroup, ToggleGroup, OptionMenu, SelectBox."""
395
+ bs.Label(
396
+ page, text="Selection", font="heading-xl",
397
+ ).pack(anchor=W, padx=20, pady=(20, 10))
398
+
399
+ bs.Label(
400
+ page,
401
+ text="Checkboxes, switches, radio buttons, toggle groups, and option menus.",
402
+ accent="secondary",
403
+ ).pack(anchor=W, padx=20, pady=(0, 15))
404
+
405
+ # CheckButton + Switch
406
+ group = bs.LabelFrame(page, text="CheckButton & Switch", padding=15)
407
+ group.pack(fill=X, padx=20, pady=(0, 10))
408
+
409
+ row = bs.Frame(group)
410
+ row.pack(fill=X, pady=(0, 10))
411
+ cb1 = bs.CheckButton(row, text="Default", accent="primary")
412
+ cb1.pack(side=LEFT, padx=(0, 12))
413
+ cb1.invoke()
414
+ bs.CheckButton(row, text="Success", accent="success").pack(
415
+ side=LEFT, padx=(0, 12),
416
+ )
417
+ bs.CheckButton(row, text="Disabled", state=DISABLED).pack(side=LEFT)
418
+
419
+ row2 = bs.Frame(group)
420
+ row2.pack(fill=X)
421
+ s1 = bs.Switch(row2, text="Notifications", accent="primary")
422
+ s1.pack(side=LEFT, padx=(0, 12))
423
+ s1.invoke()
424
+ bs.Switch(row2, text="Dark Mode", accent="success").pack(
425
+ side=LEFT, padx=(0, 12),
426
+ )
427
+ bs.Switch(row2, text="Disabled", state=DISABLED).pack(side=LEFT)
428
+
429
+ # RadioButton + RadioGroup
430
+ group2 = bs.LabelFrame(page, text="RadioButton & RadioGroup", padding=15)
431
+ group2.pack(fill=X, padx=20, pady=(0, 10))
432
+
433
+ bs.Label(group2, text="Individual RadioButtons:").pack(anchor=W, pady=(0, 4))
434
+ row3 = bs.Frame(group2)
435
+ row3.pack(fill=X, pady=(0, 10))
436
+ radio_var = bs.StringVar(value="a")
437
+ for text, val in [("Alpha", "a"), ("Beta", "b"), ("Gamma", "c")]:
438
+ bs.RadioButton(
439
+ row3, text=text, value=val, variable=radio_var,
440
+ ).pack(side=LEFT, padx=(0, 12))
441
+
442
+ bs.Label(group2, text="RadioGroup (managed):").pack(anchor=W, pady=(0, 4))
443
+ rg = bs.RadioGroup(group2, value="opt1", accent="primary")
444
+ rg.pack(fill=X)
445
+ rg.add(text="Option 1", value="opt1")
446
+ rg.add(text="Option 2", value="opt2")
447
+ rg.add(text="Option 3", value="opt3")
448
+
449
+ # ToggleGroup
450
+ group3 = bs.LabelFrame(page, text="ToggleGroup", padding=15)
451
+ group3.pack(fill=X, padx=20, pady=(0, 10))
452
+
453
+ bs.Label(group3, text="Single select:").pack(anchor=W, pady=(0, 4))
454
+ tg = bs.ToggleGroup(
455
+ group3, mode="single", accent="primary", variant="outline", value="B",
456
+ )
457
+ tg.pack(anchor=W, pady=(0, 10))
458
+ tg.add(text="Bold", value="B")
459
+ tg.add(text="Italic", value="I")
460
+ tg.add(text="Underline", value="U")
461
+
462
+ bs.Label(group3, text="Multi select:").pack(anchor=W, pady=(0, 4))
463
+ tg2 = bs.ToggleGroup(
464
+ group3, mode="multi", accent="success", variant="outline",
465
+ )
466
+ tg2.pack(anchor=W)
467
+ tg2.add(text="Python", value="python")
468
+ tg2.add(text="JavaScript", value="javascript")
469
+ tg2.add(text="Rust", value="rust")
470
+
471
+ # OptionMenu + SelectBox
472
+ group4 = bs.LabelFrame(page, text="OptionMenu & SelectBox", padding=15)
473
+ group4.pack(fill=X, padx=20, pady=(0, 10))
474
+
475
+ row4 = bs.Frame(group4)
476
+ row4.pack(fill=X, pady=(0, 8))
477
+ bs.Label(row4, text="OptionMenu:", width=12).pack(side=LEFT)
478
+ bs.OptionMenu(
479
+ row4, value="Red",
480
+ options=["Red", "Green", "Blue", "Yellow"],
481
+ ).pack(side=LEFT)
482
+
483
+ bs.SelectBox(
484
+ group4, label="SelectBox:",
485
+ items=["Small", "Medium", "Large", "Extra Large"],
486
+ value="Medium",
487
+ ).pack(fill=X)
488
+
489
+
490
+ # -- Calendar -----------------------------------------------------------------
491
+
492
+
493
+ def _build_calendar_page(page):
494
+ """Calendar widget demonstration."""
495
+ bs.Label(
496
+ page, text="Calendar", font="heading-xl",
497
+ ).pack(anchor=W, padx=20, pady=(20, 10))
498
+
499
+ bs.Label(
500
+ page,
501
+ text="Interactive date picker with single and range selection modes.",
502
+ accent="secondary",
503
+ ).pack(anchor=W, padx=20, pady=(0, 15))
504
+
505
+ group = bs.LabelFrame(page, text="Single Selection", padding=15)
506
+ group.pack(fill=X, padx=20, pady=(0, 10))
507
+
508
+ bs.Calendar(group, accent="primary").pack()
509
+
510
+ group2 = bs.LabelFrame(page, text="Range Selection", padding=15)
511
+ group2.pack(fill=X, padx=20, pady=(0, 10))
512
+
513
+ bs.Calendar(
514
+ group2, selection_mode="range", accent="success",
515
+ ).pack()
516
+
517
+
518
+ # -- Forms --------------------------------------------------------------------
519
+
520
+
521
+ def _build_forms_page(page):
522
+ """Form widget with various editor types and grouping."""
523
+ bs.Label(
524
+ page, text="Forms", font="heading-xl",
525
+ ).pack(anchor=W, padx=20, pady=(20, 10))
526
+
527
+ bs.Label(
528
+ page,
529
+ text="Spec-driven form builder for consistent data-entry UIs.",
530
+ accent="secondary",
531
+ ).pack(anchor=W, padx=20, pady=(0, 15))
532
+
533
+ # Inferred form (from data)
534
+ bs.Label(page, text="Inferred Form", font="body[bold]").pack(anchor=W, padx=20, pady=(0, 4))
535
+
536
+ form1 = bs.Form(
537
+ page,
538
+ data={
539
+ "first_name": "Jane",
540
+ "last_name": "Doe",
541
+ "age": 34,
542
+ "email": "jane@example.com",
543
+ "salary": 120000.50,
544
+ "active": True,
545
+ },
546
+ col_count=2,
547
+ min_col_width=220,
548
+ )
549
+ form1.pack(fill=X, padx=20)
550
+
551
+ # Explicit form with groups
552
+ bs.Label(page, text="Explicit Form Layout", font="body[bold]").pack(anchor=W, padx=20, pady=(16, 4))
553
+
554
+ form2 = bs.Form(
555
+ page,
556
+ data={"username": "jdoe", "role": "Admin", "newsletter": True, "timezone": "UTC"},
557
+ items=[
558
+ {
559
+ "type": "group",
560
+ "label": "Profile",
561
+ "col_count": 2,
562
+ "items": [
563
+ {"key": "username", "label": "Username"},
564
+ {"key": "password", "label": "Password", "editor": "passwordentry"},
565
+ {"key": "role", "label": "Role", "editor": "selectbox", "items": ["Admin", "User", "Viewer"]},
566
+ ],
567
+ },
568
+ {
569
+ "type": "group",
570
+ "label": "Preferences",
571
+ "items": [
572
+ {"key": "newsletter", "label": "Newsletter", "editor": "switch"},
573
+ {"key": "timezone", "label": "Time Zone", "editor": "selectbox", "items": ["UTC", "US/Eastern", "US/Central", "US/Pacific"]},
574
+ ],
575
+ },
576
+ ],
577
+ )
578
+ form2.pack(fill=X, padx=20, pady=(0, 15))
579
+
580
+
581
+ # -- Data Display -------------------------------------------------------------
582
+
583
+
584
+ def _build_data_page(page):
585
+ """Label, Badge, TreeView, TableView."""
586
+ bs.Label(
587
+ page, text="Data Display", font="heading-xl",
588
+ ).pack(anchor=W, padx=20, pady=(20, 10))
589
+
590
+ bs.Label(
591
+ page,
592
+ text="Labels, badges, trees, and tables for presenting data.",
593
+ accent="secondary",
594
+ ).pack(anchor=W, padx=20, pady=(0, 15))
595
+
596
+ # Labels with accents
597
+ group = bs.LabelFrame(page, text="Labels", padding=15)
598
+ group.pack(fill=X, padx=20, pady=(0, 10))
599
+
600
+ row = bs.Frame(group)
601
+ row.pack(fill=X)
602
+ for color in ("primary", "secondary", "success", "info", "warning", "danger"):
603
+ bs.Label(
604
+ row, text=color.title(), accent=color, padding=(8, 4),
605
+ ).pack(side=LEFT, padx=2)
606
+
607
+ # Badges
608
+ group2 = bs.LabelFrame(page, text="Badges", padding=15)
609
+ group2.pack(fill=X, padx=20, pady=(0, 10))
610
+
611
+ row2 = bs.Frame(group2)
612
+ row2.pack(fill=X)
613
+ for color in ("primary", "success", "warning", "danger", "info"):
614
+ bs.Badge(
615
+ row2, text=color.title(), accent=color,
616
+ ).pack(side=LEFT, padx=4)
617
+
618
+ row3 = bs.Frame(group2)
619
+ row3.pack(fill=X, pady=(8, 0))
620
+ bs.Badge(row3, text="Pill", accent="primary", variant="pill").pack(
621
+ side=LEFT, padx=4,
622
+ )
623
+ bs.Badge(row3, text="99+", accent="danger", variant="pill").pack(
624
+ side=LEFT, padx=4,
625
+ )
626
+ bs.Badge(row3, text="New", accent="success").pack(side=LEFT, padx=4)
627
+
628
+ # TreeView
629
+ group3 = bs.LabelFrame(page, text="TreeView", padding=15)
630
+ group3.pack(fill=BOTH, expand=YES, padx=20, pady=(0, 10))
631
+
632
+ columns = ("name", "status", "progress")
633
+ tree = bs.TreeView(group3, columns=columns, show="headings", height=5)
634
+
635
+ tree.heading("name", text="Task Name")
636
+ tree.heading("status", text="Status")
637
+ tree.heading("progress", text="Progress")
638
+
639
+ tree.column("name", width=200)
640
+ tree.column("status", width=100, anchor=CENTER)
641
+ tree.column("progress", width=100, anchor=CENTER)
642
+
643
+ for item in [
644
+ ("Database Migration", "Complete", "100%"),
645
+ ("API Integration", "In Progress", "65%"),
646
+ ("UI Redesign", "In Progress", "40%"),
647
+ ("Testing Suite", "Pending", "0%"),
648
+ ("Documentation", "In Progress", "80%"),
649
+ ]:
650
+ tree.insert("", END, values=item)
651
+
652
+ tree.pack(fill=BOTH, expand=YES)
653
+ tree.selection_set(tree.get_children()[0])
654
+
655
+ # TableView
656
+ group4 = bs.LabelFrame(page, text="TableView", padding=15, height=300)
657
+ group4.pack(fill=X, padx=20, pady=(0, 10))
658
+ group4.pack_propagate(False)
659
+
660
+ tv = bs.TableView(
661
+ group4,
662
+ columns=[
663
+ {"text": "Name", "stretch": True},
664
+ {"text": "Department", "width": 120},
665
+ {"text": "Salary", "width": 100, "anchor": "e"},
666
+ ],
667
+ rows=[
668
+ ("Alice Johnson", "Engineering", "$120,000"),
669
+ ("Bob Smith", "Marketing", "$85,000"),
670
+ ("Carol White", "Engineering", "$115,000"),
671
+ ("David Brown", "Design", "$95,000"),
672
+ ("Eve Davis", "Marketing", "$88,000"),
673
+ ],
674
+ )
675
+ tv.pack(fill=BOTH, expand=YES)
676
+
677
+
678
+ # -- Progress & Meters --------------------------------------------------------
679
+
680
+
681
+ def _build_progress_page(page):
682
+ """Progressbar, Meter, FloodGauge, Scale (interactive)."""
683
+ bs.Label(
684
+ page, text="Progress & Meters", font="heading-xl",
685
+ ).pack(anchor=W, padx=20, pady=(20, 10))
686
+
687
+ bs.Label(
688
+ page,
689
+ text="Progress bars, meters, and gauges for showing values and status.",
690
+ accent="secondary",
691
+ ).pack(anchor=W, padx=20, pady=(0, 15))
692
+
693
+ slider_value = Signal[float](65.0)
694
+
695
+ # Scale driving progress bars
696
+ group = bs.LabelFrame(
697
+ page, text="Scale (drag to control progress bars)", padding=15,
698
+ )
699
+ group.pack(fill=X, padx=20, pady=(0, 10))
700
+
701
+ bs.Scale(group, from_=0, to=100, signal=slider_value).pack(fill=X)
702
+
703
+ # Progressbar
704
+ group2 = bs.LabelFrame(page, text="Progressbar", padding=15)
705
+ group2.pack(fill=X, padx=20, pady=(0, 10))
706
+
707
+ bs.Progressbar(group2, signal=slider_value, maximum=100).pack(
708
+ fill=X, pady=(0, 8),
709
+ )
710
+ bs.Progressbar(
711
+ group2, value=75, maximum=100, accent="success", variant="striped",
712
+ ).pack(fill=X, pady=(0, 8))
713
+ bs.Progressbar(
714
+ group2, value=45, maximum=100, accent="danger",
715
+ ).pack(fill=X, pady=(0, 8))
716
+ bs.Progressbar(
717
+ group2, value=30, maximum=100, accent="warning", variant="thin",
718
+ ).pack(fill=X)
719
+
720
+ # Meters
721
+ group3 = bs.LabelFrame(page, text="Meter", padding=15)
722
+ group3.pack(fill=X, padx=20, pady=(0, 10))
723
+
724
+ meter_row = bs.Frame(group3)
725
+ meter_row.pack()
726
+
727
+ for amount, label, color in [
728
+ (45, "CPU Usage", "info"),
729
+ (78, "Memory", "warning"),
730
+ (92, "Disk", "danger"),
731
+ ]:
732
+ bs.Meter(
733
+ meter_row,
734
+ metersize=120,
735
+ amountused=amount,
736
+ amounttotal=100,
737
+ subtext=label,
738
+ accent=color,
739
+ interactive=True,
740
+ ).pack(side=LEFT, padx=10)
741
+
742
+ # FloodGauge
743
+ # Note: FloodGauge has a known issue with theme changes, so we
744
+ # guard against errors during the demo.
745
+ group4 = bs.LabelFrame(page, text="FloodGauge", padding=15)
746
+ group4.pack(fill=X, padx=20, pady=(0, 10))
747
+
748
+ fg_row = bs.Frame(group4)
749
+ fg_row.pack(fill=X)
750
+
751
+ for val, color, mask in [
752
+ (65, "primary", "{}%"),
753
+ (82, "success", "{}% Done"),
754
+ (35, "danger", "{}% Used"),
755
+ ]:
756
+ bs.FloodGauge(
757
+ fg_row, value=val, maximum=100, accent=color,
758
+ mask=mask, length=150, thickness=40,
759
+ ).pack(side=LEFT, padx=4, expand=YES)
760
+
761
+
762
+ # -- Layout -------------------------------------------------------------------
763
+
764
+
765
+ def _build_layout_page(page):
766
+ """Card, LabelFrame, Expander, Accordion, PanedWindow, Separator."""
767
+ bs.Label(
768
+ page, text="Layout", font="heading-xl",
769
+ ).pack(anchor=W, padx=20, pady=(20, 10))
770
+
771
+ bs.Label(
772
+ page,
773
+ text="Containers, expandable panels, and split panes for organizing content.",
774
+ accent="secondary",
775
+ ).pack(anchor=W, padx=20, pady=(0, 15))
776
+
777
+ # Card
778
+ group = bs.LabelFrame(page, text="Card", padding=15)
779
+ group.pack(fill=X, padx=20, pady=(0, 10))
780
+
781
+ card_row = bs.Frame(group)
782
+ card_row.pack(fill=X)
783
+
784
+ for title, body, color in [
785
+ ("Users", "1,234 active", "primary"),
786
+ ("Revenue", "$45,678", "success"),
787
+ ("Errors", "12 today", "danger"),
788
+ ]:
789
+ card = bs.Card(card_row, padding=16)
790
+ card.pack(side=LEFT, padx=4, expand=YES, fill=X)
791
+ bs.Label(card, text=title, accent=color, font="body[bold]").pack(anchor=W)
792
+ bs.Label(card, text=body, font="heading-lg").pack(anchor=W)
793
+
794
+ # Expander
795
+ group2 = bs.LabelFrame(page, text="Expander", padding=15)
796
+ group2.pack(fill=X, padx=20, pady=(0, 10))
797
+
798
+ exp = bs.Expander(group2, title="Click to expand", expanded=False)
799
+ exp.pack(fill=X, pady=(0, 8))
800
+ bs.Label(
801
+ exp.content, text="This content is revealed when the expander is opened.",
802
+ padding=10,
803
+ ).pack(fill=X)
804
+
805
+ exp2 = bs.Expander(group2, title="Already expanded", expanded=True)
806
+ exp2.pack(fill=X)
807
+ bs.Label(
808
+ exp2.content, text="Expanders can start open or closed.",
809
+ padding=10,
810
+ ).pack(fill=X)
811
+
812
+ # Accordion
813
+ group3 = bs.LabelFrame(page, text="Accordion", padding=15)
814
+ group3.pack(fill=X, padx=20, pady=(0, 10))
815
+
816
+ acc = bs.Accordion(group3)
817
+ acc.pack(fill=X)
818
+
819
+ sec1 = acc.add(title="Section 1")
820
+ bs.Label(sec1.content, text="Content for section one.", padding=10).pack(fill=X)
821
+
822
+ sec2 = acc.add(title="Section 2")
823
+ bs.Label(sec2.content, text="Content for section two.", padding=10).pack(fill=X)
824
+
825
+ sec3 = acc.add(title="Section 3")
826
+ bs.Label(sec3.content, text="Content for section three.", padding=10).pack(fill=X)
827
+
828
+ # PanedWindow
829
+ group4 = bs.LabelFrame(page, text="PanedWindow", padding=15)
830
+ group4.pack(fill=BOTH, expand=YES, padx=20, pady=(0, 10))
831
+
832
+ pw = bs.PanedWindow(group4, orient=HORIZONTAL)
833
+ pw.pack(fill=BOTH, expand=YES)
834
+
835
+ left = bs.LabelFrame(pw, text="Left Pane", padding=10)
836
+ bs.Label(left, text="Drag the\nsash to resize", justify=CENTER).pack(expand=YES)
837
+ pw.add(left, weight=1)
838
+
839
+ right = bs.LabelFrame(pw, text="Right Pane", padding=10)
840
+ bs.Label(right, text="Both panes\nare resizable", justify=CENTER).pack(expand=YES)
841
+ pw.add(right, weight=1)
842
+
843
+ # Separator
844
+ group5 = bs.LabelFrame(page, text="Separator", padding=15)
845
+ group5.pack(fill=X, padx=20, pady=(0, 10))
846
+
847
+ bs.Separator(group5).pack(fill=X, pady=(0, 8))
848
+ bs.Separator(group5, accent="primary").pack(fill=X, pady=(0, 8))
849
+ bs.Separator(group5, accent="success").pack(fill=X, pady=(0, 8))
850
+ bs.Separator(group5, accent="danger").pack(fill=X)
851
+
852
+
853
+ # -- Navigation ---------------------------------------------------------------
854
+
855
+
856
+ def _build_navigation_page(page):
857
+ """TabView and Notebook demonstrations."""
858
+ bs.Label(
859
+ page, text="Navigation", font="heading-xl",
860
+ ).pack(anchor=W, padx=20, pady=(20, 10))
861
+
862
+ bs.Label(
863
+ page,
864
+ text="Tab-based navigation widgets for organizing content into views.",
865
+ accent="secondary",
866
+ ).pack(anchor=W, padx=20, pady=(0, 15))
867
+
868
+ # TabView
869
+ group = bs.LabelFrame(page, text="TabView (bar variant)", padding=15)
870
+ group.pack(fill=BOTH, expand=YES, padx=20, pady=(0, 10))
871
+
872
+ tv = TabView(group, variant="bar")
873
+ tv.pack(fill=BOTH, expand=YES)
874
+
875
+ tab1 = tv.add("dashboard", text="Dashboard")
876
+ bs.Label(tab1, text="Dashboard content goes here.", padding=20).pack(expand=YES)
877
+
878
+ tab2 = tv.add("analytics", text="Analytics")
879
+ bs.Label(tab2, text="Analytics content goes here.", padding=20).pack(expand=YES)
880
+
881
+ tab3 = tv.add("settings", text="Settings")
882
+ bs.Label(tab3, text="Settings content goes here.", padding=20).pack(expand=YES)
883
+
884
+ # TabView with accent
885
+ group2 = bs.LabelFrame(page, text="TabView (with accent)", padding=15)
886
+ group2.pack(fill=BOTH, expand=YES, padx=20, pady=(0, 10))
887
+
888
+ tv2 = TabView(group2, variant="bar", accent="success")
889
+ tv2.pack(fill=BOTH, expand=YES)
890
+
891
+ p1 = tv2.add("all", text="All")
892
+ bs.Label(p1, text="Showing all items.", padding=20).pack(expand=YES)
893
+
894
+ p2 = tv2.add("active", text="Active")
895
+ bs.Label(p2, text="Showing active items.", padding=20).pack(expand=YES)
896
+
897
+ p3 = tv2.add("archived", text="Archived")
898
+ bs.Label(p3, text="Showing archived items.", padding=20).pack(expand=YES)
899
+
900
+ # Notebook
901
+ group3 = bs.LabelFrame(page, text="Notebook (classic tabs)", padding=15)
902
+ group3.pack(fill=BOTH, expand=YES, padx=20, pady=(0, 10))
903
+
904
+ nb = bs.Notebook(group3)
905
+ nb.pack(fill=BOTH, expand=YES)
906
+
907
+ for label in ("General", "Advanced", "About"):
908
+ f = bs.Frame(nb, padding=15)
909
+ nb.add(f, text=label)
910
+ bs.Label(f, text=f"{label} tab content.").pack(expand=YES)
911
+
912
+
913
+ # -- Overlays ----------------------------------------------------------------
914
+
915
+
916
+ def _build_overlays_page(page):
917
+ """Toast and ToolTip demonstrations."""
918
+ bs.Label(
919
+ page, text="Overlays", font="heading-xl",
920
+ ).pack(anchor=W, padx=20, pady=(20, 10))
921
+
922
+ bs.Label(
923
+ page,
924
+ text="Toasts, tooltips, and other overlay widgets.",
925
+ accent="secondary",
926
+ ).pack(anchor=W, padx=20, pady=(0, 15))
927
+
928
+ # ToolTip
929
+ group = bs.LabelFrame(page, text="ToolTip", padding=15)
930
+ group.pack(fill=X, padx=20, pady=(0, 10))
931
+
932
+ bs.Label(
933
+ group, text="Hover over the buttons below to see tooltips:",
934
+ ).pack(anchor=W, pady=(0, 8))
935
+
936
+ row = bs.Frame(group)
937
+ row.pack(fill=X)
938
+
939
+ btn1 = bs.Button(row, text="Default Tooltip", accent="primary")
940
+ btn1.pack(side=LEFT, padx=(0, 8))
941
+ bs.ToolTip(btn1, text="This is a basic tooltip.")
942
+
943
+ btn2 = bs.Button(row, text="Info Tooltip", accent="info")
944
+ btn2.pack(side=LEFT, padx=(0, 8))
945
+ bs.ToolTip(btn2, text="Tooltips can have accent colors.", accent="info")
946
+
947
+ btn3 = bs.Button(row, text="Long Tooltip", accent="secondary")
948
+ btn3.pack(side=LEFT)
949
+ bs.ToolTip(
950
+ btn3,
951
+ text="This is a longer tooltip that wraps text. It shows how "
952
+ "tooltips handle multi-line content with wraplength.",
953
+ wraplength=200,
954
+ )
955
+
956
+ # Toast
957
+ group2 = bs.LabelFrame(page, text="Toast", padding=15)
958
+ group2.pack(fill=X, padx=20, pady=(0, 10))
959
+
960
+ bs.Label(
961
+ group2, text="Click buttons to show toast notifications:",
962
+ ).pack(anchor=W, pady=(0, 8))
963
+
964
+ row2 = bs.Frame(group2)
965
+ row2.pack(fill=X)
966
+
967
+ def show_toast(title, message, accent):
968
+ bs.Toast(
969
+ title=title,
970
+ message=message,
971
+ accent=accent,
972
+ duration=3000,
973
+ ).show()
974
+
975
+ bs.Button(
976
+ row2, text="Success Toast", accent="success",
977
+ command=lambda: show_toast("Success", "Operation completed.", "success"),
978
+ ).pack(side=LEFT, padx=(0, 8))
979
+
980
+ bs.Button(
981
+ row2, text="Warning Toast", accent="warning",
982
+ command=lambda: show_toast("Warning", "Check your settings.", "warning"),
983
+ ).pack(side=LEFT, padx=(0, 8))
984
+
985
+ bs.Button(
986
+ row2, text="Error Toast", accent="danger",
987
+ command=lambda: show_toast("Error", "Something went wrong.", "danger"),
988
+ ).pack(side=LEFT)
989
+
990
+
991
+ # -- Dialogs ------------------------------------------------------------------
992
+
993
+
994
+ def _build_dialogs_page(page):
995
+ """Dialog demonstrations — each button launches a dialog."""
996
+ bs.Label(
997
+ page, text="Dialogs", font="heading-xl",
998
+ ).pack(anchor=W, padx=20, pady=(20, 10))
999
+
1000
+ bs.Label(
1001
+ page,
1002
+ text="Click buttons to launch various dialog types.",
1003
+ accent="secondary",
1004
+ ).pack(anchor=W, padx=20, pady=(0, 15))
1005
+
1006
+ # MessageBox / MessageDialog
1007
+ group1 = bs.LabelFrame(page, text="MessageBox & MessageDialog", padding=15)
1008
+ group1.pack(fill=X, padx=20, pady=(0, 10))
1009
+
1010
+ row1 = bs.Frame(group1)
1011
+ row1.pack(fill=X)
1012
+
1013
+ def show_message_ok():
1014
+ bs.MessageBox.ok("This is an informational message.", title="Info")
1015
+
1016
+ def show_message_yesno():
1017
+ result = bs.MessageBox.yesno("Do you want to continue?", title="Confirm")
1018
+ bs.Toast(title="Result", message=f"You chose: {result}", duration=2000).show()
1019
+
1020
+ def show_message_okcancel():
1021
+ result = bs.MessageBox.okcancel("Save changes before closing?", title="Save")
1022
+ bs.Toast(title="Result", message=f"You chose: {result}", duration=2000).show()
1023
+
1024
+ bs.Button(row1, text="Info (OK)", command=show_message_ok).pack(side=LEFT, padx=(0, 8))
1025
+ bs.Button(row1, text="Yes / No", command=show_message_yesno).pack(side=LEFT, padx=(0, 8))
1026
+ bs.Button(row1, text="OK / Cancel", command=show_message_okcancel).pack(side=LEFT)
1027
+
1028
+ # QueryBox
1029
+ group2 = bs.LabelFrame(page, text="QueryBox", padding=15)
1030
+ group2.pack(fill=X, padx=20, pady=(0, 10))
1031
+
1032
+ row2 = bs.Frame(group2)
1033
+ row2.pack(fill=X)
1034
+
1035
+ def show_query_string():
1036
+ result = bs.QueryBox.get_string("Enter your name:", title="String Input")
1037
+ if result is not None:
1038
+ bs.Toast(title="Input", message=f"You entered: {result}", duration=2000).show()
1039
+
1040
+ def show_query_integer():
1041
+ result = bs.QueryBox.get_integer("Enter a number:", title="Integer Input", minvalue=0, maxvalue=100)
1042
+ if result is not None:
1043
+ bs.Toast(title="Input", message=f"You entered: {result}", duration=2000).show()
1044
+
1045
+ bs.Button(row2, text="Get String", command=show_query_string).pack(side=LEFT, padx=(0, 8))
1046
+ bs.Button(row2, text="Get Integer", command=show_query_integer).pack(side=LEFT)
1047
+
1048
+ # ColorChooser
1049
+ group3 = bs.LabelFrame(page, text="ColorChooser", padding=15)
1050
+ group3.pack(fill=X, padx=20, pady=(0, 10))
1051
+
1052
+ color_swatch = bs.Label(group3, text=" Selected Color ", padding=8, surface="card")
1053
+ color_swatch.pack(side=LEFT, padx=(0, 12))
1054
+
1055
+ def show_color_chooser():
1056
+ result = bs.ColorChooserDialog().show()
1057
+ if result:
1058
+ bs.Toast(title="Color", message=f"Selected: {result}", duration=2000).show()
1059
+
1060
+ bs.Button(group3, text="Choose Color", command=show_color_chooser).pack(side=LEFT)
1061
+
1062
+ # FontDialog
1063
+ group4 = bs.LabelFrame(page, text="FontDialog", padding=15)
1064
+ group4.pack(fill=X, padx=20, pady=(0, 10))
1065
+
1066
+ def show_font_dialog():
1067
+ result = bs.FontDialog().show()
1068
+ if result:
1069
+ bs.Toast(title="Font", message=f"Selected: {result}", duration=2000).show()
1070
+
1071
+ bs.Button(group4, text="Choose Font", command=show_font_dialog).pack(side=LEFT)
1072
+
1073
+ # DateDialog
1074
+ group5 = bs.LabelFrame(page, text="DateDialog", padding=15)
1075
+ group5.pack(fill=X, padx=20, pady=(0, 10))
1076
+
1077
+ def show_date_dialog():
1078
+ result = bs.DateDialog().show()
1079
+ if result:
1080
+ bs.Toast(title="Date", message=f"Selected: {result}", duration=2000).show()
1081
+
1082
+ bs.Button(group5, text="Pick Date", command=show_date_dialog).pack(side=LEFT)
1083
+
1084
+
1085
+ # -- Themes -------------------------------------------------------------------
1086
+
1087
+
1088
+ def _build_theme_page(page):
1089
+ """Theme settings page."""
1090
+ bs.Label(
1091
+ page, text="Themes", font="heading-xl",
1092
+ ).pack(anchor=W, padx=20, pady=(20, 10))
1093
+
1094
+ bs.Label(
1095
+ page,
1096
+ text="Switch themes to see all widgets update in real time.",
1097
+ accent="secondary",
1098
+ ).pack(anchor=W, padx=20, pady=(0, 15))
1099
+
1100
+ group = bs.LabelFrame(page, text="Theme Selector", padding=15)
1101
+ group.pack(fill=X, padx=20, pady=(0, 10))
1102
+
1103
+ style = bs.get_style()
1104
+ theme_names = sorted(
1105
+ s["name"] for s in style.theme_provider.list_themes()
1106
+ )
1107
+
1108
+ row = bs.Frame(group)
1109
+ row.pack(fill=X, pady=(0, 10))
1110
+ bs.Label(row, text="Theme:", width=12).pack(side=LEFT)
1111
+
1112
+ combo = bs.Combobox(row, values=theme_names, width=20, state="readonly")
1113
+ combo.pack(side=LEFT)
1114
+ combo.set(style.current_theme)
1115
+
1116
+ def on_change(event):
1117
+ style.theme_use(combo.get())
1118
+ combo.selection_clear()
1119
+
1120
+ combo.bind("<<ComboboxSelected>>", on_change)
1121
+
1122
+ # Quick toggle
1123
+ row2 = bs.Frame(group)
1124
+ row2.pack(fill=X)
1125
+ bs.Label(row2, text="Quick:", width=12).pack(side=LEFT)
1126
+ bs.Button(
1127
+ row2, text="Toggle Light / Dark", command=bs.toggle_theme,
1128
+ ).pack(side=LEFT)
1129
+
1130
+ # Semantic accent colors
1131
+ group2 = bs.LabelFrame(page, text="Semantic Colors", padding=15)
1132
+ group2.pack(fill=X, padx=20, pady=(0, 10))
1133
+
1134
+ accent_row = bs.Frame(group2)
1135
+ accent_row.pack(fill=X, pady=(0, 10))
1136
+ for color in ("primary", "secondary", "success", "info", "warning", "danger"):
1137
+ bs.Button(accent_row, text=color.title(), accent=color).pack(side=LEFT, padx=4, expand=YES, fill=X)
1138
+
1139
+ extra_row = bs.Frame(group2)
1140
+ extra_row.pack(fill=X)
1141
+ for color in ("light", "dark"):
1142
+ bs.Button(extra_row, text=color.title(), accent=color).pack(side=LEFT, padx=4, expand=YES, fill=X)
1143
+
1144
+ # Surface colors
1145
+ group3 = bs.LabelFrame(page, text="Surfaces", padding=15)
1146
+ group3.pack(fill=X, padx=20, pady=(0, 10))
1147
+
1148
+ surface_row = bs.Frame(group3)
1149
+ surface_row.pack(fill=X)
1150
+ for surface in ("chrome", "content", "card", "overlay", "input"):
1151
+ f = bs.Frame(surface_row, surface=surface, padding=12)
1152
+ f.pack(side=LEFT, padx=4, expand=YES, fill=X)
1153
+ bs.Label(f, text=surface.title(), surface=surface).pack()
1154
+
1155
+ # Stroke / border colors
1156
+ group4 = bs.LabelFrame(page, text="Borders & Strokes", padding=15)
1157
+ group4.pack(fill=X, padx=20, pady=(0, 10))
1158
+
1159
+ bs.Label(group4, text="stroke").pack(anchor=W)
1160
+ bs.Separator(group4).pack(fill=X, pady=(2, 8))
1161
+ bs.Label(group4, text="stroke_subtle").pack(anchor=W)
1162
+ bs.Separator(group4, accent="stroke_subtle").pack(fill=X, pady=(2, 0))
1163
+
1164
+
1165
+ # =============================================================================
1166
+ # Gallery app
1167
+ # =============================================================================
1168
+
1169
+
1170
+ def run_demo():
1171
+ """Run the bootstack widget gallery as an AppShell application."""
1172
+ shell = bs.AppShell(
1173
+ title="bootstack Widget Gallery",
1174
+ theme="bootstrap-light",
1175
+ size=(1100, 750),
1176
+ )
1177
+
1178
+ # Toolbar: theme toggle
1179
+ shell.toolbar.add_button(icon="sun", command=bs.toggle_theme)
1180
+
1181
+ # Page definitions: (key, text, icon, builder, scrollable, extra_kwargs)
1182
+ pages = [
1183
+ # Home
1184
+ ("home", "Home", "house", _build_home_page, False, {}),
1185
+ # Actions
1186
+ ("buttons", "Buttons", "hand-index-thumb", _build_buttons_page, True, {}),
1187
+ # Inputs
1188
+ ("text-inputs", "Text Inputs", "input-cursor-text", _build_text_inputs_page, True, {}),
1189
+ ("numeric", "Numeric Date", "123", _build_numeric_page, True, {}),
1190
+ ("forms", "Forms", "journal-text", _build_forms_page, True, {}),
1191
+ # Selection
1192
+ ("selection", "Selection", "ui-checks", _build_selection_page, True, {}),
1193
+ ("calendar", "Calendar", "calendar3", _build_calendar_page, True, {}),
1194
+ # Data Display
1195
+ ("data", "Data Tables", "table", _build_data_page, True, {}),
1196
+ ("progress", "Progress", "speedometer2", _build_progress_page, True, {}),
1197
+ # Layout
1198
+ ("layout", "Containers", "layout-wtf", _build_layout_page, True, {}),
1199
+ # Navigation
1200
+ ("navigation", "Tabs Views", "window-stack", _build_navigation_page, True, {}),
1201
+ # Overlays
1202
+ ("overlays", "Overlays", "layers", _build_overlays_page, True, {}),
1203
+ # Dialogs
1204
+ ("dialogs", "Dialogs", "chat-square-text", _build_dialogs_page, True, {}),
1205
+ # Design System
1206
+ ("themes", "Themes", "palette", _build_theme_page, True, {}),
1207
+ ("typography", "Typography", "fonts", _build_typography_page, True, {}),
1208
+ ("icons", "Icons", "grid-3x3-gap", _build_icons_page, True, {}),
1209
+ ]
1210
+
1211
+ # Sidebar structure (headers and separators between groups)
1212
+ sidebar_structure = {
1213
+ "buttons": ("separator", "Actions"),
1214
+ "text-inputs": ("separator", "Inputs"),
1215
+ "selection": ("separator", "Selection"),
1216
+ "data": ("separator", "Data Display"),
1217
+ "layout": ("separator", "Layout"),
1218
+ "themes": ("separator", "Design System"),
1219
+ }
1220
+
1221
+ # Register all pages (lightweight — no widget building yet)
1222
+ _builders = {} # key -> (builder, page_widget)
1223
+
1224
+ for key, text, icon, builder, scrollable, kwargs in pages:
1225
+ # Insert sidebar headers/separators before certain pages
1226
+ if key in sidebar_structure:
1227
+ shell.add_separator()
1228
+ shell.add_header(sidebar_structure[key][1])
1229
+
1230
+ page = shell.add_page(key, text=text, icon=icon, scrollable=scrollable, **kwargs)
1231
+ _builders[key] = (builder, page)
1232
+
1233
+ # Build only the home page eagerly
1234
+ _built = set()
1235
+ _build_home_page(_builders["home"][1])
1236
+ _built.add("home")
1237
+
1238
+ # Lazy-build pages on first navigation via PageStack hook
1239
+ _orig_ps_navigate = shell.pages.navigate
1240
+
1241
+ def _lazy_ps_navigate(key, data=None, **kwargs):
1242
+ if key not in _built and key in _builders:
1243
+ builder, page = _builders[key]
1244
+ builder(page)
1245
+ _built.add(key)
1246
+ return _orig_ps_navigate(key, data=data, **kwargs)
1247
+
1248
+ shell.pages.navigate = _lazy_ps_navigate
1249
+
1250
+ shell.navigate("home")
1251
+ shell.mainloop()
1252
+
1253
+
1254
+ def setup_demo(master):
1255
+ """Setup the demo widgets - legacy compatibility.
1256
+
1257
+ Note: The gallery now uses AppShell, so this legacy entry point
1258
+ creates a simplified widget showcase in the given master frame.
1259
+ """
1260
+ from bootstack.constants import BOTH, YES
1261
+
1262
+ bs.Label(
1263
+ master,
1264
+ text="Use 'bootstack demo' to launch the full Widget Gallery.",
1265
+ font="heading-lg",
1266
+ padding=40,
1267
+ ).pack(fill=BOTH, expand=YES)