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,367 @@
1
+ """File-backed data source — streams a file into a SQLite working store.
2
+
3
+ `FileDataSource` reads CSV / TSV / JSON / JSONL (and, with the optional extras,
4
+ Parquet / Feather / HDF5) and ingests it — a chunk at a time, with bounded
5
+ memory — into a SQLite working store. After loading it *is* a
6
+ `SqliteDataSource`, so paging, filtering, sorting, and CRUD are all fast SQL at
7
+ flat memory, no matter how large the file.
8
+
9
+ The original file is treated as **read-only input**: edits live in the working
10
+ store, never written back. Save changes by exporting to a new file.
11
+
12
+ Working store (where the ingested data lives):
13
+ - default: a temporary on-disk SQLite file, removed on `close()` — bounded
14
+ memory even for millions of rows.
15
+ - `cache="path.db"`: a named, persistent store. Edits survive restarts, and
16
+ re-opening skips re-ingest while the cache is newer than the source file.
17
+ - `cache=":memory:"`: an in-memory store — compact, but RAM-bound.
18
+
19
+ Transformation pipeline (applied per record during ingest):
20
+ column selection / renames / default values / type conversions / per-column
21
+ transforms / row filter / row transform.
22
+
23
+ Example:
24
+ .. code-block:: python
25
+
26
+ ds = FileDataSource("data.csv")
27
+ ds.load()
28
+ ds.where(col("age") > 25)
29
+ first = ds.page(0)
30
+ ds.close() # or use `with FileDataSource(...) as ds:`
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import os
36
+ import sqlite3
37
+ import tempfile
38
+ import weakref
39
+ from dataclasses import dataclass
40
+ from pathlib import Path
41
+ from typing import Any, Callable, Dict, Iterator, List, Literal, Optional, Type
42
+
43
+ from bootstack.data.sqlite_source import SqliteDataSource
44
+ from bootstack.data.types import Record
45
+
46
+
47
+ def _cleanup_temp_store(conn: "sqlite3.Connection", path: str) -> None:
48
+ """Close the connection and remove a temporary working-store file.
49
+
50
+ Registered as a `weakref.finalize` callback so a `FileDataSource` whose
51
+ `close()` was never called still cleans up its temp file when garbage
52
+ collected or at interpreter exit — no orphaned files left behind. Takes the
53
+ connection and path directly (never the source) so it holds no strong
54
+ reference that would keep the source alive.
55
+ """
56
+ try:
57
+ conn.close()
58
+ except Exception:
59
+ pass
60
+ try:
61
+ os.remove(path)
62
+ except OSError:
63
+ pass
64
+
65
+
66
+ @dataclass
67
+ class FileSourceConfig:
68
+ """Configuration for file parsing and the per-record transformation pipeline.
69
+
70
+ Example:
71
+ .. code-block:: python
72
+
73
+ config = FileSourceConfig(
74
+ column_renames={'emp_id': 'id'},
75
+ column_types={'age': int},
76
+ )
77
+ """
78
+
79
+ file_format: Literal[
80
+ 'auto', 'csv', 'tsv', 'json', 'jsonl', 'ndjson', 'xml', 'parquet', 'feather', 'hdf5'
81
+ ] = 'auto'
82
+ """Format override; auto-detected from the extension when `'auto'`."""
83
+
84
+ encoding: str = 'utf-8'
85
+ """Character encoding for reading the file."""
86
+
87
+ delimiter: Optional[str] = None
88
+ """Field separator. `None` auto-selects `','` for CSV and a tab for TSV."""
89
+
90
+ quotechar: str = '"'
91
+ """Quote character for fields containing the delimiter."""
92
+
93
+ skip_rows: int = 0
94
+ """Number of leading rows to skip before the header (CSV/TSV)."""
95
+
96
+ header_row: Optional[int] = 0
97
+ """Row index containing column names (`None` = no header)."""
98
+
99
+ has_header: bool = True
100
+ """Whether the first row contains column names."""
101
+
102
+ json_lines: bool = False
103
+ """`True` for line-delimited JSON (JSONL/NDJSON)."""
104
+
105
+ json_records_key: Optional[str] = None
106
+ """Key whose value is the records list in a JSON object (e.g. `'data'` for
107
+ `{'data': [...]}`); `None` = a top-level array, or the object itself as one
108
+ record."""
109
+
110
+ xml_record_tag: Optional[str] = None
111
+ """Element tag that marks one record (`None` = direct children of the root)."""
112
+
113
+ hdf5_key: Optional[str] = None
114
+ """Dataset/table key to read from an HDF5 file (`None` = the first key)."""
115
+
116
+ column_renames: Optional[Dict[str, str]] = None
117
+ """Mapping from each existing column name to its replacement."""
118
+
119
+ column_types: Optional[Dict[str, Type]] = None
120
+ """Mapping from a column name to the target type to convert its values to."""
121
+
122
+ column_transforms: Optional[Dict[str, Callable[[Any], Any]]] = None
123
+ """Mapping from a column name to a transform applied to its values."""
124
+
125
+ columns_to_load: Optional[List[str]] = None
126
+ """List of columns to keep (`None` = all columns)."""
127
+
128
+ default_values: Optional[Dict[str, Any]] = None
129
+ """Mapping from a column name to a fill value for missing or null entries."""
130
+
131
+ row_filter: Optional[Callable[[Dict[str, Any]], bool]] = None
132
+ """Function `(row_dict) -> bool` to filter rows during load."""
133
+
134
+ row_transform: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None
135
+ """Function `(row_dict) -> row_dict` for row-level transforms."""
136
+
137
+ chunk_size: int = 10_000
138
+ """Rows ingested per batch (bounds memory during load)."""
139
+
140
+ progress_callback: Optional[Callable[[int], None]] = None
141
+ """Function `(count)` called after each ingested chunk with the running total
142
+ of rows loaded so far."""
143
+
144
+
145
+ class FileDataSource(SqliteDataSource):
146
+ """A `SqliteDataSource` whose data is streamed in from a file.
147
+
148
+ Reads a CSV / TSV / JSON / JSONL file (or an optional Parquet / Feather /
149
+ HDF5 file) and ingests it chunk-by-chunk into a SQLite working store, so even
150
+ a multi-million-row file loads with bounded memory. Once loaded it behaves
151
+ exactly like a `SqliteDataSource` — fast SQL paging, filtering, sorting, CRUD.
152
+
153
+ The original file is read-only input; edits live in the working store and are
154
+ never written back. Export to save changes.
155
+
156
+ Args:
157
+ filepath: Path to the data file.
158
+ config: Optional `FileSourceConfig` for parsing and transforms.
159
+ page_size: Number of records returned per page.
160
+ cache: Working store location. None (default) uses a temporary on-disk
161
+ file removed on `close()`. A path names a persistent store whose data
162
+ survives restarts (and is reused without re-ingest while it is newer
163
+ than the source). `":memory:"` keeps the store in memory.
164
+ id_field: Record field used as the stable row identity.
165
+
166
+ Attributes:
167
+ filepath: Path to the data file.
168
+ config: Active configuration.
169
+ is_loaded: Whether the file has been ingested (or a fresh cache adopted).
170
+
171
+ Example:
172
+ .. code-block:: python
173
+
174
+ with FileDataSource("people.csv") as ds:
175
+ ds.load()
176
+ ds.where(col("age") > 25)
177
+ first = ds.page(0)
178
+
179
+ Note:
180
+ - `reload()` re-ingests from the file.
181
+ - Background-thread ingest and progressive display are a planned follow-up;
182
+ `load()` is currently synchronous (but streamed, so memory stays bounded).
183
+ """
184
+
185
+ def __init__(
186
+ self,
187
+ filepath: str | Path,
188
+ config: Optional[FileSourceConfig] = None,
189
+ page_size: int = 10,
190
+ *,
191
+ cache: Optional[str] = None,
192
+ id_field: str = "id",
193
+ ):
194
+ """Configure a file-backed source and open its SQLite working store.
195
+
196
+ Raises:
197
+ FileNotFoundError: If the supplied file path cannot be found.
198
+ """
199
+ self.filepath = Path(filepath)
200
+ self.config = config or FileSourceConfig()
201
+ if not self.filepath.exists():
202
+ raise FileNotFoundError(f"File not found: {self.filepath}")
203
+
204
+ # Choose the working store.
205
+ self._is_temp = False
206
+ self._cache_path: Optional[Path] = None
207
+ if cache is None:
208
+ fd, tmp = tempfile.mkstemp(suffix=".bsdb")
209
+ os.close(fd)
210
+ store_name = tmp
211
+ self._is_temp = True
212
+ elif cache == ":memory:":
213
+ store_name = ":memory:"
214
+ else:
215
+ store_name = str(cache)
216
+ self._cache_path = Path(cache)
217
+ self._store_name = store_name
218
+
219
+ super().__init__(name=store_name, page_size=page_size, id_field=id_field)
220
+
221
+ # Safety net: a temp store is removed even if close() is never called —
222
+ # the finalizer runs on garbage collection and at interpreter exit, so no
223
+ # orphaned temp files accumulate.
224
+ self._finalizer: Optional[weakref.finalize] = None
225
+ if self._is_temp:
226
+ self._finalizer = weakref.finalize(
227
+ self, _cleanup_temp_store, self.conn, store_name
228
+ )
229
+
230
+ # Reuse a fresh persistent cache without re-ingesting.
231
+ self.is_loaded = False
232
+ if self._cache_is_fresh():
233
+ self._adopt_existing_schema()
234
+ self.is_loaded = True
235
+
236
+ # ------------------------------------------------------------------ loading
237
+
238
+ def load(self, *, force: bool = False) -> "FileDataSource":
239
+ """Ingest the file into the working store (streamed, in chunks).
240
+
241
+ Unlike other sources, a `FileDataSource` draws its records from the file
242
+ given at construction, so `load()` takes no records. It is a no-op when a
243
+ fresh persistent cache was adopted, unless `force=True`.
244
+
245
+ Args:
246
+ force: Re-ingest even if data is already present (used by `reload`).
247
+
248
+ Returns:
249
+ Self for method chaining.
250
+ """
251
+ if self.is_loaded and not force:
252
+ return self
253
+ super().load(
254
+ self._iter_records(),
255
+ chunk_size=self.config.chunk_size,
256
+ on_progress=self._on_progress,
257
+ )
258
+ self.is_loaded = True
259
+ return self
260
+
261
+ def reload(self) -> None:
262
+ """Re-ingest the file from disk, replacing the working store's contents."""
263
+ self.load(force=True)
264
+
265
+ def _iter_records(self) -> Iterator[Record]:
266
+ """Stream parsed, transformed records from the file."""
267
+ from bootstack.data.readers import read_records
268
+
269
+ for raw in read_records(self.filepath, self.config):
270
+ record = self._apply_transformations(raw)
271
+ if record is not None:
272
+ yield record
273
+
274
+ def _on_progress(self, count: int) -> None:
275
+ if self.config.progress_callback:
276
+ self.config.progress_callback(count)
277
+
278
+ # ------------------------------------------------------------------ cache
279
+
280
+ def _cache_is_fresh(self) -> bool:
281
+ """Whether a named cache already holds up-to-date data for the source."""
282
+ if self._cache_path is None:
283
+ return False
284
+ try:
285
+ if not self._cache_path.exists() or self._cache_path.stat().st_size == 0:
286
+ return False
287
+ if self._cache_path.stat().st_mtime < self.filepath.stat().st_mtime:
288
+ return False
289
+ count = self.conn.execute(f"SELECT COUNT(*) FROM {self._table}").fetchone()[0]
290
+ return count > 0
291
+ except sqlite3.Error:
292
+ return False
293
+ except OSError:
294
+ return False
295
+
296
+ def _adopt_existing_schema(self) -> None:
297
+ """Populate the column list from an existing cache table (no re-ingest)."""
298
+ try:
299
+ cur = self.conn.execute(f"PRAGMA table_info({self._table})")
300
+ self._columns = [row["name"] for row in cur.fetchall()]
301
+ except sqlite3.Error:
302
+ self._columns = []
303
+
304
+ # ------------------------------------------------------------------ lifecycle
305
+
306
+ def close(self) -> None:
307
+ """Close the working store, removing the temporary file if one was used.
308
+
309
+ Idempotent. A temporary store is also cleaned automatically if this is
310
+ never called (see the finalizer registered in `__init__`).
311
+ """
312
+ if self._finalizer is not None:
313
+ self._finalizer() # closes the connection and removes the temp file (once)
314
+ else:
315
+ super().close()
316
+
317
+ # ------------------------------------------------------------------ transforms
318
+
319
+ def _apply_transformations(self, record: Record) -> Optional[Record]:
320
+ """Apply the configured transformation pipeline to a single record.
321
+
322
+ Returns:
323
+ The transformed record, or None if filtered out.
324
+ """
325
+ # Apply row filter first.
326
+ if self.config.row_filter and not self.config.row_filter(record):
327
+ return None
328
+
329
+ # Column selection.
330
+ if self.config.columns_to_load:
331
+ record = {k: v for k, v in record.items() if k in self.config.columns_to_load}
332
+
333
+ # Column renames.
334
+ if self.config.column_renames:
335
+ for old_name, new_name in self.config.column_renames.items():
336
+ if old_name in record:
337
+ record[new_name] = record.pop(old_name)
338
+
339
+ # Default values.
340
+ if self.config.default_values:
341
+ for col, default in self.config.default_values.items():
342
+ if col not in record or record[col] is None:
343
+ record[col] = default
344
+
345
+ # Type conversions.
346
+ if self.config.column_types:
347
+ for col, col_type in self.config.column_types.items():
348
+ if col in record and record[col] is not None:
349
+ try:
350
+ record[col] = col_type(record[col])
351
+ except (ValueError, TypeError):
352
+ pass # Keep original value if conversion fails
353
+
354
+ # Per-column transforms.
355
+ if self.config.column_transforms:
356
+ for col, transform_func in self.config.column_transforms.items():
357
+ if col in record:
358
+ try:
359
+ record[col] = transform_func(record[col])
360
+ except Exception:
361
+ pass # Keep original value if transform fails
362
+
363
+ # Row-level transform.
364
+ if self.config.row_transform:
365
+ record = self.config.row_transform(record)
366
+
367
+ return record