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,482 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import tkinter
|
|
4
|
+
from typing import Any, Callable, overload
|
|
5
|
+
|
|
6
|
+
from bootstack.widgets._impl.primitives.panedwindow import PanedWindow as _InternalPanedWindow
|
|
7
|
+
from bootstack.widgets._impl.primitives.frame import Frame as _InternalFrame
|
|
8
|
+
from bootstack.widgets._impl.primitives.flexframe import FlexFrame
|
|
9
|
+
from bootstack.widgets._impl.primitives.gridframe import GridFrame
|
|
10
|
+
from bootstack.widgets._core.base import PublicWidgetBase, adapt_handler
|
|
11
|
+
from bootstack.widgets._core.container import (
|
|
12
|
+
GRID_KEYS, grid_sticky, place_flex_child, _reject_legacy_child_kwargs,
|
|
13
|
+
_expand_margin,
|
|
14
|
+
)
|
|
15
|
+
from bootstack.widgets._core.context import push_container, pop_container
|
|
16
|
+
from bootstack.events import SashMoveEvent, Subscription
|
|
17
|
+
from bootstack.streams import Stream
|
|
18
|
+
from bootstack.widgets.types import (
|
|
19
|
+
AccentToken, SurfaceToken, Padding, LayoutKind, AutoFlow, Orient,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SplitPane:
|
|
24
|
+
"""A handle for one split pane — both a layout context and a live controller.
|
|
25
|
+
|
|
26
|
+
Returned by `SplitView.add()` and by `SplitView.item()` / `panes`. Use it as a
|
|
27
|
+
`with` block to place child widgets inside the pane, and read or set `key` /
|
|
28
|
+
`weight` or call `remove()` to inspect, resize, or drop the pane afterward.
|
|
29
|
+
|
|
30
|
+
As a context manager it accepts the same layout kwargs as the standalone
|
|
31
|
+
layout containers — `layout=`, `gap=`, `horizontal_items=`, `vertical_items=`,
|
|
32
|
+
`grow_items=`, `columns=`, `rows=`, `auto_flow=`.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
frame: tkinter.Widget,
|
|
38
|
+
*,
|
|
39
|
+
key: str,
|
|
40
|
+
paned: _InternalPanedWindow,
|
|
41
|
+
owner: "SplitView",
|
|
42
|
+
layout: LayoutKind = "column",
|
|
43
|
+
padding: Padding | None = None,
|
|
44
|
+
gap: int = 0,
|
|
45
|
+
horizontal_items: str | None = None,
|
|
46
|
+
vertical_items: str | None = None,
|
|
47
|
+
grow_items: bool = False,
|
|
48
|
+
columns: int | list[int | str] | None = None,
|
|
49
|
+
rows: int | list[int | str] | None = None,
|
|
50
|
+
auto_flow: AutoFlow = "row",
|
|
51
|
+
) -> None:
|
|
52
|
+
self._frame = frame
|
|
53
|
+
self._key = key
|
|
54
|
+
self._paned = paned
|
|
55
|
+
self._owner = owner
|
|
56
|
+
self._layout = layout
|
|
57
|
+
|
|
58
|
+
if horizontal_items is None:
|
|
59
|
+
horizontal_items = (
|
|
60
|
+
"stretch" if layout == "grid"
|
|
61
|
+
else "center" if layout == "column" else "left"
|
|
62
|
+
)
|
|
63
|
+
if vertical_items is None:
|
|
64
|
+
vertical_items = (
|
|
65
|
+
"stretch" if layout == "grid"
|
|
66
|
+
else "center" if layout == "row" else "top"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if layout in ("column", "row"):
|
|
70
|
+
self._layout_frame = FlexFrame(
|
|
71
|
+
frame,
|
|
72
|
+
direction="vertical" if layout == "column" else "horizontal",
|
|
73
|
+
padding=padding,
|
|
74
|
+
gap=gap,
|
|
75
|
+
horizontal_items=horizontal_items,
|
|
76
|
+
vertical_items=vertical_items,
|
|
77
|
+
grow_items=grow_items,
|
|
78
|
+
)
|
|
79
|
+
elif layout == "grid":
|
|
80
|
+
self._layout_frame = GridFrame(
|
|
81
|
+
frame,
|
|
82
|
+
columns=columns,
|
|
83
|
+
rows=rows,
|
|
84
|
+
padding=padding,
|
|
85
|
+
gap=gap,
|
|
86
|
+
auto_flow=auto_flow,
|
|
87
|
+
)
|
|
88
|
+
else:
|
|
89
|
+
raise ValueError(
|
|
90
|
+
f"SplitPane layout must be 'column', 'row', or 'grid', got {layout!r}"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
self._horizontal_items = horizontal_items
|
|
94
|
+
self._vertical_items = vertical_items
|
|
95
|
+
self._layout_frame.pack(fill="both", expand=True)
|
|
96
|
+
|
|
97
|
+
def _child_master(self) -> tkinter.Widget:
|
|
98
|
+
return self._layout_frame
|
|
99
|
+
|
|
100
|
+
def guide_layout(self, child: PublicWidgetBase, **layout_kw: Any) -> None:
|
|
101
|
+
if self._layout == "grid":
|
|
102
|
+
_reject_legacy_child_kwargs(layout_kw, "SplitPane")
|
|
103
|
+
_expand_margin(layout_kw)
|
|
104
|
+
options = {k: v for k, v in layout_kw.items() if k in GRID_KEYS}
|
|
105
|
+
h = layout_kw.get("horizontal") or self._horizontal_items
|
|
106
|
+
v = layout_kw.get("vertical") or self._vertical_items
|
|
107
|
+
options["sticky"] = grid_sticky(h, v)
|
|
108
|
+
child._internal.grid(in_=self._child_master(), **options)
|
|
109
|
+
return
|
|
110
|
+
place_flex_child(self._layout_frame, child, layout_kw, "SplitPane")
|
|
111
|
+
|
|
112
|
+
# ----- Pane identity, weight, and removal -----
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def key(self) -> str:
|
|
116
|
+
"""The pane's unique key, used with `SplitView.item()` / `remove()`."""
|
|
117
|
+
return self._key
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def weight(self) -> int:
|
|
121
|
+
"""Relative resize weight — panes with a higher weight take proportionally
|
|
122
|
+
more space when the container grows. A pane with `weight=0` keeps its size
|
|
123
|
+
and does not grow (a fixed pane). Assigning a new value resizes live.
|
|
124
|
+
"""
|
|
125
|
+
return int(self._paned.pane(self._frame, "weight"))
|
|
126
|
+
|
|
127
|
+
@weight.setter
|
|
128
|
+
def weight(self, value: int) -> None:
|
|
129
|
+
self._paned.pane(self._frame, weight=value)
|
|
130
|
+
|
|
131
|
+
def remove(self) -> None:
|
|
132
|
+
"""Remove this pane and its content from the split view."""
|
|
133
|
+
self._owner.remove(self._key)
|
|
134
|
+
|
|
135
|
+
def __enter__(self) -> "SplitPane":
|
|
136
|
+
push_container(self)
|
|
137
|
+
return self
|
|
138
|
+
|
|
139
|
+
def __exit__(self, exc_type, exc, tb) -> None:
|
|
140
|
+
pop_container(self)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class SplitView(PublicWidgetBase):
|
|
144
|
+
"""A resizable split container with panes separated by draggable sashes.
|
|
145
|
+
|
|
146
|
+
Add panes with `.add()` and place children inside each pane using the
|
|
147
|
+
returned `SplitPane` handle. Sashes between panes can be dragged at runtime
|
|
148
|
+
to resize them. Panes are addressable by key — enumerate them with `panes`,
|
|
149
|
+
look one up with `item()`, place a new one next to another with
|
|
150
|
+
`add(before=)` / `add(after=)`, reorder with `move()`, and drop one with
|
|
151
|
+
`remove()`.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
orient: Pane arrangement — `'horizontal'` (side-by-side) or
|
|
155
|
+
`'vertical'` (stacked top-to-bottom). Defaults to `'horizontal'`.
|
|
156
|
+
padding: Space in pixels between the outer border and the pane area.
|
|
157
|
+
A single value applies to all sides; `(x, y)` sets the horizontal
|
|
158
|
+
and vertical amounts. Defaults to `None`.
|
|
159
|
+
accent: Color intent token applied to the sash. When omitted, the
|
|
160
|
+
sash uses the theme's default color.
|
|
161
|
+
surface: Surface token for the pane background.
|
|
162
|
+
sash_thickness: Width (horizontal split) or height (vertical split)
|
|
163
|
+
of the draggable sash in pixels. Defaults to the theme's
|
|
164
|
+
standard sash size (typically 6 px).
|
|
165
|
+
width: Requested width in pixels.
|
|
166
|
+
height: Requested height in pixels.
|
|
167
|
+
parent: Override the context-stack parent.
|
|
168
|
+
**kwargs: Layout placement options applied by the parent container —
|
|
169
|
+
`fill`, `expand`, `anchor`, `margin`, `row`, `column`, `sticky`.
|
|
170
|
+
See :doc:`/tasks/layout`.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
def __init__(
|
|
174
|
+
self,
|
|
175
|
+
orient: Orient = "horizontal",
|
|
176
|
+
*,
|
|
177
|
+
padding: Padding | None = None,
|
|
178
|
+
accent: AccentToken | str | None = None,
|
|
179
|
+
surface: SurfaceToken | str | None = None,
|
|
180
|
+
sash_thickness: int | None = None,
|
|
181
|
+
width: int | None = None,
|
|
182
|
+
height: int | None = None,
|
|
183
|
+
parent: Any = None,
|
|
184
|
+
**kwargs: Any,
|
|
185
|
+
) -> None:
|
|
186
|
+
self._parent = self._resolve_parent(parent)
|
|
187
|
+
layout_kw = self._split_layout_kwargs(kwargs)
|
|
188
|
+
|
|
189
|
+
pw_kwargs: dict[str, Any] = {"orient": orient}
|
|
190
|
+
if padding is not None:
|
|
191
|
+
pw_kwargs["padding"] = padding
|
|
192
|
+
if accent is not None:
|
|
193
|
+
pw_kwargs["accent"] = accent
|
|
194
|
+
if surface is not None:
|
|
195
|
+
pw_kwargs["surface"] = surface
|
|
196
|
+
if width is not None:
|
|
197
|
+
pw_kwargs["width"] = width
|
|
198
|
+
if height is not None:
|
|
199
|
+
pw_kwargs["height"] = height
|
|
200
|
+
if sash_thickness is not None:
|
|
201
|
+
pw_kwargs["style_options"] = {"sash_thickness": sash_thickness}
|
|
202
|
+
|
|
203
|
+
self._panes: dict[str, SplitPane] = {}
|
|
204
|
+
self._pane_counter = 0
|
|
205
|
+
self._dragging_sash: int | None = None
|
|
206
|
+
|
|
207
|
+
tk_master = self._parent._child_master() if self._parent else None
|
|
208
|
+
self._internal = _InternalPanedWindow(tk_master, **pw_kwargs)
|
|
209
|
+
self._setup_sash_bindings()
|
|
210
|
+
self._attach_to_parent(layout_kw)
|
|
211
|
+
|
|
212
|
+
# ----- Pane management -----
|
|
213
|
+
|
|
214
|
+
def add(
|
|
215
|
+
self,
|
|
216
|
+
key: str | None = None,
|
|
217
|
+
*,
|
|
218
|
+
before: str | None = None,
|
|
219
|
+
after: str | None = None,
|
|
220
|
+
weight: int = 1,
|
|
221
|
+
layout: LayoutKind = "column",
|
|
222
|
+
padding: Padding | None = None,
|
|
223
|
+
gap: int = 0,
|
|
224
|
+
horizontal_items: str | None = None,
|
|
225
|
+
vertical_items: str | None = None,
|
|
226
|
+
grow_items: bool = False,
|
|
227
|
+
columns: int | list[int | str] | None = None,
|
|
228
|
+
rows: int | list[int | str] | None = None,
|
|
229
|
+
auto_flow: AutoFlow = "row",
|
|
230
|
+
) -> SplitPane:
|
|
231
|
+
"""Add a pane and return a handle for placing its children and controlling it.
|
|
232
|
+
|
|
233
|
+
Panes are appended in order by default. Pass `before=` or `after=` (the
|
|
234
|
+
key of an existing pane) to place the new pane next to another one.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
key: Unique identifier for the pane, used with `item()`, `move()`,
|
|
238
|
+
and `remove()`. Auto-generated when omitted.
|
|
239
|
+
before: Place the new pane immediately before the pane with this key.
|
|
240
|
+
after: Place the new pane immediately after the pane with this key.
|
|
241
|
+
Mutually exclusive with `before`; omit both to append at the end.
|
|
242
|
+
weight: Relative size weight when the container is resized. Panes
|
|
243
|
+
with a higher weight take proportionally more space. Settable
|
|
244
|
+
afterward via the pane's `weight` property. Defaults to `1`.
|
|
245
|
+
layout: Internal pane layout. Defaults to `'column'`.
|
|
246
|
+
padding: Space in pixels inside the pane border. Defaults to `None`.
|
|
247
|
+
gap: Space in pixels between children. Defaults to `0`.
|
|
248
|
+
horizontal_items: How children sit on the horizontal axis — edge
|
|
249
|
+
values `'left'`/`'center'`/`'right'`/`'stretch'`, plus `'space-*'`
|
|
250
|
+
when horizontal is the stacking axis. Defaults to `'stretch'` in grid mode,
|
|
251
|
+
`'center'` in a column and `'left'` in a row.
|
|
252
|
+
vertical_items: How children sit on the vertical axis — edge values
|
|
253
|
+
`'top'`/`'center'`/`'bottom'`/`'stretch'`, plus `'space-*'` when
|
|
254
|
+
vertical is the stacking axis. Defaults to `'stretch'` in grid mode,
|
|
255
|
+
`'center'` in a row and `'top'` in a column.
|
|
256
|
+
grow_items: For `'column'`/`'row'`, when `True` every child grows
|
|
257
|
+
equally to share the main axis. Defaults to `False`.
|
|
258
|
+
columns: Column definitions for `'grid'` layout. An integer sets
|
|
259
|
+
the number of equal-weight columns; a list sets per-column
|
|
260
|
+
weights or sizes (e.g. `[1, 2, 'auto', '120px']`).
|
|
261
|
+
rows: Row definitions for `'grid'` layout, same format as `columns`.
|
|
262
|
+
auto_flow: Grid auto-flow direction. Defaults to `'row'`.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
`SplitPane` — use as a context manager to place children, and as a
|
|
266
|
+
handle to read/set `weight` or `remove()` the pane.
|
|
267
|
+
|
|
268
|
+
Raises:
|
|
269
|
+
KeyError: If `before`/`after` names a pane that does not exist.
|
|
270
|
+
ValueError: If both `before` and `after` are given.
|
|
271
|
+
"""
|
|
272
|
+
index = self._resolve_placement(before, after)
|
|
273
|
+
return self._create_pane(
|
|
274
|
+
index=index, key=key, weight=weight,
|
|
275
|
+
layout=layout, padding=padding, gap=gap,
|
|
276
|
+
horizontal_items=horizontal_items, vertical_items=vertical_items,
|
|
277
|
+
grow_items=grow_items,
|
|
278
|
+
columns=columns, rows=rows, auto_flow=auto_flow,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
def move(self, key: str, *, before: str | None = None, after: str | None = None) -> None:
|
|
282
|
+
"""Move an existing pane next to another pane.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
key: The key of the pane to move.
|
|
286
|
+
before: Move it immediately before the pane with this key.
|
|
287
|
+
after: Move it immediately after the pane with this key. Pass
|
|
288
|
+
exactly one of `before` or `after`.
|
|
289
|
+
|
|
290
|
+
Raises:
|
|
291
|
+
KeyError: If `key`, `before`, or `after` names a pane that does
|
|
292
|
+
not exist.
|
|
293
|
+
ValueError: If not exactly one of `before`/`after` is given, or if
|
|
294
|
+
the anchor is the pane being moved.
|
|
295
|
+
"""
|
|
296
|
+
if key not in self._panes:
|
|
297
|
+
raise KeyError(f"no pane with key {key!r}")
|
|
298
|
+
if (before is None) == (after is None):
|
|
299
|
+
raise ValueError("pass exactly one of before= or after=")
|
|
300
|
+
anchor = before if before is not None else after
|
|
301
|
+
if anchor == key:
|
|
302
|
+
raise ValueError(f"cannot move pane {key!r} relative to itself")
|
|
303
|
+
# The target index is computed against the order with the moved pane
|
|
304
|
+
# removed, which is how ttk's insert(pos, existing) re-seats it.
|
|
305
|
+
order = [k for k in self._panes if k != key]
|
|
306
|
+
if anchor not in order:
|
|
307
|
+
raise KeyError(f"no pane with key {anchor!r}")
|
|
308
|
+
pos = order.index(anchor)
|
|
309
|
+
index = pos if before is not None else pos + 1
|
|
310
|
+
self._internal.insert(index, self._panes[key]._frame)
|
|
311
|
+
self._resync_order()
|
|
312
|
+
|
|
313
|
+
def _resolve_placement(self, before: str | None, after: str | None) -> int | None:
|
|
314
|
+
"""Translate a `before=`/`after=` key into an insertion index for a new pane.
|
|
315
|
+
|
|
316
|
+
Returns `None` to append (the default, and the case where the anchor is
|
|
317
|
+
the last pane — ttk cannot target the slot past the final pane).
|
|
318
|
+
"""
|
|
319
|
+
if before is not None and after is not None:
|
|
320
|
+
raise ValueError("pass only one of before= or after=, not both")
|
|
321
|
+
if before is None and after is None:
|
|
322
|
+
return None
|
|
323
|
+
anchor = before if before is not None else after
|
|
324
|
+
order = list(self._panes)
|
|
325
|
+
if anchor not in order:
|
|
326
|
+
raise KeyError(f"no pane with key {anchor!r}")
|
|
327
|
+
pos = order.index(anchor)
|
|
328
|
+
index = pos if before is not None else pos + 1
|
|
329
|
+
return index if index < len(order) else None
|
|
330
|
+
|
|
331
|
+
def _create_pane(self, *, index: int | None, key: str | None, weight: int, **layout_kw: Any) -> SplitPane:
|
|
332
|
+
if key is None:
|
|
333
|
+
key = f"pane_{self._pane_counter}"
|
|
334
|
+
self._pane_counter += 1
|
|
335
|
+
if key in self._panes:
|
|
336
|
+
raise ValueError(f"A pane with the key {key!r} already exists.")
|
|
337
|
+
|
|
338
|
+
frame = _InternalFrame(self._internal)
|
|
339
|
+
if index is None:
|
|
340
|
+
self._internal.add(frame, weight=weight)
|
|
341
|
+
else:
|
|
342
|
+
self._internal.insert(index, frame, weight=weight)
|
|
343
|
+
pane = SplitPane(frame, key=key, paned=self._internal, owner=self, **layout_kw)
|
|
344
|
+
self._panes[key] = pane
|
|
345
|
+
if index is not None:
|
|
346
|
+
self._resync_order()
|
|
347
|
+
return pane
|
|
348
|
+
|
|
349
|
+
def _resync_order(self) -> None:
|
|
350
|
+
"""Reorder `_panes` to match the live pane order after an insert/move."""
|
|
351
|
+
by_frame = {str(p._frame): (k, p) for k, p in self._panes.items()}
|
|
352
|
+
self._panes = dict(
|
|
353
|
+
by_frame[fid] for fid in self._internal.panes() if fid in by_frame
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
@property
|
|
357
|
+
def panes(self) -> tuple[SplitPane, ...]:
|
|
358
|
+
"""All panes in left-to-right (or top-to-bottom) order."""
|
|
359
|
+
return tuple(self._panes.values())
|
|
360
|
+
|
|
361
|
+
def __len__(self) -> int:
|
|
362
|
+
return len(self._panes)
|
|
363
|
+
|
|
364
|
+
def keys(self) -> tuple[str, ...]:
|
|
365
|
+
"""All pane keys in order."""
|
|
366
|
+
return tuple(self._panes)
|
|
367
|
+
|
|
368
|
+
def item(self, key: str) -> SplitPane:
|
|
369
|
+
"""Return the pane handle for `key`.
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
key: Pane key.
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
The `SplitPane` for that key — read/set its `weight` or `remove()` it.
|
|
376
|
+
"""
|
|
377
|
+
return self._panes[key]
|
|
378
|
+
|
|
379
|
+
def remove(self, key: str) -> None:
|
|
380
|
+
"""Remove a pane and destroy its content.
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
key: The key assigned when the pane was added.
|
|
384
|
+
"""
|
|
385
|
+
pane = self._panes.pop(key)
|
|
386
|
+
# `WidgetCapabilitiesMixin.forget()` shadows `ttk.Panedwindow.forget(pane)`
|
|
387
|
+
# with a zero-arg method, so invoke the underlying Tk command directly.
|
|
388
|
+
self._internal.tk.call(str(self._internal), "forget", pane._frame)
|
|
389
|
+
pane._frame.destroy()
|
|
390
|
+
|
|
391
|
+
# ----- Sash control -----
|
|
392
|
+
|
|
393
|
+
@property
|
|
394
|
+
def sash_positions(self) -> list[int]:
|
|
395
|
+
"""Current position of every sash in pixels, in order."""
|
|
396
|
+
n = len(self._internal.panes())
|
|
397
|
+
return [self._internal.sashpos(i) for i in range(max(0, n - 1))]
|
|
398
|
+
|
|
399
|
+
def sash_position(self, index: int, position: int | None = None) -> int | None:
|
|
400
|
+
"""Get or set the position of a sash.
|
|
401
|
+
|
|
402
|
+
Args:
|
|
403
|
+
index: Zero-based sash index. A split view with `n` panes has
|
|
404
|
+
`n - 1` sashes.
|
|
405
|
+
position: New position in pixels. If `None`, returns the
|
|
406
|
+
current position.
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
Current sash position in pixels when `position` is `None`.
|
|
410
|
+
|
|
411
|
+
Raises:
|
|
412
|
+
IndexError: If `index` does not refer to an existing sash.
|
|
413
|
+
"""
|
|
414
|
+
n_sashes = max(0, len(self._internal.panes()) - 1)
|
|
415
|
+
if not 0 <= index < n_sashes:
|
|
416
|
+
if n_sashes == 0:
|
|
417
|
+
raise IndexError(
|
|
418
|
+
f"sash index {index} out of range: this split view has no "
|
|
419
|
+
f"sashes yet (add at least two panes)"
|
|
420
|
+
)
|
|
421
|
+
raise IndexError(
|
|
422
|
+
f"sash index {index} out of range: valid sash indices are "
|
|
423
|
+
f"0..{n_sashes - 1}"
|
|
424
|
+
)
|
|
425
|
+
if position is None:
|
|
426
|
+
return self._internal.sashpos(index)
|
|
427
|
+
self._internal.sashpos(index, position)
|
|
428
|
+
return None
|
|
429
|
+
|
|
430
|
+
def _setup_sash_bindings(self) -> None:
|
|
431
|
+
pw = self._internal
|
|
432
|
+
pw.bind("<Button-1>", self._on_sash_press, add="+")
|
|
433
|
+
pw.bind("<ButtonRelease-1>", self._on_sash_release, add="+")
|
|
434
|
+
|
|
435
|
+
def _on_sash_press(self, event: Any) -> None:
|
|
436
|
+
result = self._internal.identify(event.x, event.y)
|
|
437
|
+
if result != "":
|
|
438
|
+
try:
|
|
439
|
+
self._dragging_sash = int(result)
|
|
440
|
+
except (ValueError, TypeError):
|
|
441
|
+
self._dragging_sash = None
|
|
442
|
+
|
|
443
|
+
def _on_sash_release(self, event: Any) -> None:
|
|
444
|
+
idx = self._dragging_sash
|
|
445
|
+
self._dragging_sash = None
|
|
446
|
+
if idx is None:
|
|
447
|
+
return
|
|
448
|
+
try:
|
|
449
|
+
pos = self._internal.sashpos(idx)
|
|
450
|
+
except Exception:
|
|
451
|
+
return
|
|
452
|
+
self._internal.event_generate(
|
|
453
|
+
"<<BsSashMoved>>", data=SashMoveEvent(index=idx, position=pos)
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
# ----- Events -----
|
|
457
|
+
|
|
458
|
+
@overload
|
|
459
|
+
def on_sash_moved(self) -> Stream: ...
|
|
460
|
+
@overload
|
|
461
|
+
def on_sash_moved(self, handler: Callable[[SashMoveEvent], Any]) -> Subscription: ...
|
|
462
|
+
def on_sash_moved(self, handler: Callable[[SashMoveEvent], Any] | None = None) -> Stream | Subscription:
|
|
463
|
+
"""Register a callback fired when a sash drag is released.
|
|
464
|
+
|
|
465
|
+
Args:
|
|
466
|
+
handler: Called with a :class:`~bootstack.events.SashMoveEvent`
|
|
467
|
+
carrying `index` (zero-based sash index) and `position` (pixel
|
|
468
|
+
offset from the leading edge). Omit to get a composable
|
|
469
|
+
:class:`~bootstack.streams.Stream`.
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
A cancellable :class:`~bootstack.events.Subscription` when a handler
|
|
473
|
+
is given, otherwise a :class:`~bootstack.streams.Stream`.
|
|
474
|
+
"""
|
|
475
|
+
sequence = "<<BsSashMoved>>"
|
|
476
|
+
if handler is None:
|
|
477
|
+
def _source(h):
|
|
478
|
+
bid = self._internal.bind(sequence, adapt_handler(h), add="+")
|
|
479
|
+
return Subscription(self._internal, sequence, bid)
|
|
480
|
+
return Stream(self._internal, _source=_source)
|
|
481
|
+
bid = self._internal.bind(sequence, adapt_handler(handler), add="+")
|
|
482
|
+
return Subscription(self._internal, sequence, bid)
|