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.
- bootstack/__init__.py +249 -0
- bootstack/__main__.py +5 -0
- bootstack/api/__init__.py +127 -0
- bootstack/api/app.py +30 -0
- bootstack/api/constants.py +3 -0
- bootstack/api/data.py +23 -0
- bootstack/api/dialogs.py +44 -0
- bootstack/api/i18n.py +17 -0
- bootstack/api/localization.py +16 -0
- bootstack/api/menu.py +7 -0
- bootstack/api/style.py +23 -0
- bootstack/api/utils.py +24 -0
- bootstack/api/widgets.py +137 -0
- bootstack/assets/__init__.py +24 -0
- bootstack/assets/bootstack-transparent.png +0 -0
- bootstack/assets/bootstack.ico +0 -0
- bootstack/assets/bootstack.png +0 -0
- bootstack/assets/elements/__init__.py +0 -0
- bootstack/assets/elements/badge-pill.png +0 -0
- bootstack/assets/elements/badge-square.png +0 -0
- bootstack/assets/elements/border.png +0 -0
- bootstack/assets/elements/button-compact.png +0 -0
- bootstack/assets/elements/button-default.png +0 -0
- bootstack/assets/elements/button-group-horizontal-after-compact.png +0 -0
- bootstack/assets/elements/button-group-horizontal-after-default.png +0 -0
- bootstack/assets/elements/button-group-horizontal-before-compact.png +0 -0
- bootstack/assets/elements/button-group-horizontal-before-default.png +0 -0
- bootstack/assets/elements/button-group-horizontal-center-compact.png +0 -0
- bootstack/assets/elements/button-group-horizontal-center-default.png +0 -0
- bootstack/assets/elements/button-group-vertical-after-compact.png +0 -0
- bootstack/assets/elements/button-group-vertical-after-default.png +0 -0
- bootstack/assets/elements/button-group-vertical-before-compact.png +0 -0
- bootstack/assets/elements/button-group-vertical-before-default.png +0 -0
- bootstack/assets/elements/button-group-vertical-center-compact.png +0 -0
- bootstack/assets/elements/button-group-vertical-center-default.png +0 -0
- bootstack/assets/elements/checkbox-checked.png +0 -0
- bootstack/assets/elements/checkbox-indeterminate.png +0 -0
- bootstack/assets/elements/checkbox-unchecked.png +0 -0
- bootstack/assets/elements/field.png +0 -0
- bootstack/assets/elements/input-after-compact.png +0 -0
- bootstack/assets/elements/input-after-default.png +0 -0
- bootstack/assets/elements/input-before-compact.png +0 -0
- bootstack/assets/elements/input-before-default.png +0 -0
- bootstack/assets/elements/input-compact.png +0 -0
- bootstack/assets/elements/input-default.png +0 -0
- bootstack/assets/elements/list-item-separated.png +0 -0
- bootstack/assets/elements/list-item.png +0 -0
- bootstack/assets/elements/manifest.toml +480 -0
- bootstack/assets/elements/menu-item.png +0 -0
- bootstack/assets/elements/nav-button-compact.png +0 -0
- bootstack/assets/elements/nav-button-default.png +0 -0
- bootstack/assets/elements/nav-icon-button-compact.png +0 -0
- bootstack/assets/elements/nav-icon-button-default.png +0 -0
- bootstack/assets/elements/notebook-client-border.png +0 -0
- bootstack/assets/elements/notebook-tab-active.png +0 -0
- bootstack/assets/elements/notebook-tab-bar.png +0 -0
- bootstack/assets/elements/notebook-tab-normal.png +0 -0
- bootstack/assets/elements/notebook-tab-pill.png +0 -0
- bootstack/assets/elements/progress-bar-horizontal-striped.png +0 -0
- bootstack/assets/elements/progress-bar-solid.png +0 -0
- bootstack/assets/elements/progress-bar-thin.png +0 -0
- bootstack/assets/elements/progress-bar-vertical-striped.png +0 -0
- bootstack/assets/elements/radio-selected.png +0 -0
- bootstack/assets/elements/radio-unselected.png +0 -0
- bootstack/assets/elements/scrollbar-horizontal.png +0 -0
- bootstack/assets/elements/scrollbar-vertical.png +0 -0
- bootstack/assets/elements/slider-handle-focus.png +0 -0
- bootstack/assets/elements/slider-handle.png +0 -0
- bootstack/assets/elements/slider-track-horizontal.png +0 -0
- bootstack/assets/elements/slider-track-vertical.png +0 -0
- bootstack/assets/elements/switch-off.png +0 -0
- bootstack/assets/elements/switch-on.png +0 -0
- bootstack/assets/elements/tabs-bar-horizontal.png +0 -0
- bootstack/assets/elements/tabs-bar-vertical.png +0 -0
- bootstack/assets/elements/tabs-pill.png +0 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +875 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +875 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +851 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +841 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +914 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +841 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +841 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/themes/__init__.py +0 -0
- bootstack/assets/themes/amber-dark.json +32 -0
- bootstack/assets/themes/amber-light.json +32 -0
- bootstack/assets/themes/aurora-dark.json +32 -0
- bootstack/assets/themes/aurora-light.json +32 -0
- bootstack/assets/themes/bootstrap-dark.json +32 -0
- bootstack/assets/themes/bootstrap-light.json +32 -0
- bootstack/assets/themes/classic-dark.json +32 -0
- bootstack/assets/themes/classic-light.json +32 -0
- bootstack/assets/themes/docs-dark.json +32 -0
- bootstack/assets/themes/docs-light.json +32 -0
- bootstack/assets/themes/forest-dark.json +32 -0
- bootstack/assets/themes/forest-light.json +32 -0
- bootstack/assets/themes/ocean-dark.json +32 -0
- bootstack/assets/themes/ocean-light.json +32 -0
- bootstack/assets/themes/rose-dark.json +32 -0
- bootstack/assets/themes/rose-light.json +32 -0
- bootstack/assets/widgets/__init__.py +0 -0
- bootstack/assets/widgets/badge-default.png +0 -0
- bootstack/assets/widgets/badge-pill.png +0 -0
- bootstack/assets/widgets/border.png +0 -0
- bootstack/assets/widgets/button-group-horizontal-after.png +0 -0
- bootstack/assets/widgets/button-group-horizontal-before.png +0 -0
- bootstack/assets/widgets/button-group-horizontal-center.png +0 -0
- bootstack/assets/widgets/button-group-vertical-after.png +0 -0
- bootstack/assets/widgets/button-group-vertical-before.png +0 -0
- bootstack/assets/widgets/button-group-vertical-center.png +0 -0
- bootstack/assets/widgets/button.png +0 -0
- bootstack/assets/widgets/checkbox-checked.png +0 -0
- bootstack/assets/widgets/checkbox-indeterminate.png +0 -0
- bootstack/assets/widgets/checkbox-unchecked.png +0 -0
- bootstack/assets/widgets/field.png +0 -0
- bootstack/assets/widgets/icon-button.png +0 -0
- bootstack/assets/widgets/input-inner.png +0 -0
- bootstack/assets/widgets/input-prefix.png +0 -0
- bootstack/assets/widgets/input-suffix.png +0 -0
- bootstack/assets/widgets/input.png +0 -0
- bootstack/assets/widgets/list-item-focus.png +0 -0
- bootstack/assets/widgets/list-item-separated.png +0 -0
- bootstack/assets/widgets/menu-item-separated.png +0 -0
- bootstack/assets/widgets/notebook-client-border.png +0 -0
- bootstack/assets/widgets/notebook-pill-active.png +0 -0
- bootstack/assets/widgets/notebook-pill-inactive.png +0 -0
- bootstack/assets/widgets/notebook-tab-active.png +0 -0
- bootstack/assets/widgets/notebook-tab-border.png +0 -0
- bootstack/assets/widgets/notebook-tab-normal.png +0 -0
- bootstack/assets/widgets/notebook-underline.png +0 -0
- bootstack/assets/widgets/progress-bar-horizontal-default.png +0 -0
- bootstack/assets/widgets/progress-bar-horizontal-striped.png +0 -0
- bootstack/assets/widgets/progress-bar-vertical-default.png +0 -0
- bootstack/assets/widgets/progress-bar-vertical-striped.png +0 -0
- bootstack/assets/widgets/progress-trough-horizontal.png +0 -0
- bootstack/assets/widgets/progress-trough-vertical.png +0 -0
- bootstack/assets/widgets/radio-selected.png +0 -0
- bootstack/assets/widgets/radio-unselected.png +0 -0
- bootstack/assets/widgets/scrollbar-horizontal-rounded.png +0 -0
- bootstack/assets/widgets/scrollbar-vertical-rounded.png +0 -0
- bootstack/assets/widgets/separator-horizontal.png +0 -0
- bootstack/assets/widgets/separator-vertical.png +0 -0
- bootstack/assets/widgets/slider-handle-focus.png +0 -0
- bootstack/assets/widgets/slider-handle.png +0 -0
- bootstack/assets/widgets/slider-track-horizontal.png +0 -0
- bootstack/assets/widgets/slider-track-vertical.png +0 -0
- bootstack/assets/widgets/switch-off.png +0 -0
- bootstack/assets/widgets/switch-on.png +0 -0
- bootstack/assets/widgets/tabs-bar-horizontal.png +0 -0
- bootstack/assets/widgets/tabs-bar-vertical.png +0 -0
- bootstack/assets/widgets/tabs-pill.png +0 -0
- bootstack/cli/__init__.py +124 -0
- bootstack/cli/__main__.py +6 -0
- bootstack/cli/add.py +439 -0
- bootstack/cli/build.py +115 -0
- bootstack/cli/config.py +287 -0
- bootstack/cli/demo.py +1267 -0
- bootstack/cli/doctor.py +195 -0
- bootstack/cli/list_cmd.py +71 -0
- bootstack/cli/promote.py +120 -0
- bootstack/cli/pyinstaller.py +246 -0
- bootstack/cli/run.py +99 -0
- bootstack/cli/start.py +105 -0
- bootstack/cli/templates/__init__.py +861 -0
- bootstack/constants.py +325 -0
- bootstack/core/__init__.py +34 -0
- bootstack/core/capabilities/__init__.py +45 -0
- bootstack/core/capabilities/after.py +103 -0
- bootstack/core/capabilities/bind.py +154 -0
- bootstack/core/capabilities/bindtags.py +112 -0
- bootstack/core/capabilities/busy.py +61 -0
- bootstack/core/capabilities/clipboard.py +88 -0
- bootstack/core/capabilities/focus.py +118 -0
- bootstack/core/capabilities/grab.py +65 -0
- bootstack/core/capabilities/grid.py +188 -0
- bootstack/core/capabilities/localization.py +231 -0
- bootstack/core/capabilities/pack.py +119 -0
- bootstack/core/capabilities/place.py +92 -0
- bootstack/core/capabilities/selection.py +136 -0
- bootstack/core/capabilities/signals.py +242 -0
- bootstack/core/capabilities/winfo.py +315 -0
- bootstack/core/colorutils.py +234 -0
- bootstack/core/exceptions.py +95 -0
- bootstack/core/images.py +283 -0
- bootstack/core/localization/README.md +90 -0
- bootstack/core/localization/__init__.py +13 -0
- bootstack/core/localization/intl_format.py +580 -0
- bootstack/core/localization/msgcat.py +425 -0
- bootstack/core/localization/specs.py +143 -0
- bootstack/core/mixins/__init__.py +1 -0
- bootstack/core/mixins/ttk_state.py +35 -0
- bootstack/core/mixins/widget.py +132 -0
- bootstack/core/publisher.py +147 -0
- bootstack/core/signals/README.md +112 -0
- bootstack/core/signals/__init__.py +8 -0
- bootstack/core/signals/integration.py +100 -0
- bootstack/core/signals/signal.py +317 -0
- bootstack/core/signals/types.py +4 -0
- bootstack/core/validation/__init__.py +5 -0
- bootstack/core/validation/types.py +13 -0
- bootstack/core/validation/validation_result.py +17 -0
- bootstack/core/validation/validation_rules.py +112 -0
- bootstack/core/variables.py +62 -0
- bootstack/datasource/README.md +607 -0
- bootstack/datasource/__init__.py +51 -0
- bootstack/datasource/base.py +474 -0
- bootstack/datasource/file_source.py +541 -0
- bootstack/datasource/memory_source.py +482 -0
- bootstack/datasource/sqlite_source.py +453 -0
- bootstack/datasource/types.py +259 -0
- bootstack/dialogs/__init__.py +56 -0
- bootstack/dialogs/colorchooser.py +674 -0
- bootstack/dialogs/colordropper.py +257 -0
- bootstack/dialogs/datedialog.py +404 -0
- bootstack/dialogs/dialog.py +514 -0
- bootstack/dialogs/filterdialog.py +358 -0
- bootstack/dialogs/fontdialog.py +339 -0
- bootstack/dialogs/formdialog.py +541 -0
- bootstack/dialogs/message.py +489 -0
- bootstack/dialogs/query.py +561 -0
- bootstack/py.typed +1 -0
- bootstack/runtime/__init__.py +3 -0
- bootstack/runtime/app.py +879 -0
- bootstack/runtime/base_window.py +786 -0
- bootstack/runtime/events.py +399 -0
- bootstack/runtime/menu.py +510 -0
- bootstack/runtime/shortcuts.py +423 -0
- bootstack/runtime/tk_patch.py +31 -0
- bootstack/runtime/toplevel.py +131 -0
- bootstack/runtime/utility.py +371 -0
- bootstack/runtime/visual_focus.py +228 -0
- bootstack/runtime/window_utilities.py +1043 -0
- bootstack/style/__init__.py +5498 -0
- bootstack/style/bootstyle.py +507 -0
- bootstack/style/bootstyle_builder_base.py +752 -0
- bootstack/style/bootstyle_builder_mixed.py +93 -0
- bootstack/style/bootstyle_builder_tk.py +109 -0
- bootstack/style/bootstyle_builder_ttk.py +354 -0
- bootstack/style/builders/__init__.py +51 -0
- bootstack/style/builders/badge.py +44 -0
- bootstack/style/builders/button.py +453 -0
- bootstack/style/builders/buttongroup.py +344 -0
- bootstack/style/builders/calendar.py +271 -0
- bootstack/style/builders/checkbutton.py +95 -0
- bootstack/style/builders/combobox.py +112 -0
- bootstack/style/builders/contextmenu.py +268 -0
- bootstack/style/builders/entry.py +83 -0
- bootstack/style/builders/expander.py +171 -0
- bootstack/style/builders/field.py +312 -0
- bootstack/style/builders/frame.py +27 -0
- bootstack/style/builders/label.py +28 -0
- bootstack/style/builders/labelframe.py +41 -0
- bootstack/style/builders/listview.py +267 -0
- bootstack/style/builders/menubar.py +74 -0
- bootstack/style/builders/menubutton.py +408 -0
- bootstack/style/builders/notebook.py +316 -0
- bootstack/style/builders/panedwindow.py +25 -0
- bootstack/style/builders/progressbar.py +71 -0
- bootstack/style/builders/radiobutton.py +68 -0
- bootstack/style/builders/scale.py +66 -0
- bootstack/style/builders/scrollbar.py +360 -0
- bootstack/style/builders/separator.py +45 -0
- bootstack/style/builders/sidenav.py +313 -0
- bootstack/style/builders/sizegrip.py +15 -0
- bootstack/style/builders/spinbox.py +119 -0
- bootstack/style/builders/switch.py +67 -0
- bootstack/style/builders/tabitem.py +205 -0
- bootstack/style/builders/toolbutton.py +260 -0
- bootstack/style/builders/tooltip.py +26 -0
- bootstack/style/builders/treeview.py +269 -0
- bootstack/style/builders/utils.py +404 -0
- bootstack/style/builders_tk/__init__.py +16 -0
- bootstack/style/builders_tk/defaults.py +229 -0
- bootstack/style/element.py +173 -0
- bootstack/style/style.py +499 -0
- bootstack/style/theme_provider.py +449 -0
- bootstack/style/tk_patch.py +5 -0
- bootstack/style/token_maps.py +42 -0
- bootstack/style/types.py +32 -0
- bootstack/style/typography.py +527 -0
- bootstack/style/utility.py +696 -0
- bootstack/themes/__init__.py +12 -0
- bootstack/themes/standard.py +415 -0
- bootstack/themes/user.py +45 -0
- bootstack/widgets/__init__.py +53 -0
- bootstack/widgets/composites/__init__.py +38 -0
- bootstack/widgets/composites/accordion.py +385 -0
- bootstack/widgets/composites/appshell.py +445 -0
- bootstack/widgets/composites/buttongroup.py +391 -0
- bootstack/widgets/composites/calendar.py +914 -0
- bootstack/widgets/composites/compositeframe.py +282 -0
- bootstack/widgets/composites/contextmenu.py +1754 -0
- bootstack/widgets/composites/dateentry.py +261 -0
- bootstack/widgets/composites/dropdownbutton.py +190 -0
- bootstack/widgets/composites/expander.py +508 -0
- bootstack/widgets/composites/field.py +448 -0
- bootstack/widgets/composites/floodgauge.py +434 -0
- bootstack/widgets/composites/form.py +983 -0
- bootstack/widgets/composites/labeledscale.py +209 -0
- bootstack/widgets/composites/list/__init__.py +15 -0
- bootstack/widgets/composites/list/listitem.py +733 -0
- bootstack/widgets/composites/list/listview.py +1507 -0
- bootstack/widgets/composites/menubar.py +303 -0
- bootstack/widgets/composites/meter.py +882 -0
- bootstack/widgets/composites/numericentry.py +183 -0
- bootstack/widgets/composites/pagestack.py +330 -0
- bootstack/widgets/composites/passwordentry.py +149 -0
- bootstack/widgets/composites/pathentry.py +223 -0
- bootstack/widgets/composites/radiogroup.py +466 -0
- bootstack/widgets/composites/scrolledtext.py +388 -0
- bootstack/widgets/composites/scrolledtext.pyi +186 -0
- bootstack/widgets/composites/scrollview.py +675 -0
- bootstack/widgets/composites/selectbox.py +544 -0
- bootstack/widgets/composites/sidenav/__init__.py +24 -0
- bootstack/widgets/composites/sidenav/group.py +485 -0
- bootstack/widgets/composites/sidenav/header.py +83 -0
- bootstack/widgets/composites/sidenav/item.py +413 -0
- bootstack/widgets/composites/sidenav/separator.py +51 -0
- bootstack/widgets/composites/sidenav/view.py +919 -0
- bootstack/widgets/composites/spinnerentry.py +232 -0
- bootstack/widgets/composites/tableview/__init__.py +5 -0
- bootstack/widgets/composites/tableview/tableview.py +2254 -0
- bootstack/widgets/composites/tableview/types.py +169 -0
- bootstack/widgets/composites/tabs/__init__.py +6 -0
- bootstack/widgets/composites/tabs/tabitem.py +372 -0
- bootstack/widgets/composites/tabs/tabs.py +478 -0
- bootstack/widgets/composites/tabs/tabview.py +352 -0
- bootstack/widgets/composites/textentry.py +90 -0
- bootstack/widgets/composites/timeentry.py +189 -0
- bootstack/widgets/composites/toast.py +364 -0
- bootstack/widgets/composites/togglegroup.py +382 -0
- bootstack/widgets/composites/toolbar.py +393 -0
- bootstack/widgets/composites/tooltip.py +404 -0
- bootstack/widgets/internal/__init__.py +0 -0
- bootstack/widgets/internal/wrapper_base.py +304 -0
- bootstack/widgets/mixins/__init__.py +25 -0
- bootstack/widgets/mixins/configure_mixin.py +186 -0
- bootstack/widgets/mixins/entry_mixin.py +70 -0
- bootstack/widgets/mixins/font_mixin.py +346 -0
- bootstack/widgets/mixins/icon_mixin.py +38 -0
- bootstack/widgets/mixins/localization_mixin.py +255 -0
- bootstack/widgets/mixins/signal_mixin.py +272 -0
- bootstack/widgets/mixins/validation_mixin.py +204 -0
- bootstack/widgets/parts/__init__.py +11 -0
- bootstack/widgets/parts/numberentry_part.py +345 -0
- bootstack/widgets/parts/spinnerentry_part.py +394 -0
- bootstack/widgets/parts/textentry_part.py +344 -0
- bootstack/widgets/primitives/__init__.py +55 -0
- bootstack/widgets/primitives/badge.py +44 -0
- bootstack/widgets/primitives/button.py +89 -0
- bootstack/widgets/primitives/card.py +66 -0
- bootstack/widgets/primitives/checkbutton.py +124 -0
- bootstack/widgets/primitives/checktoggle.py +53 -0
- bootstack/widgets/primitives/combobox.py +165 -0
- bootstack/widgets/primitives/entry.py +98 -0
- bootstack/widgets/primitives/frame.py +206 -0
- bootstack/widgets/primitives/gridframe.py +479 -0
- bootstack/widgets/primitives/label.py +95 -0
- bootstack/widgets/primitives/labelframe.py +63 -0
- bootstack/widgets/primitives/menubutton.py +118 -0
- bootstack/widgets/primitives/notebook.py +551 -0
- bootstack/widgets/primitives/optionmenu.py +248 -0
- bootstack/widgets/primitives/packframe.py +228 -0
- bootstack/widgets/primitives/panedwindow.py +58 -0
- bootstack/widgets/primitives/progressbar.py +95 -0
- bootstack/widgets/primitives/radiobutton.py +115 -0
- bootstack/widgets/primitives/radiotoggle.py +50 -0
- bootstack/widgets/primitives/scale.py +85 -0
- bootstack/widgets/primitives/scrollbar.py +56 -0
- bootstack/widgets/primitives/separator.py +56 -0
- bootstack/widgets/primitives/sizegrip.py +47 -0
- bootstack/widgets/primitives/spinbox.py +91 -0
- bootstack/widgets/primitives/switch.py +41 -0
- bootstack/widgets/primitives/treeview.py +77 -0
- bootstack/widgets/types.py +20 -0
- bootstack-0.1.0a1.dist-info/METADATA +196 -0
- bootstack-0.1.0a1.dist-info/RECORD +419 -0
- bootstack-0.1.0a1.dist-info/WHEEL +5 -0
- bootstack-0.1.0a1.dist-info/entry_points.txt +2 -0
- bootstack-0.1.0a1.dist-info/licenses/LICENSE +22 -0
- bootstack-0.1.0a1.dist-info/licenses/NOTICE +10 -0
- bootstack-0.1.0a1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
# import tomllib
|
|
5
|
+
from importlib import resources
|
|
6
|
+
|
|
7
|
+
from bootstack.runtime.app import get_app_settings
|
|
8
|
+
from bootstack.core.exceptions import ThemeError
|
|
9
|
+
from bootstack.style.utility import (
|
|
10
|
+
shade_color, tint_color, color_to_hsl, hsl_to_hex, best_foreground
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
_registered_themes = {}
|
|
14
|
+
_current_theme = None
|
|
15
|
+
|
|
16
|
+
# Weights for generating color spectrum tints (toward white) and shades (toward black)
|
|
17
|
+
# Each weight represents how much of the original color to retain when mixing
|
|
18
|
+
# Full 50-step increments from 50-450 (tints) and 550-950 (shades)
|
|
19
|
+
TINT_WEIGHTS = {
|
|
20
|
+
50: 0.90,
|
|
21
|
+
100: 0.80,
|
|
22
|
+
150: 0.70,
|
|
23
|
+
200: 0.60,
|
|
24
|
+
250: 0.50,
|
|
25
|
+
300: 0.40,
|
|
26
|
+
350: 0.325,
|
|
27
|
+
400: 0.25,
|
|
28
|
+
450: 0.125,
|
|
29
|
+
}
|
|
30
|
+
SHADE_WEIGHTS = {
|
|
31
|
+
550: 0.125,
|
|
32
|
+
600: 0.25,
|
|
33
|
+
650: 0.325,
|
|
34
|
+
700: 0.40,
|
|
35
|
+
750: 0.50,
|
|
36
|
+
800: 0.60,
|
|
37
|
+
850: 0.725,
|
|
38
|
+
900: 0.85,
|
|
39
|
+
950: 0.95,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def register_user_theme(name, path):
|
|
44
|
+
data = load_user_defined_theme(path)
|
|
45
|
+
_registered_themes[name] = data
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_theme(name):
|
|
49
|
+
"""Return a registered theme by name.
|
|
50
|
+
|
|
51
|
+
If the theme is not currently registered this will re-run
|
|
52
|
+
`load_system_themes` once to pick up any newly added themes before
|
|
53
|
+
failing.
|
|
54
|
+
"""
|
|
55
|
+
if name in _registered_themes:
|
|
56
|
+
return _registered_themes[name]
|
|
57
|
+
|
|
58
|
+
# Lazy fallback: configuration (e.g., load_all_themes/include_legacy_themes)
|
|
59
|
+
# may have changed after the provider singleton was first initialized.
|
|
60
|
+
# Re-run the loader once to pick up any additional themes.
|
|
61
|
+
try:
|
|
62
|
+
load_system_themes()
|
|
63
|
+
if name in _registered_themes:
|
|
64
|
+
return _registered_themes[name]
|
|
65
|
+
except Exception:
|
|
66
|
+
# If anything goes wrong here, fall through to the ThemeError.
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
# Build a helpful error message listing available themes
|
|
70
|
+
available = sorted(
|
|
71
|
+
{
|
|
72
|
+
data.get("name")
|
|
73
|
+
for data in _registered_themes.values()
|
|
74
|
+
if isinstance(data, dict) and data.get("name")
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
available_str = ", ".join(available) if available else "<none>"
|
|
78
|
+
raise ThemeError(
|
|
79
|
+
f"Theme '{name}' is not registered. "
|
|
80
|
+
f"Registered themes: {available_str}"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def load_system_themes():
|
|
85
|
+
"""Load system themes from the package.
|
|
86
|
+
|
|
87
|
+
Loads all v2 themes from `bootstack.assets.themes`. Themes matching
|
|
88
|
+
app settings dark_theme or light_theme are also registered with
|
|
89
|
+
'dark' and 'light' aliases for convenience.
|
|
90
|
+
"""
|
|
91
|
+
from importlib import resources
|
|
92
|
+
|
|
93
|
+
global _registered_themes
|
|
94
|
+
|
|
95
|
+
base_package = 'bootstack.assets.themes'
|
|
96
|
+
|
|
97
|
+
# Get configured theme names for dark/light aliases
|
|
98
|
+
app_settings = get_app_settings()
|
|
99
|
+
dark_theme_name = app_settings.dark_theme
|
|
100
|
+
light_theme_name = app_settings.light_theme
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
base_dir = resources.files(base_package)
|
|
104
|
+
except ModuleNotFoundError:
|
|
105
|
+
base_dir = None
|
|
106
|
+
if base_dir is not None:
|
|
107
|
+
for theme_file in base_dir.iterdir():
|
|
108
|
+
if not theme_file.name.endswith(".json"):
|
|
109
|
+
continue
|
|
110
|
+
data = load_package_theme(theme_file.name, base_package)
|
|
111
|
+
name = data.get("name")
|
|
112
|
+
if not name:
|
|
113
|
+
continue
|
|
114
|
+
_registered_themes[name] = data
|
|
115
|
+
|
|
116
|
+
# Register aliases for dark/light themes
|
|
117
|
+
if name == dark_theme_name:
|
|
118
|
+
_registered_themes['dark'] = data
|
|
119
|
+
elif name == light_theme_name:
|
|
120
|
+
_registered_themes['light'] = data
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def load_user_defined_theme(path):
|
|
124
|
+
"""Load a user-defined theme from a JSON file on disk.
|
|
125
|
+
|
|
126
|
+
The file must follow the v2 theme schema (top-level `name`,
|
|
127
|
+
`display_name`, `mode`, `foreground`, `background`, plus
|
|
128
|
+
`shades` and `semantic` mappings).
|
|
129
|
+
"""
|
|
130
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
131
|
+
return json.load(f)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def load_package_theme(filename: str, package="bootstack.assets.themes"):
|
|
135
|
+
with resources.files(package).joinpath(filename).open("r", encoding="utf-8") as f:
|
|
136
|
+
return json.load(f)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def color_spectrum(token, value):
|
|
140
|
+
"""Generate a color spectrum with 50-step increments from 50-950.
|
|
141
|
+
|
|
142
|
+
Creates tints (lighter) and shades (darker) of the base color:
|
|
143
|
+
- 50-450: Tints toward white (50 is lightest)
|
|
144
|
+
- 500: Base color
|
|
145
|
+
- 550-950: Shades toward black (950 is darkest)
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
token: The color token name (e.g., 'gray', 'blue')
|
|
149
|
+
value: The base hex color value
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Dict mapping spectrum names to hex colors
|
|
153
|
+
"""
|
|
154
|
+
result = {}
|
|
155
|
+
|
|
156
|
+
# Generate tints (50-450)
|
|
157
|
+
for stop, weight in TINT_WEIGHTS.items():
|
|
158
|
+
result[f'{token}[{stop}]'] = tint_color(value, weight)
|
|
159
|
+
|
|
160
|
+
# Base color (500)
|
|
161
|
+
result[f'{token}[500]'] = value
|
|
162
|
+
|
|
163
|
+
# Generate shades (550-950)
|
|
164
|
+
for stop, weight in SHADE_WEIGHTS.items():
|
|
165
|
+
result[f'{token}[{stop}]'] = shade_color(value, weight)
|
|
166
|
+
|
|
167
|
+
return result
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class ThemeProvider:
|
|
171
|
+
"""Theme data provider with singleton access and helpers.
|
|
172
|
+
|
|
173
|
+
Mirrors the pattern used by Style/use_style():
|
|
174
|
+
- `ThemeProvider()` returns the global instance (singleton)
|
|
175
|
+
- `use_theme(name)` returns/initializes the singleton with optional theme
|
|
176
|
+
- `ThemeProvider.instance(name)` remains for backward compatibility
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
# Class-level global singleton instance
|
|
180
|
+
_instance: ThemeProvider | None = None
|
|
181
|
+
|
|
182
|
+
def __new__(cls, *args, **kwargs):
|
|
183
|
+
"""Ensure ThemeProvider() always returns the global singleton instance."""
|
|
184
|
+
if cls._instance is None:
|
|
185
|
+
cls._instance = super().__new__(cls)
|
|
186
|
+
return cls._instance
|
|
187
|
+
|
|
188
|
+
def __init__(self, name: str = "dark"):
|
|
189
|
+
# Prevent reinitialization on subsequent ThemeProvider() calls
|
|
190
|
+
if getattr(self, "_initialized", False):
|
|
191
|
+
if name and name != self.name:
|
|
192
|
+
self.use(name)
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
self._theme = {}
|
|
196
|
+
self._colors = {}
|
|
197
|
+
load_system_themes()
|
|
198
|
+
|
|
199
|
+
from bootstack.style.typography import Typography
|
|
200
|
+
Typography.initialize()
|
|
201
|
+
|
|
202
|
+
self.use(name)
|
|
203
|
+
self._initialized = True
|
|
204
|
+
|
|
205
|
+
# ----- Theme metadata helpers -------------------------------------------------
|
|
206
|
+
|
|
207
|
+
def list_themes(self) -> list[dict[str, str]]:
|
|
208
|
+
"""Return a list of available themes with names and display names.
|
|
209
|
+
|
|
210
|
+
The result is a list of dictionaries in the form:
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
{"name": "bootstrap-light", "display_name": "Bootstrap Light"}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Aliases such as `\"light\"` and `\"dark\"` are not included; only the
|
|
217
|
+
canonical theme entries loaded into the provider are returned.
|
|
218
|
+
"""
|
|
219
|
+
themes: list[dict[str, str]] = []
|
|
220
|
+
seen: set[str] = set()
|
|
221
|
+
|
|
222
|
+
for key, data in _registered_themes.items():
|
|
223
|
+
# Skip alias keys that point at an existing theme object
|
|
224
|
+
name = data.get("name")
|
|
225
|
+
if not name:
|
|
226
|
+
continue
|
|
227
|
+
if key != name and name in _registered_themes:
|
|
228
|
+
# This is an alias like 'light' or 'dark'
|
|
229
|
+
continue
|
|
230
|
+
if name in seen:
|
|
231
|
+
continue
|
|
232
|
+
seen.add(name)
|
|
233
|
+
themes.append(
|
|
234
|
+
{
|
|
235
|
+
"name": name,
|
|
236
|
+
"display_name": data.get("display_name", name),
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# If the application has declared a specific set/order of themes
|
|
241
|
+
# to expose, filter and order by that list.
|
|
242
|
+
select_themes = get_app_settings().available_themes
|
|
243
|
+
if select_themes:
|
|
244
|
+
by_name = {t["name"]: t for t in themes}
|
|
245
|
+
ordered: list[dict[str, str]] = []
|
|
246
|
+
for name in select_themes:
|
|
247
|
+
t = by_name.get(name)
|
|
248
|
+
if t is not None:
|
|
249
|
+
ordered.append(t)
|
|
250
|
+
return ordered
|
|
251
|
+
|
|
252
|
+
# Otherwise, sort for stable UI ordering (by display name, then name)
|
|
253
|
+
themes.sort(key=lambda t: (t["display_name"].lower(), t["name"].lower()))
|
|
254
|
+
return themes
|
|
255
|
+
|
|
256
|
+
def use(self, name):
|
|
257
|
+
self._theme = get_theme(name)
|
|
258
|
+
self.build_theme_colors()
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def raw(self):
|
|
262
|
+
"""Return the raw source dictionary"""
|
|
263
|
+
return self._theme
|
|
264
|
+
|
|
265
|
+
def build_theme_colors(self):
|
|
266
|
+
colors = {}
|
|
267
|
+
colors.update(
|
|
268
|
+
foreground=self.raw.get('foreground'),
|
|
269
|
+
background=self.raw.get('background'),
|
|
270
|
+
white=self.raw.get('white'),
|
|
271
|
+
black=self.raw.get('black'),
|
|
272
|
+
**self._shades,
|
|
273
|
+
)
|
|
274
|
+
# add shaded spectrum
|
|
275
|
+
for color, value in self._shades.items():
|
|
276
|
+
colors.update(**color_spectrum(color, value))
|
|
277
|
+
|
|
278
|
+
# semantic tokens
|
|
279
|
+
neutral_tokens = {
|
|
280
|
+
"foreground",
|
|
281
|
+
"muted",
|
|
282
|
+
"muted_alt",
|
|
283
|
+
"subtle",
|
|
284
|
+
"border",
|
|
285
|
+
"border_subtle",
|
|
286
|
+
}
|
|
287
|
+
for token, value in self._semantic.items():
|
|
288
|
+
# Neutral roles are derived elsewhere from the top-level
|
|
289
|
+
# foreground/background and should not be overridden here.
|
|
290
|
+
if token in neutral_tokens:
|
|
291
|
+
continue
|
|
292
|
+
colors[token] = colors[value]
|
|
293
|
+
|
|
294
|
+
# Surface tokens - semantic ramps for container backgrounds
|
|
295
|
+
self._build_surface_tokens(colors)
|
|
296
|
+
|
|
297
|
+
self._colors.clear()
|
|
298
|
+
self._colors.update(**colors)
|
|
299
|
+
|
|
300
|
+
def _build_surface_tokens(self, colors: dict):
|
|
301
|
+
"""Build semantic surface tokens for container backgrounds.
|
|
302
|
+
|
|
303
|
+
Surface tokens provide deterministic, theme-defined backgrounds
|
|
304
|
+
that don't rely on "background +1 math" for elevation. The hue
|
|
305
|
+
and saturation are derived from the theme's background color to
|
|
306
|
+
ensure consistent tinting across all surfaces.
|
|
307
|
+
|
|
308
|
+
Token families:
|
|
309
|
+
- chrome: UI shell (sidebars, toolbars, navigation)
|
|
310
|
+
- content: Main content area background (= theme background)
|
|
311
|
+
- card: Elevated content (cards, panels)
|
|
312
|
+
- overlay: Floating elements (menus, dialogs, tooltips)
|
|
313
|
+
- input: Form control backgrounds
|
|
314
|
+
"""
|
|
315
|
+
is_dark = self.mode == 'dark'
|
|
316
|
+
bg = colors['background']
|
|
317
|
+
fg = colors['foreground']
|
|
318
|
+
|
|
319
|
+
# Extract hue and saturation from background for tinting
|
|
320
|
+
hue, sat, bg_lightness = color_to_hsl(bg, model='hex')
|
|
321
|
+
|
|
322
|
+
# Cap saturation for subtle tinting (avoid garish surfaces)
|
|
323
|
+
# Use 0 saturation for near-neutral backgrounds
|
|
324
|
+
tint_sat = min(sat, 25) if sat >= 5 else 0
|
|
325
|
+
|
|
326
|
+
def tinted_surface(lightness: float) -> str:
|
|
327
|
+
"""Generate a surface color with the theme's tint at given lightness."""
|
|
328
|
+
if tint_sat == 0:
|
|
329
|
+
# No tint - pure neutral grey
|
|
330
|
+
return hsl_to_hex(0, 0, lightness)
|
|
331
|
+
return hsl_to_hex(hue, tint_sat, lightness)
|
|
332
|
+
|
|
333
|
+
# Define surface lightness levels for each mode
|
|
334
|
+
# content = theme background (as defined in JSON)
|
|
335
|
+
# Other surfaces are relative to it
|
|
336
|
+
if is_dark:
|
|
337
|
+
# Dark mode: lower lightness = darker/recessed
|
|
338
|
+
surfaces = {
|
|
339
|
+
'chrome': tinted_surface(max(bg_lightness - 3, 3)), # Darker than content
|
|
340
|
+
'content': bg, # Theme background
|
|
341
|
+
'card': tinted_surface(min(bg_lightness + 4, 20)), # Elevated cards
|
|
342
|
+
'overlay': tinted_surface(min(bg_lightness + 7, 25)), # Menus, dialogs
|
|
343
|
+
'input': tinted_surface(max(bg_lightness - 5, 2)), # Recessed inputs
|
|
344
|
+
}
|
|
345
|
+
# Stroke colors for borders
|
|
346
|
+
colors['stroke'] = tinted_surface(min(bg_lightness + 12, 30))
|
|
347
|
+
colors['stroke_subtle'] = tinted_surface(min(bg_lightness + 6, 22))
|
|
348
|
+
else:
|
|
349
|
+
# Light mode: use subtle darkening for elevation (since bg is often near-white)
|
|
350
|
+
surfaces = {
|
|
351
|
+
'chrome': tinted_surface(max(bg_lightness - 8, 88)), # Noticeably darker
|
|
352
|
+
'content': bg, # Theme background
|
|
353
|
+
'card': tinted_surface(max(bg_lightness - 4, 92)), # Slightly darker for contrast
|
|
354
|
+
'overlay': tinted_surface(max(bg_lightness - 2, 96)), # Subtle for popups
|
|
355
|
+
'input': tinted_surface(max(bg_lightness - 5, 90)), # Recessed inputs
|
|
356
|
+
}
|
|
357
|
+
# Stroke colors for borders
|
|
358
|
+
colors['stroke'] = tinted_surface(max(bg_lightness - 20, 70))
|
|
359
|
+
colors['stroke_subtle'] = tinted_surface(max(bg_lightness - 10, 80))
|
|
360
|
+
|
|
361
|
+
# Add surfaces to colors
|
|
362
|
+
for name, value in surfaces.items():
|
|
363
|
+
colors[name] = value
|
|
364
|
+
|
|
365
|
+
# Pre-compute foreground colors for each surface
|
|
366
|
+
candidates = [fg, '#ffffff', '#000000']
|
|
367
|
+
for name, value in surfaces.items():
|
|
368
|
+
colors[f'on_{name}'] = best_foreground(value, candidates)
|
|
369
|
+
|
|
370
|
+
# Secondary/muted foreground for each surface (reduced contrast)
|
|
371
|
+
for name, value in surfaces.items():
|
|
372
|
+
# Generate a muted foreground by mixing fg with the surface
|
|
373
|
+
on_color = colors[f'on_{name}']
|
|
374
|
+
if on_color in ('#ffffff', '#FFFFFF'):
|
|
375
|
+
colors[f'on_{name}_secondary'] = tinted_surface(65) if is_dark else tinted_surface(45)
|
|
376
|
+
else:
|
|
377
|
+
colors[f'on_{name}_secondary'] = tinted_surface(35) if is_dark else tinted_surface(55)
|
|
378
|
+
|
|
379
|
+
# Hover states for each surface (subtle highlight)
|
|
380
|
+
for name in surfaces.keys():
|
|
381
|
+
if name == 'content':
|
|
382
|
+
# Content hover: slightly elevated
|
|
383
|
+
hover_l = min(bg_lightness + 5, 25) if is_dark else max(bg_lightness - 5, 90)
|
|
384
|
+
else:
|
|
385
|
+
# Other surfaces: shift toward content
|
|
386
|
+
surface_h, surface_s, surface_l = color_to_hsl(colors[name], model='hex')
|
|
387
|
+
hover_l = min(surface_l + 5, 30) if is_dark else max(surface_l - 5, 85)
|
|
388
|
+
colors[f'{name}_hover'] = tinted_surface(hover_l)
|
|
389
|
+
|
|
390
|
+
@property
|
|
391
|
+
def name(self):
|
|
392
|
+
"""The name of the theme"""
|
|
393
|
+
return self.raw.get('name')
|
|
394
|
+
|
|
395
|
+
@property
|
|
396
|
+
def display_name(self):
|
|
397
|
+
"""The display name of the theme"""
|
|
398
|
+
return self.raw.get('display_name')
|
|
399
|
+
|
|
400
|
+
@property
|
|
401
|
+
def mode(self):
|
|
402
|
+
"""Returns the color mode 'light' or 'dark'"""
|
|
403
|
+
return self.raw.get('mode')
|
|
404
|
+
|
|
405
|
+
@property
|
|
406
|
+
def colors(self):
|
|
407
|
+
return self._colors
|
|
408
|
+
|
|
409
|
+
@property
|
|
410
|
+
def typography(self):
|
|
411
|
+
"""Returns the current typography configuration as FontTokens"""
|
|
412
|
+
from bootstack.style.typography import Typography
|
|
413
|
+
return Typography.all()
|
|
414
|
+
|
|
415
|
+
@property
|
|
416
|
+
def _shades(self):
|
|
417
|
+
return self.raw.get('shades')
|
|
418
|
+
|
|
419
|
+
@property
|
|
420
|
+
def _semantic(self):
|
|
421
|
+
return self.raw.get('semantic')
|
|
422
|
+
|
|
423
|
+
def __repr__(self):
|
|
424
|
+
"""Return a string representation of the current theme"""
|
|
425
|
+
return f"<Theme name={self.name} mode={self.mode}>"
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def use_theme(name: str = None) -> ThemeProvider:
|
|
429
|
+
"""Return the global ThemeProvider singleton instance.
|
|
430
|
+
|
|
431
|
+
Convenience helper that mirrors `use_style()` so callers can obtain
|
|
432
|
+
the current ThemeProvider without handling singleton state.
|
|
433
|
+
|
|
434
|
+
Args:
|
|
435
|
+
name: Optional theme name to switch to, or None to return current instance.
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
Global ThemeProvider instance.
|
|
439
|
+
"""
|
|
440
|
+
# If instance doesn't exist yet, create it with default theme
|
|
441
|
+
if ThemeProvider._instance is None:
|
|
442
|
+
return ThemeProvider(name or "dark")
|
|
443
|
+
|
|
444
|
+
# If name is provided and different, switch themes
|
|
445
|
+
if name is not None and name != ThemeProvider._instance.name:
|
|
446
|
+
ThemeProvider._instance.use(name)
|
|
447
|
+
|
|
448
|
+
# Return the singleton
|
|
449
|
+
return ThemeProvider._instance
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
COLOR_TOKENS = {
|
|
4
|
+
'primary', 'secondary', 'success', 'info',
|
|
5
|
+
'warning', 'danger', 'light', 'dark',
|
|
6
|
+
'foreground', 'background', 'white', 'black',
|
|
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
|
+
'notebook': 'TNotebook',
|
|
26
|
+
'treeview': 'Treeview',
|
|
27
|
+
'separator': 'TSeparator',
|
|
28
|
+
'sizegrip': 'TSizegrip',
|
|
29
|
+
'panedwindow': 'TPanedwindow',
|
|
30
|
+
'spinbox': 'TSpinbox',
|
|
31
|
+
'menubutton': 'TMenubutton',
|
|
32
|
+
'field': 'TField',
|
|
33
|
+
'toolbutton': 'Toolbutton',
|
|
34
|
+
'tooltip': 'Tooltip',
|
|
35
|
+
'calendar': 'TCalendar',
|
|
36
|
+
'buttongroup': 'ButtonGroup'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
WIDGET_NAME_MAP = {v: k for k, v in WIDGET_CLASS_MAP.items()}
|
|
40
|
+
CONTAINER_CLASSES = {'TFrame', 'TLabelframe'}
|
|
41
|
+
ORIENT_CLASSES = {'TProgressbar', 'TScale', 'TScrollbar', 'TPanedwindow', 'TSeparator'}
|
|
42
|
+
ICON_CLASSES = {'TLabel', 'TButton', 'TCheckbutton', 'TRadiobutton', 'TMenubutton'}
|
bootstack/style/types.py
ADDED
|
@@ -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', 'secondary', 'success', 'info', 'warning', 'danger', 'light', 'dark']
|
|
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]
|