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,485 @@
|
|
|
1
|
+
"""SideNavGroup widget for grouping navigation items."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from tkinter import TclError, Variable
|
|
6
|
+
from typing import Any, TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from typing_extensions import TypedDict, Unpack
|
|
9
|
+
|
|
10
|
+
from bootstack.widgets.primitives.frame import Frame
|
|
11
|
+
from bootstack.widgets.primitives.gridframe import GridFrame
|
|
12
|
+
from bootstack.widgets.primitives.label import Label
|
|
13
|
+
from bootstack.widgets.composites.compositeframe import CompositeFrame
|
|
14
|
+
from bootstack.widgets.composites.contextmenu import ContextMenu
|
|
15
|
+
from bootstack.widgets.mixins import configure_delegate
|
|
16
|
+
from bootstack.widgets.types import Master
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from bootstack.core.signals import Signal
|
|
20
|
+
from bootstack.widgets.composites.sidenav.item import SideNavItem
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SideNavGroupKwargs(TypedDict, total=False):
|
|
24
|
+
key: str
|
|
25
|
+
text: str
|
|
26
|
+
icon: Any
|
|
27
|
+
signal: Any
|
|
28
|
+
variable: Variable
|
|
29
|
+
is_expanded: bool
|
|
30
|
+
accent: str
|
|
31
|
+
# Frame options
|
|
32
|
+
padding: Any
|
|
33
|
+
width: int
|
|
34
|
+
height: int
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SideNavGroup(Frame):
|
|
38
|
+
"""A collapsible group of navigation items.
|
|
39
|
+
|
|
40
|
+
SideNavGroup provides a container for related navigation items.
|
|
41
|
+
In expanded mode, it behaves like an Expander with a chevron toggle.
|
|
42
|
+
In compact mode, clicking the group shows a popup flyout to the right
|
|
43
|
+
containing the group's items.
|
|
44
|
+
|
|
45
|
+
Uses a single CompositeFrame container with separate icon, text, and
|
|
46
|
+
chevron labels. In compact mode, text and chevron are hidden and the
|
|
47
|
+
icon is centered.
|
|
48
|
+
|
|
49
|
+
The group shows as selected if any of its child items are currently selected.
|
|
50
|
+
|
|
51
|
+
!!! note "Events"
|
|
52
|
+
- `<<GroupExpanding>>`: Fired before the group expands.
|
|
53
|
+
`event.data = {'key': str}`
|
|
54
|
+
- `<<GroupCollapsed>>`: Fired after the group collapses.
|
|
55
|
+
`event.data = {'key': str}`
|
|
56
|
+
|
|
57
|
+
Example:
|
|
58
|
+
```python
|
|
59
|
+
# Groups are created via SideNav.add_group()
|
|
60
|
+
nav.add_group('files', text='Files', icon='folder')
|
|
61
|
+
nav.add_item('local', text='Local', icon='hdd', group='files')
|
|
62
|
+
nav.add_item('cloud', text='Cloud', icon='cloud', group='files')
|
|
63
|
+
```
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
# Default padding values (same as SideNavItem)
|
|
67
|
+
DEFAULT_PADDING_X = 12
|
|
68
|
+
DEFAULT_PADDING_Y = 6
|
|
69
|
+
DEFAULT_ICON_GAP = 10
|
|
70
|
+
COMPACT_PADDING_X = 6
|
|
71
|
+
COMPACT_PADDING_Y = 6
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
master: Master = None,
|
|
76
|
+
key: str = '',
|
|
77
|
+
text: str = '',
|
|
78
|
+
icon: str | dict = None,
|
|
79
|
+
signal: 'Signal[Any]' = None,
|
|
80
|
+
variable: Variable = None,
|
|
81
|
+
is_expanded: bool = False,
|
|
82
|
+
**kwargs: Unpack[SideNavGroupKwargs]
|
|
83
|
+
):
|
|
84
|
+
"""Initialize a SideNavGroup.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
master (Master | None): Parent widget.
|
|
88
|
+
key (str): Unique identifier for this group.
|
|
89
|
+
text (str): Display text for the group.
|
|
90
|
+
icon (str | dict | None): Icon name or configuration dict.
|
|
91
|
+
signal (Signal | None): Reactive signal for selection state.
|
|
92
|
+
variable (Variable | None): Shared variable for selection tracking.
|
|
93
|
+
is_expanded (bool): Initial expansion state. Default False.
|
|
94
|
+
**kwargs: Additional arguments passed to Frame.
|
|
95
|
+
"""
|
|
96
|
+
if not key:
|
|
97
|
+
raise ValueError("SideNavGroup requires a non-empty 'key'")
|
|
98
|
+
|
|
99
|
+
# Extract styling kwargs before super().__init__
|
|
100
|
+
# Must set these AFTER super().__init__ because TTKWrapperBase also sets _accent
|
|
101
|
+
saved_accent = kwargs.pop('accent', 'primary')
|
|
102
|
+
|
|
103
|
+
super().__init__(master, **kwargs)
|
|
104
|
+
|
|
105
|
+
# Set after super() to avoid being overwritten by TTKWrapperBase
|
|
106
|
+
self._accent = saved_accent
|
|
107
|
+
|
|
108
|
+
self._key = key
|
|
109
|
+
self._text = text
|
|
110
|
+
self._icon = icon
|
|
111
|
+
self._signal = signal
|
|
112
|
+
self._variable = variable
|
|
113
|
+
self._is_expanded = is_expanded
|
|
114
|
+
|
|
115
|
+
# Track child items (managed by SideNav)
|
|
116
|
+
self._items: dict[str, 'SideNavItem'] = {}
|
|
117
|
+
self._item_order: list[str] = []
|
|
118
|
+
|
|
119
|
+
# Compact mode state
|
|
120
|
+
self._compact = False
|
|
121
|
+
|
|
122
|
+
# Widget references
|
|
123
|
+
self._container: CompositeFrame | None = None
|
|
124
|
+
self._icon_label: Label | None = None
|
|
125
|
+
self._text_label: Label | None = None
|
|
126
|
+
self._chevron_label: Label | None = None
|
|
127
|
+
self._content_frame: Frame | None = None
|
|
128
|
+
self._popup: ContextMenu | None = None
|
|
129
|
+
|
|
130
|
+
# Selection state is now managed centrally by SideNav
|
|
131
|
+
# for better performance (only affected items are updated)
|
|
132
|
+
self._trace_id: str | None = None
|
|
133
|
+
|
|
134
|
+
# Build internal structure
|
|
135
|
+
self._build_widget()
|
|
136
|
+
|
|
137
|
+
def _build_widget(self):
|
|
138
|
+
"""Build the internal widget structure.
|
|
139
|
+
|
|
140
|
+
Uses CompositeFrame with NavigationButton styles for the header.
|
|
141
|
+
Child labels are registered for automatic state coordination.
|
|
142
|
+
"""
|
|
143
|
+
# Container using NavigationButton.TFrame for selection indicator
|
|
144
|
+
self._container = CompositeFrame(
|
|
145
|
+
self,
|
|
146
|
+
ttk_class='NavigationButton.TFrame',
|
|
147
|
+
accent=self._accent,
|
|
148
|
+
padding=(self.DEFAULT_PADDING_X, self.DEFAULT_PADDING_Y,
|
|
149
|
+
self.DEFAULT_PADDING_X, self.DEFAULT_PADDING_Y),
|
|
150
|
+
takefocus=True,
|
|
151
|
+
)
|
|
152
|
+
self._container.pack(fill='x')
|
|
153
|
+
|
|
154
|
+
# Icon label
|
|
155
|
+
if self._icon:
|
|
156
|
+
self._icon_label = Label(
|
|
157
|
+
self._container,
|
|
158
|
+
icon=self._icon,
|
|
159
|
+
icon_only=True,
|
|
160
|
+
ttk_class='NavigationButton.TLabel',
|
|
161
|
+
accent=self._accent,
|
|
162
|
+
takefocus=False,
|
|
163
|
+
)
|
|
164
|
+
self._container.register_composite(self._icon_label)
|
|
165
|
+
|
|
166
|
+
# Text label
|
|
167
|
+
self._text_label = Label(
|
|
168
|
+
self._container,
|
|
169
|
+
text=self._text,
|
|
170
|
+
anchor='w',
|
|
171
|
+
ttk_class='NavigationButton.TLabel',
|
|
172
|
+
accent=self._accent,
|
|
173
|
+
takefocus=False,
|
|
174
|
+
)
|
|
175
|
+
self._container.register_composite(self._text_label)
|
|
176
|
+
|
|
177
|
+
# Chevron for expand/collapse indicator
|
|
178
|
+
self._chevron_label = Label(
|
|
179
|
+
self._container,
|
|
180
|
+
icon=self._get_chevron_icon(),
|
|
181
|
+
icon_only=True,
|
|
182
|
+
ttk_class='NavigationButton.TLabel',
|
|
183
|
+
accent=self._accent,
|
|
184
|
+
takefocus=False,
|
|
185
|
+
)
|
|
186
|
+
self._container.register_composite(self._chevron_label)
|
|
187
|
+
|
|
188
|
+
# Apply initial layout
|
|
189
|
+
self._apply_layout()
|
|
190
|
+
|
|
191
|
+
# Bind click events
|
|
192
|
+
self._bind_events()
|
|
193
|
+
|
|
194
|
+
# Content frame for child items
|
|
195
|
+
self._content_frame = GridFrame(
|
|
196
|
+
self,
|
|
197
|
+
columns=1,
|
|
198
|
+
gap=(0, 0),
|
|
199
|
+
sticky_items='ew',
|
|
200
|
+
)
|
|
201
|
+
if self._is_expanded:
|
|
202
|
+
self._content_frame.pack(fill='both', expand=True)
|
|
203
|
+
|
|
204
|
+
# Set initial selection state
|
|
205
|
+
self._update_selection_state()
|
|
206
|
+
|
|
207
|
+
def _apply_layout(self):
|
|
208
|
+
"""Apply the current layout based on compact mode."""
|
|
209
|
+
# Clear current layout
|
|
210
|
+
if self._icon_label:
|
|
211
|
+
self._icon_label.pack_forget()
|
|
212
|
+
self._text_label.pack_forget()
|
|
213
|
+
self._chevron_label.pack_forget()
|
|
214
|
+
|
|
215
|
+
if self._compact:
|
|
216
|
+
# Compact mode: tighter padding, icon centered
|
|
217
|
+
px = self.COMPACT_PADDING_X
|
|
218
|
+
py = self.COMPACT_PADDING_Y
|
|
219
|
+
self._container.configure(padding=(px, py, px, py))
|
|
220
|
+
if self._icon_label:
|
|
221
|
+
self._icon_label.pack(expand=True)
|
|
222
|
+
else:
|
|
223
|
+
# Expanded mode: restore default padding, icon + text + chevron
|
|
224
|
+
self._container.configure(
|
|
225
|
+
padding=(self.DEFAULT_PADDING_X, self.DEFAULT_PADDING_Y,
|
|
226
|
+
self.DEFAULT_PADDING_X, self.DEFAULT_PADDING_Y)
|
|
227
|
+
)
|
|
228
|
+
if self._icon_label:
|
|
229
|
+
self._icon_label.pack(side='left', padx=(0, self.DEFAULT_ICON_GAP))
|
|
230
|
+
self._text_label.pack(side='left', fill='x', expand=True)
|
|
231
|
+
self._chevron_label.pack(side='right')
|
|
232
|
+
|
|
233
|
+
def _bind_events(self):
|
|
234
|
+
"""Bind click and keyboard events.
|
|
235
|
+
|
|
236
|
+
Binds click to container and all child widgets since Tkinter
|
|
237
|
+
doesn't bubble events from children to parents.
|
|
238
|
+
"""
|
|
239
|
+
self._container.bind('<Button-1>', self._on_header_click, add='+')
|
|
240
|
+
if self._icon_label:
|
|
241
|
+
self._icon_label.bind('<Button-1>', self._on_header_click, add='+')
|
|
242
|
+
self._text_label.bind('<Button-1>', self._on_header_click, add='+')
|
|
243
|
+
self._chevron_label.bind('<Button-1>', self._on_header_click, add='+')
|
|
244
|
+
|
|
245
|
+
# Keyboard support on focusable container
|
|
246
|
+
self._container.bind('<Return>', self._on_header_click, add='+')
|
|
247
|
+
self._container.bind('<space>', self._on_header_click, add='+')
|
|
248
|
+
|
|
249
|
+
def _get_chevron_icon(self) -> dict:
|
|
250
|
+
"""Get the appropriate chevron icon for current state."""
|
|
251
|
+
if self._is_expanded:
|
|
252
|
+
return {'name': 'chevron-up', 'size': 16}
|
|
253
|
+
else:
|
|
254
|
+
return {'name': 'chevron-down', 'size': 16}
|
|
255
|
+
|
|
256
|
+
def _on_header_click(self, event=None):
|
|
257
|
+
"""Handle header click - toggle expand/collapse or show popup."""
|
|
258
|
+
self._container.focus_set()
|
|
259
|
+
|
|
260
|
+
if self._compact:
|
|
261
|
+
# In compact mode, show popup
|
|
262
|
+
self._show_popup()
|
|
263
|
+
else:
|
|
264
|
+
# In expanded mode, toggle expand/collapse
|
|
265
|
+
self.toggle()
|
|
266
|
+
|
|
267
|
+
def _update_selection_state(self):
|
|
268
|
+
"""Update visual state based on whether any child is selected."""
|
|
269
|
+
is_any_selected = self._is_any_child_selected()
|
|
270
|
+
self._container.set_selected(is_any_selected)
|
|
271
|
+
|
|
272
|
+
def set_child_selected(self, has_selected_child: bool) -> None:
|
|
273
|
+
"""Directly set whether a child is selected.
|
|
274
|
+
|
|
275
|
+
This is called by SideNav for efficient selection updates,
|
|
276
|
+
avoiding the need to iterate through children.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
has_selected_child: True if any child item is selected.
|
|
280
|
+
"""
|
|
281
|
+
self._container.set_selected(has_selected_child)
|
|
282
|
+
|
|
283
|
+
def _is_any_child_selected(self) -> bool:
|
|
284
|
+
"""Check if any child item is currently selected."""
|
|
285
|
+
if not self._variable:
|
|
286
|
+
return False
|
|
287
|
+
current = self._variable.get()
|
|
288
|
+
return current in self._items
|
|
289
|
+
|
|
290
|
+
def _show_popup(self):
|
|
291
|
+
"""Show the popup flyout with group items using ContextMenu."""
|
|
292
|
+
# Always destroy existing popup and create fresh one
|
|
293
|
+
# (ContextMenu handles click-outside-to-close automatically)
|
|
294
|
+
self._hide_popup()
|
|
295
|
+
|
|
296
|
+
# Create ContextMenu positioned to the right of the container.
|
|
297
|
+
# SideNav opens this popup via its own click flow, so opt out of
|
|
298
|
+
# ContextMenu's auto right-click trigger.
|
|
299
|
+
self._popup = ContextMenu(
|
|
300
|
+
self,
|
|
301
|
+
target=self._container,
|
|
302
|
+
anchor='nw',
|
|
303
|
+
attach='ne',
|
|
304
|
+
offset=(4, 0),
|
|
305
|
+
minwidth=180,
|
|
306
|
+
trigger=None,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# Add items as commands
|
|
310
|
+
for key in self._item_order:
|
|
311
|
+
item = self._items[key]
|
|
312
|
+
item_key = key # Capture for closure
|
|
313
|
+
self._popup.add_command(
|
|
314
|
+
text=item.cget('text'),
|
|
315
|
+
icon=item.cget('icon'),
|
|
316
|
+
command=lambda k=item_key: self._on_popup_item_click(k),
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Show the popup
|
|
320
|
+
self._popup.show()
|
|
321
|
+
|
|
322
|
+
def _on_popup_item_click(self, key: str):
|
|
323
|
+
"""Handle click on a popup menu item."""
|
|
324
|
+
if key in self._items:
|
|
325
|
+
# Set selection
|
|
326
|
+
if self._variable:
|
|
327
|
+
self._variable.set(key)
|
|
328
|
+
# Fire item invoked event
|
|
329
|
+
self._items[key].event_generate('<<ItemInvoked>>', data={'key': key})
|
|
330
|
+
|
|
331
|
+
def _hide_popup(self):
|
|
332
|
+
"""Hide and destroy the popup."""
|
|
333
|
+
if self._popup:
|
|
334
|
+
try:
|
|
335
|
+
self._popup.destroy()
|
|
336
|
+
except TclError:
|
|
337
|
+
pass
|
|
338
|
+
self._popup = None
|
|
339
|
+
|
|
340
|
+
# --- Public API ---
|
|
341
|
+
|
|
342
|
+
def set_compact(self, compact: bool) -> None:
|
|
343
|
+
"""Set compact mode.
|
|
344
|
+
|
|
345
|
+
In compact mode, only the icon is shown and clicking opens a popup
|
|
346
|
+
with the group's items.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
compact (bool): True for compact mode, False for full display.
|
|
350
|
+
"""
|
|
351
|
+
if self._compact == compact:
|
|
352
|
+
return
|
|
353
|
+
|
|
354
|
+
self._compact = compact
|
|
355
|
+
self._hide_popup()
|
|
356
|
+
self._apply_layout()
|
|
357
|
+
|
|
358
|
+
if compact:
|
|
359
|
+
# Hide content in compact mode
|
|
360
|
+
self._content_frame.pack_forget()
|
|
361
|
+
else:
|
|
362
|
+
# Restore content if expanded
|
|
363
|
+
if self._is_expanded:
|
|
364
|
+
self._content_frame.pack(fill='both', expand=True)
|
|
365
|
+
|
|
366
|
+
self._update_selection_state()
|
|
367
|
+
|
|
368
|
+
def expand(self) -> None:
|
|
369
|
+
"""Expand to show items (expanded mode only)."""
|
|
370
|
+
if self._compact or self._is_expanded:
|
|
371
|
+
return
|
|
372
|
+
|
|
373
|
+
self._is_expanded = True
|
|
374
|
+
self._content_frame.pack(fill='both', expand=True)
|
|
375
|
+
self._chevron_label.configure(icon=self._get_chevron_icon())
|
|
376
|
+
self.event_generate('<<GroupExpanding>>', data={'key': self._key})
|
|
377
|
+
|
|
378
|
+
def collapse(self) -> None:
|
|
379
|
+
"""Collapse to hide items (expanded mode only)."""
|
|
380
|
+
if self._compact or not self._is_expanded:
|
|
381
|
+
return
|
|
382
|
+
|
|
383
|
+
self._is_expanded = False
|
|
384
|
+
self._content_frame.pack_forget()
|
|
385
|
+
self._chevron_label.configure(icon=self._get_chevron_icon())
|
|
386
|
+
self.event_generate('<<GroupCollapsed>>', data={'key': self._key})
|
|
387
|
+
|
|
388
|
+
def toggle(self) -> None:
|
|
389
|
+
"""Toggle expansion state (expanded mode only)."""
|
|
390
|
+
if self._compact:
|
|
391
|
+
return
|
|
392
|
+
|
|
393
|
+
if self._is_expanded:
|
|
394
|
+
self.collapse()
|
|
395
|
+
else:
|
|
396
|
+
self.expand()
|
|
397
|
+
|
|
398
|
+
# --- Internal: Item management (called by SideNav) ---
|
|
399
|
+
|
|
400
|
+
def _add_item(self, item: 'SideNavItem') -> None:
|
|
401
|
+
"""Add an item to this group (internal use by SideNav)."""
|
|
402
|
+
self._items[item.key] = item
|
|
403
|
+
self._item_order.append(item.key)
|
|
404
|
+
|
|
405
|
+
def _remove_item(self, key: str) -> None:
|
|
406
|
+
"""Remove an item from this group (internal use by SideNav)."""
|
|
407
|
+
if key in self._items:
|
|
408
|
+
del self._items[key]
|
|
409
|
+
self._item_order.remove(key)
|
|
410
|
+
|
|
411
|
+
# --- Properties ---
|
|
412
|
+
|
|
413
|
+
@property
|
|
414
|
+
def key(self) -> str:
|
|
415
|
+
"""Get the group's unique key."""
|
|
416
|
+
return self._key
|
|
417
|
+
|
|
418
|
+
@property
|
|
419
|
+
def content_frame(self) -> Frame:
|
|
420
|
+
"""Get the content frame for adding items."""
|
|
421
|
+
return self._content_frame
|
|
422
|
+
|
|
423
|
+
@property
|
|
424
|
+
def is_expanded(self) -> bool:
|
|
425
|
+
"""Check if group is currently expanded."""
|
|
426
|
+
return self._is_expanded
|
|
427
|
+
|
|
428
|
+
@property
|
|
429
|
+
def has_items(self) -> bool:
|
|
430
|
+
"""Check if this group has any items."""
|
|
431
|
+
return len(self._items) > 0
|
|
432
|
+
|
|
433
|
+
@property
|
|
434
|
+
def items(self) -> list['SideNavItem']:
|
|
435
|
+
"""Get all items in order."""
|
|
436
|
+
return [self._items[key] for key in self._item_order]
|
|
437
|
+
|
|
438
|
+
@property
|
|
439
|
+
def is_any_selected(self) -> bool:
|
|
440
|
+
"""Check if any child item is selected."""
|
|
441
|
+
return self._is_any_child_selected()
|
|
442
|
+
|
|
443
|
+
# --- Configuration Delegates ---
|
|
444
|
+
|
|
445
|
+
@configure_delegate('text')
|
|
446
|
+
def _delegate_text(self, value: str = None):
|
|
447
|
+
"""Configure the group text."""
|
|
448
|
+
if value is None:
|
|
449
|
+
return self._text
|
|
450
|
+
self._text = value
|
|
451
|
+
if self._text_label:
|
|
452
|
+
self._text_label.configure(text=value)
|
|
453
|
+
return None
|
|
454
|
+
|
|
455
|
+
@configure_delegate('icon')
|
|
456
|
+
def _delegate_icon(self, value: str | dict = None):
|
|
457
|
+
"""Configure the group icon."""
|
|
458
|
+
if value is None:
|
|
459
|
+
return self._icon
|
|
460
|
+
self._icon = value
|
|
461
|
+
|
|
462
|
+
if self._icon_label is not None:
|
|
463
|
+
self._icon_label.configure(icon=value)
|
|
464
|
+
elif value is not None:
|
|
465
|
+
# Create icon label if it doesn't exist
|
|
466
|
+
self._icon_label = Label(
|
|
467
|
+
self._container,
|
|
468
|
+
icon=value,
|
|
469
|
+
icon_only=True,
|
|
470
|
+
ttk_class='NavigationButton.TLabel',
|
|
471
|
+
accent=self._accent,
|
|
472
|
+
takefocus=False,
|
|
473
|
+
)
|
|
474
|
+
self._container.register_composite(self._icon_label)
|
|
475
|
+
self._icon_label.bind('<Button-1>', self._on_header_click, add='+')
|
|
476
|
+
# Re-apply layout
|
|
477
|
+
self._apply_layout()
|
|
478
|
+
return None
|
|
479
|
+
|
|
480
|
+
# --- Cleanup ---
|
|
481
|
+
|
|
482
|
+
def destroy(self):
|
|
483
|
+
"""Clean up resources."""
|
|
484
|
+
self._hide_popup()
|
|
485
|
+
super().destroy()
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""SideNavHeader widget for section labels in navigation menus."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from typing_extensions import TypedDict, Unpack
|
|
6
|
+
|
|
7
|
+
from bootstack.widgets.primitives.frame import Frame
|
|
8
|
+
from bootstack.widgets.primitives.label import Label
|
|
9
|
+
from bootstack.widgets.mixins import configure_delegate
|
|
10
|
+
from bootstack.widgets.types import Master
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SideNavHeaderKwargs(TypedDict, total=False):
|
|
14
|
+
text: str
|
|
15
|
+
# Frame options
|
|
16
|
+
padding: Any
|
|
17
|
+
width: int
|
|
18
|
+
height: int
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SideNavHeader(Frame):
|
|
22
|
+
"""A non-selectable section header for grouping navigation items.
|
|
23
|
+
|
|
24
|
+
SideNavHeader provides a text label to identify groups of related
|
|
25
|
+
navigation items. Unlike SideNavItem, headers are not selectable
|
|
26
|
+
and serve only as visual labels. Uses the 'label' font token for styling.
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
```python
|
|
30
|
+
nav.add_item('home', text='Home', icon='house')
|
|
31
|
+
nav.add_header('Favorites') # Creates SideNavHeader
|
|
32
|
+
nav.add_item('photos', text='Photos', icon='image')
|
|
33
|
+
nav.add_item('music', text='Music', icon='music-note')
|
|
34
|
+
```
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
DEFAULT_PADDING = (8, 20, 8, 4)
|
|
38
|
+
DEFAULT_FONT = 'label'
|
|
39
|
+
DEFAULT_ACCENT = 'secondary'
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
master: Master = None,
|
|
44
|
+
text: str = '',
|
|
45
|
+
**kwargs: Unpack[SideNavHeaderKwargs]
|
|
46
|
+
):
|
|
47
|
+
"""Initialize a SideNavHeader.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
master (Master | None): Parent widget.
|
|
51
|
+
text (str): The header text to display.
|
|
52
|
+
**kwargs: Additional arguments passed to Frame.
|
|
53
|
+
"""
|
|
54
|
+
self._text = text
|
|
55
|
+
|
|
56
|
+
# Default padding: more top margin for visual separation between sections
|
|
57
|
+
kwargs.setdefault('padding', self.DEFAULT_PADDING)
|
|
58
|
+
|
|
59
|
+
super().__init__(master, **kwargs)
|
|
60
|
+
|
|
61
|
+
# Create header label with 'label' font (smaller, bold) and secondary accent
|
|
62
|
+
self._text_label = Label(
|
|
63
|
+
self,
|
|
64
|
+
text=text,
|
|
65
|
+
font=self.DEFAULT_FONT,
|
|
66
|
+
accent=self.DEFAULT_ACCENT,
|
|
67
|
+
anchor='w',
|
|
68
|
+
)
|
|
69
|
+
self._text_label.pack(fill='x')
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def text(self) -> str:
|
|
73
|
+
"""Get the header text."""
|
|
74
|
+
return self._text
|
|
75
|
+
|
|
76
|
+
@configure_delegate('text')
|
|
77
|
+
def _delegate_text(self, value: str = None):
|
|
78
|
+
"""Configure the header text."""
|
|
79
|
+
if value is None:
|
|
80
|
+
return self._text
|
|
81
|
+
self._text = value
|
|
82
|
+
self._text_label.configure(text=value)
|
|
83
|
+
return None
|