bootstack 0.1.7__tar.gz → 0.2.0__tar.gz
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-0.1.7/src/bootstack.egg-info → bootstack-0.2.0}/PKG-INFO +1 -1
- {bootstack-0.1.7 → bootstack-0.2.0}/pyproject.toml +2 -2
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_runtime/events.py +156 -9
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_runtime/utility.py +14 -2
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/errors.py +12 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/typography.py +15 -6
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/base.py +14 -4
- bootstack-0.2.0/src/bootstack/widgets/_core/choices.py +56 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/container.py +57 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/field_mixin.py +7 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/_parts/spinnerentry_part.py +5 -3
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/_parts/textentry_part.py +9 -3
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/dateentry.py +30 -7
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/field.py +7 -1
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/form.py +16 -4
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/shell/layout.py +33 -28
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/shell/shell.py +3 -3
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/flexframe.py +74 -15
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/appshell.py +12 -9
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/calendar.py +2 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/card.py +4 -11
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/codeeditor.py +2 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/datatable.py +4 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/datefield.py +2 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/expander.py +7 -21
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/form.py +5 -1
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/gallery.py +2 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/groupbox.py +4 -11
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/listview.py +2 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/pagestack.py +4 -11
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/pathfield.py +2 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/scrollview.py +3 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/select.py +7 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/sidebar_toggle.py +2 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/splitview.py +4 -11
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/stacks.py +16 -7
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/tabs.py +4 -11
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/textarea.py +2 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/togglegroup.py +2 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/tree.py +2 -0
- {bootstack-0.1.7 → bootstack-0.2.0/src/bootstack.egg-info}/PKG-INFO +1 -1
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack.egg-info/SOURCES.txt +1 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/tests/test_public_surface.py +1 -1
- {bootstack-0.1.7 → bootstack-0.2.0}/LICENSE +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/MANIFEST.in +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/NOTICE +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/README.md +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/setup.cfg +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/__main__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/after.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/bind.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/bindtags.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/busy.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/clipboard.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/focus.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/grab.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/grid.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/localization.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/pack.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/place.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/selection.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/signals.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/capabilities/winfo.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/colorutils.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/exceptions.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/images.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/mixins/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/mixins/ttk_state.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/mixins/widget.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/paths.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/publisher.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_core/variables.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_runtime/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_runtime/app.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_runtime/base_window.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_runtime/shortcuts.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_runtime/tk_patch.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_runtime/toplevel.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_runtime/visual_focus.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_runtime/wheel.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/_runtime/window_utilities.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/bootstack.ico +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/bootstack.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/badge-pill.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/badge-square.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/button-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/button-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/card.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/checkbox-checked.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/checkbox-indeterminate.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/checkbox-unchecked.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/field.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/input-addon-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/input-addon-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/input-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/input-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/list-item-separated.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/list-item.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/listrow-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/listrow-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/manifest.toml +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/menu-item.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/navitem-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/navitem-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/progressbar-h-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/progressbar-h-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/progressbar-v-compact.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/progressbar-v-default.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/radiobutton.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/scrollbar-horizontal.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/scrollbar-vertical.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/slider-handle.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/slider-track-h.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/slider-track-v.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/switch-off.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/switch-on.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/tab-h.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/elements/tab-v.png +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/icons/bootstrap.ttf +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/icons/glyphmap.json +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/icons/icon_metrics.json +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/__main__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/add.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/appicon.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/build.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/config.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/demo.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/dev.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/doctor.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/icons.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/promote.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/pyinstaller.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/run.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/start.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/cli/templates/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/clipboard.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/constants.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/data/README.md +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/data/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/data/_observable.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/data/base.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/data/file_source.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/data/memory_source.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/data/query.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/data/readers.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/data/sqlite_source.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/data/types.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/data/writers.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dev/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dev/_capture.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dev/_env.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dev/_registry.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dev/_reloader.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dev/_reset.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dev/_watcher.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dialogs/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dialogs/_impl/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dialogs/_impl/colorchooser.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dialogs/_impl/datedialog.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dialogs/_impl/dialog.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dialogs/_impl/filterdialog.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dialogs/_impl/fontdialog.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dialogs/_impl/formdialog.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dialogs/_impl/message.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/dialogs/_impl/query.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/events/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/events/_event.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/events/_payloads.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/events/_subscription.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/i18n/README.md +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/i18n/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/i18n/catalog.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/i18n/intl_format.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/i18n/msgcat.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/i18n/specs.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/images.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/py.typed +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/scheduling/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/scheduling/_schedule.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/shortcuts.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/signals/README.md +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/signals/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/signals/integration.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/signals/signal.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/signals/types.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/store.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/streams/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/streams/_stream.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/badge.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/button.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/buttongroup.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/calendar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/checkbutton.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/combobox.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/contextmenu.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/entry.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/expander.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/field.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/frame.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/label.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/labelframe.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/listview.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/menubar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/menubutton.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/panedwindow.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/progressbar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/radiobutton.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/scale.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/scrollbar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/separator.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/sidenav.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/sizegrip.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/spinbox.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/switch.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/tabitem.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/togglegroup.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/toolbutton.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/tooltip.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/treeview.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders/utils.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders_tk/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/builders_tk/defaults.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/element.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/fonts.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/style.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/style_builder_base.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/style_builder_mixed.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/style_builder_tk.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/style_builder_ttk.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/style_resolver.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/theme.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/theme_provider.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/themes/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/tk_patch.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/token_maps.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/types.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/style/utility.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/types.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/validation/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/validation/types.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/validation/validation_result.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/validation/validation_rules.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/app_config.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/context.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/events.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/icon_image_props.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/image_binding.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/kwargs.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/navmodel.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/options.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/selection_group.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/window_controls.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_core/window_menu.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/_internal/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/_internal/wrapper_base.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/_parts/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/_parts/numberentry_part.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/_dateutils.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/_image_fit.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/accordion.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/avatar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/buttongroup.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/calendar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/carousel.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/chart.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/compositeframe.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/contextmenu.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/dropdownbutton.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/expander.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/gallery.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/list/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/list/listitem.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/list/listview.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/menu/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/menu/model.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/menu/render_native.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/menu/render_themed.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/meter.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/numericentry.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/pagestack.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/passwordentry.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/pathentry.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/picture.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/radiogroup.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/scrolledtext.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/scrolledtext.pyi +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/scrollview.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/selectbox.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/shell/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/shell/content_host.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/shell/nav_panel.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/shell/providers.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/shell/rail.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/shell/workspace.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/sidenav/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/sidenav/header.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/sidenav/separator.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/slider/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/slider/_shared.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/slider/rangeslider.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/slider/slider.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/spinnerentry.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tableview/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tableview/tableview.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tableview/types.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tabs/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tabs/tabitem.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tabs/tabs.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tabs/tabview.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/change.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/codeeditor.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/core.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/decoration.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/diff.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/filter.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/search_overlay.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/sidebar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/style_registry.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/textarea.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textarea/undo.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/textentry.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/timeentry.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/toast.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/toast_stack.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/togglegroup.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/toolbar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tooltip.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tree/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tree/source_binding.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tree/treeitem.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tree/treenode.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/composites/tree/treeview.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/mixins/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/mixins/configure_mixin.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/mixins/entry_mixin.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/mixins/font_mixin.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/mixins/icon_mixin.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/mixins/localization_mixin.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/mixins/signal_mixin.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/mixins/validation_mixin.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/__init__.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/_menubutton.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/badge.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/button.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/card.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/checkbutton.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/checktoggle.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/combobox.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/entry.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/frame.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/gridframe.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/label.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/labelframe.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/optionmenu.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/packframe.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/panedwindow.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/progressbar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/radiobutton.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/radiotoggle.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/scrollbar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/separator.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/sizegrip.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/spinbox.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/switch.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/_impl/primitives/treeview.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/app.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/avatar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/boolean_controls.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/button.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/buttongroup.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/carousel.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/chart.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/contextmenu.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/divider.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/gauge.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/grid.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/label.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/menubutton.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/numberfield.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/passwordfield.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/picture.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/progressbar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/radio_variants.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/radiogroup.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/scrollbar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/selectbutton.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/sizegrip.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/slider.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/spinbox.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/spinnerfield.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/splash.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/statusbar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/textfield.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/theme_toggle.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/timefield.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/toast.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/toolbar.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/tooltip.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/types.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack/widgets/window.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack.egg-info/dependency_links.txt +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack.egg-info/entry_points.txt +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack.egg-info/requires.txt +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/src/bootstack.egg-info/top_level.txt +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/tests/test_dialog_anchor.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/tests/test_events_doc_coverage.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/tests/test_hot_reload.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/tests/test_images.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/tests/test_images_gui.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/tests/test_intl_format.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/tests/test_navmodel.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/tests/test_on_color_contrast.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/tests/test_store.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/tests/test_theme_from_existing.py +0 -0
- {bootstack-0.1.7 → bootstack-0.2.0}/tests/test_validation_rules.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bootstack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Build desktop GUI apps in pure Python and ship them as a standalone executable — built on Tk, no Tkinter boilerplate.
|
|
5
5
|
Author-email: Israel Dryer <israel.dryer@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "bootstack"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Build desktop GUI apps in pure Python and ship them as a standalone executable — built on Tk, no Tkinter boilerplate."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -114,7 +114,7 @@ where = ["src"]
|
|
|
114
114
|
]
|
|
115
115
|
|
|
116
116
|
[tool.bumpversion]
|
|
117
|
-
current_version = "0.
|
|
117
|
+
current_version = "0.2.0"
|
|
118
118
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)((?P<pre>a|b|rc)(?P<pre_n>\\d+))?"
|
|
119
119
|
serialize = [
|
|
120
120
|
"{major}.{minor}.{patch}{pre}{pre_n}",
|
|
@@ -32,6 +32,7 @@ Note:
|
|
|
32
32
|
applied globally and affect all tkinter widgets in the application.
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
|
+
import itertools
|
|
35
36
|
import tkinter as tk
|
|
36
37
|
import uuid
|
|
37
38
|
import weakref
|
|
@@ -43,9 +44,35 @@ from typing import Any, Callable, Optional, Union
|
|
|
43
44
|
_event_data_cache: OrderedDict[str, Any] = OrderedDict()
|
|
44
45
|
_MAX_CACHE_SIZE = 100 # Prevent unbounded growth
|
|
45
46
|
|
|
47
|
+
# Serial number used to give every registered handler a unique name. See
|
|
48
|
+
# `_unique_name` for why the stock naming scheme is not safe here.
|
|
49
|
+
_binding_seq = itertools.count()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _unique_name(prefix: str) -> str:
|
|
53
|
+
"""Return a handler name that can never collide with a previous one.
|
|
54
|
+
|
|
55
|
+
Tkinter names the Tcl command behind a binding `repr(id(f)) + f.__name__`,
|
|
56
|
+
where `f` is a bound method created for that registration alone. Nothing
|
|
57
|
+
else refers to it, so releasing the command frees the method and CPython is
|
|
58
|
+
free to hand the very same address to the next one — measured at 498 out of
|
|
59
|
+
499 consecutive bind/cancel/bind cycles. Every wrapper registered here has
|
|
60
|
+
the same `__name__`, so the whole name would repeat with it.
|
|
61
|
+
|
|
62
|
+
That is harmless while removals delete the command immediately, but
|
|
63
|
+
`_patched_unbind` defers the deletion to the next idle point. A repeated
|
|
64
|
+
name means a binding created in the meantime inherits the name a deletion is
|
|
65
|
+
already pending on, and that deletion then takes out a live handler — the
|
|
66
|
+
#392 failure again, on a new trigger. A serial number in the name keeps the
|
|
67
|
+
two apart no matter what the allocator does.
|
|
68
|
+
"""
|
|
69
|
+
return '%s%d' % (prefix, next(_binding_seq))
|
|
70
|
+
|
|
71
|
+
|
|
46
72
|
# Store original tkinter methods before patching
|
|
47
73
|
_original_event_generate: Callable = tk.Misc.event_generate
|
|
48
74
|
_original_bind: Callable = tk.Misc.bind
|
|
75
|
+
_original_unbind: Callable = tk.Misc.unbind
|
|
49
76
|
|
|
50
77
|
|
|
51
78
|
def _patched_event_generate(
|
|
@@ -219,23 +246,60 @@ def _patched_bind(
|
|
|
219
246
|
# Attach custom data (don't pop yet - multiple handlers may need it)
|
|
220
247
|
if data_guid and data_guid in _event_data_cache:
|
|
221
248
|
event.data = _event_data_cache[data_guid]
|
|
222
|
-
# Schedule cleanup after all handlers have run
|
|
223
|
-
widget
|
|
249
|
+
# Schedule cleanup after all handlers have run. On the root,
|
|
250
|
+
# not on `widget`: deferred work owned by a widget is torn
|
|
251
|
+
# down with it, so a handler that destroys its own widget
|
|
252
|
+
# would leave this callback pointing at a command destroy()
|
|
253
|
+
# had already swept — a silent background error. The root
|
|
254
|
+
# outlives every widget. (Same rule as `_patched_unbind`.)
|
|
255
|
+
try:
|
|
256
|
+
widget._root().after_idle(
|
|
257
|
+
lambda: _event_data_cache.pop(data_guid, None)
|
|
258
|
+
)
|
|
259
|
+
except (tk.TclError, AttributeError):
|
|
260
|
+
# No event loop left to defer onto; the LRU cap reclaims
|
|
261
|
+
# the entry instead.
|
|
262
|
+
pass
|
|
224
263
|
else:
|
|
225
264
|
# Data might have been evicted from cache already
|
|
226
265
|
event.data = None
|
|
227
266
|
|
|
228
267
|
return func(event)
|
|
229
268
|
|
|
230
|
-
# Register wrapper function with tkinter
|
|
269
|
+
# Register wrapper function with tkinter. The name tkinter builds
|
|
270
|
+
# ends with the function's `__name__`, so making that unique is
|
|
271
|
+
# enough to keep a deferred deletion off a later binding.
|
|
272
|
+
wrapper.__name__ = _unique_name('bsvirtual')
|
|
231
273
|
name = self._register(wrapper)
|
|
232
274
|
|
|
233
|
-
# Build
|
|
275
|
+
# Build the binding script. Its exact shape is load-bearing, and
|
|
276
|
+
# this must stay byte-for-byte what stock `Misc._bind` emits:
|
|
277
|
+
#
|
|
278
|
+
# * Removing a single handler works by matching the line prefix
|
|
279
|
+
# `if {"[<funcid> ` — note the TRAILING SPACE, which means the
|
|
280
|
+
# substitution list below must never be empty. A script in any
|
|
281
|
+
# other shape is invisible to the matcher, so the handler
|
|
282
|
+
# survives while its command is deleted, leaving an orphan that
|
|
283
|
+
# aborts every handler after it. All handlers for an event share
|
|
284
|
+
# ONE concatenated script, so that took out every handler
|
|
285
|
+
# registered after the cancelled one (#392).
|
|
286
|
+
# * The `if {...} == "break"` test is what lets a handler return
|
|
287
|
+
# 'break' to stop the remaining handlers for that event.
|
|
288
|
+
#
|
|
289
|
+
# (The trailing newline only separates this line from the next
|
|
290
|
+
# handler's; Tk inserts its own separator when appending a `+`
|
|
291
|
+
# script. It is kept to match stock exactly.)
|
|
292
|
+
#
|
|
293
|
+
# The substitution list carries %d (our data token) ahead of the
|
|
294
|
+
# standard values; its order must match `wrapper`'s parameters.
|
|
234
295
|
# Substitution codes: https://tcl.tk/man/tcl8.6/TkCmd/bind.htm
|
|
235
|
-
|
|
296
|
+
subst = '%d %# %b %h %w %k %s %t %x %y %X %Y %A %K %N %T %E %D'
|
|
297
|
+
cmd = '%sif {"[%s %s]" == "break"} break\n' % (
|
|
298
|
+
'+' if add else '', name, subst
|
|
299
|
+
)
|
|
236
300
|
|
|
237
301
|
# Bind to widget
|
|
238
|
-
self.tk.call('bind', self._w, sequence, cmd
|
|
302
|
+
self.tk.call('bind', self._w, sequence, cmd)
|
|
239
303
|
|
|
240
304
|
return name
|
|
241
305
|
else:
|
|
@@ -246,12 +310,92 @@ def _patched_bind(
|
|
|
246
310
|
event.data = None
|
|
247
311
|
return func(event)
|
|
248
312
|
|
|
313
|
+
# Real events are removed by the same patched `unbind`, so they need
|
|
314
|
+
# the same protection from a recycled name. See `_unique_name`.
|
|
315
|
+
regular_wrapper.__name__ = _unique_name('bsregular')
|
|
249
316
|
return _original_bind(self, sequence, regular_wrapper, add)
|
|
250
317
|
|
|
251
318
|
# No function or sequence provided, pass through to original
|
|
252
319
|
return _original_bind(self, sequence, func, add)
|
|
253
320
|
|
|
254
321
|
|
|
322
|
+
def _patched_unbind(
|
|
323
|
+
self: tk.Misc,
|
|
324
|
+
sequence: str,
|
|
325
|
+
funcid: Optional[str] = None
|
|
326
|
+
) -> None:
|
|
327
|
+
"""Enhanced unbind that cannot disturb the other handlers for an event.
|
|
328
|
+
|
|
329
|
+
Stock tkinter removes one handler in two steps: rewrite the binding script
|
|
330
|
+
without that handler's line, then delete the Tcl command behind it. The
|
|
331
|
+
rewrite is correct, but the deletion is immediate — and all handlers for an
|
|
332
|
+
event share ONE script, so if the removal happens *while that script is
|
|
333
|
+
running* (a handler cancelling another handler's subscription), execution
|
|
334
|
+
later reaches the deleted command and the whole script aborts. Every handler
|
|
335
|
+
after the cancelled one is skipped for that dispatch, and the error surfaces
|
|
336
|
+
nowhere Python can see it.
|
|
337
|
+
|
|
338
|
+
This version neutralizes the command in place instead, then deletes it once
|
|
339
|
+
the current dispatch has finished. An already-running script then evaluates
|
|
340
|
+
a harmless no-op where the cancelled handler used to be and carries on to
|
|
341
|
+
the handlers after it.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
self: The widget instance (automatically provided)
|
|
345
|
+
sequence: Event sequence the handler was bound to
|
|
346
|
+
funcid: Identifier returned by `bind`. When omitted, every handler for
|
|
347
|
+
the sequence is removed and the original implementation is used.
|
|
348
|
+
"""
|
|
349
|
+
if funcid is None:
|
|
350
|
+
# Removing everything replaces the whole script, so there is no
|
|
351
|
+
# surviving line that could reference a deleted command.
|
|
352
|
+
return _original_unbind(self, sequence)
|
|
353
|
+
|
|
354
|
+
what = ('bind', self._w, sequence)
|
|
355
|
+
try:
|
|
356
|
+
script = self.tk.call(what)
|
|
357
|
+
lines = str(script).split('\n')
|
|
358
|
+
# Must match what `_patched_bind` emits (and stock `Misc._bind`).
|
|
359
|
+
prefix = 'if {"[%s ' % funcid
|
|
360
|
+
keep = [line for line in lines if not line.startswith(prefix)]
|
|
361
|
+
if len(keep) == len(lines):
|
|
362
|
+
# This funcid is not bound to this widget and sequence, so it is
|
|
363
|
+
# not ours to delete — the live binding may be somewhere else, and
|
|
364
|
+
# deleting its command would orphan it. Leave everything alone.
|
|
365
|
+
return
|
|
366
|
+
remaining = '\n'.join(keep)
|
|
367
|
+
self.tk.call(*what, remaining if remaining.strip() else '')
|
|
368
|
+
# Replace the command rather than deleting it, so an in-flight copy of
|
|
369
|
+
# the concatenated script has something harmless to call.
|
|
370
|
+
self.tk.createcommand(funcid, lambda *args: '')
|
|
371
|
+
except tk.TclError:
|
|
372
|
+
return
|
|
373
|
+
|
|
374
|
+
def _delete_command() -> None:
|
|
375
|
+
try:
|
|
376
|
+
self.deletecommand(funcid)
|
|
377
|
+
except (tk.TclError, AttributeError):
|
|
378
|
+
# Already swept, most likely because the widget was destroyed
|
|
379
|
+
# before the interpreter went idle. `AttributeError` is that same
|
|
380
|
+
# case seen from the other side: destroy() clears the widget's
|
|
381
|
+
# command bookkeeping, which `deletecommand` then cannot update.
|
|
382
|
+
pass
|
|
383
|
+
|
|
384
|
+
try:
|
|
385
|
+
# Deliberately scheduled on the root, not on `self`. Deferred work owned
|
|
386
|
+
# by a widget is torn down with it, and destroying a widget between the
|
|
387
|
+
# cancellation and the next idle point would leave the pending callback
|
|
388
|
+
# referring to a command that destroy() had already deleted — a silent
|
|
389
|
+
# error of exactly the kind this function exists to avoid. The root
|
|
390
|
+
# outlives every widget, and when the root itself goes the pending
|
|
391
|
+
# callback goes with it, which is equally fine: there is nothing left to
|
|
392
|
+
# clean up.
|
|
393
|
+
self._root().after_idle(_delete_command)
|
|
394
|
+
except (tk.TclError, AttributeError):
|
|
395
|
+
# No event loop left to defer onto; nothing can be mid-dispatch either.
|
|
396
|
+
_delete_command()
|
|
397
|
+
|
|
398
|
+
|
|
255
399
|
def cleanup_event_cache() -> None:
|
|
256
400
|
"""Manually clear the entire event data cache.
|
|
257
401
|
|
|
@@ -342,9 +486,10 @@ def get_cache_size() -> int:
|
|
|
342
486
|
def install_enhanced_events() -> None:
|
|
343
487
|
"""Install the enhanced event system by patching tkinter methods.
|
|
344
488
|
|
|
345
|
-
This function applies monkey patches to tkinter's Misc class,
|
|
346
|
-
|
|
347
|
-
|
|
489
|
+
This function applies monkey patches to tkinter's Misc class, replacing
|
|
490
|
+
event_generate and bind with enhanced versions that support passing custom
|
|
491
|
+
data through virtual events, and unbind with a version that cannot disturb
|
|
492
|
+
the other handlers for an event while one is being removed.
|
|
348
493
|
|
|
349
494
|
The patches are applied globally and affect all tkinter widgets
|
|
350
495
|
in the application. This function is called automatically when
|
|
@@ -364,6 +509,7 @@ def install_enhanced_events() -> None:
|
|
|
364
509
|
"""
|
|
365
510
|
tk.Misc.event_generate = _patched_event_generate
|
|
366
511
|
tk.Misc.bind = _patched_bind
|
|
512
|
+
tk.Misc.unbind = _patched_unbind
|
|
367
513
|
|
|
368
514
|
|
|
369
515
|
def uninstall_enhanced_events() -> None:
|
|
@@ -387,6 +533,7 @@ def uninstall_enhanced_events() -> None:
|
|
|
387
533
|
"""
|
|
388
534
|
tk.Misc.event_generate = _original_event_generate
|
|
389
535
|
tk.Misc.bind = _original_bind
|
|
536
|
+
tk.Misc.unbind = _original_unbind
|
|
390
537
|
cleanup_event_cache()
|
|
391
538
|
|
|
392
539
|
|
|
@@ -30,11 +30,18 @@ def _platform_baseline() -> float:
|
|
|
30
30
|
|
|
31
31
|
Tk scaling is measured in pixels-per-point (72 points per inch).
|
|
32
32
|
- Windows/Linux default DPI is 96 → baseline = 96/72 ≈ 1.334
|
|
33
|
-
- macOS default DPI is 72
|
|
33
|
+
- macOS default DPI is 72 on Tk 8.6 → baseline = 72/72 = 1.0
|
|
34
34
|
(Retina pixel-doubling is handled by the OS, not Tk scaling)
|
|
35
|
+
|
|
36
|
+
Tk 9 changed Aqua's nominal resolution from 72 to 96 DPI, aligning it with
|
|
37
|
+
Windows/X11 (#375). The pixel grid is unchanged — only the physical-size
|
|
38
|
+
claim moved — so the baseline must key off the Tk version, not the platform
|
|
39
|
+
alone. Keying off platform alone reports ui_scale ~1.333 instead of 1.0 on
|
|
40
|
+
macOS + Tk 9 and inflates every DPI-derived measurement by a third.
|
|
35
41
|
"""
|
|
36
42
|
import platform
|
|
37
|
-
|
|
43
|
+
import tkinter
|
|
44
|
+
if platform.system() == 'Darwin' and tkinter.TkVersion < 9.0:
|
|
38
45
|
return 1.0
|
|
39
46
|
return 1.33398982438864281 # 96 DPI / 72
|
|
40
47
|
|
|
@@ -54,6 +61,11 @@ class _ScalingState:
|
|
|
54
61
|
"""Get the current global scale factor."""
|
|
55
62
|
return cls._scale_factor
|
|
56
63
|
|
|
64
|
+
@classmethod
|
|
65
|
+
def get_baseline(cls) -> float:
|
|
66
|
+
"""Get the platform's nominal Tk scaling (pixels per point)."""
|
|
67
|
+
return cls._baseline
|
|
68
|
+
|
|
57
69
|
@classmethod
|
|
58
70
|
def get_ui_scale(cls) -> float:
|
|
59
71
|
"""Get the UI scale factor (relative to baseline)."""
|
|
@@ -31,6 +31,17 @@ class SerializationError(BootstackError):
|
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
class InvalidChoiceError(BootstackError, ValueError):
|
|
35
|
+
"""Raised when a keyword argument is given a value outside its valid set.
|
|
36
|
+
|
|
37
|
+
Applies to arguments that name a behavior mode drawn from a closed set,
|
|
38
|
+
such as `selection_mode` or `scrollbars`. The message names the value that
|
|
39
|
+
was rejected and lists the values that are accepted.
|
|
40
|
+
|
|
41
|
+
Also a `ValueError`, so either `BootstackError` or `ValueError` catches it.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
|
|
34
45
|
class NavigationError(BootstackError):
|
|
35
46
|
"""Raised when a navigation operation references a wrong key.
|
|
36
47
|
|
|
@@ -58,6 +69,7 @@ class StyleBuilderError(BootstackError):
|
|
|
58
69
|
__all__ = [
|
|
59
70
|
"BootstackError",
|
|
60
71
|
"DuplicateIdError",
|
|
72
|
+
"InvalidChoiceError",
|
|
61
73
|
"NavigationError",
|
|
62
74
|
"ParentResolutionError",
|
|
63
75
|
"SerializationError",
|
|
@@ -8,6 +8,8 @@ from tkinter import Misc, font as tkfont
|
|
|
8
8
|
from tkinter.font import Font as TkFont
|
|
9
9
|
from typing import Any, Literal, NamedTuple
|
|
10
10
|
|
|
11
|
+
from bootstack._runtime.utility import _ScalingState
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
# =============================================================================
|
|
13
15
|
# Token model
|
|
@@ -90,12 +92,19 @@ def build_desktop_tokens(
|
|
|
90
92
|
elif system == "darwin":
|
|
91
93
|
default_ui = "SF Pro Text"
|
|
92
94
|
default_mono = "Menlo"
|
|
93
|
-
# Tk's pt-to-pixel conversion uses the active scaling factor.
|
|
94
|
-
# macOS defaults to 1.0 (72 DPI baseline) vs ~1.334 on Win/Linux,
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
|
|
95
|
+
# Tk's pt-to-pixel conversion uses the active scaling factor. On Tk 8.6
|
|
96
|
+
# macOS defaults to 1.0 (72 DPI baseline) vs ~1.334 on Win/Linux, so
|
|
97
|
+
# the same point size renders ~25% smaller on Mac. Bump the base to
|
|
98
|
+
# align with the macOS HIG default control text size (13pt).
|
|
99
|
+
#
|
|
100
|
+
# Tk 9 moved Aqua to a 96 DPI baseline (#375), so a "point" is no
|
|
101
|
+
# longer the same physical size and the raw bump double-counts (a
|
|
102
|
+
# `body` label rendered at 20px linespace instead of 16px). The target
|
|
103
|
+
# is a physical size, so express it in the active point unit: divide by
|
|
104
|
+
# the baseline ratio. Tk 8.6 -> 13pt (unchanged), Tk 9 -> 10pt, which
|
|
105
|
+
# is the same rendered size and matches the point size Tk 9 itself
|
|
106
|
+
# picks for TkDefaultFont.
|
|
107
|
+
base = _clamp_base_size(round((base + 2) / _ScalingState.get_baseline()))
|
|
99
108
|
else:
|
|
100
109
|
default_ui = "DejaVu Sans"
|
|
101
110
|
default_mono = "DejaVu Sans Mono"
|
|
@@ -13,20 +13,30 @@ if TYPE_CHECKING:
|
|
|
13
13
|
from bootstack.streams import Stream
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def adapt_handler(handler: Callable[[Any], Any]) -> Callable[[Any],
|
|
16
|
+
def adapt_handler(handler: Callable[[Any], Any]) -> Callable[[Any], None]:
|
|
17
17
|
"""Wrap a public handler so it receives a bootstack event, not a raw one.
|
|
18
18
|
|
|
19
19
|
Data-carrying events (emitted with `data=<payload>`) deliver the payload
|
|
20
20
|
object directly — the handler argument *is* the payload. Native/context
|
|
21
21
|
events carry no payload, so the raw toolkit event is curated into a clean
|
|
22
22
|
:class:`~bootstack.events.Event` first.
|
|
23
|
+
|
|
24
|
+
A handler's return value is deliberately discarded. The underlying toolkit
|
|
25
|
+
treats one particular return value as "stop the remaining handlers for this
|
|
26
|
+
event", which would make any handler that happens to return that value drop
|
|
27
|
+
its siblings — silently, and with nothing in the public API to explain it.
|
|
28
|
+
Suppressing later handlers is a capability worth designing on purpose rather
|
|
29
|
+
than inheriting, so nothing is promised here. Handlers bound internally do
|
|
30
|
+
not pass through this wrapper and are unaffected.
|
|
23
31
|
"""
|
|
24
32
|
|
|
25
|
-
def _wrapped(raw: Any) ->
|
|
33
|
+
def _wrapped(raw: Any) -> None:
|
|
26
34
|
payload = getattr(raw, "data", None)
|
|
27
35
|
if payload is not None:
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
handler(payload)
|
|
37
|
+
else:
|
|
38
|
+
handler(Event._from_tk(raw))
|
|
39
|
+
return None
|
|
30
40
|
|
|
31
41
|
return _wrapped
|
|
32
42
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Construction-time checking for keyword arguments with a closed value set.
|
|
2
|
+
|
|
3
|
+
Many widget keyword arguments name a behavior mode drawn from a small, closed
|
|
4
|
+
set — `selection_mode`, `sorting_mode`, `scrollbars`. The widgets read those
|
|
5
|
+
values by comparing against one literal (`mode == 'multi'`), so a near-miss
|
|
6
|
+
spelling does not fail: it just takes the other branch. `selection_mode='multiple'`
|
|
7
|
+
turns multi-select off and reports nothing, which reads as a broken widget rather
|
|
8
|
+
than a typo.
|
|
9
|
+
|
|
10
|
+
`validate_choice` closes that gap. It runs at construction, names the value it
|
|
11
|
+
rejected and the set it accepts, and raises `InvalidChoiceError` — both a
|
|
12
|
+
`BootstackError` and a `ValueError`, so either is a valid thing to catch. Call it
|
|
13
|
+
before anything else in `__init__`, ahead of parent resolution: a bad value
|
|
14
|
+
should be reported as a bad value, not masked by whatever fails next.
|
|
15
|
+
|
|
16
|
+
Only use it for a genuinely closed set. Arguments that widen their literal with
|
|
17
|
+
`| str` — `accent`, `surface` — accept values the alias does not spell out
|
|
18
|
+
(`'primary[+1]'`, `'primary[500]'`) and must not be checked this way.
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import Any, Sequence, TypeVar, get_args
|
|
23
|
+
|
|
24
|
+
from bootstack.errors import InvalidChoiceError
|
|
25
|
+
from bootstack.widgets.types import SelectionMode
|
|
26
|
+
|
|
27
|
+
T = TypeVar("T")
|
|
28
|
+
|
|
29
|
+
SELECTION_MODES = get_args(SelectionMode)
|
|
30
|
+
"""Accepted `selection_mode` values, read from the `SelectionMode` alias so the
|
|
31
|
+
check cannot drift from the type."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def validate_choice(value: T, valid: Sequence[Any], *, param: str, widget: str) -> T:
|
|
35
|
+
"""Return `value` if it is in `valid`, otherwise raise.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
value: The value the caller supplied.
|
|
39
|
+
valid: The accepted values, in the order they should be listed back.
|
|
40
|
+
param: Name of the keyword argument being checked, e.g.
|
|
41
|
+
`'selection_mode'`.
|
|
42
|
+
widget: Name of the public widget or method the caller used, e.g.
|
|
43
|
+
`'DataTable'`, used to open the message.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
The value, unchanged.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
InvalidChoiceError: If `value` is not in `valid`.
|
|
50
|
+
"""
|
|
51
|
+
if value in valid:
|
|
52
|
+
return value
|
|
53
|
+
listed = ", ".join(repr(v) for v in valid)
|
|
54
|
+
raise InvalidChoiceError(
|
|
55
|
+
f"{widget}({param}={value!r}) is not a valid {param}. Valid values: {listed}."
|
|
56
|
+
)
|
|
@@ -255,17 +255,74 @@ def grid_sticky(horizontal: str | None, vertical: str | None) -> str:
|
|
|
255
255
|
return sticky
|
|
256
256
|
|
|
257
257
|
|
|
258
|
+
def resolve_layout_items(
|
|
259
|
+
layout: str,
|
|
260
|
+
horizontal_items: str | None,
|
|
261
|
+
vertical_items: str | None,
|
|
262
|
+
*,
|
|
263
|
+
column_horizontal: str | None = None,
|
|
264
|
+
) -> tuple[str | None, str | None]:
|
|
265
|
+
"""Fill in a multi-mode container's unset `*_items` for its layout mode.
|
|
266
|
+
|
|
267
|
+
Containers that can lay out as a column, a row, or a grid (`Card`, `GroupBox`,
|
|
268
|
+
`Expander`, `Tabs`, `PageStack`, `SplitView`, the AppShell page) take
|
|
269
|
+
`horizontal_items`/`vertical_items` as `None`-means-unset and need a default
|
|
270
|
+
that depends on the mode.
|
|
271
|
+
|
|
272
|
+
A **grid** gets concrete values, because the grid engine has no notion of an
|
|
273
|
+
unset axis — cells stretch unless told otherwise. A **column** or **row**
|
|
274
|
+
keeps `None` and lets `FlexFrame` resolve it: the engine's own defaults are
|
|
275
|
+
exactly what these containers used to hard-code (the stacking axis starts at
|
|
276
|
+
its leading edge, the cross axis centers), and leaving the cross axis unset is
|
|
277
|
+
what allows a child to contribute its own default — which is how a row of
|
|
278
|
+
input fields stays aligned whether or not each field carries a validation
|
|
279
|
+
message (#394). Pre-resolving to `'center'` here silently suppressed that.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
layout: The container's layout mode — `'column'`, `'row'` or `'grid'`.
|
|
283
|
+
horizontal_items: The x-axis value as given, or None if unset.
|
|
284
|
+
vertical_items: The y-axis value as given, or None if unset.
|
|
285
|
+
column_horizontal: Cross-axis override for column mode, for a container
|
|
286
|
+
whose column should not center — the AppShell page stretches instead.
|
|
287
|
+
Defaults to None (use the engine default).
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
The pair to hand the layout frame. Either element may be None in column or
|
|
291
|
+
row mode, meaning "unset — let the engine decide"; both are concrete for a
|
|
292
|
+
grid. The None is only ever passed to `FlexFrame`; the grid path
|
|
293
|
+
(`_merge_layout_options`) runs solely in grid mode, where both are set.
|
|
294
|
+
"""
|
|
295
|
+
if layout == "grid":
|
|
296
|
+
return (horizontal_items or "stretch", vertical_items or "stretch")
|
|
297
|
+
if layout == "column" and horizontal_items is None:
|
|
298
|
+
horizontal_items = column_horizontal
|
|
299
|
+
return (horizontal_items, vertical_items)
|
|
300
|
+
|
|
301
|
+
|
|
258
302
|
def _flex_child_opts(child: PublicWidgetBase, layout_kw: dict) -> dict:
|
|
259
303
|
"""Build the FlexFrame per-child opts dict from resolved layout kwargs.
|
|
260
304
|
|
|
261
305
|
Expects `_expand_margin` to have already converted margins to padx/pady.
|
|
262
306
|
Carries spacer metadata when `child` is a `Spacer` (duck-typed to avoid a
|
|
263
307
|
circular import).
|
|
308
|
+
|
|
309
|
+
A widget may also declare its own preferred cross-axis alignment via a
|
|
310
|
+
`_flex_horizontal_default` / `_flex_vertical_default` class attribute (same
|
|
311
|
+
duck-typing). That is a *soft* default: it applies only when neither the
|
|
312
|
+
child nor the container was given an explicit alignment, so it can never
|
|
313
|
+
override an instruction the author actually wrote. The field family uses it
|
|
314
|
+
to sit top-aligned in a Row instead of centering, since a field carrying a
|
|
315
|
+
validation message is taller than one that isn't (#394).
|
|
264
316
|
"""
|
|
265
317
|
opts: dict[str, Any] = {}
|
|
266
318
|
for key in ("grow", "horizontal", "vertical", "padx", "pady"):
|
|
267
319
|
if key in layout_kw:
|
|
268
320
|
opts[key] = layout_kw[key]
|
|
321
|
+
for axis in ("horizontal", "vertical"):
|
|
322
|
+
if axis not in opts:
|
|
323
|
+
declared = getattr(child, f"_flex_{axis}_default", None)
|
|
324
|
+
if declared is not None:
|
|
325
|
+
opts[f"_{axis}_default"] = declared
|
|
269
326
|
if getattr(child, "_is_spacer", False):
|
|
270
327
|
opts["_spacer"] = True
|
|
271
328
|
opts["_spacer_size"] = getattr(child, "_spacer_size", None)
|
|
@@ -23,6 +23,13 @@ class FieldAddonMixin:
|
|
|
23
23
|
#: field wrappers override it (`'number'`, `'date'`, `'time'`).
|
|
24
24
|
_VALIDATION_KIND: str = "text"
|
|
25
25
|
|
|
26
|
+
#: Cross-axis alignment a field contributes when the author set none. A
|
|
27
|
+
#: field that shows a validation message is taller than one that doesn't, so
|
|
28
|
+
#: centering them in a Row leaves the shorter ones sitting low; top-aligning
|
|
29
|
+
#: lines their labels and entries up instead. Soft — an explicit `vertical`
|
|
30
|
+
#: on the field or `vertical_items` on the container still wins (#394).
|
|
31
|
+
_flex_vertical_default: str = "top"
|
|
32
|
+
|
|
26
33
|
_ADDON_TYPES = {
|
|
27
34
|
"button": "bootstack.widgets._impl.primitives.button::Button",
|
|
28
35
|
"label": "bootstack.widgets._impl.primitives.label::Label",
|
|
@@ -13,6 +13,7 @@ from bootstack.i18n import MessageCatalog, IntlFormatter
|
|
|
13
13
|
from bootstack.widgets._impl.primitives.spinbox import Spinbox
|
|
14
14
|
from bootstack.widgets._impl.mixins import ValidationMixin
|
|
15
15
|
from bootstack.widgets._impl.mixins.configure_mixin import configure_delegate
|
|
16
|
+
from bootstack.widgets._impl._parts.textentry_part import _UNSET
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class SpinnerEntryPart(ValidationMixin, Spinbox):
|
|
@@ -307,16 +308,17 @@ class SpinnerEntryPart(ValidationMixin, Spinbox):
|
|
|
307
308
|
"""Remove callback from <<Change>> event."""
|
|
308
309
|
self.unbind('<<Change>>', bind_id)
|
|
309
310
|
|
|
310
|
-
def value(self, value=
|
|
311
|
+
def value(self, value=_UNSET):
|
|
311
312
|
"""Get or set the parsed/committed value.
|
|
312
313
|
|
|
313
314
|
Args:
|
|
314
|
-
value: If provided, sets the display text and internal value with
|
|
315
|
+
value: If provided, sets the display text and internal value with
|
|
316
|
+
formatting. Pass `None` to clear the field.
|
|
315
317
|
|
|
316
318
|
Returns:
|
|
317
319
|
Current parsed value if no argument provided, None otherwise
|
|
318
320
|
"""
|
|
319
|
-
if value is
|
|
321
|
+
if value is _UNSET:
|
|
320
322
|
return self._value
|
|
321
323
|
else:
|
|
322
324
|
# Store the value and format it for display
|
|
@@ -7,6 +7,11 @@ from bootstack.widgets._impl.primitives.entry import Entry
|
|
|
7
7
|
from bootstack.widgets._impl.mixins import ValidationMixin
|
|
8
8
|
from bootstack.widgets._impl.mixins.configure_mixin import configure_delegate
|
|
9
9
|
|
|
10
|
+
#: Sentinel marking "no argument passed" for the combined get/set accessors below.
|
|
11
|
+
#: `None` cannot serve as that sentinel — it is a real value meaning "empty", and
|
|
12
|
+
#: using it to mean "get" made `value(None)` a silent no-op instead of clearing.
|
|
13
|
+
_UNSET: Any = object()
|
|
14
|
+
|
|
10
15
|
|
|
11
16
|
class TextEntryPart(ValidationMixin, Entry):
|
|
12
17
|
"""Internationalization-aware entry widget with deferred parsing and formatting.
|
|
@@ -250,16 +255,17 @@ class TextEntryPart(ValidationMixin, Entry):
|
|
|
250
255
|
"""Unbind from `<<Change>>`."""
|
|
251
256
|
self.unbind('<<Change>>', bind_id)
|
|
252
257
|
|
|
253
|
-
def value(self, value=
|
|
258
|
+
def value(self, value=_UNSET):
|
|
254
259
|
"""Get or set the parsed/committed value.
|
|
255
260
|
|
|
256
261
|
Args:
|
|
257
|
-
value: If provided, sets the display text and internal value with
|
|
262
|
+
value: If provided, sets the display text and internal value with
|
|
263
|
+
formatting. Pass `None` to clear the field.
|
|
258
264
|
|
|
259
265
|
Returns:
|
|
260
266
|
Current parsed value if no argument provided, None otherwise
|
|
261
267
|
"""
|
|
262
|
-
if value is
|
|
268
|
+
if value is _UNSET:
|
|
263
269
|
return self._value
|
|
264
270
|
else:
|
|
265
271
|
# Store the value and format it for display
|
|
@@ -269,6 +269,31 @@ class DateEntry(Field):
|
|
|
269
269
|
self.date_picker_button.pack_forget()
|
|
270
270
|
return None
|
|
271
271
|
|
|
272
|
+
def _apply_picked(self, val) -> None:
|
|
273
|
+
"""Apply a value chosen in the picker and announce it as a user commit.
|
|
274
|
+
|
|
275
|
+
Choosing a date is a commit, exactly like typing one and pressing
|
|
276
|
+
Return, so it has to emit `<<Change>>` — bound signals, `on_change`
|
|
277
|
+
handlers, and form change tracking all listen for that event. Assigning
|
|
278
|
+
`value` alone does not emit it: the event comes from the entry's own
|
|
279
|
+
change detection, which otherwise only runs on FocusOut and Return.
|
|
280
|
+
|
|
281
|
+
`_check_if_changed` also advances the entry's last-seen value, so a
|
|
282
|
+
later focus-out cannot fire a second `<<Change>>` for this same
|
|
283
|
+
selection — which hand-building the event here would allow whenever the
|
|
284
|
+
entry already held focus. It is safe to call twice: the second call
|
|
285
|
+
sees no difference and does nothing.
|
|
286
|
+
|
|
287
|
+
Range mode is left alone; `_set_range` already emits its own event.
|
|
288
|
+
"""
|
|
289
|
+
if self._selection_mode == "range":
|
|
290
|
+
self.value = val
|
|
291
|
+
return
|
|
292
|
+
if isinstance(val, datetime):
|
|
293
|
+
val = val.date()
|
|
294
|
+
self.value = val
|
|
295
|
+
self._entry._check_if_changed()
|
|
296
|
+
|
|
272
297
|
def _show_date_picker(self):
|
|
273
298
|
"""Open the calendar picker dialog.
|
|
274
299
|
|
|
@@ -336,10 +361,10 @@ class DateEntry(Field):
|
|
|
336
361
|
result = payload.get('result') if isinstance(payload, dict) else payload
|
|
337
362
|
if self._selection_mode == "range":
|
|
338
363
|
if isinstance(result, tuple) and len(result) == 2:
|
|
339
|
-
self.
|
|
364
|
+
self._apply_picked(result)
|
|
340
365
|
else:
|
|
341
366
|
if isinstance(result, (date, datetime)):
|
|
342
|
-
self.
|
|
367
|
+
self._apply_picked(result)
|
|
343
368
|
|
|
344
369
|
dialog.on_result(lambda x: _on_result(x))
|
|
345
370
|
dialog.show(
|
|
@@ -366,12 +391,10 @@ class DateEntry(Field):
|
|
|
366
391
|
selected = dialog.result
|
|
367
392
|
if self._selection_mode == "range":
|
|
368
393
|
if isinstance(selected, tuple) and len(selected) == 2:
|
|
369
|
-
self.
|
|
394
|
+
self._apply_picked(selected)
|
|
370
395
|
else:
|
|
371
|
-
if isinstance(selected, datetime):
|
|
372
|
-
|
|
373
|
-
if isinstance(selected, date):
|
|
374
|
-
self.value = selected
|
|
396
|
+
if isinstance(selected, (date, datetime)):
|
|
397
|
+
self._apply_picked(selected)
|
|
375
398
|
|
|
376
399
|
# Return focus to the entry after dialog closes
|
|
377
400
|
# Note: focus_force() is needed on Windows after override-redirect dialogs
|