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.
- meltygui/__init__.py +107 -0
- meltygui/accounts/__init__.py +0 -0
- meltygui/accounts/internet_accounts.py +1355 -0
- meltygui/chat/__init__.py +91 -0
- meltygui/chat/activity.py +75 -0
- meltygui/chat/backends.py +36 -0
- meltygui/chat/chat_interface.py +732 -0
- meltygui/chat/chat_proxy.py +352 -0
- meltygui/chat/codex_proxy.py +592 -0
- meltygui/chat/codex_settings.py +100 -0
- meltygui/chat/codex_transport.py +60 -0
- meltygui/chat/command_parser.py +204 -0
- meltygui/chat/images.py +227 -0
- meltygui/chat/messages.py +417 -0
- meltygui/chat/metadata.py +139 -0
- meltygui/chat/writer_locks.py +64 -0
- meltygui/code/__init__.py +0 -0
- meltygui/code/basic_converters.py +533 -0
- meltygui/code/chain_converters.py +2111 -0
- meltygui/code/code_checks.py +2209 -0
- meltygui/code/core_syntax.py +1430 -0
- meltygui/code/file_converters.py +1933 -0
- meltygui/code/fileref.py +702 -0
- meltygui/code/hotswap_guard.py +144 -0
- meltygui/code/libcst_conversion.py +9724 -0
- meltygui/code/live_instrument.py +392 -0
- meltygui/code/live_view.py +2490 -0
- meltygui/code/melty_scan.py +2684 -0
- meltygui/code/new_codecs.py +1255 -0
- meltygui/code/new_converters.py +3017 -0
- meltygui/code/project_code.py +278 -0
- meltygui/code/source_context.py +63 -0
- meltygui/code/symbol_roster.py +1588 -0
- meltygui/code/syntax_check.py +34 -0
- meltygui/code/syntax_check_worker.py +114 -0
- meltygui/completion/__init__.py +0 -0
- meltygui/completion/fim.py +1232 -0
- meltygui/completion/fim_context.py +481 -0
- meltygui/completion/providers/__init__.py +0 -0
- meltygui/completion/providers/anthropic_oauth.py +446 -0
- meltygui/completion/providers/anthropic_requests.py +69 -0
- meltygui/completion/providers/claude.py +203 -0
- meltygui/completion/providers/claude_usage.py +499 -0
- meltygui/completion/providers/codex_accounts.py +180 -0
- meltygui/completion/providers/copilot.py +617 -0
- meltygui/completion/providers/oauth_popup.py +220 -0
- meltygui/completion/providers/ollama.py +320 -0
- meltygui/completion/providers/profiles.py +23 -0
- meltygui/core/README.md +88 -0
- meltygui/core/__init__.py +1 -0
- meltygui/core/automation/__init__.py +1 -0
- meltygui/core/automation/action_core.py +153 -0
- meltygui/core/automation/collection_action.py +45 -0
- meltygui/core/automation/mcp_eval.py +167 -0
- meltygui/core/automation/mcp_hotswap.py +107 -0
- meltygui/core/automation/mcp_query.py +552 -0
- meltygui/core/automation/mcp_server.py +657 -0
- meltygui/core/automation/orchestration_core.py +2636 -0
- meltygui/core/automation/query_core.py +124 -0
- meltygui/core/automation/search_core.py +26 -0
- meltygui/core/automation/selector_core.py +340 -0
- meltygui/core/automation/value_core.py +1752 -0
- meltygui/core/cache/__init__.py +1 -0
- meltygui/core/cache/cache_diagnostics.py +0 -0
- meltygui/core/cache/invalidation_decoration.py +153 -0
- meltygui/core/cache/invalidation_tracker.py +43 -0
- meltygui/core/cache/tile_cache.py +5968 -0
- meltygui/core/conversion/__init__.py +1 -0
- meltygui/core/conversion/bubbling.py +599 -0
- meltygui/core/conversion/cache_tree.py +188 -0
- meltygui/core/conversion/chain.py +113 -0
- meltygui/core/conversion/converter_register.py +145 -0
- meltygui/core/conversion/data_decoration.py +67 -0
- meltygui/core/conversion/dict_conversion.py +1815 -0
- meltygui/core/conversion/dict_conversion_util.py +177 -0
- meltygui/core/conversion/dynamic_obj.py +89 -0
- meltygui/core/conversion/graph_compare.py +183 -0
- meltygui/core/conversion/load_save_v2.py +1032 -0
- meltygui/core/conversion/missing_saved_class.py +39 -0
- meltygui/core/conversion/path_finder.py +606 -0
- meltygui/core/conversion/render_host.py +1083 -0
- meltygui/core/core_render.py +6537 -0
- meltygui/core/definition_hotswap.py +231 -0
- meltygui/core/diagnostics/__init__.py +1 -0
- meltygui/core/diagnostics/attribute_churn.py +38 -0
- meltygui/core/diagnostics/fps_counter.py +39 -0
- meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
- meltygui/core/diagnostics/inspection_core.py +169 -0
- meltygui/core/diagnostics/monitor_core.py +105 -0
- meltygui/core/diagnostics/notifications.py +706 -0
- meltygui/core/diagnostics/perf_trace.py +281 -0
- meltygui/core/diagnostics/profile_decoration.py +88 -0
- meltygui/core/diagnostics/resize_trace.py +62 -0
- meltygui/core/diagnostics/screenshot_core.py +247 -0
- meltygui/core/diagnostics/session_status.py +98 -0
- meltygui/core/diagnostics/trace_core.py +445 -0
- meltygui/core/files/__init__.py +1 -0
- meltygui/core/files/file_core.py +208 -0
- meltygui/core/files/file_explorer_core.py +104 -0
- meltygui/core/files/file_tree_core.py +198 -0
- meltygui/core/files/file_watch_core.py +43 -0
- meltygui/core/files/import_graph_core.py +43 -0
- meltygui/core/files/metadata_core.py +51 -0
- meltygui/core/graphics/__init__.py +1 -0
- meltygui/core/graphics/cuda_context_core.py +166 -0
- meltygui/core/graphics/cuda_interop_core.py +136 -0
- meltygui/core/graphics/cuda_kernel_core.py +91 -0
- meltygui/core/graphics/framebuffer_recorder.py +337 -0
- meltygui/core/graphics/gl_state.py +658 -0
- meltygui/core/graphics/lut_core.py +52 -0
- meltygui/core/graphics/overlay_renderer.py +984 -0
- meltygui/core/graphics/scene_target.py +180 -0
- meltygui/core/graphics/screenshot.py +439 -0
- meltygui/core/graphics/shader_func.py +478 -0
- meltygui/core/graphics/tensor_core.py +45 -0
- meltygui/core/graphics/text_texture.py +329 -0
- meltygui/core/graphics/wayland_color.py +635 -0
- meltygui/core/input/__init__.py +1 -0
- meltygui/core/input/collision.py +165 -0
- meltygui/core/input/drag_drop_core.py +1525 -0
- meltygui/core/input/hypr_left_drag.py +323 -0
- meltygui/core/input/input_core.py +245 -0
- meltygui/core/input/input_handler.py +1101 -0
- meltygui/core/input/mouse_cursor.py +355 -0
- meltygui/core/input/pynput_backend.py +1054 -0
- meltygui/core/input/space_mouse.py +338 -0
- meltygui/core/input/touchpad_backend.py +393 -0
- meltygui/core/input/view_selection.py +177 -0
- meltygui/core/layout/__init__.py +1 -0
- meltygui/core/layout/column_core.py +2153 -0
- meltygui/core/layout/cursor_core.py +161 -0
- meltygui/core/layout/dropdown_core.py +278 -0
- meltygui/core/layout/edge_constraints.py +155 -0
- meltygui/core/layout/grid_core.py +117 -0
- meltygui/core/layout/header_core.py +23 -0
- meltygui/core/layout/header_runtime.py +67 -0
- meltygui/core/layout/layout_core.py +87 -0
- meltygui/core/layout/tile_manager_core.py +591 -0
- meltygui/core/melty.py +6726 -0
- meltygui/core/module_map.json +896 -0
- meltygui/core/module_names.py +19 -0
- meltygui/core/rendering/__init__.py +1 -0
- meltygui/core/rendering/core_decoration.py +431 -0
- meltygui/core/rendering/core_render_helpers.py +328 -0
- meltygui/core/rendering/func_metadata.py +398 -0
- meltygui/core/rendering/mode.py +818 -0
- meltygui/core/rendering/mode_defaults.py +41 -0
- meltygui/core/rendering/modes.py +136 -0
- meltygui/core/rendering/parameter_core.py +1665 -0
- meltygui/core/rendering/render_dispatch.py +1891 -0
- meltygui/core/rendering/render_funcs.py +273 -0
- meltygui/core/rendering/shaped.py +312 -0
- meltygui/core/rendering/window_decoration.py +25 -0
- meltygui/core/runtime/__init__.py +1 -0
- meltygui/core/runtime/app.py +735 -0
- meltygui/core/runtime/app_session.py +140 -0
- meltygui/core/runtime/background.py +564 -0
- meltygui/core/runtime/extensions.py +53 -0
- meltygui/core/runtime/gc_manager.py +1163 -0
- meltygui/core/runtime/lifecycle.py +21 -0
- meltygui/core/runtime/paths.py +27 -0
- meltygui/core/runtime/settings.py +14 -0
- meltygui/core/runtime/singleton.py +16 -0
- meltygui/core/runtime/thread_safe_bool.py +24 -0
- meltygui/core/runtime/thread_signal.py +30 -0
- meltygui/core/runtime/toggles.py +3115 -0
- meltygui/core/services/__init__.py +1 -0
- meltygui/core/services/account_core.py +10 -0
- meltygui/core/services/chat_core.py +19 -0
- meltygui/core/services/claude_terminal_core.py +346 -0
- meltygui/core/services/terminal_core.py +458 -0
- meltygui/core/services/terminal_runtime.py +78 -0
- meltygui/core/styling/__init__.py +1 -0
- meltygui/core/styling/color_core.py +46 -0
- meltygui/core/styling/fonts.py +639 -0
- meltygui/core/styling/global_style.py +338 -0
- meltygui/core/styling/style.py +198 -0
- meltygui/core/styling/style_core.py +522 -0
- meltygui/core/styling/warm_start.py +149 -0
- meltygui/core/windowing/__init__.py +1 -0
- meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
- meltygui/core/windowing/backends/__init__.py +1 -0
- meltygui/core/windowing/backends/imgui_renderer.py +138 -0
- meltygui/core/windowing/backends/native_wayland.py +850 -0
- meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
- meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
- meltygui/core/windowing/backends/wayland_protocol.py +101 -0
- meltygui/core/windowing/dock_core.py +182 -0
- meltygui/core/windowing/frame_geometry.py +42 -0
- meltygui/core/windowing/geometry_feed.py +864 -0
- meltygui/core/windowing/glfw_utils.py +1343 -0
- meltygui/core/windowing/os_frame.py +1552 -0
- meltygui/core/windowing/surface.py +643 -0
- meltygui/core/windowing/titlebar.py +1560 -0
- meltygui/core/windowing/titlebar_buttons.py +281 -0
- meltygui/core/windowing/wayland_move.py +932 -0
- meltygui/core/windowing/window_api.py +62 -0
- meltygui/core/windowing/window_constants.py +339 -0
- meltygui/core/windowing/window_visibility.py +162 -0
- meltygui/debug/__init__.py +0 -0
- meltygui/debug/app_view_utils.py +9 -0
- meltygui/editor/__init__.py +0 -0
- meltygui/editor/bash_syntax.py +30 -0
- meltygui/editor/code_line_fast.py +152 -0
- meltygui/editor/diff.py +139 -0
- meltygui/editor/external_changes.py +159 -0
- meltygui/editor/file_header.py +41 -0
- meltygui/editor/live_usage.py +169 -0
- meltygui/editor/live_view_views.py +1803 -0
- meltygui/editor/pending_save.py +1351 -0
- meltygui/editor/roster_tints.py +547 -0
- meltygui/editor/source_preview.py +16 -0
- meltygui/editor/source_tools.py +10 -0
- meltygui/editor/source_ui.py +70 -0
- meltygui/editor/spell_check.py +77 -0
- meltygui/editor/text_editor.py +9076 -0
- meltygui/editor/usage_picker.py +538 -0
- meltygui/events/__init__.py +0 -0
- meltygui/events/example.py +118 -0
- meltygui/examples/__init__.py +0 -0
- meltygui/examples/columns_demo.py +33 -0
- meltygui/examples/columns_window_demo.py +60 -0
- meltygui/examples/context_menu_demo.py +24 -0
- meltygui/examples/context_menu_window_demo.py +49 -0
- meltygui/examples/gui_playground.py +127 -0
- meltygui/examples/live_view_playground.py +256 -0
- meltygui/examples/lora.py +43 -0
- meltygui/examples/lora_data.py +59 -0
- meltygui/examples/lora_policies.py +67 -0
- meltygui/examples/mode_demo.py +49 -0
- meltygui/examples/modifies_demo.py +119 -0
- meltygui/examples/scalar_policies.py +60 -0
- meltygui/examples/style_layouts.py +149 -0
- meltygui/examples/tile_manager_demo.py +74 -0
- meltygui/examples/tint_demo.py +160 -0
- meltygui/examples/tint_functions.py +56 -0
- meltygui/examples/trace_demo.py +88 -0
- meltygui/examples/two_windows.py +36 -0
- meltygui/files/__init__.py +0 -0
- meltygui/files/fast_file_explorer.py +439 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
- meltygui/graphics/__init__.py +6 -0
- meltygui/graphics/base.py +85 -0
- meltygui/graphics/examples.py +507 -0
- meltygui/graphics/executor.py +520 -0
- meltygui/graphics/filter.py +667 -0
- meltygui/graphics/filter.pyi +856 -0
- meltygui/graphics/generate_stubs.py +22 -0
- meltygui/graphics/registry.py +203 -0
- meltygui/graphics/shader_compiler.py +155 -0
- meltygui/graphics/shaders.py +1302 -0
- meltygui/graphics/stub_generator.py +250 -0
- meltygui/graphics/texture_manager.py +170 -0
- meltygui/graphics/texture_min_max.py +295 -0
- meltygui/hdr_color.py +757 -0
- meltygui/image_load.py +308 -0
- meltygui/model/__init__.py +1 -0
- meltygui/model/account_model.py +142 -0
- meltygui/model/camera_model.py +159 -0
- meltygui/model/chat_model.py +86 -0
- meltygui/model/code_model.py +62 -0
- meltygui/model/code_proxy_model.py +876 -0
- meltygui/model/collection_model.py +41 -0
- meltygui/model/color_model.py +127 -0
- meltygui/model/cuda_tensor_model.py +36 -0
- meltygui/model/cuda_texture_model.py +149 -0
- meltygui/model/dropdown_model.py +137 -0
- meltygui/model/file_metadata_model.py +73 -0
- meltygui/model/file_model.py +289 -0
- meltygui/model/format_model.py +390 -0
- meltygui/model/graph_model.py +98 -0
- meltygui/model/icon_model.py +1024 -0
- meltygui/model/import_graph_model.py +468 -0
- meltygui/model/layout_model.py +15 -0
- meltygui/model/lut_model.py +266 -0
- meltygui/model/search_model.py +173 -0
- meltygui/model/tensor_model.py +402 -0
- meltygui/model/terminal_model.py +329 -0
- meltygui/model/texture_model.py +146 -0
- meltygui/model/tile_model.py +24 -0
- meltygui/model/trace_model.py +198 -0
- meltygui/model/trace_report_model.py +146 -0
- meltygui/models/__init__.py +0 -0
- meltygui/models/file_meta.py +613 -0
- meltygui/models/function_console.py +184 -0
- meltygui/models/orchestration.py +48 -0
- meltygui/pbr.py +1576 -0
- meltygui/png_unfilter.c +49 -0
- meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
- meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
- meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
- meltygui/resources/dejavu/LICENSE.txt +78 -0
- meltygui/resources/fontawesome-LICENSE.txt +121 -0
- meltygui/resources/fontawesome-webfont.ttf +0 -0
- meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
- meltygui/resources/jetbrains-weights/OFL.txt +93 -0
- meltygui/resources/jetbrains-weights/README.md +2 -0
- meltygui/state/__init__.py +0 -0
- meltygui/state/account_state.py +22 -0
- meltygui/state/animation_state.py +97 -0
- meltygui/state/annotation_state.py +22 -0
- meltygui/state/chat_state.py +36 -0
- meltygui/state/code_state.py +10 -0
- meltygui/state/core_enums.py +59 -0
- meltygui/state/core_markers.py +115 -0
- meltygui/state/core_undo.py +1075 -0
- meltygui/state/file_state.py +75 -0
- meltygui/state/graph_state.py +26 -0
- meltygui/state/inspection_state.py +47 -0
- meltygui/state/menu_state.py +10 -0
- meltygui/state/model_enums.py +11 -0
- meltygui/state/new_core_model.py +2394 -0
- meltygui/state/orchestration_state.py +14 -0
- meltygui/state/query_state.py +24 -0
- meltygui/state/tensor_state.py +10 -0
- meltygui/state/terminal_state.py +23 -0
- meltygui/state/trace_state.py +32 -0
- meltygui/state/voxel_state.py +12 -0
- meltygui/text_index.py +816 -0
- meltygui/utils/__init__.py +0 -0
- meltygui/utils/jump_to_code.py +344 -0
- meltygui/utils/pkl_inspect.py +90 -0
- meltygui/utils/render_utils.py +1419 -0
- meltygui/view/__init__.py +1 -0
- meltygui/view/account_view.py +524 -0
- meltygui/view/action_view.py +73 -0
- meltygui/view/chat_decoration_view.py +112 -0
- meltygui/view/chat_view.py +1703 -0
- meltygui/view/code_view.py +2677 -0
- meltygui/view/collection_view.py +1185 -0
- meltygui/view/color_view.py +824 -0
- meltygui/view/control_view.py +559 -0
- meltygui/view/decoration_view.py +439 -0
- meltygui/view/diagnostic_view.py +177 -0
- meltygui/view/dropdown_view.py +1082 -0
- meltygui/view/file_view.py +1599 -0
- meltygui/view/graph_cuda_view.py +105 -0
- meltygui/view/graph_view.py +882 -0
- meltygui/view/header_view.py +848 -0
- meltygui/view/input_view.py +238 -0
- meltygui/view/inspection_view.py +1783 -0
- meltygui/view/layout_view.py +596 -0
- meltygui/view/lut_view.py +45 -0
- meltygui/view/menu_view.py +212 -0
- meltygui/view/orchestration_view.py +658 -0
- meltygui/view/query_view.py +118 -0
- meltygui/view/search_view.py +342 -0
- meltygui/view/tab_view.py +258 -0
- meltygui/view/tensor_view.py +430 -0
- meltygui/view/terminal_view.py +355 -0
- meltygui/view/text_view.py +8031 -0
- meltygui/view/texture_view.py +480 -0
- meltygui/view/tile_view.py +38 -0
- meltygui/view/trace_view.py +923 -0
- meltygui/view/voxel_cuda_view.py +824 -0
- meltygui/view/voxel_view.py +1860 -0
- meltygui/view/window_view.py +111 -0
- meltygui-0.1.0.dist-info/METADATA +143 -0
- meltygui-0.1.0.dist-info/RECORD +372 -0
- meltygui-0.1.0.dist-info/WHEEL +4 -0
- meltygui-0.1.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
"""OS-window surfaces: one meltygui frame per GLFW window (app.py's @glfw_window).
|
|
2
|
+
|
|
3
|
+
A Surface owns everything that belongs to ONE OS window: the GLFW window and
|
|
4
|
+
its GL context (all surfaces share one GL share group, rooted at app.py's
|
|
5
|
+
hidden owner window), an imgui context sharing the owner's font atlas, the
|
|
6
|
+
SplitOverlayRenderer, the GLFW-callback input backend and its InputHandler,
|
|
7
|
+
the tile cache (FBO-bound), the fp16 scene target, and the frameless-window
|
|
8
|
+
chrome state (titlebar.py, os_frame.py).
|
|
9
|
+
|
|
10
|
+
meltygui was written for one window: that per-window state lives in `Melty`
|
|
11
|
+
class attributes and in module globals of titlebar / os_frame / scene_target
|
|
12
|
+
/ wayland_move / wayland_color / mouse_cursor. Surfaces render strictly one
|
|
13
|
+
after another, so instead of threading a window through thousands of call
|
|
14
|
+
sites, `activate()` swaps that state: the previously active surface stashes
|
|
15
|
+
the live values, this one restores its own. Everything rebuilt inside
|
|
16
|
+
Melty.begin_frame / end_frame (layers, stacks, events) needs no swapping —
|
|
17
|
+
see the catalog in app.py's history. The studio, which never creates a
|
|
18
|
+
Surface, is untouched: with no surface active nothing is ever swapped.
|
|
19
|
+
|
|
20
|
+
Frame (``frame()``): the studio's root-window ritual (new_core_view.
|
|
21
|
+
draw_melty_windows) with the meltygui title bar, window controls, corner cut
|
|
22
|
+
and drop shadow when the window is frameless (Toggles.Melty.
|
|
23
|
+
wayland_show_frame off), and the body drawn inline into the root. Views the
|
|
24
|
+
body draws at root level fill the window (Melty.root_fill, consumed by the
|
|
25
|
+
render wrapper) so a one-view body needs no width/height.
|
|
26
|
+
"""
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import copy
|
|
30
|
+
import time
|
|
31
|
+
from collections import defaultdict
|
|
32
|
+
from types import SimpleNamespace
|
|
33
|
+
|
|
34
|
+
import meltygui.core.windowing.window_api as glfw
|
|
35
|
+
import meltygui_imgui as imgui
|
|
36
|
+
import OpenGL.GL as gl
|
|
37
|
+
|
|
38
|
+
from meltygui.core.melty import Melty
|
|
39
|
+
import meltygui.core.input.mouse_cursor as mouse_cursor
|
|
40
|
+
import meltygui.core.windowing.os_frame as os_frame
|
|
41
|
+
import meltygui.core.graphics.scene_target as scene_target
|
|
42
|
+
import meltygui.core.windowing.titlebar as titlebar
|
|
43
|
+
import meltygui.core.windowing.titlebar_buttons as titlebar_buttons
|
|
44
|
+
import meltygui.core.graphics.wayland_color as wayland_color
|
|
45
|
+
import meltygui.core.windowing.wayland_move as wayland_move
|
|
46
|
+
import meltygui.core.input.input_handler as input_handler
|
|
47
|
+
from meltygui.core.runtime.toggles import Toggles
|
|
48
|
+
from meltygui.core.diagnostics.fps_counter import FpsCounter
|
|
49
|
+
import meltygui.utils.render_utils as views
|
|
50
|
+
from meltygui.core.windowing.glfw_utils import request_render
|
|
51
|
+
|
|
52
|
+
# --- Per-window state -----------------------------------------------------------
|
|
53
|
+
# Melty class attributes that persist across frames and belong to one window.
|
|
54
|
+
MELTY_ATTRS = ('glfw_window', 'vis', 'framebuffer_size', 'frame_inset', 'frame_origin',
|
|
55
|
+
'dynamic_style_gl',
|
|
56
|
+
'root_draw_states', 'root_draw_states_by_layer', 'cache', 'backend',
|
|
57
|
+
'event_handler', 'frame_key_events', 'hovered_ds', 'imgui_main_window_hovered',
|
|
58
|
+
'glfw_close_requested', 'any_window_hovered', 'any_window_hovered_pending',
|
|
59
|
+
'filter', # its executor's VAO is per GL context
|
|
60
|
+
# The hit-test tree and the focus slots are per OS window: boxes
|
|
61
|
+
# are window-local coordinates, so one shared tree had window B's
|
|
62
|
+
# views answering window A's hit test at the same local point (and
|
|
63
|
+
# a closed window's boxes out of sync - the 09-12 two-roots repro).
|
|
64
|
+
'_bvh', '_bvh_id_to_ds', '_bvh_gen', '_bvh_query_cache', '_bvh_query_cache_gen',
|
|
65
|
+
'bvh_hover_ids', 'focused_ds', 'text_focused_ds', 'popover_focused_ds')
|
|
66
|
+
# Module globals that assume one window (titlebar/os_frame resolve "the window"
|
|
67
|
+
# through Melty.glfw_window, so with the swap they see this window's).
|
|
68
|
+
MODULE_GLOBALS = {
|
|
69
|
+
titlebar: ('_pressed_button', '_wm_move_started', '_rdrag', '_corner_gl',
|
|
70
|
+
'_input_rect_applied', '_geometry_applied', '_self_resize',
|
|
71
|
+
'_pending_surface_size', '_pending_surface_offset', '_frame_surface_offset',
|
|
72
|
+
'_pending_surface_fit', '_pending_surface_wait', '_last_surface_size'),
|
|
73
|
+
os_frame: ('_STATE',),
|
|
74
|
+
scene_target: ('_STATE',),
|
|
75
|
+
wayland_move: ('_STATE',),
|
|
76
|
+
wayland_color: ('_STATE',),
|
|
77
|
+
mouse_cursor: ('_applied', '_applied_immediate', '_requested', '_external'),
|
|
78
|
+
input_handler: ('_BUTTON_PROBE',),
|
|
79
|
+
}
|
|
80
|
+
_DEFAULTS = None # Default module globals, captured before the first surface
|
|
81
|
+
_DEBUG = bool(__import__('os').environ.get('MELTY_DEBUG'))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _capture_defaults():
|
|
85
|
+
global _DEFAULTS
|
|
86
|
+
if _DEFAULTS is not None:
|
|
87
|
+
return
|
|
88
|
+
_DEFAULTS = {}
|
|
89
|
+
for mod, names in MODULE_GLOBALS.items():
|
|
90
|
+
for name in names:
|
|
91
|
+
value = mod.__dict__[name]
|
|
92
|
+
try:
|
|
93
|
+
_DEFAULTS[(mod, name)] = copy.deepcopy(value)
|
|
94
|
+
except Exception:
|
|
95
|
+
_DEFAULTS[(mod, name)] = value
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
ROOT_FLAGS = (imgui.WINDOW_NO_BACKGROUND | imgui.WINDOW_NO_TITLE_BAR | imgui.WINDOW_NO_RESIZE
|
|
99
|
+
| imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_SCROLLBAR | imgui.WINDOW_NO_NAV_FOCUS
|
|
100
|
+
| imgui.WINDOW_NO_BRING_TO_FRONT_ON_FOCUS | imgui.WINDOW_NO_NAV_INPUTS
|
|
101
|
+
| imgui.WINDOW_NO_NAV | imgui.WINDOW_NO_COLLAPSE | imgui.WINDOW_NO_SAVED_SETTINGS
|
|
102
|
+
| imgui.WINDOW_NO_SCROLL_WITH_MOUSE)
|
|
103
|
+
ROOT_MAX_BG_VALUE = 0.130 # draw_main's max_bg_value: the ground's brightness cap
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class Surface:
|
|
107
|
+
all: list = [] # open surfaces in creation order
|
|
108
|
+
active = None
|
|
109
|
+
owner_window = None # app.py's hidden share-group root
|
|
110
|
+
owner_context = None # the imgui context that owns the font atlas
|
|
111
|
+
app_id = 'meltygui'
|
|
112
|
+
session = None # the AppSession app.py loaded (app_session.py), if any
|
|
113
|
+
|
|
114
|
+
def __init__(self, name, body, *, width=1280, height=800, parent=None,
|
|
115
|
+
draw_state=None, tint=None, on_close=None):
|
|
116
|
+
"""``name`` is the window's name AND its OS title (a child's `##suffix`
|
|
117
|
+
is stripped from the title); ``width`` / ``height`` the content size —
|
|
118
|
+
the universal meltygui names, as on `@window` and every view.
|
|
119
|
+
``body(surface)`` draws the window's content inline into the root.
|
|
120
|
+
``parent``: the Surface this one is a child of (glfw_window=True calls);
|
|
121
|
+
``draw_state``: the child's root draw_state (its window_pos/size are the
|
|
122
|
+
parent-relative geometry, exactly as for a closable meltygui window);
|
|
123
|
+
``tint``: the root ground's tint (None: Toggles.Melty.app_root_tint)."""
|
|
124
|
+
_capture_defaults()
|
|
125
|
+
self.name, self.body, self.parent, self.draw_state = name, body, parent, draw_state
|
|
126
|
+
self.tint = tint
|
|
127
|
+
self.on_close = on_close # asked when an OS close lands; False keeps the window
|
|
128
|
+
width, height = int(width), int(height)
|
|
129
|
+
self.title = _unique_title(name.split('##')[0])
|
|
130
|
+
self.request = None # the melty.surface_children entry of a child
|
|
131
|
+
self.toplevel = None # xdg_toplevel proxy (wayland_move), for set_parent
|
|
132
|
+
self.parent_linked = False
|
|
133
|
+
self.await_ack = False # a rect sent, the parent yet to show it
|
|
134
|
+
self.sent_at = 0.0
|
|
135
|
+
self.seen_rect = None # the feed's rect last tick (present_children)
|
|
136
|
+
self.stale = False # closing because its call stopped, not an OS close
|
|
137
|
+
self.children: list = []
|
|
138
|
+
self.frames = 0
|
|
139
|
+
self.fps_counter = FpsCounter()
|
|
140
|
+
self.closed = False
|
|
141
|
+
self.last_sent_rect = None
|
|
142
|
+
self.chrome = not titlebar.wants_os_decoration()
|
|
143
|
+
transparent = titlebar.wants_transparent_framebuffer()
|
|
144
|
+
self.transparent = transparent
|
|
145
|
+
|
|
146
|
+
glfw.default_window_hints()
|
|
147
|
+
glfw.window_hint(glfw.VISIBLE, True)
|
|
148
|
+
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 4)
|
|
149
|
+
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
|
|
150
|
+
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
|
|
151
|
+
glfw.window_hint(glfw.TRANSPARENT_FRAMEBUFFER, transparent)
|
|
152
|
+
glfw.window_hint(glfw.ALPHA_BITS, 8 if transparent else 0)
|
|
153
|
+
glfw.window_hint(glfw.DECORATED, glfw.FALSE if self.chrome else glfw.TRUE)
|
|
154
|
+
glfw.window_hint(glfw.STENCIL_BITS, 8)
|
|
155
|
+
glfw.window_hint(glfw.RESIZABLE, True)
|
|
156
|
+
try:
|
|
157
|
+
glfw.window_hint_string(glfw.WAYLAND_APP_ID, Surface.app_id)
|
|
158
|
+
glfw.window_hint_string(glfw.X11_CLASS_NAME, Surface.app_id)
|
|
159
|
+
except Exception:
|
|
160
|
+
pass
|
|
161
|
+
share = Surface.owner_window
|
|
162
|
+
self.window = glfw.create_window(width, height, self.title, None, share)
|
|
163
|
+
if not self.window:
|
|
164
|
+
raise SystemExit('glfw.create_window failed')
|
|
165
|
+
|
|
166
|
+
# Fresh per-window state: module defaults + a clean Melty set.
|
|
167
|
+
self._mods = {key: _fresh(value) for key, value in _DEFAULTS.items()}
|
|
168
|
+
self.root_windows = {} # live collision roots belonging to this surface window
|
|
169
|
+
# vis.root is what Melty reads and stores off (draw_state_registry,
|
|
170
|
+
# surface_windows): the user's persisted session (app_session.py)
|
|
171
|
+
# when app.py loaded one, else a stand-in around the shared registry.
|
|
172
|
+
root = Surface.session
|
|
173
|
+
if root is None:
|
|
174
|
+
registry = Melty.draw_state_registry
|
|
175
|
+
if registry is None:
|
|
176
|
+
registry = Melty.draw_state_registry = {}
|
|
177
|
+
root = SimpleNamespace(draw_state_registry=registry)
|
|
178
|
+
self._melty = dict(
|
|
179
|
+
glfw_window=self.window,
|
|
180
|
+
vis=SimpleNamespace(root=root,
|
|
181
|
+
window=self.window, tracked_keys=[], first_frame_keys=set(),
|
|
182
|
+
fa_font=None),
|
|
183
|
+
framebuffer_size=None, frame_inset=0, frame_origin=(0, 0),
|
|
184
|
+
root_draw_states=defaultdict(list), root_draw_states_by_layer=defaultdict(list),
|
|
185
|
+
cache=None, backend=None, dynamic_style_gl=None, event_handler=input_handler.InputHandler(),
|
|
186
|
+
frame_key_events=[], hovered_ds=None, imgui_main_window_hovered=False,
|
|
187
|
+
glfw_close_requested=False, any_window_hovered=False, any_window_hovered_pending=False,
|
|
188
|
+
filter=type(Melty.filter)(),
|
|
189
|
+
_bvh=type(Melty._bvh)(), _bvh_id_to_ds={}, _bvh_gen=0, _bvh_query_cache={},
|
|
190
|
+
_bvh_query_cache_gen=-1, bvh_hover_ids=set(),
|
|
191
|
+
focused_ds=None, text_focused_ds=None, popover_focused_ds=None)
|
|
192
|
+
|
|
193
|
+
# imgui context sharing the owner's atlas (the owner context OWNS it,
|
|
194
|
+
# so this surface can die without taking the atlas with it).
|
|
195
|
+
owner_io = _owner_io()
|
|
196
|
+
self.ctx = imgui.create_context(shared_font_atlas=owner_io.fonts)
|
|
197
|
+
# create_context only makes the new context current when NONE is
|
|
198
|
+
# (the owner's is), so select ours before touching its io.
|
|
199
|
+
imgui.set_current_context(self.ctx)
|
|
200
|
+
imgui.get_io().ini_file_name = None
|
|
201
|
+
# The renderer uploads the atlas in its constructor, BEFORE it stamps
|
|
202
|
+
# display_size: the FreeType hinting pass (fonts.hint_atlas) frames on
|
|
203
|
+
# the current context and dies on the (-1, -1) default.
|
|
204
|
+
imgui.get_io().display_size = (float(width), float(height))
|
|
205
|
+
self.activate()
|
|
206
|
+
titlebar.note_surface_size(self.window)
|
|
207
|
+
glfw.swap_interval(1)
|
|
208
|
+
from meltygui.core.graphics.overlay_renderer import SplitOverlayRenderer
|
|
209
|
+
from meltygui.core.cache.tile_cache import TileCacheMasked
|
|
210
|
+
self.impl = SplitOverlayRenderer(self.window)
|
|
211
|
+
Melty.init_input_backend(self.window)
|
|
212
|
+
Melty.cache = TileCacheMasked()
|
|
213
|
+
# The blit cache starts OFF and resize() switches it on after the
|
|
214
|
+
# second frame, the studio's schedule (lsd_studio: frame_count 2).
|
|
215
|
+
# Off, nothing is ever tile-cached: every view, every RenderHost
|
|
216
|
+
# body and draw_text reverts on every frame and freeze_resize
|
|
217
|
+
# never engages (09-13, melty_code_editor: 18 ms hover frames).
|
|
218
|
+
# The popover artifacts that kept it off (row tiles blank, a black
|
|
219
|
+
# block under a context menu, 09-12) were captures of a menu
|
|
220
|
+
# opened off the display's right edge served after it was pinned
|
|
221
|
+
# back in - TileCacheMasked._fill_unfilled now re-renders those.
|
|
222
|
+
Melty.cache.enabled = False
|
|
223
|
+
if glfw.get_platform() == glfw.PLATFORM_WAYLAND:
|
|
224
|
+
# The tag is the window's stable identity for the compositor
|
|
225
|
+
# (remembered floating state key on app id + tag), the name
|
|
226
|
+
# before its uniqueness suffix.
|
|
227
|
+
wayland_move.attach(self.window, tag=name.split('##')[0])
|
|
228
|
+
self.toplevel = wayland_move.toplevel_proxy()
|
|
229
|
+
self.hdr_tagged = wayland_color.attach_window(self.window)
|
|
230
|
+
else:
|
|
231
|
+
self.hdr_tagged = False
|
|
232
|
+
if parent is not None:
|
|
233
|
+
parent.children.append(self)
|
|
234
|
+
if self.toplevel and parent.toplevel:
|
|
235
|
+
self.parent_linked = wayland_move.set_parent(self.toplevel, parent.toplevel)
|
|
236
|
+
self._hook_callbacks()
|
|
237
|
+
Surface.all.append(self)
|
|
238
|
+
# Install the shadow margin BEFORE the first buffer is committed. A
|
|
239
|
+
# deferred IPC resize overwrites Hyprland's restored content size and
|
|
240
|
+
# first maps a content box smaller by twice the inset. Grow only the
|
|
241
|
+
# native surface here; subsequent compositor configures become the
|
|
242
|
+
# authority for the content size (on_surface_resized adds the margin).
|
|
243
|
+
inset = int(titlebar.window_inset()) if self.chrome else 0
|
|
244
|
+
if inset > 0:
|
|
245
|
+
content_width, content_height = glfw.get_window_size(self.window)
|
|
246
|
+
titlebar.set_surface_size(self.window, content_width + 2 * inset,
|
|
247
|
+
content_height + 2 * inset, box=False)
|
|
248
|
+
titlebar.sync_window_geometry(self.window)
|
|
249
|
+
|
|
250
|
+
# --- activation ----------------------------------------------------------------
|
|
251
|
+
def _stash(self):
|
|
252
|
+
for name in MELTY_ATTRS:
|
|
253
|
+
self._melty[name] = getattr(Melty, name)
|
|
254
|
+
for (mod, name) in self._mods:
|
|
255
|
+
self._mods[(mod, name)] = mod.__dict__[name]
|
|
256
|
+
|
|
257
|
+
def _restore(self):
|
|
258
|
+
# A surface created before the dynamic-style code was hotswapped in
|
|
259
|
+
# must not inherit another surface's GL resources.
|
|
260
|
+
self._melty.setdefault('dynamic_style_gl', None)
|
|
261
|
+
for name, value in self._melty.items():
|
|
262
|
+
setattr(Melty, name, value)
|
|
263
|
+
for (mod, name), value in self._mods.items():
|
|
264
|
+
mod.__dict__[name] = value
|
|
265
|
+
|
|
266
|
+
def activate(self):
|
|
267
|
+
"""Make this surface's window, GL context, imgui context and per-window
|
|
268
|
+
state current. Cheap when already active."""
|
|
269
|
+
if Surface.active is not self:
|
|
270
|
+
if _DEBUG:
|
|
271
|
+
print(f'[surface] activate {getattr(Surface.active, "title", None)!r} -> {self.title!r}', flush=True)
|
|
272
|
+
if Surface.active is not None:
|
|
273
|
+
Surface.active._stash()
|
|
274
|
+
Surface.active = self
|
|
275
|
+
self._restore()
|
|
276
|
+
glfw.make_context_current(self.window)
|
|
277
|
+
imgui.set_current_context(self.ctx)
|
|
278
|
+
|
|
279
|
+
def _hook_callbacks(self):
|
|
280
|
+
"""GLFW callbacks fire inside poll_events — and inside any GLFW call
|
|
281
|
+
that dispatches Wayland events (set_window_size, a roundtrip), i.e.
|
|
282
|
+
possibly while ANOTHER surface is mid-frame. The input backend
|
|
283
|
+
appends to Melty.frame_key_events and titlebar's resize hook reads
|
|
284
|
+
its globals, so chain a wrapper in front of every callback that
|
|
285
|
+
loads OUR per-window state for the duration of the callback and
|
|
286
|
+
puts the interrupted surface's back afterwards. Never the GL or
|
|
287
|
+
imgui context: the interrupted frame keeps rendering into its own."""
|
|
288
|
+
def hook(setter, extra=None):
|
|
289
|
+
prev = None
|
|
290
|
+
|
|
291
|
+
def wrapper(window, *args):
|
|
292
|
+
interrupted = Surface.active
|
|
293
|
+
if interrupted is not self:
|
|
294
|
+
if interrupted is not None:
|
|
295
|
+
interrupted._stash()
|
|
296
|
+
Surface.active = self
|
|
297
|
+
self._restore()
|
|
298
|
+
try:
|
|
299
|
+
if extra is not None:
|
|
300
|
+
extra(*args)
|
|
301
|
+
if prev is not None:
|
|
302
|
+
prev(window, *args)
|
|
303
|
+
finally:
|
|
304
|
+
if interrupted is not self:
|
|
305
|
+
self._stash()
|
|
306
|
+
Surface.active = interrupted
|
|
307
|
+
if interrupted is not None:
|
|
308
|
+
interrupted._restore()
|
|
309
|
+
prev = setter(self.window, wrapper)
|
|
310
|
+
|
|
311
|
+
hook(glfw.set_key_callback)
|
|
312
|
+
hook(glfw.set_char_callback)
|
|
313
|
+
hook(glfw.set_mouse_button_callback)
|
|
314
|
+
hook(glfw.set_scroll_callback)
|
|
315
|
+
hook(glfw.set_cursor_pos_callback)
|
|
316
|
+
hook(glfw.set_cursor_enter_callback)
|
|
317
|
+
hook(glfw.set_window_size_callback)
|
|
318
|
+
hook(glfw.set_window_focus_callback, self._on_focus)
|
|
319
|
+
hook(glfw.set_framebuffer_size_callback, self._on_framebuffer_size)
|
|
320
|
+
hook(glfw.set_window_close_callback, lambda *_: request_render())
|
|
321
|
+
|
|
322
|
+
def _on_focus(self, focused):
|
|
323
|
+
request_render()
|
|
324
|
+
if focused:
|
|
325
|
+
# The user may have changed the desktop's title-bar buttons in
|
|
326
|
+
# the meantime (titlebar_buttons: rate-limited re-read).
|
|
327
|
+
titlebar_buttons.refresh()
|
|
328
|
+
# ... or flipped left-drag move in the desktop's Settings.
|
|
329
|
+
import meltygui.core.input.hypr_left_drag as hypr_left_drag
|
|
330
|
+
hypr_left_drag.refresh()
|
|
331
|
+
|
|
332
|
+
def _on_framebuffer_size(self, width, height):
|
|
333
|
+
request_render()
|
|
334
|
+
applied = titlebar.on_surface_resized(self.window, width, height)
|
|
335
|
+
if _DEBUG:
|
|
336
|
+
print(f'[surface {self.title}] framebuffer {width}x{height} '
|
|
337
|
+
f'self_resize={titlebar._self_resize} regrow={applied}', flush=True)
|
|
338
|
+
|
|
339
|
+
# --- render ---------------------------------------------------------------------
|
|
340
|
+
def _draw_render_hosts(self):
|
|
341
|
+
"""The studio's draw_main draws every registered RenderHost once a
|
|
342
|
+
frame (RenderHost.draw_all); that is what loads a code_file_io host's
|
|
343
|
+
file, reparses it after an edit and auto-saves it. A meltygui app's body
|
|
344
|
+
is a plain draw function, so the surface runs the pump for it, after
|
|
345
|
+
the body and inside the imgui frame. Hosts are process-global: once
|
|
346
|
+
per app tick, however many surfaces draw in it."""
|
|
347
|
+
if not Melty.render_hosts or Melty.render_hosts_tick == Melty.app_tick:
|
|
348
|
+
return
|
|
349
|
+
Melty.render_hosts_tick = Melty.app_tick
|
|
350
|
+
from meltygui.core.conversion.render_host import RenderHost
|
|
351
|
+
RenderHost.draw_all()
|
|
352
|
+
|
|
353
|
+
def frame(self):
|
|
354
|
+
self.activate()
|
|
355
|
+
frame_started = self.fps_counter.frame_started()
|
|
356
|
+
if glfw.window_should_close(self.window):
|
|
357
|
+
# The window's ×, the compositor's close and set_window_should_close
|
|
358
|
+
# all land here; a window's on_close may decline (and hide instead).
|
|
359
|
+
if self.on_close is not None and self.on_close(self) is False:
|
|
360
|
+
glfw.set_window_should_close(self.window, False)
|
|
361
|
+
else:
|
|
362
|
+
self.closed = True
|
|
363
|
+
return
|
|
364
|
+
titlebar.apply_pending_surface_size(self.window)
|
|
365
|
+
self.impl.process_inputs()
|
|
366
|
+
fb_w, fb_h = glfw.get_framebuffer_size(self.window)
|
|
367
|
+
if fb_w == 0 or fb_h == 0:
|
|
368
|
+
return
|
|
369
|
+
scene_target.begin(fb_w, fb_h)
|
|
370
|
+
transparent = titlebar._frame_transparent(self.window)
|
|
371
|
+
gl.glClearColor(0.0, 0.0, 0.0, 0.0 if transparent else 1.0)
|
|
372
|
+
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT | gl.GL_STENCIL_BUFFER_BIT)
|
|
373
|
+
Melty.apply_ui_scale(self.impl)
|
|
374
|
+
views.new_frame()
|
|
375
|
+
io = imgui.get_io()
|
|
376
|
+
disp_w, disp_h = io.display_size
|
|
377
|
+
imgui.set_next_window_position(0, 0)
|
|
378
|
+
imgui.set_next_window_size(disp_w, disp_h)
|
|
379
|
+
imgui.push_style_var(imgui.STYLE_WINDOW_PADDING, (0.0, 0.0))
|
|
380
|
+
imgui.push_style_var(imgui.STYLE_WINDOW_BORDERSIZE, 0.0)
|
|
381
|
+
views.begin('main##window_melty', closable=False, flags=ROOT_FLAGS)
|
|
382
|
+
imgui.pop_style_var(2)
|
|
383
|
+
# meltygui only routes pointer events to apps while the root imgui
|
|
384
|
+
# window is hovered (draw_state.hover_eligible).
|
|
385
|
+
Melty.imgui_main_window_hovered = imgui.is_window_hovered()
|
|
386
|
+
Melty.begin_frame()
|
|
387
|
+
# Standalone apps do not run the studio's draw_main. Commit source
|
|
388
|
+
# edits deferred while a picker/slider held the pointer here too.
|
|
389
|
+
from meltygui.core.rendering.parameter_core import flush_deferred_writes
|
|
390
|
+
flush_deferred_writes()
|
|
391
|
+
if self.chrome:
|
|
392
|
+
os_frame.begin_frame()
|
|
393
|
+
titlebar.poll_os_window_drag()
|
|
394
|
+
os_frame.solve()
|
|
395
|
+
imgui.set_cursor_screen_pos((0, 0))
|
|
396
|
+
imgui.set_item_allow_overlap()
|
|
397
|
+
draw_list = imgui.get_window_draw_list()
|
|
398
|
+
draw_list.channels_split(Melty.max_depth)
|
|
399
|
+
Melty.channels_split = True
|
|
400
|
+
Melty.window_stack.append((self.name, True))
|
|
401
|
+
|
|
402
|
+
radius = titlebar.frame_corner_radius() if transparent else 0.0
|
|
403
|
+
previous_tint, bg_color = self._root_background(disp_w, disp_h, radius)
|
|
404
|
+
if transparent and Toggles.Melty.window_shadow_lift > 0:
|
|
405
|
+
from meltygui.core.cache.tile_cache import add_shadow
|
|
406
|
+
add_shadow((0, 0, disp_w, disp_h), offset=0.5, corner_radius=radius, clip=False)
|
|
407
|
+
|
|
408
|
+
top = titlebar.top_inset() if self.chrome else 0.0
|
|
409
|
+
imgui.set_cursor_screen_pos((0, top))
|
|
410
|
+
# The root fills the OS MODEL's size (os_frame.content_size): equal
|
|
411
|
+
# to the display except while our own resize is still landing.
|
|
412
|
+
fill_w, fill_h = os_frame.content_size((disp_w, disp_h)) if self.chrome else (disp_w, disp_h)
|
|
413
|
+
Melty.root_fill = (float(fill_w), float(fill_h) - top, float(top)) # (w, h below the chrome, top y)
|
|
414
|
+
Melty.root_fill_used = False
|
|
415
|
+
# The body runs INSIDE the ground, as the studio's windows run inside
|
|
416
|
+
# draw_main's show_bg: one bg depth down, the ground's tint and colour
|
|
417
|
+
# on the stacks draw_bg reads for the bleed (the wrapper's own fill).
|
|
418
|
+
Melty.bg_depth += 1
|
|
419
|
+
Melty.bg_stack.append(Melty.style_manager.get_tint())
|
|
420
|
+
Melty.bg_color_stack.append(bg_color)
|
|
421
|
+
try:
|
|
422
|
+
self.body(self)
|
|
423
|
+
self._draw_render_hosts()
|
|
424
|
+
finally:
|
|
425
|
+
Melty.root_fill = None
|
|
426
|
+
Melty.bg_depth -= 1
|
|
427
|
+
Melty.bg_stack.pop()
|
|
428
|
+
Melty.bg_color_stack.pop()
|
|
429
|
+
Melty.style_manager.set_imgui_tint(*previous_tint)
|
|
430
|
+
if self.chrome:
|
|
431
|
+
titlebar.paint_window_controls(draw_list)
|
|
432
|
+
titlebar.note_fps_label(self.fps_counter.label(), self.chrome)
|
|
433
|
+
Melty.end_frame()
|
|
434
|
+
if self.request is not None:
|
|
435
|
+
Melty.finish_surface_root(self.request, self)
|
|
436
|
+
if self.chrome:
|
|
437
|
+
os_frame.flush()
|
|
438
|
+
Melty.window_stack.pop()
|
|
439
|
+
draw_list.channels_merge()
|
|
440
|
+
Melty.channels_split = False
|
|
441
|
+
views.end()
|
|
442
|
+
if self.chrome:
|
|
443
|
+
titlebar.draw_titlebar(self.window)
|
|
444
|
+
views.end_frame()
|
|
445
|
+
try:
|
|
446
|
+
Melty.post_frame(self.impl, self.window) # imgui render, shadow, corner cut, PQ, swap
|
|
447
|
+
except Exception:
|
|
448
|
+
if _DEBUG:
|
|
449
|
+
import ctypes
|
|
450
|
+
cur = glfw.get_current_context()
|
|
451
|
+
cg = titlebar._corner_gl
|
|
452
|
+
print(f'[surface {self.title}] post_frame FAILED ctx={ctypes.cast(cur, ctypes.c_void_p).value:#x} '
|
|
453
|
+
f'mine={ctypes.cast(self.window, ctypes.c_void_p).value:#x} corner_gl={id(cg):#x} '
|
|
454
|
+
f'corner_cache={getattr(cg, "_cache", None) and list(getattr(cg, "_cache").keys())} '
|
|
455
|
+
f'mods_corner={id(self._mods[(titlebar, "_corner_gl")]):#x} active={Surface.active.title!r}',
|
|
456
|
+
flush=True)
|
|
457
|
+
raise
|
|
458
|
+
self.fps_counter.frame_finished(frame_started)
|
|
459
|
+
self.frames += 1
|
|
460
|
+
if self.frames == 2 and not Melty.cache.enabled:
|
|
461
|
+
Melty.cache.set_enabled(True)
|
|
462
|
+
|
|
463
|
+
def _root_background(self, w, h, radius):
|
|
464
|
+
"""The window's ground: what the studio's Main Window paints under
|
|
465
|
+
its windows (draw_main's show_bg — draw_bg at depth 0 under the root
|
|
466
|
+
tint, capped at the same max_bg_value), edge to edge with the alpha
|
|
467
|
+
cut's corner radius and no outline stroke. Sets the style tint for
|
|
468
|
+
the body; returns (the tint to restore, the ground's colour)."""
|
|
469
|
+
from meltygui.view.decoration_view import draw_bg
|
|
470
|
+
style_manager = Melty.style_manager
|
|
471
|
+
previous_tint = style_manager.get_tint()
|
|
472
|
+
tint = self.tint if self.tint is not None else Toggles.Melty.app_root_tint
|
|
473
|
+
style_manager.set_imgui_tint(*tint[:4])
|
|
474
|
+
_, bg_color = draw_bg(bypass=True, left=0, top=0, width=w, height=h, rounding=radius,
|
|
475
|
+
outline=False, opacity=1.0, max_bg_value=ROOT_MAX_BG_VALUE,
|
|
476
|
+
depth=Melty.shadow_depth, style_manager=style_manager)
|
|
477
|
+
return previous_tint, bg_color
|
|
478
|
+
|
|
479
|
+
# --- geometry (children) ---------------------------------------------------------
|
|
480
|
+
def content_size(self):
|
|
481
|
+
return glfw.get_framebuffer_size(self.window)
|
|
482
|
+
|
|
483
|
+
# --- teardown --------------------------------------------------------------------
|
|
484
|
+
def destroy(self):
|
|
485
|
+
# A parent destroys its children recursively. The app shutdown loop
|
|
486
|
+
# may still hold those children in its snapshot of Surface.all.
|
|
487
|
+
if self not in Surface.all:
|
|
488
|
+
return
|
|
489
|
+
if _DEBUG:
|
|
490
|
+
print(f'[surface] destroy {self.title!r} (active={getattr(Surface.active, "title", None)!r})', flush=True)
|
|
491
|
+
for child in list(self.children):
|
|
492
|
+
child.destroy()
|
|
493
|
+
if self.parent is not None and self in self.parent.children:
|
|
494
|
+
self.parent.children.remove(self)
|
|
495
|
+
self.activate()
|
|
496
|
+
from meltygui.core.graphics.gl_state import GLState
|
|
497
|
+
from meltygui.core.graphics.gl_state import current_context
|
|
498
|
+
context = current_context()
|
|
499
|
+
try:
|
|
500
|
+
if len(Surface.all) > 1:
|
|
501
|
+
# Other surfaces live on: the renderer's shutdown would
|
|
502
|
+
# delete the shader program and the font texture, SHARED
|
|
503
|
+
# across the GL share group. Drop only this context's own
|
|
504
|
+
# vertex objects.
|
|
505
|
+
impl = self.impl
|
|
506
|
+
if getattr(impl, '_vao_handle', -1) > 0:
|
|
507
|
+
gl.glDeleteVertexArrays(1, [impl._vao_handle])
|
|
508
|
+
for name in ('_vbo_handle', '_elements_handle'):
|
|
509
|
+
handle = getattr(impl, name, -1)
|
|
510
|
+
if handle > 0:
|
|
511
|
+
gl.glDeleteBuffers(1, [handle])
|
|
512
|
+
impl._vao_handle = impl._vbo_handle = impl._elements_handle = 0
|
|
513
|
+
else:
|
|
514
|
+
self.impl.shutdown()
|
|
515
|
+
except Exception:
|
|
516
|
+
pass
|
|
517
|
+
# This surface's GL objects, deleted while it's still current: the
|
|
518
|
+
# scene target, the chrome's GLState and the shader filter. Whatever
|
|
519
|
+
# else is queued for it later (the GLState updates after the window
|
|
520
|
+
# is gone) is discarded below - its names die with the context.
|
|
521
|
+
GLState.release_context(context)
|
|
522
|
+
scene_target.shutdown()
|
|
523
|
+
for state in (titlebar._corner_gl, scene_target._STATE.get('gl'), Melty.dynamic_style_gl):
|
|
524
|
+
if state is not None:
|
|
525
|
+
try:
|
|
526
|
+
state.release()
|
|
527
|
+
except Exception:
|
|
528
|
+
pass
|
|
529
|
+
try:
|
|
530
|
+
Melty.filter.cleanup()
|
|
531
|
+
except Exception:
|
|
532
|
+
pass
|
|
533
|
+
try:
|
|
534
|
+
GLState.flush_deletes()
|
|
535
|
+
except Exception:
|
|
536
|
+
pass
|
|
537
|
+
# This window's Wayland objects, released while its proxies are alive
|
|
538
|
+
# (glfw.destroy_window takes the wl_surface): the HDR buffer / feedback
|
|
539
|
+
# / description, and wayland_move's per-window references. The
|
|
540
|
+
# connection-wide seat, pointer and managers stay for the others.
|
|
541
|
+
if self.hdr_tagged or glfw.get_platform() == glfw.PLATFORM_WAYLAND:
|
|
542
|
+
try:
|
|
543
|
+
wayland_color.detach()
|
|
544
|
+
except Exception:
|
|
545
|
+
pass
|
|
546
|
+
try:
|
|
547
|
+
wayland_move.detach(self.window)
|
|
548
|
+
except Exception:
|
|
549
|
+
pass
|
|
550
|
+
if self in Surface.all:
|
|
551
|
+
Surface.all.remove(self)
|
|
552
|
+
Surface.active = None
|
|
553
|
+
# The imgui context must not be current when destroyed.
|
|
554
|
+
imgui.set_current_context(Surface.owner_context)
|
|
555
|
+
imgui.destroy_context(self.ctx)
|
|
556
|
+
glfw.destroy_window(self.window)
|
|
557
|
+
self.window = None
|
|
558
|
+
GLState.discard_context(context)
|
|
559
|
+
if Surface.all:
|
|
560
|
+
Surface.all[0].activate()
|
|
561
|
+
elif Surface.owner_window is not None:
|
|
562
|
+
glfw.make_context_current(Surface.owner_window)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def root_view_kwargs(name, /, **kwargs):
|
|
566
|
+
"""The kwargs that draw a render func as the active surface's ROOT meltygui
|
|
567
|
+
window: a closable window pinned to the OS window (layouts — draw_rows /
|
|
568
|
+
draw_columns — register their edges on the enclosing WINDOW, so the
|
|
569
|
+
root must be one), sized to it, never dragged, its own close and shadow
|
|
570
|
+
off (the OS window has the chrome). Shared by app._draw_root (a
|
|
571
|
+
`@glfw_window` over a render func) and Melty.draw_surface_root (a
|
|
572
|
+
`glfw_window=True` child).
|
|
573
|
+
|
|
574
|
+
``with_header=`` (the decorator's, the call's) puts the MELTY HEADER in
|
|
575
|
+
the chrome row — the same header a studio window wears, beside the OS
|
|
576
|
+
window's controls: `show_name` names it after the OS window's title
|
|
577
|
+
(`display_name`, live through a retitle), the tint chip edits the
|
|
578
|
+
view's tint, and the geometry keeps clear of the controls on both sides
|
|
579
|
+
— `header_indent` past a left-side group, and a `with_header_end` that
|
|
580
|
+
only claims the right group's width (titlebar.draw_header_controls), so
|
|
581
|
+
the wrapper right-aligns and clips the header exactly as it does
|
|
582
|
+
around a header's close button. No collapse arrow (`is_tree=False`): an
|
|
583
|
+
OS window does not fold to its header. Without a header the body
|
|
584
|
+
starts under the control row (titlebar.top_inset).
|
|
585
|
+
|
|
586
|
+
Caller kwargs win over every pinned value (``show_header=False`` hides
|
|
587
|
+
a passed header, ``disable_scroll=False`` scrolls the root, a ``name=``
|
|
588
|
+
the fallback ``name`` — positional-only, so a child's own kwargs pass)."""
|
|
589
|
+
width, height, top = Melty.root_fill
|
|
590
|
+
header = kwargs.get('with_header') is not None
|
|
591
|
+
if header:
|
|
592
|
+
surface = Surface.active
|
|
593
|
+
chrome = surface is not None and surface.chrome
|
|
594
|
+
left_inset, right_inset = titlebar.chrome_insets() if chrome else (0.0, 0.0)
|
|
595
|
+
# The header row IS the chrome row: the view starts at the very
|
|
596
|
+
# top and the body is immediately under the header.
|
|
597
|
+
height, top = height + top, 0.0
|
|
598
|
+
kwargs.setdefault('is_tree', False)
|
|
599
|
+
kwargs.setdefault('show_tint', True)
|
|
600
|
+
kwargs.setdefault('display_name', surface.title if surface is not None else name)
|
|
601
|
+
kwargs.setdefault('header_indent', left_inset)
|
|
602
|
+
kwargs.setdefault('with_header_end', titlebar.draw_header_controls if right_inset > 0 else None)
|
|
603
|
+
else:
|
|
604
|
+
kwargs.setdefault('with_header_end', None)
|
|
605
|
+
# OS bodies orchestrate render calls and shortcuts every requested frame.
|
|
606
|
+
# Descendant bodies have their own caches; the app still sleeps when idle.
|
|
607
|
+
pinned = dict(name=name, closable=True, draggable=False, frame_pinned=True,
|
|
608
|
+
window_pos=(0, top), width=width, height=height,
|
|
609
|
+
auto_resize=False, show_header=header, with_footer=None, shadow=False, show_bg=True,
|
|
610
|
+
selectable=False, use_cache=False, disable_scroll=True, indent_size=5,
|
|
611
|
+
initial={'width': width, 'height': height, 'window_pos': (0, top)})
|
|
612
|
+
return pinned | kwargs
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
def _fresh(value):
|
|
616
|
+
try:
|
|
617
|
+
return copy.deepcopy(value)
|
|
618
|
+
except Exception:
|
|
619
|
+
return value
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
def _owner_io():
|
|
623
|
+
if Surface.owner_context is None:
|
|
624
|
+
raise RuntimeError('Surface.owner_context is not set (app.py creates it)')
|
|
625
|
+
prev = imgui.get_current_context()
|
|
626
|
+
imgui.set_current_context(Surface.owner_context)
|
|
627
|
+
try:
|
|
628
|
+
return imgui.get_io()
|
|
629
|
+
finally:
|
|
630
|
+
if prev is not None:
|
|
631
|
+
imgui.set_current_context(prev)
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
def _unique_title(title):
|
|
635
|
+
"""Window titles double as the geometry feed's per-window key (several
|
|
636
|
+
windows share our pid and class), so no two surfaces share one."""
|
|
637
|
+
taken = {s.title for s in Surface.all}
|
|
638
|
+
if title not in taken:
|
|
639
|
+
return title
|
|
640
|
+
n = 2
|
|
641
|
+
while f'{title} ({n})' in taken:
|
|
642
|
+
n += 1
|
|
643
|
+
return f'{title} ({n})'
|