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,200 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Callable, Mapping, Sequence, overload
|
|
4
|
+
|
|
5
|
+
from bootstack.constants import DEFAULT_MIN_COL_WIDTH
|
|
6
|
+
from bootstack.widgets._impl.composites.form import Form as _InternalForm
|
|
7
|
+
from bootstack.widgets._impl.composites.form import (
|
|
8
|
+
FormItem, FieldItem, GroupItem, TabsItem, TabItem,
|
|
9
|
+
)
|
|
10
|
+
from bootstack.widgets._core.base import PublicWidgetBase
|
|
11
|
+
from bootstack.widgets._core.events import register_widget_events
|
|
12
|
+
from bootstack.streams import Stream
|
|
13
|
+
from bootstack.events import Subscription
|
|
14
|
+
from bootstack.signals import Signal
|
|
15
|
+
from bootstack.widgets.types import AccentToken
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from bootstack.dialogs import DialogButton
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Form(PublicWidgetBase):
|
|
22
|
+
"""Data-entry form built from data or explicit field definitions.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
data: Initial data dict. Keys become field names; types are inferred
|
|
26
|
+
when `items` is not provided.
|
|
27
|
+
items: Explicit form layout as `FieldItem`/`GroupItem`/`TabsItem`
|
|
28
|
+
instances or equivalent dicts.
|
|
29
|
+
col_count: Number of top-level columns. Default `1`.
|
|
30
|
+
min_col_width: Minimum column width in pixels.
|
|
31
|
+
on_data_change: Callback invoked with the updated data dict on each
|
|
32
|
+
field change. Equivalent to calling `on_data_change()` after
|
|
33
|
+
construction.
|
|
34
|
+
width: Fixed form width in pixels.
|
|
35
|
+
height: Fixed form height in pixels.
|
|
36
|
+
accent: Accent token for the form container.
|
|
37
|
+
buttons: Footer buttons — strings, `DialogButton` instances, or dicts.
|
|
38
|
+
parent: Override the context-stack parent.
|
|
39
|
+
**kwargs: Layout placement options applied by the parent container —
|
|
40
|
+
`fill`, `expand`, `anchor`, `margin`, `row`, `column`, `sticky`.
|
|
41
|
+
See :doc:`/tasks/layout`.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
*,
|
|
47
|
+
data: dict[str, Any] | None = None,
|
|
48
|
+
items: Sequence[FormItem | Mapping[str, Any]] | None = None,
|
|
49
|
+
col_count: int = 1,
|
|
50
|
+
min_col_width: int = DEFAULT_MIN_COL_WIDTH,
|
|
51
|
+
on_data_change: Callable[[dict[str, Any]], Any] | None = None,
|
|
52
|
+
width: int | None = None,
|
|
53
|
+
height: int | None = None,
|
|
54
|
+
accent: AccentToken | str | None = None,
|
|
55
|
+
buttons: Sequence[str | DialogButton | dict[str, Any]] | None = None,
|
|
56
|
+
parent: Any = None,
|
|
57
|
+
**kwargs: Any,
|
|
58
|
+
) -> None:
|
|
59
|
+
self._parent = self._resolve_parent(parent)
|
|
60
|
+
layout_kw = self._split_layout_kwargs(kwargs)
|
|
61
|
+
tk_master = self._parent._child_master() if self._parent else None
|
|
62
|
+
|
|
63
|
+
kw: dict[str, Any] = {"col_count": col_count, "min_col_width": min_col_width}
|
|
64
|
+
if data is not None:
|
|
65
|
+
kw["data"] = data
|
|
66
|
+
if items is not None:
|
|
67
|
+
kw["items"] = items
|
|
68
|
+
if on_data_change is not None:
|
|
69
|
+
kw["on_data_change"] = on_data_change
|
|
70
|
+
if width is not None:
|
|
71
|
+
kw["width"] = width
|
|
72
|
+
if height is not None:
|
|
73
|
+
kw["height"] = height
|
|
74
|
+
if accent is not None:
|
|
75
|
+
kw["accent"] = accent
|
|
76
|
+
if buttons is not None:
|
|
77
|
+
kw["buttons"] = buttons
|
|
78
|
+
|
|
79
|
+
self._internal = _InternalForm(tk_master, **kw)
|
|
80
|
+
self._attach_to_parent(layout_kw)
|
|
81
|
+
|
|
82
|
+
# ----- Value API -----
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def value(self) -> dict[str, Any]:
|
|
86
|
+
"""All field values as a dictionary."""
|
|
87
|
+
return self._internal.value
|
|
88
|
+
|
|
89
|
+
@value.setter
|
|
90
|
+
def value(self, values: Mapping[str, Any]) -> None:
|
|
91
|
+
self._internal.value = values
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def data(self) -> dict[str, Any]:
|
|
95
|
+
"""Current form data dict."""
|
|
96
|
+
return self._internal.data
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def result(self) -> Any:
|
|
100
|
+
"""Value set by button commands. None until a button is pressed."""
|
|
101
|
+
return self._internal.result
|
|
102
|
+
|
|
103
|
+
def get(self) -> dict[str, Any]:
|
|
104
|
+
"""Return all field values as a dictionary."""
|
|
105
|
+
return self._internal.get()
|
|
106
|
+
|
|
107
|
+
def set(self, values: Mapping[str, Any]) -> None:
|
|
108
|
+
"""Set multiple field values from a dictionary."""
|
|
109
|
+
self._internal.set(values)
|
|
110
|
+
|
|
111
|
+
def validate(self) -> bool:
|
|
112
|
+
"""Run validation rules; returns True if all fields pass."""
|
|
113
|
+
return self._internal.validate()
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def valid(self) -> Signal:
|
|
117
|
+
"""Reactive `Signal[bool]` — `True` when every field passes validation.
|
|
118
|
+
|
|
119
|
+
AND-ed over the fields' own `valid` signals and recomputed whenever any
|
|
120
|
+
field's validity changes, so a submit button can react to it directly::
|
|
121
|
+
|
|
122
|
+
form = bs.Form(items=[...])
|
|
123
|
+
submit = bs.Button("Save", on_click=save)
|
|
124
|
+
|
|
125
|
+
def gate(ok):
|
|
126
|
+
submit.disabled = not ok
|
|
127
|
+
|
|
128
|
+
form.valid.subscribe(gate)
|
|
129
|
+
|
|
130
|
+
A field is valid until proven otherwise, so this reads `True` before any
|
|
131
|
+
rule has run — call `validate()` to force a full pass (for example, to
|
|
132
|
+
gate the submit button's initial state).
|
|
133
|
+
"""
|
|
134
|
+
return self._internal.valid
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def errors(self) -> dict[str, str]:
|
|
138
|
+
"""Current validation errors keyed by field key; empty when all valid.
|
|
139
|
+
|
|
140
|
+
Read live from the fields' `error` signals, so it always reflects the
|
|
141
|
+
latest validation run.
|
|
142
|
+
"""
|
|
143
|
+
return self._internal.errors
|
|
144
|
+
|
|
145
|
+
# ----- Field access -----
|
|
146
|
+
|
|
147
|
+
def field(self, key: str) -> Any:
|
|
148
|
+
"""Return the Field widget for the given key."""
|
|
149
|
+
return self._internal.field(key)
|
|
150
|
+
|
|
151
|
+
def fields(self) -> tuple:
|
|
152
|
+
"""Return all field widgets in insertion order."""
|
|
153
|
+
return self._internal.fields()
|
|
154
|
+
|
|
155
|
+
def keys(self) -> tuple[str, ...]:
|
|
156
|
+
"""Return all field keys in insertion order."""
|
|
157
|
+
return self._internal.keys()
|
|
158
|
+
|
|
159
|
+
def get_field_value(self, key: str) -> Any:
|
|
160
|
+
"""Return the current value of the named field."""
|
|
161
|
+
return self._internal.get_field_value(key)
|
|
162
|
+
|
|
163
|
+
def set_field_value(self, key: str, value: Any) -> None:
|
|
164
|
+
"""Set the value of the named field."""
|
|
165
|
+
self._internal.set_field_value(key, value)
|
|
166
|
+
|
|
167
|
+
# ----- Signal access -----
|
|
168
|
+
|
|
169
|
+
def field_signal(self, key: str) -> Any:
|
|
170
|
+
"""Return the Signal for the named field value."""
|
|
171
|
+
return self._internal.field_signal(key)
|
|
172
|
+
|
|
173
|
+
def field_textsignal(self, key: str) -> Any:
|
|
174
|
+
"""Return the text Signal for the named field."""
|
|
175
|
+
return self._internal.field_textsignal(key)
|
|
176
|
+
|
|
177
|
+
# ----- Events -----
|
|
178
|
+
|
|
179
|
+
@overload
|
|
180
|
+
def on_data_change(self) -> Stream: ...
|
|
181
|
+
@overload
|
|
182
|
+
def on_data_change(self, handler: Callable[[dict[str, Any]], Any]) -> Subscription: ...
|
|
183
|
+
def on_data_change(self, handler: Callable[[dict[str, Any]], Any] | None = None) -> Stream | Subscription:
|
|
184
|
+
"""Register a callback fired whenever any field value changes.
|
|
185
|
+
|
|
186
|
+
The handler receives the current form data as a dict. Called with no
|
|
187
|
+
handler, returns a composable `Stream`.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
handler: Called with the updated data dict on each field change.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
`Subscription` (with handler) or `Stream` (without handler).
|
|
194
|
+
"""
|
|
195
|
+
if handler is None:
|
|
196
|
+
return self.on("data_change")
|
|
197
|
+
return self.on("data_change", lambda _e: handler(self.value))
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
register_widget_events(Form, {"data_change": "<<BsDataChange>>"})
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable, Literal, overload, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from bootstack.events import Event, Subscription
|
|
6
|
+
from bootstack.streams import Stream
|
|
7
|
+
from bootstack.widgets._core.base import PublicWidgetBase
|
|
8
|
+
from bootstack.widgets._core.events import register_widget_events
|
|
9
|
+
from bootstack.widgets._impl.composites.gallery import Gallery as _InternalGallery
|
|
10
|
+
from bootstack.widgets.types import AccentToken, SelectionMode, SurfaceToken, ScrollbarVariant
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from bootstack.data.types import DataSourceProtocol
|
|
14
|
+
|
|
15
|
+
ImageFit = Literal["contain", "cover", "fill", "none", "scale-down"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Gallery(PublicWidgetBase):
|
|
19
|
+
"""A scrollable, selectable grid of image thumbnails.
|
|
20
|
+
|
|
21
|
+
`Gallery` displays a collection of image records as a reflowing grid of
|
|
22
|
+
thumbnails, recycling tiles so it stays efficient for large collections. It
|
|
23
|
+
is record-native — populate it with `items=` (a list of dicts) or a
|
|
24
|
+
`data_source=`, and read the chosen tile(s) back through `selection`, the
|
|
25
|
+
same model `ListView`, `DataTable`, and `Tree` share.
|
|
26
|
+
|
|
27
|
+
Each record names the image to show through `image_field` (an `Image`, a file
|
|
28
|
+
path, or a PIL image) and, optionally, a caption through `caption_field`.
|
|
29
|
+
Other keys ride along in the record bag and are returned by `selection` and
|
|
30
|
+
events.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
items: Initial list of record dicts.
|
|
34
|
+
data_source: A data source for database- or API-backed data. Any object
|
|
35
|
+
implementing the data-source protocol is accepted.
|
|
36
|
+
image_field: Record key holding the image to display in each tile — an
|
|
37
|
+
`Image` handle, a file path, or a PIL image. Default `'image'`.
|
|
38
|
+
caption_field: Record key for the caption shown under each thumbnail.
|
|
39
|
+
Default `None` (no captions).
|
|
40
|
+
columns: Number of columns, or `'auto'` (default) to reflow the grid to
|
|
41
|
+
fit the available width as it resizes.
|
|
42
|
+
tile_size: Thumbnail size as `(width, height)` in pixels. Default
|
|
43
|
+
`(160, 160)`.
|
|
44
|
+
fit: How each image is scaled into its tile — `'contain'`, `'cover'`
|
|
45
|
+
(default), `'fill'`, `'none'`, or `'scale-down'`. See `Picture`.
|
|
46
|
+
corner_radius: Rounded-corner radius for thumbnails, in pixels. Default
|
|
47
|
+
`0`.
|
|
48
|
+
gap: Spacing between tiles in pixels. Default `8`.
|
|
49
|
+
selection_mode: Tile selection behavior — `'single'`, `'multi'`, or
|
|
50
|
+
`'none'` (default). Selected tiles show an accent highlight ring.
|
|
51
|
+
scrollbar_variant: Scrollbar style — `'thin'` (default) for a slim bar,
|
|
52
|
+
or `'default'` for the standard rounded bar.
|
|
53
|
+
rows: Number of tile rows used to derive the gallery's minimum height
|
|
54
|
+
when it sits in a container that doesn't impose one (a scroll view,
|
|
55
|
+
an auto-fit window). Default `2`. `fill`/`expand` still grow the grid
|
|
56
|
+
past this floor and reflow more rows in.
|
|
57
|
+
accent: Color intent token for the selection ring. Defaults to the
|
|
58
|
+
theme's primary color.
|
|
59
|
+
surface: Background surface token shown behind the tiles.
|
|
60
|
+
parent: Override the context-stack parent.
|
|
61
|
+
**kwargs: Layout placement options applied by the parent container —
|
|
62
|
+
`fill`, `expand`, `anchor`, `margin`, `row`, `column`, `sticky`.
|
|
63
|
+
See :doc:`/tasks/layout`.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
*,
|
|
69
|
+
items: list[dict] | None = None,
|
|
70
|
+
data_source: "DataSourceProtocol | None" = None,
|
|
71
|
+
image_field: str = "image",
|
|
72
|
+
caption_field: str | None = None,
|
|
73
|
+
columns: "int | Literal['auto']" = "auto",
|
|
74
|
+
tile_size: tuple[int, int] = (160, 160),
|
|
75
|
+
fit: ImageFit = "cover",
|
|
76
|
+
corner_radius: int = 0,
|
|
77
|
+
gap: int = 8,
|
|
78
|
+
selection_mode: SelectionMode = "none",
|
|
79
|
+
scrollbar_variant: ScrollbarVariant = "thin",
|
|
80
|
+
rows: int = 2,
|
|
81
|
+
accent: AccentToken | str | None = None,
|
|
82
|
+
surface: SurfaceToken | str | None = None,
|
|
83
|
+
parent: Any = None,
|
|
84
|
+
**kwargs: Any,
|
|
85
|
+
) -> None:
|
|
86
|
+
self._parent = self._resolve_parent(parent)
|
|
87
|
+
self._selection_mode = selection_mode
|
|
88
|
+
layout_kw = self._split_layout_kwargs(kwargs)
|
|
89
|
+
tk_master = self._parent._child_master() if self._parent else None
|
|
90
|
+
|
|
91
|
+
internal_kwargs: dict[str, Any] = {
|
|
92
|
+
"image_field": image_field,
|
|
93
|
+
"caption_field": caption_field,
|
|
94
|
+
"columns": columns,
|
|
95
|
+
"tile_size": tile_size,
|
|
96
|
+
"fit": fit,
|
|
97
|
+
"corner_radius": corner_radius,
|
|
98
|
+
"gap": gap,
|
|
99
|
+
"selection_mode": selection_mode,
|
|
100
|
+
"scrollbar_variant": scrollbar_variant,
|
|
101
|
+
"rows": rows,
|
|
102
|
+
}
|
|
103
|
+
if items is not None:
|
|
104
|
+
internal_kwargs["items"] = items
|
|
105
|
+
if data_source is not None:
|
|
106
|
+
internal_kwargs["datasource"] = data_source
|
|
107
|
+
if accent is not None:
|
|
108
|
+
internal_kwargs["accent"] = accent
|
|
109
|
+
if surface is not None:
|
|
110
|
+
internal_kwargs["surface"] = surface
|
|
111
|
+
|
|
112
|
+
self._internal = _InternalGallery(tk_master, **internal_kwargs)
|
|
113
|
+
self._internal.pack_propagate(False)
|
|
114
|
+
self._attach_to_parent(layout_kw)
|
|
115
|
+
|
|
116
|
+
# ----- Selection -----
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def selection(self) -> dict | list[dict] | None:
|
|
120
|
+
"""The selected record(s) — the data bag.
|
|
121
|
+
|
|
122
|
+
In `'single'` mode, the selected record `dict` (or `None`). In `'multi'`
|
|
123
|
+
mode, a `list` of record dicts (empty when nothing is selected). Each
|
|
124
|
+
record carries its non-displayed fields too. Read-only.
|
|
125
|
+
"""
|
|
126
|
+
rows = self._internal.get_selected()
|
|
127
|
+
if self._selection_mode == "multi":
|
|
128
|
+
return rows
|
|
129
|
+
return rows[0] if rows else None
|
|
130
|
+
|
|
131
|
+
def select_items(self, record_ids: list) -> None:
|
|
132
|
+
"""Select tiles by record `id`.
|
|
133
|
+
|
|
134
|
+
In `'single'` mode this replaces the current selection; in `'multi'`
|
|
135
|
+
mode the items are added to it.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
record_ids: Record ids of the tiles to select.
|
|
139
|
+
"""
|
|
140
|
+
self._internal.select_items(record_ids)
|
|
141
|
+
|
|
142
|
+
def deselect_items(self, record_ids: list) -> None:
|
|
143
|
+
"""Remove the given tiles from the selection by record `id`.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
record_ids: Record ids to deselect.
|
|
147
|
+
"""
|
|
148
|
+
self._internal.deselect_items(record_ids)
|
|
149
|
+
|
|
150
|
+
def select_all(self) -> None:
|
|
151
|
+
"""Select all tiles. Only effective when `selection_mode='multi'`."""
|
|
152
|
+
self._internal.select_all()
|
|
153
|
+
|
|
154
|
+
def clear_selection(self) -> None:
|
|
155
|
+
"""Deselect all tiles."""
|
|
156
|
+
self._internal.clear_selection()
|
|
157
|
+
|
|
158
|
+
# ----- Scrolling -----
|
|
159
|
+
|
|
160
|
+
def scroll_to_top(self) -> None:
|
|
161
|
+
"""Scroll to the first tile."""
|
|
162
|
+
self._internal.scroll_to_top()
|
|
163
|
+
|
|
164
|
+
def scroll_to_bottom(self) -> None:
|
|
165
|
+
"""Scroll to the last tile."""
|
|
166
|
+
self._internal.scroll_to_bottom()
|
|
167
|
+
|
|
168
|
+
def reload(self) -> None:
|
|
169
|
+
"""Reload from the data source and refresh the grid."""
|
|
170
|
+
self._internal.reload()
|
|
171
|
+
|
|
172
|
+
# ----- Properties -----
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def data_source(self) -> "DataSourceProtocol":
|
|
176
|
+
"""The underlying data source instance."""
|
|
177
|
+
return self._internal.get_datasource()
|
|
178
|
+
|
|
179
|
+
# ----- Events -----
|
|
180
|
+
|
|
181
|
+
@overload
|
|
182
|
+
def on_item_click(self) -> Stream: ...
|
|
183
|
+
@overload
|
|
184
|
+
def on_item_click(self, handler: Callable[[dict[str, Any]], Any]) -> Subscription: ...
|
|
185
|
+
def on_item_click(
|
|
186
|
+
self, handler: Callable[[dict[str, Any]], Any] | None = None
|
|
187
|
+
) -> Stream | Subscription:
|
|
188
|
+
"""Fired when a tile is clicked.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
handler: Called with the record `dict` — read fields with
|
|
192
|
+
`e["field"]`. Omit to get a composable
|
|
193
|
+
:class:`~bootstack.streams.Stream`.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
A cancellable :class:`~bootstack.events.Subscription` when a handler
|
|
197
|
+
is given, otherwise a :class:`~bootstack.streams.Stream`.
|
|
198
|
+
"""
|
|
199
|
+
return self.on("item_click", handler)
|
|
200
|
+
|
|
201
|
+
@overload
|
|
202
|
+
def on_item_activate(self) -> Stream: ...
|
|
203
|
+
@overload
|
|
204
|
+
def on_item_activate(self, handler: Callable[[dict[str, Any]], Any]) -> Subscription: ...
|
|
205
|
+
def on_item_activate(
|
|
206
|
+
self, handler: Callable[[dict[str, Any]], Any] | None = None
|
|
207
|
+
) -> Stream | Subscription:
|
|
208
|
+
"""Fired when a tile is activated (double-clicked).
|
|
209
|
+
|
|
210
|
+
The natural hook for opening a full-size viewer or detail page for the
|
|
211
|
+
chosen record.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
handler: Called with the record `dict`. Omit to get a composable
|
|
215
|
+
:class:`~bootstack.streams.Stream`.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
A cancellable :class:`~bootstack.events.Subscription` when a handler
|
|
219
|
+
is given, otherwise a :class:`~bootstack.streams.Stream`.
|
|
220
|
+
"""
|
|
221
|
+
return self.on("item_activate", handler)
|
|
222
|
+
|
|
223
|
+
@overload
|
|
224
|
+
def on_select(self) -> Stream: ...
|
|
225
|
+
@overload
|
|
226
|
+
def on_select(self, handler: Callable[[Event], Any]) -> Subscription: ...
|
|
227
|
+
def on_select(
|
|
228
|
+
self, handler: Callable[[Event], Any] | None = None
|
|
229
|
+
) -> Stream | Subscription:
|
|
230
|
+
"""Fired when the selection changes.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
handler: Called with the curated :class:`~bootstack.events.Event`;
|
|
234
|
+
read `selection` for the current selection. Omit to get a
|
|
235
|
+
composable :class:`~bootstack.streams.Stream`.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
A cancellable :class:`~bootstack.events.Subscription` when a handler
|
|
239
|
+
is given, otherwise a :class:`~bootstack.streams.Stream`.
|
|
240
|
+
"""
|
|
241
|
+
return self.on("select", handler)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
_GALLERY_EVENTS: dict[str, str] = {
|
|
245
|
+
"item_click": "<<ItemClick>>",
|
|
246
|
+
"item_activate": "<<ItemActivate>>",
|
|
247
|
+
"select": "<<SelectionChange>>",
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
register_widget_events(Gallery, _GALLERY_EVENTS)
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Literal, overload, Any, Callable
|
|
4
|
+
|
|
5
|
+
from bootstack.widgets._impl.composites.meter import Meter as _InternalMeter
|
|
6
|
+
from bootstack.widgets._core.base import PublicWidgetBase
|
|
7
|
+
from bootstack.widgets._core.events import register_widget_events
|
|
8
|
+
from bootstack.events import SliderEvent, Subscription
|
|
9
|
+
from bootstack.streams import Stream
|
|
10
|
+
from bootstack.widgets.types import AccentToken, Event, SurfaceToken
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Gauge(PublicWidgetBase):
|
|
14
|
+
"""A circular gauge for displaying a value within a range.
|
|
15
|
+
|
|
16
|
+
Renders a circular arc gauge with an optional center text display. Supports
|
|
17
|
+
full-circle and semicircle layouts, solid and segmented arcs, and an
|
|
18
|
+
interactive drag mode for value input.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
value: Initial value. Defaults to `0`.
|
|
22
|
+
min_value: Minimum of the range. Defaults to `0`.
|
|
23
|
+
max_value: Maximum of the range. Defaults to `100`.
|
|
24
|
+
subtitle: Secondary label displayed below the value text.
|
|
25
|
+
value_prefix: Text prepended to the displayed value (e.g. `'$'`).
|
|
26
|
+
value_suffix: Text appended to the displayed value (e.g. `'%'`).
|
|
27
|
+
value_template: Python format string applied to the value (e.g.
|
|
28
|
+
`'{:.0f}'`, `'{:.1f}'`). Pair with `value_prefix`/`value_suffix` for
|
|
29
|
+
units. Default `'{:.0f}'` (integer display). For locale-aware
|
|
30
|
+
formatting, format the value yourself and pass it via `value=`.
|
|
31
|
+
size: Diameter of the gauge in pixels. Defaults to `200`.
|
|
32
|
+
thickness: Width of the arc stroke in pixels. Defaults to `10`.
|
|
33
|
+
variant: Shape variant. `'full'` (default) draws a complete 360° ring;
|
|
34
|
+
`'semi'` draws a half-circle arc along the bottom.
|
|
35
|
+
segment_width: Width of each dash segment for a dashed arc style.
|
|
36
|
+
`0` (default) draws a solid arc.
|
|
37
|
+
interactive: If `True`, the user can click or drag the arc to change
|
|
38
|
+
the value. Defaults to `False`.
|
|
39
|
+
step: Value increment per drag step in interactive mode. Defaults to `1`.
|
|
40
|
+
show_text: If `False`, hides the center value text. Defaults to `True`.
|
|
41
|
+
accent: Color intent token for the arc. Defaults to the theme's default
|
|
42
|
+
color.
|
|
43
|
+
surface: Surface token for the gauge background, so it matches the
|
|
44
|
+
container it sits on (e.g. `'card'` inside a `Card`). Defaults to the
|
|
45
|
+
window background.
|
|
46
|
+
parent: Override the context-stack parent.
|
|
47
|
+
**kwargs: Layout placement options applied by the parent container —
|
|
48
|
+
`fill`, `expand`, `anchor`, `margin`, `row`, `column`, `sticky`.
|
|
49
|
+
See :doc:`/tasks/layout`.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
value: int | float = 0,
|
|
55
|
+
*,
|
|
56
|
+
min_value: int | float = 0,
|
|
57
|
+
max_value: int | float = 100,
|
|
58
|
+
subtitle: str | None = None,
|
|
59
|
+
value_prefix: str | None = None,
|
|
60
|
+
value_suffix: str | None = None,
|
|
61
|
+
value_template: str = "{:.0f}",
|
|
62
|
+
size: int = 200,
|
|
63
|
+
thickness: int = 10,
|
|
64
|
+
variant: Literal["full", "semi"] = "full",
|
|
65
|
+
segment_width: int = 0,
|
|
66
|
+
interactive: bool = False,
|
|
67
|
+
step: int | float = 1,
|
|
68
|
+
show_text: bool = True,
|
|
69
|
+
accent: AccentToken | str | None = None,
|
|
70
|
+
surface: SurfaceToken | str | None = None,
|
|
71
|
+
parent: Any = None,
|
|
72
|
+
**kwargs: Any,
|
|
73
|
+
) -> None:
|
|
74
|
+
self._parent = self._resolve_parent(parent)
|
|
75
|
+
layout_kw = self._split_layout_kwargs(kwargs)
|
|
76
|
+
|
|
77
|
+
tk_master = self._parent._child_master() if self._parent else None
|
|
78
|
+
|
|
79
|
+
internal_kwargs: dict[str, Any] = {
|
|
80
|
+
"value": value,
|
|
81
|
+
"minvalue": min_value,
|
|
82
|
+
"maxvalue": max_value,
|
|
83
|
+
"value_format": value_template,
|
|
84
|
+
"size": size,
|
|
85
|
+
"thickness": thickness,
|
|
86
|
+
"meter_type": variant,
|
|
87
|
+
"segment_width": segment_width,
|
|
88
|
+
"interactive": interactive,
|
|
89
|
+
"step_size": step,
|
|
90
|
+
"show_text": show_text,
|
|
91
|
+
}
|
|
92
|
+
if subtitle is not None:
|
|
93
|
+
internal_kwargs["subtitle"] = subtitle
|
|
94
|
+
if value_prefix is not None:
|
|
95
|
+
internal_kwargs["value_prefix"] = value_prefix
|
|
96
|
+
if value_suffix is not None:
|
|
97
|
+
internal_kwargs["value_suffix"] = value_suffix
|
|
98
|
+
if accent is not None:
|
|
99
|
+
internal_kwargs["accent"] = accent
|
|
100
|
+
if surface is not None:
|
|
101
|
+
internal_kwargs["surface"] = surface
|
|
102
|
+
|
|
103
|
+
self._internal = _InternalMeter(tk_master, **internal_kwargs)
|
|
104
|
+
self._attach_to_parent(layout_kw)
|
|
105
|
+
|
|
106
|
+
# ----- Properties -----
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def value(self) -> int | float:
|
|
110
|
+
"""The current gauge value."""
|
|
111
|
+
return self._internal.value
|
|
112
|
+
|
|
113
|
+
@value.setter
|
|
114
|
+
def value(self, v: int | float) -> None:
|
|
115
|
+
self._internal.value = v
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def subtitle(self) -> str:
|
|
119
|
+
"""The secondary label shown below the value text."""
|
|
120
|
+
return self._internal.subtitle
|
|
121
|
+
|
|
122
|
+
@subtitle.setter
|
|
123
|
+
def subtitle(self, v: str) -> None:
|
|
124
|
+
self._internal.subtitle = v
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def min_value(self) -> int | float:
|
|
128
|
+
"""The low end of the range. Assigning to it rescales the gauge live."""
|
|
129
|
+
return self._internal.cget("minvalue")
|
|
130
|
+
|
|
131
|
+
@min_value.setter
|
|
132
|
+
def min_value(self, v: int | float) -> None:
|
|
133
|
+
self._internal.configure(minvalue=v)
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def max_value(self) -> int | float:
|
|
137
|
+
"""The high end of the range. Assigning to it rescales the gauge live."""
|
|
138
|
+
return self._internal.cget("maxvalue")
|
|
139
|
+
|
|
140
|
+
@max_value.setter
|
|
141
|
+
def max_value(self, v: int | float) -> None:
|
|
142
|
+
self._internal.configure(maxvalue=v)
|
|
143
|
+
|
|
144
|
+
# ----- Event shorthands -----
|
|
145
|
+
|
|
146
|
+
@overload
|
|
147
|
+
def on_change(self) -> Stream: ...
|
|
148
|
+
@overload
|
|
149
|
+
def on_change(self, handler: Callable[[SliderEvent], Any]) -> Subscription: ...
|
|
150
|
+
def on_change(self, handler: Callable[[SliderEvent], Any] | None = None) -> Stream | Subscription:
|
|
151
|
+
"""Register a callback fired when the gauge value changes.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
handler: Called with a :class:`~bootstack.events.SliderEvent`. Omit to
|
|
155
|
+
get a composable :class:`~bootstack.streams.Stream` instead.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
A cancellable :class:`~bootstack.events.Subscription` when a
|
|
159
|
+
handler is given, otherwise a :class:`~bootstack.streams.Stream`.
|
|
160
|
+
"""
|
|
161
|
+
return self.on("change", handler)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
_GAUGE_EVENTS: dict[str, str] = {
|
|
165
|
+
"change": "<<Change>>",
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
register_widget_events(Gauge, _GAUGE_EVENTS)
|