bootstack 0.1.8__tar.gz → 0.2.1__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.8/src/bootstack.egg-info → bootstack-0.2.1}/PKG-INFO +1 -1
- {bootstack-0.1.8 → bootstack-0.2.1}/pyproject.toml +2 -2
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/base_window.py +17 -2
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/events.py +190 -9
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/shortcuts.py +10 -2
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/utility.py +22 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/colorchooser.py +19 -23
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/datedialog.py +15 -27
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/dialog.py +52 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/message.py +18 -22
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/query.py +15 -22
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/errors.py +12 -0
- bootstack-0.2.1/src/bootstack/events/_subscription.py +60 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/base.py +65 -15
- bootstack-0.2.1/src/bootstack/widgets/_core/choices.py +56 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/container.py +57 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/field_mixin.py +7 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_parts/numberentry_part.py +7 -2
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_parts/spinnerentry_part.py +5 -3
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_parts/textentry_part.py +9 -3
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/dateentry.py +30 -7
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/field.py +7 -1
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/form.py +16 -4
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/layout.py +33 -28
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/shell.py +8 -4
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/flexframe.py +74 -15
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/appshell.py +12 -9
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/calendar.py +2 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/card.py +4 -11
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/codeeditor.py +9 -33
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/datatable.py +4 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/datefield.py +9 -34
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/expander.py +7 -21
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/form.py +5 -1
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/gallery.py +2 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/groupbox.py +4 -11
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/listview.py +2 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/numberfield.py +7 -33
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/pagestack.py +4 -11
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/passwordfield.py +7 -33
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/pathfield.py +9 -33
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/scrollview.py +3 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/select.py +15 -34
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/sidebar_toggle.py +2 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/spinnerfield.py +7 -33
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/splitview.py +4 -11
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/stacks.py +16 -7
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/tabs.py +6 -32
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/textarea.py +9 -33
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/textfield.py +7 -33
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/timefield.py +7 -34
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/togglegroup.py +2 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/tree.py +2 -0
- {bootstack-0.1.8 → bootstack-0.2.1/src/bootstack.egg-info}/PKG-INFO +1 -1
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack.egg-info/SOURCES.txt +1 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_public_surface.py +1 -1
- bootstack-0.1.8/src/bootstack/events/_subscription.py +0 -38
- {bootstack-0.1.8 → bootstack-0.2.1}/LICENSE +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/MANIFEST.in +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/NOTICE +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/README.md +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/setup.cfg +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/__main__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/after.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/bind.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/bindtags.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/busy.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/clipboard.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/focus.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/grab.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/grid.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/localization.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/pack.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/place.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/selection.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/signals.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/capabilities/winfo.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/colorutils.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/exceptions.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/images.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/mixins/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/mixins/ttk_state.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/mixins/widget.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/paths.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/publisher.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_core/variables.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/app.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/tk_patch.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/toplevel.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/visual_focus.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/wheel.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/_runtime/window_utilities.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/bootstack.ico +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/bootstack.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/badge-pill.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/badge-square.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/button-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/button-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/card.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/checkbox-checked.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/checkbox-indeterminate.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/checkbox-unchecked.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/field.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/input-addon-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/input-addon-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/input-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/input-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/list-item-separated.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/list-item.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/listrow-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/listrow-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/manifest.toml +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/menu-item.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/navitem-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/navitem-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/progressbar-h-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/progressbar-h-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/progressbar-v-compact.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/progressbar-v-default.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/radiobutton.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/scrollbar-horizontal.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/scrollbar-vertical.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/slider-handle.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/slider-track-h.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/slider-track-v.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/switch-off.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/switch-on.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/tab-h.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/elements/tab-v.png +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/icons/bootstrap.ttf +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/icons/glyphmap.json +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/icons/icon_metrics.json +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/__main__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/add.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/appicon.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/build.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/config.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/demo.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/dev.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/doctor.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/icons.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/promote.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/pyinstaller.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/run.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/start.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/cli/templates/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/clipboard.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/constants.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/README.md +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/_observable.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/base.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/file_source.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/memory_source.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/query.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/readers.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/sqlite_source.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/types.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/data/writers.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/_capture.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/_env.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/_registry.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/_reloader.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/_reset.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dev/_watcher.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/filterdialog.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/fontdialog.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/dialogs/_impl/formdialog.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/events/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/events/_event.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/events/_payloads.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/i18n/README.md +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/i18n/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/i18n/catalog.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/i18n/intl_format.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/i18n/msgcat.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/i18n/specs.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/images.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/py.typed +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/scheduling/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/scheduling/_schedule.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/shortcuts.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/signals/README.md +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/signals/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/signals/integration.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/signals/signal.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/signals/types.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/store.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/streams/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/streams/_stream.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/badge.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/button.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/buttongroup.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/calendar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/checkbutton.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/combobox.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/contextmenu.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/entry.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/expander.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/field.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/frame.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/label.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/labelframe.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/listview.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/menubar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/menubutton.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/panedwindow.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/progressbar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/radiobutton.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/scale.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/scrollbar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/separator.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/sidenav.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/sizegrip.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/spinbox.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/switch.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/tabitem.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/togglegroup.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/toolbutton.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/tooltip.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/treeview.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders/utils.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders_tk/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/builders_tk/defaults.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/element.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/fonts.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/style.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/style_builder_base.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/style_builder_mixed.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/style_builder_tk.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/style_builder_ttk.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/style_resolver.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/theme.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/theme_provider.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/themes/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/tk_patch.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/token_maps.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/types.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/typography.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/style/utility.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/types.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/validation/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/validation/types.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/validation/validation_result.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/validation/validation_rules.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/app_config.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/context.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/events.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/icon_image_props.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/image_binding.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/kwargs.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/navmodel.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/options.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/selection_group.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/window_controls.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_core/window_menu.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_internal/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_internal/wrapper_base.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/_parts/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/_dateutils.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/_image_fit.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/accordion.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/avatar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/buttongroup.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/calendar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/carousel.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/chart.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/compositeframe.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/contextmenu.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/dropdownbutton.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/expander.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/gallery.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/list/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/list/listitem.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/list/listview.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/menu/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/menu/model.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/menu/render_native.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/menu/render_themed.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/meter.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/numericentry.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/pagestack.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/passwordentry.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/pathentry.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/picture.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/radiogroup.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/scrolledtext.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/scrolledtext.pyi +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/scrollview.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/selectbox.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/content_host.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/nav_panel.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/providers.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/rail.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/shell/workspace.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/sidenav/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/sidenav/header.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/sidenav/separator.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/slider/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/slider/_shared.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/slider/rangeslider.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/slider/slider.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/spinnerentry.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tableview/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tableview/tableview.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tableview/types.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tabs/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tabs/tabitem.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tabs/tabs.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tabs/tabview.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/change.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/codeeditor.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/core.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/decoration.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/diff.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/filter.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/search_overlay.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/sidebar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/style_registry.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/textarea.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textarea/undo.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/textentry.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/timeentry.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/toast.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/toast_stack.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/togglegroup.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/toolbar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tooltip.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/source_binding.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/treeitem.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/treenode.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/composites/tree/treeview.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/configure_mixin.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/entry_mixin.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/font_mixin.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/icon_mixin.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/localization_mixin.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/signal_mixin.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/mixins/validation_mixin.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/__init__.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/_menubutton.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/badge.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/button.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/card.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/checkbutton.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/checktoggle.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/combobox.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/entry.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/frame.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/gridframe.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/label.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/labelframe.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/optionmenu.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/packframe.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/panedwindow.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/progressbar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/radiobutton.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/radiotoggle.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/scrollbar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/separator.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/sizegrip.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/spinbox.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/switch.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/_impl/primitives/treeview.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/app.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/avatar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/boolean_controls.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/button.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/buttongroup.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/carousel.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/chart.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/contextmenu.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/divider.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/gauge.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/grid.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/label.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/menubutton.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/picture.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/progressbar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/radio_variants.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/radiogroup.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/scrollbar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/selectbutton.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/sizegrip.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/slider.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/spinbox.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/splash.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/statusbar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/theme_toggle.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/toast.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/toolbar.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/tooltip.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/types.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack/widgets/window.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack.egg-info/dependency_links.txt +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack.egg-info/entry_points.txt +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack.egg-info/requires.txt +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/src/bootstack.egg-info/top_level.txt +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_dialog_anchor.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_events_doc_coverage.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_hot_reload.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_images.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_images_gui.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_intl_format.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_navmodel.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_on_color_contrast.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_store.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_theme_from_existing.py +0 -0
- {bootstack-0.1.8 → bootstack-0.2.1}/tests/test_validation_rules.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bootstack
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
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.1
|
|
7
|
+
version = "0.2.1"
|
|
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.1
|
|
117
|
+
current_version = "0.2.1"
|
|
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}",
|
|
@@ -34,14 +34,29 @@ def on_visibility_alpha(event: tkinter.Event) -> None:
|
|
|
34
34
|
X11 requires alpha to be set after the window is visible, so we bind
|
|
35
35
|
to the <Visibility> event and set alpha then, unbinding after first use.
|
|
36
36
|
|
|
37
|
+
Being woken by this event at all is what proves the precondition: X11
|
|
38
|
+
reports visibility only for a window that is already on screen, so the very
|
|
39
|
+
first delivery is a valid moment to apply alpha. Whether that first report
|
|
40
|
+
says obscured or unobscured makes no difference — opacity is a property of
|
|
41
|
+
the window, not of how much of it happens to be uncovered.
|
|
42
|
+
|
|
43
|
+
Alpha is therefore applied before the binding is released, so first use is
|
|
44
|
+
genuinely the last one needed. Applying first also means that if the request
|
|
45
|
+
itself fails, the binding is still in place and the next visibility change
|
|
46
|
+
tries again — the one failure where a retry could help (#398).
|
|
47
|
+
|
|
37
48
|
Args:
|
|
38
49
|
event: The visibility event containing the widget.
|
|
39
50
|
"""
|
|
40
51
|
widget = event.widget
|
|
41
52
|
if hasattr(widget, 'alpha') and hasattr(widget, 'alpha_bind'):
|
|
42
|
-
if widget.alpha_bind:
|
|
43
|
-
widget.unbind(widget.alpha_bind)
|
|
44
53
|
widget.attributes("-alpha", widget.alpha)
|
|
54
|
+
if widget.alpha_bind:
|
|
55
|
+
# `alpha_bind` is the id `bind` returned, so it is the *funcid*
|
|
56
|
+
# argument — passing it as the sequence removes nothing at all and
|
|
57
|
+
# raises nothing either, which is how this stayed unnoticed (#398).
|
|
58
|
+
widget.unbind("<Visibility>", widget.alpha_bind)
|
|
59
|
+
widget.alpha_bind = None
|
|
45
60
|
|
|
46
61
|
|
|
47
62
|
class BaseWindow:
|
|
@@ -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,126 @@ 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
|
+
except tk.TclError:
|
|
358
|
+
# Reading the script is the one step whose failure is expected: the
|
|
359
|
+
# widget is gone, and its bindings and commands went with it. Nothing
|
|
360
|
+
# to remove and nothing to leak.
|
|
361
|
+
return
|
|
362
|
+
|
|
363
|
+
lines = str(script).split('\n')
|
|
364
|
+
# Must match what `_patched_bind` emits (and stock `Misc._bind`).
|
|
365
|
+
prefix = 'if {"[%s ' % funcid
|
|
366
|
+
keep = [line for line in lines if not line.startswith(prefix)]
|
|
367
|
+
if len(keep) == len(lines):
|
|
368
|
+
# This funcid is not bound to this widget and sequence, so it is
|
|
369
|
+
# not ours to delete — the live binding may be somewhere else, and
|
|
370
|
+
# deleting its command would orphan it. Leave everything alone.
|
|
371
|
+
#
|
|
372
|
+
# Correct, but it means the command and its closure survive for the
|
|
373
|
+
# life of the interpreter, invisibly. Surface it in development so the
|
|
374
|
+
# drift that produced it can be found (#399).
|
|
375
|
+
#
|
|
376
|
+
# Reported, not warned. This runs inside event dispatch and on the
|
|
377
|
+
# teardown path behind `Subscription.cancel()` and its `__exit__`, both
|
|
378
|
+
# documented as safe to call on a dead or already-removed handler. A
|
|
379
|
+
# `warnings.warn` here becomes an exception out of both under
|
|
380
|
+
# `-W error`, which is a worse outcome than the leak it describes.
|
|
381
|
+
from bootstack._runtime.utility import debug_log
|
|
382
|
+
debug_log(
|
|
383
|
+
f"unbind({sequence!r}, {funcid!r}) on {self._w!r} matched no "
|
|
384
|
+
"binding, so its Tcl command cannot safely be released and will "
|
|
385
|
+
"leak. The usual cause is an unbind that targeted a different "
|
|
386
|
+
"widget than the bind did; hold the Subscription that bind() "
|
|
387
|
+
"returned instead of re-deriving the target."
|
|
388
|
+
)
|
|
389
|
+
return
|
|
390
|
+
|
|
391
|
+
remaining = '\n'.join(keep)
|
|
392
|
+
# Deliberately NOT guarded: if the rewrite fails the handler is still bound
|
|
393
|
+
# and still firing, and swallowing that would report a successful
|
|
394
|
+
# cancellation for a live handler — a silent wrong answer (#400).
|
|
395
|
+
self.tk.call(*what, remaining if remaining.strip() else '')
|
|
396
|
+
|
|
397
|
+
try:
|
|
398
|
+
# Replace the command rather than deleting it, so an in-flight copy of
|
|
399
|
+
# the concatenated script has something harmless to call.
|
|
400
|
+
self.tk.createcommand(funcid, lambda *args: '')
|
|
401
|
+
except tk.TclError:
|
|
402
|
+
# The binding line is already gone, so the handler is genuinely removed
|
|
403
|
+
# and an in-flight script would find the original command rather than a
|
|
404
|
+
# deleted one. Fall through to the deferred delete instead of returning,
|
|
405
|
+
# which would strand the command forever (#400).
|
|
406
|
+
pass
|
|
407
|
+
|
|
408
|
+
def _delete_command() -> None:
|
|
409
|
+
try:
|
|
410
|
+
self.deletecommand(funcid)
|
|
411
|
+
except (tk.TclError, AttributeError):
|
|
412
|
+
# Already swept, most likely because the widget was destroyed
|
|
413
|
+
# before the interpreter went idle. `AttributeError` is that same
|
|
414
|
+
# case seen from the other side: destroy() clears the widget's
|
|
415
|
+
# command bookkeeping, which `deletecommand` then cannot update.
|
|
416
|
+
pass
|
|
417
|
+
|
|
418
|
+
try:
|
|
419
|
+
# Deliberately scheduled on the root, not on `self`. Deferred work owned
|
|
420
|
+
# by a widget is torn down with it, and destroying a widget between the
|
|
421
|
+
# cancellation and the next idle point would leave the pending callback
|
|
422
|
+
# referring to a command that destroy() had already deleted — a silent
|
|
423
|
+
# error of exactly the kind this function exists to avoid. The root
|
|
424
|
+
# outlives every widget, and when the root itself goes the pending
|
|
425
|
+
# callback goes with it, which is equally fine: there is nothing left to
|
|
426
|
+
# clean up.
|
|
427
|
+
self._root().after_idle(_delete_command)
|
|
428
|
+
except (tk.TclError, AttributeError):
|
|
429
|
+
# No event loop left to defer onto; nothing can be mid-dispatch either.
|
|
430
|
+
_delete_command()
|
|
431
|
+
|
|
432
|
+
|
|
255
433
|
def cleanup_event_cache() -> None:
|
|
256
434
|
"""Manually clear the entire event data cache.
|
|
257
435
|
|
|
@@ -342,9 +520,10 @@ def get_cache_size() -> int:
|
|
|
342
520
|
def install_enhanced_events() -> None:
|
|
343
521
|
"""Install the enhanced event system by patching tkinter methods.
|
|
344
522
|
|
|
345
|
-
This function applies monkey patches to tkinter's Misc class,
|
|
346
|
-
|
|
347
|
-
|
|
523
|
+
This function applies monkey patches to tkinter's Misc class, replacing
|
|
524
|
+
event_generate and bind with enhanced versions that support passing custom
|
|
525
|
+
data through virtual events, and unbind with a version that cannot disturb
|
|
526
|
+
the other handlers for an event while one is being removed.
|
|
348
527
|
|
|
349
528
|
The patches are applied globally and affect all tkinter widgets
|
|
350
529
|
in the application. This function is called automatically when
|
|
@@ -364,6 +543,7 @@ def install_enhanced_events() -> None:
|
|
|
364
543
|
"""
|
|
365
544
|
tk.Misc.event_generate = _patched_event_generate
|
|
366
545
|
tk.Misc.bind = _patched_bind
|
|
546
|
+
tk.Misc.unbind = _patched_unbind
|
|
367
547
|
|
|
368
548
|
|
|
369
549
|
def uninstall_enhanced_events() -> None:
|
|
@@ -387,6 +567,7 @@ def uninstall_enhanced_events() -> None:
|
|
|
387
567
|
"""
|
|
388
568
|
tk.Misc.event_generate = _original_event_generate
|
|
389
569
|
tk.Misc.bind = _original_bind
|
|
570
|
+
tk.Misc.unbind = _original_unbind
|
|
390
571
|
cleanup_event_cache()
|
|
391
572
|
|
|
392
573
|
|
|
@@ -57,13 +57,21 @@ _WIN_NAMES = {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
# Tkinter binding modifiers
|
|
60
|
+
#
|
|
61
|
+
# `command` and `option` are platform-mapped for the same reason `mod` and `alt`
|
|
62
|
+
# are. Off macOS the toolkit resolves the words "Command" and "Option" to its
|
|
63
|
+
# generic Mod1 and Mod2 slots, and Windows reports NumLock as Mod1 — so a
|
|
64
|
+
# `Command+S` binding fired on a bare `s` for anyone with NumLock switched on,
|
|
65
|
+
# while the menu beside it read "Ctrl+S". Naming the modifier the display has
|
|
66
|
+
# always claimed keeps the two from disagreeing (#405, and the same trap as
|
|
67
|
+
# #403).
|
|
60
68
|
_BINDING_MODIFIERS = {
|
|
61
69
|
'mod': 'Command' if IS_MAC else 'Control',
|
|
62
|
-
'command': 'Command',
|
|
70
|
+
'command': 'Command' if IS_MAC else 'Control',
|
|
63
71
|
'ctrl': 'Control',
|
|
64
72
|
'control': 'Control',
|
|
65
73
|
'alt': 'Option' if IS_MAC else 'Alt',
|
|
66
|
-
'option': 'Option',
|
|
74
|
+
'option': 'Option' if IS_MAC else 'Alt',
|
|
67
75
|
'shift': 'Shift',
|
|
68
76
|
}
|
|
69
77
|
|
|
@@ -344,6 +344,28 @@ def _debug_enabled() -> bool:
|
|
|
344
344
|
return str(os.environ.get("BOOTSTACK_DEBUG", "")).lower() in {"1", "true", "yes"}
|
|
345
345
|
|
|
346
346
|
|
|
347
|
+
def debug_log(message: str) -> None:
|
|
348
|
+
"""Print a diagnostic message if debug is enabled.
|
|
349
|
+
|
|
350
|
+
For reporting a condition that has no exception behind it. Use
|
|
351
|
+
`debug_log_exception` when there is one.
|
|
352
|
+
|
|
353
|
+
Never raises, and deliberately does not use `warnings.warn`: these fire from
|
|
354
|
+
inside event dispatch and teardown, where a warning filter set to `error`
|
|
355
|
+
would turn a diagnostic into a failure on a path documented as safe.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
message: The text to print.
|
|
359
|
+
"""
|
|
360
|
+
if not _debug_enabled():
|
|
361
|
+
return
|
|
362
|
+
try:
|
|
363
|
+
print(f"bootstack DEBUG: {message}")
|
|
364
|
+
except Exception:
|
|
365
|
+
# Never raise from debug logging
|
|
366
|
+
pass
|
|
367
|
+
|
|
368
|
+
|
|
347
369
|
def debug_log_exception(message: str = "") -> None:
|
|
348
370
|
"""Print the current exception traceback if debug is enabled.
|
|
349
371
|
|
|
@@ -15,7 +15,10 @@ from PIL import ImageColor
|
|
|
15
15
|
from bootstack.constants import *
|
|
16
16
|
from bootstack._core import colorutils
|
|
17
17
|
from bootstack._core.colorutils import HEX, HSL, HUE, LUM, RGB, SAT
|
|
18
|
-
from bootstack.
|
|
18
|
+
from bootstack.events import Subscription
|
|
19
|
+
from bootstack.dialogs._impl.dialog import (
|
|
20
|
+
DIALOG_RESULT, Dialog, emit_dialog_result, result_target,
|
|
21
|
+
)
|
|
19
22
|
from bootstack.i18n import MessageCatalog
|
|
20
23
|
from bootstack._runtime import utility
|
|
21
24
|
from bootstack.style.style import get_style, get_theme_color
|
|
@@ -554,35 +557,28 @@ class ColorChooserDialog:
|
|
|
554
557
|
if not self._emitted_result:
|
|
555
558
|
self._emit_result(confirmed=self.result is not None)
|
|
556
559
|
|
|
557
|
-
def on_dialog_result(self, callback: Callable[[Any], None]) -> Optional[
|
|
558
|
-
"""Bind a callback fired when the dialog produces a result.
|
|
559
|
-
|
|
560
|
+
def on_dialog_result(self, callback: Callable[[Any], None]) -> "Optional[Subscription]":
|
|
561
|
+
"""Bind a callback fired when the dialog produces a result.
|
|
562
|
+
|
|
563
|
+
Returns:
|
|
564
|
+
A `Subscription`, or `None` if there is no widget to bind to.
|
|
565
|
+
Cancel it with `.cancel()`.
|
|
566
|
+
"""
|
|
567
|
+
target = result_target(self._dialog, self._master)
|
|
560
568
|
if target is None:
|
|
561
569
|
return None
|
|
562
570
|
|
|
563
571
|
def handler(event):
|
|
564
572
|
callback(getattr(event, "data", None))
|
|
565
573
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
"""Unbind a previously bound dialog result callback."""
|
|
570
|
-
target = self._master or self._dialog.toplevel
|
|
571
|
-
if target is None:
|
|
572
|
-
return
|
|
573
|
-
target.unbind("<<DialogResult>>", funcid)
|
|
574
|
+
# The Subscription remembers the widget it bound to — see #397.
|
|
575
|
+
bind_id = target.bind(DIALOG_RESULT, handler, add="+")
|
|
576
|
+
return Subscription(target, DIALOG_RESULT, bind_id)
|
|
574
577
|
|
|
575
578
|
# helpers ------------------------------------------------------------------
|
|
576
579
|
def _emit_result(self, confirmed: bool) -> None:
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
try:
|
|
582
|
-
target.event_generate("<<DialogResult>>", data=payload)
|
|
583
|
-
except Exception:
|
|
584
|
-
try:
|
|
585
|
-
target.event_generate("<<DialogResult>>")
|
|
586
|
-
except Exception:
|
|
587
|
-
pass
|
|
580
|
+
emit_dialog_result(
|
|
581
|
+
result_target(self._dialog, self._master),
|
|
582
|
+
{"result": self.result, "confirmed": confirmed},
|
|
583
|
+
)
|
|
588
584
|
self._emitted_result = True
|
|
@@ -14,7 +14,10 @@ from tkinter import Widget
|
|
|
14
14
|
|
|
15
15
|
from bootstack.widgets._impl.primitives import Frame
|
|
16
16
|
from bootstack.constants import BOTH, YES
|
|
17
|
-
from bootstack.
|
|
17
|
+
from bootstack.events import Subscription
|
|
18
|
+
from bootstack.dialogs._impl.dialog import (
|
|
19
|
+
DIALOG_RESULT, Dialog, DialogButton, emit_dialog_result, result_target,
|
|
20
|
+
)
|
|
18
21
|
from bootstack._runtime.window_utilities import AnchorPoint
|
|
19
22
|
from bootstack.widgets._impl.composites.calendar import Calendar
|
|
20
23
|
|
|
@@ -404,7 +407,7 @@ class DateDialog:
|
|
|
404
407
|
"""The selected date (single mode) or `(start, end)` tuple (range mode), or None if cancelled."""
|
|
405
408
|
return self._dialog.result
|
|
406
409
|
|
|
407
|
-
def on_result(self, callback: Callable[[date], None]) -> Optional[
|
|
410
|
+
def on_result(self, callback: Callable[[date], None]) -> Optional[Subscription]:
|
|
408
411
|
"""Bind a callback fired when a result is produced.
|
|
409
412
|
|
|
410
413
|
The callback receives `event.data["result"]` (a `datetime.date`).
|
|
@@ -413,38 +416,23 @@ class DateDialog:
|
|
|
413
416
|
callback: Callable that receives the selected `datetime.date`.
|
|
414
417
|
|
|
415
418
|
Returns:
|
|
416
|
-
|
|
419
|
+
A `Subscription`, or `None` if there is no widget to bind to.
|
|
420
|
+
Cancel it with `.cancel()`.
|
|
417
421
|
"""
|
|
418
|
-
target = self._dialog
|
|
422
|
+
target = result_target(self._dialog, self._master)
|
|
419
423
|
if target is None:
|
|
420
424
|
return None
|
|
421
425
|
|
|
422
426
|
def handler(event: tkinter.Event) -> None:
|
|
423
427
|
callback(getattr(event, "data", None))
|
|
424
428
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
"""Unbind a previously bound `on_result` callback.
|
|
429
|
-
|
|
430
|
-
Args:
|
|
431
|
-
funcid: Binding identifier returned by `on_result`.
|
|
432
|
-
"""
|
|
433
|
-
target = self._dialog.toplevel or self._master
|
|
434
|
-
if target is None:
|
|
435
|
-
return
|
|
436
|
-
target.unbind("<<DialogResult>>", funcid)
|
|
429
|
+
# The Subscription remembers the widget it bound to — see #397.
|
|
430
|
+
bind_id = target.bind(DIALOG_RESULT, handler, add="+")
|
|
431
|
+
return Subscription(target, DIALOG_RESULT, bind_id)
|
|
437
432
|
|
|
438
433
|
def _emit_result(self, value: date, confirmed: bool) -> None:
|
|
439
434
|
"""Emit a virtual Tk event with the dialog result."""
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
try:
|
|
445
|
-
target.event_generate("<<DialogResult>>", data=payload)
|
|
446
|
-
except Exception:
|
|
447
|
-
try:
|
|
448
|
-
target.event_generate("<<DialogResult>>")
|
|
449
|
-
except Exception:
|
|
450
|
-
pass
|
|
435
|
+
emit_dialog_result(
|
|
436
|
+
result_target(self._dialog, self._master),
|
|
437
|
+
{"result": value, "confirmed": confirmed},
|
|
438
|
+
)
|
|
@@ -127,6 +127,58 @@ class ShowOptions(TypedDict, total=False):
|
|
|
127
127
|
auto_flip: Union[bool, Literal['vertical', 'horizontal']]
|
|
128
128
|
|
|
129
129
|
|
|
130
|
+
# --- Dialog result delivery -------------------------------------------------
|
|
131
|
+
|
|
132
|
+
DIALOG_RESULT = "<<DialogResult>>"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def result_target(dialog: "Dialog", master: Optional[tkinter.Misc]) -> Optional[tkinter.Misc]:
|
|
136
|
+
"""The widget `<<DialogResult>>` is bound on and fired from.
|
|
137
|
+
|
|
138
|
+
Binding and firing both route through here so the two cannot resolve
|
|
139
|
+
differently. That mattered: `Dialog.toplevel` is `None` before `show()`
|
|
140
|
+
and a *destroyed* widget after it, because nothing resets `_toplevel`
|
|
141
|
+
when the modal wait ends. A target derived from it is therefore never
|
|
142
|
+
live at both ends — subscribers bound to the master while the result
|
|
143
|
+
fired at a dead toplevel, where `event_generate` raised and the error
|
|
144
|
+
was swallowed (#397).
|
|
145
|
+
|
|
146
|
+
`master` comes first for that reason: it outlives the dialog.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
dialog: The dialog producing the result.
|
|
150
|
+
master: The dialog's master widget, if it has one.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
A live widget, or `None` when there is nothing left to fire on.
|
|
154
|
+
"""
|
|
155
|
+
if master is not None:
|
|
156
|
+
return master
|
|
157
|
+
top = dialog.toplevel
|
|
158
|
+
if top is not None and top.winfo_exists():
|
|
159
|
+
return top
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def emit_dialog_result(target: Optional[tkinter.Misc], payload: dict) -> None:
|
|
164
|
+
"""Fire `<<DialogResult>>` on `target` with `payload` attached.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
target: The widget from `result_target`, or `None` to do nothing.
|
|
168
|
+
payload: Mapping with `result` and `confirmed` keys.
|
|
169
|
+
"""
|
|
170
|
+
if target is None:
|
|
171
|
+
return
|
|
172
|
+
try:
|
|
173
|
+
target.event_generate(DIALOG_RESULT, data=payload)
|
|
174
|
+
except tkinter.TclError:
|
|
175
|
+
# `result_target` hands back a live widget, so reaching here means it
|
|
176
|
+
# was destroyed in between. Nothing can receive the result; surface it
|
|
177
|
+
# in development rather than letting it vanish the way it used to.
|
|
178
|
+
from bootstack._runtime.utility import debug_log_exception
|
|
179
|
+
debug_log_exception(f"could not deliver {DIALOG_RESULT}")
|
|
180
|
+
|
|
181
|
+
|
|
130
182
|
# --- Dialog ----------------------------------------------------------------
|
|
131
183
|
|
|
132
184
|
class Dialog:
|
|
@@ -10,7 +10,10 @@ from bootstack.widgets._impl.primitives import Frame, Label
|
|
|
10
10
|
from bootstack._core.images import _ImageService
|
|
11
11
|
from bootstack.constants import *
|
|
12
12
|
from bootstack.i18n import MessageCatalog
|
|
13
|
-
from .
|
|
13
|
+
from bootstack.events import Subscription
|
|
14
|
+
from .dialog import (
|
|
15
|
+
DIALOG_RESULT, ButtonRole, Dialog, DialogButton, emit_dialog_result, result_target,
|
|
16
|
+
)
|
|
14
17
|
|
|
15
18
|
ttk = SimpleNamespace(Frame=Frame, Label=Label)
|
|
16
19
|
|
|
@@ -198,23 +201,17 @@ class MessageDialog:
|
|
|
198
201
|
position: x and y coordinates to position the dialog. If None, centers on parent.
|
|
199
202
|
"""
|
|
200
203
|
self._dialog.show(position=position, modal=True)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
target.event_generate("<<DialogResult>>", data=payload)
|
|
206
|
-
except Exception:
|
|
207
|
-
try:
|
|
208
|
-
target.event_generate("<<DialogResult>>")
|
|
209
|
-
except Exception:
|
|
210
|
-
pass
|
|
204
|
+
emit_dialog_result(
|
|
205
|
+
result_target(self._dialog, self._master),
|
|
206
|
+
{"result": self._dialog.result, "confirmed": self._dialog.result is not None},
|
|
207
|
+
)
|
|
211
208
|
|
|
212
209
|
@property
|
|
213
210
|
def result(self) -> Any:
|
|
214
211
|
"""The dialog result value (the text of the button pressed)."""
|
|
215
212
|
return self._dialog.result
|
|
216
213
|
|
|
217
|
-
def on_dialog_result(self, callback: Callable[[Any], None]) -> Optional[
|
|
214
|
+
def on_dialog_result(self, callback: Callable[[Any], None]) -> "Optional[Subscription]":
|
|
218
215
|
"""Bind a callback fired when the dialog produces a result.
|
|
219
216
|
|
|
220
217
|
The callback receives `event.data["result"]` when available.
|
|
@@ -223,23 +220,22 @@ class MessageDialog:
|
|
|
223
220
|
callback: Callable that receives the result payload.
|
|
224
221
|
|
|
225
222
|
Returns:
|
|
226
|
-
|
|
223
|
+
A `Subscription`, or `None` if there is no widget to bind to.
|
|
224
|
+
Cancel it with `.cancel()`.
|
|
227
225
|
"""
|
|
228
|
-
target = self._dialog
|
|
226
|
+
target = result_target(self._dialog, self._master)
|
|
229
227
|
if target is None:
|
|
230
228
|
return None
|
|
231
229
|
|
|
232
230
|
def handler(event):
|
|
233
231
|
callback(getattr(event, "data", None))
|
|
234
232
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return
|
|
242
|
-
target.unbind("<<DialogResult>>", funcid)
|
|
233
|
+
# The Subscription remembers the widget it bound to. Resolving the
|
|
234
|
+
# target a second time to unbind would not: `_dialog.toplevel` is None
|
|
235
|
+
# until show(), so subscribing before and cancelling after resolved to
|
|
236
|
+
# two different widgets and the removal silently matched nothing (#397).
|
|
237
|
+
bind_id = target.bind(DIALOG_RESULT, handler, add="+")
|
|
238
|
+
return Subscription(target, DIALOG_RESULT, bind_id)
|
|
243
239
|
|
|
244
240
|
|
|
245
241
|
class MessageBox:
|