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.
Files changed (471) hide show
  1. bootstack/__init__.py +157 -0
  2. bootstack/__main__.py +5 -0
  3. bootstack/_core/__init__.py +21 -0
  4. bootstack/_core/capabilities/__init__.py +45 -0
  5. bootstack/_core/capabilities/after.py +103 -0
  6. bootstack/_core/capabilities/bind.py +192 -0
  7. bootstack/_core/capabilities/bindtags.py +112 -0
  8. bootstack/_core/capabilities/busy.py +72 -0
  9. bootstack/_core/capabilities/clipboard.py +89 -0
  10. bootstack/_core/capabilities/focus.py +118 -0
  11. bootstack/_core/capabilities/grab.py +65 -0
  12. bootstack/_core/capabilities/grid.py +211 -0
  13. bootstack/_core/capabilities/localization.py +231 -0
  14. bootstack/_core/capabilities/pack.py +140 -0
  15. bootstack/_core/capabilities/place.py +113 -0
  16. bootstack/_core/capabilities/selection.py +136 -0
  17. bootstack/_core/capabilities/signals.py +244 -0
  18. bootstack/_core/capabilities/winfo.py +315 -0
  19. bootstack/_core/colorutils.py +234 -0
  20. bootstack/_core/exceptions.py +25 -0
  21. bootstack/_core/images.py +463 -0
  22. bootstack/_core/mixins/__init__.py +1 -0
  23. bootstack/_core/mixins/ttk_state.py +35 -0
  24. bootstack/_core/mixins/widget.py +132 -0
  25. bootstack/_core/paths.py +49 -0
  26. bootstack/_core/publisher.py +149 -0
  27. bootstack/_core/variables.py +62 -0
  28. bootstack/_runtime/__init__.py +3 -0
  29. bootstack/_runtime/app.py +930 -0
  30. bootstack/_runtime/base_window.py +945 -0
  31. bootstack/_runtime/events.py +399 -0
  32. bootstack/_runtime/shortcuts.py +496 -0
  33. bootstack/_runtime/tk_patch.py +43 -0
  34. bootstack/_runtime/toplevel.py +276 -0
  35. bootstack/_runtime/utility.py +457 -0
  36. bootstack/_runtime/visual_focus.py +240 -0
  37. bootstack/_runtime/window_utilities.py +1123 -0
  38. bootstack/assets/__init__.py +21 -0
  39. bootstack/assets/bootstack.ico +0 -0
  40. bootstack/assets/bootstack.png +0 -0
  41. bootstack/assets/elements/__init__.py +0 -0
  42. bootstack/assets/elements/badge-pill.png +0 -0
  43. bootstack/assets/elements/badge-square.png +0 -0
  44. bootstack/assets/elements/button-compact.png +0 -0
  45. bootstack/assets/elements/button-default.png +0 -0
  46. bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
  47. bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
  48. bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
  49. bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
  50. bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
  51. bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
  52. bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
  53. bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
  54. bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
  55. bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
  56. bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
  57. bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
  58. bootstack/assets/elements/card.png +0 -0
  59. bootstack/assets/elements/checkbox-checked.png +0 -0
  60. bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  61. bootstack/assets/elements/checkbox-unchecked.png +0 -0
  62. bootstack/assets/elements/field.png +0 -0
  63. bootstack/assets/elements/input-addon-compact.png +0 -0
  64. bootstack/assets/elements/input-addon-default.png +0 -0
  65. bootstack/assets/elements/input-compact.png +0 -0
  66. bootstack/assets/elements/input-default.png +0 -0
  67. bootstack/assets/elements/list-item-separated.png +0 -0
  68. bootstack/assets/elements/list-item.png +0 -0
  69. bootstack/assets/elements/listrow-compact.png +0 -0
  70. bootstack/assets/elements/listrow-default.png +0 -0
  71. bootstack/assets/elements/manifest.toml +361 -0
  72. bootstack/assets/elements/menu-item.png +0 -0
  73. bootstack/assets/elements/navitem-compact.png +0 -0
  74. bootstack/assets/elements/navitem-default.png +0 -0
  75. bootstack/assets/elements/progressbar-h-compact.png +0 -0
  76. bootstack/assets/elements/progressbar-h-default.png +0 -0
  77. bootstack/assets/elements/progressbar-v-compact.png +0 -0
  78. bootstack/assets/elements/progressbar-v-default.png +0 -0
  79. bootstack/assets/elements/radiobutton.png +0 -0
  80. bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  81. bootstack/assets/elements/scrollbar-vertical.png +0 -0
  82. bootstack/assets/elements/slider-handle.png +0 -0
  83. bootstack/assets/elements/slider-track-h.png +0 -0
  84. bootstack/assets/elements/slider-track-v.png +0 -0
  85. bootstack/assets/elements/switch-off.png +0 -0
  86. bootstack/assets/elements/switch-on.png +0 -0
  87. bootstack/assets/elements/tab-h.png +0 -0
  88. bootstack/assets/elements/tab-v.png +0 -0
  89. bootstack/assets/icons/bootstrap.ttf +0 -0
  90. bootstack/assets/icons/glyphmap.json +2080 -0
  91. bootstack/assets/icons/icon_metrics.json +12470 -0
  92. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  93. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +856 -0
  94. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
  95. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +878 -0
  96. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  97. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +856 -0
  98. bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  99. bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +856 -0
  100. bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  101. bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +856 -0
  102. bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  103. bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +878 -0
  104. bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  105. bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +856 -0
  106. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  107. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +856 -0
  108. bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  109. bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +854 -0
  110. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  111. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +845 -0
  112. bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  113. bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +844 -0
  114. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  115. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +917 -0
  116. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  117. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +845 -0
  118. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  119. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +844 -0
  120. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  121. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +844 -0
  122. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  123. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +845 -0
  124. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  125. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +845 -0
  126. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  127. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +845 -0
  128. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  129. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +845 -0
  130. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  131. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +845 -0
  132. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  133. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +845 -0
  134. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  135. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +845 -0
  136. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  137. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +845 -0
  138. bootstack/cli/__init__.py +133 -0
  139. bootstack/cli/__main__.py +6 -0
  140. bootstack/cli/add.py +395 -0
  141. bootstack/cli/appicon.py +285 -0
  142. bootstack/cli/build.py +115 -0
  143. bootstack/cli/config.py +313 -0
  144. bootstack/cli/demo.py +564 -0
  145. bootstack/cli/dev.py +153 -0
  146. bootstack/cli/doctor.py +195 -0
  147. bootstack/cli/icons.py +98 -0
  148. bootstack/cli/promote.py +120 -0
  149. bootstack/cli/pyinstaller.py +268 -0
  150. bootstack/cli/run.py +95 -0
  151. bootstack/cli/start.py +117 -0
  152. bootstack/cli/templates/__init__.py +931 -0
  153. bootstack/clipboard.py +48 -0
  154. bootstack/constants.py +318 -0
  155. bootstack/data/README.md +615 -0
  156. bootstack/data/__init__.py +78 -0
  157. bootstack/data/_observable.py +276 -0
  158. bootstack/data/base.py +780 -0
  159. bootstack/data/file_source.py +367 -0
  160. bootstack/data/memory_source.py +450 -0
  161. bootstack/data/query.py +367 -0
  162. bootstack/data/readers.py +289 -0
  163. bootstack/data/sqlite_source.py +869 -0
  164. bootstack/data/types.py +354 -0
  165. bootstack/data/writers.py +232 -0
  166. bootstack/dev/__init__.py +36 -0
  167. bootstack/dev/_capture.py +141 -0
  168. bootstack/dev/_env.py +43 -0
  169. bootstack/dev/_registry.py +140 -0
  170. bootstack/dev/_reloader.py +351 -0
  171. bootstack/dev/_reset.py +50 -0
  172. bootstack/dev/_watcher.py +91 -0
  173. bootstack/dialogs/__init__.py +948 -0
  174. bootstack/dialogs/_impl/__init__.py +47 -0
  175. bootstack/dialogs/_impl/colorchooser.py +588 -0
  176. bootstack/dialogs/_impl/datedialog.py +450 -0
  177. bootstack/dialogs/_impl/dialog.py +594 -0
  178. bootstack/dialogs/_impl/filterdialog.py +358 -0
  179. bootstack/dialogs/_impl/fontdialog.py +364 -0
  180. bootstack/dialogs/_impl/formdialog.py +564 -0
  181. bootstack/dialogs/_impl/message.py +486 -0
  182. bootstack/dialogs/_impl/query.py +570 -0
  183. bootstack/errors.py +67 -0
  184. bootstack/events/__init__.py +111 -0
  185. bootstack/events/_event.py +135 -0
  186. bootstack/events/_payloads.py +539 -0
  187. bootstack/events/_subscription.py +38 -0
  188. bootstack/i18n/README.md +77 -0
  189. bootstack/i18n/__init__.py +23 -0
  190. bootstack/i18n/catalog.py +121 -0
  191. bootstack/i18n/intl_format.py +584 -0
  192. bootstack/i18n/msgcat.py +425 -0
  193. bootstack/i18n/specs.py +156 -0
  194. bootstack/images.py +563 -0
  195. bootstack/py.typed +1 -0
  196. bootstack/scheduling/__init__.py +11 -0
  197. bootstack/scheduling/_schedule.py +218 -0
  198. bootstack/shortcuts.py +21 -0
  199. bootstack/signals/README.md +98 -0
  200. bootstack/signals/__init__.py +10 -0
  201. bootstack/signals/integration.py +100 -0
  202. bootstack/signals/signal.py +353 -0
  203. bootstack/signals/types.py +6 -0
  204. bootstack/store.py +286 -0
  205. bootstack/streams/__init__.py +12 -0
  206. bootstack/streams/_stream.py +321 -0
  207. bootstack/style/__init__.py +38 -0
  208. bootstack/style/builders/__init__.py +51 -0
  209. bootstack/style/builders/badge.py +46 -0
  210. bootstack/style/builders/button.py +339 -0
  211. bootstack/style/builders/buttongroup.py +311 -0
  212. bootstack/style/builders/calendar.py +271 -0
  213. bootstack/style/builders/checkbutton.py +110 -0
  214. bootstack/style/builders/combobox.py +113 -0
  215. bootstack/style/builders/contextmenu.py +268 -0
  216. bootstack/style/builders/entry.py +82 -0
  217. bootstack/style/builders/expander.py +148 -0
  218. bootstack/style/builders/field.py +335 -0
  219. bootstack/style/builders/frame.py +50 -0
  220. bootstack/style/builders/label.py +28 -0
  221. bootstack/style/builders/labelframe.py +34 -0
  222. bootstack/style/builders/listview.py +369 -0
  223. bootstack/style/builders/menubar.py +91 -0
  224. bootstack/style/builders/menubutton.py +359 -0
  225. bootstack/style/builders/panedwindow.py +25 -0
  226. bootstack/style/builders/progressbar.py +67 -0
  227. bootstack/style/builders/radiobutton.py +99 -0
  228. bootstack/style/builders/scale.py +64 -0
  229. bootstack/style/builders/scrollbar.py +225 -0
  230. bootstack/style/builders/separator.py +49 -0
  231. bootstack/style/builders/sidenav.py +643 -0
  232. bootstack/style/builders/sizegrip.py +15 -0
  233. bootstack/style/builders/spinbox.py +119 -0
  234. bootstack/style/builders/switch.py +70 -0
  235. bootstack/style/builders/tabitem.py +204 -0
  236. bootstack/style/builders/togglegroup.py +294 -0
  237. bootstack/style/builders/toolbutton.py +275 -0
  238. bootstack/style/builders/tooltip.py +26 -0
  239. bootstack/style/builders/treeview.py +193 -0
  240. bootstack/style/builders/utils.py +455 -0
  241. bootstack/style/builders_tk/__init__.py +16 -0
  242. bootstack/style/builders_tk/defaults.py +229 -0
  243. bootstack/style/element.py +173 -0
  244. bootstack/style/fonts.py +123 -0
  245. bootstack/style/style.py +609 -0
  246. bootstack/style/style_builder_base.py +716 -0
  247. bootstack/style/style_builder_mixed.py +93 -0
  248. bootstack/style/style_builder_tk.py +109 -0
  249. bootstack/style/style_builder_ttk.py +353 -0
  250. bootstack/style/style_resolver.py +447 -0
  251. bootstack/style/theme.py +245 -0
  252. bootstack/style/theme_provider.py +471 -0
  253. bootstack/style/themes/__init__.py +128 -0
  254. bootstack/style/tk_patch.py +5 -0
  255. bootstack/style/token_maps.py +41 -0
  256. bootstack/style/types.py +32 -0
  257. bootstack/style/typography.py +523 -0
  258. bootstack/style/utility.py +746 -0
  259. bootstack/types.py +39 -0
  260. bootstack/validation/__init__.py +6 -0
  261. bootstack/validation/types.py +15 -0
  262. bootstack/validation/validation_result.py +17 -0
  263. bootstack/validation/validation_rules.py +205 -0
  264. bootstack/widgets/__init__.py +74 -0
  265. bootstack/widgets/_core/__init__.py +31 -0
  266. bootstack/widgets/_core/app_config.py +266 -0
  267. bootstack/widgets/_core/base.py +461 -0
  268. bootstack/widgets/_core/container.py +334 -0
  269. bootstack/widgets/_core/context.py +35 -0
  270. bootstack/widgets/_core/events.py +130 -0
  271. bootstack/widgets/_core/field_mixin.py +353 -0
  272. bootstack/widgets/_core/icon_image_props.py +72 -0
  273. bootstack/widgets/_core/image_binding.py +93 -0
  274. bootstack/widgets/_core/navmodel.py +345 -0
  275. bootstack/widgets/_core/options.py +210 -0
  276. bootstack/widgets/_core/selection_group.py +130 -0
  277. bootstack/widgets/_core/window_controls.py +96 -0
  278. bootstack/widgets/_core/window_menu.py +246 -0
  279. bootstack/widgets/_impl/__init__.py +1 -0
  280. bootstack/widgets/_impl/_internal/__init__.py +0 -0
  281. bootstack/widgets/_impl/_internal/wrapper_base.py +307 -0
  282. bootstack/widgets/_impl/_parts/__init__.py +11 -0
  283. bootstack/widgets/_impl/_parts/numberentry_part.py +385 -0
  284. bootstack/widgets/_impl/_parts/spinnerentry_part.py +434 -0
  285. bootstack/widgets/_impl/_parts/textentry_part.py +406 -0
  286. bootstack/widgets/_impl/composites/__init__.py +33 -0
  287. bootstack/widgets/_impl/composites/_dateutils.py +33 -0
  288. bootstack/widgets/_impl/composites/_image_fit.py +105 -0
  289. bootstack/widgets/_impl/composites/accordion.py +381 -0
  290. bootstack/widgets/_impl/composites/avatar.py +191 -0
  291. bootstack/widgets/_impl/composites/buttongroup.py +371 -0
  292. bootstack/widgets/_impl/composites/calendar.py +972 -0
  293. bootstack/widgets/_impl/composites/carousel.py +567 -0
  294. bootstack/widgets/_impl/composites/chart.py +882 -0
  295. bootstack/widgets/_impl/composites/compositeframe.py +298 -0
  296. bootstack/widgets/_impl/composites/contextmenu.py +1951 -0
  297. bootstack/widgets/_impl/composites/dateentry.py +404 -0
  298. bootstack/widgets/_impl/composites/dropdownbutton.py +325 -0
  299. bootstack/widgets/_impl/composites/expander.py +515 -0
  300. bootstack/widgets/_impl/composites/field.py +670 -0
  301. bootstack/widgets/_impl/composites/form.py +1066 -0
  302. bootstack/widgets/_impl/composites/gallery.py +551 -0
  303. bootstack/widgets/_impl/composites/list/__init__.py +15 -0
  304. bootstack/widgets/_impl/composites/list/listitem.py +802 -0
  305. bootstack/widgets/_impl/composites/list/listview.py +1433 -0
  306. bootstack/widgets/_impl/composites/menu/__init__.py +18 -0
  307. bootstack/widgets/_impl/composites/menu/model.py +358 -0
  308. bootstack/widgets/_impl/composites/menu/render_native.py +134 -0
  309. bootstack/widgets/_impl/composites/menu/render_themed.py +134 -0
  310. bootstack/widgets/_impl/composites/meter.py +860 -0
  311. bootstack/widgets/_impl/composites/numericentry.py +201 -0
  312. bootstack/widgets/_impl/composites/pagestack.py +395 -0
  313. bootstack/widgets/_impl/composites/passwordentry.py +142 -0
  314. bootstack/widgets/_impl/composites/pathentry.py +168 -0
  315. bootstack/widgets/_impl/composites/picture.py +289 -0
  316. bootstack/widgets/_impl/composites/radiogroup.py +511 -0
  317. bootstack/widgets/_impl/composites/scrolledtext.py +375 -0
  318. bootstack/widgets/_impl/composites/scrolledtext.pyi +186 -0
  319. bootstack/widgets/_impl/composites/scrollview.py +764 -0
  320. bootstack/widgets/_impl/composites/selectbox.py +1026 -0
  321. bootstack/widgets/_impl/composites/shell/__init__.py +40 -0
  322. bootstack/widgets/_impl/composites/shell/content_host.py +52 -0
  323. bootstack/widgets/_impl/composites/shell/layout.py +335 -0
  324. bootstack/widgets/_impl/composites/shell/nav_panel.py +345 -0
  325. bootstack/widgets/_impl/composites/shell/providers.py +558 -0
  326. bootstack/widgets/_impl/composites/shell/rail.py +117 -0
  327. bootstack/widgets/_impl/composites/shell/shell.py +581 -0
  328. bootstack/widgets/_impl/composites/shell/workspace.py +273 -0
  329. bootstack/widgets/_impl/composites/sidenav/__init__.py +16 -0
  330. bootstack/widgets/_impl/composites/sidenav/header.py +81 -0
  331. bootstack/widgets/_impl/composites/sidenav/separator.py +44 -0
  332. bootstack/widgets/_impl/composites/slider/__init__.py +7 -0
  333. bootstack/widgets/_impl/composites/slider/_shared.py +195 -0
  334. bootstack/widgets/_impl/composites/slider/rangeslider.py +982 -0
  335. bootstack/widgets/_impl/composites/slider/slider.py +851 -0
  336. bootstack/widgets/_impl/composites/spinnerentry.py +185 -0
  337. bootstack/widgets/_impl/composites/tableview/__init__.py +5 -0
  338. bootstack/widgets/_impl/composites/tableview/tableview.py +3388 -0
  339. bootstack/widgets/_impl/composites/tableview/types.py +169 -0
  340. bootstack/widgets/_impl/composites/tabs/__init__.py +23 -0
  341. bootstack/widgets/_impl/composites/tabs/tabitem.py +389 -0
  342. bootstack/widgets/_impl/composites/tabs/tabs.py +974 -0
  343. bootstack/widgets/_impl/composites/tabs/tabview.py +650 -0
  344. bootstack/widgets/_impl/composites/textarea/__init__.py +26 -0
  345. bootstack/widgets/_impl/composites/textarea/change.py +46 -0
  346. bootstack/widgets/_impl/composites/textarea/codeeditor.py +526 -0
  347. bootstack/widgets/_impl/composites/textarea/core.py +495 -0
  348. bootstack/widgets/_impl/composites/textarea/decoration.py +42 -0
  349. bootstack/widgets/_impl/composites/textarea/diff.py +127 -0
  350. bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +1 -0
  351. bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +138 -0
  352. bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +158 -0
  353. bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +138 -0
  354. bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +312 -0
  355. bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +177 -0
  356. bootstack/widgets/_impl/composites/textarea/filter.py +171 -0
  357. bootstack/widgets/_impl/composites/textarea/search_overlay.py +459 -0
  358. bootstack/widgets/_impl/composites/textarea/sidebar.py +88 -0
  359. bootstack/widgets/_impl/composites/textarea/style_registry.py +178 -0
  360. bootstack/widgets/_impl/composites/textarea/textarea.py +606 -0
  361. bootstack/widgets/_impl/composites/textarea/undo.py +217 -0
  362. bootstack/widgets/_impl/composites/textentry.py +57 -0
  363. bootstack/widgets/_impl/composites/timeentry.py +176 -0
  364. bootstack/widgets/_impl/composites/toast.py +390 -0
  365. bootstack/widgets/_impl/composites/toast_stack.py +156 -0
  366. bootstack/widgets/_impl/composites/togglegroup.py +418 -0
  367. bootstack/widgets/_impl/composites/toolbar.py +608 -0
  368. bootstack/widgets/_impl/composites/tooltip.py +491 -0
  369. bootstack/widgets/_impl/composites/tree/__init__.py +7 -0
  370. bootstack/widgets/_impl/composites/tree/source_binding.py +136 -0
  371. bootstack/widgets/_impl/composites/tree/treeitem.py +393 -0
  372. bootstack/widgets/_impl/composites/tree/treenode.py +174 -0
  373. bootstack/widgets/_impl/composites/tree/treeview.py +841 -0
  374. bootstack/widgets/_impl/mixins/__init__.py +24 -0
  375. bootstack/widgets/_impl/mixins/configure_mixin.py +216 -0
  376. bootstack/widgets/_impl/mixins/entry_mixin.py +134 -0
  377. bootstack/widgets/_impl/mixins/font_mixin.py +368 -0
  378. bootstack/widgets/_impl/mixins/icon_mixin.py +61 -0
  379. bootstack/widgets/_impl/mixins/localization_mixin.py +253 -0
  380. bootstack/widgets/_impl/mixins/signal_mixin.py +268 -0
  381. bootstack/widgets/_impl/mixins/validation_mixin.py +226 -0
  382. bootstack/widgets/_impl/primitives/__init__.py +49 -0
  383. bootstack/widgets/_impl/primitives/_menubutton.py +107 -0
  384. bootstack/widgets/_impl/primitives/badge.py +45 -0
  385. bootstack/widgets/_impl/primitives/button.py +76 -0
  386. bootstack/widgets/_impl/primitives/card.py +45 -0
  387. bootstack/widgets/_impl/primitives/checkbutton.py +124 -0
  388. bootstack/widgets/_impl/primitives/checktoggle.py +62 -0
  389. bootstack/widgets/_impl/primitives/combobox.py +156 -0
  390. bootstack/widgets/_impl/primitives/entry.py +87 -0
  391. bootstack/widgets/_impl/primitives/flexframe.py +448 -0
  392. bootstack/widgets/_impl/primitives/frame.py +185 -0
  393. bootstack/widgets/_impl/primitives/gridframe.py +546 -0
  394. bootstack/widgets/_impl/primitives/label.py +84 -0
  395. bootstack/widgets/_impl/primitives/labelframe.py +54 -0
  396. bootstack/widgets/_impl/primitives/optionmenu.py +387 -0
  397. bootstack/widgets/_impl/primitives/packframe.py +227 -0
  398. bootstack/widgets/_impl/primitives/panedwindow.py +45 -0
  399. bootstack/widgets/_impl/primitives/progressbar.py +83 -0
  400. bootstack/widgets/_impl/primitives/radiobutton.py +115 -0
  401. bootstack/widgets/_impl/primitives/radiotoggle.py +54 -0
  402. bootstack/widgets/_impl/primitives/scrollbar.py +42 -0
  403. bootstack/widgets/_impl/primitives/separator.py +43 -0
  404. bootstack/widgets/_impl/primitives/sizegrip.py +33 -0
  405. bootstack/widgets/_impl/primitives/spinbox.py +95 -0
  406. bootstack/widgets/_impl/primitives/switch.py +44 -0
  407. bootstack/widgets/_impl/primitives/treeview.py +69 -0
  408. bootstack/widgets/app.py +371 -0
  409. bootstack/widgets/appshell.py +1179 -0
  410. bootstack/widgets/avatar.py +140 -0
  411. bootstack/widgets/boolean_controls.py +455 -0
  412. bootstack/widgets/button.py +224 -0
  413. bootstack/widgets/buttongroup.py +239 -0
  414. bootstack/widgets/calendar.py +195 -0
  415. bootstack/widgets/card.py +159 -0
  416. bootstack/widgets/carousel.py +241 -0
  417. bootstack/widgets/chart.py +302 -0
  418. bootstack/widgets/codeeditor.py +675 -0
  419. bootstack/widgets/contextmenu.py +371 -0
  420. bootstack/widgets/datatable.py +688 -0
  421. bootstack/widgets/datefield.py +395 -0
  422. bootstack/widgets/divider.py +60 -0
  423. bootstack/widgets/expander.py +579 -0
  424. bootstack/widgets/form.py +200 -0
  425. bootstack/widgets/gallery.py +250 -0
  426. bootstack/widgets/gauge.py +168 -0
  427. bootstack/widgets/grid.py +121 -0
  428. bootstack/widgets/groupbox.py +162 -0
  429. bootstack/widgets/label.py +229 -0
  430. bootstack/widgets/listview.py +354 -0
  431. bootstack/widgets/menubutton.py +419 -0
  432. bootstack/widgets/numberfield.py +431 -0
  433. bootstack/widgets/pagestack.py +345 -0
  434. bootstack/widgets/passwordfield.py +383 -0
  435. bootstack/widgets/pathfield.py +454 -0
  436. bootstack/widgets/picture.py +251 -0
  437. bootstack/widgets/progressbar.py +106 -0
  438. bootstack/widgets/radio_variants.py +271 -0
  439. bootstack/widgets/radiogroup.py +228 -0
  440. bootstack/widgets/scrollbar.py +64 -0
  441. bootstack/widgets/scrollview.py +186 -0
  442. bootstack/widgets/select.py +302 -0
  443. bootstack/widgets/selectbutton.py +182 -0
  444. bootstack/widgets/sidebar_toggle.py +130 -0
  445. bootstack/widgets/sizegrip.py +42 -0
  446. bootstack/widgets/slider.py +408 -0
  447. bootstack/widgets/spinbox.py +147 -0
  448. bootstack/widgets/spinnerfield.py +413 -0
  449. bootstack/widgets/splash.py +367 -0
  450. bootstack/widgets/splitview.py +482 -0
  451. bootstack/widgets/stacks.py +249 -0
  452. bootstack/widgets/statusbar.py +189 -0
  453. bootstack/widgets/tabs.py +425 -0
  454. bootstack/widgets/textarea.py +459 -0
  455. bootstack/widgets/textfield.py +410 -0
  456. bootstack/widgets/theme_toggle.py +92 -0
  457. bootstack/widgets/timefield.py +340 -0
  458. bootstack/widgets/toast.py +314 -0
  459. bootstack/widgets/togglegroup.py +231 -0
  460. bootstack/widgets/toolbar.py +347 -0
  461. bootstack/widgets/tooltip.py +94 -0
  462. bootstack/widgets/tree.py +648 -0
  463. bootstack/widgets/types.py +305 -0
  464. bootstack/widgets/window.py +307 -0
  465. bootstack-0.1.0.dist-info/METADATA +301 -0
  466. bootstack-0.1.0.dist-info/RECORD +471 -0
  467. bootstack-0.1.0.dist-info/WHEEL +5 -0
  468. bootstack-0.1.0.dist-info/entry_points.txt +2 -0
  469. bootstack-0.1.0.dist-info/licenses/LICENSE +21 -0
  470. bootstack-0.1.0.dist-info/licenses/NOTICE +10 -0
  471. bootstack-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,169 @@
1
+ """Type definitions and configuration normalizers for TableView widget."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+ from typing_extensions import Literal, TypedDict
7
+
8
+
9
+ class EditingOptions(TypedDict, total=False):
10
+ """Configure add/update/delete support and form dialog options."""
11
+ adding: bool
12
+ updating: bool
13
+ deleting: bool
14
+ form: dict[str, Any]
15
+
16
+
17
+ class SelectionOptions(TypedDict, total=False):
18
+ """Control selection mode (single/multi/none) and select-all allowance."""
19
+ mode: Literal['single', 'multi', 'none']
20
+ allow_select_all: bool
21
+
22
+
23
+ class ExportingOptions(TypedDict, total=False):
24
+ """Configure export availability and formats."""
25
+ enabled: bool
26
+ allow_export_selection: bool
27
+ export_scope: Literal['page', 'all']
28
+ formats: tuple[str, ...]
29
+
30
+
31
+ class PagingOptions(TypedDict, total=False):
32
+ """Paging mode and sizing; toggles x/y scrollbars."""
33
+ mode: Literal['standard', 'virtual']
34
+ page_size: int
35
+ page_index: int
36
+ cache_size: int
37
+ xscroll: bool
38
+ yscroll: bool
39
+
40
+
41
+ class RowAlternationOptions(TypedDict, total=False):
42
+ """Alternating row striping (enabled flag and accent token)."""
43
+ enabled: bool
44
+ accent: str
45
+
46
+
47
+ class FilteringOptions(TypedDict, total=False):
48
+ """Toggle filtering and which menus expose filter actions."""
49
+ enabled: bool
50
+ header_menu_filtering: bool
51
+ row_menu_filtering: bool
52
+
53
+
54
+ class SearchOptions(TypedDict, total=False):
55
+ """Configure searchbar visibility, advanced mode, and trigger timing."""
56
+ enabled: bool
57
+ mode: Literal['standard', 'advanced']
58
+ event: Literal['input', 'enter']
59
+
60
+
61
+ # ------ Helper methods --------
62
+
63
+ def parse_selection_mode(mode: str):
64
+ """Convert TableView selection mode to Treeview selectmode."""
65
+ if mode == 'single':
66
+ return 'browse'
67
+ elif mode == 'multi':
68
+ return 'extended'
69
+ else:
70
+ return 'none'
71
+
72
+
73
+ # ------ Build internal dicts from flattened kwargs --------
74
+
75
+ def build_editing_options(
76
+ enable_adding: bool = False,
77
+ enable_editing: bool = False,
78
+ enable_deleting: bool = False,
79
+ form_options: dict | None = None,
80
+ ) -> EditingOptions:
81
+ """Build editing options dict from flattened kwargs."""
82
+ return dict(
83
+ adding=enable_adding,
84
+ updating=enable_editing,
85
+ deleting=enable_deleting,
86
+ form=form_options or {}
87
+ )
88
+
89
+
90
+ def build_selection_options(
91
+ selection_mode: Literal['none', 'single', 'multi'] = 'single',
92
+ allow_select_all: bool = True,
93
+ ) -> SelectionOptions:
94
+ """Build selection options dict from flattened kwargs."""
95
+ return dict(
96
+ mode=selection_mode,
97
+ allow_select_all=allow_select_all,
98
+ )
99
+
100
+
101
+ def build_filtering_options(
102
+ enable_filtering: bool = True,
103
+ enable_header_filtering: bool = True,
104
+ enable_row_filtering: bool = True,
105
+ ) -> FilteringOptions:
106
+ """Build filtering options dict from flattened kwargs."""
107
+ return dict(
108
+ enabled=enable_filtering,
109
+ header_menu_filtering=enable_header_filtering,
110
+ row_menu_filtering=enable_row_filtering,
111
+ )
112
+
113
+
114
+ def build_exporting_options(
115
+ enable_exporting: bool = False,
116
+ allow_export_selection: bool = True,
117
+ export_scope: Literal['page', 'all'] = 'page',
118
+ export_formats: tuple[str, ...] | None = None,
119
+ ) -> ExportingOptions:
120
+ """Build exporting options dict from flattened kwargs."""
121
+ return dict(
122
+ enabled=enable_exporting,
123
+ allow_export_selection=allow_export_selection,
124
+ export_scope=export_scope,
125
+ formats=export_formats or ('csv',)
126
+ )
127
+
128
+
129
+ def build_paging_options(
130
+ paging_mode: Literal['standard', 'virtual'] = 'standard',
131
+ page_size: int = 25,
132
+ page_index: int = 0,
133
+ page_cache_size: int = 3,
134
+ show_vscrollbar: bool = True,
135
+ show_hscrollbar: bool = False,
136
+ ) -> PagingOptions:
137
+ """Build paging options dict from flattened kwargs."""
138
+ return dict(
139
+ mode=paging_mode,
140
+ page_size=page_size,
141
+ page_index=page_index,
142
+ cache_size=page_cache_size,
143
+ xscroll=show_hscrollbar,
144
+ yscroll=show_vscrollbar,
145
+ )
146
+
147
+
148
+ def build_search_options(
149
+ enable_search: bool = True,
150
+ search_mode: Literal['standard', 'advanced'] = 'standard',
151
+ search_trigger: Literal['enter', 'input'] = 'enter',
152
+ ) -> SearchOptions:
153
+ """Build search options dict from flattened kwargs."""
154
+ return dict(
155
+ enabled=enable_search,
156
+ mode=search_mode,
157
+ event=search_trigger,
158
+ )
159
+
160
+
161
+ def build_row_alternation_options(
162
+ striped: bool = False,
163
+ striped_background: str = 'background[+1]',
164
+ ) -> RowAlternationOptions:
165
+ """Build row alternation options dict from flattened kwargs."""
166
+ return dict(
167
+ enabled=striped,
168
+ accent=striped_background,
169
+ )
@@ -0,0 +1,23 @@
1
+ """Tabs composite widget module."""
2
+
3
+ from .tabs import Tabs
4
+ from .tabview import TabView
5
+ from bootstack.events import (
6
+ TabRef,
7
+ TabChangeEvent,
8
+ TabActivateEvent,
9
+ TabDeactivateEvent,
10
+ ChangeReason,
11
+ ChangeMethod,
12
+ )
13
+
14
+ __all__ = [
15
+ "Tabs",
16
+ "TabView",
17
+ "TabRef",
18
+ "TabChangeEvent",
19
+ "TabActivateEvent",
20
+ "TabDeactivateEvent",
21
+ "ChangeReason",
22
+ "ChangeMethod",
23
+ ]
@@ -0,0 +1,389 @@
1
+ """TabItem widget - a tab with optional close button."""
2
+ from __future__ import annotations
3
+
4
+ __all__ = ['TabItem']
5
+
6
+ from tkinter import TclError, Variable
7
+ from typing import Any, Callable, Literal, TYPE_CHECKING
8
+ from typing_extensions import Unpack
9
+
10
+ from bootstack.events import TabCloseEvent
11
+ from bootstack.widgets._impl.composites.compositeframe import CompositeFrame, CompositeFrameKwargs
12
+ from bootstack.widgets._impl.primitives.button import Button
13
+ from bootstack.widgets._impl.primitives.label import Label
14
+ from bootstack.widgets._impl.mixins import configure_delegate
15
+ from bootstack.widgets.types import Master
16
+ from bootstack._core.capabilities.signals import normalize_signal
17
+
18
+ if TYPE_CHECKING:
19
+ from bootstack.signals import Signal
20
+
21
+
22
+ class TabItem(CompositeFrame):
23
+ """A tab item widget with optional close button.
24
+
25
+ TabItem extends CompositeFrame to provide a clickable tab with coordinated
26
+ hover/pressed/focus states across all child widgets. It supports selection
27
+ via signal/variable and optional close functionality.
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ master: Master = None,
33
+ text: str = "",
34
+ icon: str | dict = None,
35
+ compound: Literal['left', 'right', 'top', 'bottom', 'center', 'none'] = 'left',
36
+ value: Any = None,
37
+ variable: Variable = None,
38
+ orient: Literal['horizontal', 'vertical'] = 'horizontal',
39
+ signal: 'Signal[Any]' = None,
40
+ command: Callable = None,
41
+ closable: bool | Literal['hover'] = False,
42
+ close_command: Callable = None,
43
+ variant: Literal['bar'] = 'default',
44
+ min_width: int = None,
45
+ **kwargs: Unpack[CompositeFrameKwargs]
46
+ ):
47
+ """Create a TabItem widget.
48
+
49
+ Args:
50
+ master: Parent widget. If None, uses the default root window.
51
+ text: Text to display on the tab.
52
+ icon: Icon to display on the tab (left of text).
53
+ compound: How to position icon relative to text. One of 'left',
54
+ 'right', 'top', 'bottom', 'center', or 'none'. Default is 'left'.
55
+ value: Value to set on signal/variable when this tab is selected.
56
+ variable: Tk variable for selection state (synced with signal).
57
+ See [tkinter Variables](https://docs.python.org/3/library/tkinter.html#tkinter-variables).
58
+ orient: Orientation hint for styling ('horizontal' or 'vertical').
59
+ Default is 'horizontal'.
60
+ signal: Reactive Signal for selection state (preferred over variable).
61
+ command: Callback invoked when the tab is selected.
62
+ closable: Close button visibility. True=always visible, False=hidden,
63
+ 'hover'=visible only on hover (space reserved).
64
+ close_command: Callback invoked when close button is clicked.
65
+ variant: Tab style variant. Only 'bar' is supported.
66
+ min_width: Minimum tab width in pixels. The tab will not shrink below
67
+ this width regardless of label content. Default is None (no minimum).
68
+ **kwargs: Additional arguments passed to CompositeFrame.
69
+ """
70
+ self._text = text
71
+ self._icon = icon
72
+ self._compound = compound
73
+ self._value = value
74
+ self._command = command
75
+ self._closable = closable
76
+ self._close_command = close_command
77
+ self._variant = variant
78
+ self._orient = orient
79
+ self._min_width = min_width
80
+ self._accent = None # Will be set from kwargs
81
+
82
+ # Selection state - signal/variable syncing
83
+ self._signal: Signal[Any] | None = None
84
+ self._variable: Variable | None = None
85
+ self._trace_id: str | None = None
86
+
87
+ # Determine ttk_class based on variant
88
+ ttk_class = self._get_ttk_class(variant)
89
+
90
+ # Extract accent for styling
91
+ accent = kwargs.pop('accent', None)
92
+ surface = kwargs.pop('surface', None)
93
+ self._accent = accent # Store for later use
94
+
95
+ # Extract width for label (frame width doesn't work well with propagation)
96
+ self._width = kwargs.pop('width', None)
97
+
98
+ # Extract anchor for label
99
+ self._anchor = kwargs.pop('anchor', 'center')
100
+
101
+ super().__init__(
102
+ master=master,
103
+ ttk_class=ttk_class,
104
+ accent=accent,
105
+ variant=variant,
106
+ takefocus=False,
107
+ style_options={"orient": orient},
108
+ **kwargs
109
+ )
110
+
111
+ # Build internal widgets
112
+ self._label: Label | None = None
113
+ self._close_button: Button | None = None
114
+
115
+ self._build_widget(accent, surface)
116
+
117
+ if self._min_width:
118
+ self.after_idle(self._apply_min_width)
119
+
120
+ # Set up signal/variable after widget is built
121
+ if signal is not None:
122
+ self._set_signal_or_variable(signal)
123
+ elif variable is not None:
124
+ self._set_signal_or_variable(variable)
125
+
126
+ # Bind click handler
127
+ self.on_invoked(self._on_tab_click)
128
+
129
+ # Bind destroy handler to clean up variable trace
130
+ self.bind('<Destroy>', self._on_destroy, add='+')
131
+
132
+ def _apply_min_width(self):
133
+ """Enforce minimum tab width after the first layout pass."""
134
+ if not self.winfo_exists():
135
+ return
136
+ if self.winfo_reqwidth() < self._min_width:
137
+ self.pack_propagate(False)
138
+ self.configure(width=self._min_width, height=self.winfo_reqheight())
139
+
140
+ def _on_destroy(self, event=None):
141
+ """Clean up variable trace when widget is destroyed."""
142
+ if event.widget is not self:
143
+ return
144
+ if self._variable is not None and self._trace_id is not None:
145
+ try:
146
+ self._variable.trace_remove('write', self._trace_id)
147
+ except Exception:
148
+ pass
149
+ self._trace_id = None
150
+
151
+ def _get_ttk_class(self, variant: str) -> str:
152
+ """Get the ttk class name for the given variant."""
153
+ return 'TabItem.TFrame'
154
+
155
+ def _build_widget(self, accent: str = None, surface: str = None):
156
+ """Build the internal widget structure."""
157
+ style_opts = {}
158
+ if surface:
159
+ style_opts['surface'] = surface
160
+
161
+ # Pass width to label if specified
162
+ label_opts = {}
163
+ if self._width is not None:
164
+ label_opts['width'] = self._width
165
+
166
+ # Close button must be packed before the label so it reserves right-side
167
+ # space before the label claims all available width with expand=True
168
+ if self._closable:
169
+ self._create_close_button(accent, style_opts)
170
+
171
+ # Main label for text/icon — fills remaining space
172
+ self._label = Label(
173
+ self,
174
+ text=self._text,
175
+ icon=self._icon,
176
+ compound=self._compound,
177
+ ttk_class='TabItem.TLabel',
178
+ accent=accent,
179
+ variant=self._variant,
180
+ takefocus=False,
181
+ anchor=self._anchor,
182
+ **style_opts,
183
+ **label_opts
184
+ )
185
+ self.register_composite(self._label)
186
+ self._label.pack(side='left', fill='both', expand=True)
187
+
188
+ def _create_close_button(self, accent: str = None, style_opts: dict = None):
189
+ """Create the close button widget."""
190
+ style_opts = style_opts or {}
191
+ # Pass closable mode to style builder via style_options
192
+ extra_style_options = {}
193
+ if self._closable == 'hover':
194
+ extra_style_options['closable'] = 'hover'
195
+ self._close_button = Button(
196
+ self,
197
+ icon={'name': 'x-lg', 'size': 14},
198
+ icon_only=True,
199
+ ttk_class='TabItem.TButton',
200
+ accent=accent,
201
+ variant=self._variant, # Use same variant as the tab for matching background
202
+ takefocus=False,
203
+ command=self._on_close_click,
204
+ padding=0,
205
+ anchor='center',
206
+ style_options=extra_style_options,
207
+ **style_opts
208
+ )
209
+ self.register_composite(self._close_button)
210
+ # Pack on the right; if label already exists (deferred creation), insert
211
+ # before it in pack order so the label's expand=True fills remaining space
212
+ pack_opts = {'side': 'right', 'padx': (8, 0)}
213
+ if self._label is not None:
214
+ self._close_button.pack(before=self._label, **pack_opts)
215
+ else:
216
+ self._close_button.pack(**pack_opts)
217
+
218
+ def _set_signal_or_variable(self, value: Any):
219
+ """Set up signal/variable binding with trace for selection updates."""
220
+ # Remove old trace if exists
221
+ if self._variable is not None and self._trace_id is not None:
222
+ try:
223
+ self._variable.trace_remove('write', self._trace_id)
224
+ except Exception:
225
+ pass
226
+
227
+ # Normalize to get both signal and variable
228
+ binding = normalize_signal(value)
229
+ if binding is not None:
230
+ self._signal = binding.signal
231
+ self._variable = binding.variable
232
+ else:
233
+ self._variable = value
234
+ self._signal = None
235
+
236
+ # Add trace to update selection state
237
+ if self._variable is not None:
238
+ self._trace_id = self._variable.trace_add('write', self._on_variable_changed)
239
+ # Initial update
240
+ self._update_selection_state()
241
+
242
+ def _on_variable_changed(self, *args):
243
+ """Handle variable changes to update selection state."""
244
+ self._update_selection_state()
245
+
246
+ def _update_selection_state(self):
247
+ """Update visual state based on selection."""
248
+ # Guard against updates after widget destruction
249
+ try:
250
+ if not self.winfo_exists():
251
+ return
252
+ except TclError:
253
+ return
254
+
255
+ if self._variable is not None and self._value is not None:
256
+ is_selected = self._variable.get() == self._value
257
+ self.set_selected(is_selected)
258
+
259
+ def _on_tab_click(self, event=None):
260
+ """Handle tab click - select and invoke command."""
261
+ # Set selection if variable is configured
262
+ if self._variable is not None and self._value is not None:
263
+ self._variable.set(self._value)
264
+
265
+ # Invoke command if provided
266
+ if self._command is not None:
267
+ self._command()
268
+
269
+ # Generate virtual event
270
+ self.event_generate('<<TabSelect>>', data={'value': self._value})
271
+
272
+ def _on_close_click(self):
273
+ """Handle close button click."""
274
+ # Store value before potential destruction
275
+ value = self._value
276
+
277
+ # Generate virtual event BEFORE close_command (which may destroy the widget)
278
+ self.event_generate('<<TabClose>>', data=TabCloseEvent(value=value))
279
+
280
+ # Invoke close command if provided (this may destroy the widget)
281
+ if self._close_command is not None:
282
+ self._close_command()
283
+
284
+ # --- Public API ---
285
+
286
+ def select(self):
287
+ """Select this tab (set variable to this tab's value)."""
288
+ if self._variable is not None and self._value is not None:
289
+ self._variable.set(self._value)
290
+
291
+ @property
292
+ def value(self) -> Any:
293
+ """Get the value associated with this tab."""
294
+ return self._value
295
+
296
+ @property
297
+ def is_selected(self) -> bool:
298
+ """Check if this tab is currently selected."""
299
+ if self._variable is not None and self._value is not None:
300
+ return self._variable.get() == self._value
301
+ return self.selected
302
+
303
+ # --- Configuration delegates ---
304
+
305
+ @configure_delegate('text')
306
+ def _delegate_text(self, value=None):
307
+ """Get or set the tab text."""
308
+ if value is None:
309
+ return self._text
310
+ self._text = value
311
+ if self._label is not None:
312
+ self._label.configure(text=value)
313
+ return None
314
+
315
+ @configure_delegate('icon')
316
+ def _delegate_icon(self, value=None):
317
+ """Get or set the tab icon."""
318
+ if value is None:
319
+ return self._icon
320
+ self._icon = value
321
+ if self._label is not None:
322
+ self._label.configure(icon=value)
323
+ return None
324
+
325
+ @configure_delegate('compound')
326
+ def _delegate_compound(self, value=None):
327
+ """Get or set the compound (icon position relative to text)."""
328
+ if value is None:
329
+ return self._compound
330
+ self._compound = value
331
+ if self._label is not None:
332
+ self._label.configure(compound=value)
333
+ return None
334
+
335
+ @configure_delegate('value')
336
+ def _delegate_value(self, value=None):
337
+ """Get or set the tab value."""
338
+ if value is None:
339
+ return self._value
340
+ self._value = value
341
+ self._update_selection_state()
342
+ return None
343
+
344
+ @configure_delegate('command')
345
+ def _delegate_command(self, value=None):
346
+ """Get or set the selection command."""
347
+ if value is None:
348
+ return self._command
349
+ self._command = value
350
+ return None
351
+
352
+ @configure_delegate('close_command')
353
+ def _delegate_close_command(self, value=None):
354
+ """Get or set the close command."""
355
+ if value is None:
356
+ return self._close_command
357
+ self._close_command = value
358
+ return None
359
+
360
+ @configure_delegate('closable')
361
+ def _delegate_closable(self, value=None):
362
+ """Get or set close button visibility (True, False, or 'hover')."""
363
+ if value is None:
364
+ return self._closable
365
+ if value != self._closable:
366
+ self._closable = value
367
+ if value and self._close_button is None:
368
+ self._create_close_button(self._accent)
369
+ elif not value and self._close_button is not None:
370
+ self._close_button.place_forget()
371
+ self._close_button.destroy()
372
+ self._close_button = None
373
+ return None
374
+
375
+ @configure_delegate('signal')
376
+ def _delegate_signal(self, value=None):
377
+ """Get or set the signal for selection state."""
378
+ if value is None:
379
+ return self._signal
380
+ self._set_signal_or_variable(value)
381
+ return None
382
+
383
+ @configure_delegate('variable')
384
+ def _delegate_variable(self, value=None):
385
+ """Get or set the variable for selection state."""
386
+ if value is None:
387
+ return self._variable
388
+ self._set_signal_or_variable(value)
389
+ return None