meltygui 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 (372) hide show
  1. meltygui/__init__.py +107 -0
  2. meltygui/accounts/__init__.py +0 -0
  3. meltygui/accounts/internet_accounts.py +1355 -0
  4. meltygui/chat/__init__.py +91 -0
  5. meltygui/chat/activity.py +75 -0
  6. meltygui/chat/backends.py +36 -0
  7. meltygui/chat/chat_interface.py +732 -0
  8. meltygui/chat/chat_proxy.py +352 -0
  9. meltygui/chat/codex_proxy.py +592 -0
  10. meltygui/chat/codex_settings.py +100 -0
  11. meltygui/chat/codex_transport.py +60 -0
  12. meltygui/chat/command_parser.py +204 -0
  13. meltygui/chat/images.py +227 -0
  14. meltygui/chat/messages.py +417 -0
  15. meltygui/chat/metadata.py +139 -0
  16. meltygui/chat/writer_locks.py +64 -0
  17. meltygui/code/__init__.py +0 -0
  18. meltygui/code/basic_converters.py +533 -0
  19. meltygui/code/chain_converters.py +2111 -0
  20. meltygui/code/code_checks.py +2209 -0
  21. meltygui/code/core_syntax.py +1430 -0
  22. meltygui/code/file_converters.py +1933 -0
  23. meltygui/code/fileref.py +702 -0
  24. meltygui/code/hotswap_guard.py +144 -0
  25. meltygui/code/libcst_conversion.py +9724 -0
  26. meltygui/code/live_instrument.py +392 -0
  27. meltygui/code/live_view.py +2490 -0
  28. meltygui/code/melty_scan.py +2684 -0
  29. meltygui/code/new_codecs.py +1255 -0
  30. meltygui/code/new_converters.py +3017 -0
  31. meltygui/code/project_code.py +278 -0
  32. meltygui/code/source_context.py +63 -0
  33. meltygui/code/symbol_roster.py +1588 -0
  34. meltygui/code/syntax_check.py +34 -0
  35. meltygui/code/syntax_check_worker.py +114 -0
  36. meltygui/completion/__init__.py +0 -0
  37. meltygui/completion/fim.py +1232 -0
  38. meltygui/completion/fim_context.py +481 -0
  39. meltygui/completion/providers/__init__.py +0 -0
  40. meltygui/completion/providers/anthropic_oauth.py +446 -0
  41. meltygui/completion/providers/anthropic_requests.py +69 -0
  42. meltygui/completion/providers/claude.py +203 -0
  43. meltygui/completion/providers/claude_usage.py +499 -0
  44. meltygui/completion/providers/codex_accounts.py +180 -0
  45. meltygui/completion/providers/copilot.py +617 -0
  46. meltygui/completion/providers/oauth_popup.py +220 -0
  47. meltygui/completion/providers/ollama.py +320 -0
  48. meltygui/completion/providers/profiles.py +23 -0
  49. meltygui/core/README.md +88 -0
  50. meltygui/core/__init__.py +1 -0
  51. meltygui/core/automation/__init__.py +1 -0
  52. meltygui/core/automation/action_core.py +153 -0
  53. meltygui/core/automation/collection_action.py +45 -0
  54. meltygui/core/automation/mcp_eval.py +167 -0
  55. meltygui/core/automation/mcp_hotswap.py +107 -0
  56. meltygui/core/automation/mcp_query.py +552 -0
  57. meltygui/core/automation/mcp_server.py +657 -0
  58. meltygui/core/automation/orchestration_core.py +2636 -0
  59. meltygui/core/automation/query_core.py +124 -0
  60. meltygui/core/automation/search_core.py +26 -0
  61. meltygui/core/automation/selector_core.py +340 -0
  62. meltygui/core/automation/value_core.py +1752 -0
  63. meltygui/core/cache/__init__.py +1 -0
  64. meltygui/core/cache/cache_diagnostics.py +0 -0
  65. meltygui/core/cache/invalidation_decoration.py +153 -0
  66. meltygui/core/cache/invalidation_tracker.py +43 -0
  67. meltygui/core/cache/tile_cache.py +5968 -0
  68. meltygui/core/conversion/__init__.py +1 -0
  69. meltygui/core/conversion/bubbling.py +599 -0
  70. meltygui/core/conversion/cache_tree.py +188 -0
  71. meltygui/core/conversion/chain.py +113 -0
  72. meltygui/core/conversion/converter_register.py +145 -0
  73. meltygui/core/conversion/data_decoration.py +67 -0
  74. meltygui/core/conversion/dict_conversion.py +1815 -0
  75. meltygui/core/conversion/dict_conversion_util.py +177 -0
  76. meltygui/core/conversion/dynamic_obj.py +89 -0
  77. meltygui/core/conversion/graph_compare.py +183 -0
  78. meltygui/core/conversion/load_save_v2.py +1032 -0
  79. meltygui/core/conversion/missing_saved_class.py +39 -0
  80. meltygui/core/conversion/path_finder.py +606 -0
  81. meltygui/core/conversion/render_host.py +1083 -0
  82. meltygui/core/core_render.py +6537 -0
  83. meltygui/core/definition_hotswap.py +231 -0
  84. meltygui/core/diagnostics/__init__.py +1 -0
  85. meltygui/core/diagnostics/attribute_churn.py +38 -0
  86. meltygui/core/diagnostics/fps_counter.py +39 -0
  87. meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
  88. meltygui/core/diagnostics/inspection_core.py +169 -0
  89. meltygui/core/diagnostics/monitor_core.py +105 -0
  90. meltygui/core/diagnostics/notifications.py +706 -0
  91. meltygui/core/diagnostics/perf_trace.py +281 -0
  92. meltygui/core/diagnostics/profile_decoration.py +88 -0
  93. meltygui/core/diagnostics/resize_trace.py +62 -0
  94. meltygui/core/diagnostics/screenshot_core.py +247 -0
  95. meltygui/core/diagnostics/session_status.py +98 -0
  96. meltygui/core/diagnostics/trace_core.py +445 -0
  97. meltygui/core/files/__init__.py +1 -0
  98. meltygui/core/files/file_core.py +208 -0
  99. meltygui/core/files/file_explorer_core.py +104 -0
  100. meltygui/core/files/file_tree_core.py +198 -0
  101. meltygui/core/files/file_watch_core.py +43 -0
  102. meltygui/core/files/import_graph_core.py +43 -0
  103. meltygui/core/files/metadata_core.py +51 -0
  104. meltygui/core/graphics/__init__.py +1 -0
  105. meltygui/core/graphics/cuda_context_core.py +166 -0
  106. meltygui/core/graphics/cuda_interop_core.py +136 -0
  107. meltygui/core/graphics/cuda_kernel_core.py +91 -0
  108. meltygui/core/graphics/framebuffer_recorder.py +337 -0
  109. meltygui/core/graphics/gl_state.py +658 -0
  110. meltygui/core/graphics/lut_core.py +52 -0
  111. meltygui/core/graphics/overlay_renderer.py +984 -0
  112. meltygui/core/graphics/scene_target.py +180 -0
  113. meltygui/core/graphics/screenshot.py +439 -0
  114. meltygui/core/graphics/shader_func.py +478 -0
  115. meltygui/core/graphics/tensor_core.py +45 -0
  116. meltygui/core/graphics/text_texture.py +329 -0
  117. meltygui/core/graphics/wayland_color.py +635 -0
  118. meltygui/core/input/__init__.py +1 -0
  119. meltygui/core/input/collision.py +165 -0
  120. meltygui/core/input/drag_drop_core.py +1525 -0
  121. meltygui/core/input/hypr_left_drag.py +323 -0
  122. meltygui/core/input/input_core.py +245 -0
  123. meltygui/core/input/input_handler.py +1101 -0
  124. meltygui/core/input/mouse_cursor.py +355 -0
  125. meltygui/core/input/pynput_backend.py +1054 -0
  126. meltygui/core/input/space_mouse.py +338 -0
  127. meltygui/core/input/touchpad_backend.py +393 -0
  128. meltygui/core/input/view_selection.py +177 -0
  129. meltygui/core/layout/__init__.py +1 -0
  130. meltygui/core/layout/column_core.py +2153 -0
  131. meltygui/core/layout/cursor_core.py +161 -0
  132. meltygui/core/layout/dropdown_core.py +278 -0
  133. meltygui/core/layout/edge_constraints.py +155 -0
  134. meltygui/core/layout/grid_core.py +117 -0
  135. meltygui/core/layout/header_core.py +23 -0
  136. meltygui/core/layout/header_runtime.py +67 -0
  137. meltygui/core/layout/layout_core.py +87 -0
  138. meltygui/core/layout/tile_manager_core.py +591 -0
  139. meltygui/core/melty.py +6726 -0
  140. meltygui/core/module_map.json +896 -0
  141. meltygui/core/module_names.py +19 -0
  142. meltygui/core/rendering/__init__.py +1 -0
  143. meltygui/core/rendering/core_decoration.py +431 -0
  144. meltygui/core/rendering/core_render_helpers.py +328 -0
  145. meltygui/core/rendering/func_metadata.py +398 -0
  146. meltygui/core/rendering/mode.py +818 -0
  147. meltygui/core/rendering/mode_defaults.py +41 -0
  148. meltygui/core/rendering/modes.py +136 -0
  149. meltygui/core/rendering/parameter_core.py +1665 -0
  150. meltygui/core/rendering/render_dispatch.py +1891 -0
  151. meltygui/core/rendering/render_funcs.py +273 -0
  152. meltygui/core/rendering/shaped.py +312 -0
  153. meltygui/core/rendering/window_decoration.py +25 -0
  154. meltygui/core/runtime/__init__.py +1 -0
  155. meltygui/core/runtime/app.py +735 -0
  156. meltygui/core/runtime/app_session.py +140 -0
  157. meltygui/core/runtime/background.py +564 -0
  158. meltygui/core/runtime/extensions.py +53 -0
  159. meltygui/core/runtime/gc_manager.py +1163 -0
  160. meltygui/core/runtime/lifecycle.py +21 -0
  161. meltygui/core/runtime/paths.py +27 -0
  162. meltygui/core/runtime/settings.py +14 -0
  163. meltygui/core/runtime/singleton.py +16 -0
  164. meltygui/core/runtime/thread_safe_bool.py +24 -0
  165. meltygui/core/runtime/thread_signal.py +30 -0
  166. meltygui/core/runtime/toggles.py +3115 -0
  167. meltygui/core/services/__init__.py +1 -0
  168. meltygui/core/services/account_core.py +10 -0
  169. meltygui/core/services/chat_core.py +19 -0
  170. meltygui/core/services/claude_terminal_core.py +346 -0
  171. meltygui/core/services/terminal_core.py +458 -0
  172. meltygui/core/services/terminal_runtime.py +78 -0
  173. meltygui/core/styling/__init__.py +1 -0
  174. meltygui/core/styling/color_core.py +46 -0
  175. meltygui/core/styling/fonts.py +639 -0
  176. meltygui/core/styling/global_style.py +338 -0
  177. meltygui/core/styling/style.py +198 -0
  178. meltygui/core/styling/style_core.py +522 -0
  179. meltygui/core/styling/warm_start.py +149 -0
  180. meltygui/core/windowing/__init__.py +1 -0
  181. meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
  182. meltygui/core/windowing/backends/__init__.py +1 -0
  183. meltygui/core/windowing/backends/imgui_renderer.py +138 -0
  184. meltygui/core/windowing/backends/native_wayland.py +850 -0
  185. meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
  186. meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
  187. meltygui/core/windowing/backends/wayland_protocol.py +101 -0
  188. meltygui/core/windowing/dock_core.py +182 -0
  189. meltygui/core/windowing/frame_geometry.py +42 -0
  190. meltygui/core/windowing/geometry_feed.py +864 -0
  191. meltygui/core/windowing/glfw_utils.py +1343 -0
  192. meltygui/core/windowing/os_frame.py +1552 -0
  193. meltygui/core/windowing/surface.py +643 -0
  194. meltygui/core/windowing/titlebar.py +1560 -0
  195. meltygui/core/windowing/titlebar_buttons.py +281 -0
  196. meltygui/core/windowing/wayland_move.py +932 -0
  197. meltygui/core/windowing/window_api.py +62 -0
  198. meltygui/core/windowing/window_constants.py +339 -0
  199. meltygui/core/windowing/window_visibility.py +162 -0
  200. meltygui/debug/__init__.py +0 -0
  201. meltygui/debug/app_view_utils.py +9 -0
  202. meltygui/editor/__init__.py +0 -0
  203. meltygui/editor/bash_syntax.py +30 -0
  204. meltygui/editor/code_line_fast.py +152 -0
  205. meltygui/editor/diff.py +139 -0
  206. meltygui/editor/external_changes.py +159 -0
  207. meltygui/editor/file_header.py +41 -0
  208. meltygui/editor/live_usage.py +169 -0
  209. meltygui/editor/live_view_views.py +1803 -0
  210. meltygui/editor/pending_save.py +1351 -0
  211. meltygui/editor/roster_tints.py +547 -0
  212. meltygui/editor/source_preview.py +16 -0
  213. meltygui/editor/source_tools.py +10 -0
  214. meltygui/editor/source_ui.py +70 -0
  215. meltygui/editor/spell_check.py +77 -0
  216. meltygui/editor/text_editor.py +9076 -0
  217. meltygui/editor/usage_picker.py +538 -0
  218. meltygui/events/__init__.py +0 -0
  219. meltygui/events/example.py +118 -0
  220. meltygui/examples/__init__.py +0 -0
  221. meltygui/examples/columns_demo.py +33 -0
  222. meltygui/examples/columns_window_demo.py +60 -0
  223. meltygui/examples/context_menu_demo.py +24 -0
  224. meltygui/examples/context_menu_window_demo.py +49 -0
  225. meltygui/examples/gui_playground.py +127 -0
  226. meltygui/examples/live_view_playground.py +256 -0
  227. meltygui/examples/lora.py +43 -0
  228. meltygui/examples/lora_data.py +59 -0
  229. meltygui/examples/lora_policies.py +67 -0
  230. meltygui/examples/mode_demo.py +49 -0
  231. meltygui/examples/modifies_demo.py +119 -0
  232. meltygui/examples/scalar_policies.py +60 -0
  233. meltygui/examples/style_layouts.py +149 -0
  234. meltygui/examples/tile_manager_demo.py +74 -0
  235. meltygui/examples/tint_demo.py +160 -0
  236. meltygui/examples/tint_functions.py +56 -0
  237. meltygui/examples/trace_demo.py +88 -0
  238. meltygui/examples/two_windows.py +36 -0
  239. meltygui/files/__init__.py +0 -0
  240. meltygui/files/fast_file_explorer.py +439 -0
  241. meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
  242. meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
  243. meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
  244. meltygui/graphics/__init__.py +6 -0
  245. meltygui/graphics/base.py +85 -0
  246. meltygui/graphics/examples.py +507 -0
  247. meltygui/graphics/executor.py +520 -0
  248. meltygui/graphics/filter.py +667 -0
  249. meltygui/graphics/filter.pyi +856 -0
  250. meltygui/graphics/generate_stubs.py +22 -0
  251. meltygui/graphics/registry.py +203 -0
  252. meltygui/graphics/shader_compiler.py +155 -0
  253. meltygui/graphics/shaders.py +1302 -0
  254. meltygui/graphics/stub_generator.py +250 -0
  255. meltygui/graphics/texture_manager.py +170 -0
  256. meltygui/graphics/texture_min_max.py +295 -0
  257. meltygui/hdr_color.py +757 -0
  258. meltygui/image_load.py +308 -0
  259. meltygui/model/__init__.py +1 -0
  260. meltygui/model/account_model.py +142 -0
  261. meltygui/model/camera_model.py +159 -0
  262. meltygui/model/chat_model.py +86 -0
  263. meltygui/model/code_model.py +62 -0
  264. meltygui/model/code_proxy_model.py +876 -0
  265. meltygui/model/collection_model.py +41 -0
  266. meltygui/model/color_model.py +127 -0
  267. meltygui/model/cuda_tensor_model.py +36 -0
  268. meltygui/model/cuda_texture_model.py +149 -0
  269. meltygui/model/dropdown_model.py +137 -0
  270. meltygui/model/file_metadata_model.py +73 -0
  271. meltygui/model/file_model.py +289 -0
  272. meltygui/model/format_model.py +390 -0
  273. meltygui/model/graph_model.py +98 -0
  274. meltygui/model/icon_model.py +1024 -0
  275. meltygui/model/import_graph_model.py +468 -0
  276. meltygui/model/layout_model.py +15 -0
  277. meltygui/model/lut_model.py +266 -0
  278. meltygui/model/search_model.py +173 -0
  279. meltygui/model/tensor_model.py +402 -0
  280. meltygui/model/terminal_model.py +329 -0
  281. meltygui/model/texture_model.py +146 -0
  282. meltygui/model/tile_model.py +24 -0
  283. meltygui/model/trace_model.py +198 -0
  284. meltygui/model/trace_report_model.py +146 -0
  285. meltygui/models/__init__.py +0 -0
  286. meltygui/models/file_meta.py +613 -0
  287. meltygui/models/function_console.py +184 -0
  288. meltygui/models/orchestration.py +48 -0
  289. meltygui/pbr.py +1576 -0
  290. meltygui/png_unfilter.c +49 -0
  291. meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
  292. meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
  293. meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
  294. meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
  295. meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
  296. meltygui/resources/dejavu/LICENSE.txt +78 -0
  297. meltygui/resources/fontawesome-LICENSE.txt +121 -0
  298. meltygui/resources/fontawesome-webfont.ttf +0 -0
  299. meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
  300. meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
  301. meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
  302. meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
  303. meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
  304. meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
  305. meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
  306. meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
  307. meltygui/resources/jetbrains-weights/OFL.txt +93 -0
  308. meltygui/resources/jetbrains-weights/README.md +2 -0
  309. meltygui/state/__init__.py +0 -0
  310. meltygui/state/account_state.py +22 -0
  311. meltygui/state/animation_state.py +97 -0
  312. meltygui/state/annotation_state.py +22 -0
  313. meltygui/state/chat_state.py +36 -0
  314. meltygui/state/code_state.py +10 -0
  315. meltygui/state/core_enums.py +59 -0
  316. meltygui/state/core_markers.py +115 -0
  317. meltygui/state/core_undo.py +1075 -0
  318. meltygui/state/file_state.py +75 -0
  319. meltygui/state/graph_state.py +26 -0
  320. meltygui/state/inspection_state.py +47 -0
  321. meltygui/state/menu_state.py +10 -0
  322. meltygui/state/model_enums.py +11 -0
  323. meltygui/state/new_core_model.py +2394 -0
  324. meltygui/state/orchestration_state.py +14 -0
  325. meltygui/state/query_state.py +24 -0
  326. meltygui/state/tensor_state.py +10 -0
  327. meltygui/state/terminal_state.py +23 -0
  328. meltygui/state/trace_state.py +32 -0
  329. meltygui/state/voxel_state.py +12 -0
  330. meltygui/text_index.py +816 -0
  331. meltygui/utils/__init__.py +0 -0
  332. meltygui/utils/jump_to_code.py +344 -0
  333. meltygui/utils/pkl_inspect.py +90 -0
  334. meltygui/utils/render_utils.py +1419 -0
  335. meltygui/view/__init__.py +1 -0
  336. meltygui/view/account_view.py +524 -0
  337. meltygui/view/action_view.py +73 -0
  338. meltygui/view/chat_decoration_view.py +112 -0
  339. meltygui/view/chat_view.py +1703 -0
  340. meltygui/view/code_view.py +2677 -0
  341. meltygui/view/collection_view.py +1185 -0
  342. meltygui/view/color_view.py +824 -0
  343. meltygui/view/control_view.py +559 -0
  344. meltygui/view/decoration_view.py +439 -0
  345. meltygui/view/diagnostic_view.py +177 -0
  346. meltygui/view/dropdown_view.py +1082 -0
  347. meltygui/view/file_view.py +1599 -0
  348. meltygui/view/graph_cuda_view.py +105 -0
  349. meltygui/view/graph_view.py +882 -0
  350. meltygui/view/header_view.py +848 -0
  351. meltygui/view/input_view.py +238 -0
  352. meltygui/view/inspection_view.py +1783 -0
  353. meltygui/view/layout_view.py +596 -0
  354. meltygui/view/lut_view.py +45 -0
  355. meltygui/view/menu_view.py +212 -0
  356. meltygui/view/orchestration_view.py +658 -0
  357. meltygui/view/query_view.py +118 -0
  358. meltygui/view/search_view.py +342 -0
  359. meltygui/view/tab_view.py +258 -0
  360. meltygui/view/tensor_view.py +430 -0
  361. meltygui/view/terminal_view.py +355 -0
  362. meltygui/view/text_view.py +8031 -0
  363. meltygui/view/texture_view.py +480 -0
  364. meltygui/view/tile_view.py +38 -0
  365. meltygui/view/trace_view.py +923 -0
  366. meltygui/view/voxel_cuda_view.py +824 -0
  367. meltygui/view/voxel_view.py +1860 -0
  368. meltygui/view/window_view.py +111 -0
  369. meltygui-0.1.0.dist-info/METADATA +143 -0
  370. meltygui-0.1.0.dist-info/RECORD +372 -0
  371. meltygui-0.1.0.dist-info/WHEEL +4 -0
  372. meltygui-0.1.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,596 @@
1
+ """Layout view functions and supporting definitions."""
2
+ from meltygui.core.layout.layout_core import dock_header
3
+ from meltygui.core.melty import Melty
4
+ from meltygui.model.layout_model import Columns
5
+ from meltygui.model.layout_model import Rows
6
+ from meltygui.core.core_render import render_func
7
+ from meltygui.core.rendering.core_decoration import Core
8
+ from meltygui.state.core_undo import NavUndo
9
+ from meltygui.core.runtime.toggles import Toggles
10
+ from meltygui.core.runtime.toggles import WindowManager
11
+ import meltygui_imgui as imgui
12
+
13
+
14
+ @render_func(use_cache=True, show_bg=False, shadow=False, selectable=False,
15
+ is_default_for=Columns)
16
+ def draw_columns(input_value, column_widths=None, column_edges=None,
17
+ draw_state=None, resizable=True, child_kwargs=None, **kwargs):
18
+ """Side-by-side cells lined up with shared draggable edges.
19
+
20
+ Edges are {"x": float} dicts in window coordinates (see the edge-model
21
+ comment above). This view owns its interior edges (auto-state
22
+ ``column_edges``); when it renders as a cell of another columns view it
23
+ adopts the enclosing cell's two edge objects as its far edges
24
+ (``left_edge``/``right_edge`` kwargs, passed by reference). All edges
25
+ register on the ROOT WINDOW, which solves collisions once per frame and
26
+ drives its own frame from the direct row's far edges; this view just
27
+ queues drags and lines its cells up with its edges.
28
+ """
29
+ from meltygui.model.layout_model import Rows
30
+ from meltygui.core.layout.column_core import ColumnLayout
31
+ from meltygui.core.rendering.render_dispatch import draw_any
32
+
33
+ if child_kwargs is None:
34
+ child_kwargs = {}
35
+
36
+ if isinstance(input_value, dict):
37
+ keys = [k for k in input_value
38
+ if not (isinstance(k, str) and (k.startswith("_") or k.endswith("_")))]
39
+ elif isinstance(input_value, (list, tuple)):
40
+ keys = list(range(len(input_value)))
41
+ else:
42
+ imgui.text(f"draw_columns: no view for {type(input_value).__name__}")
43
+ return False, input_value
44
+
45
+ if not keys:
46
+ return False, input_value
47
+ n_cols = len(keys)
48
+
49
+ cols = ColumnLayout(draw_state, n_cols, column_edges=column_edges,
50
+ column_widths=column_widths,
51
+ left_edge=kwargs.get("left_edge"),
52
+ right_edge=kwargs.get("right_edge"),
53
+ resizable=resizable)
54
+
55
+ # ----- cells: lined up with their edges; nested Columns get the cell's
56
+ # edge objects by reference -----
57
+ changed = False
58
+ for idx, key in enumerate(keys):
59
+ item = input_value[key]
60
+ with cols.cell(idx) as cell_width:
61
+ item_kwargs = {"name": f"{key}", "align_header": False,
62
+ "width": cell_width} | child_kwargs
63
+ if isinstance(item, Columns):
64
+ item_kwargs |= {"left_edge": cols.edges[idx],
65
+ "right_edge": cols.edges[idx + 1]}
66
+ if isinstance(item, (Columns, Rows)):
67
+ # Row edges pass THROUGH a Columns cell: a Rows nested in
68
+ # this cell adopts the enclosing row's edges (handed to us
69
+ # by draw_rows), so its cells will collide with them.
70
+ for key in ("top_edge", "bottom_edge"):
71
+ if kwargs.get(key) is not None:
72
+ item_kwargs[key] = kwargs[key]
73
+ item_changed, out_value, cell_ds = draw_any(
74
+ item, return_extras=True, **item_kwargs)
75
+ cols.note_child(idx, cell_ds) # border follows the child's corners
76
+ if item_changed:
77
+ changed = True
78
+ if isinstance(input_value, (dict, list)):
79
+ input_value[key] = out_value
80
+
81
+ cols.finish()
82
+
83
+ return changed, input_value
84
+
85
+
86
+ @render_func(use_cache=True, show_bg=False, shadow=False, selectable=False,
87
+ is_default_for=Rows)
88
+ def draw_rows(input_value, row_heights=None, row_edges=None,
89
+ draw_state=None, resizable=True, child_kwargs=None, **kwargs):
90
+ """Stacked cells lined up with shared draggable edges — draw_columns
91
+ along y.
92
+
93
+ Edges are {"y": float} dicts in window coordinates. This view owns its
94
+ interior edges (auto-state ``row_edges``); as a cell of another rows
95
+ view it adopts the enclosing cell's two edge objects as its far edges
96
+ (``top_edge``/``bottom_edge`` kwargs, by reference); as the window's
97
+ rows view it adopts the window's top/bottom frame edges. Every cell is
98
+ pinned to its row's height AND the rows' width (a passed height alone
99
+ collapses a child's width), so a Columns cell fills its row.
100
+ """
101
+ from meltygui.model.layout_model import Columns
102
+ from meltygui.core.layout.column_core import RowLayout
103
+ from meltygui.core.rendering.render_dispatch import draw_any
104
+
105
+ if child_kwargs is None:
106
+ child_kwargs = {}
107
+
108
+ if isinstance(input_value, dict):
109
+ keys = [k for k in input_value
110
+ if not (isinstance(k, str) and (k.startswith("_") or k.endswith("_")))]
111
+ elif isinstance(input_value, (list, tuple)):
112
+ keys = list(range(len(input_value)))
113
+ else:
114
+ imgui.text(f"draw_rows: no view for {type(input_value).__name__}")
115
+ return False, input_value
116
+
117
+ if not keys:
118
+ return False, input_value
119
+ n_rows = len(keys)
120
+
121
+ rows = RowLayout(draw_state, n_rows, row_edges=row_edges,
122
+ row_heights=row_heights,
123
+ top_edge=kwargs.get("top_edge"),
124
+ bottom_edge=kwargs.get("bottom_edge"),
125
+ resizable=resizable)
126
+
127
+ changed = False
128
+ cell_width = rows.inner_width()
129
+ for idx, key in enumerate(keys):
130
+ item = input_value[key]
131
+ with rows.cell(idx) as cell_height:
132
+ item_kwargs = {"name": f"{key}", "align_header": False,
133
+ "height": cell_height,
134
+ "width": cell_width} | child_kwargs
135
+ if isinstance(item, (Rows, Columns)):
136
+ # A nested Rows adopts this row's edges; a Columns carries
137
+ # them through to any Rows in ITS cells (draw_columns).
138
+ item_kwargs |= {"top_edge": rows.edges[idx],
139
+ "bottom_edge": rows.edges[idx + 1]}
140
+ # And column edges pass THROUGH a Rows cell to the_columns.
141
+ for key in ("left_edge", "right_edge"):
142
+ if kwargs.get(key) is not None:
143
+ item_kwargs[key] = kwargs[key]
144
+ item_changed, out_value, cell_ds = draw_any(
145
+ item, return_extras=True, **item_kwargs)
146
+ rows.note_child(idx, cell_ds)
147
+ if item_changed:
148
+ changed = True
149
+ if isinstance(input_value, (dict, list)):
150
+ input_value[key] = out_value
151
+
152
+ rows.finish()
153
+
154
+ return changed, input_value
155
+
156
+
157
+ @render_func(use_cache=True, selectable=False, show_add_delete=False, show_close=False, is_tree=False,
158
+ show_name=False, searchable=True, shadow=True, with_header=dock_header)
159
+ def draw_fast_dock(input_value, draw_state, style_manager=None, hide_internal=False,
160
+ dock_tab="all", left_mouse_down=False, search_text="", **kwargs):
161
+ # input_value is Melty.registered_windows - a plain defaultdict - so there
162
+ # is no is_default_for registration: the root calls this view explicitly.
163
+ from meltygui.core.styling.fonts import Font
164
+ from meltygui.core.windowing.glfw_utils import request_render
165
+ from meltygui.view.search_view import draw_search_highlight
166
+ from meltygui.core.cache.tile_cache import add_glow
167
+ from meltygui.core.cache.tile_cache import add_shadow
168
+ from meltygui.core.cache.tile_cache import clear_glows
169
+ from meltygui.core.windowing.dock_core import _color_u32
170
+ from meltygui.core.windowing.dock_core import _draw_glyph_ink_centered
171
+ from meltygui.core.windowing.dock_core import _floor_value
172
+ from meltygui.core.windowing.dock_core import _mix
173
+ from meltygui.core.windowing.dock_core import _row_icon
174
+ from meltygui.core.windowing.dock_core import _scale_saturation
175
+ from meltygui.core.windowing.dock_core import _summon
176
+
177
+ imgui.dummy(0, 10)
178
+ # ---- styling ----
179
+ open_bg_value, open_text_value = 0.16, 1.357 # name button, window open
180
+ open_factor, open_saturation = 0.659, 1.315
181
+ closed_bg_value, closed_text_value = 0.045, 0.463 # name button, window closed
182
+ closed_factor, closed_saturation = 0.46, 0.630
183
+ target_bg_value, target_text_value = 0.103, 1.269 # summon button
184
+ target_factor, target_saturation = 0.799, 0.764
185
+ hover_bg_boost, hover_text_boost = 0.05, 1.5
186
+ text_saturation = 0.8
187
+ # Important tab's tiles mix like APP ICONS: more raw window tint (lower
188
+ # factor keeps more of the tint's source rgb) at a higher saturation and
189
+ # value than the row buttons, so each tile reads as its window's colour.
190
+ icon_bg_value, icon_bg_saturation, icon_bg_factor = 0.32, 2.2, 0.45
191
+ icon_bg_closed_value = 0.12 # dimmer value for a closed window's icon
192
+ icon_bg_closed_saturation = 0.45 # closed tiles sit muted (multiplier on the saturation)
193
+ live_tint = (0.409, 0.1, 0.1) # the bolt on live rows (All tab)
194
+ # Important tab: live shows as a little circle badge riding the tile's
195
+ # top-right corner, iOS-notification style. Fixed design colour (rule 18).
196
+ live_badge_color = (0.86, 0.24, 0.2)
197
+
198
+ # ---- icons (glyph literals so the editor renders them as a picker) ----
199
+ target_icon = f"" # summon button
200
+ live_icon = f"" # live-row bolt
201
+ recent_fallback_icon = f"" # Recently added tile of a window with no icon
202
+
203
+ # ---- geometry, authored at ui_scale 1.0 and scaled once per frame ----
204
+ # The dock draws straight to the draw list, so nothing here follows the
205
+ # scale the way a laid-out widget does - every one of these goes through
206
+ # px(), or the rows lose their 31px boxes while the labels grow out of
207
+ # them.
208
+ px = Melty.px
209
+ important = dock_tab == "important"
210
+ # [tint=(0.939, 0.453, 0.245)]
211
+ row_height = px(40) if important else px(25.0)
212
+ # [tint=(0.939, 0.453, 0.245)]
213
+ icon_box = px(40) # Important tab: SQUARE tile around the icon
214
+ if important:
215
+ # The tile is always square (icon_box a constant): a tile taller than the
216
+ # row keeps the line spacing up here instead of squashing.
217
+ row_height = max(row_height, icon_box)
218
+ row_gap = px(12) if important else px(2.0)
219
+ row_stride = row_height + row_gap
220
+ # [tint=(0.35, 0.85, 0.94)]
221
+ # Rows' left inset: the All tab keeps it for the live bolt; the Important
222
+ # tab's live state rides the tile as a badge, so its rows sit flush left.
223
+ name_x = px(3.8) if important else px(20.0)
224
+ live_badge_inset = px(3.0) # live bolt's center in from the tile corner
225
+ target_width = px(20.0)
226
+ right_pad = px(1)
227
+
228
+ # Default radius for name/rows/summon, a custom one for the icon tiles
229
+ corner = px(6)
230
+ icon_corner = px(12) # Important tab's icon tile
231
+ # [tint=(0.939, 0.453, 0.245)]
232
+ icon_nudge_x, icon_nudge_y = px(0.0), px(0.0) # optical centering of the tile's glyph
233
+ text_nudge_x, text_nudge_y = px(2.0), px(-1.0) # optical centering of glyphs
234
+ name_target_gap = px(6.0) # gap between name and summon button
235
+ name_pad_x = px(10) # left inset for icon/name text
236
+ icon_gap = px(6) # gap between icon and name
237
+ manager_row_text_x, manager_row_text_y = px(8.0), px(7.0) # "Window Manager" label offsets
238
+ # Important tab's section headings ("Recently added" / "Important"):
239
+ # a small muted label on its own line above the section's tiles.
240
+ heading_height = px(18.0)
241
+ heading_gap = px(8.0) # space between a section's last tile and the next heading
242
+ heading_value, heading_saturation = 0.62, 0.4 # theme mix of the heading text
243
+ if style_manager is None:
244
+ style_manager = Melty.style_manager
245
+ draw_list = imgui.get_window_draw_list()
246
+ origin_x, origin_y = imgui.get_cursor_screen_pos()
247
+ content_width = draw_state.content_width or (draw_state.width or 200)
248
+ # Flipped whenever this frame mutated window state (a row click, a stale
249
+ # window pruned) - it is the view's `bool` return (style guide rule 10).
250
+ changed = False
251
+ # Clear this body run's glow group: the Important tab's open tiles re-emit
252
+ # below; a run that stops emitting (tab switch, window closed) sheds its
253
+ # retained marks, while cache-served frames keep glowing.
254
+ clear_glows(draw_state)
255
+
256
+ # ---- collect + sort rows (same grouping as WINDOW_MANAGER_SORTED) ----
257
+ # [tint=(0.989, 0.17, 0.497)]
258
+ rows = []
259
+ candidates = [] # every dock-visible row before the icon filter
260
+ for key, managed_window in list(input_value.items()):
261
+ window_draw_state = managed_window.draw_state
262
+ if window_draw_state is None:
263
+ continue
264
+ name = str(window_draw_state.name)
265
+ if name in WindowManager.excluded_windows or str(key) in WindowManager.excluded_windows:
266
+ continue
267
+ if hide_internal and not window_draw_state._kwargs.get("icon", None):
268
+ continue
269
+ if (not window_draw_state.persistent and not window_draw_state.seen
270
+ and window_draw_state.closed):
271
+ Core.melty.delete_window(window_draw_state)
272
+ changed = True
273
+ continue
274
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
275
+ icon = _row_icon(name, managed_window, window_draw_state)
276
+ candidates.append((name, managed_window, window_draw_state, icon))
277
+ if dock_tab == "important" and not icon:
278
+ continue
279
+ rows.append((name, managed_window, window_draw_state, icon))
280
+ rows.sort(key=lambda row: row[0].lower())
281
+
282
+ # ---- Important tab: "Recently added" = the newest first-seen names
283
+ # (AppModel.render_windows via melty.recent_windows), newest first, icon
284
+ # or not - a window with no icon wears recent_fallback_icon on its tile.
285
+ # Rows are placed as (row_top, row) so a heading can inter between the
286
+ # sections; the regular tiles follow under their own "Important" label.
287
+ # Names that no longer resolve to a registered window are skipped.
288
+ sections = [(None, rows)]
289
+ if important:
290
+ by_name = {row[0]: row for row in candidates}
291
+ recent_rows = []
292
+ for recent_name in Melty.recent_windows(Toggles.FastDock.recently_added_count):
293
+ row = by_name.get(recent_name)
294
+ if row is not None:
295
+ name, managed_window, window_draw_state, icon = row
296
+ recent_rows.append((name, managed_window, window_draw_state,
297
+ icon or recent_fallback_icon))
298
+ if recent_rows:
299
+ sections = [("Recently added", recent_rows), ("Important", rows)]
300
+ # [tint=(0.989, 0.17, 0.497)]
301
+ placed = [] # (row_top, row) in draw order
302
+ headings = [] # (heading_top, label)
303
+ cursor_y = origin_y
304
+ for section_label, section_rows in sections:
305
+ if section_label is not None:
306
+ if placed:
307
+ cursor_y += heading_gap
308
+ headings.append((cursor_y, section_label))
309
+ cursor_y += heading_height
310
+ for row in section_rows:
311
+ placed.append((cursor_y, row))
312
+ cursor_y += row_stride
313
+
314
+ # Dummy rows set the full content height so the window scrolls normally.
315
+ # Keep the trailing row_gap as bottom padding so the last row isn't clipped.
316
+ # The scroll clamp is content_height - clipped_height, but clipped_height
317
+ # spans the WHOLE window (header included) while rows start below the
318
+ # header - boost the content height by that top inset or the last row can
319
+ # never scroll fully into view.
320
+ top_inset = (origin_y + draw_state.scroll_offset[1]) - draw_state.abs_top
321
+ imgui.dummy(content_width, max(1.0, (cursor_y - origin_y) + max(0.0, top_inset)))
322
+
323
+ mouse_x, mouse_y = imgui.get_mouse_pos()
324
+ hover_ok = draw_state._bounding_hovered
325
+ press = left_mouse_down
326
+ # [tint=(0.62, 0.47, 0.95)]
327
+ click = (press.x, press.y) if (press and hasattr(press, "x")) else None
328
+ clip = getattr(draw_state, "abs_clip_rect", None)
329
+
330
+ # ---- tab switching (All / Important) ----
331
+ # The strip is drawn by dock_header in the HEADER band, where the body's
332
+ # left_mouse_down param never reaches (the window's own header handlers
333
+ # block that area). Each tab is therefore its own on_action sub-rect over
334
+ # the screen rect the header stamped: priority_delta=4 outranks the
335
+ # header chrome (flat_button's rule), and as body on_actions they replay
336
+ # on blit-cache hits, so tabs stay interactive on cached frames too.
337
+ for tab_key, tab_rect in (getattr(draw_state, "_dock_tab_rects", None) or ()):
338
+ pressed = draw_state.on_action("left_mouse_down", view_id="dock_tab_" + tab_key,
339
+ rect=tab_rect, priority_delta=4)
340
+ if pressed is not None and dock_tab != tab_key:
341
+ draw_state.dock_tab = tab_key # auto-update: persists + invalidates
342
+ changed = True
343
+ request_render()
344
+
345
+ # ---- name loop-geometry (loop-invariant; only y varies per row) ----
346
+ target_right = origin_x + content_width - right_pad
347
+ target_left = target_right - target_width
348
+ name_left, name_right = origin_x + name_x, target_left - name_target_gap
349
+
350
+ # ---- local find-bar search ----
351
+ # The window's find UI (searchable=True) counts matches by walking
352
+ # draw_states and calling each node's _search_matcher (meltygui.draw_walk)
353
+ # - rows here aren't draw_states, so this view is its own single matcher
354
+ # node claiming one slot per matching row name, in the same ordinal order
355
+ # the row loop draws them, keeping count and current-index aligned.
356
+ from meltygui.core.melty import SearchTerm
357
+ from meltygui.model.search_model import _fuzzy_key_match
358
+ from meltygui.editor.text_editor import _scroll_into_view
359
+
360
+ names_lower = tuple(row[0].split("##")[0].lower() for _, row in placed)
361
+
362
+ def _search_matcher(term, session, names=names_lower):
363
+ query = str(term).lower()
364
+ if query:
365
+ session.claim(sum(1 for name in names if _fuzzy_key_match(query, name)))
366
+
367
+ draw_state._search_matcher = _search_matcher
368
+
369
+ # Session resolution mirrors draw_collection: a forwarded SearchTerm in
370
+ # search_text, else our own session when this view hosts the find UI.
371
+ term = search_text or (draw_state.search_text if draw_state.search_active else "")
372
+ if isinstance(term, SearchTerm):
373
+ session = term
374
+ elif draw_state.search_active and draw_state._search_session is not None:
375
+ session = draw_state._search_session
376
+ else:
377
+ session = None
378
+ query = str(term).lower() if (session is not None and term) else ""
379
+ current_local = draw_state._search_active_local if query else None
380
+ match_ordinal = 0
381
+ # Required for Ctrl+Enter (search_activate_target): the current match's
382
+ # row rect, so the injected "click the target" lands on the row instead of
383
+ # the view's center.
384
+ draw_state._search_current_rect = None
385
+
386
+ # Highlights are deferred to a second pass AFTER the row loop: the current
387
+ # match's radial glow spills over neighbouring rows, so drawn in-row it gets
388
+ # painted over by the later row's background rect. draw_search_highlight
389
+ # clips its own font out of the glow, so drawing it on top stays legible.
390
+ highlight_rects = []
391
+
392
+ # ---- section headings (Important tab with a Recently added section) ----
393
+ heading_color = _color_u32(style_manager.make_color_rgb(
394
+ 0.0, 0.0, 0.0, value=heading_value, factor=1.0, saturation_scale=heading_saturation))
395
+ for heading_top, heading_label in headings:
396
+ if clip is not None and (heading_top + heading_height < clip[1] or heading_top > clip[3]):
397
+ continue
398
+ heading_size = imgui.calc_text_size(heading_label)
399
+ draw_list.add_text(name_left + text_nudge_x,
400
+ heading_top + (heading_height - heading_size[1]) / 2.0 + text_nudge_y,
401
+ heading_color, heading_label)
402
+
403
+ for row_top, (name, managed_window, window_draw_state, icon) in placed:
404
+ row_bottom = row_top + row_height
405
+
406
+ # Match bookkeeping runs for EVERY row - clipped ones too - so the
407
+ # ordinal sequence stays aligned with the matcher's count, and the
408
+ # current match can scroll into view from off-screen.
409
+ display = name.split("##")[0]
410
+ is_match = bool(query) and _fuzzy_key_match(query, display.lower())
411
+ is_current = is_match and current_local is not None and match_ordinal == current_local
412
+ if is_match:
413
+ match_ordinal += 1
414
+ if is_current:
415
+ draw_state._search_current_rect = (name_left, row_top,
416
+ name_right - name_left, row_height)
417
+ if session.scroll_to:
418
+ _scroll_into_view(draw_state, row_top, row_bottom, center=True)
419
+
420
+ if clip is not None and (row_bottom < clip[1] or row_top > clip[3]):
421
+ continue
422
+
423
+ tint = window_draw_state.locate_tint
424
+
425
+ if name == "Window Manager":
426
+ text_color = _mix(style_manager, tint, target_text_value, 1.0, text_saturation)
427
+ draw_list.add_text(origin_x + name_x + manager_row_text_x,
428
+ row_top + manager_row_text_y, _color_u32(text_color), name)
429
+ continue
430
+
431
+ # ---- hit-testing (x is loop-invariant, hoisted above) ----
432
+ in_target = target_left <= mouse_x <= target_right and row_top <= mouse_y <= row_bottom
433
+ in_name = name_left <= mouse_x <= name_right and row_top <= mouse_y <= row_bottom
434
+
435
+ # ---- name button (open vs closed styling from the old dock) ----
436
+ is_open = not window_draw_state.closed
437
+ name_hovered = hover_ok and in_name
438
+ if is_open:
439
+ factor, bg_value, text_value, saturation = (open_factor, open_bg_value,
440
+ open_text_value, open_saturation)
441
+ else:
442
+ factor, bg_value, text_value, saturation = (closed_factor, closed_bg_value,
443
+ closed_text_value, closed_saturation)
444
+ bg_color = _mix(style_manager, tint, bg_value + (hover_bg_boost if name_hovered else 0.0),
445
+ factor, saturation)
446
+ text_color = _mix(style_manager, tint,
447
+ text_value + (hover_text_boost if name_hovered else 0.0),
448
+ factor, text_saturation)
449
+ # Legibility floor on the text's hsv value: a dark window tint would
450
+ # otherwise scale it toward black. Change the floors in Toggles.FastDock.
451
+ text_color = _floor_value(text_color, Toggles.FastDock.active_text_min_brightness if is_open
452
+ else Toggles.FastDock.inactive_text_min_brightness)
453
+ if important:
454
+ # An Important tab: the background is a rounded tile around the
455
+ # icon alone; the name rides outside it to the right, bare. Open
456
+ # tiles get a shadow AND emit light (add_glow), so the active set
457
+ # reads at a glance. The icon draws a glyph up through the
458
+ # DEJAVU_SANS_24 group (its merged FONTAWESOME_18).
459
+ box_left = name_left
460
+ box_right = box_left + icon_box
461
+ box_top = row_top + (row_height - icon_box) / 2.0
462
+ box_bottom = box_top + icon_box
463
+ # The tile always shows; a CLOSED window's is dimmer and flat -
464
+ # no shadow, no glow - so the lit, floating tiles read as open.
465
+ tile_color = _mix(style_manager, tint,
466
+ (icon_bg_value if is_open else icon_bg_closed_value)
467
+ + (hover_bg_boost if name_hovered else 0.0),
468
+ icon_bg_factor, icon_bg_saturation)
469
+
470
+ # todo take the tint and boost the saturation
471
+ glow_color = _mix(style_manager, tint,
472
+ 0.0, 0.5, 1.0)
473
+
474
+ if not is_open:
475
+ # Post-mix desaturation - the _mix saturation knob only
476
+ # touches the tint half of the blend (see _scale_saturation).
477
+ tile_color = _scale_saturation(tile_color, icon_bg_closed_saturation)
478
+ if is_open:
479
+ add_shadow((box_left, box_top, icon_box, icon_box), offset=11,
480
+ corner_radius=icon_corner, clip=clip)
481
+ # offset=0: the glow's receiver band tops out at emitter
482
+ # offset + Toggles.glow_mask_upper_offset (8 steps); at the
483
+ # default offset 2 that reached +10 - one quantized step
484
+ # under the tile's's +11 mark - so glow bled onto the
485
+ # tile box and icon. At 0 the band ends at +8 and the glow
486
+ # is cast on the WINDOW around the tile, never over it.
487
+ glow_padding = -1
488
+ add_glow((box_left + glow_padding, box_top + glow_padding,
489
+ icon_box - glow_padding*2, icon_box-glow_padding*2), glow_color[:3],
490
+ offset=0.0, corner_radius=icon_corner, clip=clip,
491
+ draw_state=draw_state)
492
+ draw_list.add_rect_filled(box_left, box_top, box_right, box_bottom,
493
+ _color_u32(tile_color), rounding=icon_corner)
494
+ if is_match:
495
+ highlight_rects.append((row_top, row_bottom, is_current))
496
+ icon_font = Melty.font_mgr.get(Font.DEJAVU_SANS_24) if Melty.font_mgr is not None else None
497
+ if icon_font is not None:
498
+ imgui.push_font(icon_font)
499
+ # The glyph's INK rect (not its line box) centers on the tile -
500
+ # see _draw_glyph_ink_centered; the ridges stay inside optical trim.
501
+ _draw_glyph_ink_centered(draw_list, icon,
502
+ box_left + icon_box / 2.0 + icon_nudge_x,
503
+ box_top + icon_box / 2.0 + icon_nudge_y,
504
+ _color_u32(text_color))
505
+ if icon_font is not None:
506
+ imgui.pop_font()
507
+ # ---- live indicator: a bare red bolt over the tile corner ----
508
+ if window_draw_state.live:
509
+ _draw_glyph_ink_centered(draw_list, live_icon,
510
+ box_right - live_badge_inset,
511
+ box_top + live_badge_inset,
512
+ _color_u32(live_badge_color, alpha=1.0))
513
+ text_x = box_right + icon_gap
514
+ else:
515
+ if is_open:
516
+ # Shadow under the open row's name button - a visible presence
517
+ # mark (rows aren't draw_states the compositor can use). Clipped
518
+ # to the dock's rect: partially scrolled rows still draw here.
519
+ add_shadow((name_left, row_top, name_right - name_left, row_height), offset=11,
520
+ corner_radius=corner, clip=clip)
521
+
522
+ # [tint=(0.444, 0.427, 0.393, 1.0), show_tint=True]
523
+ show_inactive_bg = False
524
+ if is_open or show_inactive_bg:
525
+ draw_list.add_rect_filled(name_left, row_top, name_right, row_bottom,
526
+ _color_u32(bg_color), rounding=corner)
527
+
528
+ if is_match:
529
+ highlight_rects.append((row_top, row_bottom, is_current))
530
+
531
+ text_x = name_left + name_pad_x + text_nudge_x
532
+
533
+ if icon:
534
+ icon_size = imgui.calc_text_size(icon)
535
+ draw_list.add_text(text_x, row_top + (row_height - icon_size[1]) / 2.0 + text_nudge_y,
536
+ _color_u32(text_color), icon)
537
+ text_x += icon_size[0] + icon_gap
538
+
539
+ text_size = imgui.calc_text_size(display)
540
+ draw_list.add_text(text_x, row_top + (row_height - text_size[1]) / 2.0 + text_nudge_y,
541
+ _color_u32(text_color), display)
542
+
543
+ # ---- target (summon) button - open windows, and only while the
544
+ # pointer is on the row (the row tile re-renders on the hover
545
+ # edges, so the button appears/disappears with the pointer) ----
546
+ row_hovered = hover_ok and row_top <= mouse_y <= row_bottom
547
+ if is_open and row_hovered:
548
+ target_hovered = hover_ok and in_target
549
+ target_bg_color = _mix(style_manager, tint,
550
+ target_bg_value + (hover_bg_boost if target_hovered else 0.0),
551
+ target_factor, target_saturation)
552
+ target_text_color = _mix(style_manager, tint,
553
+ target_text_value + (hover_text_boost if target_hovered else 0.0),
554
+ target_factor, text_saturation)
555
+ add_shadow((target_left, row_top, target_right - target_left, row_height),
556
+ corner_radius=corner, clip=clip)
557
+ draw_list.add_rect_filled(target_left, row_top, target_right, row_bottom,
558
+ _color_u32(target_bg_color), rounding=corner)
559
+ target_icon_size = imgui.calc_text_size(target_icon)
560
+ draw_list.add_text(target_left + (target_width - target_icon_size[0]) / 2.0 + text_nudge_x,
561
+ row_top + (row_height - target_icon_size[1]) / 2.0 + text_nudge_y,
562
+ _color_u32(target_text_color), target_icon)
563
+
564
+ # ---- live indicator (All tab's bolt; Important has the badge) ----
565
+ if window_draw_state.live and not important:
566
+ live_icon_size = imgui.calc_text_size(live_icon)
567
+ draw_list.add_text(origin_x + (name_x - live_icon_size[0]) / 2.0,
568
+ row_top + (row_height - live_icon_size[1]) / 2.0 + text_nudge_y,
569
+ _color_u32(live_tint), live_icon)
570
+
571
+ # ---- clicks ----
572
+ if click is not None and row_top <= click[1] <= row_bottom:
573
+ click_x = click[0]
574
+ if name_left <= click_x <= name_right:
575
+ was_closed = window_draw_state.closed
576
+ if window_draw_state.closed:
577
+ window_draw_state.closed = False
578
+ _summon(window_draw_state, draw_state, row_top)
579
+ else:
580
+ window_draw_state.closed = True
581
+ NavUndo.record_window(window_draw_state, was_closed, window_draw_state.closed)
582
+ Core.melty.cache.invalidate_up_by_obj(managed_window)
583
+ changed = True
584
+ request_render()
585
+ elif is_open and target_left <= click_x <= target_right:
586
+ _summon(window_draw_state, draw_state, row_top)
587
+ Core.melty.cache.invalidate_up_by_obj(managed_window)
588
+ changed = True
589
+ request_render()
590
+
591
+ # ---- search highlights (second pass, over every row's background) ----
592
+ for highlight_top, highlight_bottom, highlight_current in highlight_rects:
593
+ draw_search_highlight(draw_list, name_left, highlight_top, name_right, highlight_bottom,
594
+ current=highlight_current, rounding=corner)
595
+
596
+ return changed, input_value
@@ -0,0 +1,45 @@
1
+ """Reusable palette selection and preview functions."""
2
+ import meltygui_imgui as imgui
3
+ from meltygui.core.core_render import render_func
4
+ from meltygui.hdr_color import pack_color
5
+ from meltygui.model.lut_model import Lut
6
+ from meltygui.view.header_view import draw_header
7
+
8
+
9
+ @render_func(is_default_for="Lut", show_bg=False, is_tree=False,
10
+ header_same_line=True, with_header=draw_header)
11
+ def draw_lut(input_value: Lut = None, draw_state=None, unique=0, luts=None, **kwargs):
12
+ """Choose a palette from supplied data and preserve the Lut string type."""
13
+ from meltygui.view.dropdown_view import draw_dropdown
14
+
15
+ names = [str(k) for k in luts]
16
+ current = str(input_value) if input_value else "jet"
17
+ changed, picked = draw_dropdown(
18
+ current, collection={n: n for n in names},
19
+ name=f"lut##{unique}", show_header=False, width=140)
20
+ if changed and picked:
21
+ return True, Lut(picked)
22
+ return False, input_value
23
+
24
+
25
+ @render_func(show_bg=False, tint=(0.45, 0.55, 0.7))
26
+ def draw_luts(input_value: dict, draw_state=None):
27
+ """Preview supplied flat RGB lists; edits use the ordinary collection views."""
28
+ luts = input_value if isinstance(input_value, dict) else {}
29
+ draw_list = imgui.get_window_draw_list()
30
+ bar_w, bar_h, segs = 160.0, 13.0, 48
31
+ for name, lut in luts.items():
32
+ if not isinstance(lut, (list, tuple)) or len(lut) < 6 or len(lut) % 3:
33
+ imgui.text(f"{name}: not a flat [r,g,b,...] list")
34
+ continue
35
+ n = len(lut) // 3
36
+ x, y = imgui.get_cursor_screen_pos()
37
+ for s in range(segs):
38
+ i = min(n - 1, int(s * (n - 1) / max(1, segs - 1))) * 3
39
+ col = pack_color(lut[i], lut[i + 1], lut[i + 2], 1.0)
40
+ draw_list.add_rect_filled(x + bar_w * s / segs, y,
41
+ x + bar_w * (s + 1) / segs, y + bar_h, col)
42
+ imgui.dummy(bar_w, bar_h)
43
+ imgui.same_line()
44
+ imgui.text(f"{name} ({n})")
45
+ return False, input_value