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,228 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import tkinter
|
|
4
|
+
from typing import overload, Any, Callable, Literal, TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from bootstack.widgets._impl.composites.radiogroup import RadioGroup as _InternalRadioGroup
|
|
7
|
+
from bootstack.widgets._core.base import PublicWidgetBase
|
|
8
|
+
from bootstack.widgets._core.selection_group import SelectionGroupMixin
|
|
9
|
+
from bootstack.widgets._core.options import normalize_options, option_display
|
|
10
|
+
from bootstack.widgets._core.events import register_widget_events
|
|
11
|
+
from bootstack.events import Subscription, ChangeEvent
|
|
12
|
+
from bootstack.streams import Stream
|
|
13
|
+
from bootstack.widgets.types import AccentToken, Event, Option, Orient
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from bootstack.signals import Signal
|
|
17
|
+
|
|
18
|
+
_RADIOGROUP_EVENTS: dict[str, str] = {
|
|
19
|
+
"change": "<<Change>>",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class RadioGroup(SelectionGroupMixin, PublicWidgetBase):
|
|
24
|
+
"""A group of mutually exclusive radio buttons.
|
|
25
|
+
|
|
26
|
+
Exactly one option can be selected at a time. Options are supplied at
|
|
27
|
+
construction via `options=` and can be added or removed at runtime
|
|
28
|
+
using `add()` and `remove()`.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
options: Choices for the group. Each item is a plain string (text and
|
|
32
|
+
value are the same), a `(text, value)` tuple, or a
|
|
33
|
+
`{'text': ..., 'value': ...}` dict — e.g. `["S", "M", "L"]` or
|
|
34
|
+
`[("Small", "s"), ("Medium", "m"), ("Large", "l")]`. A dict option
|
|
35
|
+
may also carry `'icon'` (a glyph rendered beside the label) and
|
|
36
|
+
`'disabled'` (when `True` the option is dimmed and cannot be
|
|
37
|
+
selected); any other keys ride along as carried data on `selection`.
|
|
38
|
+
An option with an `'icon'` and no text — e.g.
|
|
39
|
+
`{'icon': 'star', 'value': 's'}` — renders as an icon-only button.
|
|
40
|
+
signal: Reactive `Signal` holding the selected value. When
|
|
41
|
+
provided, `value=` is ignored — seed the Signal directly.
|
|
42
|
+
value: Initially selected value. Ignored when `signal=` is passed.
|
|
43
|
+
orient: Layout direction. Default `'horizontal'`.
|
|
44
|
+
title: Optional label rendered above the button group.
|
|
45
|
+
accent: Accent token applied to all buttons.
|
|
46
|
+
disabled: If `True`, all buttons are non-interactive and dimmed.
|
|
47
|
+
Defaults to `False`.
|
|
48
|
+
localize: Whether option labels (and the `title`) are translated through
|
|
49
|
+
the catalog — `True`, `False`, or `'auto'` (translate when a
|
|
50
|
+
translation is registered, otherwise show the literal). Defaults to
|
|
51
|
+
the app's `localize_mode`. Set `False` to keep proper nouns
|
|
52
|
+
untranslated; override a single option with its `localize` key.
|
|
53
|
+
parent: Explicit parent widget. If omitted, the current
|
|
54
|
+
context-stack container is used.
|
|
55
|
+
**kwargs: Layout placement options applied by the parent container —
|
|
56
|
+
`fill`, `expand`, `anchor`, `margin`, `row`, `column`, `sticky`.
|
|
57
|
+
See :doc:`/tasks/layout`.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
options: list[Option] | None = None,
|
|
63
|
+
*,
|
|
64
|
+
signal: "Signal | None" = None,
|
|
65
|
+
value: Any = None,
|
|
66
|
+
orient: Orient = "horizontal",
|
|
67
|
+
title: str | None = None,
|
|
68
|
+
accent: AccentToken | str | None = None,
|
|
69
|
+
disabled: bool = False,
|
|
70
|
+
localize: bool | Literal['auto'] | None = None,
|
|
71
|
+
parent: Any = None,
|
|
72
|
+
**kwargs: Any,
|
|
73
|
+
) -> None:
|
|
74
|
+
self._localize = localize
|
|
75
|
+
self._parent = self._resolve_parent(parent)
|
|
76
|
+
layout_kw = self._split_layout_kwargs(kwargs)
|
|
77
|
+
|
|
78
|
+
tk_master = self._parent._child_master() if self._parent else None
|
|
79
|
+
|
|
80
|
+
internal_kwargs: dict[str, Any] = {"orient": orient}
|
|
81
|
+
if signal is not None:
|
|
82
|
+
internal_kwargs["signal"] = signal
|
|
83
|
+
if value is not None:
|
|
84
|
+
internal_kwargs["value"] = value
|
|
85
|
+
if title is not None:
|
|
86
|
+
internal_kwargs["text"] = title
|
|
87
|
+
if accent is not None:
|
|
88
|
+
internal_kwargs["accent"] = accent
|
|
89
|
+
if disabled:
|
|
90
|
+
internal_kwargs["state"] = "disabled"
|
|
91
|
+
if localize is not None:
|
|
92
|
+
internal_kwargs["localize"] = localize
|
|
93
|
+
|
|
94
|
+
self._internal = _InternalRadioGroup(tk_master, **internal_kwargs)
|
|
95
|
+
|
|
96
|
+
# Populate options passed at construction; keep the records (the data
|
|
97
|
+
# bag) so `selection` can return each option's full dict.
|
|
98
|
+
records = normalize_options(options)
|
|
99
|
+
self._set_option_records(records)
|
|
100
|
+
for record in records:
|
|
101
|
+
icon, disabled = option_display(record)
|
|
102
|
+
add_kwargs, _ = self._option_render(icon, disabled, record.text)
|
|
103
|
+
item_localize = self._resolve_localize(record)
|
|
104
|
+
if item_localize is not None:
|
|
105
|
+
add_kwargs["localize"] = item_localize
|
|
106
|
+
self._internal.add(text=record.text, value=record.value, **add_kwargs)
|
|
107
|
+
|
|
108
|
+
# Trace the signal so <<Change>> fires on the internal Frame whenever
|
|
109
|
+
# the value changes — this lets on_change() use standard Subscription binding.
|
|
110
|
+
self._prev_value = self._internal.signal()
|
|
111
|
+
|
|
112
|
+
def _on_value_change(new_value: Any) -> None:
|
|
113
|
+
try:
|
|
114
|
+
prev, self._prev_value = self._prev_value, new_value
|
|
115
|
+
self._internal.event_generate(
|
|
116
|
+
"<<Change>>", data=ChangeEvent(value=new_value, prev_value=prev)
|
|
117
|
+
)
|
|
118
|
+
except tkinter.TclError:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
self._trace_id = self._internal.signal.subscribe(_on_value_change)
|
|
122
|
+
self._attach_to_parent(layout_kw)
|
|
123
|
+
|
|
124
|
+
# ----- Properties -----
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def value(self) -> Any:
|
|
128
|
+
"""The currently selected value, or `None` if nothing is selected."""
|
|
129
|
+
return self._internal.value
|
|
130
|
+
|
|
131
|
+
@value.setter
|
|
132
|
+
def value(self, v: Any) -> None:
|
|
133
|
+
self._internal.value = v
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def text(self) -> str | None:
|
|
137
|
+
"""The label of the selected option, or `None` if nothing is selected.
|
|
138
|
+
|
|
139
|
+
Read-only; the display complement of `value` (the selection's value).
|
|
140
|
+
"""
|
|
141
|
+
return self._internal.text_for(self._internal.value)
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def selection(self) -> dict | None:
|
|
145
|
+
"""The selected option as a full record dict — the data bag — or `None`.
|
|
146
|
+
|
|
147
|
+
`{'text': ..., 'value': ..., ...any extra keys}`, indexed by key like any
|
|
148
|
+
record. Read-only.
|
|
149
|
+
"""
|
|
150
|
+
return self._record_dict_for(self._internal.value)
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def disabled(self) -> bool:
|
|
154
|
+
"""Whether all buttons in the group are non-interactive."""
|
|
155
|
+
return self._internal._state == "disabled"
|
|
156
|
+
|
|
157
|
+
@disabled.setter
|
|
158
|
+
def disabled(self, v: bool) -> None:
|
|
159
|
+
self._internal.configure(state="disabled" if v else "normal")
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def title(self) -> str | None:
|
|
163
|
+
"""The label rendered above the group. Assigning to it updates it live.
|
|
164
|
+
|
|
165
|
+
Set to `None` (or `''`) to remove the label entirely.
|
|
166
|
+
"""
|
|
167
|
+
return self._internal.cget("text") or None
|
|
168
|
+
|
|
169
|
+
@title.setter
|
|
170
|
+
def title(self, value: str | None) -> None:
|
|
171
|
+
self._internal.configure(text=value or "")
|
|
172
|
+
|
|
173
|
+
# ----- Methods -----
|
|
174
|
+
|
|
175
|
+
def add(
|
|
176
|
+
self,
|
|
177
|
+
label: str,
|
|
178
|
+
value: Any | None = None,
|
|
179
|
+
*,
|
|
180
|
+
icon: Any = None,
|
|
181
|
+
disabled: bool = False,
|
|
182
|
+
localize: bool | Literal['auto'] | None = None,
|
|
183
|
+
**kwargs: Any,
|
|
184
|
+
) -> None:
|
|
185
|
+
"""Add a radio button to the group at runtime.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
label: Display text for the button. Pass `''` with an `icon` for an
|
|
189
|
+
icon-only button.
|
|
190
|
+
value: Value assigned when this button is selected. Defaults to `label`.
|
|
191
|
+
icon: Optional icon spec rendered beside the label (alone when
|
|
192
|
+
`label` is blank).
|
|
193
|
+
disabled: If `True`, the option is dimmed and cannot be selected.
|
|
194
|
+
localize: Translation mode for this option's label, overriding the
|
|
195
|
+
group's `localize=`. Defaults to the group setting.
|
|
196
|
+
"""
|
|
197
|
+
resolved = value if value is not None else label
|
|
198
|
+
add_kwargs, extras = self._option_render(icon, disabled, label)
|
|
199
|
+
item_localize = self._resolve_localize(override=localize)
|
|
200
|
+
if item_localize is not None:
|
|
201
|
+
add_kwargs["localize"] = item_localize
|
|
202
|
+
if localize is not None:
|
|
203
|
+
extras["localize"] = localize
|
|
204
|
+
self._internal.add(text=label, value=resolved, **add_kwargs, **kwargs)
|
|
205
|
+
self._add_option_record(label, resolved, extras)
|
|
206
|
+
|
|
207
|
+
# ----- Event shorthands -----
|
|
208
|
+
|
|
209
|
+
@overload
|
|
210
|
+
def on_change(self) -> Stream: ...
|
|
211
|
+
@overload
|
|
212
|
+
def on_change(self, handler: Callable[[ChangeEvent], Any]) -> Subscription: ...
|
|
213
|
+
def on_change(self, handler: Callable[[ChangeEvent], Any] | None = None) -> Stream | Subscription:
|
|
214
|
+
"""Register a callback fired whenever the selection changes.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
handler: Called with a :class:`~bootstack.events.ChangeEvent`; the
|
|
218
|
+
selected value is also available via `group.value`. Omit to get
|
|
219
|
+
a composable :class:`~bootstack.streams.Stream` instead.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
A cancellable :class:`~bootstack.events.Subscription` when a
|
|
223
|
+
handler is given, otherwise a :class:`~bootstack.streams.Stream`.
|
|
224
|
+
"""
|
|
225
|
+
return self.on("change", handler)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
register_widget_events(RadioGroup, _RADIOGROUP_EVENTS)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable
|
|
4
|
+
|
|
5
|
+
from bootstack.widgets._impl.primitives.scrollbar import Scrollbar as _InternalScrollbar
|
|
6
|
+
from bootstack.widgets._core.base import PublicWidgetBase
|
|
7
|
+
from bootstack.widgets.types import AccentToken, Orient, ScrollbarVariant
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Scrollbar(PublicWidgetBase):
|
|
11
|
+
"""A themed scrollbar — horizontal or vertical.
|
|
12
|
+
|
|
13
|
+
Most users will never instantiate `Scrollbar` directly; scrollable
|
|
14
|
+
containers wire scrollbars automatically. Use this when you need an
|
|
15
|
+
explicit, manually managed scrollbar.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
orient: `'vertical'` (default) or `'horizontal'`.
|
|
19
|
+
on_scroll: Callback invoked on scroll actions. Receives the scroll
|
|
20
|
+
arguments — `(fraction,)` for a move, or `(first, last)` for a drag.
|
|
21
|
+
accent: Accent token for styling.
|
|
22
|
+
variant: Scrollbar style — `'default'` (standard rounded bar) or
|
|
23
|
+
`'thin'` (slim square bar).
|
|
24
|
+
parent: Override the context-stack parent.
|
|
25
|
+
**kwargs: Layout placement options applied by the parent container —
|
|
26
|
+
`fill`, `expand`, `anchor`, `margin`, `row`, `column`, `sticky`.
|
|
27
|
+
See :doc:`/tasks/layout`.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
orient: Orient = "vertical",
|
|
33
|
+
*,
|
|
34
|
+
on_scroll: Callable | None = None,
|
|
35
|
+
accent: AccentToken | str | None = None,
|
|
36
|
+
variant: ScrollbarVariant = "default",
|
|
37
|
+
parent: Any = None,
|
|
38
|
+
**kwargs: Any,
|
|
39
|
+
) -> None:
|
|
40
|
+
self._parent = self._resolve_parent(parent)
|
|
41
|
+
layout_kw = self._split_layout_kwargs(kwargs)
|
|
42
|
+
|
|
43
|
+
tk_master = self._parent._child_master() if self._parent else None
|
|
44
|
+
|
|
45
|
+
internal_kwargs: dict[str, Any] = {"orient": orient}
|
|
46
|
+
if on_scroll is not None:
|
|
47
|
+
internal_kwargs["command"] = on_scroll
|
|
48
|
+
if accent is not None:
|
|
49
|
+
internal_kwargs["accent"] = accent
|
|
50
|
+
if variant is not None:
|
|
51
|
+
internal_kwargs["variant"] = variant
|
|
52
|
+
# **kwargs is layout-only (split out above) — not forwarded to the internal.
|
|
53
|
+
|
|
54
|
+
self._internal = _InternalScrollbar(tk_master, **internal_kwargs)
|
|
55
|
+
self._attach_to_parent(layout_kw)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def position(self) -> tuple[float, float]:
|
|
59
|
+
"""Current scroll position as a `(first, last)` fraction pair."""
|
|
60
|
+
return self._internal.get()
|
|
61
|
+
|
|
62
|
+
@position.setter
|
|
63
|
+
def position(self, value: tuple[float, float]) -> None:
|
|
64
|
+
self._internal.set(*value)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import overload, Any, Callable, Literal
|
|
4
|
+
|
|
5
|
+
from bootstack.widgets._impl.composites.scrollview import ScrollView as _InternalScrollView
|
|
6
|
+
from bootstack.widgets._impl.primitives.flexframe import FlexFrame
|
|
7
|
+
from bootstack.widgets._core.base import adapt_handler
|
|
8
|
+
from bootstack.widgets._core.container import FlexContainer
|
|
9
|
+
from bootstack.events import ScrollEvent, Subscription
|
|
10
|
+
from bootstack.streams import Stream
|
|
11
|
+
from bootstack.widgets.types import Padding, ScrollbarVariant
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ScrollView(FlexContainer):
|
|
15
|
+
"""A canvas-based scrollable container.
|
|
16
|
+
|
|
17
|
+
Place children inside the context block; they are laid out vertically
|
|
18
|
+
inside the scrollable area. Mouse-wheel scrolling is automatically
|
|
19
|
+
enabled for all descendants.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
scroll_direction: Which axis to scroll. Defaults to `'both'`.
|
|
23
|
+
scrollbar_visibility: When scrollbars appear — `'always'` (default),
|
|
24
|
+
`'never'`, `'hover'` (appears on mouse enter), or `'scroll'`
|
|
25
|
+
(auto-hides after `autohide_delay` ms of inactivity).
|
|
26
|
+
scrollbar_variant: Scrollbar style — `'default'` (standard rounded bar)
|
|
27
|
+
or `'thin'` (slim square bar).
|
|
28
|
+
autohide_delay: Milliseconds before scrollbars hide in `'scroll'` mode.
|
|
29
|
+
Defaults to `1000`.
|
|
30
|
+
height: Fixed height of the viewport in pixels. When set, the
|
|
31
|
+
canvas is pinned to this height regardless of content size.
|
|
32
|
+
Required for vertical scrolling unless the parent already
|
|
33
|
+
constrains the height. Defaults to `None`.
|
|
34
|
+
width: Fixed width of the viewport in pixels. Pins the canvas
|
|
35
|
+
width, similar to `height=`. Defaults to `None`.
|
|
36
|
+
show_border: Draw a 1 px border around the ScrollView frame.
|
|
37
|
+
Defaults to `False`.
|
|
38
|
+
padding: Space in pixels between the border and the canvas.
|
|
39
|
+
Defaults to `None` (no padding).
|
|
40
|
+
parent: Override the context-stack parent.
|
|
41
|
+
**kwargs: Layout placement options applied by the parent container —
|
|
42
|
+
`fill`, `expand`, `anchor`, `margin`, `row`, `column`, `sticky`.
|
|
43
|
+
See :doc:`/tasks/layout`.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
*,
|
|
49
|
+
scroll_direction: Literal["vertical", "horizontal", "both"] = "both",
|
|
50
|
+
scrollbar_visibility: Literal["always", "never", "hover", "scroll"] = "always",
|
|
51
|
+
scrollbar_variant: ScrollbarVariant = "default",
|
|
52
|
+
autohide_delay: int = 1000,
|
|
53
|
+
height: int | None = None,
|
|
54
|
+
width: int | None = None,
|
|
55
|
+
show_border: bool = False,
|
|
56
|
+
padding: Padding | None = None,
|
|
57
|
+
parent: Any = None,
|
|
58
|
+
**kwargs: Any,
|
|
59
|
+
) -> None:
|
|
60
|
+
self._parent = self._resolve_parent(parent)
|
|
61
|
+
layout_kw = self._split_layout_kwargs(kwargs)
|
|
62
|
+
tk_master = self._parent._child_master() if self._parent else None
|
|
63
|
+
|
|
64
|
+
internal_kwargs: dict[str, Any] = {
|
|
65
|
+
"scroll_direction": scroll_direction,
|
|
66
|
+
"scrollbar_visibility": scrollbar_visibility,
|
|
67
|
+
"scrollbar_variant": scrollbar_variant,
|
|
68
|
+
"autohide_delay": autohide_delay,
|
|
69
|
+
}
|
|
70
|
+
if height is not None:
|
|
71
|
+
internal_kwargs["height"] = height
|
|
72
|
+
if width is not None:
|
|
73
|
+
internal_kwargs["width"] = width
|
|
74
|
+
if show_border:
|
|
75
|
+
internal_kwargs["show_border"] = True
|
|
76
|
+
if padding is not None:
|
|
77
|
+
internal_kwargs["padding"] = padding
|
|
78
|
+
|
|
79
|
+
self._internal = _InternalScrollView(tk_master, **internal_kwargs)
|
|
80
|
+
# The scrollable content is a vertical flex frame inside the canvas, so
|
|
81
|
+
# children flow with the Row/Column vocabulary (grow / horizontal /
|
|
82
|
+
# vertical). It stretches children across the width by default — a scroll
|
|
83
|
+
# area fills its viewport width and scrolls overflow vertically — so
|
|
84
|
+
# content does not sit narrow against the left edge.
|
|
85
|
+
self._content_frame = FlexFrame(
|
|
86
|
+
self._internal.canvas, direction="vertical", horizontal_items="stretch"
|
|
87
|
+
)
|
|
88
|
+
self._internal.add(self._content_frame)
|
|
89
|
+
self._attach_to_parent(layout_kw)
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def _flex_frame(self) -> Any:
|
|
93
|
+
return self._content_frame
|
|
94
|
+
|
|
95
|
+
# ----- Scrolling -----
|
|
96
|
+
|
|
97
|
+
def enable_scrolling(self) -> None:
|
|
98
|
+
"""Enable mouse-wheel scrolling on the canvas and all children."""
|
|
99
|
+
self._internal.enable_scrolling()
|
|
100
|
+
|
|
101
|
+
def disable_scrolling(self) -> None:
|
|
102
|
+
"""Disable mouse-wheel scrolling on the canvas and all children."""
|
|
103
|
+
self._internal.disable_scrolling()
|
|
104
|
+
|
|
105
|
+
def refresh_bindings(self) -> None:
|
|
106
|
+
"""Refresh scroll bindings after dynamically adding many children."""
|
|
107
|
+
self._internal.refresh_bindings()
|
|
108
|
+
|
|
109
|
+
def scroll_to_top(self) -> None:
|
|
110
|
+
"""Scroll to the top of the content."""
|
|
111
|
+
self._internal.yview_moveto(0.0)
|
|
112
|
+
|
|
113
|
+
def scroll_to_bottom(self) -> None:
|
|
114
|
+
"""Scroll to the bottom of the content."""
|
|
115
|
+
self._internal.yview_moveto(1.0)
|
|
116
|
+
|
|
117
|
+
def scroll_to_left(self) -> None:
|
|
118
|
+
"""Scroll to the left edge of the content."""
|
|
119
|
+
self._internal.xview_moveto(0.0)
|
|
120
|
+
|
|
121
|
+
def scroll_to_right(self) -> None:
|
|
122
|
+
"""Scroll to the right edge of the content."""
|
|
123
|
+
self._internal.xview_moveto(1.0)
|
|
124
|
+
|
|
125
|
+
def yview_moveto(self, fraction: float) -> None:
|
|
126
|
+
"""Scroll to a vertical position.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
fraction: 0.0 (top) to 1.0 (bottom).
|
|
130
|
+
"""
|
|
131
|
+
self._internal.yview_moveto(fraction)
|
|
132
|
+
|
|
133
|
+
def xview_moveto(self, fraction: float) -> None:
|
|
134
|
+
"""Scroll to a horizontal position.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
fraction: 0.0 (left) to 1.0 (right).
|
|
138
|
+
"""
|
|
139
|
+
self._internal.xview_moveto(fraction)
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def scroll_position(self) -> tuple[float, float]:
|
|
143
|
+
"""Current scroll position as `(y, x)` fractions.
|
|
144
|
+
|
|
145
|
+
Each value is the fraction of the content that lies above (`y`) or to the
|
|
146
|
+
left (`x`) of the viewport's top-left corner. `(0.0, 0.0)` means scrolled
|
|
147
|
+
fully to the top-left. As you scroll toward the end the value grows toward
|
|
148
|
+
`1.0` but does not reach it while content fills the viewport (it tops out
|
|
149
|
+
at roughly `1.0` minus the visible fraction) — only an axis whose content
|
|
150
|
+
is shorter than the viewport reads `0.0`. Read this inside an `on_scroll`
|
|
151
|
+
handler to get the new position:
|
|
152
|
+
|
|
153
|
+
sv.on_scroll(lambda e: print(e.y, e.x))
|
|
154
|
+
"""
|
|
155
|
+
return self._internal.scroll_position
|
|
156
|
+
|
|
157
|
+
# ----- Events -----
|
|
158
|
+
|
|
159
|
+
@overload
|
|
160
|
+
def on_scroll(self) -> Stream: ...
|
|
161
|
+
@overload
|
|
162
|
+
def on_scroll(self, handler: Callable[[ScrollEvent], Any]) -> Subscription: ...
|
|
163
|
+
def on_scroll(self, handler: Callable[[ScrollEvent], Any] | None = None) -> Stream | Subscription:
|
|
164
|
+
"""Register a callback fired whenever the viewport position changes.
|
|
165
|
+
|
|
166
|
+
Fires on mouse-wheel scrolls, programmatic `scroll_to_*` / `yview_moveto`
|
|
167
|
+
calls, and keyboard scrolling (arrow keys / Page Up / Page Down / Home /
|
|
168
|
+
End when the canvas has focus from a click).
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
handler: Called with a :class:`~bootstack.events.ScrollEvent` carrying
|
|
172
|
+
`y` (vertical fraction, 0.0–1.0) and `x` (horizontal fraction).
|
|
173
|
+
Omit to get a composable :class:`~bootstack.streams.Stream`.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
A cancellable :class:`~bootstack.events.Subscription` when a handler
|
|
177
|
+
is given, otherwise a :class:`~bootstack.streams.Stream`.
|
|
178
|
+
"""
|
|
179
|
+
sequence = "<<BsScroll>>"
|
|
180
|
+
if handler is None:
|
|
181
|
+
def _source(h):
|
|
182
|
+
bid = self._internal.bind(sequence, adapt_handler(h), add="+")
|
|
183
|
+
return Subscription(self._internal, sequence, bid)
|
|
184
|
+
return Stream(self._internal, _source=_source)
|
|
185
|
+
bid = self._internal.bind(sequence, adapt_handler(handler), add="+")
|
|
186
|
+
return Subscription(self._internal, sequence, bid)
|