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,479 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import tkinter as tk
|
|
4
|
+
from typing import Literal, Optional, Any, Union
|
|
5
|
+
|
|
6
|
+
from bootstack.widgets.primitives.frame import Frame
|
|
7
|
+
from bootstack.widgets.mixins.configure_mixin import configure_delegate
|
|
8
|
+
from bootstack.widgets.types import Master
|
|
9
|
+
|
|
10
|
+
Direction = Literal["vertical", "horizontal", "row", "column", "row-reverse", "column-reverse"]
|
|
11
|
+
Fill = Literal["none", "x", "y", "both"]
|
|
12
|
+
Side = Literal["top", "bottom", "left", "right"]
|
|
13
|
+
Anchor = Literal["n", "ne", "e", "se", "s", "sw", "w", "nw", "center"]
|
|
14
|
+
Sticky = Literal["n", "s", "e", "w", "ns", "ew", "nsew", "ne", "nw", "se", "sw", "nse", "nsw", "new", "sew", ""]
|
|
15
|
+
AutoFlow = Literal["row", "column", "row-dense", "column-dense", "none"]
|
|
16
|
+
Gap = Union[int, tuple[int, int]]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _parse_size(value: Union[int, str]) -> tuple[int, int]:
|
|
20
|
+
"""Parse size value into (weight, minsize) tuple."""
|
|
21
|
+
if isinstance(value, int):
|
|
22
|
+
return value, 0
|
|
23
|
+
if isinstance(value, str):
|
|
24
|
+
if value == "auto":
|
|
25
|
+
return 0, 0
|
|
26
|
+
if value.endswith("px"):
|
|
27
|
+
try:
|
|
28
|
+
return 0, int(value[:-2])
|
|
29
|
+
except ValueError:
|
|
30
|
+
return 0, 0
|
|
31
|
+
return 0, 0
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class GridFrame(Frame):
|
|
35
|
+
"""A Frame with simplified grid-based layout management and auto-placement.
|
|
36
|
+
|
|
37
|
+
GridFrame extends the bootstack Frame with automatic grid-based
|
|
38
|
+
layout management, including support for row/column definitions,
|
|
39
|
+
gap spacing, auto-placement, and default sticky behavior.
|
|
40
|
+
|
|
41
|
+
Children gridded into this frame automatically receive the frame's
|
|
42
|
+
default layout options. Simply use the standard `grid()` method
|
|
43
|
+
on child widgets — no special `add()` method is needed.
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
```python
|
|
47
|
+
frame = GridFrame(columns=3, gap=10, sticky_items="nsew")
|
|
48
|
+
Label(frame, text="A").grid() # auto-placed at row=0, col=0
|
|
49
|
+
Label(frame, text="B").grid() # auto-placed at row=0, col=1
|
|
50
|
+
Label(frame, text="C").grid() # auto-placed at row=0, col=2
|
|
51
|
+
Label(frame, text="D").grid() # auto-placed at row=1, col=0
|
|
52
|
+
Label(frame, text="Wide").grid(columnspan=2) # spans 2 columns
|
|
53
|
+
```
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
master: Master = None,
|
|
59
|
+
*,
|
|
60
|
+
rows: Optional[Union[int, list[Union[int, str]]]] = None,
|
|
61
|
+
columns: Optional[Union[int, list[Union[int, str]]]] = None,
|
|
62
|
+
gap: Gap = 0,
|
|
63
|
+
sticky_items: Optional[Sticky] = None,
|
|
64
|
+
propagate: Optional[bool] = None,
|
|
65
|
+
auto_flow: AutoFlow = "row",
|
|
66
|
+
**kwargs: Any,
|
|
67
|
+
) -> None:
|
|
68
|
+
"""Create a GridFrame with automatic grid-based layout.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
master: Parent widget. If None, uses the default root window.
|
|
72
|
+
rows: Number of rows as an `int`, or a list of size specs where each
|
|
73
|
+
spec is an `int` weight or a string such as `'auto'` or `'100px'`.
|
|
74
|
+
columns: Number of columns or a list of size specs (same format as `rows`).
|
|
75
|
+
gap: Spacing between cells. An `int` applies the same gap in both
|
|
76
|
+
directions; a `(col_gap, row_gap)` tuple sets them independently.
|
|
77
|
+
sticky_items: Default `sticky` value applied to all children when
|
|
78
|
+
they call `grid()`. If None, no default sticky is applied.
|
|
79
|
+
propagate: If False, the frame will not resize to fit its contents.
|
|
80
|
+
Defaults to None (Tk default behaviour).
|
|
81
|
+
auto_flow: Auto-placement mode — `'row'` (default), `'column'`,
|
|
82
|
+
`'row-dense'`, `'column-dense'`, or `'none'`.
|
|
83
|
+
**kwargs: Additional keyword arguments forwarded to `Frame`.
|
|
84
|
+
"""
|
|
85
|
+
super().__init__(master, **kwargs)
|
|
86
|
+
|
|
87
|
+
self._gap = self._normalize_gap(gap)
|
|
88
|
+
self._default_sticky = sticky_items
|
|
89
|
+
self._auto_flow = auto_flow
|
|
90
|
+
|
|
91
|
+
# Track managed widgets: list of (widget, user_options, computed_position)
|
|
92
|
+
# computed_position is (row, col, rowspan, colspan)
|
|
93
|
+
self._managed: list[tuple[tk.Widget, dict[str, Any], tuple[int, int, int, int]]] = []
|
|
94
|
+
|
|
95
|
+
# Track occupied cells for auto-placement
|
|
96
|
+
self._occupied: set[tuple[int, int]] = set()
|
|
97
|
+
|
|
98
|
+
# Track widgets hidden via grid_remove (still managed, just hidden)
|
|
99
|
+
self._removed: set[tk.Widget] = set()
|
|
100
|
+
|
|
101
|
+
# Auto-placement cursor
|
|
102
|
+
self._next_row = 0
|
|
103
|
+
self._next_col = 0
|
|
104
|
+
|
|
105
|
+
# Parse and store row/column definitions
|
|
106
|
+
self._row_defs: list[tuple[int, int]] = []
|
|
107
|
+
self._col_defs: list[tuple[int, int]] = []
|
|
108
|
+
|
|
109
|
+
if isinstance(rows, int):
|
|
110
|
+
self._row_defs = [(1, 0)] * rows
|
|
111
|
+
elif isinstance(rows, list):
|
|
112
|
+
self._row_defs = [_parse_size(r) for r in rows]
|
|
113
|
+
|
|
114
|
+
if isinstance(columns, int):
|
|
115
|
+
self._col_defs = [(1, 0)] * columns
|
|
116
|
+
elif isinstance(columns, list):
|
|
117
|
+
self._col_defs = [_parse_size(c) for c in columns]
|
|
118
|
+
|
|
119
|
+
# Apply initial row/column configuration
|
|
120
|
+
for i, (weight, minsize) in enumerate(self._row_defs):
|
|
121
|
+
self.rowconfigure(i, weight=weight, minsize=minsize)
|
|
122
|
+
for i, (weight, minsize) in enumerate(self._col_defs):
|
|
123
|
+
self.columnconfigure(i, weight=weight, minsize=minsize)
|
|
124
|
+
|
|
125
|
+
if propagate is not None:
|
|
126
|
+
self.grid_propagate(propagate)
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def _normalize_gap(gap: Gap) -> tuple[int, int]:
|
|
130
|
+
"""Normalize gap to (column_gap, row_gap) tuple."""
|
|
131
|
+
if isinstance(gap, int):
|
|
132
|
+
return gap, gap
|
|
133
|
+
return gap
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def _column_gap(self) -> int:
|
|
137
|
+
"""Get the column gap."""
|
|
138
|
+
return self._gap[0]
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def _row_gap(self) -> int:
|
|
142
|
+
"""Get the row gap."""
|
|
143
|
+
return self._gap[1]
|
|
144
|
+
|
|
145
|
+
@configure_delegate('gap')
|
|
146
|
+
def _delegate_gap(self, value=None) -> tuple[int, int]:
|
|
147
|
+
"""Get or set the gap between cells."""
|
|
148
|
+
if value is None:
|
|
149
|
+
return self._gap
|
|
150
|
+
self._gap = self._normalize_gap(value)
|
|
151
|
+
# Regrid all widgets with new gap
|
|
152
|
+
self._regrid_all()
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def _num_columns(self) -> int:
|
|
156
|
+
"""Number of defined columns, or a large default for auto-placement."""
|
|
157
|
+
return len(self._col_defs) if self._col_defs else 100
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def _num_rows(self) -> int:
|
|
161
|
+
"""Number of defined rows, or a large default for auto-placement."""
|
|
162
|
+
return len(self._row_defs) if self._row_defs else 100
|
|
163
|
+
|
|
164
|
+
def _is_area_free(self, row: int, col: int, rowspan: int, colspan: int) -> bool:
|
|
165
|
+
"""Check if a rectangular area is free."""
|
|
166
|
+
return all(
|
|
167
|
+
(row + dr, col + dc) not in self._occupied
|
|
168
|
+
for dr in range(rowspan)
|
|
169
|
+
for dc in range(colspan)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def _occupy_area(self, row: int, col: int, rowspan: int, colspan: int) -> None:
|
|
173
|
+
"""Mark a rectangular area as occupied."""
|
|
174
|
+
for dr in range(rowspan):
|
|
175
|
+
for dc in range(colspan):
|
|
176
|
+
self._occupied.add((row + dr, col + dc))
|
|
177
|
+
|
|
178
|
+
def _free_area(self, row: int, col: int, rowspan: int, colspan: int) -> None:
|
|
179
|
+
"""Mark a rectangular area as free."""
|
|
180
|
+
for dr in range(rowspan):
|
|
181
|
+
for dc in range(colspan):
|
|
182
|
+
self._occupied.discard((row + dr, col + dc))
|
|
183
|
+
|
|
184
|
+
def _find_next_position(self, rowspan: int = 1, colspan: int = 1) -> tuple[int, int]:
|
|
185
|
+
"""Find the next available position using auto-flow rules."""
|
|
186
|
+
if self._auto_flow == "none":
|
|
187
|
+
return 0, 0
|
|
188
|
+
|
|
189
|
+
max_cols = self._num_columns
|
|
190
|
+
max_rows = self._num_rows
|
|
191
|
+
|
|
192
|
+
if "dense" in self._auto_flow:
|
|
193
|
+
# Dense packing: search from (0,0)
|
|
194
|
+
if self._auto_flow == "row-dense":
|
|
195
|
+
for r in range(max_rows):
|
|
196
|
+
for c in range(max_cols - colspan + 1):
|
|
197
|
+
if self._is_area_free(r, c, rowspan, colspan):
|
|
198
|
+
return r, c
|
|
199
|
+
else: # column-dense
|
|
200
|
+
for c in range(max_cols):
|
|
201
|
+
for r in range(max_rows - rowspan + 1):
|
|
202
|
+
if self._is_area_free(r, c, rowspan, colspan):
|
|
203
|
+
return r, c
|
|
204
|
+
else:
|
|
205
|
+
# Normal flow: continue from cursor
|
|
206
|
+
if self._auto_flow == "row":
|
|
207
|
+
r, c = self._next_row, self._next_col
|
|
208
|
+
while r < max_rows:
|
|
209
|
+
while c <= max_cols - colspan:
|
|
210
|
+
if self._is_area_free(r, c, rowspan, colspan):
|
|
211
|
+
# Update cursor for next placement
|
|
212
|
+
next_c = c + colspan
|
|
213
|
+
if self._col_defs and next_c >= len(self._col_defs):
|
|
214
|
+
self._next_row = r + 1
|
|
215
|
+
self._next_col = 0
|
|
216
|
+
else:
|
|
217
|
+
self._next_row = r
|
|
218
|
+
self._next_col = next_c
|
|
219
|
+
return r, c
|
|
220
|
+
c += 1
|
|
221
|
+
r += 1
|
|
222
|
+
c = 0
|
|
223
|
+
else: # column
|
|
224
|
+
r, c = self._next_row, self._next_col
|
|
225
|
+
while c < max_cols:
|
|
226
|
+
while r <= max_rows - rowspan:
|
|
227
|
+
if self._is_area_free(r, c, rowspan, colspan):
|
|
228
|
+
# Update cursor for next placement
|
|
229
|
+
next_r = r + rowspan
|
|
230
|
+
if self._row_defs and next_r >= len(self._row_defs):
|
|
231
|
+
self._next_col = c + 1
|
|
232
|
+
self._next_row = 0
|
|
233
|
+
else:
|
|
234
|
+
self._next_col = c
|
|
235
|
+
self._next_row = next_r
|
|
236
|
+
return r, c
|
|
237
|
+
r += 1
|
|
238
|
+
c += 1
|
|
239
|
+
r = 0
|
|
240
|
+
|
|
241
|
+
return 0, 0 # Fallback
|
|
242
|
+
|
|
243
|
+
def _compute_gap_padding(
|
|
244
|
+
self, row: int, col: int, user_padx: Any = None, user_pady: Any = None
|
|
245
|
+
) -> dict[str, Any]:
|
|
246
|
+
"""Compute padding that includes gap for non-first rows/columns."""
|
|
247
|
+
result: dict[str, Any] = {}
|
|
248
|
+
|
|
249
|
+
# Handle column gap (padx)
|
|
250
|
+
if col > 0 and self._column_gap:
|
|
251
|
+
gap_padx = (self._column_gap, 0)
|
|
252
|
+
if user_padx is not None:
|
|
253
|
+
result["padx"] = self._merge_padding(gap_padx, user_padx)
|
|
254
|
+
else:
|
|
255
|
+
result["padx"] = gap_padx
|
|
256
|
+
elif user_padx is not None:
|
|
257
|
+
result["padx"] = user_padx
|
|
258
|
+
|
|
259
|
+
# Handle row gap (pady)
|
|
260
|
+
if row > 0 and self._row_gap:
|
|
261
|
+
gap_pady = (self._row_gap, 0)
|
|
262
|
+
if user_pady is not None:
|
|
263
|
+
result["pady"] = self._merge_padding(gap_pady, user_pady)
|
|
264
|
+
else:
|
|
265
|
+
result["pady"] = gap_pady
|
|
266
|
+
elif user_pady is not None:
|
|
267
|
+
result["pady"] = user_pady
|
|
268
|
+
|
|
269
|
+
return result
|
|
270
|
+
|
|
271
|
+
@staticmethod
|
|
272
|
+
def _merge_padding(
|
|
273
|
+
gap_pad: tuple[int, int], user_pad: Any
|
|
274
|
+
) -> tuple[int, int]:
|
|
275
|
+
"""Merge gap padding with user padding."""
|
|
276
|
+
if isinstance(user_pad, int):
|
|
277
|
+
return (gap_pad[0] + user_pad, user_pad)
|
|
278
|
+
elif isinstance(user_pad, (tuple, list)) and len(user_pad) == 2:
|
|
279
|
+
return (gap_pad[0] + user_pad[0], user_pad[1])
|
|
280
|
+
return gap_pad
|
|
281
|
+
|
|
282
|
+
def _build_options(
|
|
283
|
+
self,
|
|
284
|
+
row: int,
|
|
285
|
+
col: int,
|
|
286
|
+
rowspan: int,
|
|
287
|
+
colspan: int,
|
|
288
|
+
user_options: dict[str, Any],
|
|
289
|
+
) -> dict[str, Any]:
|
|
290
|
+
"""Build final grid options."""
|
|
291
|
+
options: dict[str, Any] = {
|
|
292
|
+
"in_": self,
|
|
293
|
+
"row": row,
|
|
294
|
+
"column": col,
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if rowspan > 1:
|
|
298
|
+
options["rowspan"] = rowspan
|
|
299
|
+
if colspan > 1:
|
|
300
|
+
options["columnspan"] = colspan
|
|
301
|
+
|
|
302
|
+
# Apply gap padding
|
|
303
|
+
gap_padding = self._compute_gap_padding(
|
|
304
|
+
row, col,
|
|
305
|
+
user_options.get("padx"),
|
|
306
|
+
user_options.get("pady")
|
|
307
|
+
)
|
|
308
|
+
options.update(gap_padding)
|
|
309
|
+
|
|
310
|
+
# Apply default sticky
|
|
311
|
+
if self._default_sticky and "sticky" not in user_options:
|
|
312
|
+
options["sticky"] = self._default_sticky
|
|
313
|
+
|
|
314
|
+
# User options override (except padx/pady which we handled)
|
|
315
|
+
for key, value in user_options.items():
|
|
316
|
+
if key not in ("padx", "pady", "row", "column"):
|
|
317
|
+
options[key] = value
|
|
318
|
+
|
|
319
|
+
return options
|
|
320
|
+
|
|
321
|
+
def _regrid_all(self) -> None:
|
|
322
|
+
"""Remove and re-grid all widgets."""
|
|
323
|
+
# Ungrid all (except those in _removed which are already hidden)
|
|
324
|
+
for widget, _, _ in self._managed:
|
|
325
|
+
if widget not in self._removed:
|
|
326
|
+
tk.Grid.forget(widget)
|
|
327
|
+
|
|
328
|
+
# Clear and rebuild occupied set
|
|
329
|
+
self._occupied.clear()
|
|
330
|
+
self._next_row = 0
|
|
331
|
+
self._next_col = 0
|
|
332
|
+
|
|
333
|
+
# Regrid in order, recalculating positions
|
|
334
|
+
new_managed: list[tuple[tk.Widget, dict[str, Any], tuple[int, int, int, int]]] = []
|
|
335
|
+
|
|
336
|
+
for widget, user_options, _ in self._managed:
|
|
337
|
+
rowspan = int(user_options.get("rowspan", 1))
|
|
338
|
+
colspan = int(user_options.get("columnspan", 1))
|
|
339
|
+
|
|
340
|
+
# Check if user specified explicit position
|
|
341
|
+
explicit_row = user_options.get("row")
|
|
342
|
+
explicit_col = user_options.get("column")
|
|
343
|
+
|
|
344
|
+
if explicit_row is not None and explicit_col is not None:
|
|
345
|
+
row, col = int(explicit_row), int(explicit_col)
|
|
346
|
+
else:
|
|
347
|
+
row, col = self._find_next_position(rowspan, colspan)
|
|
348
|
+
|
|
349
|
+
self._occupy_area(row, col, rowspan, colspan)
|
|
350
|
+
new_managed.append((widget, user_options, (row, col, rowspan, colspan)))
|
|
351
|
+
|
|
352
|
+
# Only configure visible widgets (not those in _removed)
|
|
353
|
+
if widget not in self._removed:
|
|
354
|
+
options = self._build_options(row, col, rowspan, colspan, user_options)
|
|
355
|
+
tk.Grid.configure(widget, **options)
|
|
356
|
+
|
|
357
|
+
self._managed = new_managed
|
|
358
|
+
|
|
359
|
+
def _find_widget_index(self, widget: tk.Widget) -> int:
|
|
360
|
+
"""Find index of widget in managed list, return -1 if not found."""
|
|
361
|
+
for i, (w, _, _) in enumerate(self._managed):
|
|
362
|
+
if w is widget:
|
|
363
|
+
return i
|
|
364
|
+
return -1
|
|
365
|
+
|
|
366
|
+
# -------------------------------------------------------------------------
|
|
367
|
+
# Hook methods called by GridMixin
|
|
368
|
+
# -------------------------------------------------------------------------
|
|
369
|
+
|
|
370
|
+
def _on_child_grid(self, widget: tk.Widget, **options: Any) -> None:
|
|
371
|
+
"""Hook called when a child widget calls grid().
|
|
372
|
+
|
|
373
|
+
Applies frame defaults, handles gap spacing, auto-placement, and tracks the widget.
|
|
374
|
+
"""
|
|
375
|
+
# Check if widget was hidden via grid_remove and is being restored
|
|
376
|
+
if widget in self._removed and not options:
|
|
377
|
+
# Restore widget to its original position
|
|
378
|
+
self._removed.discard(widget)
|
|
379
|
+
idx = self._find_widget_index(widget)
|
|
380
|
+
if idx >= 0:
|
|
381
|
+
_, user_options, (row, col, rowspan, colspan) = self._managed[idx]
|
|
382
|
+
grid_options = self._build_options(row, col, rowspan, colspan, user_options)
|
|
383
|
+
tk.Grid.configure(widget, **grid_options)
|
|
384
|
+
return
|
|
385
|
+
|
|
386
|
+
# Check if widget is already managed (reconfigure case)
|
|
387
|
+
existing_idx = self._find_widget_index(widget)
|
|
388
|
+
|
|
389
|
+
rowspan = int(options.get("rowspan", 1))
|
|
390
|
+
colspan = int(options.get("columnspan", 1))
|
|
391
|
+
|
|
392
|
+
# Check if user specified explicit position
|
|
393
|
+
explicit_row = options.get("row")
|
|
394
|
+
explicit_col = options.get("column")
|
|
395
|
+
|
|
396
|
+
if existing_idx >= 0:
|
|
397
|
+
# Widget already managed - update its options and regrid all
|
|
398
|
+
_, _, old_pos = self._managed[existing_idx]
|
|
399
|
+
self._free_area(*old_pos)
|
|
400
|
+
self._managed[existing_idx] = (widget, options, (0, 0, 1, 1)) # temp position
|
|
401
|
+
self._regrid_all()
|
|
402
|
+
else:
|
|
403
|
+
# New widget
|
|
404
|
+
if explicit_row is not None and explicit_col is not None:
|
|
405
|
+
row, col = int(explicit_row), int(explicit_col)
|
|
406
|
+
else:
|
|
407
|
+
row, col = self._find_next_position(rowspan, colspan)
|
|
408
|
+
|
|
409
|
+
self._occupy_area(row, col, rowspan, colspan)
|
|
410
|
+
grid_options = self._build_options(row, col, rowspan, colspan, options)
|
|
411
|
+
tk.Grid.configure(widget, **grid_options)
|
|
412
|
+
self._managed.append((widget, options, (row, col, rowspan, colspan)))
|
|
413
|
+
|
|
414
|
+
def _on_child_grid_forget(self, widget: tk.Widget) -> None:
|
|
415
|
+
"""Hook called when a child widget calls grid_forget().
|
|
416
|
+
|
|
417
|
+
Removes widget from tracking and frees its occupied area.
|
|
418
|
+
"""
|
|
419
|
+
idx = self._find_widget_index(widget)
|
|
420
|
+
if idx < 0:
|
|
421
|
+
# Not managed by us, just forget it normally
|
|
422
|
+
tk.Grid.forget(widget)
|
|
423
|
+
return
|
|
424
|
+
|
|
425
|
+
_, _, (row, col, rowspan, colspan) = self._managed[idx]
|
|
426
|
+
tk.Grid.forget(widget)
|
|
427
|
+
self._free_area(row, col, rowspan, colspan)
|
|
428
|
+
self._managed.pop(idx)
|
|
429
|
+
self._removed.discard(widget) # Clean up if it was in removed set
|
|
430
|
+
|
|
431
|
+
def _on_child_grid_remove(self, widget: tk.Widget) -> None:
|
|
432
|
+
"""Hook called when a child widget calls grid_remove().
|
|
433
|
+
|
|
434
|
+
Hides widget but keeps its configuration for later restore via grid().
|
|
435
|
+
The widget's position is preserved so it can be restored with grid().
|
|
436
|
+
"""
|
|
437
|
+
idx = self._find_widget_index(widget)
|
|
438
|
+
if idx < 0:
|
|
439
|
+
# Not managed by us, just remove it normally
|
|
440
|
+
tk.Grid.grid_remove(widget)
|
|
441
|
+
return
|
|
442
|
+
|
|
443
|
+
# Hide the widget but keep it in _managed for position restoration
|
|
444
|
+
tk.Grid.grid_remove(widget)
|
|
445
|
+
self._removed.add(widget)
|
|
446
|
+
|
|
447
|
+
# -------------------------------------------------------------------------
|
|
448
|
+
# Public configuration methods
|
|
449
|
+
# -------------------------------------------------------------------------
|
|
450
|
+
|
|
451
|
+
def configure_row(
|
|
452
|
+
self,
|
|
453
|
+
index: int,
|
|
454
|
+
weight: int = 1,
|
|
455
|
+
minsize: Optional[int] = None,
|
|
456
|
+
pad: Optional[int] = None,
|
|
457
|
+
) -> None:
|
|
458
|
+
"""Configure a row's properties."""
|
|
459
|
+
kwargs: dict[str, Any] = {"weight": weight}
|
|
460
|
+
if minsize is not None:
|
|
461
|
+
kwargs["minsize"] = minsize
|
|
462
|
+
if pad is not None:
|
|
463
|
+
kwargs["pad"] = pad
|
|
464
|
+
self.rowconfigure(index, **kwargs)
|
|
465
|
+
|
|
466
|
+
def configure_column(
|
|
467
|
+
self,
|
|
468
|
+
index: int,
|
|
469
|
+
weight: int = 1,
|
|
470
|
+
minsize: Optional[int] = None,
|
|
471
|
+
pad: Optional[int] = None,
|
|
472
|
+
) -> None:
|
|
473
|
+
"""Configure a column's properties."""
|
|
474
|
+
kwargs: dict[str, Any] = {"weight": weight}
|
|
475
|
+
if minsize is not None:
|
|
476
|
+
kwargs["minsize"] = minsize
|
|
477
|
+
if pad is not None:
|
|
478
|
+
kwargs["pad"] = pad
|
|
479
|
+
self.columnconfigure(index, **kwargs)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from tkinter import ttk
|
|
4
|
+
from typing import Any, Literal, TYPE_CHECKING, TypedDict
|
|
5
|
+
|
|
6
|
+
from typing_extensions import Unpack
|
|
7
|
+
|
|
8
|
+
from bootstack.core.mixins.ttk_state import TtkStateMixin
|
|
9
|
+
from bootstack.core.mixins.widget import WidgetCapabilitiesMixin
|
|
10
|
+
from bootstack.widgets.internal.wrapper_base import TTKWrapperBase
|
|
11
|
+
from bootstack.widgets.mixins import IconMixin, LocalizationMixin, TextSignalMixin
|
|
12
|
+
from bootstack.widgets.types import Master
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from bootstack.core.signals import Signal
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LabelKwargs(TypedDict, total=False):
|
|
19
|
+
# Standard ttk.Label options
|
|
20
|
+
text: Any
|
|
21
|
+
image: Any
|
|
22
|
+
icon: Any
|
|
23
|
+
icon_only: bool
|
|
24
|
+
compound: Literal['text', 'image', 'top', 'bottom', 'left', 'right', 'center', 'none'] | str
|
|
25
|
+
anchor: Any
|
|
26
|
+
justify: Any
|
|
27
|
+
padding: Any
|
|
28
|
+
width: int
|
|
29
|
+
wraplength: Any
|
|
30
|
+
font: Any
|
|
31
|
+
foreground: str
|
|
32
|
+
background: str
|
|
33
|
+
relief: Any
|
|
34
|
+
localize: bool | Literal['auto']
|
|
35
|
+
value_format: dict | str
|
|
36
|
+
state: Literal['normal', 'active', 'disabled', 'readonly'] | str
|
|
37
|
+
takefocus: Any
|
|
38
|
+
format_spec: str | dict
|
|
39
|
+
style: str
|
|
40
|
+
class_: str
|
|
41
|
+
cursor: str
|
|
42
|
+
name: str
|
|
43
|
+
textvariable: Any
|
|
44
|
+
textsignal: Signal[str]
|
|
45
|
+
|
|
46
|
+
# bootstack-specific extensions
|
|
47
|
+
bootstyle: str # DEPRECATED: Use accent and variant instead
|
|
48
|
+
accent: str
|
|
49
|
+
variant: str
|
|
50
|
+
surface: str
|
|
51
|
+
style_options: dict[str, Any]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Label(LocalizationMixin, TextSignalMixin, IconMixin, TTKWrapperBase, WidgetCapabilitiesMixin, TtkStateMixin, ttk.Label):
|
|
55
|
+
"""bootstack wrapper for `ttk.Label` with bootstyle and icon support."""
|
|
56
|
+
|
|
57
|
+
_ttk_base = ttk.Label
|
|
58
|
+
|
|
59
|
+
def __init__(self, master: Master = None, **kwargs: Unpack[LabelKwargs]) -> None:
|
|
60
|
+
"""Create a themed bootstack Label.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
master: Parent widget. If None, uses the default root window.
|
|
64
|
+
|
|
65
|
+
Other Parameters:
|
|
66
|
+
text (str): Text to display in the label.
|
|
67
|
+
textvariable (Variable): Tk variable linked to the label text.
|
|
68
|
+
textsignal (Signal[str]): Reactive Signal linked to the label text (auto-synced with textvariable).
|
|
69
|
+
image (PhotoImage): Image to display.
|
|
70
|
+
icon (str | dict): Theme-aware icon spec handled by the style system.
|
|
71
|
+
icon_only (bool): If True, removes the additional padding reserved for label text.
|
|
72
|
+
compound (str): Placement of the image relative to text.
|
|
73
|
+
anchor (str): Alignment of the label's content within its area.
|
|
74
|
+
justify (str): How to justify multiple lines of text.
|
|
75
|
+
localize (bool | Literal['auto']): Determines the widget's localization mode.
|
|
76
|
+
value_format (str | dict): Format specification for the label value.
|
|
77
|
+
padding (int | tuple): Extra space around the label content.
|
|
78
|
+
width (int): Width of the label in characters.
|
|
79
|
+
wraplength (int): Maximum width before wrapping text.
|
|
80
|
+
font (str | Font): Font for text.
|
|
81
|
+
foreground (str): Text color.
|
|
82
|
+
background (str): Background color.
|
|
83
|
+
relief (str): Border style.
|
|
84
|
+
state (str): Widget state.
|
|
85
|
+
takefocus (bool): Whether the widget participates in focus traversal.
|
|
86
|
+
style (str): Explicit ttk style name (overrides accent/variant).
|
|
87
|
+
accent (str): Accent token for styling, e.g. 'primary', 'danger', 'success'.
|
|
88
|
+
variant (str): Style variant, e.g. 'default', 'inverse'.
|
|
89
|
+
bootstyle (str): DEPRECATED - Use `accent` and `variant` instead.
|
|
90
|
+
Combined style tokens (e.g., 'secondary', 'info').
|
|
91
|
+
surface (str): Optional surface token; otherwise inherited.
|
|
92
|
+
style_options (dict): Optional dict forwarded to the style builder.
|
|
93
|
+
"""
|
|
94
|
+
kwargs.update(style_options=self._capture_style_options(['icon_only', 'icon', 'density'], kwargs))
|
|
95
|
+
super().__init__(master, **kwargs)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from tkinter import ttk
|
|
4
|
+
from typing import Any, Literal, TypedDict
|
|
5
|
+
|
|
6
|
+
from typing_extensions import Unpack
|
|
7
|
+
|
|
8
|
+
from bootstack.core.mixins.ttk_state import TtkStateMixin
|
|
9
|
+
from bootstack.core.mixins.widget import WidgetCapabilitiesMixin
|
|
10
|
+
from bootstack.widgets.internal.wrapper_base import TTKWrapperBase
|
|
11
|
+
from bootstack.widgets.mixins import LocalizationMixin
|
|
12
|
+
from bootstack.widgets.types import Master
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LabelFrameKwargs(TypedDict, total=False):
|
|
16
|
+
# Standard ttk.Labelframe options
|
|
17
|
+
text: Any
|
|
18
|
+
labelanchor: Any
|
|
19
|
+
padding: Any
|
|
20
|
+
relief: Any
|
|
21
|
+
borderwidth: Any
|
|
22
|
+
width: int
|
|
23
|
+
height: int
|
|
24
|
+
style: str
|
|
25
|
+
class_: str
|
|
26
|
+
cursor: str
|
|
27
|
+
name: str
|
|
28
|
+
|
|
29
|
+
# bootstack-specific extensions
|
|
30
|
+
bootstyle: str # DEPRECATED: Use accent and variant instead
|
|
31
|
+
accent: str
|
|
32
|
+
surface: str
|
|
33
|
+
style_options: dict[str, Any]
|
|
34
|
+
localize: bool | Literal['auto']
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class LabelFrame(LocalizationMixin, TTKWrapperBase, WidgetCapabilitiesMixin, TtkStateMixin, ttk.LabelFrame):
|
|
38
|
+
"""bootstack wrapper for `ttk.Labelframe` with bootstyle support."""
|
|
39
|
+
|
|
40
|
+
_ttk_base = ttk.Labelframe
|
|
41
|
+
|
|
42
|
+
def __init__(self, master: Master = None, **kwargs: Unpack[LabelFrameKwargs]) -> None:
|
|
43
|
+
"""Create a themed bootstack Labelframe.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
master: Parent widget. If None, uses the default root window.
|
|
47
|
+
|
|
48
|
+
Other Parameters:
|
|
49
|
+
text (str): Text for the embedded label.
|
|
50
|
+
labelanchor (str): Position of the label relative to the frame.
|
|
51
|
+
padding (int | tuple): Extra internal padding.
|
|
52
|
+
relief (str): Border style.
|
|
53
|
+
borderwidth (int): Border width.
|
|
54
|
+
width (int): Requested width in pixels.
|
|
55
|
+
height (int): Requested height in pixels.
|
|
56
|
+
style (str): Explicit ttk style name (overrides accent/variant).
|
|
57
|
+
accent (str): Accent token for styling, e.g. 'primary', 'secondary'.
|
|
58
|
+
bootstyle (str): DEPRECATED - Use `accent` instead.
|
|
59
|
+
surface (str): Optional surface token; otherwise inherited.
|
|
60
|
+
style_options (dict): Optional dict forwarded to the style builder.
|
|
61
|
+
localize (bool | Literal['auto']): Determines the widget's localization mode.
|
|
62
|
+
"""
|
|
63
|
+
super().__init__(master, **kwargs)
|