bootstack 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bootstack/__init__.py +157 -0
- bootstack/__main__.py +5 -0
- bootstack/_core/__init__.py +21 -0
- bootstack/_core/capabilities/__init__.py +45 -0
- bootstack/_core/capabilities/after.py +103 -0
- bootstack/_core/capabilities/bind.py +192 -0
- bootstack/_core/capabilities/bindtags.py +112 -0
- bootstack/_core/capabilities/busy.py +72 -0
- bootstack/_core/capabilities/clipboard.py +89 -0
- bootstack/_core/capabilities/focus.py +118 -0
- bootstack/_core/capabilities/grab.py +65 -0
- bootstack/_core/capabilities/grid.py +211 -0
- bootstack/_core/capabilities/localization.py +231 -0
- bootstack/_core/capabilities/pack.py +140 -0
- bootstack/_core/capabilities/place.py +113 -0
- bootstack/_core/capabilities/selection.py +136 -0
- bootstack/_core/capabilities/signals.py +244 -0
- bootstack/_core/capabilities/winfo.py +315 -0
- bootstack/_core/colorutils.py +234 -0
- bootstack/_core/exceptions.py +25 -0
- bootstack/_core/images.py +463 -0
- bootstack/_core/mixins/__init__.py +1 -0
- bootstack/_core/mixins/ttk_state.py +35 -0
- bootstack/_core/mixins/widget.py +132 -0
- bootstack/_core/paths.py +49 -0
- bootstack/_core/publisher.py +149 -0
- bootstack/_core/variables.py +62 -0
- bootstack/_runtime/__init__.py +3 -0
- bootstack/_runtime/app.py +930 -0
- bootstack/_runtime/base_window.py +945 -0
- bootstack/_runtime/events.py +399 -0
- bootstack/_runtime/shortcuts.py +496 -0
- bootstack/_runtime/tk_patch.py +43 -0
- bootstack/_runtime/toplevel.py +276 -0
- bootstack/_runtime/utility.py +457 -0
- bootstack/_runtime/visual_focus.py +240 -0
- bootstack/_runtime/window_utilities.py +1123 -0
- bootstack/assets/__init__.py +21 -0
- bootstack/assets/bootstack.ico +0 -0
- bootstack/assets/bootstack.png +0 -0
- bootstack/assets/elements/__init__.py +0 -0
- bootstack/assets/elements/badge-pill.png +0 -0
- bootstack/assets/elements/badge-square.png +0 -0
- bootstack/assets/elements/button-compact.png +0 -0
- bootstack/assets/elements/button-default.png +0 -0
- bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
- bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
- bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
- bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
- bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
- bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
- bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
- bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
- bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
- bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
- bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
- bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
- bootstack/assets/elements/card.png +0 -0
- bootstack/assets/elements/checkbox-checked.png +0 -0
- bootstack/assets/elements/checkbox-indeterminate.png +0 -0
- bootstack/assets/elements/checkbox-unchecked.png +0 -0
- bootstack/assets/elements/field.png +0 -0
- bootstack/assets/elements/input-addon-compact.png +0 -0
- bootstack/assets/elements/input-addon-default.png +0 -0
- bootstack/assets/elements/input-compact.png +0 -0
- bootstack/assets/elements/input-default.png +0 -0
- bootstack/assets/elements/list-item-separated.png +0 -0
- bootstack/assets/elements/list-item.png +0 -0
- bootstack/assets/elements/listrow-compact.png +0 -0
- bootstack/assets/elements/listrow-default.png +0 -0
- bootstack/assets/elements/manifest.toml +361 -0
- bootstack/assets/elements/menu-item.png +0 -0
- bootstack/assets/elements/navitem-compact.png +0 -0
- bootstack/assets/elements/navitem-default.png +0 -0
- bootstack/assets/elements/progressbar-h-compact.png +0 -0
- bootstack/assets/elements/progressbar-h-default.png +0 -0
- bootstack/assets/elements/progressbar-v-compact.png +0 -0
- bootstack/assets/elements/progressbar-v-default.png +0 -0
- bootstack/assets/elements/radiobutton.png +0 -0
- bootstack/assets/elements/scrollbar-horizontal.png +0 -0
- bootstack/assets/elements/scrollbar-vertical.png +0 -0
- bootstack/assets/elements/slider-handle.png +0 -0
- bootstack/assets/elements/slider-track-h.png +0 -0
- bootstack/assets/elements/slider-track-v.png +0 -0
- bootstack/assets/elements/switch-off.png +0 -0
- bootstack/assets/elements/switch-on.png +0 -0
- bootstack/assets/elements/tab-h.png +0 -0
- bootstack/assets/elements/tab-v.png +0 -0
- bootstack/assets/icons/bootstrap.ttf +0 -0
- bootstack/assets/icons/glyphmap.json +2080 -0
- bootstack/assets/icons/icon_metrics.json +12470 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +878 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +878 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +856 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +854 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +844 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +917 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +844 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +844 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +845 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +845 -0
- bootstack/cli/__init__.py +133 -0
- bootstack/cli/__main__.py +6 -0
- bootstack/cli/add.py +395 -0
- bootstack/cli/appicon.py +285 -0
- bootstack/cli/build.py +115 -0
- bootstack/cli/config.py +313 -0
- bootstack/cli/demo.py +564 -0
- bootstack/cli/dev.py +153 -0
- bootstack/cli/doctor.py +195 -0
- bootstack/cli/icons.py +98 -0
- bootstack/cli/promote.py +120 -0
- bootstack/cli/pyinstaller.py +268 -0
- bootstack/cli/run.py +95 -0
- bootstack/cli/start.py +117 -0
- bootstack/cli/templates/__init__.py +931 -0
- bootstack/clipboard.py +48 -0
- bootstack/constants.py +318 -0
- bootstack/data/README.md +615 -0
- bootstack/data/__init__.py +78 -0
- bootstack/data/_observable.py +276 -0
- bootstack/data/base.py +780 -0
- bootstack/data/file_source.py +367 -0
- bootstack/data/memory_source.py +450 -0
- bootstack/data/query.py +367 -0
- bootstack/data/readers.py +289 -0
- bootstack/data/sqlite_source.py +869 -0
- bootstack/data/types.py +354 -0
- bootstack/data/writers.py +232 -0
- bootstack/dev/__init__.py +36 -0
- bootstack/dev/_capture.py +141 -0
- bootstack/dev/_env.py +43 -0
- bootstack/dev/_registry.py +140 -0
- bootstack/dev/_reloader.py +351 -0
- bootstack/dev/_reset.py +50 -0
- bootstack/dev/_watcher.py +91 -0
- bootstack/dialogs/__init__.py +948 -0
- bootstack/dialogs/_impl/__init__.py +47 -0
- bootstack/dialogs/_impl/colorchooser.py +588 -0
- bootstack/dialogs/_impl/datedialog.py +450 -0
- bootstack/dialogs/_impl/dialog.py +594 -0
- bootstack/dialogs/_impl/filterdialog.py +358 -0
- bootstack/dialogs/_impl/fontdialog.py +364 -0
- bootstack/dialogs/_impl/formdialog.py +564 -0
- bootstack/dialogs/_impl/message.py +486 -0
- bootstack/dialogs/_impl/query.py +570 -0
- bootstack/errors.py +67 -0
- bootstack/events/__init__.py +111 -0
- bootstack/events/_event.py +135 -0
- bootstack/events/_payloads.py +539 -0
- bootstack/events/_subscription.py +38 -0
- bootstack/i18n/README.md +77 -0
- bootstack/i18n/__init__.py +23 -0
- bootstack/i18n/catalog.py +121 -0
- bootstack/i18n/intl_format.py +584 -0
- bootstack/i18n/msgcat.py +425 -0
- bootstack/i18n/specs.py +156 -0
- bootstack/images.py +563 -0
- bootstack/py.typed +1 -0
- bootstack/scheduling/__init__.py +11 -0
- bootstack/scheduling/_schedule.py +218 -0
- bootstack/shortcuts.py +21 -0
- bootstack/signals/README.md +98 -0
- bootstack/signals/__init__.py +10 -0
- bootstack/signals/integration.py +100 -0
- bootstack/signals/signal.py +353 -0
- bootstack/signals/types.py +6 -0
- bootstack/store.py +286 -0
- bootstack/streams/__init__.py +12 -0
- bootstack/streams/_stream.py +321 -0
- bootstack/style/__init__.py +38 -0
- bootstack/style/builders/__init__.py +51 -0
- bootstack/style/builders/badge.py +46 -0
- bootstack/style/builders/button.py +339 -0
- bootstack/style/builders/buttongroup.py +311 -0
- bootstack/style/builders/calendar.py +271 -0
- bootstack/style/builders/checkbutton.py +110 -0
- bootstack/style/builders/combobox.py +113 -0
- bootstack/style/builders/contextmenu.py +268 -0
- bootstack/style/builders/entry.py +82 -0
- bootstack/style/builders/expander.py +148 -0
- bootstack/style/builders/field.py +335 -0
- bootstack/style/builders/frame.py +50 -0
- bootstack/style/builders/label.py +28 -0
- bootstack/style/builders/labelframe.py +34 -0
- bootstack/style/builders/listview.py +369 -0
- bootstack/style/builders/menubar.py +91 -0
- bootstack/style/builders/menubutton.py +359 -0
- bootstack/style/builders/panedwindow.py +25 -0
- bootstack/style/builders/progressbar.py +67 -0
- bootstack/style/builders/radiobutton.py +99 -0
- bootstack/style/builders/scale.py +64 -0
- bootstack/style/builders/scrollbar.py +225 -0
- bootstack/style/builders/separator.py +49 -0
- bootstack/style/builders/sidenav.py +643 -0
- bootstack/style/builders/sizegrip.py +15 -0
- bootstack/style/builders/spinbox.py +119 -0
- bootstack/style/builders/switch.py +70 -0
- bootstack/style/builders/tabitem.py +204 -0
- bootstack/style/builders/togglegroup.py +294 -0
- bootstack/style/builders/toolbutton.py +275 -0
- bootstack/style/builders/tooltip.py +26 -0
- bootstack/style/builders/treeview.py +193 -0
- bootstack/style/builders/utils.py +455 -0
- bootstack/style/builders_tk/__init__.py +16 -0
- bootstack/style/builders_tk/defaults.py +229 -0
- bootstack/style/element.py +173 -0
- bootstack/style/fonts.py +123 -0
- bootstack/style/style.py +609 -0
- bootstack/style/style_builder_base.py +716 -0
- bootstack/style/style_builder_mixed.py +93 -0
- bootstack/style/style_builder_tk.py +109 -0
- bootstack/style/style_builder_ttk.py +353 -0
- bootstack/style/style_resolver.py +447 -0
- bootstack/style/theme.py +245 -0
- bootstack/style/theme_provider.py +471 -0
- bootstack/style/themes/__init__.py +128 -0
- bootstack/style/tk_patch.py +5 -0
- bootstack/style/token_maps.py +41 -0
- bootstack/style/types.py +32 -0
- bootstack/style/typography.py +523 -0
- bootstack/style/utility.py +746 -0
- bootstack/types.py +39 -0
- bootstack/validation/__init__.py +6 -0
- bootstack/validation/types.py +15 -0
- bootstack/validation/validation_result.py +17 -0
- bootstack/validation/validation_rules.py +205 -0
- bootstack/widgets/__init__.py +74 -0
- bootstack/widgets/_core/__init__.py +31 -0
- bootstack/widgets/_core/app_config.py +266 -0
- bootstack/widgets/_core/base.py +461 -0
- bootstack/widgets/_core/container.py +334 -0
- bootstack/widgets/_core/context.py +35 -0
- bootstack/widgets/_core/events.py +130 -0
- bootstack/widgets/_core/field_mixin.py +353 -0
- bootstack/widgets/_core/icon_image_props.py +72 -0
- bootstack/widgets/_core/image_binding.py +93 -0
- bootstack/widgets/_core/navmodel.py +345 -0
- bootstack/widgets/_core/options.py +210 -0
- bootstack/widgets/_core/selection_group.py +130 -0
- bootstack/widgets/_core/window_controls.py +96 -0
- bootstack/widgets/_core/window_menu.py +246 -0
- bootstack/widgets/_impl/__init__.py +1 -0
- bootstack/widgets/_impl/_internal/__init__.py +0 -0
- bootstack/widgets/_impl/_internal/wrapper_base.py +307 -0
- bootstack/widgets/_impl/_parts/__init__.py +11 -0
- bootstack/widgets/_impl/_parts/numberentry_part.py +385 -0
- bootstack/widgets/_impl/_parts/spinnerentry_part.py +434 -0
- bootstack/widgets/_impl/_parts/textentry_part.py +406 -0
- bootstack/widgets/_impl/composites/__init__.py +33 -0
- bootstack/widgets/_impl/composites/_dateutils.py +33 -0
- bootstack/widgets/_impl/composites/_image_fit.py +105 -0
- bootstack/widgets/_impl/composites/accordion.py +381 -0
- bootstack/widgets/_impl/composites/avatar.py +191 -0
- bootstack/widgets/_impl/composites/buttongroup.py +371 -0
- bootstack/widgets/_impl/composites/calendar.py +972 -0
- bootstack/widgets/_impl/composites/carousel.py +567 -0
- bootstack/widgets/_impl/composites/chart.py +882 -0
- bootstack/widgets/_impl/composites/compositeframe.py +298 -0
- bootstack/widgets/_impl/composites/contextmenu.py +1951 -0
- bootstack/widgets/_impl/composites/dateentry.py +404 -0
- bootstack/widgets/_impl/composites/dropdownbutton.py +325 -0
- bootstack/widgets/_impl/composites/expander.py +515 -0
- bootstack/widgets/_impl/composites/field.py +670 -0
- bootstack/widgets/_impl/composites/form.py +1066 -0
- bootstack/widgets/_impl/composites/gallery.py +551 -0
- bootstack/widgets/_impl/composites/list/__init__.py +15 -0
- bootstack/widgets/_impl/composites/list/listitem.py +802 -0
- bootstack/widgets/_impl/composites/list/listview.py +1433 -0
- bootstack/widgets/_impl/composites/menu/__init__.py +18 -0
- bootstack/widgets/_impl/composites/menu/model.py +358 -0
- bootstack/widgets/_impl/composites/menu/render_native.py +134 -0
- bootstack/widgets/_impl/composites/menu/render_themed.py +134 -0
- bootstack/widgets/_impl/composites/meter.py +860 -0
- bootstack/widgets/_impl/composites/numericentry.py +201 -0
- bootstack/widgets/_impl/composites/pagestack.py +395 -0
- bootstack/widgets/_impl/composites/passwordentry.py +142 -0
- bootstack/widgets/_impl/composites/pathentry.py +168 -0
- bootstack/widgets/_impl/composites/picture.py +289 -0
- bootstack/widgets/_impl/composites/radiogroup.py +511 -0
- bootstack/widgets/_impl/composites/scrolledtext.py +375 -0
- bootstack/widgets/_impl/composites/scrolledtext.pyi +186 -0
- bootstack/widgets/_impl/composites/scrollview.py +764 -0
- bootstack/widgets/_impl/composites/selectbox.py +1026 -0
- bootstack/widgets/_impl/composites/shell/__init__.py +40 -0
- bootstack/widgets/_impl/composites/shell/content_host.py +52 -0
- bootstack/widgets/_impl/composites/shell/layout.py +335 -0
- bootstack/widgets/_impl/composites/shell/nav_panel.py +345 -0
- bootstack/widgets/_impl/composites/shell/providers.py +558 -0
- bootstack/widgets/_impl/composites/shell/rail.py +117 -0
- bootstack/widgets/_impl/composites/shell/shell.py +581 -0
- bootstack/widgets/_impl/composites/shell/workspace.py +273 -0
- bootstack/widgets/_impl/composites/sidenav/__init__.py +16 -0
- bootstack/widgets/_impl/composites/sidenav/header.py +81 -0
- bootstack/widgets/_impl/composites/sidenav/separator.py +44 -0
- bootstack/widgets/_impl/composites/slider/__init__.py +7 -0
- bootstack/widgets/_impl/composites/slider/_shared.py +195 -0
- bootstack/widgets/_impl/composites/slider/rangeslider.py +982 -0
- bootstack/widgets/_impl/composites/slider/slider.py +851 -0
- bootstack/widgets/_impl/composites/spinnerentry.py +185 -0
- bootstack/widgets/_impl/composites/tableview/__init__.py +5 -0
- bootstack/widgets/_impl/composites/tableview/tableview.py +3388 -0
- bootstack/widgets/_impl/composites/tableview/types.py +169 -0
- bootstack/widgets/_impl/composites/tabs/__init__.py +23 -0
- bootstack/widgets/_impl/composites/tabs/tabitem.py +389 -0
- bootstack/widgets/_impl/composites/tabs/tabs.py +974 -0
- bootstack/widgets/_impl/composites/tabs/tabview.py +650 -0
- bootstack/widgets/_impl/composites/textarea/__init__.py +26 -0
- bootstack/widgets/_impl/composites/textarea/change.py +46 -0
- bootstack/widgets/_impl/composites/textarea/codeeditor.py +526 -0
- bootstack/widgets/_impl/composites/textarea/core.py +495 -0
- bootstack/widgets/_impl/composites/textarea/decoration.py +42 -0
- bootstack/widgets/_impl/composites/textarea/diff.py +127 -0
- bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +1 -0
- bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +138 -0
- bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +158 -0
- bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +138 -0
- bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +312 -0
- bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +177 -0
- bootstack/widgets/_impl/composites/textarea/filter.py +171 -0
- bootstack/widgets/_impl/composites/textarea/search_overlay.py +459 -0
- bootstack/widgets/_impl/composites/textarea/sidebar.py +88 -0
- bootstack/widgets/_impl/composites/textarea/style_registry.py +178 -0
- bootstack/widgets/_impl/composites/textarea/textarea.py +606 -0
- bootstack/widgets/_impl/composites/textarea/undo.py +217 -0
- bootstack/widgets/_impl/composites/textentry.py +57 -0
- bootstack/widgets/_impl/composites/timeentry.py +176 -0
- bootstack/widgets/_impl/composites/toast.py +390 -0
- bootstack/widgets/_impl/composites/toast_stack.py +156 -0
- bootstack/widgets/_impl/composites/togglegroup.py +418 -0
- bootstack/widgets/_impl/composites/toolbar.py +608 -0
- bootstack/widgets/_impl/composites/tooltip.py +491 -0
- bootstack/widgets/_impl/composites/tree/__init__.py +7 -0
- bootstack/widgets/_impl/composites/tree/source_binding.py +136 -0
- bootstack/widgets/_impl/composites/tree/treeitem.py +393 -0
- bootstack/widgets/_impl/composites/tree/treenode.py +174 -0
- bootstack/widgets/_impl/composites/tree/treeview.py +841 -0
- bootstack/widgets/_impl/mixins/__init__.py +24 -0
- bootstack/widgets/_impl/mixins/configure_mixin.py +216 -0
- bootstack/widgets/_impl/mixins/entry_mixin.py +134 -0
- bootstack/widgets/_impl/mixins/font_mixin.py +368 -0
- bootstack/widgets/_impl/mixins/icon_mixin.py +61 -0
- bootstack/widgets/_impl/mixins/localization_mixin.py +253 -0
- bootstack/widgets/_impl/mixins/signal_mixin.py +268 -0
- bootstack/widgets/_impl/mixins/validation_mixin.py +226 -0
- bootstack/widgets/_impl/primitives/__init__.py +49 -0
- bootstack/widgets/_impl/primitives/_menubutton.py +107 -0
- bootstack/widgets/_impl/primitives/badge.py +45 -0
- bootstack/widgets/_impl/primitives/button.py +76 -0
- bootstack/widgets/_impl/primitives/card.py +45 -0
- bootstack/widgets/_impl/primitives/checkbutton.py +124 -0
- bootstack/widgets/_impl/primitives/checktoggle.py +62 -0
- bootstack/widgets/_impl/primitives/combobox.py +156 -0
- bootstack/widgets/_impl/primitives/entry.py +87 -0
- bootstack/widgets/_impl/primitives/flexframe.py +448 -0
- bootstack/widgets/_impl/primitives/frame.py +185 -0
- bootstack/widgets/_impl/primitives/gridframe.py +546 -0
- bootstack/widgets/_impl/primitives/label.py +84 -0
- bootstack/widgets/_impl/primitives/labelframe.py +54 -0
- bootstack/widgets/_impl/primitives/optionmenu.py +387 -0
- bootstack/widgets/_impl/primitives/packframe.py +227 -0
- bootstack/widgets/_impl/primitives/panedwindow.py +45 -0
- bootstack/widgets/_impl/primitives/progressbar.py +83 -0
- bootstack/widgets/_impl/primitives/radiobutton.py +115 -0
- bootstack/widgets/_impl/primitives/radiotoggle.py +54 -0
- bootstack/widgets/_impl/primitives/scrollbar.py +42 -0
- bootstack/widgets/_impl/primitives/separator.py +43 -0
- bootstack/widgets/_impl/primitives/sizegrip.py +33 -0
- bootstack/widgets/_impl/primitives/spinbox.py +95 -0
- bootstack/widgets/_impl/primitives/switch.py +44 -0
- bootstack/widgets/_impl/primitives/treeview.py +69 -0
- bootstack/widgets/app.py +371 -0
- bootstack/widgets/appshell.py +1179 -0
- bootstack/widgets/avatar.py +140 -0
- bootstack/widgets/boolean_controls.py +455 -0
- bootstack/widgets/button.py +224 -0
- bootstack/widgets/buttongroup.py +239 -0
- bootstack/widgets/calendar.py +195 -0
- bootstack/widgets/card.py +159 -0
- bootstack/widgets/carousel.py +241 -0
- bootstack/widgets/chart.py +302 -0
- bootstack/widgets/codeeditor.py +675 -0
- bootstack/widgets/contextmenu.py +371 -0
- bootstack/widgets/datatable.py +688 -0
- bootstack/widgets/datefield.py +395 -0
- bootstack/widgets/divider.py +60 -0
- bootstack/widgets/expander.py +579 -0
- bootstack/widgets/form.py +200 -0
- bootstack/widgets/gallery.py +250 -0
- bootstack/widgets/gauge.py +168 -0
- bootstack/widgets/grid.py +121 -0
- bootstack/widgets/groupbox.py +162 -0
- bootstack/widgets/label.py +229 -0
- bootstack/widgets/listview.py +354 -0
- bootstack/widgets/menubutton.py +419 -0
- bootstack/widgets/numberfield.py +431 -0
- bootstack/widgets/pagestack.py +345 -0
- bootstack/widgets/passwordfield.py +383 -0
- bootstack/widgets/pathfield.py +454 -0
- bootstack/widgets/picture.py +251 -0
- bootstack/widgets/progressbar.py +106 -0
- bootstack/widgets/radio_variants.py +271 -0
- bootstack/widgets/radiogroup.py +228 -0
- bootstack/widgets/scrollbar.py +64 -0
- bootstack/widgets/scrollview.py +186 -0
- bootstack/widgets/select.py +302 -0
- bootstack/widgets/selectbutton.py +182 -0
- bootstack/widgets/sidebar_toggle.py +130 -0
- bootstack/widgets/sizegrip.py +42 -0
- bootstack/widgets/slider.py +408 -0
- bootstack/widgets/spinbox.py +147 -0
- bootstack/widgets/spinnerfield.py +413 -0
- bootstack/widgets/splash.py +367 -0
- bootstack/widgets/splitview.py +482 -0
- bootstack/widgets/stacks.py +249 -0
- bootstack/widgets/statusbar.py +189 -0
- bootstack/widgets/tabs.py +425 -0
- bootstack/widgets/textarea.py +459 -0
- bootstack/widgets/textfield.py +410 -0
- bootstack/widgets/theme_toggle.py +92 -0
- bootstack/widgets/timefield.py +340 -0
- bootstack/widgets/toast.py +314 -0
- bootstack/widgets/togglegroup.py +231 -0
- bootstack/widgets/toolbar.py +347 -0
- bootstack/widgets/tooltip.py +94 -0
- bootstack/widgets/tree.py +648 -0
- bootstack/widgets/types.py +305 -0
- bootstack/widgets/window.py +307 -0
- bootstack-0.1.0.dist-info/METADATA +301 -0
- bootstack-0.1.0.dist-info/RECORD +471 -0
- bootstack-0.1.0.dist-info/WHEEL +5 -0
- bootstack-0.1.0.dist-info/entry_points.txt +2 -0
- bootstack-0.1.0.dist-info/licenses/LICENSE +21 -0
- bootstack-0.1.0.dist-info/licenses/NOTICE +10 -0
- bootstack-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
"""Date entry field widget with calendar picker button.
|
|
2
|
+
|
|
3
|
+
Provides a specialized entry field for date input with locale-aware formatting
|
|
4
|
+
and an optional calendar picker button.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import date, datetime
|
|
8
|
+
from typing import TYPE_CHECKING, Iterable, Literal
|
|
9
|
+
from bootstack.i18n.intl_format import DateFormatSpec
|
|
10
|
+
|
|
11
|
+
from typing_extensions import Unpack
|
|
12
|
+
|
|
13
|
+
from bootstack.events import ChangeEvent
|
|
14
|
+
from bootstack.widgets._impl.primitives.button import Button
|
|
15
|
+
from bootstack.widgets._impl.composites._dateutils import coerce_date
|
|
16
|
+
from bootstack.widgets._impl.composites.field import Field, FieldOptions
|
|
17
|
+
from bootstack.widgets._impl.mixins import configure_delegate
|
|
18
|
+
from bootstack.widgets.types import Master
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DateEntry(Field):
|
|
25
|
+
"""A date entry field widget with calendar picker button.
|
|
26
|
+
|
|
27
|
+
DateEntry extends the Field widget to provide specialized date input with
|
|
28
|
+
locale-aware formatting and an optional calendar picker button. The widget
|
|
29
|
+
supports various date format presets and custom ICU date format patterns,
|
|
30
|
+
and can accept input as strings, date objects, or datetime objects.
|
|
31
|
+
|
|
32
|
+
The available date format presets are: `longDate` (January 15, 2025),
|
|
33
|
+
`shortDate` (1/15/25), `monthAndDate` (January 15), `monthAndYear`
|
|
34
|
+
(January 2025), `quarterAndYear` (Q1 2025), `day` (15), `dayOfWeek`
|
|
35
|
+
(Wednesday), `month` (January), `quarter` (Q1), `year` (2025),
|
|
36
|
+
`longTime` (3:30:45 PM PST), `shortTime` (3:30 PM), `longDateLongTime`,
|
|
37
|
+
`shortDateShortTime`, or any custom ICU date format pattern (e.g., "yyyy-MM-dd").
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
master: Master = None,
|
|
43
|
+
value: str | date | datetime = None,
|
|
44
|
+
value_format: DateFormatSpec = "longDate",
|
|
45
|
+
label: str = None,
|
|
46
|
+
message: str = None,
|
|
47
|
+
show_picker_button=True,
|
|
48
|
+
picker_title: str = None,
|
|
49
|
+
picker_first_weekday: int = 6,
|
|
50
|
+
selection_mode: Literal["single", "range"] = "single",
|
|
51
|
+
start_date: date | datetime | str | None = None,
|
|
52
|
+
end_date: date | datetime | str | None = None,
|
|
53
|
+
min_date: date | datetime | str | None = None,
|
|
54
|
+
max_date: date | datetime | str | None = None,
|
|
55
|
+
disabled_dates: Iterable[date | datetime | str] | None = None,
|
|
56
|
+
**kwargs: Unpack[FieldOptions]
|
|
57
|
+
):
|
|
58
|
+
"""Args:
|
|
59
|
+
master: Parent widget. If None, uses the default root window.
|
|
60
|
+
value: Initial date value to display (single
|
|
61
|
+
mode). Ignored when `selection_mode='range'`.
|
|
62
|
+
value_format: Date format pattern to use for parsing and displaying
|
|
63
|
+
dates. See class docstring for complete list of presets.
|
|
64
|
+
label: Optional label text to display above the entry field.
|
|
65
|
+
If required=True, an asterisk (*) is automatically appended.
|
|
66
|
+
message: Optional message text to display below the entry field.
|
|
67
|
+
Used for hints or help text. Replaced by validation errors when
|
|
68
|
+
validation fails.
|
|
69
|
+
show_picker_button: If True, displays the calendar picker button
|
|
70
|
+
to the right of the entry. If False, hides the button.
|
|
71
|
+
picker_title: Title text for the calendar picker dialog. Defaults
|
|
72
|
+
to "Select date range" in range mode and "Select new date" otherwise.
|
|
73
|
+
picker_first_weekday: First day of the week to display in the
|
|
74
|
+
calendar picker. 0=Monday, 6=Sunday.
|
|
75
|
+
selection_mode: `'single'` (default) for a single date or
|
|
76
|
+
`'range'` for a start/end date range. In range mode the entry
|
|
77
|
+
is readonly and the user selects dates exclusively via the picker.
|
|
78
|
+
`value` returns `tuple[date, date] | None` in range mode.
|
|
79
|
+
start_date: Initial range start date (range
|
|
80
|
+
mode only).
|
|
81
|
+
end_date: Initial range end date (range mode
|
|
82
|
+
only).
|
|
83
|
+
min_date: Lower bound for selectable dates.
|
|
84
|
+
Dates before this are disabled in the picker.
|
|
85
|
+
max_date: Upper bound for selectable dates.
|
|
86
|
+
Dates after this are disabled in the picker.
|
|
87
|
+
disabled_dates: Specific dates to disable in the picker.
|
|
88
|
+
|
|
89
|
+
Other Parameters:
|
|
90
|
+
locale: Locale identifier for date formatting (e.g., 'en_US').
|
|
91
|
+
required: If True, field cannot be empty.
|
|
92
|
+
allow_blank: Allow empty input.
|
|
93
|
+
cursor: Cursor style when hovering.
|
|
94
|
+
exportselection: Export selection to clipboard.
|
|
95
|
+
font: Font for text display.
|
|
96
|
+
foreground: Text color.
|
|
97
|
+
initial_focus: If True, widget receives focus on creation.
|
|
98
|
+
justify: Text alignment ('left', 'center', 'right').
|
|
99
|
+
show_message: If True, displays message area.
|
|
100
|
+
padding: Padding around entry widget.
|
|
101
|
+
takefocus: If True, widget accepts Tab focus.
|
|
102
|
+
textvariable: Tkinter Variable to link with text.
|
|
103
|
+
See [tkinter Variables](https://docs.python.org/3/library/tkinter.html#tkinter-variables).
|
|
104
|
+
textsignal: Signal object for reactive updates.
|
|
105
|
+
width: Width in characters.
|
|
106
|
+
xscrollcommand: Callback for horizontal scrolling.
|
|
107
|
+
"""
|
|
108
|
+
# Store before super().__init__ so property override is safe if called during init
|
|
109
|
+
self._selection_mode = selection_mode
|
|
110
|
+
self._range_value: tuple[date, date] | None = None
|
|
111
|
+
self._value_format = value_format
|
|
112
|
+
|
|
113
|
+
if picker_title is None:
|
|
114
|
+
picker_title = "Select date range" if selection_mode == "range" else "Select new date"
|
|
115
|
+
|
|
116
|
+
kwargs.setdefault('accent', 'primary')
|
|
117
|
+
# In range mode pass value=None so the entry starts empty; display text
|
|
118
|
+
# is set manually below after range initialisation.
|
|
119
|
+
super().__init__(
|
|
120
|
+
master=master,
|
|
121
|
+
value=None if selection_mode == "range" else value,
|
|
122
|
+
value_format=value_format,
|
|
123
|
+
label=label,
|
|
124
|
+
message=message,
|
|
125
|
+
**kwargs,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# configuration
|
|
129
|
+
self._show_picker_button = show_picker_button
|
|
130
|
+
self._picker_title = picker_title
|
|
131
|
+
self._picker_first_weekday = picker_first_weekday
|
|
132
|
+
self._min_date = min_date
|
|
133
|
+
self._max_date = max_date
|
|
134
|
+
self._disabled_dates = disabled_dates
|
|
135
|
+
|
|
136
|
+
self._button_pack = {}
|
|
137
|
+
|
|
138
|
+
self.insert_addon(
|
|
139
|
+
Button,
|
|
140
|
+
position="after",
|
|
141
|
+
name="date-picker",
|
|
142
|
+
icon="calendar-week",
|
|
143
|
+
icon_only=True,
|
|
144
|
+
command=self._show_date_picker
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
self._delegate_show_picker_button(self._show_picker_button)
|
|
148
|
+
|
|
149
|
+
if selection_mode == "range":
|
|
150
|
+
# Make the entry readonly — user must use the picker
|
|
151
|
+
self._entry.state(['readonly'])
|
|
152
|
+
self._field.state(['!disabled'])
|
|
153
|
+
|
|
154
|
+
# Seed initial range
|
|
155
|
+
s = self._coerce_date(start_date)
|
|
156
|
+
e = self._coerce_date(end_date)
|
|
157
|
+
if s is not None and e is not None:
|
|
158
|
+
self._set_range((s, e))
|
|
159
|
+
|
|
160
|
+
# --- value property override for range mode -------------------------
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def value(self) -> date | tuple[date, date] | None:
|
|
164
|
+
"""Selected date (single mode) or `(start, end)` tuple (range mode)."""
|
|
165
|
+
if self._selection_mode == 'range':
|
|
166
|
+
return self._range_value
|
|
167
|
+
return Field.value.fget(self)
|
|
168
|
+
|
|
169
|
+
@value.setter
|
|
170
|
+
def value(self, val):
|
|
171
|
+
if self._selection_mode == 'range':
|
|
172
|
+
self._set_range(val)
|
|
173
|
+
else:
|
|
174
|
+
Field.value.fset(self, val)
|
|
175
|
+
|
|
176
|
+
# --- addon state override for range mode ----------------------------
|
|
177
|
+
|
|
178
|
+
def _sync_addon_state(self, event=None):
|
|
179
|
+
if getattr(self, '_selection_mode', 'single') == 'range':
|
|
180
|
+
# Entry is intentionally readonly in range mode; keep addons active
|
|
181
|
+
self._set_addons_state(False)
|
|
182
|
+
else:
|
|
183
|
+
super()._sync_addon_state(event)
|
|
184
|
+
|
|
185
|
+
# --- range helpers --------------------------------------------------
|
|
186
|
+
|
|
187
|
+
def _set_range(self, val) -> None:
|
|
188
|
+
"""Set the range value, update display text, and fire <<Change>>."""
|
|
189
|
+
if val is None:
|
|
190
|
+
prev = self._range_value
|
|
191
|
+
self._range_value = None
|
|
192
|
+
self.variable.set("")
|
|
193
|
+
if prev is not None:
|
|
194
|
+
try:
|
|
195
|
+
self._entry.event_generate("<<Change>>", data=ChangeEvent(
|
|
196
|
+
value=None, prev_value=prev, text="",
|
|
197
|
+
))
|
|
198
|
+
except Exception:
|
|
199
|
+
pass
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
if not (isinstance(val, (tuple, list)) and len(val) == 2):
|
|
203
|
+
return
|
|
204
|
+
|
|
205
|
+
s = self._coerce_date(val[0])
|
|
206
|
+
e = self._coerce_date(val[1])
|
|
207
|
+
if s is None or e is None:
|
|
208
|
+
return
|
|
209
|
+
if e < s:
|
|
210
|
+
s, e = e, s
|
|
211
|
+
|
|
212
|
+
prev = self._range_value
|
|
213
|
+
self._range_value = (s, e)
|
|
214
|
+
self.variable.set(self._format_range_display(s, e))
|
|
215
|
+
|
|
216
|
+
if prev != self._range_value:
|
|
217
|
+
try:
|
|
218
|
+
self._entry.event_generate("<<Change>>", data=ChangeEvent(
|
|
219
|
+
value=self._range_value,
|
|
220
|
+
prev_value=prev,
|
|
221
|
+
text=self.variable.get(),
|
|
222
|
+
))
|
|
223
|
+
except Exception:
|
|
224
|
+
pass
|
|
225
|
+
|
|
226
|
+
def _format_range_display(self, start: date, end: date) -> str:
|
|
227
|
+
"""Format a date range as 'Jan 1 – Jan 31, 2025' using value_format."""
|
|
228
|
+
from bootstack.i18n import IntlFormatter, MessageCatalog
|
|
229
|
+
fmt = IntlFormatter(locale=MessageCatalog.locale())
|
|
230
|
+
start_str = fmt.format(start, self._value_format)
|
|
231
|
+
end_str = fmt.format(end, self._value_format)
|
|
232
|
+
return f"{start_str} – {end_str}"
|
|
233
|
+
|
|
234
|
+
_coerce_date = staticmethod(coerce_date)
|
|
235
|
+
|
|
236
|
+
def set_min_date(self, value: date | datetime | str | None) -> None:
|
|
237
|
+
"""Set the earliest selectable date for the picker. None clears the bound."""
|
|
238
|
+
self._min_date = self._coerce_date(value)
|
|
239
|
+
|
|
240
|
+
def set_max_date(self, value: date | datetime | str | None) -> None:
|
|
241
|
+
"""Set the latest selectable date for the picker. None clears the bound."""
|
|
242
|
+
self._max_date = self._coerce_date(value)
|
|
243
|
+
|
|
244
|
+
def set_disabled_dates(self, dates: Iterable[date | datetime | str] | None) -> None:
|
|
245
|
+
"""Replace the dates disabled in the picker. The iterable is coerced and
|
|
246
|
+
materialized, so a one-shot generator is consumed exactly once here."""
|
|
247
|
+
self._disabled_dates = [
|
|
248
|
+
d for d in (self._coerce_date(x) for x in (dates or [])) if d is not None
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
# --- public properties ----------------------------------------------
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def date_picker_button(self) -> Button | None:
|
|
255
|
+
"""Get the calendar picker button widget."""
|
|
256
|
+
return self.addons.get('date-picker')
|
|
257
|
+
|
|
258
|
+
@configure_delegate('show_picker_button')
|
|
259
|
+
def _delegate_show_picker_button(self, value: bool = None):
|
|
260
|
+
"""Get or set the visibility of the calendar picker button."""
|
|
261
|
+
if value is None:
|
|
262
|
+
return self._show_picker_button
|
|
263
|
+
else:
|
|
264
|
+
if value:
|
|
265
|
+
if not self.date_picker_button.winfo_ismapped():
|
|
266
|
+
self.date_picker_button.pack(**self._button_pack)
|
|
267
|
+
else:
|
|
268
|
+
self._button_pack = self.date_picker_button.pack_info()
|
|
269
|
+
self.date_picker_button.pack_forget()
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
def _show_date_picker(self):
|
|
273
|
+
"""Open the calendar picker dialog.
|
|
274
|
+
|
|
275
|
+
Opens a DateDialog seeded with the current value (or range), updates
|
|
276
|
+
the entry when a date is picked, and leaves the field unchanged on
|
|
277
|
+
cancel.
|
|
278
|
+
"""
|
|
279
|
+
from bootstack.dialogs._impl.datedialog import DateDialog
|
|
280
|
+
|
|
281
|
+
# Prevent multiple dialogs: reuse existing if still open
|
|
282
|
+
existing = getattr(self, "_active_date_dialog", None)
|
|
283
|
+
top = getattr(getattr(existing, "_dialog", None), "toplevel", None) if existing else None
|
|
284
|
+
try:
|
|
285
|
+
exists = bool(top and top.winfo_exists())
|
|
286
|
+
except Exception:
|
|
287
|
+
exists = False
|
|
288
|
+
|
|
289
|
+
if exists:
|
|
290
|
+
try:
|
|
291
|
+
top.lift()
|
|
292
|
+
top.focus_force()
|
|
293
|
+
except Exception:
|
|
294
|
+
pass
|
|
295
|
+
return
|
|
296
|
+
self._active_date_dialog = None
|
|
297
|
+
|
|
298
|
+
if self._selection_mode == "range":
|
|
299
|
+
cur = self._range_value
|
|
300
|
+
dialog = DateDialog(
|
|
301
|
+
master=self.winfo_toplevel(),
|
|
302
|
+
title=self._picker_title,
|
|
303
|
+
first_weekday=self._picker_first_weekday,
|
|
304
|
+
selection_mode="range",
|
|
305
|
+
start_date=cur[0] if cur else None,
|
|
306
|
+
end_date=cur[1] if cur else None,
|
|
307
|
+
accent=self._accent,
|
|
308
|
+
disabled_dates=self._disabled_dates,
|
|
309
|
+
min_date=self._min_date,
|
|
310
|
+
max_date=self._max_date,
|
|
311
|
+
hide_window_chrome=True,
|
|
312
|
+
close_on_click_outside=False,
|
|
313
|
+
)
|
|
314
|
+
else:
|
|
315
|
+
current_value = self.value
|
|
316
|
+
if isinstance(current_value, datetime):
|
|
317
|
+
current_value = current_value.date()
|
|
318
|
+
if not isinstance(current_value, date):
|
|
319
|
+
current_value = date.today()
|
|
320
|
+
dialog = DateDialog(
|
|
321
|
+
master=self.winfo_toplevel(),
|
|
322
|
+
title=self._picker_title,
|
|
323
|
+
first_weekday=self._picker_first_weekday,
|
|
324
|
+
initial_date=current_value,
|
|
325
|
+
accent=self._accent,
|
|
326
|
+
disabled_dates=self._disabled_dates,
|
|
327
|
+
min_date=self._min_date,
|
|
328
|
+
max_date=self._max_date,
|
|
329
|
+
hide_window_chrome=True,
|
|
330
|
+
close_on_click_outside=False,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
self._active_date_dialog = dialog
|
|
334
|
+
|
|
335
|
+
def _on_result(payload):
|
|
336
|
+
result = payload.get('result') if isinstance(payload, dict) else payload
|
|
337
|
+
if self._selection_mode == "range":
|
|
338
|
+
if isinstance(result, tuple) and len(result) == 2:
|
|
339
|
+
self.value = result
|
|
340
|
+
else:
|
|
341
|
+
if isinstance(result, (date, datetime)):
|
|
342
|
+
self.value = result
|
|
343
|
+
|
|
344
|
+
dialog.on_result(lambda x: _on_result(x))
|
|
345
|
+
dialog.show(
|
|
346
|
+
anchor_to=self._field,
|
|
347
|
+
anchor_point='se',
|
|
348
|
+
window_point='ne',
|
|
349
|
+
offset=(-4, 2),
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
top = getattr(getattr(dialog, "_dialog", None), "toplevel", None)
|
|
353
|
+
cleared = False
|
|
354
|
+
try:
|
|
355
|
+
if top and top.winfo_exists():
|
|
356
|
+
def _clear(_=None):
|
|
357
|
+
self._active_date_dialog = None
|
|
358
|
+
top.bind("<Destroy>", _clear, add="+")
|
|
359
|
+
cleared = True
|
|
360
|
+
except Exception:
|
|
361
|
+
cleared = False
|
|
362
|
+
if not cleared:
|
|
363
|
+
self._active_date_dialog = None
|
|
364
|
+
|
|
365
|
+
# Fallback: ensure value is applied after modal dialog closes.
|
|
366
|
+
selected = dialog.result
|
|
367
|
+
if self._selection_mode == "range":
|
|
368
|
+
if isinstance(selected, tuple) and len(selected) == 2:
|
|
369
|
+
self.value = selected
|
|
370
|
+
else:
|
|
371
|
+
if isinstance(selected, datetime):
|
|
372
|
+
selected = selected.date()
|
|
373
|
+
if isinstance(selected, date):
|
|
374
|
+
self.value = selected
|
|
375
|
+
|
|
376
|
+
# Return focus to the entry after dialog closes
|
|
377
|
+
# Note: focus_force() is needed on Windows after override-redirect dialogs
|
|
378
|
+
try:
|
|
379
|
+
self._entry.focus_force()
|
|
380
|
+
except Exception:
|
|
381
|
+
pass
|
|
382
|
+
|
|
383
|
+
def _picker_position(self):
|
|
384
|
+
"""Choose a dialog position beneath the entry mirroring SelectBox spacing."""
|
|
385
|
+
try:
|
|
386
|
+
# The field is already rendered when the user clicks, so no layout
|
|
387
|
+
# flush is needed — update_idletasks() here would drain unrelated
|
|
388
|
+
# idle callbacks (e.g. Calendar._lock_size from a previous dialog)
|
|
389
|
+
# and cause noticeable lag on repeated opens.
|
|
390
|
+
field_widget = self._field
|
|
391
|
+
x = field_widget.winfo_rootx() + 4
|
|
392
|
+
y = field_widget.winfo_rooty() + field_widget.winfo_height() + 2
|
|
393
|
+
return x, y
|
|
394
|
+
except Exception:
|
|
395
|
+
pass
|
|
396
|
+
|
|
397
|
+
try:
|
|
398
|
+
top = self.winfo_toplevel()
|
|
399
|
+
x = top.winfo_rootx() + top.winfo_width() // 2
|
|
400
|
+
y = top.winfo_rooty() + top.winfo_height() // 2
|
|
401
|
+
return x, y
|
|
402
|
+
except Exception:
|
|
403
|
+
return None
|
|
404
|
+
|