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,217 @@
1
+ """UndoManager filter — word-level undo via character-class grouping.
2
+
3
+ Adaptation of IDLE's UndoDelegator (idlelib/undo.py). Key differences:
4
+ - Renamed UndoManager / _Command for clarity.
5
+ - Character-class merging gives word-level undo without needing Tk's
6
+ built-in undo stack (undo=False on the Text widget).
7
+ - Canonical line.col indices recorded before forwarding so undo positions
8
+ stay correct after the cursor moves.
9
+ - Adjacency check uses line/col arithmetic rather than Tk mark resolution.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ from typing import TYPE_CHECKING
14
+
15
+ from bootstack.events import InputEvent
16
+ from bootstack.widgets._impl.composites.textarea.filter import EditFilter
17
+
18
+ if TYPE_CHECKING:
19
+ from bootstack.widgets._impl.composites.textarea.core import _MultilineCore
20
+
21
+ # Character classes for merging adjacent typed characters into one undo step
22
+ _ALPHANUMERIC = frozenset(
23
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
24
+ )
25
+
26
+
27
+ def _char_class(ch: str) -> str:
28
+ if ch in _ALPHANUMERIC:
29
+ return 'alphanum'
30
+ if ch == '\n':
31
+ return 'newline'
32
+ return 'punct'
33
+
34
+
35
+ class _Command:
36
+ """One reversible edit operation."""
37
+
38
+ def __init__(self, op: str, index: str, chars: str, tags=None) -> None:
39
+ self.op = op # 'insert' or 'delete'
40
+ self.index = index # canonical line.col (e.g. "1.5")
41
+ self.chars = chars
42
+ self.tags = tags
43
+
44
+ def do(self, core: _MultilineCore) -> None:
45
+ """Re-apply this operation (used by redo)."""
46
+ if self.op == 'insert':
47
+ core.text.insert(self.index, self.chars, self.tags or ())
48
+ else:
49
+ end = core.text.index(f'{self.index} +{len(self.chars)}c')
50
+ core.text.delete(self.index, end)
51
+
52
+ def undo(self, core: _MultilineCore) -> None:
53
+ """Reverse this operation."""
54
+ if self.op == 'insert':
55
+ end = core.text.index(f'{self.index} +{len(self.chars)}c')
56
+ core.text.delete(self.index, end)
57
+ else:
58
+ core.text.insert(self.index, self.chars, self.tags or ())
59
+
60
+ def merge(self, other: '_Command') -> bool:
61
+ """Try to merge *other* into this command (in-place). Returns True if merged.
62
+
63
+ Two single-character inserts merge when they are:
64
+ - Both inserts
65
+ - Adjacent (other starts where self ends, same row)
66
+ - Same character class
67
+ - Neither contains a newline (newlines always start a new block)
68
+ """
69
+ if self.op != 'insert' or other.op != 'insert':
70
+ return False
71
+ if len(other.chars) != 1 or '\n' in other.chars:
72
+ return False
73
+ if not self.chars or '\n' in self.chars:
74
+ return False
75
+ if _char_class(self.chars[-1]) != _char_class(other.chars[0]):
76
+ return False
77
+ # Adjacency: other must start exactly where self ends (same row)
78
+ try:
79
+ row1, col1 = map(int, self.index.split('.'))
80
+ row2, col2 = map(int, other.index.split('.'))
81
+ if row1 == row2 and col1 + len(self.chars) == col2:
82
+ self.chars += other.chars
83
+ return True
84
+ except (ValueError, AttributeError):
85
+ pass
86
+ return False
87
+
88
+
89
+ class UndoManager(EditFilter):
90
+ """Undo/redo filter with character-class word-level grouping.
91
+
92
+ Intercepts every insert/delete, records canonical operations, and groups
93
+ consecutive same-class single-character inserts into one undo block.
94
+ Provides `undo()`, `redo()`, `undo_block_start()`,
95
+ `undo_block_stop()`, and `reset_undo()` via the owning core.
96
+ """
97
+
98
+ MAX_UNDO = 1000
99
+
100
+ def __init__(self) -> None:
101
+ super().__init__()
102
+ self._stack: list[list[_Command]] = []
103
+ self._redo_stack: list[list[_Command]] = []
104
+ self._current_block: list[_Command] | None = None
105
+ self._block_depth: int = 0
106
+ self._saved_index: int = 0 # stack length at last save
107
+ self._core: _MultilineCore | None = None
108
+ self._undoing: bool = False
109
+
110
+ # ── lifecycle ──────────────────────────────────────────────────────────
111
+
112
+ def attach(self, core: _MultilineCore) -> None:
113
+ self._core = core
114
+
115
+ def detach(self, core: _MultilineCore) -> None:
116
+ self._core = None
117
+
118
+ # ── filter chain ───────────────────────────────────────────────────────
119
+
120
+ def insert(self, index: str, chars: str, tags=None) -> None:
121
+ if not self._undoing:
122
+ canonical = self.index(index)
123
+ self.delegate.insert(index, chars, tags)
124
+ self._record(_Command('insert', canonical, chars, tags))
125
+ else:
126
+ self.delegate.insert(index, chars, tags)
127
+
128
+ def delete(self, index1: str, index2: str | None = None) -> None:
129
+ if not self._undoing:
130
+ canonical = self.index(index1)
131
+ end = index2 if index2 else self.index(f'{index1} +1c')
132
+ chars = self.get(canonical, end)
133
+ self.delegate.delete(index1, index2)
134
+ self._record(_Command('delete', canonical, chars))
135
+ else:
136
+ self.delegate.delete(index1, index2)
137
+
138
+ # ── recording ─────────────────────────────────────────────────────────
139
+
140
+ def _record(self, cmd: _Command) -> None:
141
+ self._redo_stack.clear()
142
+ if self._current_block is not None:
143
+ self._current_block.append(cmd)
144
+ return
145
+ # Try to merge with the last single-command block
146
+ if self._stack and len(self._stack[-1]) == 1:
147
+ if self._stack[-1][0].merge(cmd):
148
+ return
149
+ self._stack.append([cmd])
150
+ if len(self._stack) > self.MAX_UNDO:
151
+ self._stack.pop(0)
152
+ if self._saved_index > 0:
153
+ self._saved_index -= 1
154
+
155
+ # ── public API ────────────────────────────────────────────────────────
156
+
157
+ def undo_block_start(self) -> None:
158
+ """Begin a compound undo block. Nested calls are safe."""
159
+ if self._block_depth == 0:
160
+ self._current_block = []
161
+ self._block_depth += 1
162
+
163
+ def undo_block_stop(self) -> None:
164
+ """End a compound undo block. Commits when depth reaches zero."""
165
+ self._block_depth -= 1
166
+ if self._block_depth == 0 and self._current_block is not None:
167
+ if self._current_block:
168
+ self._stack.append(self._current_block)
169
+ self._current_block = None
170
+
171
+ def undo(self) -> None:
172
+ """Undo the last edit group."""
173
+ if not self._stack or self._core is None:
174
+ return
175
+ block = self._stack.pop()
176
+ self._redo_stack.append(block)
177
+ self._undoing = True
178
+ try:
179
+ for cmd in reversed(block):
180
+ cmd.undo(self._core)
181
+ finally:
182
+ self._undoing = False
183
+ self._core.text.event_generate(
184
+ '<<TextUndo>>', data=InputEvent(text=self._core.value), when='tail'
185
+ )
186
+
187
+ def redo(self) -> None:
188
+ """Redo the last undone edit group."""
189
+ if not self._redo_stack or self._core is None:
190
+ return
191
+ block = self._redo_stack.pop()
192
+ self._stack.append(block)
193
+ self._undoing = True
194
+ try:
195
+ for cmd in block:
196
+ cmd.do(self._core)
197
+ finally:
198
+ self._undoing = False
199
+ self._core.text.event_generate(
200
+ '<<TextRedo>>', data=InputEvent(text=self._core.value), when='tail'
201
+ )
202
+
203
+ def reset_undo(self) -> None:
204
+ """Clear undo/redo history and mark current state as saved."""
205
+ self._stack.clear()
206
+ self._redo_stack.clear()
207
+ self._redo_stack.clear()
208
+ self._saved_index = 0
209
+
210
+ def mark_saved(self) -> None:
211
+ """Mark the current stack depth as the saved baseline."""
212
+ self._saved_index = len(self._stack)
213
+
214
+ @property
215
+ def is_dirty(self) -> bool:
216
+ """True if the stack depth differs from the saved baseline."""
217
+ return len(self._stack) != self._saved_index
@@ -0,0 +1,57 @@
1
+ from typing import Any
2
+
3
+ from typing_extensions import Unpack
4
+
5
+ from bootstack.widgets._impl.composites.field import Field, FieldOptions
6
+ from bootstack.widgets.types import Master
7
+
8
+
9
+ class TextEntry(Field):
10
+ """A text entry field widget with label, validation, and formatting support.
11
+
12
+ TextEntry is a composite widget that combines a label, text entry input, and
13
+ message area into a single component. It provides internationalization-aware
14
+ text input with deferred parsing, validation support, and visual feedback.
15
+
16
+ The widget separates user input (display text) from the committed/parsed value,
17
+ only parsing and formatting when the user commits via `<FocusOut>` or `<Return>`.
18
+ """
19
+
20
+ def __init__(
21
+ self, master: Master = None, value: Any = None, label: str = None, message: str = None,
22
+ **kwargs: Unpack[FieldOptions]):
23
+ """Args:
24
+ master: Parent widget. If None, uses the default root window.
25
+ value: Initial value to display. Default is None.
26
+ label: Optional label text to display above the entry field.
27
+ If required=True, an asterisk (*) is automatically appended.
28
+ message: Optional message text to display below the entry field.
29
+ This is replaced by validation error messages when validation fails.
30
+
31
+ Other Parameters:
32
+ accent: Accent token for the focus ring and active border.
33
+ density: Widget density — 'default' or 'compact'.
34
+ state: Initial widget state — 'normal', 'disabled', or 'readonly'.
35
+ allow_blank: Allow empty input. Default is True.
36
+ cursor: Cursor style when hovering.
37
+ value_format: ICU format pattern for parsing/formatting.
38
+ exportselection: Export selection to clipboard.
39
+ font: Font for text display.
40
+ foreground: Text color.
41
+ initial_focus: If True, widget receives focus on creation.
42
+ justify: Text alignment ('left', 'center', 'right').
43
+ show_message: If True, displays message area. Defaults to False,
44
+ but auto-enables when `message=` or `required=True` is set.
45
+ padding: Padding around entry widget.
46
+ show: Character to mask input (e.g., '*' for passwords).
47
+ takefocus: If True, widget accepts Tab focus.
48
+ textvariable: Tkinter Variable to link with text.
49
+ See [tkinter Variables](https://docs.python.org/3/library/tkinter.html#tkinter-variables).
50
+ textsignal: Signal object for reactive updates.
51
+ width: Width in characters.
52
+ required: If True, field cannot be empty. Adds 'required'
53
+ validation rule and appends '*' to label.
54
+ xscrollcommand: Callback for horizontal scrolling.
55
+ """
56
+ super().__init__(master, value=value, label=label, message=message, **kwargs)
57
+
@@ -0,0 +1,176 @@
1
+ """Time entry field widget with dropdown list of time intervals.
2
+
3
+ Provides a specialized entry field for time input with a searchable dropdown
4
+ list of time values at specified intervals.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import datetime
10
+ from bootstack.i18n.intl_format import DateFormatSpec
11
+
12
+ from typing_extensions import Unpack
13
+
14
+ from bootstack._runtime.app import get_app_settings
15
+ from bootstack.i18n import IntlFormatter
16
+ from bootstack.widgets._impl.composites.field import FieldOptions
17
+ from bootstack.widgets._impl.composites.selectbox import SelectBox
18
+ from bootstack.widgets.types import Master
19
+
20
+
21
+ class TimeEntry(SelectBox):
22
+ """Time entry field with dropdown list of time intervals.
23
+
24
+ TimeEntry extends SelectBox to provide specialized time input with
25
+ locale-aware formatting and a searchable dropdown of time intervals.
26
+ The widget supports various time format presets and custom time patterns.
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ master: Master = None,
32
+ value: datetime.time | str = None,
33
+ value_format: DateFormatSpec = 'shortTime',
34
+ interval: int = 30,
35
+ min_time: datetime.time | str = None,
36
+ max_time: datetime.time | str = None,
37
+ label: str = None,
38
+ message: str = None,
39
+ **kwargs: Unpack[FieldOptions]
40
+ ):
41
+ """Args:
42
+ master: Parent widget. If None, uses the default root window.
43
+ value: Initial time value to display. Can be a time object or string.
44
+ Empty by default.
45
+ value_format: Time format pattern for parsing and displaying times.
46
+ Default is "shortTime" (e.g., "3:30 PM"). Common formats:
47
+ "shortTime" (3:30 PM), "longTime" (3:30:45 PM PST),
48
+ "mediumTime" (3:30:45 PM), "HH:mm" (15:30), "h:mm a" (3:30 PM).
49
+ interval: Time interval in minutes for dropdown items (e.g., 15, 30, 60).
50
+ Default is 30 minutes.
51
+ min_time: Minimum time value for the dropdown list. Can be a time object
52
+ or string (e.g., "09:00" or "9:00 AM"). Default is midnight (00:00).
53
+ max_time: Maximum time value for the dropdown list. Can be a time object
54
+ or string (e.g., "17:00" or "5:00 PM"). Default is 11:59 PM (23:59).
55
+ label: Optional label text to display above the entry field.
56
+ If required=True, an asterisk (*) is automatically appended.
57
+ message: Optional message text to display below the entry field.
58
+ Used for hints or help text. Replaced by validation errors when
59
+ validation fails.
60
+
61
+ Other Parameters:
62
+ locale: Locale identifier for time formatting (e.g., 'en_US').
63
+ required: If True, field cannot be empty.
64
+ accent: Accent token for the focus ring and active border.
65
+ allow_blank: Allow empty input.
66
+ width: Width in characters.
67
+ textvariable: Tkinter Variable to link with text.
68
+ See [tkinter Variables](https://docs.python.org/3/library/tkinter.html#tkinter-variables).
69
+ textsignal: Signal object for reactive updates.
70
+ """
71
+ self._interval = interval
72
+ self._value_format = value_format
73
+ self._locale = kwargs.get('locale') or get_app_settings().locale
74
+
75
+ # Parse and store time range
76
+ self._min_time = self._parse_time(min_time) if min_time else datetime.time(0, 0)
77
+ self._max_time = self._parse_time(max_time) if max_time else datetime.time(23, 59)
78
+
79
+ # An empty field stays empty — consistent with the other field widgets,
80
+ # so `required=` meaningfully demands input (was: defaulted to the current
81
+ # time, which silently pre-filled and defeated `required`). Pass an
82
+ # explicit `value=` to seed a starting time.
83
+ if value is None or value == "":
84
+ formatted_value = ""
85
+ else:
86
+ # Normalize value to a time object using our own parser (avoids
87
+ # dateparser mis-reading "08:30" as "August 30"), then format it with
88
+ # the same IntlFormatter used to build items so the strings compare
89
+ # equal.
90
+ formatter = IntlFormatter(locale=self._locale)
91
+ time_val = self._parse_time(value) if isinstance(value, str) else value
92
+ formatted_value = formatter.format(time_val, value_format)
93
+
94
+ # Generate time intervals for dropdown
95
+ items = self._generate_time_intervals()
96
+
97
+ # Initialize SelectBox with time-specific configuration
98
+ super().__init__(
99
+ master=master,
100
+ value=formatted_value,
101
+ value_format=value_format,
102
+ items=items,
103
+ allow_custom_values=True,
104
+ enable_search=True,
105
+ dropdown_button_icon='clock',
106
+ message=message,
107
+ label=label,
108
+ **kwargs
109
+ )
110
+
111
+ def _parse_time(self, time_value: datetime.time | str) -> datetime.time:
112
+ """Parse time value from various input formats.
113
+
114
+ Args:
115
+ time_value: Time value as time object or string
116
+
117
+ Returns:
118
+ Parsed time object, or midnight (00:00) if parsing fails
119
+ """
120
+ if isinstance(time_value, datetime.time):
121
+ return time_value
122
+
123
+ if isinstance(time_value, str):
124
+ # Try 24-hour format (HH:MM)
125
+ try:
126
+ dt = datetime.datetime.strptime(time_value, '%H:%M')
127
+ return dt.time()
128
+ except ValueError:
129
+ pass
130
+
131
+ # Try 12-hour format (h:MM AM/PM)
132
+ try:
133
+ dt = datetime.datetime.strptime(time_value, '%I:%M %p')
134
+ return dt.time()
135
+ except ValueError:
136
+ pass
137
+
138
+ # Fallback to midnight
139
+ return datetime.time(0, 0)
140
+
141
+ def _generate_time_intervals(self) -> list[str]:
142
+ """Generate list of formatted time strings at specified intervals.
143
+
144
+ Creates a list of time values from min_time to max_time at the specified
145
+ interval, formatted according to value_format. Uses IntlFormatter for
146
+ consistent locale-aware formatting with Field.
147
+
148
+ Returns:
149
+ List of formatted time strings
150
+ """
151
+ times = []
152
+ formatter = IntlFormatter(locale=self._locale)
153
+
154
+ # Convert time objects to datetime for iteration
155
+ current = datetime.datetime.combine(datetime.date.today(), self._min_time)
156
+ end = datetime.datetime.combine(datetime.date.today(), self._max_time)
157
+
158
+ # Handle midnight crossing (e.g., min_time=22:00, max_time=02:00)
159
+ if end < current:
160
+ end += datetime.timedelta(days=1)
161
+
162
+ # Generate intervals
163
+ while current <= end:
164
+ # Format using IntlFormatter (consistent with TextEntryPart)
165
+ # Note: Pass datetime (not time) as IntlFormatter expects it for Babel
166
+ try:
167
+ formatted_time = formatter.format(current, self._value_format)
168
+ except:
169
+ # Fallback to 24-hour format if formatting fails
170
+ formatted_time = current.strftime('%H:%M')
171
+
172
+ times.append(formatted_time)
173
+ current += datetime.timedelta(minutes=self._interval)
174
+
175
+ return times
176
+