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,510 @@
|
|
|
1
|
+
"""Menu utilities for bootstack.
|
|
2
|
+
|
|
3
|
+
This module provides utilities for creating menus with icon support and
|
|
4
|
+
theme-aware color updates.
|
|
5
|
+
|
|
6
|
+
Examples:
|
|
7
|
+
```python
|
|
8
|
+
import bootstack as bs
|
|
9
|
+
from bootstack.menu import create_menu
|
|
10
|
+
|
|
11
|
+
app = bs.Window()
|
|
12
|
+
|
|
13
|
+
menu_items = [
|
|
14
|
+
{
|
|
15
|
+
"label": "File",
|
|
16
|
+
"items": [
|
|
17
|
+
{"label": "Open", "icon": "folder2-open"},
|
|
18
|
+
{"label": "Save", "icon": "save"},
|
|
19
|
+
{"type": "separator"},
|
|
20
|
+
{"label": "Exit", "command": app.quit, "icon": "x-circle"}
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"label": "Edit",
|
|
25
|
+
"items": [
|
|
26
|
+
{"label": "Undo", "icon": "arrow-counterclockwise"},
|
|
27
|
+
{"label": "Redo", "icon": "arrow-clockwise"},
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
create_menu(app, menu_items)
|
|
33
|
+
app.mainloop()
|
|
34
|
+
```
|
|
35
|
+
"""
|
|
36
|
+
import tkinter as tk
|
|
37
|
+
from tkinter import font, ttk
|
|
38
|
+
from typing import Any, Optional, Union
|
|
39
|
+
|
|
40
|
+
from ttkbootstrap_icons_bs import BootstrapIcon
|
|
41
|
+
from bootstack.core.localization import MessageCatalog
|
|
42
|
+
from bootstack.runtime.shortcuts import format_shortcut
|
|
43
|
+
from bootstack.style.style import get_style
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class MenuManager:
|
|
47
|
+
"""Manages menus with icon support and theme-aware color updates.
|
|
48
|
+
|
|
49
|
+
This class provides a declarative way to create menus with automatic icon
|
|
50
|
+
support and theme-aware color updates. When the theme changes, all menu
|
|
51
|
+
icons are automatically updated to match the new theme's foreground color.
|
|
52
|
+
|
|
53
|
+
The MenuManager tracks all menu items that have icons and listens for
|
|
54
|
+
`<<ThemeChanged>>` events on the root window. When a theme change is detected,
|
|
55
|
+
it recreates and updates all icons to ensure they match the new theme.
|
|
56
|
+
|
|
57
|
+
Attributes:
|
|
58
|
+
parent: The parent widget (typically a Window or Toplevel).
|
|
59
|
+
style: The Style instance for accessing theme colors.
|
|
60
|
+
menu_items: Dictionary tracking menu items with icons for theme updates.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self, parent: Any):
|
|
64
|
+
"""Initialize the MenuManager.
|
|
65
|
+
|
|
66
|
+
Sets up the menu manager with the given parent widget, initializes
|
|
67
|
+
the style and icon provider, and establishes theme change monitoring
|
|
68
|
+
to automatically update icon colors when themes change.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
parent: The parent widget, typically a Window, Toplevel, or root
|
|
72
|
+
widget. This is used to access the style, icon provider, and
|
|
73
|
+
to bind theme change events.
|
|
74
|
+
"""
|
|
75
|
+
self.parent: Any = parent
|
|
76
|
+
self.style = get_style()
|
|
77
|
+
self.menu_items: dict[str, tuple[tk.Menu, int, str, int]] = {}
|
|
78
|
+
|
|
79
|
+
# Set up theme change monitoring
|
|
80
|
+
self._setup_theme_monitoring()
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def for_widget(cls, widget: Any) -> "MenuManager":
|
|
84
|
+
"""Return the singleton MenuManager for `widget`'s root window.
|
|
85
|
+
|
|
86
|
+
Creates one on first access and stores it on the root as
|
|
87
|
+
`_menu_manager`. Use this to share icon-tracking and theme
|
|
88
|
+
monitoring across all tk.Menu-based widgets in an application
|
|
89
|
+
(menubars, context menus, etc.) without each one binding its own
|
|
90
|
+
`<<ThemeChanged>>` handler.
|
|
91
|
+
"""
|
|
92
|
+
root = widget.winfo_toplevel() if hasattr(widget, 'winfo_toplevel') else widget
|
|
93
|
+
existing = getattr(root, '_menu_manager', None)
|
|
94
|
+
if existing is not None:
|
|
95
|
+
return existing
|
|
96
|
+
mgr = cls(root)
|
|
97
|
+
try:
|
|
98
|
+
root._menu_manager = mgr
|
|
99
|
+
except Exception:
|
|
100
|
+
pass
|
|
101
|
+
return mgr
|
|
102
|
+
|
|
103
|
+
def _setup_theme_monitoring(self):
|
|
104
|
+
"""Set up monitoring for theme and macOS appearance changes."""
|
|
105
|
+
# Get the root window
|
|
106
|
+
root = self.parent.winfo_toplevel() if hasattr(self.parent, 'winfo_toplevel') else self.parent
|
|
107
|
+
|
|
108
|
+
if not hasattr(root, 'bind'):
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
# bootstack theme changes
|
|
112
|
+
root.bind('<<ThemeChanged>>', self._on_theme_changed, add='+')
|
|
113
|
+
# macOS light/dark appearance toggle (Tk 8.6.10+ on Aqua). Without
|
|
114
|
+
# this, icons rendered with a cached system-text color stay stale
|
|
115
|
+
# after the user flips system Appearance.
|
|
116
|
+
try:
|
|
117
|
+
root.bind('<<TkSystemAppearanceChanged>>', self._on_theme_changed, add='+')
|
|
118
|
+
except tk.TclError:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
def _menu_icon_color(self) -> str:
|
|
122
|
+
"""Return the right foreground color for icons going into a tk.Menu.
|
|
123
|
+
|
|
124
|
+
On Aqua, NSMenu always uses system appearance regardless of the
|
|
125
|
+
app theme, so icons must match the system text color (which Tk
|
|
126
|
+
keeps in sync with macOS light/dark mode). Everywhere else, the
|
|
127
|
+
app theme's foreground is the right answer.
|
|
128
|
+
"""
|
|
129
|
+
try:
|
|
130
|
+
winsys = self.parent.tk.call('tk', 'windowingsystem')
|
|
131
|
+
except tk.TclError:
|
|
132
|
+
winsys = None
|
|
133
|
+
|
|
134
|
+
if winsys == 'aqua':
|
|
135
|
+
try:
|
|
136
|
+
# winfo_rgb returns 16-bit channels; pack into a hex string
|
|
137
|
+
# BootstrapIcon (and PIL underneath) understands.
|
|
138
|
+
r, g, b = self.parent.winfo_rgb('systemTextColor')
|
|
139
|
+
return f'#{r >> 8:02x}{g >> 8:02x}{b >> 8:02x}'
|
|
140
|
+
except tk.TclError:
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
return self.style.style_builder.color('foreground')
|
|
144
|
+
|
|
145
|
+
def _on_theme_changed(self, event=None):
|
|
146
|
+
"""Update all menu icon colors when theme or system appearance changes."""
|
|
147
|
+
fg_color = self._menu_icon_color()
|
|
148
|
+
|
|
149
|
+
# Update all menu items (including cascades)
|
|
150
|
+
for menu, index, icon_name, size in self.menu_items.values():
|
|
151
|
+
# Recreate the icon with new foreground color
|
|
152
|
+
new_icon = BootstrapIcon(icon_name, size, fg_color)
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
menu.entryconfigure(index, image=new_icon)
|
|
156
|
+
except tk.TclError:
|
|
157
|
+
# Menu item may have been deleted
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
# ----- Public helpers for tk.Menu callers --------------------------------
|
|
161
|
+
|
|
162
|
+
@staticmethod
|
|
163
|
+
def translate_label(text: Optional[str]) -> Optional[str]:
|
|
164
|
+
"""Run a label through `MessageCatalog.translate`.
|
|
165
|
+
|
|
166
|
+
Semantic keys (e.g. `'table.sort_asc'`) become localized strings;
|
|
167
|
+
plain text passes through unchanged because `translate` returns
|
|
168
|
+
the source when no translation is registered.
|
|
169
|
+
"""
|
|
170
|
+
if not text:
|
|
171
|
+
return text
|
|
172
|
+
try:
|
|
173
|
+
return MessageCatalog.translate(text)
|
|
174
|
+
except Exception:
|
|
175
|
+
return text
|
|
176
|
+
|
|
177
|
+
def resolve_icon(self, icon_spec: Union[str, dict, None]) -> tuple[Any, Optional[str], int]:
|
|
178
|
+
"""Resolve an icon spec to a `(PhotoImage, name, size)` triple.
|
|
179
|
+
|
|
180
|
+
Returns `(None, None, 0)` for empty/unsupported specs. The returned
|
|
181
|
+
PhotoImage is the same kind `MenuManager` uses internally for its
|
|
182
|
+
own menus, so the caller can pass it straight to
|
|
183
|
+
`menu.add_command(image=..., compound='left')`.
|
|
184
|
+
"""
|
|
185
|
+
if not icon_spec or icon_spec == 'empty':
|
|
186
|
+
return None, None, 0
|
|
187
|
+
name, size = self._parse_icon_spec(icon_spec)
|
|
188
|
+
if not name:
|
|
189
|
+
return None, None, 0
|
|
190
|
+
try:
|
|
191
|
+
icon = BootstrapIcon(name, size, self._menu_icon_color())
|
|
192
|
+
except Exception:
|
|
193
|
+
return None, None, 0
|
|
194
|
+
return icon, name, size
|
|
195
|
+
|
|
196
|
+
def register_icon(self, menu: tk.Menu, index: int, icon_name: str, icon_size: int) -> None:
|
|
197
|
+
"""Track a menu item for theme-aware icon re-rendering.
|
|
198
|
+
|
|
199
|
+
Call after `menu.add_*(image=icon)` so subsequent
|
|
200
|
+
`<<ThemeChanged>>` events refresh the entry's icon color.
|
|
201
|
+
"""
|
|
202
|
+
self._track_icon(menu, index, icon_name, icon_size)
|
|
203
|
+
|
|
204
|
+
def unregister_menu(self, menu: tk.Menu) -> None:
|
|
205
|
+
"""Drop all tracking entries for `menu`.
|
|
206
|
+
|
|
207
|
+
Call when the menu is being destroyed or fully rebuilt so stale
|
|
208
|
+
`(menu, index)` references don't try to reconfigure deleted entries
|
|
209
|
+
on the next `<<ThemeChanged>>`.
|
|
210
|
+
"""
|
|
211
|
+
prefix = f"{id(menu)}_"
|
|
212
|
+
for key in [k for k in self.menu_items if k.startswith(prefix)]:
|
|
213
|
+
del self.menu_items[key]
|
|
214
|
+
|
|
215
|
+
def _parse_icon_spec(self, icon_spec: Union[str, dict]) -> tuple[Optional[str], int]:
|
|
216
|
+
"""Parse icon specification into name and size tuple."""
|
|
217
|
+
# Use menu font linespace as default icon size
|
|
218
|
+
menu_font = font.nametofont("TkMenuFont")
|
|
219
|
+
default_size = menu_font.metrics("linespace")
|
|
220
|
+
|
|
221
|
+
if isinstance(icon_spec, str):
|
|
222
|
+
return icon_spec, default_size
|
|
223
|
+
elif isinstance(icon_spec, dict):
|
|
224
|
+
name = icon_spec.get('name')
|
|
225
|
+
size = icon_spec.get('size', default_size)
|
|
226
|
+
return name, size
|
|
227
|
+
return None, default_size
|
|
228
|
+
|
|
229
|
+
def _is_aqua_special_name(self, name: str) -> bool:
|
|
230
|
+
"""Return True if `name` triggers macOS-native menu handling.
|
|
231
|
+
|
|
232
|
+
Tk on Aqua reserves three submenu names off the menubar: `apple`
|
|
233
|
+
(the app menu — gets auto-filled with About/Hide/Quit), `window`
|
|
234
|
+
(auto-populated with open Toplevels), and `help` (gets the
|
|
235
|
+
system Help search field). Names are case-sensitive and only have
|
|
236
|
+
meaning under windowingsystem='aqua'.
|
|
237
|
+
"""
|
|
238
|
+
try:
|
|
239
|
+
winsys = self.parent.tk.call('tk', 'windowingsystem')
|
|
240
|
+
except tk.TclError:
|
|
241
|
+
return False
|
|
242
|
+
return winsys == 'aqua' and name in ('apple', 'window', 'help')
|
|
243
|
+
|
|
244
|
+
def create_menu(self, parent: Any, items: list[dict]) -> tk.Menu:
|
|
245
|
+
"""Create a menu from a list of item dictionaries.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
parent: The parent widget (Window, Toplevel, or Menu).
|
|
249
|
+
items: List of menu item dictionaries defining the menu structure.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
The created Menu object.
|
|
253
|
+
"""
|
|
254
|
+
menubar = None
|
|
255
|
+
|
|
256
|
+
# If parent is a window, create the menubar first
|
|
257
|
+
if not isinstance(parent, tk.Menu):
|
|
258
|
+
menubar = tk.Menu(parent, tearoff=0)
|
|
259
|
+
parent['menu'] = menubar
|
|
260
|
+
parent = menubar # Now work with the menubar
|
|
261
|
+
|
|
262
|
+
for options in items:
|
|
263
|
+
options = options.copy()
|
|
264
|
+
sub_items = options.pop('items', [])
|
|
265
|
+
|
|
266
|
+
# Pull off the optional `name` early so it's never passed to
|
|
267
|
+
# add_cascade. On Aqua, three magic names get system handling:
|
|
268
|
+
# 'apple' → app menu (auto-fills with About/Hide/Quit),
|
|
269
|
+
# 'window' → Tk auto-populates the open Toplevel list,
|
|
270
|
+
# 'help' → system menu search across all menus.
|
|
271
|
+
menu_name = options.pop('name', None)
|
|
272
|
+
|
|
273
|
+
# Translate the label so semantic keys (e.g. 'menu.file') resolve
|
|
274
|
+
# via MessageCatalog. Plain text passes through unchanged.
|
|
275
|
+
if 'label' in options:
|
|
276
|
+
options['label'] = self.translate_label(options['label'])
|
|
277
|
+
|
|
278
|
+
# Handle icon for cascade menu BEFORE popping tearoff
|
|
279
|
+
icon_spec = options.pop('icon', None)
|
|
280
|
+
icon_name = None
|
|
281
|
+
icon_size = 16
|
|
282
|
+
|
|
283
|
+
if icon_spec:
|
|
284
|
+
icon, icon_name, icon_size = self.resolve_icon(icon_spec)
|
|
285
|
+
if icon is not None:
|
|
286
|
+
options['image'] = icon
|
|
287
|
+
options['compound'] = options.get('compound', 'left')
|
|
288
|
+
|
|
289
|
+
options.setdefault('tearoff', 0)
|
|
290
|
+
|
|
291
|
+
# Create a menu for this item. On Aqua, propagate the magic name
|
|
292
|
+
# so Tk wires up NSApp's app/window/help menu integration.
|
|
293
|
+
menu_kwargs: dict[str, Any] = {'tearoff': options.pop('tearoff')}
|
|
294
|
+
if menu_name and self._is_aqua_special_name(menu_name):
|
|
295
|
+
menu_kwargs['name'] = menu_name
|
|
296
|
+
menu = tk.Menu(parent, **menu_kwargs)
|
|
297
|
+
|
|
298
|
+
# Add it as a cascade to the parent menu
|
|
299
|
+
parent.add_cascade(menu=menu, **options)
|
|
300
|
+
|
|
301
|
+
# Track cascade icon if present
|
|
302
|
+
if icon_name:
|
|
303
|
+
self.register_icon(parent, parent.index('end'), icon_name, icon_size)
|
|
304
|
+
|
|
305
|
+
# Add all sub-items to this menu
|
|
306
|
+
self._add_menu_items(menu, sub_items)
|
|
307
|
+
|
|
308
|
+
return parent if isinstance(parent, tk.Menu) else menubar
|
|
309
|
+
|
|
310
|
+
def _add_menu_items(self, menu: tk.Menu, items: list[dict]):
|
|
311
|
+
"""Add items to a menu with icon support and theme tracking."""
|
|
312
|
+
for opts in items:
|
|
313
|
+
opts = opts.copy()
|
|
314
|
+
|
|
315
|
+
# Check if this is a submenu (cascade)
|
|
316
|
+
if 'items' in opts:
|
|
317
|
+
# This item has subitems, so it's a cascade
|
|
318
|
+
# Pass to create_menu which will handle the cascade
|
|
319
|
+
self.create_menu(menu, [opts])
|
|
320
|
+
continue
|
|
321
|
+
|
|
322
|
+
# Regular menu item (not a cascade)
|
|
323
|
+
if 'label' in opts:
|
|
324
|
+
opts['label'] = self.translate_label(opts['label'])
|
|
325
|
+
|
|
326
|
+
# Resolve a shortcut spec (registered key or modifier pattern)
|
|
327
|
+
# to a platform-correct accelerator display. Caller-supplied
|
|
328
|
+
# `accelerator` wins (legacy literal pass-through), so existing
|
|
329
|
+
# menus with `accelerator='Ctrl+S'` are not auto-translated.
|
|
330
|
+
shortcut_spec = opts.pop('shortcut', None)
|
|
331
|
+
if shortcut_spec and 'accelerator' not in opts:
|
|
332
|
+
display = format_shortcut(shortcut_spec)
|
|
333
|
+
if display:
|
|
334
|
+
opts['accelerator'] = display
|
|
335
|
+
|
|
336
|
+
icon_spec = opts.pop('icon', None)
|
|
337
|
+
icon_name = None
|
|
338
|
+
icon_size = 0
|
|
339
|
+
if icon_spec:
|
|
340
|
+
icon, icon_name, icon_size = self.resolve_icon(icon_spec)
|
|
341
|
+
if icon is not None:
|
|
342
|
+
opts['image'] = icon
|
|
343
|
+
opts['compound'] = opts.get('compound', 'left')
|
|
344
|
+
|
|
345
|
+
item_type = opts.pop('type', 'command')
|
|
346
|
+
if item_type == 'separator':
|
|
347
|
+
menu.add_separator()
|
|
348
|
+
continue
|
|
349
|
+
|
|
350
|
+
adder = {
|
|
351
|
+
'command': menu.add_command,
|
|
352
|
+
'checkbutton': menu.add_checkbutton,
|
|
353
|
+
'radiobutton': menu.add_radiobutton,
|
|
354
|
+
}.get(item_type, menu.add_command)
|
|
355
|
+
adder(**opts)
|
|
356
|
+
|
|
357
|
+
if icon_name:
|
|
358
|
+
self.register_icon(menu, menu.index('end'), icon_name, icon_size)
|
|
359
|
+
|
|
360
|
+
def _track_icon(self, menu: tk.Menu, index: int, icon_name: str, icon_size: int):
|
|
361
|
+
"""Register a menu item with an icon for automatic theme updates."""
|
|
362
|
+
item_id = f"{id(menu)}_{index}"
|
|
363
|
+
self.menu_items[item_id] = (menu, index, icon_name, icon_size)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def create_menu(parent: Any, items: list[dict]) -> tk.Menu:
|
|
367
|
+
"""Create a menu with icon and theme support.
|
|
368
|
+
|
|
369
|
+
This is a convenience function that creates or retrieves a MenuManager
|
|
370
|
+
for the parent window and uses it to build a menu from a declarative
|
|
371
|
+
structure. The resulting menu automatically updates icon colors when
|
|
372
|
+
the theme changes.
|
|
373
|
+
|
|
374
|
+
Each menu item is defined by a dictionary that can contain:
|
|
375
|
+
- **label** (str): The text displayed for the menu item
|
|
376
|
+
- **icon** (str or dict): Icon specification. Can be a string icon
|
|
377
|
+
name (e.g., "folder2-open") or a dict with 'name' and 'size' keys
|
|
378
|
+
(e.g., {"name": "folder2-open", "size": 20})
|
|
379
|
+
- **items** (list): List of submenu items for cascade menus
|
|
380
|
+
- **command** (callable): Callback function executed when clicked
|
|
381
|
+
- **type** (str): Menu item type - 'command', 'checkbutton',
|
|
382
|
+
'radiobutton', or 'separator'
|
|
383
|
+
- **variable** (Variable): Tkinter variable for checkbutton/radiobutton
|
|
384
|
+
- **value** (Any): Value for radiobutton items
|
|
385
|
+
- **name** (str): Optional Tcl widget name for the cascade. On macOS
|
|
386
|
+
three names trigger system-native menu integration: `'apple'`
|
|
387
|
+
gives you the application menu (Tk auto-fills it with About,
|
|
388
|
+
Hide, Hide Others, Show All, Quit; any items you add appear
|
|
389
|
+
before the system items), `'window'` gets auto-populated with
|
|
390
|
+
open Toplevels, and `'help'` enables system Help search.
|
|
391
|
+
Ignored on Win/Linux.
|
|
392
|
+
- **shortcut** (str): Platform-aware accelerator. Accepts a
|
|
393
|
+
registered shortcut key (e.g. `'save'` if you've called
|
|
394
|
+
`Shortcuts.register('save', 'Mod+S', save_file)`) or a
|
|
395
|
+
modifier pattern like `'Mod+S'`, `'Ctrl+Shift+N'`, `'F5'`.
|
|
396
|
+
Renders as `⌘S` on macOS and `Ctrl+S` on Win/Linux.
|
|
397
|
+
For the actual keypress binding, register the shortcut and call
|
|
398
|
+
`Shortcuts.bind_to(app)` — that's the canonical pathway.
|
|
399
|
+
- **accelerator** (str): Legacy literal display string passed
|
|
400
|
+
straight through to `tk.Menu` (e.g. `'Ctrl+S'`). No platform
|
|
401
|
+
translation. Prefer `shortcut` for new code. If both are
|
|
402
|
+
provided, `accelerator` wins.
|
|
403
|
+
- Any other valid Tkinter menu item options (underline, etc.)
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
parent: The parent widget (Window, Toplevel, or Menu). If a Window
|
|
407
|
+
or Toplevel is provided, the menu becomes the window's menubar.
|
|
408
|
+
If a Menu is provided, items are added to that menu.
|
|
409
|
+
items: List of dictionaries defining the menu structure. Each
|
|
410
|
+
dictionary represents a menu item with its configuration.
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
The created Menu object. For window menubars, this is the menubar
|
|
414
|
+
itself. For menus attached to other widgets, this is the menu.
|
|
415
|
+
|
|
416
|
+
Examples:
|
|
417
|
+
Basic menubar with icons:
|
|
418
|
+
```python
|
|
419
|
+
import bootstack as bs
|
|
420
|
+
|
|
421
|
+
app = bs.App()
|
|
422
|
+
|
|
423
|
+
menu_items = [
|
|
424
|
+
{
|
|
425
|
+
"label": "File",
|
|
426
|
+
"items": [
|
|
427
|
+
{"label": "New", "icon": "file-plus", "command": new_file},
|
|
428
|
+
{"label": "Open", "icon": "folder2-open", "command": open_file},
|
|
429
|
+
{"type": "separator"},
|
|
430
|
+
{"label": "Exit", "icon": "x-circle", "command": app.quit}
|
|
431
|
+
]
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
"label": "Edit",
|
|
435
|
+
"items": [
|
|
436
|
+
{"label": "Undo", "icon": "arrow-counterclockwise"},
|
|
437
|
+
{"label": "Redo", "icon": "arrow-clockwise"}
|
|
438
|
+
]
|
|
439
|
+
}
|
|
440
|
+
]
|
|
441
|
+
|
|
442
|
+
bs.create_menu(app, menu_items)
|
|
443
|
+
app.mainloop()
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Nested submenus with custom icon sizes:
|
|
447
|
+
```python
|
|
448
|
+
menu_items = [
|
|
449
|
+
{
|
|
450
|
+
"label": "File",
|
|
451
|
+
"items": [
|
|
452
|
+
{
|
|
453
|
+
"label": "Recent",
|
|
454
|
+
"icon": {"name": "clock-history", "size": 18},
|
|
455
|
+
"items": [
|
|
456
|
+
{"label": "Document 1.txt"},
|
|
457
|
+
{"label": "Document 2.txt"}
|
|
458
|
+
]
|
|
459
|
+
}
|
|
460
|
+
]
|
|
461
|
+
}
|
|
462
|
+
]
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Menu with checkbuttons and radiobuttons:
|
|
466
|
+
```python
|
|
467
|
+
view_var = bs.BooleanVar(value=True)
|
|
468
|
+
theme_var = bs.StringVar(value="light")
|
|
469
|
+
|
|
470
|
+
menu_items = [
|
|
471
|
+
{
|
|
472
|
+
"label": "View",
|
|
473
|
+
"items": [
|
|
474
|
+
{
|
|
475
|
+
"label": "Show Toolbar",
|
|
476
|
+
"type": "checkbutton",
|
|
477
|
+
"variable": view_var
|
|
478
|
+
}
|
|
479
|
+
]
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
"label": "Theme",
|
|
483
|
+
"items": [
|
|
484
|
+
{
|
|
485
|
+
"label": "Light",
|
|
486
|
+
"type": "radiobutton",
|
|
487
|
+
"variable": theme_var,
|
|
488
|
+
"value": "light"
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
"label": "Dark",
|
|
492
|
+
"type": "radiobutton",
|
|
493
|
+
"variable": theme_var,
|
|
494
|
+
"value": "dark"
|
|
495
|
+
}
|
|
496
|
+
]
|
|
497
|
+
}
|
|
498
|
+
]
|
|
499
|
+
```
|
|
500
|
+
"""
|
|
501
|
+
# Get or create MenuManager for this window
|
|
502
|
+
root = parent.winfo_toplevel() if hasattr(parent, 'winfo_toplevel') else parent
|
|
503
|
+
|
|
504
|
+
if not hasattr(root, '_menu_manager'):
|
|
505
|
+
root._menu_manager = MenuManager(root)
|
|
506
|
+
|
|
507
|
+
return root._menu_manager.create_menu(parent, items)
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
__all__ = ['create_menu']
|