bootstack 0.1.0a1__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 +249 -0
- bootstack/__main__.py +5 -0
- bootstack/api/__init__.py +127 -0
- bootstack/api/app.py +30 -0
- bootstack/api/constants.py +3 -0
- bootstack/api/data.py +23 -0
- bootstack/api/dialogs.py +44 -0
- bootstack/api/i18n.py +17 -0
- bootstack/api/localization.py +16 -0
- bootstack/api/menu.py +7 -0
- bootstack/api/style.py +23 -0
- bootstack/api/utils.py +24 -0
- bootstack/api/widgets.py +137 -0
- bootstack/assets/__init__.py +24 -0
- bootstack/assets/bootstack-transparent.png +0 -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/border.png +0 -0
- bootstack/assets/elements/button-compact.png +0 -0
- bootstack/assets/elements/button-default.png +0 -0
- bootstack/assets/elements/button-group-horizontal-after-compact.png +0 -0
- bootstack/assets/elements/button-group-horizontal-after-default.png +0 -0
- bootstack/assets/elements/button-group-horizontal-before-compact.png +0 -0
- bootstack/assets/elements/button-group-horizontal-before-default.png +0 -0
- bootstack/assets/elements/button-group-horizontal-center-compact.png +0 -0
- bootstack/assets/elements/button-group-horizontal-center-default.png +0 -0
- bootstack/assets/elements/button-group-vertical-after-compact.png +0 -0
- bootstack/assets/elements/button-group-vertical-after-default.png +0 -0
- bootstack/assets/elements/button-group-vertical-before-compact.png +0 -0
- bootstack/assets/elements/button-group-vertical-before-default.png +0 -0
- bootstack/assets/elements/button-group-vertical-center-compact.png +0 -0
- bootstack/assets/elements/button-group-vertical-center-default.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-after-compact.png +0 -0
- bootstack/assets/elements/input-after-default.png +0 -0
- bootstack/assets/elements/input-before-compact.png +0 -0
- bootstack/assets/elements/input-before-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/manifest.toml +480 -0
- bootstack/assets/elements/menu-item.png +0 -0
- bootstack/assets/elements/nav-button-compact.png +0 -0
- bootstack/assets/elements/nav-button-default.png +0 -0
- bootstack/assets/elements/nav-icon-button-compact.png +0 -0
- bootstack/assets/elements/nav-icon-button-default.png +0 -0
- bootstack/assets/elements/notebook-client-border.png +0 -0
- bootstack/assets/elements/notebook-tab-active.png +0 -0
- bootstack/assets/elements/notebook-tab-bar.png +0 -0
- bootstack/assets/elements/notebook-tab-normal.png +0 -0
- bootstack/assets/elements/notebook-tab-pill.png +0 -0
- bootstack/assets/elements/progress-bar-horizontal-striped.png +0 -0
- bootstack/assets/elements/progress-bar-solid.png +0 -0
- bootstack/assets/elements/progress-bar-thin.png +0 -0
- bootstack/assets/elements/progress-bar-vertical-striped.png +0 -0
- bootstack/assets/elements/radio-selected.png +0 -0
- bootstack/assets/elements/radio-unselected.png +0 -0
- bootstack/assets/elements/scrollbar-horizontal.png +0 -0
- bootstack/assets/elements/scrollbar-vertical.png +0 -0
- bootstack/assets/elements/slider-handle-focus.png +0 -0
- bootstack/assets/elements/slider-handle.png +0 -0
- bootstack/assets/elements/slider-track-horizontal.png +0 -0
- bootstack/assets/elements/slider-track-vertical.png +0 -0
- bootstack/assets/elements/switch-off.png +0 -0
- bootstack/assets/elements/switch-on.png +0 -0
- bootstack/assets/elements/tabs-bar-horizontal.png +0 -0
- bootstack/assets/elements/tabs-bar-vertical.png +0 -0
- bootstack/assets/elements/tabs-pill.png +0 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +875 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +875 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +853 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +851 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +841 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +914 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +841 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +841 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
- bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +842 -0
- bootstack/assets/themes/__init__.py +0 -0
- bootstack/assets/themes/amber-dark.json +32 -0
- bootstack/assets/themes/amber-light.json +32 -0
- bootstack/assets/themes/aurora-dark.json +32 -0
- bootstack/assets/themes/aurora-light.json +32 -0
- bootstack/assets/themes/bootstrap-dark.json +32 -0
- bootstack/assets/themes/bootstrap-light.json +32 -0
- bootstack/assets/themes/classic-dark.json +32 -0
- bootstack/assets/themes/classic-light.json +32 -0
- bootstack/assets/themes/docs-dark.json +32 -0
- bootstack/assets/themes/docs-light.json +32 -0
- bootstack/assets/themes/forest-dark.json +32 -0
- bootstack/assets/themes/forest-light.json +32 -0
- bootstack/assets/themes/ocean-dark.json +32 -0
- bootstack/assets/themes/ocean-light.json +32 -0
- bootstack/assets/themes/rose-dark.json +32 -0
- bootstack/assets/themes/rose-light.json +32 -0
- bootstack/assets/widgets/__init__.py +0 -0
- bootstack/assets/widgets/badge-default.png +0 -0
- bootstack/assets/widgets/badge-pill.png +0 -0
- bootstack/assets/widgets/border.png +0 -0
- bootstack/assets/widgets/button-group-horizontal-after.png +0 -0
- bootstack/assets/widgets/button-group-horizontal-before.png +0 -0
- bootstack/assets/widgets/button-group-horizontal-center.png +0 -0
- bootstack/assets/widgets/button-group-vertical-after.png +0 -0
- bootstack/assets/widgets/button-group-vertical-before.png +0 -0
- bootstack/assets/widgets/button-group-vertical-center.png +0 -0
- bootstack/assets/widgets/button.png +0 -0
- bootstack/assets/widgets/checkbox-checked.png +0 -0
- bootstack/assets/widgets/checkbox-indeterminate.png +0 -0
- bootstack/assets/widgets/checkbox-unchecked.png +0 -0
- bootstack/assets/widgets/field.png +0 -0
- bootstack/assets/widgets/icon-button.png +0 -0
- bootstack/assets/widgets/input-inner.png +0 -0
- bootstack/assets/widgets/input-prefix.png +0 -0
- bootstack/assets/widgets/input-suffix.png +0 -0
- bootstack/assets/widgets/input.png +0 -0
- bootstack/assets/widgets/list-item-focus.png +0 -0
- bootstack/assets/widgets/list-item-separated.png +0 -0
- bootstack/assets/widgets/menu-item-separated.png +0 -0
- bootstack/assets/widgets/notebook-client-border.png +0 -0
- bootstack/assets/widgets/notebook-pill-active.png +0 -0
- bootstack/assets/widgets/notebook-pill-inactive.png +0 -0
- bootstack/assets/widgets/notebook-tab-active.png +0 -0
- bootstack/assets/widgets/notebook-tab-border.png +0 -0
- bootstack/assets/widgets/notebook-tab-normal.png +0 -0
- bootstack/assets/widgets/notebook-underline.png +0 -0
- bootstack/assets/widgets/progress-bar-horizontal-default.png +0 -0
- bootstack/assets/widgets/progress-bar-horizontal-striped.png +0 -0
- bootstack/assets/widgets/progress-bar-vertical-default.png +0 -0
- bootstack/assets/widgets/progress-bar-vertical-striped.png +0 -0
- bootstack/assets/widgets/progress-trough-horizontal.png +0 -0
- bootstack/assets/widgets/progress-trough-vertical.png +0 -0
- bootstack/assets/widgets/radio-selected.png +0 -0
- bootstack/assets/widgets/radio-unselected.png +0 -0
- bootstack/assets/widgets/scrollbar-horizontal-rounded.png +0 -0
- bootstack/assets/widgets/scrollbar-vertical-rounded.png +0 -0
- bootstack/assets/widgets/separator-horizontal.png +0 -0
- bootstack/assets/widgets/separator-vertical.png +0 -0
- bootstack/assets/widgets/slider-handle-focus.png +0 -0
- bootstack/assets/widgets/slider-handle.png +0 -0
- bootstack/assets/widgets/slider-track-horizontal.png +0 -0
- bootstack/assets/widgets/slider-track-vertical.png +0 -0
- bootstack/assets/widgets/switch-off.png +0 -0
- bootstack/assets/widgets/switch-on.png +0 -0
- bootstack/assets/widgets/tabs-bar-horizontal.png +0 -0
- bootstack/assets/widgets/tabs-bar-vertical.png +0 -0
- bootstack/assets/widgets/tabs-pill.png +0 -0
- bootstack/cli/__init__.py +124 -0
- bootstack/cli/__main__.py +6 -0
- bootstack/cli/add.py +439 -0
- bootstack/cli/build.py +115 -0
- bootstack/cli/config.py +287 -0
- bootstack/cli/demo.py +1267 -0
- bootstack/cli/doctor.py +195 -0
- bootstack/cli/list_cmd.py +71 -0
- bootstack/cli/promote.py +120 -0
- bootstack/cli/pyinstaller.py +246 -0
- bootstack/cli/run.py +99 -0
- bootstack/cli/start.py +105 -0
- bootstack/cli/templates/__init__.py +861 -0
- bootstack/constants.py +325 -0
- bootstack/core/__init__.py +34 -0
- bootstack/core/capabilities/__init__.py +45 -0
- bootstack/core/capabilities/after.py +103 -0
- bootstack/core/capabilities/bind.py +154 -0
- bootstack/core/capabilities/bindtags.py +112 -0
- bootstack/core/capabilities/busy.py +61 -0
- bootstack/core/capabilities/clipboard.py +88 -0
- bootstack/core/capabilities/focus.py +118 -0
- bootstack/core/capabilities/grab.py +65 -0
- bootstack/core/capabilities/grid.py +188 -0
- bootstack/core/capabilities/localization.py +231 -0
- bootstack/core/capabilities/pack.py +119 -0
- bootstack/core/capabilities/place.py +92 -0
- bootstack/core/capabilities/selection.py +136 -0
- bootstack/core/capabilities/signals.py +242 -0
- bootstack/core/capabilities/winfo.py +315 -0
- bootstack/core/colorutils.py +234 -0
- bootstack/core/exceptions.py +95 -0
- bootstack/core/images.py +283 -0
- bootstack/core/localization/README.md +90 -0
- bootstack/core/localization/__init__.py +13 -0
- bootstack/core/localization/intl_format.py +580 -0
- bootstack/core/localization/msgcat.py +425 -0
- bootstack/core/localization/specs.py +143 -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/publisher.py +147 -0
- bootstack/core/signals/README.md +112 -0
- bootstack/core/signals/__init__.py +8 -0
- bootstack/core/signals/integration.py +100 -0
- bootstack/core/signals/signal.py +317 -0
- bootstack/core/signals/types.py +4 -0
- bootstack/core/validation/__init__.py +5 -0
- bootstack/core/validation/types.py +13 -0
- bootstack/core/validation/validation_result.py +17 -0
- bootstack/core/validation/validation_rules.py +112 -0
- bootstack/core/variables.py +62 -0
- bootstack/datasource/README.md +607 -0
- bootstack/datasource/__init__.py +51 -0
- bootstack/datasource/base.py +474 -0
- bootstack/datasource/file_source.py +541 -0
- bootstack/datasource/memory_source.py +482 -0
- bootstack/datasource/sqlite_source.py +453 -0
- bootstack/datasource/types.py +259 -0
- bootstack/dialogs/__init__.py +56 -0
- bootstack/dialogs/colorchooser.py +674 -0
- bootstack/dialogs/colordropper.py +257 -0
- bootstack/dialogs/datedialog.py +404 -0
- bootstack/dialogs/dialog.py +514 -0
- bootstack/dialogs/filterdialog.py +358 -0
- bootstack/dialogs/fontdialog.py +339 -0
- bootstack/dialogs/formdialog.py +541 -0
- bootstack/dialogs/message.py +489 -0
- bootstack/dialogs/query.py +561 -0
- bootstack/py.typed +1 -0
- bootstack/runtime/__init__.py +3 -0
- bootstack/runtime/app.py +879 -0
- bootstack/runtime/base_window.py +786 -0
- bootstack/runtime/events.py +399 -0
- bootstack/runtime/menu.py +510 -0
- bootstack/runtime/shortcuts.py +423 -0
- bootstack/runtime/tk_patch.py +31 -0
- bootstack/runtime/toplevel.py +131 -0
- bootstack/runtime/utility.py +371 -0
- bootstack/runtime/visual_focus.py +228 -0
- bootstack/runtime/window_utilities.py +1043 -0
- bootstack/style/__init__.py +5498 -0
- bootstack/style/bootstyle.py +507 -0
- bootstack/style/bootstyle_builder_base.py +752 -0
- bootstack/style/bootstyle_builder_mixed.py +93 -0
- bootstack/style/bootstyle_builder_tk.py +109 -0
- bootstack/style/bootstyle_builder_ttk.py +354 -0
- bootstack/style/builders/__init__.py +51 -0
- bootstack/style/builders/badge.py +44 -0
- bootstack/style/builders/button.py +453 -0
- bootstack/style/builders/buttongroup.py +344 -0
- bootstack/style/builders/calendar.py +271 -0
- bootstack/style/builders/checkbutton.py +95 -0
- bootstack/style/builders/combobox.py +112 -0
- bootstack/style/builders/contextmenu.py +268 -0
- bootstack/style/builders/entry.py +83 -0
- bootstack/style/builders/expander.py +171 -0
- bootstack/style/builders/field.py +312 -0
- bootstack/style/builders/frame.py +27 -0
- bootstack/style/builders/label.py +28 -0
- bootstack/style/builders/labelframe.py +41 -0
- bootstack/style/builders/listview.py +267 -0
- bootstack/style/builders/menubar.py +74 -0
- bootstack/style/builders/menubutton.py +408 -0
- bootstack/style/builders/notebook.py +316 -0
- bootstack/style/builders/panedwindow.py +25 -0
- bootstack/style/builders/progressbar.py +71 -0
- bootstack/style/builders/radiobutton.py +68 -0
- bootstack/style/builders/scale.py +66 -0
- bootstack/style/builders/scrollbar.py +360 -0
- bootstack/style/builders/separator.py +45 -0
- bootstack/style/builders/sidenav.py +313 -0
- bootstack/style/builders/sizegrip.py +15 -0
- bootstack/style/builders/spinbox.py +119 -0
- bootstack/style/builders/switch.py +67 -0
- bootstack/style/builders/tabitem.py +205 -0
- bootstack/style/builders/toolbutton.py +260 -0
- bootstack/style/builders/tooltip.py +26 -0
- bootstack/style/builders/treeview.py +269 -0
- bootstack/style/builders/utils.py +404 -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/style.py +499 -0
- bootstack/style/theme_provider.py +449 -0
- bootstack/style/tk_patch.py +5 -0
- bootstack/style/token_maps.py +42 -0
- bootstack/style/types.py +32 -0
- bootstack/style/typography.py +527 -0
- bootstack/style/utility.py +696 -0
- bootstack/themes/__init__.py +12 -0
- bootstack/themes/standard.py +415 -0
- bootstack/themes/user.py +45 -0
- bootstack/widgets/__init__.py +53 -0
- bootstack/widgets/composites/__init__.py +38 -0
- bootstack/widgets/composites/accordion.py +385 -0
- bootstack/widgets/composites/appshell.py +445 -0
- bootstack/widgets/composites/buttongroup.py +391 -0
- bootstack/widgets/composites/calendar.py +914 -0
- bootstack/widgets/composites/compositeframe.py +282 -0
- bootstack/widgets/composites/contextmenu.py +1754 -0
- bootstack/widgets/composites/dateentry.py +261 -0
- bootstack/widgets/composites/dropdownbutton.py +190 -0
- bootstack/widgets/composites/expander.py +508 -0
- bootstack/widgets/composites/field.py +448 -0
- bootstack/widgets/composites/floodgauge.py +434 -0
- bootstack/widgets/composites/form.py +983 -0
- bootstack/widgets/composites/labeledscale.py +209 -0
- bootstack/widgets/composites/list/__init__.py +15 -0
- bootstack/widgets/composites/list/listitem.py +733 -0
- bootstack/widgets/composites/list/listview.py +1507 -0
- bootstack/widgets/composites/menubar.py +303 -0
- bootstack/widgets/composites/meter.py +882 -0
- bootstack/widgets/composites/numericentry.py +183 -0
- bootstack/widgets/composites/pagestack.py +330 -0
- bootstack/widgets/composites/passwordentry.py +149 -0
- bootstack/widgets/composites/pathentry.py +223 -0
- bootstack/widgets/composites/radiogroup.py +466 -0
- bootstack/widgets/composites/scrolledtext.py +388 -0
- bootstack/widgets/composites/scrolledtext.pyi +186 -0
- bootstack/widgets/composites/scrollview.py +675 -0
- bootstack/widgets/composites/selectbox.py +544 -0
- bootstack/widgets/composites/sidenav/__init__.py +24 -0
- bootstack/widgets/composites/sidenav/group.py +485 -0
- bootstack/widgets/composites/sidenav/header.py +83 -0
- bootstack/widgets/composites/sidenav/item.py +413 -0
- bootstack/widgets/composites/sidenav/separator.py +51 -0
- bootstack/widgets/composites/sidenav/view.py +919 -0
- bootstack/widgets/composites/spinnerentry.py +232 -0
- bootstack/widgets/composites/tableview/__init__.py +5 -0
- bootstack/widgets/composites/tableview/tableview.py +2254 -0
- bootstack/widgets/composites/tableview/types.py +169 -0
- bootstack/widgets/composites/tabs/__init__.py +6 -0
- bootstack/widgets/composites/tabs/tabitem.py +372 -0
- bootstack/widgets/composites/tabs/tabs.py +478 -0
- bootstack/widgets/composites/tabs/tabview.py +352 -0
- bootstack/widgets/composites/textentry.py +90 -0
- bootstack/widgets/composites/timeentry.py +189 -0
- bootstack/widgets/composites/toast.py +364 -0
- bootstack/widgets/composites/togglegroup.py +382 -0
- bootstack/widgets/composites/toolbar.py +393 -0
- bootstack/widgets/composites/tooltip.py +404 -0
- bootstack/widgets/internal/__init__.py +0 -0
- bootstack/widgets/internal/wrapper_base.py +304 -0
- bootstack/widgets/mixins/__init__.py +25 -0
- bootstack/widgets/mixins/configure_mixin.py +186 -0
- bootstack/widgets/mixins/entry_mixin.py +70 -0
- bootstack/widgets/mixins/font_mixin.py +346 -0
- bootstack/widgets/mixins/icon_mixin.py +38 -0
- bootstack/widgets/mixins/localization_mixin.py +255 -0
- bootstack/widgets/mixins/signal_mixin.py +272 -0
- bootstack/widgets/mixins/validation_mixin.py +204 -0
- bootstack/widgets/parts/__init__.py +11 -0
- bootstack/widgets/parts/numberentry_part.py +345 -0
- bootstack/widgets/parts/spinnerentry_part.py +394 -0
- bootstack/widgets/parts/textentry_part.py +344 -0
- bootstack/widgets/primitives/__init__.py +55 -0
- bootstack/widgets/primitives/badge.py +44 -0
- bootstack/widgets/primitives/button.py +89 -0
- bootstack/widgets/primitives/card.py +66 -0
- bootstack/widgets/primitives/checkbutton.py +124 -0
- bootstack/widgets/primitives/checktoggle.py +53 -0
- bootstack/widgets/primitives/combobox.py +165 -0
- bootstack/widgets/primitives/entry.py +98 -0
- bootstack/widgets/primitives/frame.py +206 -0
- bootstack/widgets/primitives/gridframe.py +479 -0
- bootstack/widgets/primitives/label.py +95 -0
- bootstack/widgets/primitives/labelframe.py +63 -0
- bootstack/widgets/primitives/menubutton.py +118 -0
- bootstack/widgets/primitives/notebook.py +551 -0
- bootstack/widgets/primitives/optionmenu.py +248 -0
- bootstack/widgets/primitives/packframe.py +228 -0
- bootstack/widgets/primitives/panedwindow.py +58 -0
- bootstack/widgets/primitives/progressbar.py +95 -0
- bootstack/widgets/primitives/radiobutton.py +115 -0
- bootstack/widgets/primitives/radiotoggle.py +50 -0
- bootstack/widgets/primitives/scale.py +85 -0
- bootstack/widgets/primitives/scrollbar.py +56 -0
- bootstack/widgets/primitives/separator.py +56 -0
- bootstack/widgets/primitives/sizegrip.py +47 -0
- bootstack/widgets/primitives/spinbox.py +91 -0
- bootstack/widgets/primitives/switch.py +41 -0
- bootstack/widgets/primitives/treeview.py +77 -0
- bootstack/widgets/types.py +20 -0
- bootstack-0.1.0a1.dist-info/METADATA +196 -0
- bootstack-0.1.0a1.dist-info/RECORD +419 -0
- bootstack-0.1.0a1.dist-info/WHEEL +5 -0
- bootstack-0.1.0a1.dist-info/entry_points.txt +2 -0
- bootstack-0.1.0a1.dist-info/licenses/LICENSE +22 -0
- bootstack-0.1.0a1.dist-info/licenses/NOTICE +10 -0
- bootstack-0.1.0a1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
"""Field widget module.
|
|
2
|
+
|
|
3
|
+
Provides a flexible generic entry field composite widget used as the foundation
|
|
4
|
+
for creating specialized entry widgets like TextEntry, PasswordEntry, NumberEntry, etc.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from tkinter import TclError, Variable
|
|
8
|
+
from typing import Any, Callable, Literal, Type, TypedDict, Union, cast
|
|
9
|
+
|
|
10
|
+
from bootstack.core.signals import Signal
|
|
11
|
+
from bootstack.widgets.primitives.button import Button
|
|
12
|
+
from bootstack.widgets.primitives.frame import Frame
|
|
13
|
+
from bootstack.widgets.primitives.label import Label
|
|
14
|
+
from bootstack.widgets.primitives.checkbutton import CheckButton
|
|
15
|
+
from bootstack.widgets.mixins import configure_delegate
|
|
16
|
+
from bootstack.widgets.mixins.entry_mixin import EntryMixin
|
|
17
|
+
from bootstack.widgets.parts.numberentry_part import NumberEntryPart
|
|
18
|
+
from bootstack.widgets.parts.textentry_part import TextEntryPart
|
|
19
|
+
from bootstack.widgets.parts.spinnerentry_part import SpinnerEntryPart
|
|
20
|
+
from bootstack.widgets.types import Master
|
|
21
|
+
|
|
22
|
+
FieldKind = Literal['text', 'numeric', 'spinbox']
|
|
23
|
+
"""Type alias for field kind specification.
|
|
24
|
+
|
|
25
|
+
Determines which entry part widget to use:
|
|
26
|
+
- 'text': Uses TextEntryPart for text input with formatting support
|
|
27
|
+
- 'numeric': Uses NumberEntryPart for numeric input with bounds and stepping
|
|
28
|
+
- 'spinbox': Uses SpinnerEntryPart for spinner input (supports text or numeric values)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class FieldOptions(TypedDict, total=False):
|
|
33
|
+
"""Type hints for Field widget configuration options.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
allow_blank: If True, empty input is allowed. If False, empty input preserves previous value.
|
|
37
|
+
accent: Accent token for the focus ring and active border of the input.
|
|
38
|
+
density: Widget density. 'default' for normal size, 'compact' for smaller size.
|
|
39
|
+
variant: Style variant (if applicable).
|
|
40
|
+
cursor: Cursor to display when hovering over the widget.
|
|
41
|
+
value_format: ICU format pattern for parsing/formatting (e.g., '$#,##0.00' for currency).
|
|
42
|
+
exportselection: If True, selected text is exported to X selection.
|
|
43
|
+
font: Font to use for text display.
|
|
44
|
+
foreground: Text color.
|
|
45
|
+
initial_focus: If True, widget receives focus when created.
|
|
46
|
+
justify: Text justification ('left', 'center', 'right').
|
|
47
|
+
show_message: If True, displays message text below the field.
|
|
48
|
+
padding: Padding around the entry widget.
|
|
49
|
+
show: Character to display instead of typed characters (for password fields).
|
|
50
|
+
state: The widget state. One of 'normal', 'disabled', or 'readonly'.
|
|
51
|
+
takefocus: If True, widget can receive focus via Tab key.
|
|
52
|
+
textvariable: Tkinter Variable to link with the entry text.
|
|
53
|
+
textsignal: Signal object for reactive text updates.
|
|
54
|
+
width: Width of the entry in characters.
|
|
55
|
+
required: If True, field cannot be empty (adds validation rule).
|
|
56
|
+
xscrollcommand: Callback for horizontal scrolling.
|
|
57
|
+
localize: Determines the field label localization mode. 'auto', True, False.
|
|
58
|
+
"""
|
|
59
|
+
allow_blank: bool
|
|
60
|
+
accent: str
|
|
61
|
+
density: Literal['default', 'compact']
|
|
62
|
+
variant: str
|
|
63
|
+
cursor: str
|
|
64
|
+
value_format: str
|
|
65
|
+
exportselection: bool
|
|
66
|
+
font: str
|
|
67
|
+
foreground: str
|
|
68
|
+
initial_focus: bool
|
|
69
|
+
justify: str
|
|
70
|
+
show_message: bool
|
|
71
|
+
padding: str
|
|
72
|
+
show: str
|
|
73
|
+
state: Literal['normal', 'disabled', 'readonly']
|
|
74
|
+
takefocus: bool
|
|
75
|
+
textvariable: Variable
|
|
76
|
+
textsignal: Signal
|
|
77
|
+
width: int
|
|
78
|
+
required: bool
|
|
79
|
+
xscrollcommand: Callable[[int, int], None]
|
|
80
|
+
localize: bool | Literal['auto']
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Field(EntryMixin, Frame):
|
|
84
|
+
"""A flexible generic composite entry field widget.
|
|
85
|
+
|
|
86
|
+
Field is a base composite widget that combines a label, entry input, and
|
|
87
|
+
message area into a complete input field component. It serves as the foundation
|
|
88
|
+
for creating specialized entry widgets like TextEntry, PasswordEntry, NumberEntry,
|
|
89
|
+
and other custom entry types.
|
|
90
|
+
|
|
91
|
+
The widget automatically handles layout, focus states, validation feedback, and
|
|
92
|
+
provides a consistent API for all entry-based components. It supports both text
|
|
93
|
+
and numeric input types through the `kind` parameter.
|
|
94
|
+
|
|
95
|
+
!!! note "Events"
|
|
96
|
+
|
|
97
|
+
- `<<Input>>`: Triggered on each keystroke.
|
|
98
|
+
Provides `event.data` with keys: `text`.
|
|
99
|
+
|
|
100
|
+
- `<<Change>>`: Triggered when value changes after commit.
|
|
101
|
+
Provides `event.data` with keys: `value`, `prev_value`, `text`.
|
|
102
|
+
|
|
103
|
+
- `<<Valid>>`: Triggered when validation passes.
|
|
104
|
+
Provides `event.data` with keys: `value`, `is_valid` (True), `message`.
|
|
105
|
+
|
|
106
|
+
- `<<Invalid>>`: Triggered when validation fails.
|
|
107
|
+
Provides `event.data` with keys: `value`, `is_valid` (False), `message`.
|
|
108
|
+
|
|
109
|
+
- `<<Validate>>`: Triggered after any validation.
|
|
110
|
+
Provides `event.data` with keys: `value`, `is_valid` (bool), `message`.
|
|
111
|
+
|
|
112
|
+
Attributes:
|
|
113
|
+
variable (Variable): Tkinter Variable linked to entry text.
|
|
114
|
+
signal (Signal): Signal object for reactive updates.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(
|
|
118
|
+
self,
|
|
119
|
+
master: Master = None,
|
|
120
|
+
*,
|
|
121
|
+
value: str | int | float = None,
|
|
122
|
+
label: str = None,
|
|
123
|
+
message: str = None,
|
|
124
|
+
show_message: bool = False,
|
|
125
|
+
required: bool = False,
|
|
126
|
+
kind: FieldKind = "text",
|
|
127
|
+
**kwargs: Any
|
|
128
|
+
):
|
|
129
|
+
"""Initialize a Field widget.
|
|
130
|
+
|
|
131
|
+
Creates a composite entry field with optional label, message area, and
|
|
132
|
+
validation support. The field type is determined by the 'kind' parameter,
|
|
133
|
+
which selects either TextEntryPart or NumberEntryPart as the underlying
|
|
134
|
+
entry widget.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
master: Parent widget. If None, uses the default root window.
|
|
138
|
+
value: Initial value to display. Can be str, int, or float depending
|
|
139
|
+
on the field kind. For 'text' kind, should be a string. For
|
|
140
|
+
'numeric' kind, can be int or float. Default is None.
|
|
141
|
+
label: Optional label text to display above the entry field.
|
|
142
|
+
If required=True, an asterisk (*) is automatically appended to
|
|
143
|
+
indicate the field is mandatory.
|
|
144
|
+
message: Optional message text to display below the entry field.
|
|
145
|
+
Used for hints, instructions, or help text. This text is replaced
|
|
146
|
+
by validation error messages when validation fails, and restored
|
|
147
|
+
when validation passes. Default is None (no message).
|
|
148
|
+
show_message: If True, displays the message area below the field.
|
|
149
|
+
If False, hides the message area entirely (validation errors
|
|
150
|
+
won't be shown). Default is True.
|
|
151
|
+
required: If True, marks the field as required and automatically adds
|
|
152
|
+
a 'required' validation rule. An asterisk (*) is appended to the
|
|
153
|
+
label. The field cannot be left empty. Default is False.
|
|
154
|
+
kind: Type of entry field to create. Either 'text' for text input
|
|
155
|
+
(uses TextEntryPart) or 'numeric' for numeric input (uses
|
|
156
|
+
NumberEntryPart). Default is 'text'.
|
|
157
|
+
|
|
158
|
+
Other Parameters:
|
|
159
|
+
value_format (str): ICU format pattern for parsing/formatting.
|
|
160
|
+
allow_blank (bool): Allow empty input.
|
|
161
|
+
locale (str): Locale for formatting (e.g., 'en_US').
|
|
162
|
+
initial_focus (bool): Focus on creation.
|
|
163
|
+
show (str): Character to mask input (e.g., '*' for passwords).
|
|
164
|
+
width (int): Width in characters.
|
|
165
|
+
font (str): Font specification.
|
|
166
|
+
justify (str): Text alignment ('left', 'center', 'right').
|
|
167
|
+
minvalue (int | float): Minimum allowed value (numeric kind only).
|
|
168
|
+
maxvalue (int | float): Maximum allowed value (numeric kind only).
|
|
169
|
+
increment (int | float): Step size for up/down arrows (numeric kind only).
|
|
170
|
+
wrap (bool): Wrap around at boundaries (numeric kind only).
|
|
171
|
+
"""
|
|
172
|
+
# Accept legacy parameter name and prevent it from reaching the Tk widget.
|
|
173
|
+
if 'show_messages' in kwargs:
|
|
174
|
+
show_message = kwargs.pop('show_messages')
|
|
175
|
+
# Track if user explicitly provided show_message
|
|
176
|
+
show_message_explicit = 'show_message' in kwargs
|
|
177
|
+
show_message = kwargs.pop('show_message', show_message)
|
|
178
|
+
|
|
179
|
+
# Auto-enable show_message if message is provided and user didn't explicitly disable it
|
|
180
|
+
if message and not show_message_explicit:
|
|
181
|
+
show_message = True
|
|
182
|
+
|
|
183
|
+
accent = kwargs.pop('accent', None)
|
|
184
|
+
self._density = kwargs.pop('density', 'default')
|
|
185
|
+
self._localize = cast(bool | Literal['auto'], kwargs.pop('localize', 'auto'))
|
|
186
|
+
|
|
187
|
+
# Field itself (outer Frame) doesn't need styling - only pass master
|
|
188
|
+
super().__init__(master)
|
|
189
|
+
|
|
190
|
+
# Set accent AFTER super().__init__ to avoid being overwritten by wrapper
|
|
191
|
+
self._accent = accent
|
|
192
|
+
|
|
193
|
+
# configuration
|
|
194
|
+
self._message_text = message
|
|
195
|
+
self._show_messages = show_message
|
|
196
|
+
self._addons: dict[str, Button | Label | CheckButton] = {}
|
|
197
|
+
self._required = required
|
|
198
|
+
self._kind = kind
|
|
199
|
+
self._label_text = label
|
|
200
|
+
self._value = value
|
|
201
|
+
|
|
202
|
+
self._entry: TextEntryPart | NumberEntryPart | SpinnerEntryPart
|
|
203
|
+
self._addons: dict[str, Union[Button, Label, CheckButton]] = {}
|
|
204
|
+
|
|
205
|
+
# layout
|
|
206
|
+
label_text = self._label_text or ''
|
|
207
|
+
self._label_lbl = Label(
|
|
208
|
+
self,
|
|
209
|
+
localize=self._localize,
|
|
210
|
+
text=f"{label_text}*" if required else label_text,
|
|
211
|
+
font="label[normal]"
|
|
212
|
+
)
|
|
213
|
+
self._message_lbl = Label(self, localize=self._localize, text=message or '', font="caption", accent="secondary")
|
|
214
|
+
|
|
215
|
+
# field container & field - pass density for styling via style_options
|
|
216
|
+
field_padding = 4 if self._density == 'compact' else 5
|
|
217
|
+
self._field = Frame(self, accent=self._accent, padding=field_padding, ttk_class="TField", style_options={'density': self._density})
|
|
218
|
+
|
|
219
|
+
if kind == "numeric":
|
|
220
|
+
self._entry = NumberEntryPart(self._field, value=value, density=self._density, **kwargs)
|
|
221
|
+
elif kind == "spinbox":
|
|
222
|
+
self._entry = SpinnerEntryPart(self._field, value=value, density=self._density, **kwargs)
|
|
223
|
+
else:
|
|
224
|
+
self._entry = TextEntryPart(self._field, value=value, density=self._density, **kwargs)
|
|
225
|
+
|
|
226
|
+
# attach widgets
|
|
227
|
+
if label:
|
|
228
|
+
self._label_lbl.pack(side='top', fill='x', padx=(4, 0))
|
|
229
|
+
|
|
230
|
+
self._field.pack(side='top', fill='x', expand=True)
|
|
231
|
+
self._entry.pack(side='left', fill='x', expand=True, padx=(0, 6) if kind == "spinbox" else 0, pady=0)
|
|
232
|
+
|
|
233
|
+
self._entry.bind('<<StateChanged>>', self._sync_addon_state, add=True)
|
|
234
|
+
self._sync_addon_state()
|
|
235
|
+
|
|
236
|
+
if self._show_messages:
|
|
237
|
+
self._message_lbl.pack(side='top', fill='x', padx=4)
|
|
238
|
+
|
|
239
|
+
self._entry.bind('<<Invalid>>', self._show_error, add=True)
|
|
240
|
+
self._entry.bind('<<Valid>>', self._clear_error, add=True)
|
|
241
|
+
|
|
242
|
+
# bind focus styling to the field frame
|
|
243
|
+
self._entry.bind('<FocusIn>', lambda _: self._field.state(['focus']), add=True)
|
|
244
|
+
self._entry.bind('<FocusOut>', lambda _: self._field.state(['!focus']), add=True)
|
|
245
|
+
|
|
246
|
+
# add required validation
|
|
247
|
+
if required:
|
|
248
|
+
self._entry.add_validation_rule("required")
|
|
249
|
+
|
|
250
|
+
# forward reference entry methods
|
|
251
|
+
self.on_input = self._entry.on_input
|
|
252
|
+
self.off_input = self._entry.off_input
|
|
253
|
+
self.on_changed = self._entry.on_changed
|
|
254
|
+
self.off_changed = self._entry.off_changed
|
|
255
|
+
self.on_enter = self._entry.on_enter
|
|
256
|
+
self.off_enter = self._entry.off_enter
|
|
257
|
+
self.on_invalid = self._entry.on_invalid
|
|
258
|
+
self.off_invalid = self._entry.off_invalid
|
|
259
|
+
self.on_valid = self._entry.on_valid
|
|
260
|
+
self.off_valid = self._entry.off_valid
|
|
261
|
+
self.on_validated = self._entry.on_validated
|
|
262
|
+
self.off_validated = self._entry.off_validated
|
|
263
|
+
|
|
264
|
+
# entry state
|
|
265
|
+
self.variable = self._entry.textvariable
|
|
266
|
+
self.signal = self._entry.textsignal
|
|
267
|
+
|
|
268
|
+
# enty validation
|
|
269
|
+
self.add_validation_rule = self._entry.add_validation_rule
|
|
270
|
+
self.add_validation_rules = self._entry.add_validation_rules
|
|
271
|
+
self.validation = self._entry.validate
|
|
272
|
+
|
|
273
|
+
# Copy Field's delegate handlers to entry for configuration forwarding
|
|
274
|
+
for key, method_name in self._configure_delegate_map.items():
|
|
275
|
+
if hasattr(self, method_name):
|
|
276
|
+
# Attach the Field's handler method to the entry instance
|
|
277
|
+
setattr(self._entry, method_name, getattr(self, method_name))
|
|
278
|
+
# Add to entry's delegate map
|
|
279
|
+
self._entry._configure_delegate_map[key] = method_name
|
|
280
|
+
|
|
281
|
+
# Forward configuration methods to entry widget
|
|
282
|
+
self.configure = self._entry.configure
|
|
283
|
+
self.config = self._entry.config
|
|
284
|
+
self.cget = self._entry.cget
|
|
285
|
+
self.__getitem__ = self._entry.__getitem__
|
|
286
|
+
self.__setitem__ = self._entry.__setitem__
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def value(self):
|
|
290
|
+
"""Get or set the parsed value via the underlying entry widget."""
|
|
291
|
+
return self._entry.value()
|
|
292
|
+
|
|
293
|
+
@value.setter
|
|
294
|
+
def value(self, value):
|
|
295
|
+
self._entry.value(value)
|
|
296
|
+
|
|
297
|
+
def get(self):
|
|
298
|
+
"""Return the raw text from the underlying entry widget."""
|
|
299
|
+
return self._entry.get()
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def entry_widget(self) -> NumberEntryPart | TextEntryPart:
|
|
303
|
+
"""Get the underlying entry widget."""
|
|
304
|
+
return self._entry
|
|
305
|
+
|
|
306
|
+
@property
|
|
307
|
+
def label_widget(self):
|
|
308
|
+
"""Get the label widget."""
|
|
309
|
+
return self._label_lbl
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def message_widget(self):
|
|
313
|
+
"""Get the message widget."""
|
|
314
|
+
return self._message_lbl
|
|
315
|
+
|
|
316
|
+
@property
|
|
317
|
+
def addons(self):
|
|
318
|
+
"""Get the dictionary of inserted addon widgets"""
|
|
319
|
+
return self._addons
|
|
320
|
+
|
|
321
|
+
@configure_delegate
|
|
322
|
+
def _config_accent(self, value=None):
|
|
323
|
+
if value is None:
|
|
324
|
+
return self._accent
|
|
325
|
+
else:
|
|
326
|
+
self._accent = value
|
|
327
|
+
self._field['accent'] = value
|
|
328
|
+
return None
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def disable(self):
|
|
332
|
+
"""Disable the field, preventing user input."""
|
|
333
|
+
self._entry.state(['disabled !readonly'])
|
|
334
|
+
self._field.state(['disabled'])
|
|
335
|
+
self._set_addons_state(True)
|
|
336
|
+
|
|
337
|
+
def enable(self):
|
|
338
|
+
"""Enable the field, allowing user input."""
|
|
339
|
+
self._entry.state(['!disabled !readonly'])
|
|
340
|
+
self._field.state(['!disabled'])
|
|
341
|
+
self._set_addons_state(False)
|
|
342
|
+
|
|
343
|
+
def readonly(self, value: bool = None):
|
|
344
|
+
"""Set or toggle the readonly state of the field."""
|
|
345
|
+
if value == False:
|
|
346
|
+
self._field.state(['disabled'])
|
|
347
|
+
self._entry.state(['readonly'])
|
|
348
|
+
elif value:
|
|
349
|
+
self._field.state(['!disabled'])
|
|
350
|
+
self._entry.state(['readonly'])
|
|
351
|
+
else:
|
|
352
|
+
self._entry.state(['readonly !disabled'])
|
|
353
|
+
self._field.state(['disabled'])
|
|
354
|
+
self._sync_addon_state()
|
|
355
|
+
|
|
356
|
+
def insert_addon(
|
|
357
|
+
self,
|
|
358
|
+
widget: Type[Union[Button, Label, CheckButton]],
|
|
359
|
+
position: Literal['before', 'after'],
|
|
360
|
+
name: str | None = None,
|
|
361
|
+
pack_options: dict[str, Any] = None,
|
|
362
|
+
**kwargs: Any
|
|
363
|
+
):
|
|
364
|
+
"""Insert a widget addon before or after the entry input.
|
|
365
|
+
|
|
366
|
+
Addons are Button, Label, or Checkbutton widgets positioned inside the field container,
|
|
367
|
+
either before (left of) or after (right of) the entry input. Common use
|
|
368
|
+
cases include search buttons, icons, clear buttons, or status indicators.
|
|
369
|
+
|
|
370
|
+
The addon widget automatically:
|
|
371
|
+
- Inherits the field's disabled state
|
|
372
|
+
- Participates in focus state styling (highlights field on addon focus)
|
|
373
|
+
- Is stored in the addons dictionary for later reference
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
widget: Widget class to instantiate. Must be Button, Label, or Checkbutton.
|
|
377
|
+
position: Position relative to the entry input:
|
|
378
|
+
- 'before': Insert to the left of the entry (prefix)
|
|
379
|
+
- 'after': Insert to the right of the entry (suffix)
|
|
380
|
+
name: Optional name for the addon. If provided, the addon can be
|
|
381
|
+
retrieved from the addons dictionary using this name. If None,
|
|
382
|
+
the widget's string representation is used as the key.
|
|
383
|
+
pack_options: Optional dictionary of additional pack() options to
|
|
384
|
+
apply when placing the addon widget. Common options include
|
|
385
|
+
padx, pady, etc. The side and after/before options are set
|
|
386
|
+
automatically based on position.
|
|
387
|
+
**kwargs (Any): Additional keyword arguments passed to the widget constructor.
|
|
388
|
+
For Button: text, command, icon, bootstyle, etc.
|
|
389
|
+
For Label: text, icon, image, bootstyle, etc.
|
|
390
|
+
Note: bootstyle and takefocus are set automatically but can be
|
|
391
|
+
overridden.
|
|
392
|
+
"""
|
|
393
|
+
variant = "suffix" if position == "after" else "prefix"
|
|
394
|
+
kwargs.setdefault('ttk_class', 'TField')
|
|
395
|
+
kwargs.setdefault('variant', variant)
|
|
396
|
+
kwargs.setdefault('takefocus', False)
|
|
397
|
+
kwargs.setdefault('density', self._density)
|
|
398
|
+
|
|
399
|
+
if widget in (Button, CheckButton):
|
|
400
|
+
icon_only = kwargs.get('icon_only', False)
|
|
401
|
+
if 'style_options' in kwargs:
|
|
402
|
+
kwargs['style_options'].update(use_active_states=True, density=self._density, icon_only=icon_only)
|
|
403
|
+
else:
|
|
404
|
+
kwargs['style_options'] = dict(use_active_states=True, density=self._density, icon_only=icon_only)
|
|
405
|
+
instance = widget(master=self._field, **kwargs)
|
|
406
|
+
key = name or str(instance)
|
|
407
|
+
self._addons[key] = instance
|
|
408
|
+
|
|
409
|
+
# configure layout
|
|
410
|
+
options = pack_options or {}
|
|
411
|
+
if position == "after":
|
|
412
|
+
options.update(side="right", after=self._entry)
|
|
413
|
+
else:
|
|
414
|
+
options.update(side="left", before=self._entry)
|
|
415
|
+
instance.pack(**options)
|
|
416
|
+
|
|
417
|
+
# match parent disabled state
|
|
418
|
+
self._sync_addon_state()
|
|
419
|
+
|
|
420
|
+
# bind focus events to field frame
|
|
421
|
+
instance.bind('<FocusIn>', lambda _: self._field.state(['focus']), add=True)
|
|
422
|
+
instance.bind('<FocusOut>', lambda _: self._field.state(['!focus']), add=True)
|
|
423
|
+
|
|
424
|
+
def _show_error(self, event: Any) -> None:
|
|
425
|
+
"""Display a validation error message below the input field."""
|
|
426
|
+
self._message_lbl['text'] = event.data['message']
|
|
427
|
+
self._message_lbl['accent'] = "danger"
|
|
428
|
+
self._message_lbl.pack(side='top', after=self._field, padx=4)
|
|
429
|
+
|
|
430
|
+
def _clear_error(self, _: Any) -> None:
|
|
431
|
+
"""Clear the error message and restore the original message text."""
|
|
432
|
+
self._message_lbl['text'] = self._message_text
|
|
433
|
+
self._message_lbl['accent'] = "secondary"
|
|
434
|
+
|
|
435
|
+
def _set_addons_state(self, disabled: bool) -> None:
|
|
436
|
+
"""Configure addon widgets based on whether the entry is interactive."""
|
|
437
|
+
state_value = 'disabled' if disabled else '!disabled'
|
|
438
|
+
for item in self._addons.values():
|
|
439
|
+
try:
|
|
440
|
+
item.configure(state=state_value)
|
|
441
|
+
except TclError:
|
|
442
|
+
pass
|
|
443
|
+
|
|
444
|
+
def _sync_addon_state(self, event: Any = None) -> None:
|
|
445
|
+
"""Ensure addons match the entry's interactivity state."""
|
|
446
|
+
entry_states = self._entry.state()
|
|
447
|
+
disabled = 'disabled' in entry_states or 'readonly' in entry_states
|
|
448
|
+
self._set_addons_state(disabled)
|