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.
Files changed (419) hide show
  1. bootstack/__init__.py +249 -0
  2. bootstack/__main__.py +5 -0
  3. bootstack/api/__init__.py +127 -0
  4. bootstack/api/app.py +30 -0
  5. bootstack/api/constants.py +3 -0
  6. bootstack/api/data.py +23 -0
  7. bootstack/api/dialogs.py +44 -0
  8. bootstack/api/i18n.py +17 -0
  9. bootstack/api/localization.py +16 -0
  10. bootstack/api/menu.py +7 -0
  11. bootstack/api/style.py +23 -0
  12. bootstack/api/utils.py +24 -0
  13. bootstack/api/widgets.py +137 -0
  14. bootstack/assets/__init__.py +24 -0
  15. bootstack/assets/bootstack-transparent.png +0 -0
  16. bootstack/assets/bootstack.ico +0 -0
  17. bootstack/assets/bootstack.png +0 -0
  18. bootstack/assets/elements/__init__.py +0 -0
  19. bootstack/assets/elements/badge-pill.png +0 -0
  20. bootstack/assets/elements/badge-square.png +0 -0
  21. bootstack/assets/elements/border.png +0 -0
  22. bootstack/assets/elements/button-compact.png +0 -0
  23. bootstack/assets/elements/button-default.png +0 -0
  24. bootstack/assets/elements/button-group-horizontal-after-compact.png +0 -0
  25. bootstack/assets/elements/button-group-horizontal-after-default.png +0 -0
  26. bootstack/assets/elements/button-group-horizontal-before-compact.png +0 -0
  27. bootstack/assets/elements/button-group-horizontal-before-default.png +0 -0
  28. bootstack/assets/elements/button-group-horizontal-center-compact.png +0 -0
  29. bootstack/assets/elements/button-group-horizontal-center-default.png +0 -0
  30. bootstack/assets/elements/button-group-vertical-after-compact.png +0 -0
  31. bootstack/assets/elements/button-group-vertical-after-default.png +0 -0
  32. bootstack/assets/elements/button-group-vertical-before-compact.png +0 -0
  33. bootstack/assets/elements/button-group-vertical-before-default.png +0 -0
  34. bootstack/assets/elements/button-group-vertical-center-compact.png +0 -0
  35. bootstack/assets/elements/button-group-vertical-center-default.png +0 -0
  36. bootstack/assets/elements/checkbox-checked.png +0 -0
  37. bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  38. bootstack/assets/elements/checkbox-unchecked.png +0 -0
  39. bootstack/assets/elements/field.png +0 -0
  40. bootstack/assets/elements/input-after-compact.png +0 -0
  41. bootstack/assets/elements/input-after-default.png +0 -0
  42. bootstack/assets/elements/input-before-compact.png +0 -0
  43. bootstack/assets/elements/input-before-default.png +0 -0
  44. bootstack/assets/elements/input-compact.png +0 -0
  45. bootstack/assets/elements/input-default.png +0 -0
  46. bootstack/assets/elements/list-item-separated.png +0 -0
  47. bootstack/assets/elements/list-item.png +0 -0
  48. bootstack/assets/elements/manifest.toml +480 -0
  49. bootstack/assets/elements/menu-item.png +0 -0
  50. bootstack/assets/elements/nav-button-compact.png +0 -0
  51. bootstack/assets/elements/nav-button-default.png +0 -0
  52. bootstack/assets/elements/nav-icon-button-compact.png +0 -0
  53. bootstack/assets/elements/nav-icon-button-default.png +0 -0
  54. bootstack/assets/elements/notebook-client-border.png +0 -0
  55. bootstack/assets/elements/notebook-tab-active.png +0 -0
  56. bootstack/assets/elements/notebook-tab-bar.png +0 -0
  57. bootstack/assets/elements/notebook-tab-normal.png +0 -0
  58. bootstack/assets/elements/notebook-tab-pill.png +0 -0
  59. bootstack/assets/elements/progress-bar-horizontal-striped.png +0 -0
  60. bootstack/assets/elements/progress-bar-solid.png +0 -0
  61. bootstack/assets/elements/progress-bar-thin.png +0 -0
  62. bootstack/assets/elements/progress-bar-vertical-striped.png +0 -0
  63. bootstack/assets/elements/radio-selected.png +0 -0
  64. bootstack/assets/elements/radio-unselected.png +0 -0
  65. bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  66. bootstack/assets/elements/scrollbar-vertical.png +0 -0
  67. bootstack/assets/elements/slider-handle-focus.png +0 -0
  68. bootstack/assets/elements/slider-handle.png +0 -0
  69. bootstack/assets/elements/slider-track-horizontal.png +0 -0
  70. bootstack/assets/elements/slider-track-vertical.png +0 -0
  71. bootstack/assets/elements/switch-off.png +0 -0
  72. bootstack/assets/elements/switch-on.png +0 -0
  73. bootstack/assets/elements/tabs-bar-horizontal.png +0 -0
  74. bootstack/assets/elements/tabs-bar-vertical.png +0 -0
  75. bootstack/assets/elements/tabs-pill.png +0 -0
  76. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  77. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +853 -0
  78. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +875 -0
  79. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  80. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +853 -0
  81. bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  82. bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +853 -0
  83. bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  84. bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +853 -0
  85. bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  86. bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +875 -0
  87. bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  88. bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +853 -0
  89. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  90. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +853 -0
  91. bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  92. bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +851 -0
  93. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  94. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +842 -0
  95. bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  96. bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +841 -0
  97. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  98. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +914 -0
  99. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  100. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +842 -0
  101. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  102. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +841 -0
  103. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  104. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +841 -0
  105. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  106. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +842 -0
  107. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  108. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +842 -0
  109. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  110. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +842 -0
  111. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  112. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +842 -0
  113. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  114. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +842 -0
  115. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  116. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +842 -0
  117. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  118. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +842 -0
  119. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  120. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +842 -0
  121. bootstack/assets/themes/__init__.py +0 -0
  122. bootstack/assets/themes/amber-dark.json +32 -0
  123. bootstack/assets/themes/amber-light.json +32 -0
  124. bootstack/assets/themes/aurora-dark.json +32 -0
  125. bootstack/assets/themes/aurora-light.json +32 -0
  126. bootstack/assets/themes/bootstrap-dark.json +32 -0
  127. bootstack/assets/themes/bootstrap-light.json +32 -0
  128. bootstack/assets/themes/classic-dark.json +32 -0
  129. bootstack/assets/themes/classic-light.json +32 -0
  130. bootstack/assets/themes/docs-dark.json +32 -0
  131. bootstack/assets/themes/docs-light.json +32 -0
  132. bootstack/assets/themes/forest-dark.json +32 -0
  133. bootstack/assets/themes/forest-light.json +32 -0
  134. bootstack/assets/themes/ocean-dark.json +32 -0
  135. bootstack/assets/themes/ocean-light.json +32 -0
  136. bootstack/assets/themes/rose-dark.json +32 -0
  137. bootstack/assets/themes/rose-light.json +32 -0
  138. bootstack/assets/widgets/__init__.py +0 -0
  139. bootstack/assets/widgets/badge-default.png +0 -0
  140. bootstack/assets/widgets/badge-pill.png +0 -0
  141. bootstack/assets/widgets/border.png +0 -0
  142. bootstack/assets/widgets/button-group-horizontal-after.png +0 -0
  143. bootstack/assets/widgets/button-group-horizontal-before.png +0 -0
  144. bootstack/assets/widgets/button-group-horizontal-center.png +0 -0
  145. bootstack/assets/widgets/button-group-vertical-after.png +0 -0
  146. bootstack/assets/widgets/button-group-vertical-before.png +0 -0
  147. bootstack/assets/widgets/button-group-vertical-center.png +0 -0
  148. bootstack/assets/widgets/button.png +0 -0
  149. bootstack/assets/widgets/checkbox-checked.png +0 -0
  150. bootstack/assets/widgets/checkbox-indeterminate.png +0 -0
  151. bootstack/assets/widgets/checkbox-unchecked.png +0 -0
  152. bootstack/assets/widgets/field.png +0 -0
  153. bootstack/assets/widgets/icon-button.png +0 -0
  154. bootstack/assets/widgets/input-inner.png +0 -0
  155. bootstack/assets/widgets/input-prefix.png +0 -0
  156. bootstack/assets/widgets/input-suffix.png +0 -0
  157. bootstack/assets/widgets/input.png +0 -0
  158. bootstack/assets/widgets/list-item-focus.png +0 -0
  159. bootstack/assets/widgets/list-item-separated.png +0 -0
  160. bootstack/assets/widgets/menu-item-separated.png +0 -0
  161. bootstack/assets/widgets/notebook-client-border.png +0 -0
  162. bootstack/assets/widgets/notebook-pill-active.png +0 -0
  163. bootstack/assets/widgets/notebook-pill-inactive.png +0 -0
  164. bootstack/assets/widgets/notebook-tab-active.png +0 -0
  165. bootstack/assets/widgets/notebook-tab-border.png +0 -0
  166. bootstack/assets/widgets/notebook-tab-normal.png +0 -0
  167. bootstack/assets/widgets/notebook-underline.png +0 -0
  168. bootstack/assets/widgets/progress-bar-horizontal-default.png +0 -0
  169. bootstack/assets/widgets/progress-bar-horizontal-striped.png +0 -0
  170. bootstack/assets/widgets/progress-bar-vertical-default.png +0 -0
  171. bootstack/assets/widgets/progress-bar-vertical-striped.png +0 -0
  172. bootstack/assets/widgets/progress-trough-horizontal.png +0 -0
  173. bootstack/assets/widgets/progress-trough-vertical.png +0 -0
  174. bootstack/assets/widgets/radio-selected.png +0 -0
  175. bootstack/assets/widgets/radio-unselected.png +0 -0
  176. bootstack/assets/widgets/scrollbar-horizontal-rounded.png +0 -0
  177. bootstack/assets/widgets/scrollbar-vertical-rounded.png +0 -0
  178. bootstack/assets/widgets/separator-horizontal.png +0 -0
  179. bootstack/assets/widgets/separator-vertical.png +0 -0
  180. bootstack/assets/widgets/slider-handle-focus.png +0 -0
  181. bootstack/assets/widgets/slider-handle.png +0 -0
  182. bootstack/assets/widgets/slider-track-horizontal.png +0 -0
  183. bootstack/assets/widgets/slider-track-vertical.png +0 -0
  184. bootstack/assets/widgets/switch-off.png +0 -0
  185. bootstack/assets/widgets/switch-on.png +0 -0
  186. bootstack/assets/widgets/tabs-bar-horizontal.png +0 -0
  187. bootstack/assets/widgets/tabs-bar-vertical.png +0 -0
  188. bootstack/assets/widgets/tabs-pill.png +0 -0
  189. bootstack/cli/__init__.py +124 -0
  190. bootstack/cli/__main__.py +6 -0
  191. bootstack/cli/add.py +439 -0
  192. bootstack/cli/build.py +115 -0
  193. bootstack/cli/config.py +287 -0
  194. bootstack/cli/demo.py +1267 -0
  195. bootstack/cli/doctor.py +195 -0
  196. bootstack/cli/list_cmd.py +71 -0
  197. bootstack/cli/promote.py +120 -0
  198. bootstack/cli/pyinstaller.py +246 -0
  199. bootstack/cli/run.py +99 -0
  200. bootstack/cli/start.py +105 -0
  201. bootstack/cli/templates/__init__.py +861 -0
  202. bootstack/constants.py +325 -0
  203. bootstack/core/__init__.py +34 -0
  204. bootstack/core/capabilities/__init__.py +45 -0
  205. bootstack/core/capabilities/after.py +103 -0
  206. bootstack/core/capabilities/bind.py +154 -0
  207. bootstack/core/capabilities/bindtags.py +112 -0
  208. bootstack/core/capabilities/busy.py +61 -0
  209. bootstack/core/capabilities/clipboard.py +88 -0
  210. bootstack/core/capabilities/focus.py +118 -0
  211. bootstack/core/capabilities/grab.py +65 -0
  212. bootstack/core/capabilities/grid.py +188 -0
  213. bootstack/core/capabilities/localization.py +231 -0
  214. bootstack/core/capabilities/pack.py +119 -0
  215. bootstack/core/capabilities/place.py +92 -0
  216. bootstack/core/capabilities/selection.py +136 -0
  217. bootstack/core/capabilities/signals.py +242 -0
  218. bootstack/core/capabilities/winfo.py +315 -0
  219. bootstack/core/colorutils.py +234 -0
  220. bootstack/core/exceptions.py +95 -0
  221. bootstack/core/images.py +283 -0
  222. bootstack/core/localization/README.md +90 -0
  223. bootstack/core/localization/__init__.py +13 -0
  224. bootstack/core/localization/intl_format.py +580 -0
  225. bootstack/core/localization/msgcat.py +425 -0
  226. bootstack/core/localization/specs.py +143 -0
  227. bootstack/core/mixins/__init__.py +1 -0
  228. bootstack/core/mixins/ttk_state.py +35 -0
  229. bootstack/core/mixins/widget.py +132 -0
  230. bootstack/core/publisher.py +147 -0
  231. bootstack/core/signals/README.md +112 -0
  232. bootstack/core/signals/__init__.py +8 -0
  233. bootstack/core/signals/integration.py +100 -0
  234. bootstack/core/signals/signal.py +317 -0
  235. bootstack/core/signals/types.py +4 -0
  236. bootstack/core/validation/__init__.py +5 -0
  237. bootstack/core/validation/types.py +13 -0
  238. bootstack/core/validation/validation_result.py +17 -0
  239. bootstack/core/validation/validation_rules.py +112 -0
  240. bootstack/core/variables.py +62 -0
  241. bootstack/datasource/README.md +607 -0
  242. bootstack/datasource/__init__.py +51 -0
  243. bootstack/datasource/base.py +474 -0
  244. bootstack/datasource/file_source.py +541 -0
  245. bootstack/datasource/memory_source.py +482 -0
  246. bootstack/datasource/sqlite_source.py +453 -0
  247. bootstack/datasource/types.py +259 -0
  248. bootstack/dialogs/__init__.py +56 -0
  249. bootstack/dialogs/colorchooser.py +674 -0
  250. bootstack/dialogs/colordropper.py +257 -0
  251. bootstack/dialogs/datedialog.py +404 -0
  252. bootstack/dialogs/dialog.py +514 -0
  253. bootstack/dialogs/filterdialog.py +358 -0
  254. bootstack/dialogs/fontdialog.py +339 -0
  255. bootstack/dialogs/formdialog.py +541 -0
  256. bootstack/dialogs/message.py +489 -0
  257. bootstack/dialogs/query.py +561 -0
  258. bootstack/py.typed +1 -0
  259. bootstack/runtime/__init__.py +3 -0
  260. bootstack/runtime/app.py +879 -0
  261. bootstack/runtime/base_window.py +786 -0
  262. bootstack/runtime/events.py +399 -0
  263. bootstack/runtime/menu.py +510 -0
  264. bootstack/runtime/shortcuts.py +423 -0
  265. bootstack/runtime/tk_patch.py +31 -0
  266. bootstack/runtime/toplevel.py +131 -0
  267. bootstack/runtime/utility.py +371 -0
  268. bootstack/runtime/visual_focus.py +228 -0
  269. bootstack/runtime/window_utilities.py +1043 -0
  270. bootstack/style/__init__.py +5498 -0
  271. bootstack/style/bootstyle.py +507 -0
  272. bootstack/style/bootstyle_builder_base.py +752 -0
  273. bootstack/style/bootstyle_builder_mixed.py +93 -0
  274. bootstack/style/bootstyle_builder_tk.py +109 -0
  275. bootstack/style/bootstyle_builder_ttk.py +354 -0
  276. bootstack/style/builders/__init__.py +51 -0
  277. bootstack/style/builders/badge.py +44 -0
  278. bootstack/style/builders/button.py +453 -0
  279. bootstack/style/builders/buttongroup.py +344 -0
  280. bootstack/style/builders/calendar.py +271 -0
  281. bootstack/style/builders/checkbutton.py +95 -0
  282. bootstack/style/builders/combobox.py +112 -0
  283. bootstack/style/builders/contextmenu.py +268 -0
  284. bootstack/style/builders/entry.py +83 -0
  285. bootstack/style/builders/expander.py +171 -0
  286. bootstack/style/builders/field.py +312 -0
  287. bootstack/style/builders/frame.py +27 -0
  288. bootstack/style/builders/label.py +28 -0
  289. bootstack/style/builders/labelframe.py +41 -0
  290. bootstack/style/builders/listview.py +267 -0
  291. bootstack/style/builders/menubar.py +74 -0
  292. bootstack/style/builders/menubutton.py +408 -0
  293. bootstack/style/builders/notebook.py +316 -0
  294. bootstack/style/builders/panedwindow.py +25 -0
  295. bootstack/style/builders/progressbar.py +71 -0
  296. bootstack/style/builders/radiobutton.py +68 -0
  297. bootstack/style/builders/scale.py +66 -0
  298. bootstack/style/builders/scrollbar.py +360 -0
  299. bootstack/style/builders/separator.py +45 -0
  300. bootstack/style/builders/sidenav.py +313 -0
  301. bootstack/style/builders/sizegrip.py +15 -0
  302. bootstack/style/builders/spinbox.py +119 -0
  303. bootstack/style/builders/switch.py +67 -0
  304. bootstack/style/builders/tabitem.py +205 -0
  305. bootstack/style/builders/toolbutton.py +260 -0
  306. bootstack/style/builders/tooltip.py +26 -0
  307. bootstack/style/builders/treeview.py +269 -0
  308. bootstack/style/builders/utils.py +404 -0
  309. bootstack/style/builders_tk/__init__.py +16 -0
  310. bootstack/style/builders_tk/defaults.py +229 -0
  311. bootstack/style/element.py +173 -0
  312. bootstack/style/style.py +499 -0
  313. bootstack/style/theme_provider.py +449 -0
  314. bootstack/style/tk_patch.py +5 -0
  315. bootstack/style/token_maps.py +42 -0
  316. bootstack/style/types.py +32 -0
  317. bootstack/style/typography.py +527 -0
  318. bootstack/style/utility.py +696 -0
  319. bootstack/themes/__init__.py +12 -0
  320. bootstack/themes/standard.py +415 -0
  321. bootstack/themes/user.py +45 -0
  322. bootstack/widgets/__init__.py +53 -0
  323. bootstack/widgets/composites/__init__.py +38 -0
  324. bootstack/widgets/composites/accordion.py +385 -0
  325. bootstack/widgets/composites/appshell.py +445 -0
  326. bootstack/widgets/composites/buttongroup.py +391 -0
  327. bootstack/widgets/composites/calendar.py +914 -0
  328. bootstack/widgets/composites/compositeframe.py +282 -0
  329. bootstack/widgets/composites/contextmenu.py +1754 -0
  330. bootstack/widgets/composites/dateentry.py +261 -0
  331. bootstack/widgets/composites/dropdownbutton.py +190 -0
  332. bootstack/widgets/composites/expander.py +508 -0
  333. bootstack/widgets/composites/field.py +448 -0
  334. bootstack/widgets/composites/floodgauge.py +434 -0
  335. bootstack/widgets/composites/form.py +983 -0
  336. bootstack/widgets/composites/labeledscale.py +209 -0
  337. bootstack/widgets/composites/list/__init__.py +15 -0
  338. bootstack/widgets/composites/list/listitem.py +733 -0
  339. bootstack/widgets/composites/list/listview.py +1507 -0
  340. bootstack/widgets/composites/menubar.py +303 -0
  341. bootstack/widgets/composites/meter.py +882 -0
  342. bootstack/widgets/composites/numericentry.py +183 -0
  343. bootstack/widgets/composites/pagestack.py +330 -0
  344. bootstack/widgets/composites/passwordentry.py +149 -0
  345. bootstack/widgets/composites/pathentry.py +223 -0
  346. bootstack/widgets/composites/radiogroup.py +466 -0
  347. bootstack/widgets/composites/scrolledtext.py +388 -0
  348. bootstack/widgets/composites/scrolledtext.pyi +186 -0
  349. bootstack/widgets/composites/scrollview.py +675 -0
  350. bootstack/widgets/composites/selectbox.py +544 -0
  351. bootstack/widgets/composites/sidenav/__init__.py +24 -0
  352. bootstack/widgets/composites/sidenav/group.py +485 -0
  353. bootstack/widgets/composites/sidenav/header.py +83 -0
  354. bootstack/widgets/composites/sidenav/item.py +413 -0
  355. bootstack/widgets/composites/sidenav/separator.py +51 -0
  356. bootstack/widgets/composites/sidenav/view.py +919 -0
  357. bootstack/widgets/composites/spinnerentry.py +232 -0
  358. bootstack/widgets/composites/tableview/__init__.py +5 -0
  359. bootstack/widgets/composites/tableview/tableview.py +2254 -0
  360. bootstack/widgets/composites/tableview/types.py +169 -0
  361. bootstack/widgets/composites/tabs/__init__.py +6 -0
  362. bootstack/widgets/composites/tabs/tabitem.py +372 -0
  363. bootstack/widgets/composites/tabs/tabs.py +478 -0
  364. bootstack/widgets/composites/tabs/tabview.py +352 -0
  365. bootstack/widgets/composites/textentry.py +90 -0
  366. bootstack/widgets/composites/timeentry.py +189 -0
  367. bootstack/widgets/composites/toast.py +364 -0
  368. bootstack/widgets/composites/togglegroup.py +382 -0
  369. bootstack/widgets/composites/toolbar.py +393 -0
  370. bootstack/widgets/composites/tooltip.py +404 -0
  371. bootstack/widgets/internal/__init__.py +0 -0
  372. bootstack/widgets/internal/wrapper_base.py +304 -0
  373. bootstack/widgets/mixins/__init__.py +25 -0
  374. bootstack/widgets/mixins/configure_mixin.py +186 -0
  375. bootstack/widgets/mixins/entry_mixin.py +70 -0
  376. bootstack/widgets/mixins/font_mixin.py +346 -0
  377. bootstack/widgets/mixins/icon_mixin.py +38 -0
  378. bootstack/widgets/mixins/localization_mixin.py +255 -0
  379. bootstack/widgets/mixins/signal_mixin.py +272 -0
  380. bootstack/widgets/mixins/validation_mixin.py +204 -0
  381. bootstack/widgets/parts/__init__.py +11 -0
  382. bootstack/widgets/parts/numberentry_part.py +345 -0
  383. bootstack/widgets/parts/spinnerentry_part.py +394 -0
  384. bootstack/widgets/parts/textentry_part.py +344 -0
  385. bootstack/widgets/primitives/__init__.py +55 -0
  386. bootstack/widgets/primitives/badge.py +44 -0
  387. bootstack/widgets/primitives/button.py +89 -0
  388. bootstack/widgets/primitives/card.py +66 -0
  389. bootstack/widgets/primitives/checkbutton.py +124 -0
  390. bootstack/widgets/primitives/checktoggle.py +53 -0
  391. bootstack/widgets/primitives/combobox.py +165 -0
  392. bootstack/widgets/primitives/entry.py +98 -0
  393. bootstack/widgets/primitives/frame.py +206 -0
  394. bootstack/widgets/primitives/gridframe.py +479 -0
  395. bootstack/widgets/primitives/label.py +95 -0
  396. bootstack/widgets/primitives/labelframe.py +63 -0
  397. bootstack/widgets/primitives/menubutton.py +118 -0
  398. bootstack/widgets/primitives/notebook.py +551 -0
  399. bootstack/widgets/primitives/optionmenu.py +248 -0
  400. bootstack/widgets/primitives/packframe.py +228 -0
  401. bootstack/widgets/primitives/panedwindow.py +58 -0
  402. bootstack/widgets/primitives/progressbar.py +95 -0
  403. bootstack/widgets/primitives/radiobutton.py +115 -0
  404. bootstack/widgets/primitives/radiotoggle.py +50 -0
  405. bootstack/widgets/primitives/scale.py +85 -0
  406. bootstack/widgets/primitives/scrollbar.py +56 -0
  407. bootstack/widgets/primitives/separator.py +56 -0
  408. bootstack/widgets/primitives/sizegrip.py +47 -0
  409. bootstack/widgets/primitives/spinbox.py +91 -0
  410. bootstack/widgets/primitives/switch.py +41 -0
  411. bootstack/widgets/primitives/treeview.py +77 -0
  412. bootstack/widgets/types.py +20 -0
  413. bootstack-0.1.0a1.dist-info/METADATA +196 -0
  414. bootstack-0.1.0a1.dist-info/RECORD +419 -0
  415. bootstack-0.1.0a1.dist-info/WHEEL +5 -0
  416. bootstack-0.1.0a1.dist-info/entry_points.txt +2 -0
  417. bootstack-0.1.0a1.dist-info/licenses/LICENSE +22 -0
  418. bootstack-0.1.0a1.dist-info/licenses/NOTICE +10 -0
  419. bootstack-0.1.0a1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,346 @@
1
+ """Font modifier syntax for inline font customization in bootstack widgets.
2
+
3
+ This module provides a mixin that enables concise font modification syntax for all
4
+ bootstack widgets. The syntax uses bracket notation similar to bootstyle modifiers,
5
+ allowing inline font customization without creating custom Font objects.
6
+
7
+ ## Syntax
8
+
9
+ The full modifier syntax follows the pattern: `family[size][weight][style]`
10
+
11
+ All components are optional and can be mixed in any combination. When components are
12
+ omitted, the widget's current font values are preserved.
13
+
14
+ Components:
15
+ - family: Font family name or typography token (e.g., 'helvetica', 'body', 'heading-lg')
16
+ - size: Point size (e.g., '16'), pixel size (e.g., '16px'), or size token (e.g., 'sm', 'lg')
17
+ - weight: 'bold' or 'normal'
18
+ - style: 'italic', 'roman', 'underline', 'overstrike' (comma-separated for multiple)
19
+
20
+ Size Tokens:
21
+ xs=8pt, sm=10pt, md=12pt, lg=14pt, xl=16pt, xxl=18pt
22
+
23
+ Font Tokens:
24
+ body, label, heading-md, heading-lg, heading-xl, display-lg, display-xl,
25
+ code, hyperlink, caption, body-sm, body-lg, body-xl
26
+
27
+ ## Behavior
28
+
29
+ - **At widget creation**: Modifiers are applied to the widget's default style font
30
+ - **At runtime**: Modifiers are applied to the widget's current font
31
+ - **Missing family**: Uses widget's current font family (or 'body' token if none)
32
+ - **Missing size**: Uses widget's current font size (or 'body' token size if none)
33
+
34
+ ## Examples
35
+
36
+ Basic modifications:
37
+
38
+ ```python
39
+ # Use body token, make it bold
40
+ Label(root, text="Title", font="body[bold]")
41
+
42
+ # Change current font to 16pt (preserves family)
43
+ label.configure(font="[16]")
44
+
45
+ # Make current font bold and italic (preserves family and size)
46
+ label.configure(font="[bold,italic]")
47
+ ```
48
+
49
+ Custom font families:
50
+
51
+ ```python
52
+ # Helvetica, 16pt, bold
53
+ Button(root, text="Click", font="helvetica[16][bold]")
54
+
55
+ # Arial, 14 pixels (negative in Tk), bold and italic
56
+ Label(root, text="Text", font="arial[14px][bold,italic]")
57
+ ```
58
+
59
+ Size tokens:
60
+
61
+ ```python
62
+ # Small size (10pt) with bold
63
+ Entry(root, font="[sm][bold]")
64
+
65
+ # Large size (14pt)
66
+ Label(root, font="[lg]")
67
+ ```
68
+
69
+ Font tokens with modifiers:
70
+
71
+ ```python
72
+ # Heading-lg token with italic style
73
+ Label(root, text="Heading", font="heading-lg[italic]")
74
+
75
+ # Label token at custom size
76
+ Button(root, text="Button", font="label[16]")
77
+ ```
78
+
79
+ Multiple style modifiers:
80
+
81
+ ```python
82
+ # Bold, italic, and underlined
83
+ Label(root, text="Emphasis", font="[16][bold,italic,underline]")
84
+ ```
85
+
86
+ ## Integration
87
+
88
+ FontMixin is automatically integrated into all bootstack widgets via TTKWrapperBase.
89
+ No additional setup is required - all widgets supporting the 'font' argument automatically
90
+ gain modifier syntax support.
91
+
92
+ The mixin uses the @configure_delegate pattern to intercept font configuration, parse
93
+ the modifier syntax, and apply the resolved font specification to the underlying ttk widget.
94
+ """
95
+
96
+ from __future__ import annotations
97
+
98
+ import re
99
+ from typing import Any, Literal, TYPE_CHECKING
100
+
101
+ if TYPE_CHECKING:
102
+ from bootstack.style.typography import Typography, FontTokenNames
103
+
104
+ from bootstack.widgets.mixins.configure_mixin import configure_delegate
105
+
106
+
107
+ # Size tokens mapping (shortcuts for common sizes)
108
+ SIZE_TOKENS = {
109
+ 'xs': 8,
110
+ 'sm': 10,
111
+ 'md': 12,
112
+ 'lg': 14,
113
+ 'xl': 16,
114
+ 'xxl': 18,
115
+ }
116
+
117
+
118
+ def _get_font_token_names() -> set[str]:
119
+ """Get all valid font token names (lazy to avoid import issues)."""
120
+ from bootstack.style.typography import FontTokenNames
121
+ return {
122
+ name.replace('_', '-')
123
+ for name in dir(FontTokenNames)
124
+ if not name.startswith('_')
125
+ }
126
+
127
+
128
+ def parse_font_modifier(font_spec: str) -> dict[str, Any]:
129
+ """Parse font modifier syntax string into configuration dict.
130
+
131
+ Args:
132
+ font_spec: Font specification string (e.g., "helvetica[16][bold]")
133
+
134
+ Returns:
135
+ Dict with keys: family, size, weight, slant, underline, overstrike
136
+ """
137
+ if not font_spec or not font_spec.strip():
138
+ return {}
139
+
140
+ result = {}
141
+
142
+ # Find all bracketed parts: [content]
143
+ bracket_pattern = r'\[([^\]]+)\]'
144
+ parts = re.findall(bracket_pattern, font_spec)
145
+
146
+ # Get family/token (everything before first bracket, or whole string if no brackets)
147
+ if '[' in font_spec:
148
+ family_part = font_spec[:font_spec.index('[')].strip()
149
+ else:
150
+ family_part = font_spec.strip()
151
+
152
+ if family_part:
153
+ result['family'] = family_part
154
+
155
+ # Process each bracketed part
156
+ for part in parts:
157
+ part = part.strip()
158
+ if not part:
159
+ continue
160
+
161
+ # Check if it's a pixel size (e.g., "16px")
162
+ if part.endswith('px'):
163
+ try:
164
+ # Pixel sizes are negative in Tk
165
+ result['size'] = -int(part[:-2])
166
+ continue
167
+ except ValueError:
168
+ pass
169
+
170
+ # Check if it's a point size (e.g., "16")
171
+ if part.isdigit():
172
+ result['size'] = int(part)
173
+ continue
174
+
175
+ # Check if it's a size token (e.g., "sm", "lg")
176
+ if part in SIZE_TOKENS:
177
+ result['size'] = SIZE_TOKENS[part]
178
+ continue
179
+
180
+ # Otherwise, treat as comma-separated modifiers
181
+ modifiers = [m.strip().lower() for m in part.split(',')]
182
+ for modifier in modifiers:
183
+ if not modifier:
184
+ continue
185
+
186
+ # Weight modifiers
187
+ if modifier in ('bold', 'normal'):
188
+ result['weight'] = modifier
189
+ # Slant modifiers
190
+ elif modifier in ('italic', 'roman'):
191
+ result['slant'] = modifier
192
+ # Boolean modifiers
193
+ elif modifier == 'underline':
194
+ result['underline'] = True
195
+ elif modifier == 'overstrike':
196
+ result['overstrike'] = True
197
+
198
+ return result
199
+
200
+
201
+ def build_font_from_modifier(font_spec: str, base_font: Any = None) -> tuple | str:
202
+ """Build Tk-compatible font tuple from modifier syntax, using base_font for missing values.
203
+
204
+ Args:
205
+ font_spec: Font specification with modifier syntax (e.g., "[16][bold]")
206
+ base_font: Base font to extend (token name or tuple); defaults to 'body' token
207
+
208
+ Returns:
209
+ Font tuple like ('Helvetica', 16, 'bold italic') or token name
210
+ """
211
+ from bootstack.style.typography import Typography
212
+
213
+ parsed = parse_font_modifier(font_spec)
214
+ if not parsed:
215
+ return base_font or 'body'
216
+
217
+ # Start with base font configuration
218
+ config = {}
219
+ family_is_token = False
220
+ font_token_names = _get_font_token_names()
221
+
222
+ # Check if family is a known token
223
+ if 'family' in parsed:
224
+ family_name = parsed['family']
225
+ if family_name in font_token_names:
226
+ # It's a font token - get its spec
227
+ token_spec = Typography.get_token(family_name)
228
+ config['family'] = token_spec.font
229
+ config['size'] = token_spec.size
230
+ config['weight'] = token_spec.weight
231
+ if token_spec.underline:
232
+ config['underline'] = token_spec.underline
233
+ family_is_token = True
234
+ else:
235
+ # It's a font family name
236
+ config['family'] = family_name
237
+ elif base_font:
238
+ # Use base font if provided
239
+ if isinstance(base_font, str) and base_font in font_token_names:
240
+ token_spec = Typography.get_token(base_font)
241
+ config['family'] = token_spec.font
242
+ config['size'] = token_spec.size
243
+ config['weight'] = token_spec.weight
244
+ if token_spec.underline:
245
+ config['underline'] = token_spec.underline
246
+ elif isinstance(base_font, tuple) and len(base_font) >= 2:
247
+ config['family'] = base_font[0]
248
+ config['size'] = base_font[1]
249
+ if len(base_font) >= 3:
250
+ # Parse weight/slant from tuple
251
+ styles = base_font[2].split()
252
+ for style in styles:
253
+ if style in ('bold', 'normal'):
254
+ config['weight'] = style
255
+ elif style in ('italic', 'roman'):
256
+ config['slant'] = style
257
+
258
+ # Apply parsed modifiers (override base)
259
+ if 'size' in parsed:
260
+ config['size'] = parsed['size']
261
+ if 'weight' in parsed:
262
+ config['weight'] = parsed['weight']
263
+ if 'slant' in parsed:
264
+ config['slant'] = parsed['slant']
265
+ if 'underline' in parsed:
266
+ config['underline'] = parsed['underline']
267
+ if 'overstrike' in parsed:
268
+ config['overstrike'] = parsed['overstrike']
269
+
270
+ # Ensure we have family and size - use body token as fallback
271
+ if 'family' not in config or 'size' not in config:
272
+ body_spec = Typography.get_token('body')
273
+ if 'family' not in config:
274
+ config['family'] = body_spec.font
275
+ if 'size' not in config:
276
+ config['size'] = body_spec.size
277
+
278
+ # Build Tk font specification as tuple (family, size, modifiers_string)
279
+ family = config['family']
280
+ size = config['size']
281
+ modifiers = []
282
+
283
+ if config.get('weight') == 'bold':
284
+ modifiers.append('bold')
285
+ if config.get('slant') == 'italic':
286
+ modifiers.append('italic')
287
+ if config.get('underline'):
288
+ modifiers.append('underline')
289
+ if config.get('overstrike'):
290
+ modifiers.append('overstrike')
291
+
292
+ if modifiers:
293
+ return (family, size, ' '.join(modifiers))
294
+ else:
295
+ return (family, size)
296
+
297
+
298
+ class FontMixin:
299
+ """
300
+ Constructor + runtime font modifier support.
301
+
302
+ Contract expected by WrapperBase:
303
+ - _init_font_mixin(kwargs) -> returns processed font value (or None)
304
+ - _delegate_font(value) -> applies/handles delegated configure
305
+ """
306
+
307
+ def _init_font_mixin(self, kwargs: dict[str, Any]) -> Any:
308
+ if "font" not in kwargs:
309
+ return None
310
+
311
+ value = kwargs.pop("font")
312
+
313
+ if isinstance(value, str):
314
+ font_token_names = _get_font_token_names()
315
+
316
+ if value in font_token_names and "[" not in value:
317
+ return value
318
+
319
+ if "[" in value:
320
+ # Constructor-time: no widget yet, so use a stable base
321
+ return build_font_from_modifier(value, base_font="body")
322
+
323
+ return value
324
+
325
+ @configure_delegate("font")
326
+ def _delegate_font(self, value: Any = None):
327
+ if value is None:
328
+ return self._ttk_base.cget(self, "font") # type: ignore[misc]
329
+
330
+ if isinstance(value, str):
331
+ font_token_names = _get_font_token_names()
332
+
333
+ if value in font_token_names and "[" not in value:
334
+ font_value = value
335
+ elif "[" in value:
336
+ try:
337
+ current_font = self._ttk_base.cget(self, "font") # type: ignore[misc]
338
+ except Exception:
339
+ current_font = "body"
340
+ font_value = build_font_from_modifier(value, base_font=current_font)
341
+ else:
342
+ font_value = value
343
+ else:
344
+ font_value = value
345
+
346
+ return self._ttk_base.configure(self, font=font_value) # type: ignore[misc]
@@ -0,0 +1,38 @@
1
+ """Icon mixin for widgets that support theme-aware icons.
2
+
3
+ Provides a `@configure_delegate('icon')` handler that applies an icon through
4
+ the style system and preserves current bootstyle tokens, orientation, and
5
+ surface color where applicable.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Callable
11
+
12
+ from bootstack.widgets.mixins.configure_mixin import configure_delegate
13
+
14
+
15
+ class IconMixin:
16
+ """Adds `icon` configuration support via the style engine."""
17
+
18
+ configure_style_options: Callable
19
+ rebuild_style: Callable
20
+
21
+ @configure_delegate("icon")
22
+ def _delegate_icon(self, value: Any = None):
23
+ if value is None:
24
+ return self.configure_style_options("icon")
25
+ else:
26
+ self.configure_style_options(icon=value)
27
+ return self.rebuild_style()
28
+
29
+ @configure_delegate("icon_only")
30
+ def _delegate_icon_only(self, value: Any = None):
31
+ if value is None:
32
+ return self.configure_style_options("icon_only")
33
+ else:
34
+ self.configure_style_options(icon_only=value)
35
+ return self.rebuild_style()
36
+
37
+
38
+ __all__ = ["IconMixin"]
@@ -0,0 +1,255 @@
1
+ """Localization mixin for bootstack widgets.
2
+
3
+ Provides automatic text translation and value formatting based on the current
4
+ locale. Widgets using this mixin will automatically update when the locale
5
+ changes.
6
+
7
+ This mixin is a thin glue layer that delegates resolution logic to the core
8
+ localization capability module.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any, Dict
14
+ from tkinter import StringVar, Misc
15
+
16
+ from bootstack.core.capabilities.localization import (
17
+ resolve_text,
18
+ resolve_variable_text,
19
+ apply_spec,
20
+ get_current_locale,
21
+ create_formatted_signal,
22
+ )
23
+ from bootstack.core.localization.specs import (
24
+ LocalizedSpec,
25
+ LocalizedTextSpec,
26
+ LocalizedValueSpec,
27
+ )
28
+ from bootstack.runtime.app import get_app_settings
29
+
30
+
31
+ class LocalizationMixin(Misc):
32
+ """Mixin for widgets that support automatic text and value localization.
33
+
34
+ This mixin enables widgets to automatically localize text and format values
35
+ according to the current locale. It listens for locale change events and
36
+ updates all registered fields accordingly.
37
+
38
+ This mixin delegates resolution logic to the core localization capability.
39
+
40
+ Attributes:
41
+ localize: Controls whether literals are auto-wrapped into localization specs.
42
+ Can be True, False, or "auto".
43
+ value_format: Default IntlFormatter spec for non-string values (e.g., 'currency',
44
+ 'decimal', 'percent').
45
+ """
46
+
47
+ def __init__(self, *args, **kwargs):
48
+ """Initialize the localizable widget mixin.
49
+
50
+ This mixin intercepts localization-related kwargs. It inspects 'text'
51
+ for localization without consuming it, so it can be passed to the
52
+ underlying widget. It consumes 'localize' and 'value_format' as they
53
+ are not standard ttk options.
54
+ """
55
+ # Determine the localization mode. A widget-specific 'localize' argument
56
+ # overrides the global app setting. This argument is consumed from kwargs.
57
+ localize = kwargs.pop('localize', get_app_settings().localize_mode)
58
+ value_format = kwargs.pop('value_format', None)
59
+
60
+ # Get 'text' for localization without removing it from kwargs.
61
+ text_to_localize = kwargs.get('text')
62
+
63
+ # Call the next class in the MRO with the remaining kwargs.
64
+ # 'text' is still in kwargs for the underlying widget to use.
65
+ super().__init__(*args, **kwargs)
66
+
67
+ # --- Post-init setup ---
68
+ self._localized_fields: Dict[str, LocalizedSpec] = {}
69
+ self._localize_mode = localize
70
+ self._default_value_format = value_format
71
+
72
+ root = self.winfo_toplevel() # event is generated on root
73
+ root.bind("<<LocaleChanged>>", self._on_locale_changed, add="+")
74
+
75
+ # Check if widget has a textsignal or textvariable with value_format
76
+ if value_format and self._has_signal_or_variable():
77
+ self._setup_signal_formatting(value_format)
78
+ else:
79
+ # Register the text field for static localization
80
+ self.register_localized_field('text', text_to_localize, value_format=value_format)
81
+
82
+ def _has_signal_or_variable(self) -> bool:
83
+ """Check if the widget has a textsignal, textvariable, or configured textvariable.
84
+
85
+ Returns:
86
+ True if any signal/variable binding exists.
87
+ """
88
+ if hasattr(self, '_textsignal') or hasattr(self, '_textvariable'):
89
+ return True
90
+ try:
91
+ textvariable_name = self.cget('textvariable')
92
+ return bool(textvariable_name)
93
+ except Exception:
94
+ return False
95
+
96
+ def register_localized_field(
97
+ self,
98
+ field_name: str,
99
+ value: Any,
100
+ *,
101
+ value_format: str | None = None,
102
+ localize: bool | str | None = None,
103
+ ) -> None:
104
+ """Register a widget field for automatic localization.
105
+
106
+ Args:
107
+ field_name: The widget field name (e.g., "text", "textvariable").
108
+ value: The value to localize. Can be a literal string/number or a LocalizedSpec.
109
+ value_format: Optional IntlFormatter spec for non-string values (e.g., 'currency').
110
+ localize: Override the widget's default localization mode for this field.
111
+ """
112
+ if value is None:
113
+ return
114
+
115
+ if isinstance(value, str) and value == "":
116
+ return
117
+
118
+ localize_mode = self._localize_mode if localize is None else localize
119
+
120
+ # If already a spec, use it directly
121
+ if isinstance(value, LocalizedSpec):
122
+ self._localized_fields[field_name] = value
123
+ self._apply_spec_now(field_name, value)
124
+ return
125
+
126
+ if localize_mode is False:
127
+ return
128
+
129
+ # Resolve the value to a spec using core capability
130
+ if isinstance(value, str):
131
+ spec = resolve_text(value, localize_mode=localize_mode)
132
+ else:
133
+ spec = resolve_variable_text(
134
+ value,
135
+ value_format=value_format,
136
+ default_format=self._default_value_format or "decimal",
137
+ )
138
+
139
+ if spec is not None:
140
+ self._localized_fields[field_name] = spec
141
+ self._apply_spec_now(field_name, spec)
142
+
143
+ def _apply_spec_now(self, field_name: str, spec: LocalizedSpec) -> None:
144
+ """Resolve a localization spec using the current locale and apply immediately.
145
+
146
+ Args:
147
+ field_name: The widget field name to update.
148
+ spec: The LocalizedSpec to resolve and apply.
149
+ """
150
+ if not spec.enabled:
151
+ return
152
+ value = apply_spec(spec)
153
+ self._apply_localized_value(field_name, value)
154
+
155
+ def _on_locale_changed(self, event=None):
156
+ """Handle locale change events by refreshing all localized fields.
157
+
158
+ Args:
159
+ event: The Tkinter event object (unused).
160
+ """
161
+ self._refresh_localized_fields()
162
+
163
+ # If we have signal formatting, trigger a re-format with new locale
164
+ if hasattr(self, '_signal_formatter'):
165
+ value_format, formatter, source_signal = self._signal_formatter
166
+ # Re-format current value from source signal with new locale
167
+ formatter(source_signal.get())
168
+
169
+ def _refresh_localized_fields(self) -> None:
170
+ """Refresh all registered localized fields with the current locale."""
171
+ for field_name, spec in self._localized_fields.items():
172
+ if not spec.enabled:
173
+ continue
174
+ value = apply_spec(spec)
175
+ self._apply_localized_value(field_name, value)
176
+
177
+ def _setup_signal_formatting(self, value_format: str) -> None:
178
+ """Subscribe to textsignal and format its values.
179
+
180
+ When formatting is enabled, this creates a private textvariable for this widget
181
+ and subscribes to the source signal to format and display values independently.
182
+
183
+ Args:
184
+ value_format: The format spec (e.g., 'currency', 'decimal').
185
+ """
186
+ # Ensure textsignal exists (triggers lazy creation if needed)
187
+ if not hasattr(self, '_textsignal'):
188
+ _ = self.textsignal
189
+
190
+ # Save reference to the source signal (the one we're subscribing to)
191
+ source_signal = self._textsignal
192
+
193
+ # Create formatted signal using core capability
194
+ formatted_signal, formatter = create_formatted_signal(source_signal, value_format)
195
+
196
+ # Update our internal references to use the new private signal/variable
197
+ self._textsignal = formatted_signal
198
+ self._textvariable = formatted_signal.var
199
+
200
+ # Configure widget to use the new private variable
201
+ try:
202
+ self._ttk_base.configure(self, textvariable=self._textvariable) # type: ignore[misc]
203
+ except Exception:
204
+ pass
205
+
206
+ # Store references for locale changes
207
+ self._signal_formatter = (value_format, formatter, source_signal)
208
+
209
+ def _apply_localized_value(self, field_name: str, value: str) -> None:
210
+ """Apply a localized value to the widget field.
211
+
212
+ Checks for associated textvariable/variable first to ensure proper
213
+ integration with textsignal/signal reactive bindings. Falls back to
214
+ direct widget configuration if no variable is found.
215
+
216
+ Args:
217
+ field_name: The widget field name to update.
218
+ value: The localized value to apply.
219
+ """
220
+ # Check if field has a direct attribute (less common)
221
+ var = getattr(self, field_name, None)
222
+ if isinstance(var, StringVar):
223
+ var.set(value)
224
+ return
225
+
226
+ # Check for textvariable (for 'text' field)
227
+ if field_name == 'text':
228
+ # Try cached _textvariable first
229
+ if hasattr(self, '_textvariable') and self._textvariable:
230
+ self._textvariable.set(value)
231
+ return
232
+
233
+ # Try to get textvariable from widget configuration
234
+ try:
235
+ textvariable_name = self.cget('textvariable')
236
+ if textvariable_name:
237
+ # Get the actual variable object via the property
238
+ self.textvariable.set(value)
239
+ return
240
+ except Exception:
241
+ pass
242
+
243
+ # Check for variable (for 'value' or other fields)
244
+ if hasattr(self, '_variable') and self._variable and field_name != 'text':
245
+ self._variable.set(value)
246
+ return
247
+
248
+ # Fallback: configure the widget option directly
249
+ try:
250
+ self.configure({field_name: value})
251
+ except Exception:
252
+ pass
253
+
254
+
255
+ __all__ = ["LocalizationMixin"]