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,461 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import tkinter
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable, overload
|
|
5
|
+
|
|
6
|
+
from bootstack.widgets._core.context import current_container
|
|
7
|
+
from bootstack.widgets._core.events import resolve_event
|
|
8
|
+
from bootstack.events import Event, Subscription
|
|
9
|
+
from bootstack.errors import ParentResolutionError
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from bootstack.scheduling import Schedule
|
|
13
|
+
from bootstack.streams import Stream
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def adapt_handler(handler: Callable[[Any], Any]) -> Callable[[Any], Any]:
|
|
17
|
+
"""Wrap a public handler so it receives a bootstack event, not a raw one.
|
|
18
|
+
|
|
19
|
+
Data-carrying events (emitted with `data=<payload>`) deliver the payload
|
|
20
|
+
object directly — the handler argument *is* the payload. Native/context
|
|
21
|
+
events carry no payload, so the raw toolkit event is curated into a clean
|
|
22
|
+
:class:`~bootstack.events.Event` first.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def _wrapped(raw: Any) -> Any:
|
|
26
|
+
payload = getattr(raw, "data", None)
|
|
27
|
+
if payload is not None:
|
|
28
|
+
return handler(payload)
|
|
29
|
+
return handler(Event._from_tk(raw))
|
|
30
|
+
|
|
31
|
+
return _wrapped
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class _RawTkContainer:
|
|
35
|
+
"""Adapts a bare Tk widget so a public widget can be parented into it.
|
|
36
|
+
|
|
37
|
+
Public widgets expect a parent that exposes `_child_master()` and
|
|
38
|
+
`guide_layout()`. A raw Tk frame — e.g. the content frame a dialog
|
|
39
|
+
`content_builder` receives — has neither, so `parent=<that frame>` wraps it
|
|
40
|
+
here. The child is packed to fill the frame; flex/grid placement kwargs do
|
|
41
|
+
not apply to a bare frame and are ignored.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
__slots__ = ("_internal",)
|
|
45
|
+
|
|
46
|
+
def __init__(self, tk_widget: tkinter.Misc) -> None:
|
|
47
|
+
self._internal = tk_widget
|
|
48
|
+
|
|
49
|
+
def _child_master(self) -> tkinter.Misc:
|
|
50
|
+
return self._internal
|
|
51
|
+
|
|
52
|
+
def guide_layout(self, child: "PublicWidgetBase", **layout_kw: Any) -> None:
|
|
53
|
+
from bootstack.widgets._core.container import Placement
|
|
54
|
+
|
|
55
|
+
attached = layout_kw.pop("attached", True)
|
|
56
|
+
opts = {"fill": "both", "expand": True}
|
|
57
|
+
if attached:
|
|
58
|
+
child._internal.pack(in_=self._internal, **opts)
|
|
59
|
+
child._placement = Placement("pack", self._internal, dict(opts))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class PublicWidgetBase:
|
|
63
|
+
"""Base class for every public widget.
|
|
64
|
+
|
|
65
|
+
Subclasses must set `self._internal` before calling `_attach_to_parent`,
|
|
66
|
+
and should use `_split_layout_kwargs` to strip layout kwargs before
|
|
67
|
+
constructing the internal widget.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
_auto_place: bool = True
|
|
71
|
+
|
|
72
|
+
_internal: tkinter.Misc
|
|
73
|
+
_parent: "PublicWidgetBase | None"
|
|
74
|
+
_placement: Any # container.Placement — set when first placed in a layout
|
|
75
|
+
|
|
76
|
+
def _resolve_parent(
|
|
77
|
+
self,
|
|
78
|
+
explicit_parent: "PublicWidgetBase | None",
|
|
79
|
+
) -> "PublicWidgetBase | None":
|
|
80
|
+
if explicit_parent is not None:
|
|
81
|
+
# A bare Tk widget (e.g. a dialog content frame handed to a
|
|
82
|
+
# content_builder) can't host public widgets directly — wrap it so
|
|
83
|
+
# `parent=<tk frame>` works with the public API.
|
|
84
|
+
if not isinstance(explicit_parent, PublicWidgetBase) and isinstance(
|
|
85
|
+
explicit_parent, tkinter.Misc
|
|
86
|
+
):
|
|
87
|
+
return _RawTkContainer(explicit_parent)
|
|
88
|
+
return explicit_parent
|
|
89
|
+
container = current_container()
|
|
90
|
+
if container is None and self._auto_place:
|
|
91
|
+
# A placed widget needs somewhere to mount. Resolving to None here
|
|
92
|
+
# would fail later with a cryptic Tk error; surface the real cause —
|
|
93
|
+
# this is the one failure mode of the builder-function pattern (a
|
|
94
|
+
# builder called outside any layout context).
|
|
95
|
+
from bootstack.errors import BootstackError
|
|
96
|
+
|
|
97
|
+
raise BootstackError(
|
|
98
|
+
f"{type(self).__name__} was created outside any container and "
|
|
99
|
+
f"without parent=. Create it inside a layout context "
|
|
100
|
+
f"(e.g. `with bs.App():` or `with bs.Column():`), or pass "
|
|
101
|
+
f"parent=<container>. A reusable builder function must be called "
|
|
102
|
+
f"inside a container so its widgets have somewhere to mount."
|
|
103
|
+
)
|
|
104
|
+
return container
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def _split_layout_kwargs(kwargs: dict) -> dict:
|
|
108
|
+
"""Pop and return layout kwargs from `kwargs`, mutating it in place."""
|
|
109
|
+
from bootstack.widgets._core.container import (
|
|
110
|
+
PACK_KEYS, GRID_KEYS, PLACE_KEYS, PLACE_TRIGGER_KEYS, FLEX_CHILD_KEYS,
|
|
111
|
+
)
|
|
112
|
+
place_mode = any(k in kwargs for k in PLACE_TRIGGER_KEYS)
|
|
113
|
+
if place_mode:
|
|
114
|
+
layout_keys = (PLACE_KEYS - {"width", "height", "anchor"}) | PLACE_TRIGGER_KEYS
|
|
115
|
+
else:
|
|
116
|
+
layout_keys = PACK_KEYS | GRID_KEYS | FLEX_CHILD_KEYS
|
|
117
|
+
layout = {k: kwargs.pop(k) for k in list(kwargs) if k in layout_keys}
|
|
118
|
+
# `attached` is geometry-manager-agnostic — capture it in every mode.
|
|
119
|
+
if "attached" in kwargs:
|
|
120
|
+
layout["attached"] = kwargs.pop("attached")
|
|
121
|
+
return layout
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def _reject_signal_text(text: Any) -> None:
|
|
125
|
+
"""Guard against a `Signal` landing in the plain `text=` slot.
|
|
126
|
+
|
|
127
|
+
A `Signal` is truthy and stringifies to its name, so passing one as
|
|
128
|
+
`text=` silently renders something like `"SIG2"` instead of binding.
|
|
129
|
+
The supported path is `textsignal=` (two-way binding). Raise a clear
|
|
130
|
+
`TypeError` rather than render the name.
|
|
131
|
+
"""
|
|
132
|
+
from bootstack._core.capabilities.signals import is_signal
|
|
133
|
+
|
|
134
|
+
if is_signal(text):
|
|
135
|
+
raise TypeError(
|
|
136
|
+
"text= expects a string; pass a Signal via textsignal= to bind "
|
|
137
|
+
"reactive text (text= would render the Signal's name)."
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def _attach_to_parent(self, layout_kw: dict) -> None:
|
|
141
|
+
if not self._auto_place:
|
|
142
|
+
return
|
|
143
|
+
parent = self._parent
|
|
144
|
+
if parent is None:
|
|
145
|
+
return
|
|
146
|
+
guide = getattr(parent, "guide_layout", None)
|
|
147
|
+
if guide is None:
|
|
148
|
+
raise ParentResolutionError(
|
|
149
|
+
f"{type(parent).__name__} is not a container (no guide_layout); "
|
|
150
|
+
f"cannot place {type(self).__name__} inside it."
|
|
151
|
+
)
|
|
152
|
+
guide(self, **layout_kw)
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def tk(self) -> tkinter.Misc:
|
|
156
|
+
"""Underlying tk/ttk widget. UNSUPPORTED — escape-hatch use only."""
|
|
157
|
+
return self._internal
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def schedule(self) -> "Schedule":
|
|
161
|
+
"""Scheduler tied to this widget's lifetime.
|
|
162
|
+
|
|
163
|
+
All jobs are automatically cancelled when the widget is destroyed.
|
|
164
|
+
First access creates the `Schedule` instance; subsequent accesses
|
|
165
|
+
return the same instance.
|
|
166
|
+
|
|
167
|
+
Usage::
|
|
168
|
+
|
|
169
|
+
self.schedule.delay(500, callback)
|
|
170
|
+
self.schedule.every(1000, tick)
|
|
171
|
+
job = self.schedule.idle(refresh)
|
|
172
|
+
job.cancel()
|
|
173
|
+
"""
|
|
174
|
+
if not hasattr(self, "_schedule"):
|
|
175
|
+
from bootstack.scheduling import Schedule
|
|
176
|
+
self._schedule = Schedule(self._internal)
|
|
177
|
+
return self._schedule
|
|
178
|
+
|
|
179
|
+
# ----- on() — overloaded -------------------------------------------------
|
|
180
|
+
|
|
181
|
+
@overload
|
|
182
|
+
def on(self, event: str) -> "Stream": ...
|
|
183
|
+
@overload
|
|
184
|
+
def on(self, event: str, handler: Callable[[Any], Any]) -> Subscription: ...
|
|
185
|
+
|
|
186
|
+
def on(
|
|
187
|
+
self,
|
|
188
|
+
event: str,
|
|
189
|
+
handler: Callable[[Any], Any] | None = None,
|
|
190
|
+
) -> "Stream | Subscription":
|
|
191
|
+
"""Bind `handler` to `event`, or return a composable `Stream`.
|
|
192
|
+
|
|
193
|
+
With a handler — binds immediately and returns a `Subscription`::
|
|
194
|
+
|
|
195
|
+
sub = widget.on("change", handler)
|
|
196
|
+
sub.cancel()
|
|
197
|
+
|
|
198
|
+
Without a handler — returns a `Stream` for operator chaining.
|
|
199
|
+
The Tk binding is created lazily when `.listen()` is called::
|
|
200
|
+
|
|
201
|
+
sub = widget.on("change").debounce(300).listen(handler)
|
|
202
|
+
sub.cancel()
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
event: Event name (e.g. `"change"`, `"click"`).
|
|
206
|
+
handler: Optional callback. If omitted, a `Stream` is returned.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
`Subscription` when a handler is provided; `Stream` otherwise.
|
|
210
|
+
"""
|
|
211
|
+
sequence = resolve_event(self, str(event))
|
|
212
|
+
|
|
213
|
+
if handler is not None:
|
|
214
|
+
bind_id = self._internal.bind(sequence, adapt_handler(handler), add="+")
|
|
215
|
+
return Subscription(self._internal, sequence, bind_id)
|
|
216
|
+
|
|
217
|
+
# No handler — return a lazy Stream.
|
|
218
|
+
from bootstack.streams import Stream
|
|
219
|
+
|
|
220
|
+
widget = self._internal
|
|
221
|
+
|
|
222
|
+
def _source(downstream: Callable[[Any], Any]) -> Subscription:
|
|
223
|
+
bind_id = widget.bind(sequence, adapt_handler(downstream), add="+")
|
|
224
|
+
return Subscription(widget, sequence, bind_id)
|
|
225
|
+
|
|
226
|
+
return Stream(self._internal, _source=_source)
|
|
227
|
+
|
|
228
|
+
def emit(self, event: str, *, data: Any = None) -> None:
|
|
229
|
+
"""Fire a named event on this widget, as if it produced the event itself.
|
|
230
|
+
|
|
231
|
+
This is how a composite widget surfaces high-level activity to its
|
|
232
|
+
listeners, and the generic counterpart to the `on_*()` shorthands for
|
|
233
|
+
firing events that have no dedicated method.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
event: The event name, unprefixed — the same name you pass to `on()`
|
|
237
|
+
or an `on_<event>()` shorthand (e.g. `'change'`, `'select'`).
|
|
238
|
+
data: The payload delivered to handlers. For a data-carrying event,
|
|
239
|
+
pass the matching payload dataclass from `bootstack.events` — the
|
|
240
|
+
same object an `on_<event>()` handler receives. Leave as None for
|
|
241
|
+
native events (click, hover, focus, …), which carry no payload.
|
|
242
|
+
|
|
243
|
+
Example:
|
|
244
|
+
.. code-block:: python
|
|
245
|
+
|
|
246
|
+
widget.emit("change", data=bs.events.ChangeEvent(value=new_value))
|
|
247
|
+
"""
|
|
248
|
+
sequence = resolve_event(self, str(event))
|
|
249
|
+
self._internal.event_generate(sequence, data=data)
|
|
250
|
+
|
|
251
|
+
# ----- Lifecycle --------------------------------------------------------
|
|
252
|
+
|
|
253
|
+
def destroy(self) -> None:
|
|
254
|
+
"""Destroy the widget and release the resources it holds.
|
|
255
|
+
|
|
256
|
+
Removes the widget from its parent, destroys its children, and cancels
|
|
257
|
+
any pending or repeating jobs on its `schedule`. After this the widget
|
|
258
|
+
must not be used again. Destroying a container destroys everything
|
|
259
|
+
inside it.
|
|
260
|
+
"""
|
|
261
|
+
self._internal.destroy()
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def is_attached(self) -> bool:
|
|
265
|
+
"""Whether the widget is currently placed in its layout.
|
|
266
|
+
|
|
267
|
+
`True` while the widget occupies space in its parent; `False` after
|
|
268
|
+
`detach` (or before it has ever been placed). A detached widget keeps
|
|
269
|
+
its state and can be returned to the layout with `attach`.
|
|
270
|
+
"""
|
|
271
|
+
try:
|
|
272
|
+
return bool(self._internal.winfo_manager())
|
|
273
|
+
except tkinter.TclError:
|
|
274
|
+
return False
|
|
275
|
+
|
|
276
|
+
def detach(self) -> None:
|
|
277
|
+
"""Remove the widget from its layout without destroying it.
|
|
278
|
+
|
|
279
|
+
The widget stops occupying space but keeps its state, children, and
|
|
280
|
+
event bindings, ready to be returned with `attach`. The current
|
|
281
|
+
position is snapshotted so a plain `attach()` restores it exactly —
|
|
282
|
+
for stacked siblings this is the index among the *currently attached*
|
|
283
|
+
siblings, so detaching other siblings first shifts that index.
|
|
284
|
+
|
|
285
|
+
Calling `detach` on a widget that is already detached, or one that was
|
|
286
|
+
never placed in a layout, does nothing. Fires `on_detach`.
|
|
287
|
+
"""
|
|
288
|
+
placement = getattr(self, "_placement", None)
|
|
289
|
+
if placement is None or not self.is_attached:
|
|
290
|
+
return
|
|
291
|
+
if placement.method == "flex":
|
|
292
|
+
# The FlexFrame's managed list IS the attached-sibling order, so the
|
|
293
|
+
# current managed index is the slot a plain attach() should restore.
|
|
294
|
+
idx = placement.master.index_of(self._internal)
|
|
295
|
+
placement.index = idx if idx >= 0 else None
|
|
296
|
+
placement.master.remove_child(self._internal)
|
|
297
|
+
elif placement.method == "pack":
|
|
298
|
+
slaves = list(placement.master.pack_slaves())
|
|
299
|
+
try:
|
|
300
|
+
placement.index = slaves.index(self._internal)
|
|
301
|
+
except ValueError:
|
|
302
|
+
placement.index = None
|
|
303
|
+
self._internal.pack_forget()
|
|
304
|
+
elif placement.method == "grid":
|
|
305
|
+
# grid_forget (not grid_remove): the "remembered" state left by
|
|
306
|
+
# grid_remove rejects an explicit re-grid; we reconstruct fully
|
|
307
|
+
# from the stored cell options instead.
|
|
308
|
+
self._internal.grid_forget()
|
|
309
|
+
else:
|
|
310
|
+
self._internal.place_forget()
|
|
311
|
+
|
|
312
|
+
def attach(self, **kwargs: Any) -> None:
|
|
313
|
+
"""Return a detached widget to its layout, optionally moving it.
|
|
314
|
+
|
|
315
|
+
With no arguments, restores the widget to exactly where `detach` took
|
|
316
|
+
it from. Any layout kwargs accepted by the original placement (e.g.
|
|
317
|
+
`fill`, `expand`, `anchor`, `sticky`, `margin`) override the stored
|
|
318
|
+
options. For stacked widgets, `index=` sets the position among the
|
|
319
|
+
currently attached siblings (or pass an explicit `before=`/`after=`
|
|
320
|
+
sibling); without one, the snapshotted position is used.
|
|
321
|
+
|
|
322
|
+
Calling `attach` on a widget that is already attached moves it (the
|
|
323
|
+
kwargs are re-applied). Fires `on_attach`.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
**kwargs: Layout placement options to override for this placement.
|
|
327
|
+
|
|
328
|
+
Raises:
|
|
329
|
+
ParentResolutionError: If the widget was never placed in a layout.
|
|
330
|
+
"""
|
|
331
|
+
from bootstack.widgets._core.container import (
|
|
332
|
+
PACK_KEYS, GRID_KEYS, PLACE_KEYS, FLEX_CHILD_KEYS,
|
|
333
|
+
normalize_fill, _expand_margin, _flex_child_opts, _reject_legacy_child_kwargs,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
placement = getattr(self, "_placement", None)
|
|
337
|
+
if placement is None:
|
|
338
|
+
raise ParentResolutionError(
|
|
339
|
+
f"{type(self).__name__} was never placed in a layout; "
|
|
340
|
+
f"nothing to attach()."
|
|
341
|
+
)
|
|
342
|
+
# Re-applying to an attached widget is a move: clear it first so the
|
|
343
|
+
# placement (and any index math) starts from a clean slate.
|
|
344
|
+
if self.is_attached:
|
|
345
|
+
self.detach()
|
|
346
|
+
|
|
347
|
+
master = placement.master
|
|
348
|
+
options = dict(placement.options)
|
|
349
|
+
|
|
350
|
+
if placement.method == "flex":
|
|
351
|
+
index = kwargs.pop("index", placement.index)
|
|
352
|
+
if kwargs:
|
|
353
|
+
_reject_legacy_child_kwargs(kwargs, type(self).__name__)
|
|
354
|
+
_expand_margin(kwargs)
|
|
355
|
+
options.update(_flex_child_opts(self, kwargs))
|
|
356
|
+
master.add_child(self._internal, options, index=index)
|
|
357
|
+
placement.options = options
|
|
358
|
+
return
|
|
359
|
+
|
|
360
|
+
if kwargs:
|
|
361
|
+
if "fill" in kwargs:
|
|
362
|
+
kwargs["fill"] = normalize_fill(kwargs["fill"])
|
|
363
|
+
_expand_margin(kwargs)
|
|
364
|
+
|
|
365
|
+
if placement.method == "pack":
|
|
366
|
+
order = {k: kwargs.pop(k) for k in ("index", "before", "after") if k in kwargs}
|
|
367
|
+
options.update({k: v for k, v in kwargs.items() if k in PACK_KEYS})
|
|
368
|
+
if not order and placement.index is not None:
|
|
369
|
+
order["index"] = placement.index
|
|
370
|
+
order_kw = resolve_pack_order(order, master)
|
|
371
|
+
self._internal.pack(in_=master, **options, **order_kw)
|
|
372
|
+
elif placement.method == "grid":
|
|
373
|
+
options.update({k: v for k, v in kwargs.items() if k in GRID_KEYS})
|
|
374
|
+
self._internal.grid(in_=master, **options)
|
|
375
|
+
else:
|
|
376
|
+
options.update({k: v for k, v in kwargs.items() if k in PLACE_KEYS})
|
|
377
|
+
self._internal.place(in_=master, **options)
|
|
378
|
+
placement.options = options
|
|
379
|
+
|
|
380
|
+
@overload
|
|
381
|
+
def on_destroy(self) -> "Stream": ...
|
|
382
|
+
@overload
|
|
383
|
+
def on_destroy(self, handler: Callable[[Event], Any]) -> Subscription: ...
|
|
384
|
+
def on_destroy(
|
|
385
|
+
self,
|
|
386
|
+
handler: Callable[[Event], Any] | None = None,
|
|
387
|
+
) -> "Stream | Subscription":
|
|
388
|
+
"""Register a callback fired when the widget is destroyed.
|
|
389
|
+
|
|
390
|
+
Fires once, as the widget is torn down — the place to release resources
|
|
391
|
+
the widget owns that aren't cleaned up automatically (file handles,
|
|
392
|
+
observers, external subscriptions). The handler receives a curated
|
|
393
|
+
:class:`~bootstack.events.Event`.
|
|
394
|
+
|
|
395
|
+
Args:
|
|
396
|
+
handler: Called as the widget is destroyed. Omit to get a composable
|
|
397
|
+
:class:`~bootstack.streams.Stream`.
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
A cancellable :class:`~bootstack.events.Subscription` when a handler
|
|
401
|
+
is given, otherwise a :class:`~bootstack.streams.Stream`.
|
|
402
|
+
"""
|
|
403
|
+
return self.on("destroy", handler)
|
|
404
|
+
|
|
405
|
+
@overload
|
|
406
|
+
def on_attach(self) -> "Stream": ...
|
|
407
|
+
@overload
|
|
408
|
+
def on_attach(self, handler: Callable[[Event], Any]) -> Subscription: ...
|
|
409
|
+
def on_attach(
|
|
410
|
+
self,
|
|
411
|
+
handler: Callable[[Event], Any] | None = None,
|
|
412
|
+
) -> "Stream | Subscription":
|
|
413
|
+
"""Register a callback fired when the widget enters the layout.
|
|
414
|
+
|
|
415
|
+
Fires each time the widget becomes visible in its parent — on initial
|
|
416
|
+
placement and on every `attach`. Pair it with `on_detach` to keep
|
|
417
|
+
per-visibility resources (timers, observers) tied to the widget's
|
|
418
|
+
presence on screen. The handler receives a curated
|
|
419
|
+
:class:`~bootstack.events.Event`.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
handler: Called when the widget is attached. Omit to get a
|
|
423
|
+
composable :class:`~bootstack.streams.Stream`.
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
A cancellable :class:`~bootstack.events.Subscription` when a handler
|
|
427
|
+
is given, otherwise a :class:`~bootstack.streams.Stream`.
|
|
428
|
+
"""
|
|
429
|
+
return self.on("attach", handler)
|
|
430
|
+
|
|
431
|
+
@overload
|
|
432
|
+
def on_detach(self) -> "Stream": ...
|
|
433
|
+
@overload
|
|
434
|
+
def on_detach(self, handler: Callable[[Event], Any]) -> Subscription: ...
|
|
435
|
+
def on_detach(
|
|
436
|
+
self,
|
|
437
|
+
handler: Callable[[Event], Any] | None = None,
|
|
438
|
+
) -> "Stream | Subscription":
|
|
439
|
+
"""Register a callback fired when the widget leaves the layout.
|
|
440
|
+
|
|
441
|
+
Fires each time the widget stops occupying space in its parent — on
|
|
442
|
+
`detach` and when an ancestor hides it. Pair it with `on_attach` to
|
|
443
|
+
release per-visibility resources. The handler receives a curated
|
|
444
|
+
:class:`~bootstack.events.Event`.
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
handler: Called when the widget is detached. Omit to get a
|
|
448
|
+
composable :class:`~bootstack.streams.Stream`.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
A cancellable :class:`~bootstack.events.Subscription` when a handler
|
|
452
|
+
is given, otherwise a :class:`~bootstack.streams.Stream`.
|
|
453
|
+
"""
|
|
454
|
+
return self.on("detach", handler)
|
|
455
|
+
|
|
456
|
+
def __repr__(self) -> str:
|
|
457
|
+
try:
|
|
458
|
+
name = self._internal._w # type: ignore[attr-defined]
|
|
459
|
+
except AttributeError:
|
|
460
|
+
name = "?"
|
|
461
|
+
return f"<{type(self).__name__} {name}>"
|