bootstack 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bootstack/__init__.py +157 -0
- bootstack/__main__.py +5 -0
- bootstack/_core/__init__.py +21 -0
- bootstack/_core/capabilities/__init__.py +45 -0
- bootstack/_core/capabilities/after.py +103 -0
- bootstack/_core/capabilities/bind.py +192 -0
- bootstack/_core/capabilities/bindtags.py +112 -0
- bootstack/_core/capabilities/busy.py +72 -0
- bootstack/_core/capabilities/clipboard.py +89 -0
- bootstack/_core/capabilities/focus.py +118 -0
- bootstack/_core/capabilities/grab.py +65 -0
- bootstack/_core/capabilities/grid.py +211 -0
- bootstack/_core/capabilities/localization.py +231 -0
- bootstack/_core/capabilities/pack.py +140 -0
- bootstack/_core/capabilities/place.py +113 -0
- bootstack/_core/capabilities/selection.py +136 -0
- bootstack/_core/capabilities/signals.py +244 -0
- bootstack/_core/capabilities/winfo.py +315 -0
- bootstack/_core/colorutils.py +234 -0
- bootstack/_core/exceptions.py +25 -0
- bootstack/_core/images.py +463 -0
- bootstack/_core/mixins/__init__.py +1 -0
- bootstack/_core/mixins/ttk_state.py +35 -0
- bootstack/_core/mixins/widget.py +132 -0
- bootstack/_core/paths.py +49 -0
- bootstack/_core/publisher.py +149 -0
- bootstack/_core/variables.py +62 -0
- bootstack/_runtime/__init__.py +3 -0
- bootstack/_runtime/app.py +930 -0
- bootstack/_runtime/base_window.py +945 -0
- bootstack/_runtime/events.py +399 -0
- bootstack/_runtime/shortcuts.py +496 -0
- bootstack/_runtime/tk_patch.py +43 -0
- bootstack/_runtime/toplevel.py +276 -0
- bootstack/_runtime/utility.py +457 -0
- bootstack/_runtime/visual_focus.py +240 -0
- bootstack/_runtime/window_utilities.py +1123 -0
- bootstack/assets/__init__.py +21 -0
- bootstack/assets/bootstack.ico +0 -0
- bootstack/assets/bootstack.png +0 -0
- bootstack/assets/elements/__init__.py +0 -0
- bootstack/assets/elements/badge-pill.png +0 -0
- bootstack/assets/elements/badge-square.png +0 -0
- bootstack/assets/elements/button-compact.png +0 -0
- bootstack/assets/elements/button-default.png +0 -0
- bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
- bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
- bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
- bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
- bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
- bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
- bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
- bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
- bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
- bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
- bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
- bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
- bootstack/assets/elements/card.png +0 -0
- bootstack/assets/elements/checkbox-checked.png +0 -0
- bootstack/assets/elements/checkbox-indeterminate.png +0 -0
- bootstack/assets/elements/checkbox-unchecked.png +0 -0
- bootstack/assets/elements/field.png +0 -0
- bootstack/assets/elements/input-addon-compact.png +0 -0
- bootstack/assets/elements/input-addon-default.png +0 -0
- bootstack/assets/elements/input-compact.png +0 -0
- bootstack/assets/elements/input-default.png +0 -0
- bootstack/assets/elements/list-item-separated.png +0 -0
- bootstack/assets/elements/list-item.png +0 -0
- bootstack/assets/elements/listrow-compact.png +0 -0
- bootstack/assets/elements/listrow-default.png +0 -0
- bootstack/assets/elements/manifest.toml +361 -0
- bootstack/assets/elements/menu-item.png +0 -0
- bootstack/assets/elements/navitem-compact.png +0 -0
- bootstack/assets/elements/navitem-default.png +0 -0
- bootstack/assets/elements/progressbar-h-compact.png +0 -0
- bootstack/assets/elements/progressbar-h-default.png +0 -0
- bootstack/assets/elements/progressbar-v-compact.png +0 -0
- bootstack/assets/elements/progressbar-v-default.png +0 -0
- bootstack/assets/elements/radiobutton.png +0 -0
- bootstack/assets/elements/scrollbar-horizontal.png +0 -0
- bootstack/assets/elements/scrollbar-vertical.png +0 -0
- bootstack/assets/elements/slider-handle.png +0 -0
- bootstack/assets/elements/slider-track-h.png +0 -0
- bootstack/assets/elements/slider-track-v.png +0 -0
- bootstack/assets/elements/switch-off.png +0 -0
- bootstack/assets/elements/switch-on.png +0 -0
- bootstack/assets/elements/tab-h.png +0 -0
- bootstack/assets/elements/tab-v.png +0 -0
- bootstack/assets/icons/bootstrap.ttf +0 -0
- bootstack/assets/icons/glyphmap.json +2080 -0
- bootstack/assets/icons/icon_metrics.json +12470 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +878 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +878 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +854 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +844 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +917 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +844 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +844 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +845 -0
- bootstack/cli/__init__.py +133 -0
- bootstack/cli/__main__.py +6 -0
- bootstack/cli/add.py +395 -0
- bootstack/cli/appicon.py +285 -0
- bootstack/cli/build.py +115 -0
- bootstack/cli/config.py +313 -0
- bootstack/cli/demo.py +564 -0
- bootstack/cli/dev.py +153 -0
- bootstack/cli/doctor.py +195 -0
- bootstack/cli/icons.py +98 -0
- bootstack/cli/promote.py +120 -0
- bootstack/cli/pyinstaller.py +268 -0
- bootstack/cli/run.py +95 -0
- bootstack/cli/start.py +117 -0
- bootstack/cli/templates/__init__.py +931 -0
- bootstack/clipboard.py +48 -0
- bootstack/constants.py +318 -0
- bootstack/data/README.md +615 -0
- bootstack/data/__init__.py +78 -0
- bootstack/data/_observable.py +276 -0
- bootstack/data/base.py +780 -0
- bootstack/data/file_source.py +367 -0
- bootstack/data/memory_source.py +450 -0
- bootstack/data/query.py +367 -0
- bootstack/data/readers.py +289 -0
- bootstack/data/sqlite_source.py +869 -0
- bootstack/data/types.py +354 -0
- bootstack/data/writers.py +232 -0
- bootstack/dev/__init__.py +36 -0
- bootstack/dev/_capture.py +141 -0
- bootstack/dev/_env.py +43 -0
- bootstack/dev/_registry.py +140 -0
- bootstack/dev/_reloader.py +351 -0
- bootstack/dev/_reset.py +50 -0
- bootstack/dev/_watcher.py +91 -0
- bootstack/dialogs/__init__.py +948 -0
- bootstack/dialogs/_impl/__init__.py +47 -0
- bootstack/dialogs/_impl/colorchooser.py +588 -0
- bootstack/dialogs/_impl/datedialog.py +450 -0
- bootstack/dialogs/_impl/dialog.py +594 -0
- bootstack/dialogs/_impl/filterdialog.py +358 -0
- bootstack/dialogs/_impl/fontdialog.py +364 -0
- bootstack/dialogs/_impl/formdialog.py +564 -0
- bootstack/dialogs/_impl/message.py +486 -0
- bootstack/dialogs/_impl/query.py +570 -0
- bootstack/errors.py +67 -0
- bootstack/events/__init__.py +111 -0
- bootstack/events/_event.py +135 -0
- bootstack/events/_payloads.py +539 -0
- bootstack/events/_subscription.py +38 -0
- bootstack/i18n/README.md +77 -0
- bootstack/i18n/__init__.py +23 -0
- bootstack/i18n/catalog.py +121 -0
- bootstack/i18n/intl_format.py +584 -0
- bootstack/i18n/msgcat.py +425 -0
- bootstack/i18n/specs.py +156 -0
- bootstack/images.py +563 -0
- bootstack/py.typed +1 -0
- bootstack/scheduling/__init__.py +11 -0
- bootstack/scheduling/_schedule.py +218 -0
- bootstack/shortcuts.py +21 -0
- bootstack/signals/README.md +98 -0
- bootstack/signals/__init__.py +10 -0
- bootstack/signals/integration.py +100 -0
- bootstack/signals/signal.py +353 -0
- bootstack/signals/types.py +6 -0
- bootstack/store.py +286 -0
- bootstack/streams/__init__.py +12 -0
- bootstack/streams/_stream.py +321 -0
- bootstack/style/__init__.py +38 -0
- bootstack/style/builders/__init__.py +51 -0
- bootstack/style/builders/badge.py +46 -0
- bootstack/style/builders/button.py +339 -0
- bootstack/style/builders/buttongroup.py +311 -0
- bootstack/style/builders/calendar.py +271 -0
- bootstack/style/builders/checkbutton.py +110 -0
- bootstack/style/builders/combobox.py +113 -0
- bootstack/style/builders/contextmenu.py +268 -0
- bootstack/style/builders/entry.py +82 -0
- bootstack/style/builders/expander.py +148 -0
- bootstack/style/builders/field.py +335 -0
- bootstack/style/builders/frame.py +50 -0
- bootstack/style/builders/label.py +28 -0
- bootstack/style/builders/labelframe.py +34 -0
- bootstack/style/builders/listview.py +369 -0
- bootstack/style/builders/menubar.py +91 -0
- bootstack/style/builders/menubutton.py +359 -0
- bootstack/style/builders/panedwindow.py +25 -0
- bootstack/style/builders/progressbar.py +67 -0
- bootstack/style/builders/radiobutton.py +99 -0
- bootstack/style/builders/scale.py +64 -0
- bootstack/style/builders/scrollbar.py +225 -0
- bootstack/style/builders/separator.py +49 -0
- bootstack/style/builders/sidenav.py +643 -0
- bootstack/style/builders/sizegrip.py +15 -0
- bootstack/style/builders/spinbox.py +119 -0
- bootstack/style/builders/switch.py +70 -0
- bootstack/style/builders/tabitem.py +204 -0
- bootstack/style/builders/togglegroup.py +294 -0
- bootstack/style/builders/toolbutton.py +275 -0
- bootstack/style/builders/tooltip.py +26 -0
- bootstack/style/builders/treeview.py +193 -0
- bootstack/style/builders/utils.py +455 -0
- bootstack/style/builders_tk/__init__.py +16 -0
- bootstack/style/builders_tk/defaults.py +229 -0
- bootstack/style/element.py +173 -0
- bootstack/style/fonts.py +123 -0
- bootstack/style/style.py +609 -0
- bootstack/style/style_builder_base.py +716 -0
- bootstack/style/style_builder_mixed.py +93 -0
- bootstack/style/style_builder_tk.py +109 -0
- bootstack/style/style_builder_ttk.py +353 -0
- bootstack/style/style_resolver.py +447 -0
- bootstack/style/theme.py +245 -0
- bootstack/style/theme_provider.py +471 -0
- bootstack/style/themes/__init__.py +128 -0
- bootstack/style/tk_patch.py +5 -0
- bootstack/style/token_maps.py +41 -0
- bootstack/style/types.py +32 -0
- bootstack/style/typography.py +523 -0
- bootstack/style/utility.py +746 -0
- bootstack/types.py +39 -0
- bootstack/validation/__init__.py +6 -0
- bootstack/validation/types.py +15 -0
- bootstack/validation/validation_result.py +17 -0
- bootstack/validation/validation_rules.py +205 -0
- bootstack/widgets/__init__.py +74 -0
- bootstack/widgets/_core/__init__.py +31 -0
- bootstack/widgets/_core/app_config.py +266 -0
- bootstack/widgets/_core/base.py +461 -0
- bootstack/widgets/_core/container.py +334 -0
- bootstack/widgets/_core/context.py +35 -0
- bootstack/widgets/_core/events.py +130 -0
- bootstack/widgets/_core/field_mixin.py +353 -0
- bootstack/widgets/_core/icon_image_props.py +72 -0
- bootstack/widgets/_core/image_binding.py +93 -0
- bootstack/widgets/_core/navmodel.py +345 -0
- bootstack/widgets/_core/options.py +210 -0
- bootstack/widgets/_core/selection_group.py +130 -0
- bootstack/widgets/_core/window_controls.py +96 -0
- bootstack/widgets/_core/window_menu.py +246 -0
- bootstack/widgets/_impl/__init__.py +1 -0
- bootstack/widgets/_impl/_internal/__init__.py +0 -0
- bootstack/widgets/_impl/_internal/wrapper_base.py +307 -0
- bootstack/widgets/_impl/_parts/__init__.py +11 -0
- bootstack/widgets/_impl/_parts/numberentry_part.py +385 -0
- bootstack/widgets/_impl/_parts/spinnerentry_part.py +434 -0
- bootstack/widgets/_impl/_parts/textentry_part.py +406 -0
- bootstack/widgets/_impl/composites/__init__.py +33 -0
- bootstack/widgets/_impl/composites/_dateutils.py +33 -0
- bootstack/widgets/_impl/composites/_image_fit.py +105 -0
- bootstack/widgets/_impl/composites/accordion.py +381 -0
- bootstack/widgets/_impl/composites/avatar.py +191 -0
- bootstack/widgets/_impl/composites/buttongroup.py +371 -0
- bootstack/widgets/_impl/composites/calendar.py +972 -0
- bootstack/widgets/_impl/composites/carousel.py +567 -0
- bootstack/widgets/_impl/composites/chart.py +882 -0
- bootstack/widgets/_impl/composites/compositeframe.py +298 -0
- bootstack/widgets/_impl/composites/contextmenu.py +1951 -0
- bootstack/widgets/_impl/composites/dateentry.py +404 -0
- bootstack/widgets/_impl/composites/dropdownbutton.py +325 -0
- bootstack/widgets/_impl/composites/expander.py +515 -0
- bootstack/widgets/_impl/composites/field.py +670 -0
- bootstack/widgets/_impl/composites/form.py +1066 -0
- bootstack/widgets/_impl/composites/gallery.py +551 -0
- bootstack/widgets/_impl/composites/list/__init__.py +15 -0
- bootstack/widgets/_impl/composites/list/listitem.py +802 -0
- bootstack/widgets/_impl/composites/list/listview.py +1433 -0
- bootstack/widgets/_impl/composites/menu/__init__.py +18 -0
- bootstack/widgets/_impl/composites/menu/model.py +358 -0
- bootstack/widgets/_impl/composites/menu/render_native.py +134 -0
- bootstack/widgets/_impl/composites/menu/render_themed.py +134 -0
- bootstack/widgets/_impl/composites/meter.py +860 -0
- bootstack/widgets/_impl/composites/numericentry.py +201 -0
- bootstack/widgets/_impl/composites/pagestack.py +395 -0
- bootstack/widgets/_impl/composites/passwordentry.py +142 -0
- bootstack/widgets/_impl/composites/pathentry.py +168 -0
- bootstack/widgets/_impl/composites/picture.py +289 -0
- bootstack/widgets/_impl/composites/radiogroup.py +511 -0
- bootstack/widgets/_impl/composites/scrolledtext.py +375 -0
- bootstack/widgets/_impl/composites/scrolledtext.pyi +186 -0
- bootstack/widgets/_impl/composites/scrollview.py +764 -0
- bootstack/widgets/_impl/composites/selectbox.py +1026 -0
- bootstack/widgets/_impl/composites/shell/__init__.py +40 -0
- bootstack/widgets/_impl/composites/shell/content_host.py +52 -0
- bootstack/widgets/_impl/composites/shell/layout.py +335 -0
- bootstack/widgets/_impl/composites/shell/nav_panel.py +345 -0
- bootstack/widgets/_impl/composites/shell/providers.py +558 -0
- bootstack/widgets/_impl/composites/shell/rail.py +117 -0
- bootstack/widgets/_impl/composites/shell/shell.py +581 -0
- bootstack/widgets/_impl/composites/shell/workspace.py +273 -0
- bootstack/widgets/_impl/composites/sidenav/__init__.py +16 -0
- bootstack/widgets/_impl/composites/sidenav/header.py +81 -0
- bootstack/widgets/_impl/composites/sidenav/separator.py +44 -0
- bootstack/widgets/_impl/composites/slider/__init__.py +7 -0
- bootstack/widgets/_impl/composites/slider/_shared.py +195 -0
- bootstack/widgets/_impl/composites/slider/rangeslider.py +982 -0
- bootstack/widgets/_impl/composites/slider/slider.py +851 -0
- bootstack/widgets/_impl/composites/spinnerentry.py +185 -0
- bootstack/widgets/_impl/composites/tableview/__init__.py +5 -0
- bootstack/widgets/_impl/composites/tableview/tableview.py +3388 -0
- bootstack/widgets/_impl/composites/tableview/types.py +169 -0
- bootstack/widgets/_impl/composites/tabs/__init__.py +23 -0
- bootstack/widgets/_impl/composites/tabs/tabitem.py +389 -0
- bootstack/widgets/_impl/composites/tabs/tabs.py +974 -0
- bootstack/widgets/_impl/composites/tabs/tabview.py +650 -0
- bootstack/widgets/_impl/composites/textarea/__init__.py +26 -0
- bootstack/widgets/_impl/composites/textarea/change.py +46 -0
- bootstack/widgets/_impl/composites/textarea/codeeditor.py +526 -0
- bootstack/widgets/_impl/composites/textarea/core.py +495 -0
- bootstack/widgets/_impl/composites/textarea/decoration.py +42 -0
- bootstack/widgets/_impl/composites/textarea/diff.py +127 -0
- bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +1 -0
- bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +138 -0
- bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +158 -0
- bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +138 -0
- bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +312 -0
- bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +177 -0
- bootstack/widgets/_impl/composites/textarea/filter.py +171 -0
- bootstack/widgets/_impl/composites/textarea/search_overlay.py +459 -0
- bootstack/widgets/_impl/composites/textarea/sidebar.py +88 -0
- bootstack/widgets/_impl/composites/textarea/style_registry.py +178 -0
- bootstack/widgets/_impl/composites/textarea/textarea.py +606 -0
- bootstack/widgets/_impl/composites/textarea/undo.py +217 -0
- bootstack/widgets/_impl/composites/textentry.py +57 -0
- bootstack/widgets/_impl/composites/timeentry.py +176 -0
- bootstack/widgets/_impl/composites/toast.py +390 -0
- bootstack/widgets/_impl/composites/toast_stack.py +156 -0
- bootstack/widgets/_impl/composites/togglegroup.py +418 -0
- bootstack/widgets/_impl/composites/toolbar.py +608 -0
- bootstack/widgets/_impl/composites/tooltip.py +491 -0
- bootstack/widgets/_impl/composites/tree/__init__.py +7 -0
- bootstack/widgets/_impl/composites/tree/source_binding.py +136 -0
- bootstack/widgets/_impl/composites/tree/treeitem.py +393 -0
- bootstack/widgets/_impl/composites/tree/treenode.py +174 -0
- bootstack/widgets/_impl/composites/tree/treeview.py +841 -0
- bootstack/widgets/_impl/mixins/__init__.py +24 -0
- bootstack/widgets/_impl/mixins/configure_mixin.py +216 -0
- bootstack/widgets/_impl/mixins/entry_mixin.py +134 -0
- bootstack/widgets/_impl/mixins/font_mixin.py +368 -0
- bootstack/widgets/_impl/mixins/icon_mixin.py +61 -0
- bootstack/widgets/_impl/mixins/localization_mixin.py +253 -0
- bootstack/widgets/_impl/mixins/signal_mixin.py +268 -0
- bootstack/widgets/_impl/mixins/validation_mixin.py +226 -0
- bootstack/widgets/_impl/primitives/__init__.py +49 -0
- bootstack/widgets/_impl/primitives/_menubutton.py +107 -0
- bootstack/widgets/_impl/primitives/badge.py +45 -0
- bootstack/widgets/_impl/primitives/button.py +76 -0
- bootstack/widgets/_impl/primitives/card.py +45 -0
- bootstack/widgets/_impl/primitives/checkbutton.py +124 -0
- bootstack/widgets/_impl/primitives/checktoggle.py +62 -0
- bootstack/widgets/_impl/primitives/combobox.py +156 -0
- bootstack/widgets/_impl/primitives/entry.py +87 -0
- bootstack/widgets/_impl/primitives/flexframe.py +448 -0
- bootstack/widgets/_impl/primitives/frame.py +185 -0
- bootstack/widgets/_impl/primitives/gridframe.py +546 -0
- bootstack/widgets/_impl/primitives/label.py +84 -0
- bootstack/widgets/_impl/primitives/labelframe.py +54 -0
- bootstack/widgets/_impl/primitives/optionmenu.py +387 -0
- bootstack/widgets/_impl/primitives/packframe.py +227 -0
- bootstack/widgets/_impl/primitives/panedwindow.py +45 -0
- bootstack/widgets/_impl/primitives/progressbar.py +83 -0
- bootstack/widgets/_impl/primitives/radiobutton.py +115 -0
- bootstack/widgets/_impl/primitives/radiotoggle.py +54 -0
- bootstack/widgets/_impl/primitives/scrollbar.py +42 -0
- bootstack/widgets/_impl/primitives/separator.py +43 -0
- bootstack/widgets/_impl/primitives/sizegrip.py +33 -0
- bootstack/widgets/_impl/primitives/spinbox.py +95 -0
- bootstack/widgets/_impl/primitives/switch.py +44 -0
- bootstack/widgets/_impl/primitives/treeview.py +69 -0
- bootstack/widgets/app.py +371 -0
- bootstack/widgets/appshell.py +1179 -0
- bootstack/widgets/avatar.py +140 -0
- bootstack/widgets/boolean_controls.py +455 -0
- bootstack/widgets/button.py +224 -0
- bootstack/widgets/buttongroup.py +239 -0
- bootstack/widgets/calendar.py +195 -0
- bootstack/widgets/card.py +159 -0
- bootstack/widgets/carousel.py +241 -0
- bootstack/widgets/chart.py +302 -0
- bootstack/widgets/codeeditor.py +675 -0
- bootstack/widgets/contextmenu.py +371 -0
- bootstack/widgets/datatable.py +688 -0
- bootstack/widgets/datefield.py +395 -0
- bootstack/widgets/divider.py +60 -0
- bootstack/widgets/expander.py +579 -0
- bootstack/widgets/form.py +200 -0
- bootstack/widgets/gallery.py +250 -0
- bootstack/widgets/gauge.py +168 -0
- bootstack/widgets/grid.py +121 -0
- bootstack/widgets/groupbox.py +162 -0
- bootstack/widgets/label.py +229 -0
- bootstack/widgets/listview.py +354 -0
- bootstack/widgets/menubutton.py +419 -0
- bootstack/widgets/numberfield.py +431 -0
- bootstack/widgets/pagestack.py +345 -0
- bootstack/widgets/passwordfield.py +383 -0
- bootstack/widgets/pathfield.py +454 -0
- bootstack/widgets/picture.py +251 -0
- bootstack/widgets/progressbar.py +106 -0
- bootstack/widgets/radio_variants.py +271 -0
- bootstack/widgets/radiogroup.py +228 -0
- bootstack/widgets/scrollbar.py +64 -0
- bootstack/widgets/scrollview.py +186 -0
- bootstack/widgets/select.py +302 -0
- bootstack/widgets/selectbutton.py +182 -0
- bootstack/widgets/sidebar_toggle.py +130 -0
- bootstack/widgets/sizegrip.py +42 -0
- bootstack/widgets/slider.py +408 -0
- bootstack/widgets/spinbox.py +147 -0
- bootstack/widgets/spinnerfield.py +413 -0
- bootstack/widgets/splash.py +367 -0
- bootstack/widgets/splitview.py +482 -0
- bootstack/widgets/stacks.py +249 -0
- bootstack/widgets/statusbar.py +189 -0
- bootstack/widgets/tabs.py +425 -0
- bootstack/widgets/textarea.py +459 -0
- bootstack/widgets/textfield.py +410 -0
- bootstack/widgets/theme_toggle.py +92 -0
- bootstack/widgets/timefield.py +340 -0
- bootstack/widgets/toast.py +314 -0
- bootstack/widgets/togglegroup.py +231 -0
- bootstack/widgets/toolbar.py +347 -0
- bootstack/widgets/tooltip.py +94 -0
- bootstack/widgets/tree.py +648 -0
- bootstack/widgets/types.py +305 -0
- bootstack/widgets/window.py +307 -0
- bootstack-0.1.0.dist-info/METADATA +301 -0
- bootstack-0.1.0.dist-info/RECORD +471 -0
- bootstack-0.1.0.dist-info/WHEEL +5 -0
- bootstack-0.1.0.dist-info/entry_points.txt +2 -0
- bootstack-0.1.0.dist-info/licenses/LICENSE +21 -0
- bootstack-0.1.0.dist-info/licenses/NOTICE +10 -0
- bootstack-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
"""TabView widget - a tabbed container combining Tabs with PageStack."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
__all__ = ['TabView']
|
|
5
|
+
|
|
6
|
+
import tkinter as tk
|
|
7
|
+
from typing_extensions import Unpack
|
|
8
|
+
from typing import Any, Callable, Literal
|
|
9
|
+
|
|
10
|
+
from bootstack.widgets._impl.primitives.frame import Frame, FrameKwargs
|
|
11
|
+
from bootstack.widgets._impl.composites.tabs.tabs import Tabs
|
|
12
|
+
from bootstack.widgets._impl.composites.tabs.tabitem import TabItem
|
|
13
|
+
from bootstack.events import (
|
|
14
|
+
ChangeMethod, ChangeReason,
|
|
15
|
+
TabActivateEvent, TabChangeEvent, TabDeactivateEvent, TabRef,
|
|
16
|
+
)
|
|
17
|
+
from bootstack.widgets._impl.composites.pagestack import PageStack
|
|
18
|
+
from bootstack.widgets.types import Master
|
|
19
|
+
from bootstack._core import NavigationError
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TabView(Frame):
|
|
23
|
+
"""A tabbed container that combines Tabs with a PageStack.
|
|
24
|
+
|
|
25
|
+
TabView provides a complete tabbed interface where each tab corresponds
|
|
26
|
+
to a page in the page stack. Selecting a tab navigates to the associated
|
|
27
|
+
page.
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
master: Master = None,
|
|
34
|
+
orient: Literal['horizontal', 'vertical'] = 'horizontal',
|
|
35
|
+
show_divider: bool = None,
|
|
36
|
+
compound: Literal['left', 'right', 'top', 'bottom', 'center', 'none'] = 'left',
|
|
37
|
+
tab_width: None | int | Literal['stretch'] = None,
|
|
38
|
+
tab_padding: tuple = (12, 8),
|
|
39
|
+
tab_anchor: str = None,
|
|
40
|
+
enable_closing: bool | Literal['hover'] = False,
|
|
41
|
+
enable_adding: bool = False,
|
|
42
|
+
max_tabs: int | None = None,
|
|
43
|
+
accent: str = None,
|
|
44
|
+
**kwargs: Unpack[FrameKwargs]
|
|
45
|
+
):
|
|
46
|
+
"""Create a TabView widget.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
master: Parent widget. If None, uses the default root window.
|
|
50
|
+
orient: Orientation of the tab bar. 'horizontal' places tabs above
|
|
51
|
+
the content, 'vertical' places tabs to the left. Default is 'horizontal'.
|
|
52
|
+
show_divider: Whether to show a divider between tabs and content.
|
|
53
|
+
compound: How to position icon relative to text in tabs.
|
|
54
|
+
tab_width: Width of tabs (None, integer, or 'stretch').
|
|
55
|
+
tab_padding: Padding for all tabs as (horizontal, vertical).
|
|
56
|
+
tab_anchor: Anchor for tab text/icon alignment. If None, defaults
|
|
57
|
+
to 'w' for vertical orientation, 'center' for horizontal.
|
|
58
|
+
enable_closing: Default close button visibility for all tabs.
|
|
59
|
+
True=always visible, False=hidden, 'hover'=visible on hover.
|
|
60
|
+
Can be overridden per-tab via `closable` in add().
|
|
61
|
+
enable_adding: If True, shows an "add" button that fires `<<TabAdd>>`.
|
|
62
|
+
max_tabs: Maximum tab count; the add button disables at the limit.
|
|
63
|
+
None (default) means no limit.
|
|
64
|
+
accent: Accent token for styling.
|
|
65
|
+
**kwargs: Additional arguments passed to Frame.
|
|
66
|
+
"""
|
|
67
|
+
super().__init__(master, **kwargs)
|
|
68
|
+
|
|
69
|
+
self._orient = orient
|
|
70
|
+
self._accent = accent
|
|
71
|
+
|
|
72
|
+
# Create internal variable for tab selection
|
|
73
|
+
self._tab_variable = tk.StringVar()
|
|
74
|
+
|
|
75
|
+
# Create tabs container
|
|
76
|
+
self._tabs = Tabs(
|
|
77
|
+
self,
|
|
78
|
+
orient=orient,
|
|
79
|
+
show_divider=show_divider,
|
|
80
|
+
compound=compound,
|
|
81
|
+
tab_width=tab_width,
|
|
82
|
+
tab_padding=tab_padding,
|
|
83
|
+
tab_anchor=tab_anchor,
|
|
84
|
+
enable_closing=enable_closing,
|
|
85
|
+
enable_adding=enable_adding,
|
|
86
|
+
max_tabs=max_tabs,
|
|
87
|
+
variable=self._tab_variable,
|
|
88
|
+
accent=accent,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Create page stack
|
|
92
|
+
self._page_stack = PageStack(self)
|
|
93
|
+
|
|
94
|
+
# Layout based on orientation
|
|
95
|
+
if orient == 'horizontal':
|
|
96
|
+
self._tabs.pack(side='top', fill='x')
|
|
97
|
+
self._page_stack.pack(side='top', fill='both', expand=True)
|
|
98
|
+
else:
|
|
99
|
+
self._tabs.pack(side='left', fill='y')
|
|
100
|
+
self._page_stack.pack(side='left', fill='both', expand=True)
|
|
101
|
+
|
|
102
|
+
# Track tabs by key for removal
|
|
103
|
+
self._tab_map: dict[str, TabItem] = {}
|
|
104
|
+
|
|
105
|
+
# Tab text tokens for locale retranslation — key → original text
|
|
106
|
+
self._tab_locale_tokens: dict[str, str] = {}
|
|
107
|
+
|
|
108
|
+
# Hidden tab keys (tab bar hidden, page still registered)
|
|
109
|
+
self._hidden_tabs: set[str] = set()
|
|
110
|
+
|
|
111
|
+
# Typed-event state — defaults represent a user click
|
|
112
|
+
self._prev_key: str | None = None
|
|
113
|
+
self._next_reason: ChangeReason = "user"
|
|
114
|
+
self._next_via: ChangeMethod = "click"
|
|
115
|
+
|
|
116
|
+
# Forward tab strip events to self so callers can bind at the TabView level
|
|
117
|
+
self._tabs.bind('<<TabAdd>>', lambda e: self.event_generate('<<TabAdd>>', when='tail'), add='+')
|
|
118
|
+
self._tabs.bind('<<TabClose>>', lambda e: self.event_generate('<<TabClose>>', data=getattr(e, 'data', None), when='tail'), add='+')
|
|
119
|
+
|
|
120
|
+
# Bind variable trace to navigate pages and fire typed events
|
|
121
|
+
self._trace_id = self._tab_variable.trace_add('write', self._on_tab_selected)
|
|
122
|
+
|
|
123
|
+
# Locale retranslation
|
|
124
|
+
self.winfo_toplevel().bind(
|
|
125
|
+
"<<LocaleChanged>>", self._on_locale_changed, add='+'
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Clean up trace on destroy
|
|
129
|
+
self.bind('<Destroy>', self._on_destroy, add='+')
|
|
130
|
+
|
|
131
|
+
# -------------------------------------------------------------------------
|
|
132
|
+
# Internal
|
|
133
|
+
# -------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
def _on_destroy(self, event=None):
|
|
136
|
+
"""Clean up variable trace when widget is destroyed."""
|
|
137
|
+
if event.widget is not self:
|
|
138
|
+
return
|
|
139
|
+
if self._trace_id is not None:
|
|
140
|
+
try:
|
|
141
|
+
self._tab_variable.trace_remove('write', self._trace_id)
|
|
142
|
+
except Exception:
|
|
143
|
+
pass
|
|
144
|
+
self._trace_id = None
|
|
145
|
+
|
|
146
|
+
def _on_tab_selected(self, *args):
|
|
147
|
+
"""Handle tab selection — navigate page stack and fire typed events."""
|
|
148
|
+
key = self._tab_variable.get()
|
|
149
|
+
if not key or key not in self._tab_map:
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
prev_key = self._prev_key
|
|
153
|
+
reason: ChangeReason = self._next_reason
|
|
154
|
+
via: ChangeMethod = self._next_via
|
|
155
|
+
|
|
156
|
+
# Reset to user-click defaults for the next change
|
|
157
|
+
self._next_reason = "user"
|
|
158
|
+
self._next_via = "click"
|
|
159
|
+
|
|
160
|
+
if key == prev_key:
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
# Navigate page stack
|
|
164
|
+
current_page = self._page_stack.current()
|
|
165
|
+
if current_page is None or current_page[0] != key:
|
|
166
|
+
self._page_stack.navigate(key)
|
|
167
|
+
|
|
168
|
+
# Build typed event payload
|
|
169
|
+
def _tab_ref(k: str) -> TabRef:
|
|
170
|
+
return TabRef(key=k, text=self._tab_locale_tokens.get(k, k))
|
|
171
|
+
|
|
172
|
+
current_ref = _tab_ref(key)
|
|
173
|
+
previous_ref = _tab_ref(prev_key) if prev_key else None
|
|
174
|
+
|
|
175
|
+
payload = TabChangeEvent(
|
|
176
|
+
current=current_ref,
|
|
177
|
+
previous=previous_ref,
|
|
178
|
+
reason=reason,
|
|
179
|
+
via=via,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
self.event_generate('<<TabChanged>>', data=payload, when='tail')
|
|
183
|
+
|
|
184
|
+
# Per-tab lifecycle events
|
|
185
|
+
if prev_key and prev_key in self._tab_map:
|
|
186
|
+
deactivate = TabDeactivateEvent(
|
|
187
|
+
key=prev_key,
|
|
188
|
+
text=self._tab_locale_tokens.get(prev_key, prev_key),
|
|
189
|
+
)
|
|
190
|
+
self.event_generate('<<TabDeactivate>>', data=deactivate, when='tail')
|
|
191
|
+
|
|
192
|
+
activate = TabActivateEvent(
|
|
193
|
+
key=key,
|
|
194
|
+
text=self._tab_locale_tokens.get(key, key),
|
|
195
|
+
)
|
|
196
|
+
self.event_generate('<<TabActivate>>', data=activate, when='tail')
|
|
197
|
+
|
|
198
|
+
self._prev_key = key
|
|
199
|
+
|
|
200
|
+
def _on_locale_changed(self, _event=None):
|
|
201
|
+
"""Retranslate all tab labels when the locale changes."""
|
|
202
|
+
try:
|
|
203
|
+
from bootstack.i18n import MessageCatalog
|
|
204
|
+
except ImportError:
|
|
205
|
+
return
|
|
206
|
+
for key, token in self._tab_locale_tokens.items():
|
|
207
|
+
if key in self._tab_map:
|
|
208
|
+
translated = MessageCatalog.translate(token)
|
|
209
|
+
self._tab_map[key].configure(text=translated)
|
|
210
|
+
|
|
211
|
+
def _set_next_change(self, reason: ChangeReason, via: ChangeMethod) -> None:
|
|
212
|
+
"""Tag the next variable change with the given reason and method."""
|
|
213
|
+
self._next_reason = reason
|
|
214
|
+
self._next_via = via
|
|
215
|
+
|
|
216
|
+
# -------------------------------------------------------------------------
|
|
217
|
+
# Public API — tab management
|
|
218
|
+
# -------------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
def add(
|
|
221
|
+
self,
|
|
222
|
+
key: str,
|
|
223
|
+
text: str = "",
|
|
224
|
+
icon: str | dict = None,
|
|
225
|
+
page: tk.Widget = None,
|
|
226
|
+
closable: bool | Literal['hover'] | None = None,
|
|
227
|
+
close_command: Callable = None,
|
|
228
|
+
command: Callable = None,
|
|
229
|
+
**kwargs
|
|
230
|
+
) -> tk.Widget:
|
|
231
|
+
"""Add a tab and its associated page.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
key: Unique identifier for the tab/page.
|
|
235
|
+
text: Text to display on the tab.
|
|
236
|
+
icon: Icon to display on the tab.
|
|
237
|
+
page: The page widget. If None, creates a Frame.
|
|
238
|
+
closable: Close button visibility (True, False, 'hover', or None).
|
|
239
|
+
If None, uses the widget's `enable_closing` setting.
|
|
240
|
+
close_command: Callback when close button is clicked.
|
|
241
|
+
If not provided and closable is enabled, removes the tab/page.
|
|
242
|
+
command: Callback when tab is selected.
|
|
243
|
+
**kwargs: Additional arguments passed to page Frame (if created).
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
The page widget.
|
|
247
|
+
"""
|
|
248
|
+
effective_closable = closable if closable is not None else self._tabs._enable_closing
|
|
249
|
+
|
|
250
|
+
if effective_closable and close_command is None:
|
|
251
|
+
close_command = lambda k=key: self.forget_tab(k)
|
|
252
|
+
|
|
253
|
+
tab = self._tabs.add(
|
|
254
|
+
text=text,
|
|
255
|
+
icon=icon,
|
|
256
|
+
key=key,
|
|
257
|
+
value=key,
|
|
258
|
+
closable=closable,
|
|
259
|
+
close_command=close_command,
|
|
260
|
+
command=command,
|
|
261
|
+
)
|
|
262
|
+
self._tab_map[key] = tab
|
|
263
|
+
self._tab_locale_tokens[key] = text
|
|
264
|
+
|
|
265
|
+
page_widget = self._page_stack.add(key, page, **kwargs)
|
|
266
|
+
|
|
267
|
+
if len(self._tab_map) == 1:
|
|
268
|
+
self._set_next_change("api", "programmatic")
|
|
269
|
+
self._tab_variable.set(key)
|
|
270
|
+
|
|
271
|
+
return page_widget
|
|
272
|
+
|
|
273
|
+
def __getitem__(self, key: str) -> tk.Widget:
|
|
274
|
+
"""Return the page widget for *key*.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
key: Tab/page identifier.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
The page widget registered under *key*.
|
|
281
|
+
|
|
282
|
+
Raises:
|
|
283
|
+
KeyError: If no tab with the given key exists.
|
|
284
|
+
"""
|
|
285
|
+
return self._page_stack.item(key)
|
|
286
|
+
|
|
287
|
+
def select(self, key: str) -> None:
|
|
288
|
+
"""Select a tab by its key.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
key: The identifier of the tab to select.
|
|
292
|
+
|
|
293
|
+
Raises:
|
|
294
|
+
NavigationError: If no tab with the given key exists.
|
|
295
|
+
"""
|
|
296
|
+
if key not in self._tab_map:
|
|
297
|
+
raise NavigationError(f"No tab with key '{key}'")
|
|
298
|
+
if key in self._hidden_tabs:
|
|
299
|
+
return
|
|
300
|
+
self._set_next_change("api", "programmatic")
|
|
301
|
+
self._tab_variable.set(key)
|
|
302
|
+
|
|
303
|
+
def navigate(self, key: str, data: dict = None) -> None:
|
|
304
|
+
"""Navigate to a tab/page with optional data.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
key: The identifier of the tab/page to navigate to.
|
|
308
|
+
data: Optional data to pass to the page.
|
|
309
|
+
|
|
310
|
+
Raises:
|
|
311
|
+
NavigationError: If no tab with the given key exists.
|
|
312
|
+
"""
|
|
313
|
+
if key not in self._tab_map:
|
|
314
|
+
raise NavigationError(f"No tab with key '{key}'")
|
|
315
|
+
if key in self._hidden_tabs:
|
|
316
|
+
return
|
|
317
|
+
self._set_next_change("api", "programmatic")
|
|
318
|
+
self._tab_variable.set(key)
|
|
319
|
+
if data:
|
|
320
|
+
self._page_stack.navigate(key, data=data)
|
|
321
|
+
|
|
322
|
+
def hide_tab(self, key: str) -> None:
|
|
323
|
+
"""Hide a tab without removing it from the registry.
|
|
324
|
+
|
|
325
|
+
The tab disappears from the bar but remains registered. Restore it
|
|
326
|
+
with `show_tab()`. If the hidden tab was selected, the next
|
|
327
|
+
available visible tab is selected automatically.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
key: The identifier of the tab to hide.
|
|
331
|
+
|
|
332
|
+
Raises:
|
|
333
|
+
NavigationError: If no tab with the given key exists.
|
|
334
|
+
"""
|
|
335
|
+
if key not in self._tab_map:
|
|
336
|
+
raise NavigationError(f"No tab with key '{key}'")
|
|
337
|
+
if key in self._hidden_tabs:
|
|
338
|
+
return
|
|
339
|
+
|
|
340
|
+
self._hidden_tabs.add(key)
|
|
341
|
+
self._tabs.hide(key)
|
|
342
|
+
|
|
343
|
+
if self._tab_variable.get() == key:
|
|
344
|
+
next_key = self._next_visible_key(exclude=key)
|
|
345
|
+
if next_key:
|
|
346
|
+
self._set_next_change("hide", "programmatic")
|
|
347
|
+
self._tab_variable.set(next_key)
|
|
348
|
+
else:
|
|
349
|
+
self._tab_variable.set('')
|
|
350
|
+
self._prev_key = None
|
|
351
|
+
|
|
352
|
+
def show_tab(self, key: str) -> None:
|
|
353
|
+
"""Restore a previously hidden tab to the bar.
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
key: The identifier of the tab to show.
|
|
357
|
+
|
|
358
|
+
Raises:
|
|
359
|
+
NavigationError: If no tab with the given key exists.
|
|
360
|
+
"""
|
|
361
|
+
if key not in self._tab_map:
|
|
362
|
+
raise NavigationError(f"No tab with key '{key}'")
|
|
363
|
+
self._hidden_tabs.discard(key)
|
|
364
|
+
self._tabs.show(key)
|
|
365
|
+
|
|
366
|
+
def forget_tab(self, key: str) -> None:
|
|
367
|
+
"""Remove a tab and its page entirely.
|
|
368
|
+
|
|
369
|
+
Unlike `hide_tab()`, the tab is unregistered and cannot be restored.
|
|
370
|
+
If the removed tab was selected, the next available tab is selected.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
key: The identifier of the tab/page to remove.
|
|
374
|
+
|
|
375
|
+
Raises:
|
|
376
|
+
NavigationError: If no tab with the given key exists.
|
|
377
|
+
"""
|
|
378
|
+
if key not in self._tab_map:
|
|
379
|
+
raise NavigationError(f"No tab with key '{key}'")
|
|
380
|
+
|
|
381
|
+
was_selected = self._tab_variable.get() == key
|
|
382
|
+
|
|
383
|
+
tab = self._tab_map.pop(key)
|
|
384
|
+
self._tab_locale_tokens.pop(key, None)
|
|
385
|
+
self._hidden_tabs.discard(key)
|
|
386
|
+
self._tabs.remove(key)
|
|
387
|
+
tab.destroy()
|
|
388
|
+
|
|
389
|
+
self._page_stack.remove(key)
|
|
390
|
+
|
|
391
|
+
if was_selected:
|
|
392
|
+
next_key = self._next_visible_key()
|
|
393
|
+
if next_key:
|
|
394
|
+
self._set_next_change("forget", "programmatic")
|
|
395
|
+
self._tab_variable.set(next_key)
|
|
396
|
+
else:
|
|
397
|
+
self._tab_variable.set('')
|
|
398
|
+
self._prev_key = None
|
|
399
|
+
|
|
400
|
+
def remove(self, key: str) -> None:
|
|
401
|
+
"""Remove a tab and its associated page.
|
|
402
|
+
|
|
403
|
+
Alias for `forget_tab()`.
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
key: The identifier of the tab/page to remove.
|
|
407
|
+
"""
|
|
408
|
+
self.forget_tab(key)
|
|
409
|
+
|
|
410
|
+
def _next_visible_key(self, exclude: str | None = None) -> str | None:
|
|
411
|
+
"""Return the first visible (non-hidden) tab key, skipping *exclude*."""
|
|
412
|
+
for k in self._tab_map:
|
|
413
|
+
if k != exclude and k not in self._hidden_tabs:
|
|
414
|
+
return k
|
|
415
|
+
return None
|
|
416
|
+
|
|
417
|
+
# -------------------------------------------------------------------------
|
|
418
|
+
# Introspection
|
|
419
|
+
# -------------------------------------------------------------------------
|
|
420
|
+
|
|
421
|
+
@property
|
|
422
|
+
def tabs_widget(self) -> Tabs:
|
|
423
|
+
"""Get the internal Tabs widget."""
|
|
424
|
+
return self._tabs
|
|
425
|
+
|
|
426
|
+
@property
|
|
427
|
+
def page_stack_widget(self) -> PageStack:
|
|
428
|
+
"""Get the internal PageStack widget."""
|
|
429
|
+
return self._page_stack
|
|
430
|
+
|
|
431
|
+
@property
|
|
432
|
+
def current(self) -> str | None:
|
|
433
|
+
"""Get the currently selected tab key."""
|
|
434
|
+
key = self._tab_variable.get()
|
|
435
|
+
return key if key else None
|
|
436
|
+
|
|
437
|
+
def page(self, key: str) -> tk.Widget:
|
|
438
|
+
"""Get a page widget by its key.
|
|
439
|
+
|
|
440
|
+
Args:
|
|
441
|
+
key: The identifier of the page.
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
The page widget.
|
|
445
|
+
|
|
446
|
+
Raises:
|
|
447
|
+
KeyError: If no page with the given key exists.
|
|
448
|
+
"""
|
|
449
|
+
return self._page_stack.item(key)
|
|
450
|
+
|
|
451
|
+
def pages(self) -> tuple[tk.Widget, ...]:
|
|
452
|
+
"""Get all page widgets.
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
A tuple of all page widgets.
|
|
456
|
+
"""
|
|
457
|
+
return self._page_stack.items()
|
|
458
|
+
|
|
459
|
+
def page_keys(self) -> tuple[str, ...]:
|
|
460
|
+
"""Get all page keys.
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
A tuple of all page keys.
|
|
464
|
+
"""
|
|
465
|
+
return self._page_stack.keys()
|
|
466
|
+
|
|
467
|
+
def tab(self, key: str) -> TabItem:
|
|
468
|
+
"""Get a TabItem by its key.
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
key: The identifier of the tab.
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
The TabItem widget.
|
|
475
|
+
|
|
476
|
+
Raises:
|
|
477
|
+
KeyError: If no tab with the given key exists.
|
|
478
|
+
"""
|
|
479
|
+
if key not in self._tab_map:
|
|
480
|
+
raise NavigationError(f"No tab with key '{key}'")
|
|
481
|
+
return self._tab_map[key]
|
|
482
|
+
|
|
483
|
+
def tabs(self) -> tuple[TabItem, ...]:
|
|
484
|
+
"""Get all TabItem widgets.
|
|
485
|
+
|
|
486
|
+
Returns:
|
|
487
|
+
A tuple of all TabItem widgets in the order they were added.
|
|
488
|
+
"""
|
|
489
|
+
return tuple(self._tab_map.values())
|
|
490
|
+
|
|
491
|
+
def tab_keys(self) -> tuple[str, ...]:
|
|
492
|
+
"""Get all tab keys.
|
|
493
|
+
|
|
494
|
+
Returns:
|
|
495
|
+
A tuple of all tab keys in the order they were added.
|
|
496
|
+
"""
|
|
497
|
+
return tuple(self._tab_map.keys())
|
|
498
|
+
|
|
499
|
+
def keys(self) -> tuple[str, ...]:
|
|
500
|
+
"""Get all tab keys.
|
|
501
|
+
|
|
502
|
+
Alias for `tab_keys()`.
|
|
503
|
+
|
|
504
|
+
Returns:
|
|
505
|
+
A tuple of all tab keys in the order they were added.
|
|
506
|
+
"""
|
|
507
|
+
return self.tab_keys()
|
|
508
|
+
|
|
509
|
+
def configure_tab(self, key: str, option: str | None = None, **kwargs: Unpack[FrameKwargs]):
|
|
510
|
+
"""Configure a specific tab by its key.
|
|
511
|
+
|
|
512
|
+
Args:
|
|
513
|
+
key: The key of the tab to configure.
|
|
514
|
+
option: If provided, return the value of this option.
|
|
515
|
+
**kwargs: Configuration options to apply to the tab.
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
If option is provided, returns the value of that option.
|
|
519
|
+
"""
|
|
520
|
+
tab = self.tab(key)
|
|
521
|
+
if option is not None:
|
|
522
|
+
return tab.cget(option)
|
|
523
|
+
tab.configure(**kwargs)
|
|
524
|
+
|
|
525
|
+
# -------------------------------------------------------------------------
|
|
526
|
+
# Events
|
|
527
|
+
# -------------------------------------------------------------------------
|
|
528
|
+
|
|
529
|
+
def on_page_changed(self, callback: Callable) -> str:
|
|
530
|
+
"""Register a callback for `<<PageChange>>` events.
|
|
531
|
+
|
|
532
|
+
Args:
|
|
533
|
+
callback: Receives `event.data` with navigation info: `page`,
|
|
534
|
+
`prev_page`, `nav`, `index`, `length`, `can_back`, `can_forward`.
|
|
535
|
+
|
|
536
|
+
Returns:
|
|
537
|
+
Bind ID — pass to `off_page_changed()` to unsubscribe.
|
|
538
|
+
"""
|
|
539
|
+
return self._page_stack.on_page_changed(callback)
|
|
540
|
+
|
|
541
|
+
def off_page_changed(self, bind_id: str | None = None) -> None:
|
|
542
|
+
"""Unsubscribe from `<<PageChange>>`.
|
|
543
|
+
|
|
544
|
+
Args:
|
|
545
|
+
bind_id: ID returned by `on_page_changed()`. If None, removes all.
|
|
546
|
+
"""
|
|
547
|
+
self._page_stack.off_page_changed(bind_id)
|
|
548
|
+
|
|
549
|
+
def on_tab_added(self, callback: Callable) -> str:
|
|
550
|
+
"""Register a callback for `<<TabAdd>>` events (fires when the add button is clicked).
|
|
551
|
+
|
|
552
|
+
Args:
|
|
553
|
+
callback: Called when the add button is clicked. `event.data` is None.
|
|
554
|
+
|
|
555
|
+
Returns:
|
|
556
|
+
Bind ID — pass to `off_tab_added()` to unsubscribe.
|
|
557
|
+
"""
|
|
558
|
+
return self._tabs.on_tab_added(callback)
|
|
559
|
+
|
|
560
|
+
def off_tab_added(self, bind_id: str | None = None) -> None:
|
|
561
|
+
"""Unsubscribe from `<<TabAdd>>`.
|
|
562
|
+
|
|
563
|
+
Args:
|
|
564
|
+
bind_id: ID returned by `on_tab_added()`. If None, removes all.
|
|
565
|
+
"""
|
|
566
|
+
self._tabs.off_tab_added(bind_id)
|
|
567
|
+
|
|
568
|
+
def on_tab_changed(self, callback: Callable[[TabChangeEvent], None]) -> str:
|
|
569
|
+
"""Register a callback for `<<TabChanged>>` events.
|
|
570
|
+
|
|
571
|
+
Args:
|
|
572
|
+
callback: Receives a `TabChangeEvent` payload with attributes
|
|
573
|
+
`current` (`TabRef`), `previous` (`TabRef | None`),
|
|
574
|
+
`reason` (`ChangeReason`), and `via` (`ChangeMethod`).
|
|
575
|
+
|
|
576
|
+
Returns:
|
|
577
|
+
Bind ID — pass to `off_tab_changed()` to unsubscribe.
|
|
578
|
+
"""
|
|
579
|
+
return self.bind('<<TabChanged>>', callback, add='+')
|
|
580
|
+
|
|
581
|
+
def off_tab_changed(self, bind_id: str | None = None) -> None:
|
|
582
|
+
"""Unsubscribe from `<<TabChanged>>`.
|
|
583
|
+
|
|
584
|
+
Args:
|
|
585
|
+
bind_id: ID returned by `on_tab_changed()`. If None, removes all.
|
|
586
|
+
"""
|
|
587
|
+
self.unbind('<<TabChanged>>', bind_id)
|
|
588
|
+
|
|
589
|
+
def on_tab_activated(self, callback: Callable[[TabActivateEvent], None]) -> str:
|
|
590
|
+
"""Register a callback for `<<TabActivate>>` events.
|
|
591
|
+
|
|
592
|
+
Fires when a tab becomes the selected (active) tab.
|
|
593
|
+
|
|
594
|
+
Args:
|
|
595
|
+
callback: Receives a `TabActivateEvent` payload with attributes
|
|
596
|
+
`key` and `text`.
|
|
597
|
+
|
|
598
|
+
Returns:
|
|
599
|
+
Bind ID — pass to `off_tab_activated()` to unsubscribe.
|
|
600
|
+
"""
|
|
601
|
+
return self.bind('<<TabActivate>>', callback, add='+')
|
|
602
|
+
|
|
603
|
+
def off_tab_activated(self, bind_id: str | None = None) -> None:
|
|
604
|
+
"""Unsubscribe from `<<TabActivate>>`.
|
|
605
|
+
|
|
606
|
+
Args:
|
|
607
|
+
bind_id: ID returned by `on_tab_activated()`. If None, removes all.
|
|
608
|
+
"""
|
|
609
|
+
self.unbind('<<TabActivate>>', bind_id)
|
|
610
|
+
|
|
611
|
+
def on_tab_deactivated(self, callback: Callable[[TabDeactivateEvent], None]) -> str:
|
|
612
|
+
"""Register a callback for `<<TabDeactivate>>` events.
|
|
613
|
+
|
|
614
|
+
Fires when a tab stops being the selected (active) tab.
|
|
615
|
+
|
|
616
|
+
Args:
|
|
617
|
+
callback: Receives a `TabDeactivateEvent` payload with attributes
|
|
618
|
+
`key` and `text`.
|
|
619
|
+
|
|
620
|
+
Returns:
|
|
621
|
+
Bind ID — pass to `off_tab_deactivated()` to unsubscribe.
|
|
622
|
+
"""
|
|
623
|
+
return self.bind('<<TabDeactivate>>', callback, add='+')
|
|
624
|
+
|
|
625
|
+
def off_tab_deactivated(self, bind_id: str | None = None) -> None:
|
|
626
|
+
"""Unsubscribe from `<<TabDeactivate>>`.
|
|
627
|
+
|
|
628
|
+
Args:
|
|
629
|
+
bind_id: ID returned by `on_tab_deactivated()`. If None, removes all.
|
|
630
|
+
"""
|
|
631
|
+
self.unbind('<<TabDeactivate>>', bind_id)
|
|
632
|
+
|
|
633
|
+
def on_tab_closed(self, callback: Callable) -> str:
|
|
634
|
+
"""Register a callback for `<<TabClose>>` events (fires when a tab's close button is clicked).
|
|
635
|
+
|
|
636
|
+
Args:
|
|
637
|
+
callback: Receives `event.data = {'value': Any}` — the closed tab's value.
|
|
638
|
+
|
|
639
|
+
Returns:
|
|
640
|
+
Bind ID — pass to `off_tab_closed()` to unsubscribe.
|
|
641
|
+
"""
|
|
642
|
+
return self._tabs.on_tab_closed(callback)
|
|
643
|
+
|
|
644
|
+
def off_tab_closed(self, bind_id: str | None = None) -> None:
|
|
645
|
+
"""Unsubscribe from `<<TabClose>>`.
|
|
646
|
+
|
|
647
|
+
Args:
|
|
648
|
+
bind_id: ID returned by `on_tab_closed()`. If None, removes all.
|
|
649
|
+
"""
|
|
650
|
+
self._tabs.off_tab_closed(bind_id)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""TextArea and CodeEditor composite widgets."""
|
|
2
|
+
|
|
3
|
+
from .textarea import TextArea
|
|
4
|
+
from .codeeditor import CodeEditor
|
|
5
|
+
from .filter import EditFilter
|
|
6
|
+
from .core import _MultilineCore
|
|
7
|
+
from .sidebar import Sidebar
|
|
8
|
+
from .decoration import Position, RangeDecoration, LineDecoration, WidgetDecoration
|
|
9
|
+
from .extensions.line_numbers import LineNumbers
|
|
10
|
+
from .extensions.bracket_matcher import BracketMatcher
|
|
11
|
+
from .extensions.smart_indent import SmartIndent
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"TextArea",
|
|
15
|
+
"CodeEditor",
|
|
16
|
+
"EditFilter",
|
|
17
|
+
"_MultilineCore",
|
|
18
|
+
"Sidebar",
|
|
19
|
+
"Position",
|
|
20
|
+
"RangeDecoration",
|
|
21
|
+
"LineDecoration",
|
|
22
|
+
"WidgetDecoration",
|
|
23
|
+
"LineNumbers",
|
|
24
|
+
"BracketMatcher",
|
|
25
|
+
"SmartIndent",
|
|
26
|
+
]
|