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,515 @@
1
+ """Expander widget - a collapsible container with header and content."""
2
+ from __future__ import annotations
3
+
4
+ from tkinter import Widget, Variable
5
+ from typing_extensions import Unpack
6
+ from typing import Any, Callable, Literal, TYPE_CHECKING, TypedDict
7
+
8
+ from bootstack.events import ToggleEvent
9
+ from bootstack.widgets._impl.primitives.frame import Frame, FrameKwargs
10
+ from bootstack.widgets._impl.primitives.label import Label
11
+ from bootstack.widgets._impl.composites.compositeframe import CompositeFrame
12
+ from bootstack.widgets._impl.mixins import configure_delegate
13
+ from bootstack.widgets.types import Master
14
+ from bootstack._core.capabilities.signals import normalize_signal
15
+
16
+ if TYPE_CHECKING:
17
+ from bootstack.signals import Signal
18
+
19
+
20
+ class SelectedEventData(TypedDict):
21
+ """Payload for `<<Selected>>` events."""
22
+ value: Any
23
+ """The expander's configured value."""
24
+
25
+
26
+ class Expander(Frame):
27
+ """A collapsible container with a clickable header and expandable content.
28
+
29
+ The Expander displays a header with an optional icon, title, and chevron
30
+ button. Clicking anywhere on the header toggles the visibility of the
31
+ content area.
32
+
33
+ Expander also supports selection state via `signal`/`variable` and
34
+ `value`, similar to RadioButton. When clicked, it sets the variable to
35
+ its value, enabling radio-group-like behavior for navigation.
36
+
37
+ """
38
+
39
+ def __init__(
40
+ self,
41
+ master: Master = None,
42
+ title: str = "",
43
+ icon: str | dict = None,
44
+ expanded: bool = True,
45
+ collapsible: bool = True,
46
+ highlight: bool = False,
47
+ icon_expanded: str | dict = None,
48
+ icon_collapsed: str | dict = None,
49
+ icon_position: Literal["before", "after"] = "after",
50
+ signal: 'Signal[Any]' = None,
51
+ variable: Variable = None,
52
+ value: Any = None,
53
+ **kwargs: Unpack[FrameKwargs]
54
+ ):
55
+ """Create an Expander widget.
56
+
57
+ Args:
58
+ master: Parent widget. If None, uses the default root window.
59
+ title: Header title text.
60
+ icon: Icon to display in header (left of title).
61
+ expanded: Initial expansion state. Default is True (expanded).
62
+ collapsible: Whether the expander can be toggled. Default is True.
63
+ highlight: If True, header shows 'selected' state when expanded.
64
+ icon_expanded: Icon spec for expanded state. Default is chevron-up.
65
+ icon_collapsed: Icon spec for collapsed state. Default is chevron-down.
66
+ icon_position: Position of chevron relative to title.
67
+ signal: Reactive Signal for selection state (preferred over variable).
68
+ variable: Tk variable for selection state (synced with signal).
69
+ See [tkinter Variables](https://docs.python.org/3/library/tkinter.html#tkinter-variables).
70
+ value: Value to set on signal/variable when selected.
71
+ **kwargs: Additional arguments passed to Frame. Use `accent` and `variant`
72
+ for styling the header and chevron.
73
+ """
74
+ self._header_accent = kwargs.pop('accent', None)
75
+ self._header_variant = kwargs.pop('variant', None)
76
+ if 'show_border' in kwargs:
77
+ kwargs.setdefault('padding', 3) # need 3 pixels to avoid cutting off corners
78
+ kwargs.setdefault('takefocus', False) # Outer container shouldn't take focus
79
+ super().__init__(master, **kwargs)
80
+
81
+ self._title = title
82
+ self._icon = icon
83
+ self._expanded = expanded
84
+ self._collapsible = collapsible
85
+ self._highlight = highlight
86
+ self._icon_expanded = icon_expanded
87
+ self._icon_collapsed = icon_collapsed
88
+ self._icon_position = icon_position
89
+ self._content_widget = None
90
+ self._value = value
91
+ self._compact = False
92
+
93
+ # Selection state - signal/variable syncing
94
+ self._signal: Signal[Any] | None = None
95
+ self._variable: Variable | None = None
96
+ self._trace_id: str | None = None
97
+
98
+ # Widget references
99
+ self._icon_label: Label | None = None
100
+ self._title_label: Label | None = None
101
+
102
+ self._build_widget()
103
+
104
+ # Set up signal/variable after widget is built
105
+ # Prefer signal if both provided
106
+ if signal is not None:
107
+ self._set_signal_or_variable(signal)
108
+ elif variable is not None:
109
+ self._set_signal_or_variable(variable)
110
+
111
+ def _set_signal_or_variable(self, value: Any):
112
+ """Set up signal/variable binding with trace for selection updates."""
113
+ # Remove old trace if exists
114
+ if self._variable is not None and self._trace_id is not None:
115
+ try:
116
+ self._variable.trace_remove('write', self._trace_id)
117
+ except Exception:
118
+ pass
119
+
120
+ # Normalize to get both signal and variable
121
+ binding = normalize_signal(value)
122
+ if binding is not None:
123
+ self._signal = binding.signal
124
+ self._variable = binding.variable
125
+ else:
126
+ # Direct variable
127
+ self._variable = value
128
+ self._signal = None
129
+
130
+ # Add trace to update selection state
131
+ if self._variable is not None:
132
+ self._trace_id = self._variable.trace_add('write', self._on_variable_changed)
133
+ # Initial update
134
+ self._update_selection_state()
135
+
136
+ def _on_variable_changed(self, *args):
137
+ """Handle variable changes to update selection state."""
138
+ self._update_selection_state()
139
+
140
+ def _update_selection_state(self):
141
+ """Update visual state based on selection."""
142
+ # For now, just track state - visual updates can be added later
143
+ # when a dedicated nav style is created
144
+ pass
145
+
146
+ @property
147
+ def _current_chevron_icon(self):
148
+ """Get the appropriate chevron icon for current state."""
149
+ if self._expanded:
150
+ return self._icon_expanded or {'name': 'chevron-up', 'size': 16}
151
+ else:
152
+ return self._icon_collapsed or {'name': 'chevron-down', 'size': 16}
153
+
154
+ def _build_widget(self):
155
+ """Build the internal widget structure."""
156
+ accent = self._header_accent
157
+ variant = self._header_variant
158
+
159
+ # Use CompositeFrame for header to enable hover/pressed/focus states
160
+ self._header_frame = CompositeFrame(
161
+ self, ttk_class='Expander.TFrame',
162
+ accent=accent, variant=variant, padding=8, takefocus=True
163
+ )
164
+ self._header_frame.pack(fill='x')
165
+
166
+ # Icon (optional, before title)
167
+ if self._icon:
168
+ self._icon_label = Label(
169
+ self._header_frame,
170
+ icon=self._icon,
171
+ icon_only=True,
172
+ ttk_class='Expander.TLabel',
173
+ accent=accent, variant=variant,
174
+ takefocus=False,
175
+ )
176
+ self._header_frame.register_composite(self._icon_label)
177
+ self._icon_label.pack(side='left', padx=(0, 8))
178
+
179
+ # Title label
180
+ self._title_label = Label(
181
+ self._header_frame,
182
+ text=self._title,
183
+ anchor='w',
184
+ ttk_class='Expander.TLabel',
185
+ accent=accent, variant=variant,
186
+ takefocus=False,
187
+ )
188
+ self._header_frame.register_composite(self._title_label)
189
+
190
+ # Toggle button (chevron)
191
+ self._toggle_button = Label(
192
+ self._header_frame,
193
+ icon=self._current_chevron_icon,
194
+ icon_only=True,
195
+ accent=accent, variant=variant,
196
+ ttk_class='Expander.TLabel',
197
+ takefocus=False,
198
+ )
199
+ self._header_frame.register_composite(self._toggle_button)
200
+
201
+ # Layout based on icon_position
202
+ if self._icon_position == "before":
203
+ self._toggle_button.pack(side='left')
204
+ self._title_label.pack(side='left', fill='x', expand=True)
205
+ else:
206
+ self._title_label.pack(side='left', fill='x', expand=True)
207
+ self._toggle_button.pack(side='right')
208
+
209
+ # Hide button if not collapsible
210
+ if not self._collapsible:
211
+ self._toggle_button.pack_forget()
212
+
213
+ # Content frame
214
+ self._content_frame = Frame(self)
215
+ if self._expanded:
216
+ self._content_frame.pack(fill='both', expand=True)
217
+
218
+ # Set initial highlight state
219
+ if self._highlight and self._expanded:
220
+ self._header_frame.set_selected(True)
221
+
222
+ # Bind header events
223
+ self._bind_header_events()
224
+
225
+ def _bind_header_events(self):
226
+ """Make entire header clickable and keyboard accessible."""
227
+ # Always bind for selection, even if not collapsible
228
+ self._header_frame.bind('<Button-1>', self._on_header_click, add='+')
229
+ self._title_label.bind('<Button-1>', self._on_header_click, add='+')
230
+ if self._icon_label:
231
+ self._icon_label.bind('<Button-1>', self._on_header_click, add='+')
232
+
233
+ # Keyboard support
234
+ self._header_frame.bind('<Return>', self._on_header_click, add='+')
235
+ self._header_frame.bind('<space>', self._on_header_click, add='+')
236
+
237
+ def _on_header_click(self, event=None):
238
+ """Handle header click - select and optionally toggle."""
239
+ # Request focus on click
240
+ self._header_frame.focus_set()
241
+
242
+ # Set selection if variable is configured
243
+ if self._variable is not None and self._value is not None:
244
+ self._variable.set(self._value)
245
+ self.event_generate('<<Selected>>', data={'value': self._value})
246
+
247
+ # Toggle expansion if collapsible
248
+ if self._collapsible:
249
+ self.toggle()
250
+
251
+ def toggle(self):
252
+ """Toggle expanded/collapsed state."""
253
+ if not self._collapsible:
254
+ return
255
+
256
+ if self._expanded:
257
+ self.collapse()
258
+ else:
259
+ self.expand()
260
+
261
+ def expand(self):
262
+ """Expand the content area."""
263
+ if self._expanded:
264
+ return
265
+
266
+ self._expanded = True
267
+ self._content_frame.pack(fill='both', expand=True)
268
+ # Recolor any widget that missed a theme change while this section was
269
+ # collapsed (the theme walk skips off-screen widgets; this is the
270
+ # genuine "now visible" trigger, like PageStack page navigation).
271
+ def _recolor(p=self._content_frame):
272
+ from bootstack.style.style import get_style
273
+ get_style().apply_theme_walk(p, only_stale=True)
274
+ self.after_idle(_recolor)
275
+ self._toggle_button.configure(icon=self._current_chevron_icon)
276
+ if self._highlight:
277
+ self._header_frame.set_selected(True)
278
+ self.event_generate('<<Toggle>>', data=ToggleEvent(expanded=True))
279
+
280
+ def collapse(self):
281
+ """Collapse the content area."""
282
+ if not self._expanded:
283
+ return
284
+
285
+ self._expanded = False
286
+ self._content_frame.pack_forget()
287
+ self._toggle_button.configure(icon=self._current_chevron_icon)
288
+ if self._highlight:
289
+ self._header_frame.set_selected(False)
290
+ self.event_generate('<<Toggle>>', data=ToggleEvent(expanded=False))
291
+
292
+ def select(self):
293
+ """Select this expander (set variable to this expander's value)."""
294
+ if self._variable is not None and self._value is not None:
295
+ self._variable.set(self._value)
296
+
297
+ def add(self, widget: Widget = None, **kwargs: Unpack[FrameKwargs]) -> Widget:
298
+ """Add content widget, or create and return an empty frame.
299
+
300
+ Args:
301
+ widget: Optional widget to use as content. If None, creates a Frame.
302
+ **kwargs: When widget is None, these are passed to Frame (e.g., padding).
303
+
304
+ Returns:
305
+ Widget: The content widget (passed or created).
306
+
307
+ Raises:
308
+ ValueError: If content already exists and widget is provided.
309
+ """
310
+ # If content exists and no widget passed, return existing (idempotent)
311
+ if self._content_widget is not None:
312
+ if widget is not None:
313
+ raise ValueError("Expander already has content.")
314
+ return self._content_widget
315
+
316
+ if widget is None:
317
+ widget = Frame(self._content_frame, **kwargs)
318
+
319
+ self._content_widget = widget
320
+ widget.pack(fill='both', expand=True)
321
+ return widget
322
+
323
+ @property
324
+ def content(self) -> Frame:
325
+ """Get the content frame (for direct child parenting)."""
326
+ return self._content_frame
327
+
328
+ @property
329
+ def is_selected(self) -> bool:
330
+ """Check if this expander is currently selected."""
331
+ if self._variable is not None and self._value is not None:
332
+ return self._variable.get() == self._value
333
+ return False
334
+
335
+ def on_toggled(self, callback: Callable[[ToggleEvent], None]) -> str:
336
+ """Register a callback for `<<Toggle>>` events.
337
+
338
+ Args:
339
+ callback: Receives a `ToggleEvent` payload with attribute
340
+ `expanded` (bool — True if now open, False if now closed).
341
+
342
+ Returns:
343
+ Bind ID — pass to `off_toggled()` to unsubscribe.
344
+ """
345
+ return self.bind('<<Toggle>>', callback, add='+')
346
+
347
+ def off_toggled(self, bind_id: str | None = None) -> None:
348
+ """Unsubscribe from `<<Toggle>>`.
349
+
350
+ Args:
351
+ bind_id: ID returned by `on_toggled()`. If None, removes all.
352
+ """
353
+ self.unbind('<<Toggle>>', bind_id)
354
+
355
+ def on_selected(self, callback: Callable[[SelectedEventData], None]) -> str:
356
+ """Register a callback for `<<Selected>>` events.
357
+
358
+ Args:
359
+ callback: Receives a `SelectedEventData` dict with key
360
+ `value` (the expander's configured value).
361
+
362
+ Returns:
363
+ Bind ID — pass to `off_selected()` to unsubscribe.
364
+ """
365
+ return self.bind('<<Selected>>', callback, add='+')
366
+
367
+ def off_selected(self, bind_id: str | None = None) -> None:
368
+ """Unsubscribe from `<<Selected>>`.
369
+
370
+ Args:
371
+ bind_id: ID returned by `on_selected()`. If None, removes all.
372
+ """
373
+ self.unbind('<<Selected>>', bind_id)
374
+
375
+ @configure_delegate('title')
376
+ def _delegate_title(self, value=None):
377
+ """Get or set the title text."""
378
+ if value is None:
379
+ return self._title
380
+ self._title = value
381
+ self._title_label.configure(text=value)
382
+ return None
383
+
384
+ @configure_delegate('icon')
385
+ def _delegate_icon(self, value=None):
386
+ """Get or set the header icon."""
387
+ if value is None:
388
+ return self._icon
389
+ self._icon = value
390
+ if self._icon_label is not None:
391
+ self._icon_label.configure(icon=value)
392
+ elif value is not None:
393
+ # Create icon label if it doesn't exist
394
+ self._icon_label = Label(
395
+ self._header_frame,
396
+ icon=value,
397
+ icon_only=True,
398
+ ttk_class='Expander.TLabel',
399
+ accent=self._header_accent, variant=self._header_variant,
400
+ takefocus=False,
401
+ )
402
+ self._header_frame.register_composite(self._icon_label)
403
+ # Insert at beginning of header
404
+ self._icon_label.pack(side='left', padx=(0, 8), before=self._title_label)
405
+ self._icon_label.bind('<Button-1>', self._on_header_click, add='+')
406
+ return None
407
+
408
+ @configure_delegate('collapsible')
409
+ def _delegate_collapsible(self, value=None):
410
+ """Get or set whether the expander can be toggled."""
411
+ if value is None:
412
+ return self._collapsible
413
+ self._collapsible = value
414
+ if value:
415
+ side = 'left' if self._icon_position == 'before' else 'right'
416
+ self._toggle_button.pack(side=side)
417
+ else:
418
+ self._toggle_button.pack_forget()
419
+ return None
420
+
421
+ @configure_delegate('icon_expanded')
422
+ def _delegate_icon_expanded(self, value=None):
423
+ """Get or set the expanded state chevron icon."""
424
+ if value is None:
425
+ return self._icon_expanded
426
+ self._icon_expanded = value
427
+ if self._expanded:
428
+ self._toggle_button.configure(icon=self._current_chevron_icon)
429
+ return None
430
+
431
+ @configure_delegate('icon_collapsed')
432
+ def _delegate_icon_collapsed(self, value=None):
433
+ """Get or set the collapsed state chevron icon."""
434
+ if value is None:
435
+ return self._icon_collapsed
436
+ self._icon_collapsed = value
437
+ if not self._expanded:
438
+ self._toggle_button.configure(icon=self._current_chevron_icon)
439
+ return None
440
+
441
+ @configure_delegate('value')
442
+ def _delegate_value(self, value=None):
443
+ """Get or set the selection value."""
444
+ if value is None:
445
+ return self._value
446
+ self._value = value
447
+ return None
448
+
449
+ @configure_delegate('compact')
450
+ def _delegate_compact(self, value=None):
451
+ """Get or set compact mode (hides title, shows icon only)."""
452
+ if value is None:
453
+ return self._compact
454
+ self._compact = value
455
+ if value:
456
+ # Hide title
457
+ if self._title_label is not None:
458
+ self._title_label.pack_forget()
459
+ # Center the icon
460
+ if self._icon_label is not None:
461
+ self._icon_label.pack_forget()
462
+ self._icon_label.pack(expand=True)
463
+ else:
464
+ # Restore icon to left-aligned
465
+ if self._icon_label is not None:
466
+ self._icon_label.pack_forget()
467
+ self._icon_label.pack(side='left', padx=(0, 8))
468
+ # Re-pack title in correct position
469
+ if self._title_label is not None:
470
+ if self._icon_position == "before":
471
+ self._title_label.pack(side='left', fill='x', expand=True)
472
+ else:
473
+ # Pack after icon (if exists) or at left
474
+ if self._icon_label is not None:
475
+ self._title_label.pack(side='left', fill='x', expand=True, after=self._icon_label)
476
+ else:
477
+ self._title_label.pack(side='left', fill='x', expand=True)
478
+ return None
479
+
480
+ @configure_delegate('highlight')
481
+ def _delegate_highlight(self, value=None):
482
+ """Get or set highlight mode (shows 'selected' state when expanded)."""
483
+ if value is None:
484
+ return self._highlight
485
+ self._highlight = value
486
+ # Update the current state based on new highlight value
487
+ self._header_frame.set_selected(value and self._expanded)
488
+ return None
489
+
490
+ @configure_delegate('expanded')
491
+ def _delegate_expanded(self, value=None):
492
+ """Get or set the expansion state."""
493
+ if value is None:
494
+ return self._expanded
495
+ if value:
496
+ self.expand()
497
+ else:
498
+ self.collapse()
499
+ return None
500
+
501
+ @configure_delegate('signal')
502
+ def _delegate_signal(self, value=None):
503
+ """Get or set the signal for selection state."""
504
+ if value is None:
505
+ return self._signal
506
+ self._set_signal_or_variable(value)
507
+ return None
508
+
509
+ @configure_delegate('variable')
510
+ def _delegate_variable(self, value=None):
511
+ """Get or set the variable for selection state."""
512
+ if value is None:
513
+ return self._variable
514
+ self._set_signal_or_variable(value)
515
+ return None