bootstack 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bootstack/__init__.py +157 -0
- bootstack/__main__.py +5 -0
- bootstack/_core/__init__.py +21 -0
- bootstack/_core/capabilities/__init__.py +45 -0
- bootstack/_core/capabilities/after.py +103 -0
- bootstack/_core/capabilities/bind.py +192 -0
- bootstack/_core/capabilities/bindtags.py +112 -0
- bootstack/_core/capabilities/busy.py +72 -0
- bootstack/_core/capabilities/clipboard.py +89 -0
- bootstack/_core/capabilities/focus.py +118 -0
- bootstack/_core/capabilities/grab.py +65 -0
- bootstack/_core/capabilities/grid.py +211 -0
- bootstack/_core/capabilities/localization.py +231 -0
- bootstack/_core/capabilities/pack.py +140 -0
- bootstack/_core/capabilities/place.py +113 -0
- bootstack/_core/capabilities/selection.py +136 -0
- bootstack/_core/capabilities/signals.py +244 -0
- bootstack/_core/capabilities/winfo.py +315 -0
- bootstack/_core/colorutils.py +234 -0
- bootstack/_core/exceptions.py +25 -0
- bootstack/_core/images.py +463 -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/paths.py +49 -0
- bootstack/_core/publisher.py +149 -0
- bootstack/_core/variables.py +62 -0
- bootstack/_runtime/__init__.py +3 -0
- bootstack/_runtime/app.py +930 -0
- bootstack/_runtime/base_window.py +945 -0
- bootstack/_runtime/events.py +399 -0
- bootstack/_runtime/shortcuts.py +496 -0
- bootstack/_runtime/tk_patch.py +43 -0
- bootstack/_runtime/toplevel.py +276 -0
- bootstack/_runtime/utility.py +457 -0
- bootstack/_runtime/visual_focus.py +240 -0
- bootstack/_runtime/window_utilities.py +1123 -0
- bootstack/assets/__init__.py +21 -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/button-compact.png +0 -0
- bootstack/assets/elements/button-default.png +0 -0
- bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
- bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
- bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
- bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
- bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
- bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
- bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
- bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
- bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
- bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
- bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
- bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
- bootstack/assets/elements/card.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-addon-compact.png +0 -0
- bootstack/assets/elements/input-addon-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/listrow-compact.png +0 -0
- bootstack/assets/elements/listrow-default.png +0 -0
- bootstack/assets/elements/manifest.toml +361 -0
- bootstack/assets/elements/menu-item.png +0 -0
- bootstack/assets/elements/navitem-compact.png +0 -0
- bootstack/assets/elements/navitem-default.png +0 -0
- bootstack/assets/elements/progressbar-h-compact.png +0 -0
- bootstack/assets/elements/progressbar-h-default.png +0 -0
- bootstack/assets/elements/progressbar-v-compact.png +0 -0
- bootstack/assets/elements/progressbar-v-default.png +0 -0
- bootstack/assets/elements/radiobutton.png +0 -0
- bootstack/assets/elements/scrollbar-horizontal.png +0 -0
- bootstack/assets/elements/scrollbar-vertical.png +0 -0
- bootstack/assets/elements/slider-handle.png +0 -0
- bootstack/assets/elements/slider-track-h.png +0 -0
- bootstack/assets/elements/slider-track-v.png +0 -0
- bootstack/assets/elements/switch-off.png +0 -0
- bootstack/assets/elements/switch-on.png +0 -0
- bootstack/assets/elements/tab-h.png +0 -0
- bootstack/assets/elements/tab-v.png +0 -0
- bootstack/assets/icons/bootstrap.ttf +0 -0
- bootstack/assets/icons/glyphmap.json +2080 -0
- bootstack/assets/icons/icon_metrics.json +12470 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +878 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +878 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +854 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +844 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +917 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +844 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +844 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +845 -0
- bootstack/cli/__init__.py +133 -0
- bootstack/cli/__main__.py +6 -0
- bootstack/cli/add.py +395 -0
- bootstack/cli/appicon.py +285 -0
- bootstack/cli/build.py +115 -0
- bootstack/cli/config.py +313 -0
- bootstack/cli/demo.py +564 -0
- bootstack/cli/dev.py +153 -0
- bootstack/cli/doctor.py +195 -0
- bootstack/cli/icons.py +98 -0
- bootstack/cli/promote.py +120 -0
- bootstack/cli/pyinstaller.py +268 -0
- bootstack/cli/run.py +95 -0
- bootstack/cli/start.py +117 -0
- bootstack/cli/templates/__init__.py +931 -0
- bootstack/clipboard.py +48 -0
- bootstack/constants.py +318 -0
- bootstack/data/README.md +615 -0
- bootstack/data/__init__.py +78 -0
- bootstack/data/_observable.py +276 -0
- bootstack/data/base.py +780 -0
- bootstack/data/file_source.py +367 -0
- bootstack/data/memory_source.py +450 -0
- bootstack/data/query.py +367 -0
- bootstack/data/readers.py +289 -0
- bootstack/data/sqlite_source.py +869 -0
- bootstack/data/types.py +354 -0
- bootstack/data/writers.py +232 -0
- bootstack/dev/__init__.py +36 -0
- bootstack/dev/_capture.py +141 -0
- bootstack/dev/_env.py +43 -0
- bootstack/dev/_registry.py +140 -0
- bootstack/dev/_reloader.py +351 -0
- bootstack/dev/_reset.py +50 -0
- bootstack/dev/_watcher.py +91 -0
- bootstack/dialogs/__init__.py +948 -0
- bootstack/dialogs/_impl/__init__.py +47 -0
- bootstack/dialogs/_impl/colorchooser.py +588 -0
- bootstack/dialogs/_impl/datedialog.py +450 -0
- bootstack/dialogs/_impl/dialog.py +594 -0
- bootstack/dialogs/_impl/filterdialog.py +358 -0
- bootstack/dialogs/_impl/fontdialog.py +364 -0
- bootstack/dialogs/_impl/formdialog.py +564 -0
- bootstack/dialogs/_impl/message.py +486 -0
- bootstack/dialogs/_impl/query.py +570 -0
- bootstack/errors.py +67 -0
- bootstack/events/__init__.py +111 -0
- bootstack/events/_event.py +135 -0
- bootstack/events/_payloads.py +539 -0
- bootstack/events/_subscription.py +38 -0
- bootstack/i18n/README.md +77 -0
- bootstack/i18n/__init__.py +23 -0
- bootstack/i18n/catalog.py +121 -0
- bootstack/i18n/intl_format.py +584 -0
- bootstack/i18n/msgcat.py +425 -0
- bootstack/i18n/specs.py +156 -0
- bootstack/images.py +563 -0
- bootstack/py.typed +1 -0
- bootstack/scheduling/__init__.py +11 -0
- bootstack/scheduling/_schedule.py +218 -0
- bootstack/shortcuts.py +21 -0
- bootstack/signals/README.md +98 -0
- bootstack/signals/__init__.py +10 -0
- bootstack/signals/integration.py +100 -0
- bootstack/signals/signal.py +353 -0
- bootstack/signals/types.py +6 -0
- bootstack/store.py +286 -0
- bootstack/streams/__init__.py +12 -0
- bootstack/streams/_stream.py +321 -0
- bootstack/style/__init__.py +38 -0
- bootstack/style/builders/__init__.py +51 -0
- bootstack/style/builders/badge.py +46 -0
- bootstack/style/builders/button.py +339 -0
- bootstack/style/builders/buttongroup.py +311 -0
- bootstack/style/builders/calendar.py +271 -0
- bootstack/style/builders/checkbutton.py +110 -0
- bootstack/style/builders/combobox.py +113 -0
- bootstack/style/builders/contextmenu.py +268 -0
- bootstack/style/builders/entry.py +82 -0
- bootstack/style/builders/expander.py +148 -0
- bootstack/style/builders/field.py +335 -0
- bootstack/style/builders/frame.py +50 -0
- bootstack/style/builders/label.py +28 -0
- bootstack/style/builders/labelframe.py +34 -0
- bootstack/style/builders/listview.py +369 -0
- bootstack/style/builders/menubar.py +91 -0
- bootstack/style/builders/menubutton.py +359 -0
- bootstack/style/builders/panedwindow.py +25 -0
- bootstack/style/builders/progressbar.py +67 -0
- bootstack/style/builders/radiobutton.py +99 -0
- bootstack/style/builders/scale.py +64 -0
- bootstack/style/builders/scrollbar.py +225 -0
- bootstack/style/builders/separator.py +49 -0
- bootstack/style/builders/sidenav.py +643 -0
- bootstack/style/builders/sizegrip.py +15 -0
- bootstack/style/builders/spinbox.py +119 -0
- bootstack/style/builders/switch.py +70 -0
- bootstack/style/builders/tabitem.py +204 -0
- bootstack/style/builders/togglegroup.py +294 -0
- bootstack/style/builders/toolbutton.py +275 -0
- bootstack/style/builders/tooltip.py +26 -0
- bootstack/style/builders/treeview.py +193 -0
- bootstack/style/builders/utils.py +455 -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/fonts.py +123 -0
- bootstack/style/style.py +609 -0
- bootstack/style/style_builder_base.py +716 -0
- bootstack/style/style_builder_mixed.py +93 -0
- bootstack/style/style_builder_tk.py +109 -0
- bootstack/style/style_builder_ttk.py +353 -0
- bootstack/style/style_resolver.py +447 -0
- bootstack/style/theme.py +245 -0
- bootstack/style/theme_provider.py +471 -0
- bootstack/style/themes/__init__.py +128 -0
- bootstack/style/tk_patch.py +5 -0
- bootstack/style/token_maps.py +41 -0
- bootstack/style/types.py +32 -0
- bootstack/style/typography.py +523 -0
- bootstack/style/utility.py +746 -0
- bootstack/types.py +39 -0
- bootstack/validation/__init__.py +6 -0
- bootstack/validation/types.py +15 -0
- bootstack/validation/validation_result.py +17 -0
- bootstack/validation/validation_rules.py +205 -0
- bootstack/widgets/__init__.py +74 -0
- bootstack/widgets/_core/__init__.py +31 -0
- bootstack/widgets/_core/app_config.py +266 -0
- bootstack/widgets/_core/base.py +461 -0
- bootstack/widgets/_core/container.py +334 -0
- bootstack/widgets/_core/context.py +35 -0
- bootstack/widgets/_core/events.py +130 -0
- bootstack/widgets/_core/field_mixin.py +353 -0
- bootstack/widgets/_core/icon_image_props.py +72 -0
- bootstack/widgets/_core/image_binding.py +93 -0
- bootstack/widgets/_core/navmodel.py +345 -0
- bootstack/widgets/_core/options.py +210 -0
- bootstack/widgets/_core/selection_group.py +130 -0
- bootstack/widgets/_core/window_controls.py +96 -0
- bootstack/widgets/_core/window_menu.py +246 -0
- bootstack/widgets/_impl/__init__.py +1 -0
- bootstack/widgets/_impl/_internal/__init__.py +0 -0
- bootstack/widgets/_impl/_internal/wrapper_base.py +307 -0
- bootstack/widgets/_impl/_parts/__init__.py +11 -0
- bootstack/widgets/_impl/_parts/numberentry_part.py +385 -0
- bootstack/widgets/_impl/_parts/spinnerentry_part.py +434 -0
- bootstack/widgets/_impl/_parts/textentry_part.py +406 -0
- bootstack/widgets/_impl/composites/__init__.py +33 -0
- bootstack/widgets/_impl/composites/_dateutils.py +33 -0
- bootstack/widgets/_impl/composites/_image_fit.py +105 -0
- bootstack/widgets/_impl/composites/accordion.py +381 -0
- bootstack/widgets/_impl/composites/avatar.py +191 -0
- bootstack/widgets/_impl/composites/buttongroup.py +371 -0
- bootstack/widgets/_impl/composites/calendar.py +972 -0
- bootstack/widgets/_impl/composites/carousel.py +567 -0
- bootstack/widgets/_impl/composites/chart.py +882 -0
- bootstack/widgets/_impl/composites/compositeframe.py +298 -0
- bootstack/widgets/_impl/composites/contextmenu.py +1951 -0
- bootstack/widgets/_impl/composites/dateentry.py +404 -0
- bootstack/widgets/_impl/composites/dropdownbutton.py +325 -0
- bootstack/widgets/_impl/composites/expander.py +515 -0
- bootstack/widgets/_impl/composites/field.py +670 -0
- bootstack/widgets/_impl/composites/form.py +1066 -0
- bootstack/widgets/_impl/composites/gallery.py +551 -0
- bootstack/widgets/_impl/composites/list/__init__.py +15 -0
- bootstack/widgets/_impl/composites/list/listitem.py +802 -0
- bootstack/widgets/_impl/composites/list/listview.py +1433 -0
- bootstack/widgets/_impl/composites/menu/__init__.py +18 -0
- bootstack/widgets/_impl/composites/menu/model.py +358 -0
- bootstack/widgets/_impl/composites/menu/render_native.py +134 -0
- bootstack/widgets/_impl/composites/menu/render_themed.py +134 -0
- bootstack/widgets/_impl/composites/meter.py +860 -0
- bootstack/widgets/_impl/composites/numericentry.py +201 -0
- bootstack/widgets/_impl/composites/pagestack.py +395 -0
- bootstack/widgets/_impl/composites/passwordentry.py +142 -0
- bootstack/widgets/_impl/composites/pathentry.py +168 -0
- bootstack/widgets/_impl/composites/picture.py +289 -0
- bootstack/widgets/_impl/composites/radiogroup.py +511 -0
- bootstack/widgets/_impl/composites/scrolledtext.py +375 -0
- bootstack/widgets/_impl/composites/scrolledtext.pyi +186 -0
- bootstack/widgets/_impl/composites/scrollview.py +764 -0
- bootstack/widgets/_impl/composites/selectbox.py +1026 -0
- bootstack/widgets/_impl/composites/shell/__init__.py +40 -0
- bootstack/widgets/_impl/composites/shell/content_host.py +52 -0
- bootstack/widgets/_impl/composites/shell/layout.py +335 -0
- bootstack/widgets/_impl/composites/shell/nav_panel.py +345 -0
- bootstack/widgets/_impl/composites/shell/providers.py +558 -0
- bootstack/widgets/_impl/composites/shell/rail.py +117 -0
- bootstack/widgets/_impl/composites/shell/shell.py +581 -0
- bootstack/widgets/_impl/composites/shell/workspace.py +273 -0
- bootstack/widgets/_impl/composites/sidenav/__init__.py +16 -0
- bootstack/widgets/_impl/composites/sidenav/header.py +81 -0
- bootstack/widgets/_impl/composites/sidenav/separator.py +44 -0
- bootstack/widgets/_impl/composites/slider/__init__.py +7 -0
- bootstack/widgets/_impl/composites/slider/_shared.py +195 -0
- bootstack/widgets/_impl/composites/slider/rangeslider.py +982 -0
- bootstack/widgets/_impl/composites/slider/slider.py +851 -0
- bootstack/widgets/_impl/composites/spinnerentry.py +185 -0
- bootstack/widgets/_impl/composites/tableview/__init__.py +5 -0
- bootstack/widgets/_impl/composites/tableview/tableview.py +3388 -0
- bootstack/widgets/_impl/composites/tableview/types.py +169 -0
- bootstack/widgets/_impl/composites/tabs/__init__.py +23 -0
- bootstack/widgets/_impl/composites/tabs/tabitem.py +389 -0
- bootstack/widgets/_impl/composites/tabs/tabs.py +974 -0
- bootstack/widgets/_impl/composites/tabs/tabview.py +650 -0
- bootstack/widgets/_impl/composites/textarea/__init__.py +26 -0
- bootstack/widgets/_impl/composites/textarea/change.py +46 -0
- bootstack/widgets/_impl/composites/textarea/codeeditor.py +526 -0
- bootstack/widgets/_impl/composites/textarea/core.py +495 -0
- bootstack/widgets/_impl/composites/textarea/decoration.py +42 -0
- bootstack/widgets/_impl/composites/textarea/diff.py +127 -0
- bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +1 -0
- bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +138 -0
- bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +158 -0
- bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +138 -0
- bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +312 -0
- bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +177 -0
- bootstack/widgets/_impl/composites/textarea/filter.py +171 -0
- bootstack/widgets/_impl/composites/textarea/search_overlay.py +459 -0
- bootstack/widgets/_impl/composites/textarea/sidebar.py +88 -0
- bootstack/widgets/_impl/composites/textarea/style_registry.py +178 -0
- bootstack/widgets/_impl/composites/textarea/textarea.py +606 -0
- bootstack/widgets/_impl/composites/textarea/undo.py +217 -0
- bootstack/widgets/_impl/composites/textentry.py +57 -0
- bootstack/widgets/_impl/composites/timeentry.py +176 -0
- bootstack/widgets/_impl/composites/toast.py +390 -0
- bootstack/widgets/_impl/composites/toast_stack.py +156 -0
- bootstack/widgets/_impl/composites/togglegroup.py +418 -0
- bootstack/widgets/_impl/composites/toolbar.py +608 -0
- bootstack/widgets/_impl/composites/tooltip.py +491 -0
- bootstack/widgets/_impl/composites/tree/__init__.py +7 -0
- bootstack/widgets/_impl/composites/tree/source_binding.py +136 -0
- bootstack/widgets/_impl/composites/tree/treeitem.py +393 -0
- bootstack/widgets/_impl/composites/tree/treenode.py +174 -0
- bootstack/widgets/_impl/composites/tree/treeview.py +841 -0
- bootstack/widgets/_impl/mixins/__init__.py +24 -0
- bootstack/widgets/_impl/mixins/configure_mixin.py +216 -0
- bootstack/widgets/_impl/mixins/entry_mixin.py +134 -0
- bootstack/widgets/_impl/mixins/font_mixin.py +368 -0
- bootstack/widgets/_impl/mixins/icon_mixin.py +61 -0
- bootstack/widgets/_impl/mixins/localization_mixin.py +253 -0
- bootstack/widgets/_impl/mixins/signal_mixin.py +268 -0
- bootstack/widgets/_impl/mixins/validation_mixin.py +226 -0
- bootstack/widgets/_impl/primitives/__init__.py +49 -0
- bootstack/widgets/_impl/primitives/_menubutton.py +107 -0
- bootstack/widgets/_impl/primitives/badge.py +45 -0
- bootstack/widgets/_impl/primitives/button.py +76 -0
- bootstack/widgets/_impl/primitives/card.py +45 -0
- bootstack/widgets/_impl/primitives/checkbutton.py +124 -0
- bootstack/widgets/_impl/primitives/checktoggle.py +62 -0
- bootstack/widgets/_impl/primitives/combobox.py +156 -0
- bootstack/widgets/_impl/primitives/entry.py +87 -0
- bootstack/widgets/_impl/primitives/flexframe.py +448 -0
- bootstack/widgets/_impl/primitives/frame.py +185 -0
- bootstack/widgets/_impl/primitives/gridframe.py +546 -0
- bootstack/widgets/_impl/primitives/label.py +84 -0
- bootstack/widgets/_impl/primitives/labelframe.py +54 -0
- bootstack/widgets/_impl/primitives/optionmenu.py +387 -0
- bootstack/widgets/_impl/primitives/packframe.py +227 -0
- bootstack/widgets/_impl/primitives/panedwindow.py +45 -0
- bootstack/widgets/_impl/primitives/progressbar.py +83 -0
- bootstack/widgets/_impl/primitives/radiobutton.py +115 -0
- bootstack/widgets/_impl/primitives/radiotoggle.py +54 -0
- bootstack/widgets/_impl/primitives/scrollbar.py +42 -0
- bootstack/widgets/_impl/primitives/separator.py +43 -0
- bootstack/widgets/_impl/primitives/sizegrip.py +33 -0
- bootstack/widgets/_impl/primitives/spinbox.py +95 -0
- bootstack/widgets/_impl/primitives/switch.py +44 -0
- bootstack/widgets/_impl/primitives/treeview.py +69 -0
- bootstack/widgets/app.py +371 -0
- bootstack/widgets/appshell.py +1179 -0
- bootstack/widgets/avatar.py +140 -0
- bootstack/widgets/boolean_controls.py +455 -0
- bootstack/widgets/button.py +224 -0
- bootstack/widgets/buttongroup.py +239 -0
- bootstack/widgets/calendar.py +195 -0
- bootstack/widgets/card.py +159 -0
- bootstack/widgets/carousel.py +241 -0
- bootstack/widgets/chart.py +302 -0
- bootstack/widgets/codeeditor.py +675 -0
- bootstack/widgets/contextmenu.py +371 -0
- bootstack/widgets/datatable.py +688 -0
- bootstack/widgets/datefield.py +395 -0
- bootstack/widgets/divider.py +60 -0
- bootstack/widgets/expander.py +579 -0
- bootstack/widgets/form.py +200 -0
- bootstack/widgets/gallery.py +250 -0
- bootstack/widgets/gauge.py +168 -0
- bootstack/widgets/grid.py +121 -0
- bootstack/widgets/groupbox.py +162 -0
- bootstack/widgets/label.py +229 -0
- bootstack/widgets/listview.py +354 -0
- bootstack/widgets/menubutton.py +419 -0
- bootstack/widgets/numberfield.py +431 -0
- bootstack/widgets/pagestack.py +345 -0
- bootstack/widgets/passwordfield.py +383 -0
- bootstack/widgets/pathfield.py +454 -0
- bootstack/widgets/picture.py +251 -0
- bootstack/widgets/progressbar.py +106 -0
- bootstack/widgets/radio_variants.py +271 -0
- bootstack/widgets/radiogroup.py +228 -0
- bootstack/widgets/scrollbar.py +64 -0
- bootstack/widgets/scrollview.py +186 -0
- bootstack/widgets/select.py +302 -0
- bootstack/widgets/selectbutton.py +182 -0
- bootstack/widgets/sidebar_toggle.py +130 -0
- bootstack/widgets/sizegrip.py +42 -0
- bootstack/widgets/slider.py +408 -0
- bootstack/widgets/spinbox.py +147 -0
- bootstack/widgets/spinnerfield.py +413 -0
- bootstack/widgets/splash.py +367 -0
- bootstack/widgets/splitview.py +482 -0
- bootstack/widgets/stacks.py +249 -0
- bootstack/widgets/statusbar.py +189 -0
- bootstack/widgets/tabs.py +425 -0
- bootstack/widgets/textarea.py +459 -0
- bootstack/widgets/textfield.py +410 -0
- bootstack/widgets/theme_toggle.py +92 -0
- bootstack/widgets/timefield.py +340 -0
- bootstack/widgets/toast.py +314 -0
- bootstack/widgets/togglegroup.py +231 -0
- bootstack/widgets/toolbar.py +347 -0
- bootstack/widgets/tooltip.py +94 -0
- bootstack/widgets/tree.py +648 -0
- bootstack/widgets/types.py +305 -0
- bootstack/widgets/window.py +307 -0
- bootstack-0.1.0.dist-info/METADATA +301 -0
- bootstack-0.1.0.dist-info/RECORD +471 -0
- bootstack-0.1.0.dist-info/WHEEL +5 -0
- bootstack-0.1.0.dist-info/entry_points.txt +2 -0
- bootstack-0.1.0.dist-info/licenses/LICENSE +21 -0
- bootstack-0.1.0.dist-info/licenses/NOTICE +10 -0
- bootstack-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
"""Style builders for SideNav widget components.
|
|
2
|
+
|
|
3
|
+
This module provides navigation-specific styling with selection indicator bars.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from bootstack.style.style_builder_ttk import StyleBuilderTtk
|
|
11
|
+
from bootstack.style.element import Element, ElementImage
|
|
12
|
+
from bootstack.style.utility import recolor_element_image, create_transparent_image, mix_colors
|
|
13
|
+
from bootstack.style.builders.utils import apply_icon_mapping, resolve_icon_spec
|
|
14
|
+
from bootstack.style.builders.toolbutton import (
|
|
15
|
+
toolbutton_layout, button_padding, button_font, icon_size, normalize_button_density
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@StyleBuilderTtk.register_builder('default', 'NavigationView.TFrame')
|
|
20
|
+
def build_navigationview_frame_style(b: StyleBuilderTtk, ttk_style: str, accent: Optional[str] = None, **options):
|
|
21
|
+
"""Build SideNav frame style for group expanders.
|
|
22
|
+
|
|
23
|
+
Uses standard button assets (no selection indicator needed since
|
|
24
|
+
groups don't get selected, only their child items do).
|
|
25
|
+
"""
|
|
26
|
+
accent_token = accent or 'primary'
|
|
27
|
+
surface_token = options.get('surface', 'content')
|
|
28
|
+
density = options.get('density', 'default')
|
|
29
|
+
image_key = f'button_{density}'
|
|
30
|
+
|
|
31
|
+
surface = b.color(surface_token)
|
|
32
|
+
surface_hover = b.color(f'{surface_token}_hover') if b.colors.get(f'{surface_token}_hover') else b.active(surface)
|
|
33
|
+
surface_pressed = b.pressed(surface_hover)
|
|
34
|
+
|
|
35
|
+
disabled = b.disabled()
|
|
36
|
+
|
|
37
|
+
normal_img = recolor_element_image(image_key, surface, surface, surface, surface)
|
|
38
|
+
hover_img = recolor_element_image(image_key, surface_hover, surface_hover, surface, surface)
|
|
39
|
+
pressed_img = recolor_element_image(image_key, surface_pressed, surface_pressed, surface, surface)
|
|
40
|
+
disabled_img = recolor_element_image(image_key, disabled, disabled, surface, surface)
|
|
41
|
+
|
|
42
|
+
b.create_style_element_image(
|
|
43
|
+
ElementImage(
|
|
44
|
+
f'{ttk_style}.border', normal_img.image, sticky="nsew",
|
|
45
|
+
border=normal_img.meta.border, padding=normal_img.meta.border
|
|
46
|
+
).state_specs(
|
|
47
|
+
[
|
|
48
|
+
('disabled', disabled_img.image),
|
|
49
|
+
('pressed', pressed_img.image),
|
|
50
|
+
('hover', hover_img.image),
|
|
51
|
+
('', normal_img.image)
|
|
52
|
+
]))
|
|
53
|
+
|
|
54
|
+
# Create layout to use the image-based border
|
|
55
|
+
b.create_style_layout(
|
|
56
|
+
ttk_style,
|
|
57
|
+
Element(f'{ttk_style}.border', sticky="nsew")
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
b.configure_style(
|
|
61
|
+
ttk_style,
|
|
62
|
+
background=surface,
|
|
63
|
+
relief='flat',
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@StyleBuilderTtk.register_builder('default', 'NavigationView.TLabel')
|
|
68
|
+
def build_navigationview_label_style(b: StyleBuilderTtk, ttk_style: str, accent: str = None, **options):
|
|
69
|
+
"""Build SideNav label style with state-aware foreground colors.
|
|
70
|
+
|
|
71
|
+
Matches the ghost Toolbutton foreground color behavior.
|
|
72
|
+
"""
|
|
73
|
+
accent_token = accent or 'primary'
|
|
74
|
+
surface_token = options.get('surface', 'content')
|
|
75
|
+
|
|
76
|
+
surface = b.color(surface_token)
|
|
77
|
+
surface_hover = b.color(f'{surface_token}_hover') if b.colors.get(f'{surface_token}_hover') else b.active(surface)
|
|
78
|
+
surface_pressed = b.pressed(surface_hover)
|
|
79
|
+
on_surface = b.on_color(surface)
|
|
80
|
+
|
|
81
|
+
accent_color = b.color(accent_token)
|
|
82
|
+
active = b.subtle(accent_token, surface)
|
|
83
|
+
|
|
84
|
+
on_disabled = b.disabled('text', surface)
|
|
85
|
+
|
|
86
|
+
b.configure_style(
|
|
87
|
+
ttk_style,
|
|
88
|
+
background=surface,
|
|
89
|
+
foreground=on_surface,
|
|
90
|
+
relief='flat',
|
|
91
|
+
font='body'
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
foreground_state_map = [
|
|
95
|
+
('disabled', on_disabled),
|
|
96
|
+
('selected', accent_color),
|
|
97
|
+
('', on_surface)
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
background_state_map = [
|
|
101
|
+
('selected', active),
|
|
102
|
+
('disabled', surface),
|
|
103
|
+
('pressed !selected', surface_pressed),
|
|
104
|
+
('hover !selected', surface_hover),
|
|
105
|
+
('', surface)
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
state_spec = dict(foreground=foreground_state_map, background=background_state_map)
|
|
109
|
+
state_spec = apply_icon_mapping(b, options, state_spec)
|
|
110
|
+
|
|
111
|
+
b.map_style(ttk_style, **state_spec)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@StyleBuilderTtk.register_builder('navigation', 'Toolbutton')
|
|
115
|
+
def build_navigation_toolbutton_style(b: StyleBuilderTtk, ttk_style: str, accent: str = None, **options):
|
|
116
|
+
"""Build navigation Toolbutton style with selection indicator.
|
|
117
|
+
|
|
118
|
+
Uses nav-button assets with a left-side selection indicator bar
|
|
119
|
+
that shows the accent color when selected.
|
|
120
|
+
"""
|
|
121
|
+
accent_token = accent or 'primary'
|
|
122
|
+
surface_token = options.get('surface', 'content')
|
|
123
|
+
density = options.get('density', 'default')
|
|
124
|
+
icon_only = options.get('icon_only', False)
|
|
125
|
+
anchor = options.get('anchor', 'center' if icon_only else 'w')
|
|
126
|
+
image_key = f'navitem_{normalize_button_density(density)}'
|
|
127
|
+
|
|
128
|
+
surface = b.color(surface_token)
|
|
129
|
+
surface_hover = b.color(f'{surface_token}_hover') if b.colors.get(f'{surface_token}_hover') else b.active(surface)
|
|
130
|
+
surface_pressed = b.pressed(surface_hover)
|
|
131
|
+
on_surface = b.on_color(surface)
|
|
132
|
+
|
|
133
|
+
active = b.subtle(accent_token, surface)
|
|
134
|
+
accent_color = b.color(accent_token)
|
|
135
|
+
accent_pressed = b.pressed(active)
|
|
136
|
+
|
|
137
|
+
disabled = b.disabled()
|
|
138
|
+
on_disabled = b.disabled('text', disabled)
|
|
139
|
+
|
|
140
|
+
# Normal states: indicator hidden (same color as button background)
|
|
141
|
+
normal_img = recolor_element_image(image_key, surface, surface, surface, surface)
|
|
142
|
+
hover_img = recolor_element_image(image_key, surface_hover, surface_hover, surface_hover, surface)
|
|
143
|
+
pressed_img = recolor_element_image(image_key, surface_pressed, surface_pressed, surface_pressed, surface)
|
|
144
|
+
|
|
145
|
+
# Selected states: in compact/icon-only mode the indicator matches the fill so it's invisible
|
|
146
|
+
if icon_only:
|
|
147
|
+
selected_img = recolor_element_image(image_key, active, active, active, surface)
|
|
148
|
+
selected_hover_img = recolor_element_image(image_key, active, active, active, surface)
|
|
149
|
+
selected_pressed_img = recolor_element_image(image_key, accent_pressed, accent_pressed, accent_pressed, surface)
|
|
150
|
+
else:
|
|
151
|
+
selected_img = recolor_element_image(image_key, active, active, accent_color, surface)
|
|
152
|
+
selected_hover_img = recolor_element_image(image_key, active, active, accent_color, surface)
|
|
153
|
+
selected_pressed_img = recolor_element_image(image_key, accent_pressed, accent_pressed, accent_color, surface)
|
|
154
|
+
|
|
155
|
+
disabled_img = recolor_element_image(image_key, disabled, disabled, disabled, surface)
|
|
156
|
+
|
|
157
|
+
b.create_style_element_image(
|
|
158
|
+
ElementImage(
|
|
159
|
+
f'{ttk_style}.border', normal_img.image, sticky="nsew",
|
|
160
|
+
border=normal_img.meta.border, padding=normal_img.meta.border
|
|
161
|
+
).state_specs(
|
|
162
|
+
[
|
|
163
|
+
('disabled', disabled_img.image),
|
|
164
|
+
('selected pressed', selected_pressed_img.image),
|
|
165
|
+
('selected hover', selected_hover_img.image),
|
|
166
|
+
('selected', selected_img.image),
|
|
167
|
+
('pressed', pressed_img.image),
|
|
168
|
+
('hover', hover_img.image),
|
|
169
|
+
('', normal_img.image)
|
|
170
|
+
]))
|
|
171
|
+
|
|
172
|
+
b.create_style_layout(
|
|
173
|
+
ttk_style,
|
|
174
|
+
toolbutton_layout(ttk_style),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
b.configure_style(
|
|
178
|
+
ttk_style,
|
|
179
|
+
background=surface,
|
|
180
|
+
foreground=on_surface,
|
|
181
|
+
relief='flat',
|
|
182
|
+
padding=button_padding(b, icon_only, density),
|
|
183
|
+
anchor=anchor,
|
|
184
|
+
font=button_font(density)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
state_spec = dict(
|
|
188
|
+
foreground=[
|
|
189
|
+
('disabled', on_disabled),
|
|
190
|
+
('selected', accent_color),
|
|
191
|
+
('', on_surface)
|
|
192
|
+
],
|
|
193
|
+
)
|
|
194
|
+
state_spec = apply_icon_mapping(b, options, state_spec, icon_size(icon_only, density))
|
|
195
|
+
|
|
196
|
+
b.map_style(ttk_style, **state_spec)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@StyleBuilderTtk.register_builder('rail', 'Toolbutton')
|
|
200
|
+
def build_rail_toolbutton_style(b: StyleBuilderTtk, ttk_style: str, accent: str = None, **options):
|
|
201
|
+
"""Build the workspace-rail item — the VS Code activity-bar treatment.
|
|
202
|
+
|
|
203
|
+
A radio toggle (single-select) rendered icon-only on the chrome surface:
|
|
204
|
+
the glyph is muted when idle and full-strength when selected (or hovered),
|
|
205
|
+
and the `navitem` asset's left indicator bar shows the **foreground** color
|
|
206
|
+
when selected (neutral by default — accent is opt-in, like the buttons). The
|
|
207
|
+
background stays flat in every state (no wash / no fill) — the bar carries the
|
|
208
|
+
selection.
|
|
209
|
+
"""
|
|
210
|
+
accent_token = accent # None -> neutral
|
|
211
|
+
surface_token = options.get('surface', 'chrome')
|
|
212
|
+
density = options.get('density', 'default')
|
|
213
|
+
icon_only = options.get('icon_only', True)
|
|
214
|
+
# Labeled rail: a small caption under the icon (compound='top' on the widget)
|
|
215
|
+
# instead of icon-only + tooltips. Dedicated variant, so we just flip the font.
|
|
216
|
+
labeled = options.get('labeled', False)
|
|
217
|
+
image_key = f'navitem_{normalize_button_density(density)}'
|
|
218
|
+
|
|
219
|
+
surface = b.color(surface_token)
|
|
220
|
+
surface_hover = b.color(f'{surface_token}_hover') if b.colors.get(f'{surface_token}_hover') else b.active(surface)
|
|
221
|
+
surface_pressed = b.pressed(surface_hover)
|
|
222
|
+
on_surface = b.on_color(surface)
|
|
223
|
+
muted = b.muted_foreground(surface)
|
|
224
|
+
# Neutral indicator bar by default (the foreground); when an accent is set
|
|
225
|
+
# (`nav_accent`), the bar shows it.
|
|
226
|
+
bar_color = b.color(accent_token) if accent_token else on_surface
|
|
227
|
+
disabled = b.disabled()
|
|
228
|
+
on_disabled = b.disabled('text', disabled)
|
|
229
|
+
|
|
230
|
+
# Flat background throughout; the left bar is hidden (== fill) unless selected.
|
|
231
|
+
normal_img = recolor_element_image(image_key, surface, surface, surface, surface)
|
|
232
|
+
hover_img = recolor_element_image(image_key, surface_hover, surface_hover, surface_hover, surface)
|
|
233
|
+
pressed_img = recolor_element_image(image_key, surface_pressed, surface_pressed, surface_pressed, surface)
|
|
234
|
+
# Selected: foreground bar visible, background still flat (no wash).
|
|
235
|
+
selected_img = recolor_element_image(image_key, surface, surface, bar_color, surface)
|
|
236
|
+
selected_hover_img = recolor_element_image(image_key, surface_hover, surface_hover, bar_color, surface)
|
|
237
|
+
selected_pressed_img = recolor_element_image(image_key, surface_pressed, surface_pressed, bar_color, surface)
|
|
238
|
+
disabled_img = recolor_element_image(image_key, disabled, disabled, surface, surface)
|
|
239
|
+
|
|
240
|
+
b.create_style_element_image(
|
|
241
|
+
ElementImage(
|
|
242
|
+
f'{ttk_style}.border', normal_img.image, sticky="nsew",
|
|
243
|
+
border=normal_img.meta.border, padding=normal_img.meta.border
|
|
244
|
+
).state_specs(
|
|
245
|
+
[
|
|
246
|
+
('disabled', disabled_img.image),
|
|
247
|
+
('selected pressed', selected_pressed_img.image),
|
|
248
|
+
('selected hover', selected_hover_img.image),
|
|
249
|
+
('selected', selected_img.image),
|
|
250
|
+
('pressed', pressed_img.image),
|
|
251
|
+
('hover', hover_img.image),
|
|
252
|
+
('', normal_img.image)
|
|
253
|
+
]))
|
|
254
|
+
|
|
255
|
+
b.create_style_layout(ttk_style, toolbutton_layout(ttk_style))
|
|
256
|
+
|
|
257
|
+
# Rail items are tall, near-square tap targets (VS Code activity bar), not the
|
|
258
|
+
# icon-height of a normal icon-only toolbutton. Generous vertical padding +
|
|
259
|
+
# anchor center gives the larger hit area and vertical rhythm. Labeled rails
|
|
260
|
+
# use a smaller caption font and a touch more room for the stacked label.
|
|
261
|
+
if labeled:
|
|
262
|
+
rail_font = 'caption'
|
|
263
|
+
rail_padding = options.get('padding') or b.scale((2, 8, 2, 8))
|
|
264
|
+
else:
|
|
265
|
+
rail_font = button_font(density)
|
|
266
|
+
rail_padding = options.get('padding') or b.scale((0, 7, 0, 7))
|
|
267
|
+
|
|
268
|
+
b.configure_style(
|
|
269
|
+
ttk_style,
|
|
270
|
+
background=surface,
|
|
271
|
+
foreground=muted,
|
|
272
|
+
relief='flat',
|
|
273
|
+
padding=rail_padding,
|
|
274
|
+
anchor='center',
|
|
275
|
+
font=rail_font,
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
state_spec = dict(
|
|
279
|
+
foreground=[
|
|
280
|
+
('disabled', on_disabled),
|
|
281
|
+
('selected', on_surface),
|
|
282
|
+
('hover', on_surface),
|
|
283
|
+
('', muted),
|
|
284
|
+
]
|
|
285
|
+
)
|
|
286
|
+
state_spec = apply_icon_mapping(b, options, state_spec, icon_size(icon_only, density))
|
|
287
|
+
|
|
288
|
+
b.map_style(ttk_style, **state_spec)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
# Sidebar nav item geometry. The icon is its OWN layout element so a real spacer
|
|
292
|
+
# can sit between it and the label — ttk gives no control over the compound
|
|
293
|
+
# image<->text gap, so the icon must be separated out (like the radio indicator).
|
|
294
|
+
_NAV_ICON_SIZE = 20
|
|
295
|
+
# Icon-only (compact) buttons read better with a slightly larger glyph and a
|
|
296
|
+
# taller, squarer footprint than the labeled rows.
|
|
297
|
+
_NAV_COMPACT_ICON_SIZE = 24
|
|
298
|
+
_NAV_GAP = 10
|
|
299
|
+
# Nine-patch content inset for nav items. Kept well below the asset border (the
|
|
300
|
+
# slice) so items don't carry the asset's full built-in padding as dead space
|
|
301
|
+
# between buttons.
|
|
302
|
+
_NAV_CONTENT_PAD = 4
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _nav_colors(b, accent_token, options):
|
|
306
|
+
surface_token = options.get('surface', 'card')
|
|
307
|
+
surface = b.color(surface_token)
|
|
308
|
+
surface_hover = b.color(f'{surface_token}_hover') if b.colors.get(f'{surface_token}_hover') else b.active(surface)
|
|
309
|
+
surface_pressed = b.pressed(surface_hover)
|
|
310
|
+
on_surface = b.on_color(surface)
|
|
311
|
+
disabled = b.disabled()
|
|
312
|
+
on_disabled = b.disabled('text', disabled)
|
|
313
|
+
return surface, surface_hover, surface_pressed, on_surface, disabled, on_disabled
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _nav_border_element(b, ttk_style, image_key, *, surface, surface_hover, surface_pressed,
|
|
317
|
+
selected_bg, selected_pressed, disabled):
|
|
318
|
+
"""Create a nav item's flat background nine-patch (no indicator bar).
|
|
319
|
+
|
|
320
|
+
Channels are `(white=fill, black, magenta, transparent=surface)`. The edge
|
|
321
|
+
channels (black/magenta) must blend correctly per asset:
|
|
322
|
+
|
|
323
|
+
- the rounded `button` asset (pill) uses black=border / magenta=focus-ring,
|
|
324
|
+
so both must match the **background** (surface) or the rounded edge fails to
|
|
325
|
+
anti-alias and the pill reads square/haloed;
|
|
326
|
+
- the square `navitem` asset (quiet) uses black as part of the fill and
|
|
327
|
+
magenta as the (here hidden) indicator bar, so both must match the **fill**.
|
|
328
|
+
|
|
329
|
+
The 4th channel (surface-behind) is always the surface so rounded corners
|
|
330
|
+
blend into the region.
|
|
331
|
+
"""
|
|
332
|
+
rounded = image_key.startswith('button')
|
|
333
|
+
|
|
334
|
+
def img(fill):
|
|
335
|
+
edge = surface if rounded else fill
|
|
336
|
+
return recolor_element_image(image_key, fill, edge, edge, surface)
|
|
337
|
+
|
|
338
|
+
normal_img = img(surface)
|
|
339
|
+
hover_img = img(surface_hover)
|
|
340
|
+
pressed_img = img(surface_pressed)
|
|
341
|
+
selected_img = img(selected_bg)
|
|
342
|
+
selected_hover_img = img(selected_bg)
|
|
343
|
+
selected_pressed_img = img(selected_pressed)
|
|
344
|
+
disabled_img = img(disabled)
|
|
345
|
+
# `border` is the nine-patch slice (for the rounded corners); `padding` is the
|
|
346
|
+
# asset's intended content inset (0 for navitem/card — no built-in dead space).
|
|
347
|
+
# The button's height therefore comes from the configure padding only; spacing
|
|
348
|
+
# BETWEEN items is the container's job (NavPanel pack).
|
|
349
|
+
b.create_style_element_image(
|
|
350
|
+
ElementImage(
|
|
351
|
+
f'{ttk_style}.border', normal_img.image, sticky="nsew",
|
|
352
|
+
border=normal_img.meta.border, padding=normal_img.meta.padding
|
|
353
|
+
).state_specs(
|
|
354
|
+
[
|
|
355
|
+
('disabled', disabled_img.image),
|
|
356
|
+
('selected pressed', selected_pressed_img.image),
|
|
357
|
+
('selected hover', selected_hover_img.image),
|
|
358
|
+
('selected', selected_img.image),
|
|
359
|
+
('pressed', pressed_img.image),
|
|
360
|
+
('hover', hover_img.image),
|
|
361
|
+
('', normal_img.image)
|
|
362
|
+
]))
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def _build_nav_expanded(b, ttk_style, *, accent_token, options, image_key, selected_bg, selected_fg):
|
|
366
|
+
"""Expanded nav item: icon element + spacer + text label (a real icon/text gap)."""
|
|
367
|
+
density = options.get('density', 'default')
|
|
368
|
+
surface, surface_hover, surface_pressed, on_surface, disabled, on_disabled = _nav_colors(b, accent_token, options)
|
|
369
|
+
# Idle items are slightly muted (a light blend toward the surface) so the
|
|
370
|
+
# selected item — and content headings — stay the brightest text. A gentle
|
|
371
|
+
# mute, not the heavy header mute, so idle rows still sit above the section
|
|
372
|
+
# headers in the 3-tier hierarchy (selected > idle > header).
|
|
373
|
+
idle_fg = mix_colors(on_surface, surface, 0.85)
|
|
374
|
+
_nav_border_element(
|
|
375
|
+
b, ttk_style, image_key, surface=surface, surface_hover=surface_hover,
|
|
376
|
+
surface_pressed=surface_pressed, selected_bg=selected_bg,
|
|
377
|
+
selected_pressed=b.pressed(selected_bg), disabled=disabled,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
fg_spec = [('disabled', on_disabled), ('selected', selected_fg), ('', idle_fg)]
|
|
381
|
+
|
|
382
|
+
# Icon as its own stateful element (recolored to follow the text foreground).
|
|
383
|
+
icon_spec = resolve_icon_spec(options)
|
|
384
|
+
has_icon = False
|
|
385
|
+
if icon_spec:
|
|
386
|
+
spec = b.normalize_icon_spec(icon_spec, _NAV_ICON_SIZE)
|
|
387
|
+
icon_map = b.map_stateful_icons(spec, fg_spec)
|
|
388
|
+
if icon_map:
|
|
389
|
+
has_icon = True
|
|
390
|
+
b.create_style_element_image(
|
|
391
|
+
ElementImage(f'{ttk_style}.icon', icon_map[-1][1], sticky="").state_specs(icon_map))
|
|
392
|
+
|
|
393
|
+
# Spacer between icon and text; collapses to ~0 in icon-only (the 'alternate'
|
|
394
|
+
# state) so the same layout also serves the compacted item.
|
|
395
|
+
gap = create_transparent_image(_NAV_GAP, 1)
|
|
396
|
+
gap0 = create_transparent_image(1, 1)
|
|
397
|
+
b.create_style_element_image(
|
|
398
|
+
ElementImage(f'{ttk_style}.spacer', gap, sticky="").state_specs([('alternate', gap0), ('', gap)]))
|
|
399
|
+
|
|
400
|
+
row = []
|
|
401
|
+
if has_icon:
|
|
402
|
+
row.append(Element(f'{ttk_style}.icon', side="left", sticky=""))
|
|
403
|
+
row.append(Element(f'{ttk_style}.spacer', side="left", sticky=""))
|
|
404
|
+
row.append(Element('Toolbutton.label', side="left", sticky="nsew"))
|
|
405
|
+
b.create_style_layout(
|
|
406
|
+
ttk_style,
|
|
407
|
+
Element(f'{ttk_style}.border', sticky="nsew").children(
|
|
408
|
+
[Element('Toolbutton.padding', sticky="nsew").children(row)]))
|
|
409
|
+
|
|
410
|
+
b.configure_style(
|
|
411
|
+
ttk_style, background=surface, foreground=idle_fg, relief='flat',
|
|
412
|
+
padding=options.get('padding') or b.scale((10, 8, 10, 8)),
|
|
413
|
+
anchor=options.get('anchor', 'w'), font=button_font(density),
|
|
414
|
+
)
|
|
415
|
+
# Only the text label's foreground here — the icon element carries its own.
|
|
416
|
+
b.map_style(ttk_style, foreground=fg_spec)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def _build_nav_compact(b, ttk_style, *, accent_token, options, image_key, selected_bg, selected_fg):
|
|
420
|
+
"""Compact nav item: a single centered icon (icon-only)."""
|
|
421
|
+
density = options.get('density', 'default')
|
|
422
|
+
surface, surface_hover, surface_pressed, on_surface, disabled, on_disabled = _nav_colors(b, accent_token, options)
|
|
423
|
+
idle_fg = mix_colors(on_surface, surface, 0.85) # slight idle mute (see _build_nav_expanded)
|
|
424
|
+
_nav_border_element(
|
|
425
|
+
b, ttk_style, image_key, surface=surface, surface_hover=surface_hover,
|
|
426
|
+
surface_pressed=surface_pressed, selected_bg=selected_bg,
|
|
427
|
+
selected_pressed=b.pressed(selected_bg), disabled=disabled,
|
|
428
|
+
)
|
|
429
|
+
b.create_style_layout(ttk_style, toolbutton_layout(ttk_style))
|
|
430
|
+
b.configure_style(
|
|
431
|
+
ttk_style, background=surface, foreground=idle_fg, relief='flat',
|
|
432
|
+
# No horizontal padding: the compact sidebar is only the rail width, and
|
|
433
|
+
# the icon centers via anchor; a bit more vertical padding makes the
|
|
434
|
+
# icon-only buttons taller/squarer.
|
|
435
|
+
padding=options.get('padding') or b.scale((0, 10, 0, 10)), anchor='center',
|
|
436
|
+
font=button_font(density),
|
|
437
|
+
)
|
|
438
|
+
state_spec = dict(foreground=[('disabled', on_disabled), ('selected', selected_fg), ('', idle_fg)])
|
|
439
|
+
state_spec = apply_icon_mapping(b, options, state_spec, _NAV_COMPACT_ICON_SIZE)
|
|
440
|
+
b.map_style(ttk_style, **state_spec)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _selection_colors(b, accent_token, options):
|
|
444
|
+
"""Selected nav-item (background, foreground), per accent + selection style.
|
|
445
|
+
|
|
446
|
+
Neutral by default (a light active-level wash + neutral fg). With an accent:
|
|
447
|
+
`'ghost'` (default) = a subtle accent wash + **full foreground** text;
|
|
448
|
+
`'solid'` = the solid accent filled + the on-accent (e.g. white) fg — the
|
|
449
|
+
higher-emphasis look. Solid needs an accent; without one falls back to neutral.
|
|
450
|
+
|
|
451
|
+
The ghost foreground is the full foreground (bright on dark, dark on light),
|
|
452
|
+
NOT the accent — accent text is too dim for low-luminance hues (blue) to pop
|
|
453
|
+
consistently. The accent identity instead lives in the wash + the indicator
|
|
454
|
+
bar, which read by hue regardless of luminance, so selection pops uniformly
|
|
455
|
+
across every accent. Idle items are muted, so the bright selected text stands
|
|
456
|
+
out (the prominence is luminance, not color).
|
|
457
|
+
"""
|
|
458
|
+
surface = b.color(options.get('surface', 'card'))
|
|
459
|
+
if accent_token:
|
|
460
|
+
if options.get('selection_style') == 'solid':
|
|
461
|
+
fill = b.color(accent_token)
|
|
462
|
+
return fill, b.on_color(fill)
|
|
463
|
+
return b.subtle(accent_token, surface), b.on_color(surface)
|
|
464
|
+
return b.active(surface), b.on_color(surface)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def _quiet_colors(b, accent_token, options):
|
|
468
|
+
return _selection_colors(b, accent_token, options)
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def _pill_colors(b, accent_token, options):
|
|
472
|
+
return _selection_colors(b, accent_token, options)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
@StyleBuilderTtk.register_builder('nav-quiet', 'Toolbutton')
|
|
476
|
+
def build_nav_quiet_toolbutton_style(b: StyleBuilderTtk, ttk_style: str, accent: str = None, **options):
|
|
477
|
+
"""Quiet sidebar nav item (under a rail): subtle wash, neutral fg, no bar."""
|
|
478
|
+
accent_token = accent # None -> neutral
|
|
479
|
+
image_key = f'navitem_{normalize_button_density(options.get("density", "default"))}'
|
|
480
|
+
sel_bg, sel_fg = _quiet_colors(b, accent_token, options)
|
|
481
|
+
_build_nav_expanded(b, ttk_style, accent_token=accent_token, options=options,
|
|
482
|
+
image_key=image_key, selected_bg=sel_bg, selected_fg=sel_fg)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
@StyleBuilderTtk.register_builder('nav-quiet-compact', 'Toolbutton')
|
|
486
|
+
def build_nav_quiet_compact_toolbutton_style(b: StyleBuilderTtk, ttk_style: str, accent: str = None, **options):
|
|
487
|
+
"""Quiet nav item compacted to a centered icon."""
|
|
488
|
+
accent_token = accent # None -> neutral
|
|
489
|
+
image_key = f'navitem_{normalize_button_density(options.get("density", "default"))}'
|
|
490
|
+
sel_bg, sel_fg = _quiet_colors(b, accent_token, options)
|
|
491
|
+
_build_nav_compact(b, ttk_style, accent_token=accent_token, options=options,
|
|
492
|
+
image_key=image_key, selected_bg=sel_bg, selected_fg=sel_fg)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
@StyleBuilderTtk.register_builder('nav-pill', 'Toolbutton')
|
|
496
|
+
def build_nav_pill_toolbutton_style(b: StyleBuilderTtk, ttk_style: str, accent: str = None, **options):
|
|
497
|
+
"""Standalone sidebar nav item: a rounded accent pill + accent fg (macOS vibe).
|
|
498
|
+
|
|
499
|
+
The primary nav (no rail): the selected row gets a rounded accent-tinted pill
|
|
500
|
+
(the `card` asset — black/white only, no focus-ring band) with accent
|
|
501
|
+
foreground. Distinct from the quiet under-a-rail treatment (square wash +
|
|
502
|
+
neutral fg).
|
|
503
|
+
"""
|
|
504
|
+
accent_token = accent # None -> neutral
|
|
505
|
+
image_key = 'card'
|
|
506
|
+
sel_bg, sel_fg = _pill_colors(b, accent_token, options)
|
|
507
|
+
_build_nav_expanded(b, ttk_style, accent_token=accent_token, options=options,
|
|
508
|
+
image_key=image_key, selected_bg=sel_bg, selected_fg=sel_fg)
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
@StyleBuilderTtk.register_builder('nav-pill-compact', 'Toolbutton')
|
|
512
|
+
def build_nav_pill_compact_toolbutton_style(b: StyleBuilderTtk, ttk_style: str, accent: str = None, **options):
|
|
513
|
+
"""Standalone nav item compacted to a centered icon (rounded pill on select)."""
|
|
514
|
+
accent_token = accent # None -> neutral
|
|
515
|
+
image_key = 'card'
|
|
516
|
+
sel_bg, sel_fg = _pill_colors(b, accent_token, options)
|
|
517
|
+
_build_nav_compact(b, ttk_style, accent_token=accent_token, options=options,
|
|
518
|
+
image_key=image_key, selected_bg=sel_bg, selected_fg=sel_fg)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
@StyleBuilderTtk.register_builder('default', 'NavigationButton.TFrame')
|
|
522
|
+
def build_navigationbutton_frame_style(b: StyleBuilderTtk, ttk_style: str, accent: str = None, **options):
|
|
523
|
+
"""Build NavigationButton frame style with selection indicator.
|
|
524
|
+
|
|
525
|
+
Uses nav-button assets with a left-side selection indicator bar
|
|
526
|
+
that shows the accent color when selected. This is the container
|
|
527
|
+
frame for the composite navigation button.
|
|
528
|
+
"""
|
|
529
|
+
accent_token = accent or 'primary'
|
|
530
|
+
surface_token = options.get('surface', 'content')
|
|
531
|
+
density = options.get('density', 'default')
|
|
532
|
+
icon_only = options.get('icon_only', False)
|
|
533
|
+
image_key = f'navitem_{normalize_button_density(density)}'
|
|
534
|
+
|
|
535
|
+
surface = b.color(surface_token)
|
|
536
|
+
surface_hover = b.color(f'{surface_token}_hover') if b.colors.get(f'{surface_token}_hover') else b.active(surface)
|
|
537
|
+
surface_pressed = b.pressed(surface_hover)
|
|
538
|
+
|
|
539
|
+
active = b.subtle(accent_token, surface)
|
|
540
|
+
accent_color = b.color(accent_token)
|
|
541
|
+
accent_pressed = b.pressed(active)
|
|
542
|
+
|
|
543
|
+
disabled = b.disabled()
|
|
544
|
+
|
|
545
|
+
# Normal states: indicator hidden (same color as button background)
|
|
546
|
+
normal_img = recolor_element_image(image_key, surface, surface, surface, surface)
|
|
547
|
+
hover_img = recolor_element_image(image_key, surface_hover, surface_hover, surface_hover, surface)
|
|
548
|
+
pressed_img = recolor_element_image(image_key, surface_pressed, surface_pressed, surface_pressed, surface)
|
|
549
|
+
|
|
550
|
+
# Selected states: in compact/icon-only mode the indicator matches the fill so it's invisible
|
|
551
|
+
if icon_only:
|
|
552
|
+
selected_img = recolor_element_image(image_key, active, active, active, surface)
|
|
553
|
+
selected_hover_img = recolor_element_image(image_key, active, active, active, surface)
|
|
554
|
+
selected_pressed_img = recolor_element_image(image_key, accent_pressed, accent_pressed, accent_pressed, surface)
|
|
555
|
+
else:
|
|
556
|
+
selected_img = recolor_element_image(image_key, active, active, accent_color, surface)
|
|
557
|
+
selected_hover_img = recolor_element_image(image_key, active, active, accent_color, surface)
|
|
558
|
+
selected_pressed_img = recolor_element_image(image_key, accent_pressed, accent_pressed, accent_color, surface)
|
|
559
|
+
|
|
560
|
+
disabled_img = recolor_element_image(image_key, disabled, disabled, disabled, surface)
|
|
561
|
+
|
|
562
|
+
b.create_style_element_image(
|
|
563
|
+
ElementImage(
|
|
564
|
+
f'{ttk_style}.border', normal_img.image, sticky="ew",
|
|
565
|
+
border=normal_img.meta.border
|
|
566
|
+
).state_specs(
|
|
567
|
+
[
|
|
568
|
+
('disabled', disabled_img.image),
|
|
569
|
+
('selected pressed', selected_pressed_img.image),
|
|
570
|
+
('selected hover', selected_hover_img.image),
|
|
571
|
+
('selected', selected_img.image),
|
|
572
|
+
('pressed', pressed_img.image),
|
|
573
|
+
('hover', hover_img.image),
|
|
574
|
+
('', normal_img.image)
|
|
575
|
+
]))
|
|
576
|
+
|
|
577
|
+
# Create layout to use the image-based border
|
|
578
|
+
b.create_style_layout(
|
|
579
|
+
ttk_style,
|
|
580
|
+
Element(f'{ttk_style}.border', sticky="nsew")
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
b.configure_style(
|
|
584
|
+
ttk_style,
|
|
585
|
+
background=surface,
|
|
586
|
+
relief='flat',
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
@StyleBuilderTtk.register_builder('default', 'NavigationButton.TLabel')
|
|
591
|
+
def build_navigationbutton_label_style(b: StyleBuilderTtk, ttk_style: str, accent: str = None, **options):
|
|
592
|
+
"""Build NavigationButton label style with state-aware colors.
|
|
593
|
+
|
|
594
|
+
Labels inside the navigation button container that respond to
|
|
595
|
+
hover, pressed, and selected states.
|
|
596
|
+
"""
|
|
597
|
+
accent_token = accent or 'primary'
|
|
598
|
+
surface_token = options.get('surface', 'content')
|
|
599
|
+
icon_only = options.get('icon_only', False)
|
|
600
|
+
density = options.get('density', 'default')
|
|
601
|
+
|
|
602
|
+
surface = b.color(surface_token)
|
|
603
|
+
surface_hover = b.color(f'{surface_token}_hover') if b.colors.get(f'{surface_token}_hover') else b.active(surface)
|
|
604
|
+
surface_pressed = b.pressed(surface_hover)
|
|
605
|
+
on_surface = b.on_color(surface)
|
|
606
|
+
|
|
607
|
+
accent_color = b.color(accent_token)
|
|
608
|
+
active = b.subtle(accent_token, surface)
|
|
609
|
+
active_pressed = b.pressed(active)
|
|
610
|
+
|
|
611
|
+
on_disabled = b.disabled('text', surface)
|
|
612
|
+
|
|
613
|
+
b.configure_style(
|
|
614
|
+
ttk_style,
|
|
615
|
+
background=surface,
|
|
616
|
+
foreground=on_surface,
|
|
617
|
+
relief='flat',
|
|
618
|
+
font='body'
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
foreground_state_map = [
|
|
622
|
+
('disabled', on_disabled),
|
|
623
|
+
('selected', accent_color),
|
|
624
|
+
('', on_surface)
|
|
625
|
+
]
|
|
626
|
+
|
|
627
|
+
background_state_map = [
|
|
628
|
+
('disabled', surface),
|
|
629
|
+
('selected pressed', active_pressed),
|
|
630
|
+
('selected', active),
|
|
631
|
+
('pressed !selected', surface_pressed),
|
|
632
|
+
('hover !selected', surface_hover),
|
|
633
|
+
('', surface)
|
|
634
|
+
]
|
|
635
|
+
|
|
636
|
+
state_spec = dict(foreground=foreground_state_map, background=background_state_map)
|
|
637
|
+
|
|
638
|
+
# Navigation icons use a smaller size than standalone icon buttons
|
|
639
|
+
# to stay visually consistent with context menu popup icons
|
|
640
|
+
nav_icon_size = 20 if icon_only else icon_size(icon_only, density)
|
|
641
|
+
state_spec = apply_icon_mapping(b, options, state_spec, nav_icon_size)
|
|
642
|
+
|
|
643
|
+
b.map_style(ttk_style, **state_spec)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Sizegrip widget style builders.
|
|
2
|
+
|
|
3
|
+
This module contains style builders for ttk.Sizegrip and variants.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from bootstack.style.style_builder_ttk import StyleBuilderTtk
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@StyleBuilderTtk.register_builder('default', 'TSizegrip')
|
|
12
|
+
def build_sizegrip_style(b: StyleBuilderTtk, ttk_style: str, accent: str = None, **options):
|
|
13
|
+
surface_token = options.get('surface', 'content')
|
|
14
|
+
surface = b.color(surface_token)
|
|
15
|
+
b.configure_style(ttk_style, background=surface)
|