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,305 @@
|
|
|
1
|
+
"""Common type aliases and base TypedDicts for bootstack widgets."""
|
|
2
|
+
|
|
3
|
+
import tkinter
|
|
4
|
+
from typing import Any, Callable, Literal, TypedDict
|
|
5
|
+
|
|
6
|
+
# ---------------------------------------------------------------------------
|
|
7
|
+
# Primitive aliases
|
|
8
|
+
# ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
# Internal: parent-widget type. Not part of the public API.
|
|
11
|
+
Master = tkinter.Misc | None
|
|
12
|
+
|
|
13
|
+
# `Event` lives in the public `bootstack.events` package; re-imported here so the
|
|
14
|
+
# many widget modules that pull it alongside the layout aliases keep working.
|
|
15
|
+
from bootstack.events import Event # noqa: E402
|
|
16
|
+
|
|
17
|
+
EventCallback = Callable[["Event"], None]
|
|
18
|
+
"""Callback that receives an `Event` object."""
|
|
19
|
+
|
|
20
|
+
CommandCallback = Callable[[], Any]
|
|
21
|
+
"""Callback invoked with no arguments, e.g. a button `command=`."""
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# Layout literals
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
Anchor = Literal['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center']
|
|
28
|
+
"""Alignment anchor position."""
|
|
29
|
+
|
|
30
|
+
Orient = Literal['horizontal', 'vertical']
|
|
31
|
+
"""Widget orientation."""
|
|
32
|
+
|
|
33
|
+
LocalizeMode = bool | Literal['auto']
|
|
34
|
+
"""Whether a widget's text is translated through the catalog — `True`, `False`, or
|
|
35
|
+
`'auto'` (translate when a translation is registered, otherwise show the literal)."""
|
|
36
|
+
|
|
37
|
+
Justify = Literal['left', 'center', 'right']
|
|
38
|
+
"""Text justification within a widget."""
|
|
39
|
+
|
|
40
|
+
Relief = Literal['flat', 'raised', 'sunken', 'groove', 'ridge', 'solid']
|
|
41
|
+
"""Border relief style."""
|
|
42
|
+
|
|
43
|
+
CompoundMode = Literal['text', 'image', 'top', 'bottom', 'left', 'right', 'center', 'none']
|
|
44
|
+
"""Relative position of image to text when both are displayed."""
|
|
45
|
+
|
|
46
|
+
Padding = int | tuple[int, int] | tuple[int, int, int, int]
|
|
47
|
+
"""Widget padding in pixels — a single value for all sides, `(x, y)` for the
|
|
48
|
+
horizontal and vertical amounts, or `(left, top, right, bottom)`."""
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
# Widget state
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
WidgetState = Literal['normal', 'active', 'disabled', 'readonly']
|
|
55
|
+
"""Widget interaction state."""
|
|
56
|
+
|
|
57
|
+
WidgetDensity = Literal['default', 'compact']
|
|
58
|
+
"""Widget density — `'default'` for normal sizing, `'compact'` for reduced padding."""
|
|
59
|
+
|
|
60
|
+
ScrollbarVariant = Literal['default', 'thin']
|
|
61
|
+
"""Scrollbar style — `'default'` for the standard rounded bar, `'thin'` for a slim
|
|
62
|
+
square bar that suits compact lists, panels, and popups."""
|
|
63
|
+
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
# bootstack styling tokens
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
AccentToken = Literal['primary', 'secondary', 'info', 'success', 'warning', 'danger']
|
|
69
|
+
"""A semantic color accent. The accent params widen this with `| str`, so
|
|
70
|
+
`'default'`, `'muted'`, and modifiers (`'primary[+1]'`, `'primary[500]'`,
|
|
71
|
+
`'primary[subtle]'`) are also accepted."""
|
|
72
|
+
|
|
73
|
+
SurfaceToken = Literal['content', 'card', 'card_raised', 'chrome', 'overlay']
|
|
74
|
+
"""Background surface context token."""
|
|
75
|
+
|
|
76
|
+
WindowStyle = Literal['mica', 'acrylic', 'aero', 'transparent', 'win7']
|
|
77
|
+
"""A Windows-only window effect (no-op elsewhere). These are the common effects;
|
|
78
|
+
the underlying pywinstyles library accepts more (pass any as a plain string).
|
|
79
|
+
See https://pypi.org/project/pywinstyles/ for the full set."""
|
|
80
|
+
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
# Geometry manager literals
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
Fill = Literal['none', 'x', 'y', 'both', 'horizontal', 'vertical', 'all']
|
|
86
|
+
"""Fill axis for stack layout. `'horizontal'`/`'vertical'`/`'all'` are readable
|
|
87
|
+
aliases for `'x'`/`'y'`/`'both'`."""
|
|
88
|
+
|
|
89
|
+
Sticky = Literal['n', 's', 'e', 'w', 'ns', 'ew', 'nsew', 'ne', 'nw', 'se', 'sw', 'nse', 'nsw', 'new', 'sew', '']
|
|
90
|
+
"""Cell alignment for Grid layout. Any combination of `'n'`, `'s'`, `'e'`, `'w'`."""
|
|
91
|
+
|
|
92
|
+
Side = Literal['left', 'top', 'right', 'bottom']
|
|
93
|
+
"""Side placement for stack layout."""
|
|
94
|
+
|
|
95
|
+
HAlign = Literal['left', 'center', 'right', 'stretch']
|
|
96
|
+
"""Horizontal alignment of a widget within its slot — `'left'`, `'center'`,
|
|
97
|
+
`'right'`, or `'stretch'` (fill the width)."""
|
|
98
|
+
|
|
99
|
+
VAlign = Literal['top', 'center', 'bottom', 'stretch']
|
|
100
|
+
"""Vertical alignment of a widget within its slot — `'top'`, `'center'`,
|
|
101
|
+
`'bottom'`, or `'stretch'` (fill the height)."""
|
|
102
|
+
|
|
103
|
+
HArrange = Literal['left', 'center', 'right', 'space-between', 'space-around', 'space-evenly']
|
|
104
|
+
"""Horizontal arrangement of a Row's children as a group — cluster them
|
|
105
|
+
(`'left'`/`'center'`/`'right'`) or spread them apart with the `'space-*'` modes."""
|
|
106
|
+
|
|
107
|
+
VArrange = Literal['top', 'center', 'bottom', 'space-between', 'space-around', 'space-evenly']
|
|
108
|
+
"""Vertical arrangement of a Column's children as a group — cluster them
|
|
109
|
+
(`'top'`/`'center'`/`'bottom'`) or spread them apart with the `'space-*'` modes."""
|
|
110
|
+
|
|
111
|
+
Direction = Literal['vertical', 'horizontal', 'row', 'column', 'row-reverse', 'column-reverse']
|
|
112
|
+
"""Layout direction."""
|
|
113
|
+
|
|
114
|
+
LayoutKind = Literal['column', 'row', 'grid']
|
|
115
|
+
"""Internal layout manager for a container — `'column'`/`'row'` pack children
|
|
116
|
+
top-to-bottom / left-to-right; `'grid'` arranges them in rows and columns."""
|
|
117
|
+
|
|
118
|
+
AutoFlow = Literal['row', 'column', 'row-dense', 'column-dense', 'none']
|
|
119
|
+
"""Grid auto-placement direction. `'row'`/`'column'` fill along that axis;
|
|
120
|
+
the `'-dense'` variants backfill gaps; `'none'` disables auto-placement."""
|
|
121
|
+
|
|
122
|
+
BorderMode = Literal['inside', 'outside']
|
|
123
|
+
"""Border mode for the place geometry manager."""
|
|
124
|
+
|
|
125
|
+
ButtonVariant = Literal['default', 'solid', 'outline', 'ghost']
|
|
126
|
+
"""Style variant shared by the button family (Button, ButtonGroup, MenuButton,
|
|
127
|
+
SelectButton, ToggleButton, ToggleGroup, Toolbar) — `'default'`, `'solid'`,
|
|
128
|
+
`'outline'`, or `'ghost'`."""
|
|
129
|
+
|
|
130
|
+
AccordionVariant = Literal['solid', 'default']
|
|
131
|
+
"""Accordion header style — `'solid'`, or `'default'` for a ghost/transparent header."""
|
|
132
|
+
|
|
133
|
+
Region = Literal['before', 'center', 'after']
|
|
134
|
+
"""Placement region within a menu bar — `'before'`, `'center'`, or `'after'`."""
|
|
135
|
+
|
|
136
|
+
IconPosition = Literal['left', 'right', 'top', 'bottom']
|
|
137
|
+
"""Position of an icon relative to a widget's text — `'left'`, `'right'`,
|
|
138
|
+
`'top'`, or `'bottom'`."""
|
|
139
|
+
|
|
140
|
+
SelectionMode = Literal['none', 'single', 'multi']
|
|
141
|
+
"""How many rows/items can be selected — `'none'`, `'single'`, or `'multi'`."""
|
|
142
|
+
|
|
143
|
+
ExportScope = Literal['all', 'page', 'selection']
|
|
144
|
+
"""Which rows an export covers — `'all'` rows, the current `'page'`, or the
|
|
145
|
+
current `'selection'`."""
|
|
146
|
+
|
|
147
|
+
ExportFormat = Literal['csv', 'tsv', 'xlsx']
|
|
148
|
+
"""A tabular export format — `'csv'`, `'tsv'`, or `'xlsx'`."""
|
|
149
|
+
|
|
150
|
+
ToastCorner = Literal['top-left', 'top-right', 'bottom-left', 'bottom-right']
|
|
151
|
+
"""A screen corner for a `toast()` or `Notification` — anchored to the corner of
|
|
152
|
+
the monitor the app window is on, where they stack."""
|
|
153
|
+
|
|
154
|
+
SnackbarAlign = Literal['left', 'center', 'right']
|
|
155
|
+
"""Horizontal alignment of a `Snackbar` along the app window's bottom edge."""
|
|
156
|
+
|
|
157
|
+
# ---------------------------------------------------------------------------
|
|
158
|
+
# Base TypedDicts
|
|
159
|
+
# ---------------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
class BaseWidgetKwargs(TypedDict, total=False):
|
|
162
|
+
"""Low-level options shared by all bootstack widget wrappers."""
|
|
163
|
+
style: str
|
|
164
|
+
class_: str
|
|
165
|
+
cursor: str
|
|
166
|
+
name: str
|
|
167
|
+
takefocus: bool
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class StyledKwargs(BaseWidgetKwargs, total=False):
|
|
171
|
+
"""Extends BaseWidgetKwargs with bootstack styling tokens."""
|
|
172
|
+
accent: AccentToken | str
|
|
173
|
+
variant: str
|
|
174
|
+
surface: SurfaceToken | str
|
|
175
|
+
style_options: dict[str, Any]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ---------------------------------------------------------------------------
|
|
179
|
+
# Data-entry config types (DataTable columns + add/edit form)
|
|
180
|
+
# ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
EditorType = Literal[
|
|
183
|
+
'textfield',
|
|
184
|
+
'numberfield',
|
|
185
|
+
'passwordfield',
|
|
186
|
+
'datefield',
|
|
187
|
+
'textarea',
|
|
188
|
+
'select',
|
|
189
|
+
'spinnerfield',
|
|
190
|
+
'switch',
|
|
191
|
+
'checkbox',
|
|
192
|
+
'slider',
|
|
193
|
+
]
|
|
194
|
+
"""The field type used to edit a value in a `Form` or `DataTable` add/edit
|
|
195
|
+
dialog. Inferred from the value's `dtype` when omitted."""
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class ColumnSpec(TypedDict, total=False):
|
|
199
|
+
"""A column definition for `DataTable(columns=...)`.
|
|
200
|
+
|
|
201
|
+
Columns may be plain key strings or these dicts. Only `key` is required; the
|
|
202
|
+
editor keys (`editor`, `editor_options`, `dtype`, `readonly`, `required`)
|
|
203
|
+
shape how the column appears in the built-in add/edit dialog.
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
key: str
|
|
207
|
+
"""Record field this column reads and writes."""
|
|
208
|
+
text: str
|
|
209
|
+
"""Header label. Defaults to `key`."""
|
|
210
|
+
width: int
|
|
211
|
+
"""Column width in pixels."""
|
|
212
|
+
minwidth: int
|
|
213
|
+
"""Minimum column width in pixels."""
|
|
214
|
+
anchor: str
|
|
215
|
+
"""Cell alignment — `'w'`, `'center'`, or `'e'`."""
|
|
216
|
+
format: "str | Callable[[Any], str]"
|
|
217
|
+
"""Display formatter for the cell — a format-spec string applied as
|
|
218
|
+
`spec.format(value)` (e.g. `'${:,.0f}'` → `$70,000`) or a callable
|
|
219
|
+
`(value) -> str`. Display only: sorting, filtering, editing, and export use
|
|
220
|
+
the raw value."""
|
|
221
|
+
dtype: str
|
|
222
|
+
"""Value type hint (e.g. `'int'`, `'text'`); drives alignment and the editor."""
|
|
223
|
+
editor: EditorType
|
|
224
|
+
"""Field type used to edit this column in the add/edit dialog. Inferred from
|
|
225
|
+
`dtype` when omitted."""
|
|
226
|
+
editor_options: dict
|
|
227
|
+
"""Keyword options passed to the editor field."""
|
|
228
|
+
readonly: bool
|
|
229
|
+
"""Show the column but make it non-editable in the dialog."""
|
|
230
|
+
required: bool
|
|
231
|
+
"""Require a value in the add/edit dialog."""
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class FormOptions(TypedDict, total=False):
|
|
235
|
+
"""Layout options for the built-in add/edit dialog (`DataTable(form=...)`)."""
|
|
236
|
+
|
|
237
|
+
col_count: int
|
|
238
|
+
"""Number of columns the form fields are laid out in. Default `2`."""
|
|
239
|
+
min_col_width: int
|
|
240
|
+
"""Minimum width in pixels of each form column. Default `260`."""
|
|
241
|
+
scrollable: bool
|
|
242
|
+
"""Scroll the form when it is taller than the dialog. Default `True`."""
|
|
243
|
+
resizable: bool
|
|
244
|
+
"""Allow the dialog to be resized. Default `True`."""
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
# ---------------------------------------------------------------------------
|
|
248
|
+
# Selection options (the shared "data bag" option shape)
|
|
249
|
+
# ---------------------------------------------------------------------------
|
|
250
|
+
|
|
251
|
+
class OptionDict(TypedDict, total=False):
|
|
252
|
+
"""A single choice for a selection widget, in dict form.
|
|
253
|
+
|
|
254
|
+
The dict form is a *data bag*: `text` is required (and `value` defaults to
|
|
255
|
+
it) UNLESS the dict carries an `icon` and a `value`, in which case `text` may
|
|
256
|
+
be omitted to request an icon-only option. `text`/`value` and the recognized
|
|
257
|
+
`icon`/`disabled` keys act on rendering; every *other* key rides along as
|
|
258
|
+
carried data — retrievable as the widget's `selection` (the selected
|
|
259
|
+
option's full record). Unrecognized keys are accepted, not rejected, so the
|
|
260
|
+
dict route trades key-typo safety for extensibility.
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
text: str
|
|
264
|
+
"""Display label. Required unless `icon` + `value` are given (icon-only)."""
|
|
265
|
+
value: Any
|
|
266
|
+
"""Stored and emitted value. Defaults to `text` when omitted."""
|
|
267
|
+
icon: str
|
|
268
|
+
"""Optional icon spec rendered beside the label — alone when `text` is blank."""
|
|
269
|
+
disabled: bool
|
|
270
|
+
"""When `True`, the option is dimmed and cannot be selected by the user."""
|
|
271
|
+
localize: "bool | Literal['auto']"
|
|
272
|
+
"""Per-option translation mode — `True`, `False`, or `'auto'`. Overrides the
|
|
273
|
+
widget's group-level `localize=` for just this option. Set `False` to keep a
|
|
274
|
+
proper noun or identifier untranslated."""
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
Option = str | tuple[str, Any] | OptionDict
|
|
278
|
+
"""A single choice for `Select`, `SelectButton`, `RadioGroup`, or `ToggleGroup`.
|
|
279
|
+
|
|
280
|
+
Three interchangeable forms, all normalized to a `(text, value)` pair:
|
|
281
|
+
|
|
282
|
+
- a plain `str` — text and value are the same (`'Small'`);
|
|
283
|
+
- a `(text, value)` tuple — decoupled label and value (`('Small', 's')`);
|
|
284
|
+
- an `OptionDict` — `{'text': 'Small', 'value': 's'}`; the extensible form."""
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class IconSpec(TypedDict, total=False):
|
|
288
|
+
"""A detailed icon specification: a Bootstrap icon name with size and color.
|
|
289
|
+
|
|
290
|
+
Use in place of a plain icon name wherever a widget accepts `icon=`, to
|
|
291
|
+
control the rendered size and color — for example
|
|
292
|
+
`icon={'name': 'bell-fill', 'size': 24, 'color': 'primary'}`.
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
name: str
|
|
296
|
+
"""Bootstrap Icons name, e.g. `'bell-fill'`."""
|
|
297
|
+
size: int
|
|
298
|
+
"""Icon size in pixels (automatically DPI-scaled)."""
|
|
299
|
+
color: str
|
|
300
|
+
"""Icon color — a theme color token (e.g. `'primary'`) or a hex string."""
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
Icon = str | IconSpec
|
|
304
|
+
"""An icon as either a Bootstrap Icons name (`'bell-fill'`) or an `IconSpec`
|
|
305
|
+
mapping for control over size and color."""
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable, Literal
|
|
5
|
+
|
|
6
|
+
from bootstack.widgets._impl.primitives.flexframe import FlexFrame
|
|
7
|
+
from bootstack.widgets._core.container import FlexContainer
|
|
8
|
+
from bootstack.widgets._core.window_controls import WindowControlsMixin
|
|
9
|
+
from bootstack.widgets._core.window_menu import ChromeHostMixin
|
|
10
|
+
from bootstack.widgets.types import (
|
|
11
|
+
Padding, HAlign, VArrange, Anchor, SurfaceToken, WindowStyle,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from bootstack.images import AppIcon, Image
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _resolve_window_parent(parent: Any) -> Any:
|
|
19
|
+
"""Resolve a public widget (or tk widget) to its containing tk window.
|
|
20
|
+
|
|
21
|
+
Accepts an `App`, a `Window`, or any widget inside one, and returns the
|
|
22
|
+
enclosing toplevel — so centering and transient-parenting act on the window,
|
|
23
|
+
not on a small child widget.
|
|
24
|
+
"""
|
|
25
|
+
if parent is None:
|
|
26
|
+
return None
|
|
27
|
+
widget = None
|
|
28
|
+
for attr in ("_tk_root", "_tk_toplevel", "_internal"):
|
|
29
|
+
widget = getattr(parent, attr, None)
|
|
30
|
+
if widget is not None:
|
|
31
|
+
break
|
|
32
|
+
if widget is None:
|
|
33
|
+
widget = parent # assume it is already a tk widget
|
|
34
|
+
try:
|
|
35
|
+
return widget.winfo_toplevel()
|
|
36
|
+
except Exception:
|
|
37
|
+
return widget
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _resolve_anchor_widget(target: Any) -> Any:
|
|
41
|
+
"""Resolve an anchor target to a tk widget (keeping it, not its toplevel).
|
|
42
|
+
|
|
43
|
+
A string (`'screen'`/`'cursor'`/`'parent'`) passes through; a public widget
|
|
44
|
+
resolves to the underlying tk widget so a window can be placed relative to
|
|
45
|
+
that exact widget (e.g. beside a field).
|
|
46
|
+
"""
|
|
47
|
+
if isinstance(target, str) or target is None:
|
|
48
|
+
return target
|
|
49
|
+
for attr in ("_tk_toplevel", "_tk_root", "_internal"):
|
|
50
|
+
widget = getattr(target, attr, None)
|
|
51
|
+
if widget is not None:
|
|
52
|
+
return widget
|
|
53
|
+
return target
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Window(WindowControlsMixin, ChromeHostMixin, FlexContainer):
|
|
57
|
+
"""A secondary top-level window.
|
|
58
|
+
|
|
59
|
+
Behaves as an implicit `Column` from the user's perspective: children
|
|
60
|
+
created inside a `with` block are automatically packed into its content
|
|
61
|
+
frame top-to-bottom. Call `show()` to display the window after building
|
|
62
|
+
its content, or `block_until_closed()` to show it and wait for the user
|
|
63
|
+
to close it.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
title: Window title bar text.
|
|
67
|
+
size: Initial size as `(width, height)`.
|
|
68
|
+
icon: Title-bar and taskbar icon — an icon file path, an `Image` handle,
|
|
69
|
+
or an `AppIcon`. Defaults to the bootstack icon.
|
|
70
|
+
position: Initial position as `(x, y)`.
|
|
71
|
+
min_size: Minimum window size as `(width, height)`.
|
|
72
|
+
max_size: Maximum window size as `(width, height)`.
|
|
73
|
+
resizable: Whether the window can be resized as `(width, height)`.
|
|
74
|
+
modal: Modality level. `False` — non-modal (default). `True` or
|
|
75
|
+
`'window'` — grabs input from the parent only. `'app'` — grabs
|
|
76
|
+
input from all windows.
|
|
77
|
+
parent: The window this one belongs to — an `App`, another `Window`, or
|
|
78
|
+
any widget inside one. Makes this window transient to it (stacks
|
|
79
|
+
above it, hides with it) and is the target for `center_on_parent`.
|
|
80
|
+
Defaults to the main application window.
|
|
81
|
+
center_on_parent: Center over `parent` (the main window if `parent` is
|
|
82
|
+
not given).
|
|
83
|
+
center_on_screen: Center on the screen.
|
|
84
|
+
topmost: Keep the window above other windows.
|
|
85
|
+
undecorated: Remove OS window decorations (`overrideredirect`).
|
|
86
|
+
Ignored on macOS. Unless `window_controls=False`, the window gets a
|
|
87
|
+
built-in draggable title bar with min/max/close so it stays movable
|
|
88
|
+
and closeable; add your own with `add_toolbar(show_window_controls=True)`
|
|
89
|
+
to take over the chrome.
|
|
90
|
+
window_controls: When `undecorated`, provide a built-in title bar with
|
|
91
|
+
window controls and dragging if you add no chrome toolbar of your
|
|
92
|
+
own. Set `False` for a truly chromeless window (e.g. a splash or
|
|
93
|
+
popover). Default `True`. No effect on a decorated window.
|
|
94
|
+
window_style: Windows-only window effect. None inherits the app's setting.
|
|
95
|
+
on_close: Callback invoked when the user clicks the close button.
|
|
96
|
+
Return `False` to veto; return `None` or `True` to allow.
|
|
97
|
+
padding: Inner padding for the content frame.
|
|
98
|
+
gap: Spacing between children.
|
|
99
|
+
horizontal_items: Horizontal alignment of children — `'left'`,
|
|
100
|
+
`'center'`, `'right'`, or `'stretch'` (fill the width). Default
|
|
101
|
+
`'center'`.
|
|
102
|
+
vertical_items: How children are arranged top to bottom — `'top'`,
|
|
103
|
+
`'center'`, `'bottom'`, or a `'space-*'` mode. Default `'top'`.
|
|
104
|
+
grow_items: When `True`, children grow equally to fill the height.
|
|
105
|
+
surface: Surface token for the content frame background.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
_auto_place = False # top-level window — no parent manages its position
|
|
109
|
+
|
|
110
|
+
def __init__(
|
|
111
|
+
self,
|
|
112
|
+
*,
|
|
113
|
+
title: str = "",
|
|
114
|
+
size: tuple[int, int] | None = None,
|
|
115
|
+
icon: "str | Image | AppIcon | None" = None,
|
|
116
|
+
position: tuple[int, int] | None = None,
|
|
117
|
+
min_size: tuple[int, int] | None = None,
|
|
118
|
+
max_size: tuple[int, int] | None = None,
|
|
119
|
+
resizable: tuple[bool, bool] | None = None,
|
|
120
|
+
modal: Literal[False, True, "window", "app"] = False,
|
|
121
|
+
parent: Any = None,
|
|
122
|
+
center_on_parent: bool = False,
|
|
123
|
+
center_on_screen: bool = False,
|
|
124
|
+
topmost: bool = False,
|
|
125
|
+
undecorated: bool = False,
|
|
126
|
+
window_controls: bool = True,
|
|
127
|
+
window_style: WindowStyle | str | None = None,
|
|
128
|
+
on_close: Callable[[], bool | None] | None = None,
|
|
129
|
+
# Content frame layout
|
|
130
|
+
padding: Padding | None = None,
|
|
131
|
+
gap: int = 0,
|
|
132
|
+
horizontal_items: HAlign = "center",
|
|
133
|
+
vertical_items: VArrange = "top",
|
|
134
|
+
grow_items: bool = False,
|
|
135
|
+
surface: SurfaceToken | str | None = None,
|
|
136
|
+
**kwargs: Any,
|
|
137
|
+
) -> None:
|
|
138
|
+
from bootstack._runtime.toplevel import Toplevel as _InternalToplevel
|
|
139
|
+
|
|
140
|
+
self._parent = None
|
|
141
|
+
# overrideredirect has no effect on macOS — so the borderless treatment
|
|
142
|
+
# (custom border + title bar) is Windows/Linux only.
|
|
143
|
+
self._undecorated = undecorated and sys.platform != "darwin"
|
|
144
|
+
self._window_controls = window_controls
|
|
145
|
+
|
|
146
|
+
init_kwargs: dict[str, Any] = {
|
|
147
|
+
"title": title,
|
|
148
|
+
"modal": modal,
|
|
149
|
+
"center_on_parent": center_on_parent,
|
|
150
|
+
"center_on_screen": center_on_screen,
|
|
151
|
+
"topmost": topmost,
|
|
152
|
+
"overrideredirect": undecorated,
|
|
153
|
+
}
|
|
154
|
+
if size is not None:
|
|
155
|
+
init_kwargs["size"] = size
|
|
156
|
+
if position is not None:
|
|
157
|
+
init_kwargs["position"] = position
|
|
158
|
+
if min_size is not None:
|
|
159
|
+
init_kwargs["minsize"] = min_size
|
|
160
|
+
if max_size is not None:
|
|
161
|
+
init_kwargs["maxsize"] = max_size
|
|
162
|
+
if resizable is not None:
|
|
163
|
+
init_kwargs["resizable"] = resizable
|
|
164
|
+
if on_close is not None:
|
|
165
|
+
init_kwargs["on_close"] = on_close
|
|
166
|
+
if window_style is not None:
|
|
167
|
+
init_kwargs["window_style"] = window_style
|
|
168
|
+
|
|
169
|
+
parent_widget = _resolve_window_parent(parent)
|
|
170
|
+
self._parent_window = parent_widget
|
|
171
|
+
if parent_widget is not None:
|
|
172
|
+
init_kwargs["transient"] = parent_widget
|
|
173
|
+
|
|
174
|
+
from bootstack.widgets._core.image_binding import resolve_window_icon
|
|
175
|
+
|
|
176
|
+
icon_path, icon_image = resolve_window_icon(icon)
|
|
177
|
+
if icon_path is not None:
|
|
178
|
+
init_kwargs["icon"] = icon_path
|
|
179
|
+
init_kwargs.update(kwargs)
|
|
180
|
+
|
|
181
|
+
self._tk_toplevel = _InternalToplevel(**init_kwargs)
|
|
182
|
+
|
|
183
|
+
# An Image handle needs the window to exist before it can be rendered.
|
|
184
|
+
self._window_icon_photo = None
|
|
185
|
+
if icon_image is not None:
|
|
186
|
+
self._window_icon_photo = icon_image._materialize()
|
|
187
|
+
self._tk_toplevel._setup_icon(self._window_icon_photo)
|
|
188
|
+
|
|
189
|
+
frame_kwargs: dict[str, Any] = {
|
|
190
|
+
"direction": "vertical",
|
|
191
|
+
"gap": gap,
|
|
192
|
+
"horizontal_items": horizontal_items,
|
|
193
|
+
"vertical_items": vertical_items,
|
|
194
|
+
"grow_items": grow_items,
|
|
195
|
+
}
|
|
196
|
+
if padding is not None:
|
|
197
|
+
frame_kwargs["padding"] = padding
|
|
198
|
+
if surface is not None:
|
|
199
|
+
frame_kwargs["surface"] = surface
|
|
200
|
+
|
|
201
|
+
# In undecorated mode the OS border is gone; a 1px themed border frame
|
|
202
|
+
# substitutes it and hosts both the chrome stack and the content. A
|
|
203
|
+
# chromeless window (`window_controls=False`, e.g. a splash) keeps no
|
|
204
|
+
# border either.
|
|
205
|
+
if self._undecorated and self._window_controls:
|
|
206
|
+
from bootstack.widgets._impl.primitives.frame import Frame
|
|
207
|
+
|
|
208
|
+
self._region_root = Frame(self._tk_toplevel, show_border=True, padding=1)
|
|
209
|
+
self._region_root.pack(fill="both", expand=True)
|
|
210
|
+
else:
|
|
211
|
+
self._region_root = self._tk_toplevel
|
|
212
|
+
|
|
213
|
+
self._content_frame = FlexFrame(self._region_root, **frame_kwargs)
|
|
214
|
+
self._content_frame.pack(fill="both", expand=True)
|
|
215
|
+
|
|
216
|
+
self._internal = self._tk_toplevel
|
|
217
|
+
|
|
218
|
+
def _toolbar_stack_parent(self) -> Any:
|
|
219
|
+
# The chrome stack lives inside the (bordered) region root, above content.
|
|
220
|
+
return getattr(self, "_region_root", self._tk_toplevel)
|
|
221
|
+
|
|
222
|
+
@property
|
|
223
|
+
def _flex_frame(self) -> Any:
|
|
224
|
+
return self._content_frame
|
|
225
|
+
|
|
226
|
+
# ----- Lifecycle -----
|
|
227
|
+
|
|
228
|
+
def show(
|
|
229
|
+
self,
|
|
230
|
+
*,
|
|
231
|
+
anchor_to: Any = None,
|
|
232
|
+
anchor_point: Anchor = "center",
|
|
233
|
+
window_point: Anchor = "center",
|
|
234
|
+
offset: tuple[int, int] = (0, 0),
|
|
235
|
+
auto_flip: bool = True,
|
|
236
|
+
) -> "Window":
|
|
237
|
+
"""Show the window, optionally positioned relative to a widget.
|
|
238
|
+
|
|
239
|
+
With no arguments the window appears at its configured position (or
|
|
240
|
+
centered on its `parent`). Pass `anchor_to` to place it relative to a
|
|
241
|
+
widget instead — for example, to the right of a field.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
anchor_to: A widget to position this window against, or one of
|
|
245
|
+
`'screen'`, `'cursor'`, or `'parent'`. When given, this overrides
|
|
246
|
+
centering.
|
|
247
|
+
anchor_point: The point on the anchor target to align to — one of
|
|
248
|
+
`'n'`, `'s'`, `'e'`, `'w'`, `'ne'`, `'nw'`, `'se'`, `'sw'`, or
|
|
249
|
+
`'center'`. For example, `'e'` is the target's right edge.
|
|
250
|
+
window_point: The matching point on this window placed at the anchor
|
|
251
|
+
(same value set). For example, `anchor_point='e'` with
|
|
252
|
+
`window_point='w'` puts this window's left edge at the target's
|
|
253
|
+
right edge — i.e. to its right.
|
|
254
|
+
offset: Extra `(x, y)` pixel offset from the anchored position.
|
|
255
|
+
auto_flip: Flip to the opposite side if the window would otherwise go
|
|
256
|
+
off-screen. Defaults to `True`.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
`self` — allows chaining: `win = Window(...).show()`.
|
|
260
|
+
"""
|
|
261
|
+
self._ensure_default_titlebar()
|
|
262
|
+
if anchor_to is not None:
|
|
263
|
+
from bootstack._runtime.window_utilities import WindowPositioning
|
|
264
|
+
|
|
265
|
+
toplevel = self._tk_toplevel
|
|
266
|
+
toplevel.update_idletasks()
|
|
267
|
+
WindowPositioning.position_anchored(
|
|
268
|
+
window=toplevel,
|
|
269
|
+
anchor_to=_resolve_anchor_widget(anchor_to),
|
|
270
|
+
parent=self._parent_window,
|
|
271
|
+
anchor_point=anchor_point,
|
|
272
|
+
window_point=window_point,
|
|
273
|
+
offset=offset,
|
|
274
|
+
auto_flip=auto_flip,
|
|
275
|
+
ensure_visible=True,
|
|
276
|
+
)
|
|
277
|
+
self._tk_toplevel.show()
|
|
278
|
+
return self
|
|
279
|
+
|
|
280
|
+
def block_until_closed(self) -> Any:
|
|
281
|
+
"""Show the window and block until it is destroyed.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
The value of `result` at the time the window was closed.
|
|
285
|
+
"""
|
|
286
|
+
self._ensure_default_titlebar()
|
|
287
|
+
return self._tk_toplevel.block_until_closed()
|
|
288
|
+
|
|
289
|
+
# ----- Properties -----
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def title(self) -> str:
|
|
293
|
+
"""The window's title bar text. Assigning to it updates the title live."""
|
|
294
|
+
return self._tk_toplevel.title()
|
|
295
|
+
|
|
296
|
+
@title.setter
|
|
297
|
+
def title(self, value: str) -> None:
|
|
298
|
+
self._tk_toplevel.title(value)
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def result(self) -> Any:
|
|
302
|
+
"""Value set before closing, returned by `block_until_closed()`."""
|
|
303
|
+
return self._tk_toplevel.result
|
|
304
|
+
|
|
305
|
+
@result.setter
|
|
306
|
+
def result(self, value: Any) -> None:
|
|
307
|
+
self._tk_toplevel.result = value
|