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,133 @@
|
|
|
1
|
+
"""CLI entry point for bootstack.
|
|
2
|
+
|
|
3
|
+
The bootstack CLI provides commands for:
|
|
4
|
+
- Creating new projects (start)
|
|
5
|
+
- Running applications (run)
|
|
6
|
+
- Building for distribution (build)
|
|
7
|
+
- Adding components (add)
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
bootstack start <appname> Create a new project
|
|
11
|
+
bootstack run [path] Run the application
|
|
12
|
+
bootstack dev [path] Run with hot reload (live edit-and-save)
|
|
13
|
+
bootstack promote --pyinstaller Enable PyInstaller support
|
|
14
|
+
bootstack build Build for distribution
|
|
15
|
+
bootstack add page <ClassName> Add a new page (AppShell)
|
|
16
|
+
bootstack add view <ClassName> Add a new view
|
|
17
|
+
bootstack add dialog <ClassName> Add a new dialog
|
|
18
|
+
bootstack add i18n Add i18n support
|
|
19
|
+
bootstack doctor Diagnose project and environment health
|
|
20
|
+
bootstack gallery Launch the widget gallery
|
|
21
|
+
bootstack icons Browse Bootstrap Icons
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import argparse
|
|
27
|
+
import sys
|
|
28
|
+
from typing import Sequence
|
|
29
|
+
|
|
30
|
+
from bootstack.cli import add, appicon, build, dev, doctor, icons, promote, run, start
|
|
31
|
+
from bootstack.cli.demo import run_demo
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main(argv: Sequence[str] | None = None) -> None:
|
|
35
|
+
"""Dispatch CLI commands registered in bootstack."""
|
|
36
|
+
parser = argparse.ArgumentParser(
|
|
37
|
+
prog="bootstack",
|
|
38
|
+
description="bootstack command line interface",
|
|
39
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
40
|
+
epilog="""\
|
|
41
|
+
Examples:
|
|
42
|
+
bootstack start MyApp Create a new project
|
|
43
|
+
bootstack start MyApp --template appshell Create an AppShell project
|
|
44
|
+
bootstack start MyApp --theme superhero Use a specific theme
|
|
45
|
+
bootstack run Run the application
|
|
46
|
+
bootstack dev app.py Run with hot reload
|
|
47
|
+
bootstack promote --pyinstaller Enable PyInstaller support
|
|
48
|
+
bootstack build Build for distribution
|
|
49
|
+
bootstack add view SettingsView Add a new view
|
|
50
|
+
bootstack doctor Diagnose project and environment health
|
|
51
|
+
bootstack gallery Launch the widget gallery
|
|
52
|
+
bootstack icons Browse Bootstrap Icons
|
|
53
|
+
|
|
54
|
+
For more information on a command:
|
|
55
|
+
bootstack <command> --help
|
|
56
|
+
""",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
parser.add_argument(
|
|
60
|
+
"--version",
|
|
61
|
+
action="version",
|
|
62
|
+
version=_get_version(),
|
|
63
|
+
)
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"-v",
|
|
66
|
+
"--verbose",
|
|
67
|
+
action="store_true",
|
|
68
|
+
help="Print full tracebacks on error",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
subparsers = parser.add_subparsers(
|
|
72
|
+
dest="command",
|
|
73
|
+
title="commands",
|
|
74
|
+
metavar="<command>",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Register commands
|
|
78
|
+
start.add_parser(subparsers)
|
|
79
|
+
run.add_parser(subparsers)
|
|
80
|
+
dev.add_parser(subparsers)
|
|
81
|
+
promote.add_parser(subparsers)
|
|
82
|
+
build.add_parser(subparsers)
|
|
83
|
+
add.add_parser(subparsers)
|
|
84
|
+
doctor.add_parser(subparsers)
|
|
85
|
+
icons.add_parser(subparsers)
|
|
86
|
+
appicon.add_parser(subparsers)
|
|
87
|
+
|
|
88
|
+
# Gallery command
|
|
89
|
+
gallery_parser = subparsers.add_parser(
|
|
90
|
+
"gallery",
|
|
91
|
+
help="Launch the widget gallery",
|
|
92
|
+
)
|
|
93
|
+
gallery_parser.set_defaults(func=lambda args: run_demo())
|
|
94
|
+
|
|
95
|
+
# demo is kept as a backwards-compatible alias
|
|
96
|
+
demo_parser = subparsers.add_parser(
|
|
97
|
+
"demo",
|
|
98
|
+
help=argparse.SUPPRESS,
|
|
99
|
+
)
|
|
100
|
+
demo_parser.set_defaults(func=lambda args: run_demo())
|
|
101
|
+
|
|
102
|
+
# Parse arguments
|
|
103
|
+
args = parser.parse_args(argv)
|
|
104
|
+
func = getattr(args, "func", None)
|
|
105
|
+
|
|
106
|
+
if func is None:
|
|
107
|
+
parser.print_help()
|
|
108
|
+
sys.exit(0)
|
|
109
|
+
|
|
110
|
+
# Execute command
|
|
111
|
+
try:
|
|
112
|
+
func(args)
|
|
113
|
+
except KeyboardInterrupt:
|
|
114
|
+
print("\nInterrupted.")
|
|
115
|
+
sys.exit(1)
|
|
116
|
+
except Exception as e:
|
|
117
|
+
if getattr(args, "verbose", False):
|
|
118
|
+
import traceback
|
|
119
|
+
traceback.print_exc()
|
|
120
|
+
else:
|
|
121
|
+
print(f"Error: {e}")
|
|
122
|
+
print("(Run with --verbose for the full traceback.)")
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _get_version() -> str:
|
|
127
|
+
"""Get the bootstack version string."""
|
|
128
|
+
try:
|
|
129
|
+
import bootstack
|
|
130
|
+
|
|
131
|
+
return f"bootstack {bootstack.__version__}"
|
|
132
|
+
except Exception:
|
|
133
|
+
return "bootstack (unknown version)"
|
bootstack/cli/add.py
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
"""bootstack add command - Add views, dialogs, and i18n to a project."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from bootstack.cli.config import TtkbConfig, find_config
|
|
9
|
+
from bootstack.cli.templates import (
|
|
10
|
+
_build_func_name,
|
|
11
|
+
create_dialog,
|
|
12
|
+
create_page,
|
|
13
|
+
create_view,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def add_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
18
|
+
"""Add the 'add' subcommand parser."""
|
|
19
|
+
parser = subparsers.add_parser(
|
|
20
|
+
"add",
|
|
21
|
+
help="Add components to the project",
|
|
22
|
+
description="Add views, pages, dialogs, or i18n support to the project.",
|
|
23
|
+
)
|
|
24
|
+
add_subparsers = parser.add_subparsers(dest="component")
|
|
25
|
+
|
|
26
|
+
# bootstack add page <ClassName>
|
|
27
|
+
page_parser = add_subparsers.add_parser(
|
|
28
|
+
"page",
|
|
29
|
+
help="Add a new page (for AppShell projects)",
|
|
30
|
+
)
|
|
31
|
+
page_parser.add_argument(
|
|
32
|
+
"class_name",
|
|
33
|
+
help="Page class name (CamelCase, e.g., 'DashboardPage')",
|
|
34
|
+
)
|
|
35
|
+
page_parser.add_argument(
|
|
36
|
+
"--dir",
|
|
37
|
+
type=Path,
|
|
38
|
+
default=None,
|
|
39
|
+
help="Target directory (default: src/<app>/pages/)",
|
|
40
|
+
)
|
|
41
|
+
page_parser.add_argument(
|
|
42
|
+
"--scrollable",
|
|
43
|
+
action="store_true",
|
|
44
|
+
help="Make the page scrollable (wraps content in a ScrollView)",
|
|
45
|
+
)
|
|
46
|
+
page_parser.set_defaults(func=run_add_page)
|
|
47
|
+
|
|
48
|
+
# bootstack add view <ClassName>
|
|
49
|
+
view_parser = add_subparsers.add_parser(
|
|
50
|
+
"view",
|
|
51
|
+
help="Add a new view",
|
|
52
|
+
)
|
|
53
|
+
view_parser.add_argument(
|
|
54
|
+
"class_name",
|
|
55
|
+
help="View class name (CamelCase, e.g., 'SettingsView')",
|
|
56
|
+
)
|
|
57
|
+
view_parser.add_argument(
|
|
58
|
+
"--container",
|
|
59
|
+
choices=["grid", "pack"],
|
|
60
|
+
default=None,
|
|
61
|
+
help="Container type (default: from bootstack.toml or 'grid')",
|
|
62
|
+
)
|
|
63
|
+
view_parser.add_argument(
|
|
64
|
+
"--dir",
|
|
65
|
+
type=Path,
|
|
66
|
+
default=None,
|
|
67
|
+
help="Target directory (default: src/<app>/views/)",
|
|
68
|
+
)
|
|
69
|
+
view_parser.set_defaults(func=run_add_view)
|
|
70
|
+
|
|
71
|
+
# bootstack add dialog <ClassName>
|
|
72
|
+
dialog_parser = add_subparsers.add_parser(
|
|
73
|
+
"dialog",
|
|
74
|
+
help="Add a new dialog",
|
|
75
|
+
)
|
|
76
|
+
dialog_parser.add_argument(
|
|
77
|
+
"class_name",
|
|
78
|
+
help="Dialog class name (CamelCase, e.g., 'ConfirmDialog')",
|
|
79
|
+
)
|
|
80
|
+
dialog_parser.add_argument(
|
|
81
|
+
"--dir",
|
|
82
|
+
type=Path,
|
|
83
|
+
default=None,
|
|
84
|
+
help="Target directory (default: src/<app>/dialogs/)",
|
|
85
|
+
)
|
|
86
|
+
dialog_parser.set_defaults(func=run_add_dialog)
|
|
87
|
+
|
|
88
|
+
# bootstack add i18n
|
|
89
|
+
i18n_parser = add_subparsers.add_parser(
|
|
90
|
+
"i18n",
|
|
91
|
+
help="Add internationalization support",
|
|
92
|
+
)
|
|
93
|
+
i18n_parser.add_argument(
|
|
94
|
+
"--languages",
|
|
95
|
+
nargs="+",
|
|
96
|
+
default=["es"],
|
|
97
|
+
help="Locale codes to scaffold (default: es)",
|
|
98
|
+
)
|
|
99
|
+
i18n_parser.add_argument(
|
|
100
|
+
"--po",
|
|
101
|
+
action="store_true",
|
|
102
|
+
help="Scaffold .po catalog files in assets/locales/ instead of a Python "
|
|
103
|
+
"translations module",
|
|
104
|
+
)
|
|
105
|
+
i18n_parser.set_defaults(func=run_add_i18n)
|
|
106
|
+
|
|
107
|
+
parser.set_defaults(func=lambda args: parser.print_help())
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def run_add_view(args: argparse.Namespace) -> None:
|
|
111
|
+
"""Add a new view to the project."""
|
|
112
|
+
class_name = args.class_name
|
|
113
|
+
|
|
114
|
+
# Validate class name
|
|
115
|
+
if not class_name[0].isupper():
|
|
116
|
+
print("Error: Class name should be CamelCase (e.g., 'SettingsView')")
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
# Find project configuration
|
|
120
|
+
config_path = find_config()
|
|
121
|
+
if config_path is None:
|
|
122
|
+
print("Error: No bootstack.toml found. Are you in a bootstack project?")
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
project_root = config_path.parent
|
|
126
|
+
config = TtkbConfig.load(config_path)
|
|
127
|
+
|
|
128
|
+
# Reject in AppShell projects - views belong to the basic template
|
|
129
|
+
if config.app.template == "appshell":
|
|
130
|
+
print("Error: 'bootstack add view' is for basic-template projects.")
|
|
131
|
+
print("This project uses the 'appshell' template. Use 'bootstack add page' instead.")
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
# Determine container type
|
|
135
|
+
container = args.container
|
|
136
|
+
if container is None:
|
|
137
|
+
container = config.layout.default_container
|
|
138
|
+
|
|
139
|
+
# Determine target directory
|
|
140
|
+
if args.dir:
|
|
141
|
+
target_dir = args.dir
|
|
142
|
+
else:
|
|
143
|
+
# Parse entry point to find source directory
|
|
144
|
+
entry_path = Path(config.app.entry)
|
|
145
|
+
if entry_path.parts[0] == "src" and len(entry_path.parts) >= 2:
|
|
146
|
+
module_name = entry_path.parts[1]
|
|
147
|
+
target_dir = project_root / "src" / module_name / "views"
|
|
148
|
+
else:
|
|
149
|
+
target_dir = project_root / "views"
|
|
150
|
+
|
|
151
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
152
|
+
|
|
153
|
+
# Ensure __init__.py exists
|
|
154
|
+
init_file = target_dir / "__init__.py"
|
|
155
|
+
if not init_file.exists():
|
|
156
|
+
init_file.write_text('"""Views package."""\n', encoding="utf-8")
|
|
157
|
+
|
|
158
|
+
# Create view
|
|
159
|
+
file_path = create_view(class_name, target_dir, container)
|
|
160
|
+
|
|
161
|
+
print(f"Created view: {file_path.relative_to(project_root)}")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def run_add_page(args: argparse.Namespace) -> None:
|
|
165
|
+
"""Add a new page to an AppShell project."""
|
|
166
|
+
class_name = args.class_name
|
|
167
|
+
|
|
168
|
+
# Validate class name
|
|
169
|
+
if not class_name[0].isupper():
|
|
170
|
+
print("Error: Class name should be CamelCase (e.g., 'DashboardPage')")
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
# Find project configuration
|
|
174
|
+
config_path = find_config()
|
|
175
|
+
if config_path is None:
|
|
176
|
+
print("Error: No bootstack.toml found. Are you in a bootstack project?")
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
project_root = config_path.parent
|
|
180
|
+
config = TtkbConfig.load(config_path)
|
|
181
|
+
|
|
182
|
+
# Check that this is an AppShell project
|
|
183
|
+
if config.app.template != "appshell":
|
|
184
|
+
print("Error: 'bootstack add page' is for AppShell projects.")
|
|
185
|
+
print("This project uses the 'basic' template. Use 'bootstack add view' instead.")
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
# Determine target directory and module name for the import hint
|
|
189
|
+
entry_path = Path(config.app.entry)
|
|
190
|
+
if entry_path.parts[0] == "src" and len(entry_path.parts) >= 2:
|
|
191
|
+
module_name = entry_path.parts[1]
|
|
192
|
+
default_target = project_root / "src" / module_name / "pages"
|
|
193
|
+
else:
|
|
194
|
+
module_name = None
|
|
195
|
+
default_target = project_root / "pages"
|
|
196
|
+
|
|
197
|
+
target_dir = args.dir if args.dir else default_target
|
|
198
|
+
|
|
199
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
200
|
+
|
|
201
|
+
# Ensure __init__.py exists
|
|
202
|
+
init_file = target_dir / "__init__.py"
|
|
203
|
+
if not init_file.exists():
|
|
204
|
+
init_file.write_text('"""Pages package."""\n', encoding="utf-8")
|
|
205
|
+
|
|
206
|
+
# Create page
|
|
207
|
+
scrollable = args.scrollable
|
|
208
|
+
file_path = create_page(class_name, target_dir, scrollable=scrollable)
|
|
209
|
+
|
|
210
|
+
func_name = _build_func_name(class_name)
|
|
211
|
+
print(f"Created page: {file_path.relative_to(project_root)}")
|
|
212
|
+
print()
|
|
213
|
+
print("This created the file only - it is NOT yet shown in the sidebar.")
|
|
214
|
+
print("To register it with the AppShell, paste these lines into main.py")
|
|
215
|
+
print("inside your shell.page_nav() block:")
|
|
216
|
+
print()
|
|
217
|
+
import_module = f"{module_name}.pages" if module_name else "<module>.pages"
|
|
218
|
+
print(f" from {import_module}.{file_path.stem} import {func_name}")
|
|
219
|
+
scrollable_arg = ", scrollable=True" if scrollable else ""
|
|
220
|
+
print(f' with nav.add_page("<id>", text="<Label>", icon="<icon>",')
|
|
221
|
+
print(f' padding=20, gap=12, horizontal_items="stretch"{scrollable_arg}):')
|
|
222
|
+
print(f" {func_name}()")
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def run_add_dialog(args: argparse.Namespace) -> None:
|
|
226
|
+
"""Add a new dialog to the project."""
|
|
227
|
+
class_name = args.class_name
|
|
228
|
+
|
|
229
|
+
# Validate class name
|
|
230
|
+
if not class_name[0].isupper():
|
|
231
|
+
print("Error: Class name should be CamelCase (e.g., 'ConfirmDialog')")
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
# Find project configuration
|
|
235
|
+
config_path = find_config()
|
|
236
|
+
if config_path is None:
|
|
237
|
+
print("Error: No bootstack.toml found. Are you in a bootstack project?")
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
project_root = config_path.parent
|
|
241
|
+
config = TtkbConfig.load(config_path)
|
|
242
|
+
|
|
243
|
+
# Determine target directory
|
|
244
|
+
if args.dir:
|
|
245
|
+
target_dir = args.dir
|
|
246
|
+
else:
|
|
247
|
+
# Parse entry point to find source directory
|
|
248
|
+
entry_path = Path(config.app.entry)
|
|
249
|
+
if entry_path.parts[0] == "src" and len(entry_path.parts) >= 2:
|
|
250
|
+
module_name = entry_path.parts[1]
|
|
251
|
+
target_dir = project_root / "src" / module_name / "dialogs"
|
|
252
|
+
else:
|
|
253
|
+
target_dir = project_root / "dialogs"
|
|
254
|
+
|
|
255
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
256
|
+
|
|
257
|
+
# Ensure __init__.py exists
|
|
258
|
+
init_file = target_dir / "__init__.py"
|
|
259
|
+
if not init_file.exists():
|
|
260
|
+
init_file.write_text('"""Dialogs package."""\n', encoding="utf-8")
|
|
261
|
+
|
|
262
|
+
# Create dialog
|
|
263
|
+
file_path = create_dialog(class_name, target_dir)
|
|
264
|
+
|
|
265
|
+
print(f"Created dialog: {file_path.relative_to(project_root)}")
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def run_add_i18n(args: argparse.Namespace) -> None:
|
|
269
|
+
"""Add internationalization support to the project.
|
|
270
|
+
|
|
271
|
+
By default this scaffolds a Python translations module (the simplest path -
|
|
272
|
+
no tooling, bundled with your code). With `--po` it scaffolds `.po`
|
|
273
|
+
catalog files in `assets/` for a file-based workflow.
|
|
274
|
+
"""
|
|
275
|
+
languages = args.languages
|
|
276
|
+
|
|
277
|
+
config_path = find_config()
|
|
278
|
+
if config_path is None:
|
|
279
|
+
print("Error: No bootstack.toml found. Are you in a bootstack project?")
|
|
280
|
+
return
|
|
281
|
+
project_root = config_path.parent
|
|
282
|
+
|
|
283
|
+
if args.po:
|
|
284
|
+
_scaffold_po_catalogs(project_root, languages)
|
|
285
|
+
else:
|
|
286
|
+
config = TtkbConfig.load(config_path)
|
|
287
|
+
entry_path = Path(config.app.entry)
|
|
288
|
+
module_name = entry_path.parts[1] if len(entry_path.parts) > 1 else None
|
|
289
|
+
_scaffold_i18n_module(project_root, module_name, languages)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _scaffold_i18n_module(project_root, module_name, languages) -> None:
|
|
293
|
+
"""Scaffold a Python translations module using add_translations()."""
|
|
294
|
+
if not module_name:
|
|
295
|
+
print("Error: could not determine the project module from bootstack.toml.")
|
|
296
|
+
return
|
|
297
|
+
|
|
298
|
+
target = project_root / "src" / module_name / "i18n.py"
|
|
299
|
+
if target.exists():
|
|
300
|
+
print(f"{target.relative_to(project_root)} already exists - not overwritten.")
|
|
301
|
+
return
|
|
302
|
+
|
|
303
|
+
target.write_text(_get_i18n_module_template(languages), encoding="utf-8")
|
|
304
|
+
print(f"Created: {target.relative_to(project_root)}")
|
|
305
|
+
print()
|
|
306
|
+
print("Wire it into main.py - call it before creating the app:")
|
|
307
|
+
print()
|
|
308
|
+
print(f" from {module_name}.i18n import install_translations")
|
|
309
|
+
print(" install_translations()")
|
|
310
|
+
print(" with bs.AppShell(...): # or bs.App(...)")
|
|
311
|
+
print(" ...")
|
|
312
|
+
print()
|
|
313
|
+
print("Then add your strings to i18n.py. Widget text auto-localizes - once a")
|
|
314
|
+
print('translation is registered, bs.Label("Save") shows it for the active locale.')
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _scaffold_po_catalogs(project_root, languages) -> None:
|
|
318
|
+
"""Scaffold .po catalog files in assets/locales/ for a file-based workflow."""
|
|
319
|
+
locales_dir = project_root / "assets" / "locales"
|
|
320
|
+
locales_dir.mkdir(parents=True, exist_ok=True)
|
|
321
|
+
|
|
322
|
+
for lang in languages:
|
|
323
|
+
po_file = locales_dir / f"{lang}.po"
|
|
324
|
+
if not po_file.exists():
|
|
325
|
+
po_file.write_text(_get_po_template(lang), encoding="utf-8")
|
|
326
|
+
print(f"Created: {po_file.relative_to(project_root)}")
|
|
327
|
+
|
|
328
|
+
print()
|
|
329
|
+
print("These .po files live under assets/, so the build bundles them already")
|
|
330
|
+
print("(assets/** is in [build.datas]). Fill in the translations, then load them")
|
|
331
|
+
print("at startup - before the app:")
|
|
332
|
+
print()
|
|
333
|
+
print(" from bootstack.i18n import load_po")
|
|
334
|
+
for lang in languages:
|
|
335
|
+
print(f' load_po("assets/locales/{lang}.po")')
|
|
336
|
+
print()
|
|
337
|
+
print('Widget text then auto-localizes - bs.Label("Save") shows the translation.')
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _get_i18n_module_template(languages) -> str:
|
|
341
|
+
"""Python translations-module template using add_translations()."""
|
|
342
|
+
blocks = []
|
|
343
|
+
for lang in languages:
|
|
344
|
+
blocks.append(
|
|
345
|
+
f' add_translations("{lang}", {{\n'
|
|
346
|
+
f' # "Save": "...",\n'
|
|
347
|
+
f' # "Cancel": "...",\n'
|
|
348
|
+
f' }})'
|
|
349
|
+
)
|
|
350
|
+
body = "\n".join(blocks) if blocks else " pass"
|
|
351
|
+
return (
|
|
352
|
+
'"""Application translations.\n'
|
|
353
|
+
"\n"
|
|
354
|
+
"Call install_translations() at startup, before creating the app, so the\n"
|
|
355
|
+
"strings are registered when widgets resolve their text. Widget text\n"
|
|
356
|
+
"auto-localizes: a plain string is translated when a translation exists.\n"
|
|
357
|
+
'"""\n'
|
|
358
|
+
"\n"
|
|
359
|
+
"from bootstack.i18n import add_translations\n"
|
|
360
|
+
"\n"
|
|
361
|
+
"\n"
|
|
362
|
+
"def install_translations() -> None:\n"
|
|
363
|
+
' """Register the application\'s translations."""\n'
|
|
364
|
+
f"{body}\n"
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _get_po_template(lang: str) -> str:
|
|
369
|
+
"""Get a .po file template."""
|
|
370
|
+
from datetime import datetime, timezone
|
|
371
|
+
|
|
372
|
+
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M%z")
|
|
373
|
+
|
|
374
|
+
return f'''\
|
|
375
|
+
# {lang.upper()} translations for the application.
|
|
376
|
+
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
|
377
|
+
# This file is distributed under the same license as the PACKAGE package.
|
|
378
|
+
#
|
|
379
|
+
msgid ""
|
|
380
|
+
msgstr ""
|
|
381
|
+
"Project-Id-Version: 1.0\\n"
|
|
382
|
+
"Report-Msgid-Bugs-To: \\n"
|
|
383
|
+
"POT-Creation-Date: {now}\\n"
|
|
384
|
+
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
|
|
385
|
+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
|
|
386
|
+
"Language-Team: {lang.upper()} <LL@li.org>\\n"
|
|
387
|
+
"Language: {lang}\\n"
|
|
388
|
+
"MIME-Version: 1.0\\n"
|
|
389
|
+
"Content-Type: text/plain; charset=UTF-8\\n"
|
|
390
|
+
"Content-Transfer-Encoding: 8bit\\n"
|
|
391
|
+
|
|
392
|
+
# Example translation
|
|
393
|
+
# msgid "Hello"
|
|
394
|
+
# msgstr "Hello"
|
|
395
|
+
'''
|