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,138 @@
|
|
|
1
|
+
"""BracketMatcher — highlights matching bracket pairs on cursor move.
|
|
2
|
+
|
|
3
|
+
Filter-style extension: binds to cursor-movement events and applies a
|
|
4
|
+
highlight tag to the bracket under the cursor and its matching pair.
|
|
5
|
+
Uses a flash-and-restore pattern borrowed from idlelib/parenmatch.py.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import tkinter as tk
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from bootstack.widgets._impl.composites.textarea.filter import EditFilter
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from bootstack.widgets._impl.composites.textarea.core import _MultilineCore
|
|
16
|
+
|
|
17
|
+
_OPENERS = "([{"
|
|
18
|
+
_CLOSERS = ")]}"
|
|
19
|
+
_PAIRS = dict(zip(_OPENERS, _CLOSERS))
|
|
20
|
+
_REVERSE = dict(zip(_CLOSERS, _OPENERS))
|
|
21
|
+
|
|
22
|
+
_LAYER = "bracket"
|
|
23
|
+
_STYLE = "match"
|
|
24
|
+
_TAG = f"bs::{_LAYER}::{_STYLE}"
|
|
25
|
+
_FLASH_MS = 500 # how long to show the highlight
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BracketMatcher(EditFilter):
|
|
29
|
+
"""Highlights matching bracket pairs when the cursor rests on a bracket.
|
|
30
|
+
|
|
31
|
+
Install via `core.add_filter(BracketMatcher())`.
|
|
32
|
+
The highlight tag `bs::bracket::match` is applied to both the bracket
|
|
33
|
+
under the cursor and its matching partner. It is removed after
|
|
34
|
+
`_FLASH_MS` milliseconds or on the next cursor move.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self) -> None:
|
|
38
|
+
super().__init__()
|
|
39
|
+
self._core: _MultilineCore | None = None
|
|
40
|
+
self._after_id: str | None = None
|
|
41
|
+
|
|
42
|
+
def attach(self, core: _MultilineCore) -> None:
|
|
43
|
+
self._core = core
|
|
44
|
+
core.define_style(_STYLE, foreground="primary", background="primary[subtle]")
|
|
45
|
+
core.register_layer(_LAYER, priority=5)
|
|
46
|
+
core.text.bind("<<CursorMove>>", self._on_cursor_move, add="+")
|
|
47
|
+
core.text.bind("<KeyRelease>", self._on_cursor_move, add="+")
|
|
48
|
+
core.text.bind("<ButtonRelease-1>", self._on_cursor_move, add="+")
|
|
49
|
+
|
|
50
|
+
def detach(self, core: _MultilineCore) -> None:
|
|
51
|
+
self._clear()
|
|
52
|
+
self._core = None
|
|
53
|
+
|
|
54
|
+
# ── filter pass-through (BracketMatcher doesn't intercept edits) ──────
|
|
55
|
+
|
|
56
|
+
def insert(self, index: str, chars: str, tags=None) -> None:
|
|
57
|
+
self.delegate.insert(index, chars, tags)
|
|
58
|
+
|
|
59
|
+
def delete(self, index1: str, index2: str | None = None) -> None:
|
|
60
|
+
self.delegate.delete(index1, index2)
|
|
61
|
+
|
|
62
|
+
# ── matching logic ────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
def _on_cursor_move(self, _event=None) -> None:
|
|
65
|
+
self._clear()
|
|
66
|
+
if self._core is None:
|
|
67
|
+
return
|
|
68
|
+
text = self._core.text
|
|
69
|
+
cursor = text.index("insert")
|
|
70
|
+
# Check character at cursor and the one before it
|
|
71
|
+
for idx in (cursor, f"{cursor}-1c"):
|
|
72
|
+
ch = text.get(idx)
|
|
73
|
+
if ch in _OPENERS:
|
|
74
|
+
partner = self._find_close(text, idx, ch)
|
|
75
|
+
if partner:
|
|
76
|
+
self._highlight(idx, partner)
|
|
77
|
+
return
|
|
78
|
+
if ch in _CLOSERS:
|
|
79
|
+
partner = self._find_open(text, idx, ch)
|
|
80
|
+
if partner:
|
|
81
|
+
self._highlight(partner, idx)
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
def _find_close(self, text: tk.Text, start: str, opener: str) -> str | None:
|
|
85
|
+
closer = _PAIRS[opener]
|
|
86
|
+
depth = 1
|
|
87
|
+
idx = text.index(f"{start}+1c")
|
|
88
|
+
end = text.index("end")
|
|
89
|
+
while text.compare(idx, "<", end):
|
|
90
|
+
ch = text.get(idx)
|
|
91
|
+
if ch == opener:
|
|
92
|
+
depth += 1
|
|
93
|
+
elif ch == closer:
|
|
94
|
+
depth -= 1
|
|
95
|
+
if depth == 0:
|
|
96
|
+
return idx
|
|
97
|
+
idx = text.index(f"{idx}+1c")
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
def _find_open(self, text: tk.Text, start: str, closer: str) -> str | None:
|
|
101
|
+
opener = _REVERSE[closer]
|
|
102
|
+
depth = 1
|
|
103
|
+
idx = text.index(f"{start}-1c")
|
|
104
|
+
while text.compare(idx, ">=", "1.0"):
|
|
105
|
+
ch = text.get(idx)
|
|
106
|
+
if ch == closer:
|
|
107
|
+
depth += 1
|
|
108
|
+
elif ch == opener:
|
|
109
|
+
depth -= 1
|
|
110
|
+
if depth == 0:
|
|
111
|
+
return idx
|
|
112
|
+
if text.compare(idx, "==", "1.0"):
|
|
113
|
+
break
|
|
114
|
+
idx = text.index(f"{idx}-1c")
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
def _highlight(self, a: str, b: str) -> None:
|
|
118
|
+
if self._core is None:
|
|
119
|
+
return
|
|
120
|
+
text = self._core.text
|
|
121
|
+
for idx in (a, b):
|
|
122
|
+
text.tag_add(_TAG, idx, f"{idx}+1c")
|
|
123
|
+
if self._after_id is not None:
|
|
124
|
+
text.after_cancel(self._after_id)
|
|
125
|
+
self._after_id = text.after(_FLASH_MS, self._clear)
|
|
126
|
+
|
|
127
|
+
def _clear(self) -> None:
|
|
128
|
+
if self._after_id is not None and self._core is not None:
|
|
129
|
+
try:
|
|
130
|
+
self._core.text.after_cancel(self._after_id)
|
|
131
|
+
except Exception:
|
|
132
|
+
pass
|
|
133
|
+
self._after_id = None
|
|
134
|
+
if self._core is not None:
|
|
135
|
+
try:
|
|
136
|
+
self._core.text.tag_remove(_TAG, "1.0", "end")
|
|
137
|
+
except Exception:
|
|
138
|
+
pass
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""IndentGuides — draws subtle guide marks at each indent stop.
|
|
2
|
+
|
|
3
|
+
Filter-style extension: on each edit (debounced), scans all lines and
|
|
4
|
+
places a single-character background highlight at the last space of each
|
|
5
|
+
tab-stop in the leading whitespace, producing a visual column guide.
|
|
6
|
+
|
|
7
|
+
Only space-based indentation is supported. Tab-character indentation is
|
|
8
|
+
ignored (guide count will be zero for tab-indented lines).
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Any, TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
from bootstack.widgets._impl.composites.textarea.filter import EditFilter
|
|
15
|
+
from bootstack.widgets._impl.composites.textarea.decoration import Position, RangeDecoration
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from bootstack.widgets._impl.composites.textarea.core import _MultilineCore
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
_LAYER = "indent"
|
|
22
|
+
_DEBOUNCE_MS = 200
|
|
23
|
+
_STYLE = "guide"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class IndentGuides(EditFilter):
|
|
27
|
+
"""Draws subtle vertical guide marks at each indent stop.
|
|
28
|
+
|
|
29
|
+
Install via the `show_indent_guides=True` parameter on `CodeEditor`,
|
|
30
|
+
or manually: `core.add_filter(IndentGuides())`.
|
|
31
|
+
|
|
32
|
+
Guide marks are placed on the last space character of each tab-stop
|
|
33
|
+
group in a line's leading whitespace (e.g. at column 3, 7, 11, ...
|
|
34
|
+
for `tab_width=4`). Only space-indented lines receive guides.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
tab_width: Spaces per indent level. Should match `SmartIndent`.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, tab_width: int = 4) -> None:
|
|
41
|
+
super().__init__()
|
|
42
|
+
self._core: _MultilineCore | None = None
|
|
43
|
+
self._after_id: str | None = None
|
|
44
|
+
self._tab_width = tab_width
|
|
45
|
+
self._theme_root: Any = None
|
|
46
|
+
self._theme_bind_id: str | None = None
|
|
47
|
+
|
|
48
|
+
# ── EditFilter protocol ───────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
def attach(self, core: _MultilineCore) -> None:
|
|
51
|
+
self._core = core
|
|
52
|
+
core.register_layer(_LAYER, priority=0)
|
|
53
|
+
self._apply_guide_style(core)
|
|
54
|
+
core.text.bind("<<EditorBgChanged>>", self._on_theme_changed, add="+")
|
|
55
|
+
# Recalibrate guide color after a theme rebuild. Bind <<BsThemeChanged>>
|
|
56
|
+
# on the root — fired ONCE by bootstack after the full rebuild (correct
|
|
57
|
+
# colors) — NOT the ttk <<ThemeChanged>>, which fires mid-rebuild.
|
|
58
|
+
self._theme_root = core.winfo_toplevel()
|
|
59
|
+
self._theme_bind_id = self._theme_root.bind(
|
|
60
|
+
"<<BsThemeChanged>>", self._on_theme_changed, add="+"
|
|
61
|
+
)
|
|
62
|
+
self._schedule()
|
|
63
|
+
|
|
64
|
+
def detach(self, core: _MultilineCore) -> None:
|
|
65
|
+
self._cancel()
|
|
66
|
+
if self._theme_bind_id is not None and self._theme_root is not None:
|
|
67
|
+
try:
|
|
68
|
+
self._theme_root.unbind("<<BsThemeChanged>>", self._theme_bind_id)
|
|
69
|
+
except Exception:
|
|
70
|
+
pass
|
|
71
|
+
self._theme_bind_id = None
|
|
72
|
+
self._theme_root = None
|
|
73
|
+
if self._core is not None:
|
|
74
|
+
core.clear_decorations(_LAYER)
|
|
75
|
+
self._core = None
|
|
76
|
+
|
|
77
|
+
def insert(self, index: str, chars: str, tags=None) -> None:
|
|
78
|
+
self.delegate.insert(index, chars, tags)
|
|
79
|
+
self._schedule()
|
|
80
|
+
|
|
81
|
+
def delete(self, index1: str, index2: str | None = None) -> None:
|
|
82
|
+
self.delegate.delete(index1, index2)
|
|
83
|
+
self._schedule()
|
|
84
|
+
|
|
85
|
+
# ── internal ──────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
def _apply_guide_style(self, core: _MultilineCore) -> None:
|
|
88
|
+
try:
|
|
89
|
+
bg = core.text.cget("background")
|
|
90
|
+
guide_bg = _subtle_variant(bg)
|
|
91
|
+
except Exception:
|
|
92
|
+
guide_bg = "#cccccc"
|
|
93
|
+
core.define_style(_STYLE, background=guide_bg)
|
|
94
|
+
|
|
95
|
+
def _on_theme_changed(self, _event=None) -> None:
|
|
96
|
+
if self._core is not None:
|
|
97
|
+
self._apply_guide_style(self._core)
|
|
98
|
+
|
|
99
|
+
def _schedule(self) -> None:
|
|
100
|
+
self._cancel()
|
|
101
|
+
if self._core is not None:
|
|
102
|
+
self._after_id = self._core.text.after(_DEBOUNCE_MS, self._update)
|
|
103
|
+
|
|
104
|
+
def _cancel(self) -> None:
|
|
105
|
+
if self._after_id is not None and self._core is not None:
|
|
106
|
+
try:
|
|
107
|
+
self._core.text.after_cancel(self._after_id)
|
|
108
|
+
except Exception:
|
|
109
|
+
pass
|
|
110
|
+
self._after_id = None
|
|
111
|
+
|
|
112
|
+
def _update(self) -> None:
|
|
113
|
+
self._after_id = None
|
|
114
|
+
if self._core is None:
|
|
115
|
+
return
|
|
116
|
+
try:
|
|
117
|
+
text = self._core.value
|
|
118
|
+
except Exception:
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
decorations = _compute_guides(text, self._tab_width)
|
|
122
|
+
self._core.set_decorations(_LAYER, decorations)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _compute_guides(text: str, tab_width: int) -> list[RangeDecoration]:
|
|
126
|
+
"""Return guide decorations for every indent stop in `text`."""
|
|
127
|
+
decorations: list[RangeDecoration] = []
|
|
128
|
+
for line_num, line in enumerate(text.split("\n"), 1):
|
|
129
|
+
n = len(line) - len(line.lstrip(" "))
|
|
130
|
+
for k in range(1, n // tab_width + 1):
|
|
131
|
+
col = k * tab_width - 1
|
|
132
|
+
decorations.append(RangeDecoration(
|
|
133
|
+
Position(line_num, col),
|
|
134
|
+
Position(line_num, col + 1),
|
|
135
|
+
_STYLE,
|
|
136
|
+
))
|
|
137
|
+
return decorations
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _subtle_variant(hex_color: str) -> str:
|
|
141
|
+
"""Return a slightly darker or lighter shade of `hex_color`."""
|
|
142
|
+
h = hex_color.lstrip("#")
|
|
143
|
+
if len(h) < 6:
|
|
144
|
+
return "#cccccc"
|
|
145
|
+
try:
|
|
146
|
+
r, g, b = int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
|
|
147
|
+
luminance = 0.299 * r + 0.587 * g + 0.114 * b
|
|
148
|
+
if luminance > 128:
|
|
149
|
+
r2 = max(0, int(r * 0.88))
|
|
150
|
+
g2 = max(0, int(g * 0.88))
|
|
151
|
+
b2 = max(0, int(b * 0.88))
|
|
152
|
+
else:
|
|
153
|
+
r2 = min(255, r + 28)
|
|
154
|
+
g2 = min(255, g + 28)
|
|
155
|
+
b2 = min(255, b + 28)
|
|
156
|
+
return f"#{r2:02x}{g2:02x}{b2:02x}"
|
|
157
|
+
except Exception:
|
|
158
|
+
return "#cccccc"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""LineNumbers sidebar — displays line numbers synced with the main text.
|
|
2
|
+
|
|
3
|
+
Port of idlelib/sidebar.py LineNumbers. Uses a sibling tk.Text widget
|
|
4
|
+
(read-only, no padding, matching font) placed to the left of the main text.
|
|
5
|
+
Width auto-adjusts when the line count crosses a power-of-ten boundary.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import tkinter as tk
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from bootstack.widgets._impl.composites.textarea.sidebar import Sidebar
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from bootstack.widgets._impl.composites.textarea.core import _MultilineCore
|
|
16
|
+
|
|
17
|
+
_PADX = 6 # horizontal padding inside the line-number gutter
|
|
18
|
+
_PADY = 1 # top padding to align with text widget baseline
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class LineNumbers(Sidebar):
|
|
22
|
+
"""Displays line numbers alongside the text, scroll-synced.
|
|
23
|
+
|
|
24
|
+
Add to a `CodeEditor` or `TextArea` core via::
|
|
25
|
+
|
|
26
|
+
core.add_sidebar(LineNumbers())
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
super().__init__()
|
|
31
|
+
self._widget: tk.Text | None = None
|
|
32
|
+
self._core: _MultilineCore | None = None
|
|
33
|
+
self._last_line_count: int = 0
|
|
34
|
+
self._current_width: int = 1 # digits in the widest line number
|
|
35
|
+
|
|
36
|
+
# ── Sidebar protocol ──────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
def _build(self, core: _MultilineCore) -> None:
|
|
39
|
+
self._core = core
|
|
40
|
+
font = core.text.cget("font") or "TkFixedFont"
|
|
41
|
+
|
|
42
|
+
self._widget = tk.Text(
|
|
43
|
+
core,
|
|
44
|
+
width=self._current_width,
|
|
45
|
+
height=core.text.cget("height"),
|
|
46
|
+
padx=_PADX,
|
|
47
|
+
pady=_PADY,
|
|
48
|
+
state=tk.DISABLED,
|
|
49
|
+
wrap=tk.NONE,
|
|
50
|
+
font=font,
|
|
51
|
+
cursor="arrow",
|
|
52
|
+
takefocus=False,
|
|
53
|
+
highlightthickness=0,
|
|
54
|
+
relief=tk.FLAT,
|
|
55
|
+
borderwidth=0,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Grid in col 0 (left sidebar column), same row as the main text
|
|
59
|
+
self._widget.grid(row=0, column=0, sticky="ns")
|
|
60
|
+
|
|
61
|
+
self._apply_colors()
|
|
62
|
+
self._update()
|
|
63
|
+
|
|
64
|
+
def _destroy(self) -> None:
|
|
65
|
+
if self._widget is not None:
|
|
66
|
+
try:
|
|
67
|
+
self._widget.destroy()
|
|
68
|
+
except Exception:
|
|
69
|
+
pass
|
|
70
|
+
self._widget = None
|
|
71
|
+
|
|
72
|
+
def on_scroll(self, first: str, last: str) -> None:
|
|
73
|
+
"""Sync the line-number gutter with the main text scroll position."""
|
|
74
|
+
if self._widget is not None:
|
|
75
|
+
self._widget.yview_moveto(first)
|
|
76
|
+
|
|
77
|
+
def on_change(self) -> None:
|
|
78
|
+
"""Refresh line numbers after every edit."""
|
|
79
|
+
self._update()
|
|
80
|
+
|
|
81
|
+
def update_font(self) -> None:
|
|
82
|
+
"""Sync the gutter font with the main text font."""
|
|
83
|
+
if self._widget and self._core:
|
|
84
|
+
font = self._core.text.cget("font")
|
|
85
|
+
self._widget.configure(font=font)
|
|
86
|
+
|
|
87
|
+
def update_colors(self, event=None) -> None:
|
|
88
|
+
"""Reapply theme colors."""
|
|
89
|
+
self._apply_colors()
|
|
90
|
+
|
|
91
|
+
# ── internals ─────────────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
def _update(self) -> None:
|
|
94
|
+
"""Recompute and render the line numbers."""
|
|
95
|
+
if self._widget is None or self._core is None:
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
line_count = self._line_count()
|
|
99
|
+
self._adjust_width(line_count)
|
|
100
|
+
|
|
101
|
+
numbers = "\n".join(str(i) for i in range(1, line_count + 1))
|
|
102
|
+
|
|
103
|
+
self._widget.configure(state=tk.NORMAL)
|
|
104
|
+
self._widget.delete("1.0", tk.END)
|
|
105
|
+
self._widget.insert("1.0", numbers)
|
|
106
|
+
self._widget.configure(state=tk.DISABLED)
|
|
107
|
+
|
|
108
|
+
self._last_line_count = line_count
|
|
109
|
+
|
|
110
|
+
def _line_count(self) -> int:
|
|
111
|
+
"""Return the number of lines in the main text widget."""
|
|
112
|
+
if self._core is None:
|
|
113
|
+
return 1
|
|
114
|
+
idx = self._core.text.index("end-1c")
|
|
115
|
+
return int(idx.split(".")[0])
|
|
116
|
+
|
|
117
|
+
def _adjust_width(self, line_count: int) -> None:
|
|
118
|
+
"""Expand or contract the gutter when line count crosses a decade."""
|
|
119
|
+
needed = len(str(line_count))
|
|
120
|
+
if needed != self._current_width:
|
|
121
|
+
self._current_width = needed
|
|
122
|
+
if self._widget:
|
|
123
|
+
self._widget.configure(width=needed)
|
|
124
|
+
|
|
125
|
+
def _apply_colors(self) -> None:
|
|
126
|
+
"""Apply theme-aware foreground/background to the gutter."""
|
|
127
|
+
if self._widget is None:
|
|
128
|
+
return
|
|
129
|
+
try:
|
|
130
|
+
from bootstack.style.style import get_theme_provider
|
|
131
|
+
colors = get_theme_provider().colors
|
|
132
|
+
fg = colors.get("muted", "#888888")
|
|
133
|
+
bg = colors.get("chrome", "#f0f0f0")
|
|
134
|
+
self._widget.configure(foreground=fg, background=bg,
|
|
135
|
+
selectbackground=bg, selectforeground=fg,
|
|
136
|
+
inactiveselectbackground=bg)
|
|
137
|
+
except Exception:
|
|
138
|
+
pass
|