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,539 @@
|
|
|
1
|
+
"""Typed event payloads — the catalog of every data-carrying framework event.
|
|
2
|
+
|
|
3
|
+
When a widget event carries application data (a new value, the typed text, a
|
|
4
|
+
validation result, a selected row), the handler bound with `on_*()` receives
|
|
5
|
+
one of the frozen dataclasses defined here, *unpacked* — the handler argument
|
|
6
|
+
IS the payload::
|
|
7
|
+
|
|
8
|
+
field.on_change(lambda e: print(e.value, e.prev_value))
|
|
9
|
+
field.on_input(lambda e: print(e.text))
|
|
10
|
+
|
|
11
|
+
Events that carry no payload (click, hover, focus, resize, key, scroll) instead
|
|
12
|
+
hand the handler a curated `Event`.
|
|
13
|
+
|
|
14
|
+
Every payload is `@dataclass(frozen=True, slots=True)` so attributes are
|
|
15
|
+
discoverable by editors and immutable in handlers.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from datetime import date as _date
|
|
22
|
+
from typing import Any, Literal, Mapping
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# Field / input family
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True, slots=True)
|
|
30
|
+
class InputEvent:
|
|
31
|
+
"""Fires on every keystroke, before the value is committed.
|
|
32
|
+
|
|
33
|
+
Use it for live feedback — character counts, as-you-type filtering. To react
|
|
34
|
+
only once editing settles, use `on_change` instead.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
text: str = ""
|
|
38
|
+
"""The current raw text in the field."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True, slots=True)
|
|
42
|
+
class ChangeEvent:
|
|
43
|
+
"""Fires when a field's value is committed (on blur or Enter)."""
|
|
44
|
+
|
|
45
|
+
value: Any = None
|
|
46
|
+
"""The committed, parsed value."""
|
|
47
|
+
|
|
48
|
+
prev_value: Any = None
|
|
49
|
+
"""The value before this change."""
|
|
50
|
+
|
|
51
|
+
text: str = ""
|
|
52
|
+
"""The raw display text behind the value."""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True, slots=True)
|
|
56
|
+
class ValidationEvent:
|
|
57
|
+
"""Fires after validation runs — `valid`, `invalid`, and `validate`."""
|
|
58
|
+
|
|
59
|
+
value: Any = None
|
|
60
|
+
"""The value that was validated."""
|
|
61
|
+
|
|
62
|
+
is_valid: bool = True
|
|
63
|
+
"""Whether validation passed."""
|
|
64
|
+
|
|
65
|
+
message: str = ""
|
|
66
|
+
"""The failure message, or an empty string when valid."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
# Sliders & meters
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass(frozen=True, slots=True)
|
|
75
|
+
class SliderEvent:
|
|
76
|
+
"""Fires while a slider (or gauge/meter) value moves."""
|
|
77
|
+
|
|
78
|
+
value: float = 0.0
|
|
79
|
+
"""The new value."""
|
|
80
|
+
|
|
81
|
+
prev_value: float = 0.0
|
|
82
|
+
"""The value before this move."""
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass(frozen=True, slots=True)
|
|
86
|
+
class SliderCommitEvent:
|
|
87
|
+
"""Fires when a slider value is committed (drag released or stepped)."""
|
|
88
|
+
|
|
89
|
+
value: float = 0.0
|
|
90
|
+
"""The committed value."""
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass(frozen=True, slots=True)
|
|
94
|
+
class RangeSliderEvent:
|
|
95
|
+
"""Fires while either handle of a range slider moves."""
|
|
96
|
+
|
|
97
|
+
low_value: float = 0.0
|
|
98
|
+
"""The new lower-bound value."""
|
|
99
|
+
|
|
100
|
+
high_value: float = 0.0
|
|
101
|
+
"""The new upper-bound value."""
|
|
102
|
+
|
|
103
|
+
prev_low_value: float = 0.0
|
|
104
|
+
"""The lower-bound value before this move."""
|
|
105
|
+
|
|
106
|
+
prev_high_value: float = 0.0
|
|
107
|
+
"""The upper-bound value before this move."""
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass(frozen=True, slots=True)
|
|
111
|
+
class RangeSliderCommitEvent:
|
|
112
|
+
"""Fires when a range slider's values are committed."""
|
|
113
|
+
|
|
114
|
+
low_value: float = 0.0
|
|
115
|
+
"""The committed lower-bound value."""
|
|
116
|
+
|
|
117
|
+
high_value: float = 0.0
|
|
118
|
+
"""The committed upper-bound value."""
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
# Calendar
|
|
123
|
+
# ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@dataclass(frozen=True, slots=True)
|
|
127
|
+
class DateSelectEvent:
|
|
128
|
+
"""Fires when a date (or date range) is chosen in a calendar."""
|
|
129
|
+
|
|
130
|
+
date: "_date | None" = None
|
|
131
|
+
"""The selected date."""
|
|
132
|
+
|
|
133
|
+
range: "tuple[_date, _date] | None" = None
|
|
134
|
+
"""The selected `(start, end)` range, or `None` in single-date mode."""
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# ---------------------------------------------------------------------------
|
|
138
|
+
# Expander / Accordion
|
|
139
|
+
# ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclass(frozen=True, slots=True)
|
|
143
|
+
class ToggleEvent:
|
|
144
|
+
"""Fires when an expander is expanded or collapsed."""
|
|
145
|
+
|
|
146
|
+
expanded: bool = False
|
|
147
|
+
"""Whether the expander is now open."""
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass(frozen=True, slots=True)
|
|
151
|
+
class AccordionChangeEvent:
|
|
152
|
+
"""Fires when an accordion's set of open panels changes."""
|
|
153
|
+
|
|
154
|
+
expanded: tuple[Any, ...] = ()
|
|
155
|
+
"""The currently expanded panels."""
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# ---------------------------------------------------------------------------
|
|
159
|
+
# Picture / media
|
|
160
|
+
# ---------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@dataclass(frozen=True, slots=True)
|
|
164
|
+
class ImageLoadEvent:
|
|
165
|
+
"""Fires when a `Picture` has decoded its source and displayed it."""
|
|
166
|
+
|
|
167
|
+
width: int = 0
|
|
168
|
+
"""The natural pixel width of the loaded image."""
|
|
169
|
+
|
|
170
|
+
height: int = 0
|
|
171
|
+
"""The natural pixel height of the loaded image."""
|
|
172
|
+
|
|
173
|
+
frames: int = 1
|
|
174
|
+
"""The number of frames — greater than one for an animated source."""
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@dataclass(frozen=True, slots=True)
|
|
178
|
+
class ImageErrorEvent:
|
|
179
|
+
"""Fires when a `Picture` fails to load or decode its source."""
|
|
180
|
+
|
|
181
|
+
message: str = ""
|
|
182
|
+
"""A human-readable description of the failure."""
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# ---------------------------------------------------------------------------
|
|
186
|
+
# Navigation — PageStack, SideNav
|
|
187
|
+
# ---------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@dataclass(frozen=True, slots=True)
|
|
191
|
+
class PageChangeEvent:
|
|
192
|
+
"""Fires when the active page changes (mount, will-mount, change)."""
|
|
193
|
+
|
|
194
|
+
page: str = ""
|
|
195
|
+
"""The key of the page now active."""
|
|
196
|
+
|
|
197
|
+
prev_page: str | None = None
|
|
198
|
+
"""The key of the previously active page, or `None`."""
|
|
199
|
+
|
|
200
|
+
nav: Any = None
|
|
201
|
+
"""Navigation direction/context for this transition, if any."""
|
|
202
|
+
|
|
203
|
+
data: dict[str, Any] = field(default_factory=dict)
|
|
204
|
+
"""Arbitrary data passed to `navigate(key, data=...)`."""
|
|
205
|
+
|
|
206
|
+
prev_data: Any = None
|
|
207
|
+
"""The data the previous page was navigated with."""
|
|
208
|
+
|
|
209
|
+
index: int = 0
|
|
210
|
+
"""Position of this page in the navigation history."""
|
|
211
|
+
|
|
212
|
+
length: int = 0
|
|
213
|
+
"""Total length of the navigation history."""
|
|
214
|
+
|
|
215
|
+
can_back: bool = False
|
|
216
|
+
"""Whether there is a previous page to go back to."""
|
|
217
|
+
|
|
218
|
+
can_forward: bool = False
|
|
219
|
+
"""Whether there is a next page to go forward to."""
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@dataclass(frozen=True, slots=True)
|
|
223
|
+
class NavEvent:
|
|
224
|
+
"""Fires when a navigation item is selected."""
|
|
225
|
+
|
|
226
|
+
key: str = ""
|
|
227
|
+
"""The key of the selected item."""
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@dataclass(frozen=True, slots=True)
|
|
231
|
+
class PaneToggleEvent:
|
|
232
|
+
"""Fires when a navigation pane opens or closes."""
|
|
233
|
+
|
|
234
|
+
is_open: bool = False
|
|
235
|
+
"""Whether the pane is now open."""
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@dataclass(frozen=True, slots=True)
|
|
239
|
+
class DisplayModeEvent:
|
|
240
|
+
"""Fires when a navigation widget's display mode changes."""
|
|
241
|
+
|
|
242
|
+
mode: Any = None
|
|
243
|
+
"""The new display mode."""
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@dataclass(frozen=True, slots=True)
|
|
247
|
+
class WorkspaceChangeEvent:
|
|
248
|
+
"""Fires when the shell's active workspace changes (a rail switch)."""
|
|
249
|
+
|
|
250
|
+
workspace: str = ""
|
|
251
|
+
"""The key of the workspace now active."""
|
|
252
|
+
|
|
253
|
+
prev_workspace: str | None = None
|
|
254
|
+
"""The key of the previously active workspace, or `None`."""
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
# ---------------------------------------------------------------------------
|
|
258
|
+
# Tabs
|
|
259
|
+
# ---------------------------------------------------------------------------
|
|
260
|
+
|
|
261
|
+
ChangeReason = Literal["user", "api", "hide", "forget", "unknown"]
|
|
262
|
+
"""Why a change event fired — a direct `user` edit, an `api` call, a `hide` or
|
|
263
|
+
`forget` of carried data, or `unknown`."""
|
|
264
|
+
|
|
265
|
+
ChangeMethod = Literal["click", "key", "programmatic", "unknown"]
|
|
266
|
+
"""How a change was made — by mouse `click`, by `key`, `programmatic`ally, or
|
|
267
|
+
`unknown`."""
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@dataclass(frozen=True, slots=True)
|
|
271
|
+
class TabRef:
|
|
272
|
+
"""Identifies a tab by key and display text."""
|
|
273
|
+
|
|
274
|
+
key: str = ""
|
|
275
|
+
"""The tab's stable key."""
|
|
276
|
+
|
|
277
|
+
text: str = ""
|
|
278
|
+
"""The tab's display text."""
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@dataclass(frozen=True, slots=True)
|
|
282
|
+
class TabChangeEvent:
|
|
283
|
+
"""Fires when the active tab changes."""
|
|
284
|
+
|
|
285
|
+
current: TabRef | None = None
|
|
286
|
+
"""The tab now active."""
|
|
287
|
+
|
|
288
|
+
previous: TabRef | None = None
|
|
289
|
+
"""The tab that was active before, or `None`."""
|
|
290
|
+
|
|
291
|
+
reason: ChangeReason = "unknown"
|
|
292
|
+
"""Why the change happened."""
|
|
293
|
+
|
|
294
|
+
via: ChangeMethod = "unknown"
|
|
295
|
+
"""How the change was triggered."""
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@dataclass(frozen=True, slots=True)
|
|
299
|
+
class TabActivateEvent:
|
|
300
|
+
"""Fires when a tab becomes active."""
|
|
301
|
+
|
|
302
|
+
key: str = ""
|
|
303
|
+
"""The activated tab's key."""
|
|
304
|
+
|
|
305
|
+
text: str = ""
|
|
306
|
+
"""The activated tab's display text."""
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
@dataclass(frozen=True, slots=True)
|
|
310
|
+
class TabDeactivateEvent:
|
|
311
|
+
"""Fires when a tab stops being active."""
|
|
312
|
+
|
|
313
|
+
key: str = ""
|
|
314
|
+
"""The deactivated tab's key."""
|
|
315
|
+
|
|
316
|
+
text: str = ""
|
|
317
|
+
"""The deactivated tab's display text."""
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
@dataclass(frozen=True, slots=True)
|
|
321
|
+
class TabCloseEvent:
|
|
322
|
+
"""Fires when a tab's close button is used."""
|
|
323
|
+
|
|
324
|
+
value: Any = None
|
|
325
|
+
"""The closed tab's value/key."""
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
# ---------------------------------------------------------------------------
|
|
329
|
+
# Table — row-oriented
|
|
330
|
+
# ---------------------------------------------------------------------------
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
@dataclass(frozen=True, slots=True)
|
|
334
|
+
class RowEvent:
|
|
335
|
+
"""Fires for a single-row action (click, double-click, right-click)."""
|
|
336
|
+
|
|
337
|
+
record: dict[str, Any] = field(default_factory=dict)
|
|
338
|
+
"""The row's record."""
|
|
339
|
+
|
|
340
|
+
id: Any = None
|
|
341
|
+
"""The row's id."""
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@dataclass(frozen=True, slots=True)
|
|
345
|
+
class RowsEvent:
|
|
346
|
+
"""Fires for a multi-row action (insert, update, delete, move)."""
|
|
347
|
+
|
|
348
|
+
records: list[dict[str, Any]] = field(default_factory=list)
|
|
349
|
+
"""The affected records."""
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@dataclass(frozen=True, slots=True)
|
|
353
|
+
class SelectionEvent:
|
|
354
|
+
"""Fires when the set of selected rows changes."""
|
|
355
|
+
|
|
356
|
+
records: list[dict[str, Any]] = field(default_factory=list)
|
|
357
|
+
"""The selected records."""
|
|
358
|
+
|
|
359
|
+
ids: list[Any] = field(default_factory=list)
|
|
360
|
+
"""The selected rows' ids."""
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
@dataclass(frozen=True, slots=True)
|
|
364
|
+
class ExportEvent:
|
|
365
|
+
"""Fires after the table's data is exported (copied or saved)."""
|
|
366
|
+
|
|
367
|
+
count: int = 0
|
|
368
|
+
"""The number of rows exported."""
|
|
369
|
+
|
|
370
|
+
target: str = ""
|
|
371
|
+
"""Where the data went — `'clipboard'` or `'file'`."""
|
|
372
|
+
|
|
373
|
+
format: str = ""
|
|
374
|
+
"""The format written — `'csv'`, `'tsv'`, or `'xlsx'`."""
|
|
375
|
+
|
|
376
|
+
path: str | None = None
|
|
377
|
+
"""The destination file path when `target` is `'file'`, else `None`."""
|
|
378
|
+
|
|
379
|
+
records: list[dict[str, Any]] = field(default_factory=list)
|
|
380
|
+
"""The exported records. Populated for in-memory targets (clipboard);
|
|
381
|
+
empty for streamed file exports, which never materialize all rows."""
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
# ---------------------------------------------------------------------------
|
|
385
|
+
# Tree — node-oriented
|
|
386
|
+
# ---------------------------------------------------------------------------
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
@dataclass(frozen=True, slots=True)
|
|
390
|
+
class TreeSelectionEvent:
|
|
391
|
+
"""Fires when a tree's set of selected nodes changes."""
|
|
392
|
+
|
|
393
|
+
nodes: list[Any] = field(default_factory=list)
|
|
394
|
+
"""The selected `TreeNode` handles, in tree order."""
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
# ---------------------------------------------------------------------------
|
|
398
|
+
# TextArea / CodeEditor
|
|
399
|
+
# ---------------------------------------------------------------------------
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
@dataclass(frozen=True, slots=True)
|
|
403
|
+
class TextModifiedEvent:
|
|
404
|
+
"""Fires when a text area's modified (dirty) flag changes."""
|
|
405
|
+
|
|
406
|
+
is_dirty: bool = False
|
|
407
|
+
"""Whether the text differs from the last saved/baseline content."""
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
# ---------------------------------------------------------------------------
|
|
411
|
+
# ButtonGroup
|
|
412
|
+
# ---------------------------------------------------------------------------
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
@dataclass(frozen=True, slots=True)
|
|
416
|
+
class ButtonGroupClickEvent:
|
|
417
|
+
"""Fires when a button in a button group is clicked."""
|
|
418
|
+
|
|
419
|
+
key: str = ""
|
|
420
|
+
"""The clicked button's key."""
|
|
421
|
+
|
|
422
|
+
text: str = ""
|
|
423
|
+
"""The clicked button's label text."""
|
|
424
|
+
|
|
425
|
+
icon: Any = None
|
|
426
|
+
"""The clicked button's icon, if any."""
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
@dataclass(frozen=True, slots=True)
|
|
430
|
+
class MenuSelectEvent:
|
|
431
|
+
"""Fires when a menu item is activated (`MenuButton` / `ContextMenu`)."""
|
|
432
|
+
|
|
433
|
+
type: str = ""
|
|
434
|
+
"""The activated item's type — `'command'`, `'check'`, or `'radio'`."""
|
|
435
|
+
|
|
436
|
+
text: str = ""
|
|
437
|
+
"""The item's label text."""
|
|
438
|
+
|
|
439
|
+
value: Any = None
|
|
440
|
+
"""The item's value (a radio/check value, or `None` for a command)."""
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
# ---------------------------------------------------------------------------
|
|
444
|
+
# Data source — change broadcasting
|
|
445
|
+
# ---------------------------------------------------------------------------
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
ChangeKind = Literal[
|
|
449
|
+
"load",
|
|
450
|
+
"insert",
|
|
451
|
+
"update",
|
|
452
|
+
"delete",
|
|
453
|
+
"move",
|
|
454
|
+
"filter",
|
|
455
|
+
"sort",
|
|
456
|
+
"reload",
|
|
457
|
+
"select",
|
|
458
|
+
]
|
|
459
|
+
"""What kind of change a `DataChangeEvent` describes.
|
|
460
|
+
|
|
461
|
+
A row mutation (`insert`/`update`/`delete`/`move`), a view change
|
|
462
|
+
(`filter`/`sort`), a bulk replace (`load`/`reload`), or a selection toggle
|
|
463
|
+
(`select`).
|
|
464
|
+
"""
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
@dataclass(frozen=True, slots=True)
|
|
468
|
+
class DataChangeEvent:
|
|
469
|
+
"""Broadcast by a data source when its data or view changes.
|
|
470
|
+
|
|
471
|
+
Delivered to handlers registered with `DataSource.on_change`. Because the
|
|
472
|
+
source coalesces rapid mutations into one notification per event-loop turn,
|
|
473
|
+
a single event may stand in for several underlying changes; in that case
|
|
474
|
+
`kind` is `reload` and the per-row fields are empty. Treat it as a coarse
|
|
475
|
+
"something changed" signal and re-read the source for current data.
|
|
476
|
+
"""
|
|
477
|
+
|
|
478
|
+
kind: ChangeKind = "reload"
|
|
479
|
+
"""What changed."""
|
|
480
|
+
|
|
481
|
+
id: Any = None
|
|
482
|
+
"""Affected record id for single-row kinds; `None` otherwise."""
|
|
483
|
+
|
|
484
|
+
ids: tuple[Any, ...] = ()
|
|
485
|
+
"""Affected record ids for bulk kinds (e.g. `select_all`); empty otherwise."""
|
|
486
|
+
|
|
487
|
+
record: Mapping[str, Any] | None = None
|
|
488
|
+
"""The affected record (or the applied updates) where cheaply available on
|
|
489
|
+
`insert`/`update`; `None` otherwise."""
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
# ---------------------------------------------------------------------------
|
|
493
|
+
# SplitView
|
|
494
|
+
# ---------------------------------------------------------------------------
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
@dataclass(frozen=True, slots=True)
|
|
498
|
+
class SashMoveEvent:
|
|
499
|
+
"""Fires when a `SplitView` sash is released after being dragged."""
|
|
500
|
+
|
|
501
|
+
index: int = 0
|
|
502
|
+
"""Zero-based index of the sash that moved."""
|
|
503
|
+
|
|
504
|
+
position: int = 0
|
|
505
|
+
"""New pixel position of the sash in pixels from the leading edge."""
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
# ---------------------------------------------------------------------------
|
|
509
|
+
# ScrollView
|
|
510
|
+
# ---------------------------------------------------------------------------
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
@dataclass(frozen=True, slots=True)
|
|
514
|
+
class ScrollEvent:
|
|
515
|
+
"""Fires when a `ScrollView`'s viewport position changes."""
|
|
516
|
+
|
|
517
|
+
y: float = 0.0
|
|
518
|
+
"""Vertical scroll position — 0.0 (top) to 1.0 (bottom)."""
|
|
519
|
+
|
|
520
|
+
x: float = 0.0
|
|
521
|
+
"""Horizontal scroll position — 0.0 (left) to 1.0 (right)."""
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
# ---------------------------------------------------------------------------
|
|
525
|
+
# Splash
|
|
526
|
+
# ---------------------------------------------------------------------------
|
|
527
|
+
|
|
528
|
+
SplashDismissReason = Literal["ready", "timer", "manual", "skip"]
|
|
529
|
+
"""Why a splash screen dismissed — `'ready'` (the app finished building),
|
|
530
|
+
`'timer'` (its timed duration elapsed), `'manual'` (an explicit `dismiss()`),
|
|
531
|
+
or `'skip'` (the user clicked or pressed Escape)."""
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
@dataclass(frozen=True, slots=True)
|
|
535
|
+
class SplashDismissEvent:
|
|
536
|
+
"""Fires when a splash screen begins dismissing."""
|
|
537
|
+
|
|
538
|
+
reason: SplashDismissReason = "ready"
|
|
539
|
+
"""Why the splash dismissed."""
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import tkinter
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Subscription:
|
|
7
|
+
"""Handle returned by `widget.on(...)`. Cancels the binding when `.cancel()`
|
|
8
|
+
is called or the context manager exits.
|
|
9
|
+
|
|
10
|
+
Idempotent: calling `cancel()` more than once is a no-op.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
__slots__ = ("_widget", "_sequence", "_bind_id", "_cancelled")
|
|
14
|
+
|
|
15
|
+
def __init__(self, widget: tkinter.Misc, sequence: str, bind_id: str) -> None:
|
|
16
|
+
self._widget = widget
|
|
17
|
+
self._sequence = sequence
|
|
18
|
+
self._bind_id = bind_id
|
|
19
|
+
self._cancelled = False
|
|
20
|
+
|
|
21
|
+
def cancel(self) -> None:
|
|
22
|
+
if self._cancelled:
|
|
23
|
+
return
|
|
24
|
+
self._cancelled = True
|
|
25
|
+
try:
|
|
26
|
+
self._widget.unbind(self._sequence, self._bind_id)
|
|
27
|
+
except tkinter.TclError:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
def __enter__(self) -> "Subscription":
|
|
31
|
+
return self
|
|
32
|
+
|
|
33
|
+
def __exit__(self, exc_type, exc, tb) -> None:
|
|
34
|
+
self.cancel()
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def cancelled(self) -> bool:
|
|
38
|
+
return self._cancelled
|
bootstack/i18n/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Internationalization (i18n)
|
|
2
|
+
|
|
3
|
+
bootstack uses a unified message catalog that bridges Python gettext (compiled
|
|
4
|
+
with Babel) and Tcl/Tk msgcat. Catalogs are distributed with the package under
|
|
5
|
+
`bootstack/assets/locales`.
|
|
6
|
+
|
|
7
|
+
Highlights
|
|
8
|
+
- Use `_ = MessageCatalog.translate` in code to mark strings.
|
|
9
|
+
- The framework auto-initializes the catalog and auto-discovers shipped catalogs.
|
|
10
|
+
- Switch language at runtime with `MessageCatalog.locale('de')`.
|
|
11
|
+
- A virtual event `<<LocaleChanged>>` is emitted on language changes so UIs can refresh.
|
|
12
|
+
- Runtime overrides (`set`, `set_many`) let you add translations not yet in catalogs.
|
|
13
|
+
|
|
14
|
+
Directory structure
|
|
15
|
+
- `src/bootstack/assets/locales/<lang>/LC_MESSAGES/bootstack.po`
|
|
16
|
+
- `src/bootstack/assets/locales/<lang>/LC_MESSAGES/bootstack.mo`
|
|
17
|
+
|
|
18
|
+
Marking strings for translation
|
|
19
|
+
- In modules that render UI:
|
|
20
|
+
- `from bootstack.i18n import MessageCatalog`
|
|
21
|
+
- `_ = MessageCatalog.translate`
|
|
22
|
+
- Example: `bs.Label(root, text=_('Cancel'))`
|
|
23
|
+
- Formatting:
|
|
24
|
+
- `_("File: %s", name)` uses Python `%` formatting.
|
|
25
|
+
- Legacy Tcl printf like `%1$s` are supported via a formatting fallback.
|
|
26
|
+
|
|
27
|
+
Language switching
|
|
28
|
+
- Change language at runtime:
|
|
29
|
+
- `MessageCatalog.locale('fr')`
|
|
30
|
+
- Bind a refresh: `root.bind('<<LocaleChanged>>', lambda e: refresh())`
|
|
31
|
+
|
|
32
|
+
Runtime overrides (non-compiled messages)
|
|
33
|
+
- Add translations for a locale during runtime:
|
|
34
|
+
- Single: `MessageCatalog.set('fr', 'Hello', 'Bonjour')`
|
|
35
|
+
- Many: `MessageCatalog.set_many('de', 'Open','Oeffnen', 'Cancel','Abbrechen')`
|
|
36
|
+
- Emit refresh if you're already in that locale: `root.event_generate('<<LocaleChanged>>')`
|
|
37
|
+
|
|
38
|
+
Developer workflow (Babel)
|
|
39
|
+
- Config lives at project root: `babel.cfg` (extracts from `src/**/*.py` and `_()`/gettext keywords).
|
|
40
|
+
- Use the helper to manage catalogs (defaults to `src/bootstack/assets/locales` and domain `bootstack`):
|
|
41
|
+
- Extract template: `python tools/make_i18n.py extract`
|
|
42
|
+
- Init locales: `python tools/make_i18n.py init -l de fr`
|
|
43
|
+
- Update catalogs: `python tools/make_i18n.py update`
|
|
44
|
+
- Compile catalogs: `python tools/make_i18n.py compile`
|
|
45
|
+
- The compiled `.mo` files are shipped in the wheel from `assets/locales`.
|
|
46
|
+
|
|
47
|
+
Import path
|
|
48
|
+
- Public API is available at the top-level namespace:
|
|
49
|
+
- `import bootstack as bs; bs.MessageCatalog, bs.L, bs.LV, bs.IntlFormatter`
|
|
50
|
+
- `from bootstack.i18n import MessageCatalog, L, LV, IntlFormatter`
|
|
51
|
+
|
|
52
|
+
Contribution notes
|
|
53
|
+
- Prefer base locales (`de`, `fr`, `nl`) unless region-specific differences are
|
|
54
|
+
required (for example `pt_BR`).
|
|
55
|
+
- Avoid embedding mnemonics `&` in messages; MessageCatalog strips them when rendering.
|
|
56
|
+
- Keep message ids consistent (case and punctuation) to avoid duplicates.
|
|
57
|
+
|
|
58
|
+
Minimum keys to translate for a new language (baseline UI):
|
|
59
|
+
- OK, Ok, Retry, Delete, Next, Prev, Previous
|
|
60
|
+
- Yes, No, Open, Close, Add, Remove, Submit, Cancel
|
|
61
|
+
- Family, Weight, Slant, Effects, Preview, Size
|
|
62
|
+
- Should be of data type, Invalid data type
|
|
63
|
+
- Number cannot be greater than, Number cannot be less than, Out of range
|
|
64
|
+
- The quick brown fox jumps over the lazy dog.
|
|
65
|
+
- Font Selector, Color Chooser, Advanced, Themed, Standard
|
|
66
|
+
- Current, New, Hue, Sat, Lum, Hex, Red, Green, Blue
|
|
67
|
+
- color dropper, Search, Page, of
|
|
68
|
+
- Reset table, Columns, Move, Align
|
|
69
|
+
- Hide column, Delete column, Show All
|
|
70
|
+
- Move to left, Move to right, Move to first, Move to last
|
|
71
|
+
- Align left, Align center, Align right
|
|
72
|
+
- Sort, Filter, Export, Delete selected rows
|
|
73
|
+
- Sort Ascending, Sort Descending, Clear filters
|
|
74
|
+
- Filter by cell's value, Hide select rows, Show only select rows
|
|
75
|
+
- Export all records, Export current page, Export current selection, Export records in filter
|
|
76
|
+
- Move up, Move down, Move to top, Move to bottom
|
|
77
|
+
- Mo, Tu, We, Th, Fr, Sa, Su
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Localization for bootstack.
|
|
2
|
+
|
|
3
|
+
Public API: `L` (lazy translated text) and `LV` (lazy locale-formatted
|
|
4
|
+
value), used wherever a widget takes text. `MessageCatalog` (the gettext /
|
|
5
|
+
Tcl-msgcat bridge) and `IntlFormatter` (the Babel value formatter) are the
|
|
6
|
+
internal engines behind them — importable for internal use but not part of the
|
|
7
|
+
public API.
|
|
8
|
+
"""
|
|
9
|
+
from .msgcat import MessageCatalog
|
|
10
|
+
from .intl_format import IntlFormatter
|
|
11
|
+
from .specs import L, LV
|
|
12
|
+
from .catalog import add_translation, add_translations, load_po, load_translations
|
|
13
|
+
|
|
14
|
+
# Public surface: L / LV and the catalog-extension helpers. MessageCatalog and
|
|
15
|
+
# IntlFormatter stay importable (internal engines) but are excluded from __all__.
|
|
16
|
+
__all__ = [
|
|
17
|
+
"L",
|
|
18
|
+
"LV",
|
|
19
|
+
"add_translation",
|
|
20
|
+
"add_translations",
|
|
21
|
+
"load_po",
|
|
22
|
+
"load_translations",
|
|
23
|
+
]
|