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,650 @@
1
+ """TabView widget - a tabbed container combining Tabs with PageStack."""
2
+ from __future__ import annotations
3
+
4
+ __all__ = ['TabView']
5
+
6
+ import tkinter as tk
7
+ from typing_extensions import Unpack
8
+ from typing import Any, Callable, Literal
9
+
10
+ from bootstack.widgets._impl.primitives.frame import Frame, FrameKwargs
11
+ from bootstack.widgets._impl.composites.tabs.tabs import Tabs
12
+ from bootstack.widgets._impl.composites.tabs.tabitem import TabItem
13
+ from bootstack.events import (
14
+ ChangeMethod, ChangeReason,
15
+ TabActivateEvent, TabChangeEvent, TabDeactivateEvent, TabRef,
16
+ )
17
+ from bootstack.widgets._impl.composites.pagestack import PageStack
18
+ from bootstack.widgets.types import Master
19
+ from bootstack._core import NavigationError
20
+
21
+
22
+ class TabView(Frame):
23
+ """A tabbed container that combines Tabs with a PageStack.
24
+
25
+ TabView provides a complete tabbed interface where each tab corresponds
26
+ to a page in the page stack. Selecting a tab navigates to the associated
27
+ page.
28
+
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ master: Master = None,
34
+ orient: Literal['horizontal', 'vertical'] = 'horizontal',
35
+ show_divider: bool = None,
36
+ compound: Literal['left', 'right', 'top', 'bottom', 'center', 'none'] = 'left',
37
+ tab_width: None | int | Literal['stretch'] = None,
38
+ tab_padding: tuple = (12, 8),
39
+ tab_anchor: str = None,
40
+ enable_closing: bool | Literal['hover'] = False,
41
+ enable_adding: bool = False,
42
+ max_tabs: int | None = None,
43
+ accent: str = None,
44
+ **kwargs: Unpack[FrameKwargs]
45
+ ):
46
+ """Create a TabView widget.
47
+
48
+ Args:
49
+ master: Parent widget. If None, uses the default root window.
50
+ orient: Orientation of the tab bar. 'horizontal' places tabs above
51
+ the content, 'vertical' places tabs to the left. Default is 'horizontal'.
52
+ show_divider: Whether to show a divider between tabs and content.
53
+ compound: How to position icon relative to text in tabs.
54
+ tab_width: Width of tabs (None, integer, or 'stretch').
55
+ tab_padding: Padding for all tabs as (horizontal, vertical).
56
+ tab_anchor: Anchor for tab text/icon alignment. If None, defaults
57
+ to 'w' for vertical orientation, 'center' for horizontal.
58
+ enable_closing: Default close button visibility for all tabs.
59
+ True=always visible, False=hidden, 'hover'=visible on hover.
60
+ Can be overridden per-tab via `closable` in add().
61
+ enable_adding: If True, shows an "add" button that fires `<<TabAdd>>`.
62
+ max_tabs: Maximum tab count; the add button disables at the limit.
63
+ None (default) means no limit.
64
+ accent: Accent token for styling.
65
+ **kwargs: Additional arguments passed to Frame.
66
+ """
67
+ super().__init__(master, **kwargs)
68
+
69
+ self._orient = orient
70
+ self._accent = accent
71
+
72
+ # Create internal variable for tab selection
73
+ self._tab_variable = tk.StringVar()
74
+
75
+ # Create tabs container
76
+ self._tabs = Tabs(
77
+ self,
78
+ orient=orient,
79
+ show_divider=show_divider,
80
+ compound=compound,
81
+ tab_width=tab_width,
82
+ tab_padding=tab_padding,
83
+ tab_anchor=tab_anchor,
84
+ enable_closing=enable_closing,
85
+ enable_adding=enable_adding,
86
+ max_tabs=max_tabs,
87
+ variable=self._tab_variable,
88
+ accent=accent,
89
+ )
90
+
91
+ # Create page stack
92
+ self._page_stack = PageStack(self)
93
+
94
+ # Layout based on orientation
95
+ if orient == 'horizontal':
96
+ self._tabs.pack(side='top', fill='x')
97
+ self._page_stack.pack(side='top', fill='both', expand=True)
98
+ else:
99
+ self._tabs.pack(side='left', fill='y')
100
+ self._page_stack.pack(side='left', fill='both', expand=True)
101
+
102
+ # Track tabs by key for removal
103
+ self._tab_map: dict[str, TabItem] = {}
104
+
105
+ # Tab text tokens for locale retranslation — key → original text
106
+ self._tab_locale_tokens: dict[str, str] = {}
107
+
108
+ # Hidden tab keys (tab bar hidden, page still registered)
109
+ self._hidden_tabs: set[str] = set()
110
+
111
+ # Typed-event state — defaults represent a user click
112
+ self._prev_key: str | None = None
113
+ self._next_reason: ChangeReason = "user"
114
+ self._next_via: ChangeMethod = "click"
115
+
116
+ # Forward tab strip events to self so callers can bind at the TabView level
117
+ self._tabs.bind('<<TabAdd>>', lambda e: self.event_generate('<<TabAdd>>', when='tail'), add='+')
118
+ self._tabs.bind('<<TabClose>>', lambda e: self.event_generate('<<TabClose>>', data=getattr(e, 'data', None), when='tail'), add='+')
119
+
120
+ # Bind variable trace to navigate pages and fire typed events
121
+ self._trace_id = self._tab_variable.trace_add('write', self._on_tab_selected)
122
+
123
+ # Locale retranslation
124
+ self.winfo_toplevel().bind(
125
+ "<<LocaleChanged>>", self._on_locale_changed, add='+'
126
+ )
127
+
128
+ # Clean up trace on destroy
129
+ self.bind('<Destroy>', self._on_destroy, add='+')
130
+
131
+ # -------------------------------------------------------------------------
132
+ # Internal
133
+ # -------------------------------------------------------------------------
134
+
135
+ def _on_destroy(self, event=None):
136
+ """Clean up variable trace when widget is destroyed."""
137
+ if event.widget is not self:
138
+ return
139
+ if self._trace_id is not None:
140
+ try:
141
+ self._tab_variable.trace_remove('write', self._trace_id)
142
+ except Exception:
143
+ pass
144
+ self._trace_id = None
145
+
146
+ def _on_tab_selected(self, *args):
147
+ """Handle tab selection — navigate page stack and fire typed events."""
148
+ key = self._tab_variable.get()
149
+ if not key or key not in self._tab_map:
150
+ return
151
+
152
+ prev_key = self._prev_key
153
+ reason: ChangeReason = self._next_reason
154
+ via: ChangeMethod = self._next_via
155
+
156
+ # Reset to user-click defaults for the next change
157
+ self._next_reason = "user"
158
+ self._next_via = "click"
159
+
160
+ if key == prev_key:
161
+ return
162
+
163
+ # Navigate page stack
164
+ current_page = self._page_stack.current()
165
+ if current_page is None or current_page[0] != key:
166
+ self._page_stack.navigate(key)
167
+
168
+ # Build typed event payload
169
+ def _tab_ref(k: str) -> TabRef:
170
+ return TabRef(key=k, text=self._tab_locale_tokens.get(k, k))
171
+
172
+ current_ref = _tab_ref(key)
173
+ previous_ref = _tab_ref(prev_key) if prev_key else None
174
+
175
+ payload = TabChangeEvent(
176
+ current=current_ref,
177
+ previous=previous_ref,
178
+ reason=reason,
179
+ via=via,
180
+ )
181
+
182
+ self.event_generate('<<TabChanged>>', data=payload, when='tail')
183
+
184
+ # Per-tab lifecycle events
185
+ if prev_key and prev_key in self._tab_map:
186
+ deactivate = TabDeactivateEvent(
187
+ key=prev_key,
188
+ text=self._tab_locale_tokens.get(prev_key, prev_key),
189
+ )
190
+ self.event_generate('<<TabDeactivate>>', data=deactivate, when='tail')
191
+
192
+ activate = TabActivateEvent(
193
+ key=key,
194
+ text=self._tab_locale_tokens.get(key, key),
195
+ )
196
+ self.event_generate('<<TabActivate>>', data=activate, when='tail')
197
+
198
+ self._prev_key = key
199
+
200
+ def _on_locale_changed(self, _event=None):
201
+ """Retranslate all tab labels when the locale changes."""
202
+ try:
203
+ from bootstack.i18n import MessageCatalog
204
+ except ImportError:
205
+ return
206
+ for key, token in self._tab_locale_tokens.items():
207
+ if key in self._tab_map:
208
+ translated = MessageCatalog.translate(token)
209
+ self._tab_map[key].configure(text=translated)
210
+
211
+ def _set_next_change(self, reason: ChangeReason, via: ChangeMethod) -> None:
212
+ """Tag the next variable change with the given reason and method."""
213
+ self._next_reason = reason
214
+ self._next_via = via
215
+
216
+ # -------------------------------------------------------------------------
217
+ # Public API — tab management
218
+ # -------------------------------------------------------------------------
219
+
220
+ def add(
221
+ self,
222
+ key: str,
223
+ text: str = "",
224
+ icon: str | dict = None,
225
+ page: tk.Widget = None,
226
+ closable: bool | Literal['hover'] | None = None,
227
+ close_command: Callable = None,
228
+ command: Callable = None,
229
+ **kwargs
230
+ ) -> tk.Widget:
231
+ """Add a tab and its associated page.
232
+
233
+ Args:
234
+ key: Unique identifier for the tab/page.
235
+ text: Text to display on the tab.
236
+ icon: Icon to display on the tab.
237
+ page: The page widget. If None, creates a Frame.
238
+ closable: Close button visibility (True, False, 'hover', or None).
239
+ If None, uses the widget's `enable_closing` setting.
240
+ close_command: Callback when close button is clicked.
241
+ If not provided and closable is enabled, removes the tab/page.
242
+ command: Callback when tab is selected.
243
+ **kwargs: Additional arguments passed to page Frame (if created).
244
+
245
+ Returns:
246
+ The page widget.
247
+ """
248
+ effective_closable = closable if closable is not None else self._tabs._enable_closing
249
+
250
+ if effective_closable and close_command is None:
251
+ close_command = lambda k=key: self.forget_tab(k)
252
+
253
+ tab = self._tabs.add(
254
+ text=text,
255
+ icon=icon,
256
+ key=key,
257
+ value=key,
258
+ closable=closable,
259
+ close_command=close_command,
260
+ command=command,
261
+ )
262
+ self._tab_map[key] = tab
263
+ self._tab_locale_tokens[key] = text
264
+
265
+ page_widget = self._page_stack.add(key, page, **kwargs)
266
+
267
+ if len(self._tab_map) == 1:
268
+ self._set_next_change("api", "programmatic")
269
+ self._tab_variable.set(key)
270
+
271
+ return page_widget
272
+
273
+ def __getitem__(self, key: str) -> tk.Widget:
274
+ """Return the page widget for *key*.
275
+
276
+ Args:
277
+ key: Tab/page identifier.
278
+
279
+ Returns:
280
+ The page widget registered under *key*.
281
+
282
+ Raises:
283
+ KeyError: If no tab with the given key exists.
284
+ """
285
+ return self._page_stack.item(key)
286
+
287
+ def select(self, key: str) -> None:
288
+ """Select a tab by its key.
289
+
290
+ Args:
291
+ key: The identifier of the tab to select.
292
+
293
+ Raises:
294
+ NavigationError: If no tab with the given key exists.
295
+ """
296
+ if key not in self._tab_map:
297
+ raise NavigationError(f"No tab with key '{key}'")
298
+ if key in self._hidden_tabs:
299
+ return
300
+ self._set_next_change("api", "programmatic")
301
+ self._tab_variable.set(key)
302
+
303
+ def navigate(self, key: str, data: dict = None) -> None:
304
+ """Navigate to a tab/page with optional data.
305
+
306
+ Args:
307
+ key: The identifier of the tab/page to navigate to.
308
+ data: Optional data to pass to the page.
309
+
310
+ Raises:
311
+ NavigationError: If no tab with the given key exists.
312
+ """
313
+ if key not in self._tab_map:
314
+ raise NavigationError(f"No tab with key '{key}'")
315
+ if key in self._hidden_tabs:
316
+ return
317
+ self._set_next_change("api", "programmatic")
318
+ self._tab_variable.set(key)
319
+ if data:
320
+ self._page_stack.navigate(key, data=data)
321
+
322
+ def hide_tab(self, key: str) -> None:
323
+ """Hide a tab without removing it from the registry.
324
+
325
+ The tab disappears from the bar but remains registered. Restore it
326
+ with `show_tab()`. If the hidden tab was selected, the next
327
+ available visible tab is selected automatically.
328
+
329
+ Args:
330
+ key: The identifier of the tab to hide.
331
+
332
+ Raises:
333
+ NavigationError: If no tab with the given key exists.
334
+ """
335
+ if key not in self._tab_map:
336
+ raise NavigationError(f"No tab with key '{key}'")
337
+ if key in self._hidden_tabs:
338
+ return
339
+
340
+ self._hidden_tabs.add(key)
341
+ self._tabs.hide(key)
342
+
343
+ if self._tab_variable.get() == key:
344
+ next_key = self._next_visible_key(exclude=key)
345
+ if next_key:
346
+ self._set_next_change("hide", "programmatic")
347
+ self._tab_variable.set(next_key)
348
+ else:
349
+ self._tab_variable.set('')
350
+ self._prev_key = None
351
+
352
+ def show_tab(self, key: str) -> None:
353
+ """Restore a previously hidden tab to the bar.
354
+
355
+ Args:
356
+ key: The identifier of the tab to show.
357
+
358
+ Raises:
359
+ NavigationError: If no tab with the given key exists.
360
+ """
361
+ if key not in self._tab_map:
362
+ raise NavigationError(f"No tab with key '{key}'")
363
+ self._hidden_tabs.discard(key)
364
+ self._tabs.show(key)
365
+
366
+ def forget_tab(self, key: str) -> None:
367
+ """Remove a tab and its page entirely.
368
+
369
+ Unlike `hide_tab()`, the tab is unregistered and cannot be restored.
370
+ If the removed tab was selected, the next available tab is selected.
371
+
372
+ Args:
373
+ key: The identifier of the tab/page to remove.
374
+
375
+ Raises:
376
+ NavigationError: If no tab with the given key exists.
377
+ """
378
+ if key not in self._tab_map:
379
+ raise NavigationError(f"No tab with key '{key}'")
380
+
381
+ was_selected = self._tab_variable.get() == key
382
+
383
+ tab = self._tab_map.pop(key)
384
+ self._tab_locale_tokens.pop(key, None)
385
+ self._hidden_tabs.discard(key)
386
+ self._tabs.remove(key)
387
+ tab.destroy()
388
+
389
+ self._page_stack.remove(key)
390
+
391
+ if was_selected:
392
+ next_key = self._next_visible_key()
393
+ if next_key:
394
+ self._set_next_change("forget", "programmatic")
395
+ self._tab_variable.set(next_key)
396
+ else:
397
+ self._tab_variable.set('')
398
+ self._prev_key = None
399
+
400
+ def remove(self, key: str) -> None:
401
+ """Remove a tab and its associated page.
402
+
403
+ Alias for `forget_tab()`.
404
+
405
+ Args:
406
+ key: The identifier of the tab/page to remove.
407
+ """
408
+ self.forget_tab(key)
409
+
410
+ def _next_visible_key(self, exclude: str | None = None) -> str | None:
411
+ """Return the first visible (non-hidden) tab key, skipping *exclude*."""
412
+ for k in self._tab_map:
413
+ if k != exclude and k not in self._hidden_tabs:
414
+ return k
415
+ return None
416
+
417
+ # -------------------------------------------------------------------------
418
+ # Introspection
419
+ # -------------------------------------------------------------------------
420
+
421
+ @property
422
+ def tabs_widget(self) -> Tabs:
423
+ """Get the internal Tabs widget."""
424
+ return self._tabs
425
+
426
+ @property
427
+ def page_stack_widget(self) -> PageStack:
428
+ """Get the internal PageStack widget."""
429
+ return self._page_stack
430
+
431
+ @property
432
+ def current(self) -> str | None:
433
+ """Get the currently selected tab key."""
434
+ key = self._tab_variable.get()
435
+ return key if key else None
436
+
437
+ def page(self, key: str) -> tk.Widget:
438
+ """Get a page widget by its key.
439
+
440
+ Args:
441
+ key: The identifier of the page.
442
+
443
+ Returns:
444
+ The page widget.
445
+
446
+ Raises:
447
+ KeyError: If no page with the given key exists.
448
+ """
449
+ return self._page_stack.item(key)
450
+
451
+ def pages(self) -> tuple[tk.Widget, ...]:
452
+ """Get all page widgets.
453
+
454
+ Returns:
455
+ A tuple of all page widgets.
456
+ """
457
+ return self._page_stack.items()
458
+
459
+ def page_keys(self) -> tuple[str, ...]:
460
+ """Get all page keys.
461
+
462
+ Returns:
463
+ A tuple of all page keys.
464
+ """
465
+ return self._page_stack.keys()
466
+
467
+ def tab(self, key: str) -> TabItem:
468
+ """Get a TabItem by its key.
469
+
470
+ Args:
471
+ key: The identifier of the tab.
472
+
473
+ Returns:
474
+ The TabItem widget.
475
+
476
+ Raises:
477
+ KeyError: If no tab with the given key exists.
478
+ """
479
+ if key not in self._tab_map:
480
+ raise NavigationError(f"No tab with key '{key}'")
481
+ return self._tab_map[key]
482
+
483
+ def tabs(self) -> tuple[TabItem, ...]:
484
+ """Get all TabItem widgets.
485
+
486
+ Returns:
487
+ A tuple of all TabItem widgets in the order they were added.
488
+ """
489
+ return tuple(self._tab_map.values())
490
+
491
+ def tab_keys(self) -> tuple[str, ...]:
492
+ """Get all tab keys.
493
+
494
+ Returns:
495
+ A tuple of all tab keys in the order they were added.
496
+ """
497
+ return tuple(self._tab_map.keys())
498
+
499
+ def keys(self) -> tuple[str, ...]:
500
+ """Get all tab keys.
501
+
502
+ Alias for `tab_keys()`.
503
+
504
+ Returns:
505
+ A tuple of all tab keys in the order they were added.
506
+ """
507
+ return self.tab_keys()
508
+
509
+ def configure_tab(self, key: str, option: str | None = None, **kwargs: Unpack[FrameKwargs]):
510
+ """Configure a specific tab by its key.
511
+
512
+ Args:
513
+ key: The key of the tab to configure.
514
+ option: If provided, return the value of this option.
515
+ **kwargs: Configuration options to apply to the tab.
516
+
517
+ Returns:
518
+ If option is provided, returns the value of that option.
519
+ """
520
+ tab = self.tab(key)
521
+ if option is not None:
522
+ return tab.cget(option)
523
+ tab.configure(**kwargs)
524
+
525
+ # -------------------------------------------------------------------------
526
+ # Events
527
+ # -------------------------------------------------------------------------
528
+
529
+ def on_page_changed(self, callback: Callable) -> str:
530
+ """Register a callback for `<<PageChange>>` events.
531
+
532
+ Args:
533
+ callback: Receives `event.data` with navigation info: `page`,
534
+ `prev_page`, `nav`, `index`, `length`, `can_back`, `can_forward`.
535
+
536
+ Returns:
537
+ Bind ID — pass to `off_page_changed()` to unsubscribe.
538
+ """
539
+ return self._page_stack.on_page_changed(callback)
540
+
541
+ def off_page_changed(self, bind_id: str | None = None) -> None:
542
+ """Unsubscribe from `<<PageChange>>`.
543
+
544
+ Args:
545
+ bind_id: ID returned by `on_page_changed()`. If None, removes all.
546
+ """
547
+ self._page_stack.off_page_changed(bind_id)
548
+
549
+ def on_tab_added(self, callback: Callable) -> str:
550
+ """Register a callback for `<<TabAdd>>` events (fires when the add button is clicked).
551
+
552
+ Args:
553
+ callback: Called when the add button is clicked. `event.data` is None.
554
+
555
+ Returns:
556
+ Bind ID — pass to `off_tab_added()` to unsubscribe.
557
+ """
558
+ return self._tabs.on_tab_added(callback)
559
+
560
+ def off_tab_added(self, bind_id: str | None = None) -> None:
561
+ """Unsubscribe from `<<TabAdd>>`.
562
+
563
+ Args:
564
+ bind_id: ID returned by `on_tab_added()`. If None, removes all.
565
+ """
566
+ self._tabs.off_tab_added(bind_id)
567
+
568
+ def on_tab_changed(self, callback: Callable[[TabChangeEvent], None]) -> str:
569
+ """Register a callback for `<<TabChanged>>` events.
570
+
571
+ Args:
572
+ callback: Receives a `TabChangeEvent` payload with attributes
573
+ `current` (`TabRef`), `previous` (`TabRef | None`),
574
+ `reason` (`ChangeReason`), and `via` (`ChangeMethod`).
575
+
576
+ Returns:
577
+ Bind ID — pass to `off_tab_changed()` to unsubscribe.
578
+ """
579
+ return self.bind('<<TabChanged>>', callback, add='+')
580
+
581
+ def off_tab_changed(self, bind_id: str | None = None) -> None:
582
+ """Unsubscribe from `<<TabChanged>>`.
583
+
584
+ Args:
585
+ bind_id: ID returned by `on_tab_changed()`. If None, removes all.
586
+ """
587
+ self.unbind('<<TabChanged>>', bind_id)
588
+
589
+ def on_tab_activated(self, callback: Callable[[TabActivateEvent], None]) -> str:
590
+ """Register a callback for `<<TabActivate>>` events.
591
+
592
+ Fires when a tab becomes the selected (active) tab.
593
+
594
+ Args:
595
+ callback: Receives a `TabActivateEvent` payload with attributes
596
+ `key` and `text`.
597
+
598
+ Returns:
599
+ Bind ID — pass to `off_tab_activated()` to unsubscribe.
600
+ """
601
+ return self.bind('<<TabActivate>>', callback, add='+')
602
+
603
+ def off_tab_activated(self, bind_id: str | None = None) -> None:
604
+ """Unsubscribe from `<<TabActivate>>`.
605
+
606
+ Args:
607
+ bind_id: ID returned by `on_tab_activated()`. If None, removes all.
608
+ """
609
+ self.unbind('<<TabActivate>>', bind_id)
610
+
611
+ def on_tab_deactivated(self, callback: Callable[[TabDeactivateEvent], None]) -> str:
612
+ """Register a callback for `<<TabDeactivate>>` events.
613
+
614
+ Fires when a tab stops being the selected (active) tab.
615
+
616
+ Args:
617
+ callback: Receives a `TabDeactivateEvent` payload with attributes
618
+ `key` and `text`.
619
+
620
+ Returns:
621
+ Bind ID — pass to `off_tab_deactivated()` to unsubscribe.
622
+ """
623
+ return self.bind('<<TabDeactivate>>', callback, add='+')
624
+
625
+ def off_tab_deactivated(self, bind_id: str | None = None) -> None:
626
+ """Unsubscribe from `<<TabDeactivate>>`.
627
+
628
+ Args:
629
+ bind_id: ID returned by `on_tab_deactivated()`. If None, removes all.
630
+ """
631
+ self.unbind('<<TabDeactivate>>', bind_id)
632
+
633
+ def on_tab_closed(self, callback: Callable) -> str:
634
+ """Register a callback for `<<TabClose>>` events (fires when a tab's close button is clicked).
635
+
636
+ Args:
637
+ callback: Receives `event.data = {'value': Any}` — the closed tab's value.
638
+
639
+ Returns:
640
+ Bind ID — pass to `off_tab_closed()` to unsubscribe.
641
+ """
642
+ return self._tabs.on_tab_closed(callback)
643
+
644
+ def off_tab_closed(self, bind_id: str | None = None) -> None:
645
+ """Unsubscribe from `<<TabClose>>`.
646
+
647
+ Args:
648
+ bind_id: ID returned by `on_tab_closed()`. If None, removes all.
649
+ """
650
+ self._tabs.off_tab_closed(bind_id)
@@ -0,0 +1,26 @@
1
+ """TextArea and CodeEditor composite widgets."""
2
+
3
+ from .textarea import TextArea
4
+ from .codeeditor import CodeEditor
5
+ from .filter import EditFilter
6
+ from .core import _MultilineCore
7
+ from .sidebar import Sidebar
8
+ from .decoration import Position, RangeDecoration, LineDecoration, WidgetDecoration
9
+ from .extensions.line_numbers import LineNumbers
10
+ from .extensions.bracket_matcher import BracketMatcher
11
+ from .extensions.smart_indent import SmartIndent
12
+
13
+ __all__ = [
14
+ "TextArea",
15
+ "CodeEditor",
16
+ "EditFilter",
17
+ "_MultilineCore",
18
+ "Sidebar",
19
+ "Position",
20
+ "RangeDecoration",
21
+ "LineDecoration",
22
+ "WidgetDecoration",
23
+ "LineNumbers",
24
+ "BracketMatcher",
25
+ "SmartIndent",
26
+ ]