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,522 @@
|
|
|
1
|
+
import meltygui_imgui as imgui
|
|
2
|
+
from meltygui.hdr_color import pack_color
|
|
3
|
+
from meltygui.hdr_color import unpack_color
|
|
4
|
+
from meltygui.hdr_color import style_color
|
|
5
|
+
from meltygui.hdr_color import scale_saturation
|
|
6
|
+
import colorsys
|
|
7
|
+
|
|
8
|
+
from meltygui.core.styling.global_style import GlobalStyle
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def cap_brightness(r, g, b, max_b):
|
|
12
|
+
"""Cap PERCEIVED brightness (0.299r + 0.587g + 0.114b) at max_b — the
|
|
13
|
+
legibility guard for widget fills that carry light text. Scales the
|
|
14
|
+
channels, so hue and saturation survive (the max-side of the editor's
|
|
15
|
+
_brightness_clamp; kept local because toggles.py imports this module).
|
|
16
|
+
max_b <= 0 disables."""
|
|
17
|
+
if max_b <= 0:
|
|
18
|
+
return r, g, b
|
|
19
|
+
lum = 0.299 * r + 0.587 * g + 0.114 * b
|
|
20
|
+
if lum > max_b:
|
|
21
|
+
k = max_b / lum
|
|
22
|
+
return r * k, g * k, b * k
|
|
23
|
+
return r, g, b
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ImGuiStyleManager:
|
|
27
|
+
def __init__(self):
|
|
28
|
+
self.saved_colors = None
|
|
29
|
+
self.saved_style = None
|
|
30
|
+
self.saved_rgb = None
|
|
31
|
+
self.current_rgb = (0.0, 0.0, 0.0)
|
|
32
|
+
self.hsv = (0.0, 0.0, 0.0)
|
|
33
|
+
self.root = None
|
|
34
|
+
|
|
35
|
+
# List of all color indices we need to save/restore
|
|
36
|
+
self.color_indices = [
|
|
37
|
+
imgui.COLOR_TEXT,
|
|
38
|
+
imgui.COLOR_TEXT_DISABLED,
|
|
39
|
+
imgui.COLOR_WINDOW_BACKGROUND,
|
|
40
|
+
imgui.COLOR_CHILD_BACKGROUND,
|
|
41
|
+
imgui.COLOR_POPUP_BACKGROUND,
|
|
42
|
+
imgui.COLOR_BORDER,
|
|
43
|
+
imgui.COLOR_BORDER_SHADOW,
|
|
44
|
+
imgui.COLOR_FRAME_BACKGROUND,
|
|
45
|
+
imgui.COLOR_FRAME_BACKGROUND_HOVERED,
|
|
46
|
+
imgui.COLOR_FRAME_BACKGROUND_ACTIVE,
|
|
47
|
+
imgui.COLOR_TITLE_BACKGROUND,
|
|
48
|
+
imgui.COLOR_TITLE_BACKGROUND_ACTIVE,
|
|
49
|
+
imgui.COLOR_TITLE_BACKGROUND_COLLAPSED,
|
|
50
|
+
imgui.COLOR_SCROLLBAR_BACKGROUND,
|
|
51
|
+
imgui.COLOR_SCROLLBAR_GRAB,
|
|
52
|
+
imgui.COLOR_SCROLLBAR_GRAB_HOVERED,
|
|
53
|
+
imgui.COLOR_SCROLLBAR_GRAB_ACTIVE,
|
|
54
|
+
imgui.COLOR_CHECK_MARK,
|
|
55
|
+
imgui.COLOR_SLIDER_GRAB,
|
|
56
|
+
imgui.COLOR_SLIDER_GRAB_ACTIVE,
|
|
57
|
+
imgui.COLOR_BUTTON,
|
|
58
|
+
imgui.COLOR_BUTTON_HOVERED,
|
|
59
|
+
imgui.COLOR_BUTTON_ACTIVE,
|
|
60
|
+
imgui.COLOR_HEADER,
|
|
61
|
+
imgui.COLOR_HEADER_HOVERED,
|
|
62
|
+
imgui.COLOR_HEADER_ACTIVE,
|
|
63
|
+
imgui.COLOR_SEPARATOR,
|
|
64
|
+
imgui.COLOR_SEPARATOR_HOVERED,
|
|
65
|
+
imgui.COLOR_SEPARATOR_ACTIVE,
|
|
66
|
+
imgui.COLOR_RESIZE_GRIP,
|
|
67
|
+
imgui.COLOR_RESIZE_GRIP_HOVERED,
|
|
68
|
+
imgui.COLOR_RESIZE_GRIP_ACTIVE,
|
|
69
|
+
imgui.COLOR_TAB,
|
|
70
|
+
imgui.COLOR_TAB_HOVERED,
|
|
71
|
+
imgui.COLOR_TAB_ACTIVE,
|
|
72
|
+
imgui.COLOR_TAB_UNFOCUSED,
|
|
73
|
+
imgui.COLOR_TAB_UNFOCUSED_ACTIVE,
|
|
74
|
+
imgui.COLOR_PLOT_LINES,
|
|
75
|
+
imgui.COLOR_PLOT_LINES_HOVERED,
|
|
76
|
+
imgui.COLOR_PLOT_HISTOGRAM,
|
|
77
|
+
imgui.COLOR_PLOT_HISTOGRAM_HOVERED,
|
|
78
|
+
imgui.COLOR_TEXT_SELECTED_BACKGROUND,
|
|
79
|
+
imgui.COLOR_DRAG_DROP_TARGET,
|
|
80
|
+
imgui.COLOR_NAV_HIGHLIGHT,
|
|
81
|
+
imgui.COLOR_NAV_WINDOWING_HIGHLIGHT,
|
|
82
|
+
imgui.COLOR_NAV_WINDOWING_DIM_BACKGROUND,
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
def set_root(self, root):
|
|
86
|
+
self.root = root
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def _safe_rgb_to_hsv(r, g, b):
|
|
90
|
+
# EXTENDED sRGB HSV: colours are never clamped into [0, 1] anymore.
|
|
91
|
+
# colorsys is exact on extended values whenever max(r, g, b) > 0 -
|
|
92
|
+
# a channel above 1 lands in v (> 1 = brighter than SDR white), a
|
|
93
|
+
# negative channel in s (> 1 = outside the sRGB gamut, the P3 tints
|
|
94
|
+
# from hdr_color.p3), and hsv_to_rgb hands both straight back. The
|
|
95
|
+
# old per-channel clamp is why draw_bg never showed a tint's HDR
|
|
96
|
+
# headroom or wide gamut (09-07). The one hazard is a max channel
|
|
97
|
+
# ≤ 0 (a black tint like (0.0, -0.005, -0.009), or all-negative):
|
|
98
|
+
# colorsys divides by it, so the only sensible result is black.
|
|
99
|
+
def clean(c):
|
|
100
|
+
try:
|
|
101
|
+
c = float(c)
|
|
102
|
+
except (TypeError, ValueError):
|
|
103
|
+
return 0.0
|
|
104
|
+
return 0.0 if c != c else c # NaN → 0
|
|
105
|
+
|
|
106
|
+
r, g, b = clean(r), clean(g), clean(b)
|
|
107
|
+
if max(r, g, b) <= 0.0:
|
|
108
|
+
return 0.0, 0.0, 0.0
|
|
109
|
+
return colorsys.rgb_to_hsv(r, g, b)
|
|
110
|
+
|
|
111
|
+
def make_custom_styled(self, r, g, b, input, alpha=1.0, value=0.5, saturation=None):
|
|
112
|
+
h, s, v = self._safe_rgb_to_hsv(r, g, b)
|
|
113
|
+
value = input["value"] + value
|
|
114
|
+
|
|
115
|
+
if saturation is not None:
|
|
116
|
+
saturation_scale = saturation
|
|
117
|
+
else:
|
|
118
|
+
saturation_scale = input["saturation"]
|
|
119
|
+
value = (v * GlobalStyle.base_value) + value
|
|
120
|
+
if 'max_value' in input:
|
|
121
|
+
value = min(value, input["max_value"])
|
|
122
|
+
|
|
123
|
+
modified_rgb = colorsys.hsv_to_rgb(h, scale_saturation(s, saturation_scale), value)
|
|
124
|
+
return (modified_rgb[0], modified_rgb[1], modified_rgb[2], alpha)
|
|
125
|
+
|
|
126
|
+
def make_custom(self, r, g, b, value, saturation_scale=1.0, alpha=1.0):
|
|
127
|
+
h, s, v = self._safe_rgb_to_hsv(r, g, b)
|
|
128
|
+
modified_rgb = colorsys.hsv_to_rgb(h, scale_saturation(s, saturation_scale), value)
|
|
129
|
+
imgui_color = pack_color(modified_rgb[0], modified_rgb[1], modified_rgb[2], alpha)
|
|
130
|
+
|
|
131
|
+
return unpack_color(imgui_color)[:3]
|
|
132
|
+
|
|
133
|
+
def make_color(self, value, saturation_scale=1.0, alpha=1.0):
|
|
134
|
+
h, s, v = self.hsv
|
|
135
|
+
value = (v * GlobalStyle.secondary_value) + value
|
|
136
|
+
|
|
137
|
+
modified_rgb = colorsys.hsv_to_rgb(h, scale_saturation(s, saturation_scale), value)
|
|
138
|
+
imgui_color = pack_color(modified_rgb[0], modified_rgb[1], modified_rgb[2], alpha)
|
|
139
|
+
return imgui_color
|
|
140
|
+
|
|
141
|
+
def make_color_rgb(self, r, g, b, saturation_scale=0.4, alpha=1.0, factor=0.3, value=0.5):
|
|
142
|
+
"""
|
|
143
|
+
Create an ImGui color from RGB values with optional saturation and alpha adjustments.
|
|
144
|
+
Args:
|
|
145
|
+
r, g, b: RGB values between 0 and 1
|
|
146
|
+
saturation_scale: Scale for saturation (default is 1.0)
|
|
147
|
+
alpha: Alpha value (default is 1.0)
|
|
148
|
+
Returns:
|
|
149
|
+
Packed u32 color value
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
def mix(r1, g1, b1, r2, g2, b2, alpha):
|
|
153
|
+
"""Mix two colors with alpha blending"""
|
|
154
|
+
return (
|
|
155
|
+
r1 * (1 - alpha) + r2 * alpha,
|
|
156
|
+
g1 * (1 - alpha) + g2 * alpha,
|
|
157
|
+
b1 * (1 - alpha) + b2 * alpha
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Pure function of the current tint and the arguments - memoize:
|
|
161
|
+
# flat_button / tabs / dock rows call this twice per widget per
|
|
162
|
+
# frame with a handful of distinct values (two hsv round trips each).
|
|
163
|
+
key = (self.current_rgb, r, g, b, saturation_scale, alpha, factor, value)
|
|
164
|
+
hit = _MAKE_COLOR_RGB_MEMO.get(key)
|
|
165
|
+
if hit is not None:
|
|
166
|
+
return hit
|
|
167
|
+
|
|
168
|
+
modified_rgb = self.make_custom(*self.current_rgb, value, saturation_scale=saturation_scale, alpha=alpha)
|
|
169
|
+
# Apply alpha blending with the original color
|
|
170
|
+
modified_rgb = mix(r, g, b, modified_rgb[0], modified_rgb[1], modified_rgb[2], factor)
|
|
171
|
+
|
|
172
|
+
result = (modified_rgb[0], modified_rgb[1], modified_rgb[2], alpha)
|
|
173
|
+
if len(_MAKE_COLOR_RGB_MEMO) > 4096:
|
|
174
|
+
_MAKE_COLOR_RGB_MEMO.clear() # bounded: drags mint many times
|
|
175
|
+
_MAKE_COLOR_RGB_MEMO[key] = result
|
|
176
|
+
return result
|
|
177
|
+
|
|
178
|
+
def make_color_style_imgui(self, input, alpha=1.0):
|
|
179
|
+
"""
|
|
180
|
+
Create an ImGui color from a dictionary input with saturation and alpha adjustments.
|
|
181
|
+
Args:
|
|
182
|
+
input: Dictionary containing 'value', 'saturation', 'alpha', and optional 'max_value'
|
|
183
|
+
Returns:
|
|
184
|
+
Packed u32 color value
|
|
185
|
+
"""
|
|
186
|
+
color = self.make_color_style(input, alpha)
|
|
187
|
+
return pack_color(color[0], color[1], color[2], alpha)
|
|
188
|
+
|
|
189
|
+
def make_color_style_value_imgui(self, input, saturation=None, alpha=1.0, value=0.0):
|
|
190
|
+
"""
|
|
191
|
+
Create an ImGui color from a dictionary input with saturation and alpha adjustments.
|
|
192
|
+
Args:
|
|
193
|
+
input: Dictionary containing 'value', 'saturation', 'alpha', and optional 'max_value'
|
|
194
|
+
Returns:
|
|
195
|
+
Packed u32 color value
|
|
196
|
+
"""
|
|
197
|
+
color = self.make_color_style_value(input, alpha, saturation=saturation, value=value)
|
|
198
|
+
return pack_color(color[0], color[1], color[2], alpha)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def make_color_style_rgb(self, r, g, b, input,factor=0.6):
|
|
202
|
+
"""
|
|
203
|
+
Create an ImGui color from RGB values with optional saturation and alpha adjustments.
|
|
204
|
+
Args:
|
|
205
|
+
r, g, b: RGB values between 0 and 1
|
|
206
|
+
saturation_scale: Scale for saturation (default is 1.0)
|
|
207
|
+
alpha: Alpha value (default is 1.0)
|
|
208
|
+
Returns:
|
|
209
|
+
Packed u32 color value
|
|
210
|
+
"""
|
|
211
|
+
h, s, v = self.hsv
|
|
212
|
+
|
|
213
|
+
value = input["value"]
|
|
214
|
+
|
|
215
|
+
saturation_scale = input["saturation"]
|
|
216
|
+
value = (v * GlobalStyle.base_value) + value
|
|
217
|
+
if 'max_value' in input:
|
|
218
|
+
value = min(value, input["max_value"])
|
|
219
|
+
alpha = input["alpha"]
|
|
220
|
+
|
|
221
|
+
modified_rgb = colorsys.hsv_to_rgb(h, scale_saturation(s, saturation_scale), value)
|
|
222
|
+
|
|
223
|
+
def mix(r1, g1, b1, r2, g2, b2, alpha):
|
|
224
|
+
"""Mix two colors with alpha blending"""
|
|
225
|
+
return (
|
|
226
|
+
r1 * (1 - alpha) + r2 * alpha,
|
|
227
|
+
g1 * (1 - alpha) + g2 * alpha,
|
|
228
|
+
b1 * (1 - alpha) + b2 * alpha
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Apply alpha blending with the original color
|
|
232
|
+
modified_rgb = mix(r, g, b, modified_rgb[0], modified_rgb[1], modified_rgb[2], factor)
|
|
233
|
+
|
|
234
|
+
return modified_rgb[0], modified_rgb[1], modified_rgb[2], alpha
|
|
235
|
+
|
|
236
|
+
def make_color_style_value(self, input, alpha=1.0, value=0.5, saturation=None):
|
|
237
|
+
h, s, v = self.hsv
|
|
238
|
+
|
|
239
|
+
value = input["value"] + value
|
|
240
|
+
|
|
241
|
+
if saturation is not None:
|
|
242
|
+
saturation_scale = saturation
|
|
243
|
+
else:
|
|
244
|
+
saturation_scale = input["saturation"]
|
|
245
|
+
value = (v * GlobalStyle.base_value) + value
|
|
246
|
+
if 'max_value' in input:
|
|
247
|
+
value = min(value, input["max_value"])
|
|
248
|
+
|
|
249
|
+
modified_rgb = colorsys.hsv_to_rgb(h, scale_saturation(s, saturation_scale), value)
|
|
250
|
+
return (modified_rgb[0], modified_rgb[1], modified_rgb[2], alpha)
|
|
251
|
+
|
|
252
|
+
def make_color_style(self, input, alpha=1.0):
|
|
253
|
+
h, s, v = self.hsv
|
|
254
|
+
|
|
255
|
+
value = input["value"]
|
|
256
|
+
|
|
257
|
+
saturation_scale = input["saturation"]
|
|
258
|
+
value = (v * GlobalStyle.base_value) + value
|
|
259
|
+
if 'max_value' in input:
|
|
260
|
+
value = min(value, input["max_value"])
|
|
261
|
+
alpha = input["alpha"]
|
|
262
|
+
|
|
263
|
+
modified_rgb = colorsys.hsv_to_rgb(h, scale_saturation(s, saturation_scale), value)
|
|
264
|
+
return (modified_rgb[0], modified_rgb[1], modified_rgb[2], alpha)
|
|
265
|
+
|
|
266
|
+
def make(self, value, saturation_scale=1.0, alpha=1.0):
|
|
267
|
+
_unpack_color = unpack_color
|
|
268
|
+
|
|
269
|
+
color = self.make_color(value, saturation_scale, alpha)
|
|
270
|
+
unpacked = _unpack_color(color)
|
|
271
|
+
return (unpacked[0], unpacked[1], unpacked[2])
|
|
272
|
+
|
|
273
|
+
def make_color_unpacked(self, value, saturation_scale=1.0, alpha=1.0):
|
|
274
|
+
_unpack_color = unpack_color
|
|
275
|
+
color = self.make_color(value, saturation_scale, alpha)
|
|
276
|
+
return _unpack_color(color)
|
|
277
|
+
|
|
278
|
+
def save_style(self):
|
|
279
|
+
"""Save the current ImGui style and colors"""
|
|
280
|
+
# style = imgui.get_style()
|
|
281
|
+
self.saved_rgb = self.current_rgb
|
|
282
|
+
|
|
283
|
+
return self.saved_rgb
|
|
284
|
+
# # Save colors by their indices
|
|
285
|
+
# self.saved_colors = {i: tuple(style.colors[i]) for i in self.color_indices}
|
|
286
|
+
#
|
|
287
|
+
# # Save other style variables
|
|
288
|
+
# self.saved_style = {
|
|
289
|
+
# 'alpha': style.alpha,
|
|
290
|
+
# 'window_padding': style.window_padding,
|
|
291
|
+
# 'window_rounding': style.window_rounding,
|
|
292
|
+
# 'frame_padding': style.frame_padding,
|
|
293
|
+
# 'frame_rounding': style.frame_rounding,
|
|
294
|
+
# 'item_spacing': style.item_spacing,
|
|
295
|
+
# 'item_inner_spacing': style.item_inner_spacing,
|
|
296
|
+
# 'touch_extra_padding': style.touch_extra_padding,
|
|
297
|
+
# 'indent_spacing': style.indent_spacing,
|
|
298
|
+
# 'scrollbar_size': style.scrollbar_size,
|
|
299
|
+
# 'grab_min_size': style.grab_min_size
|
|
300
|
+
# }
|
|
301
|
+
|
|
302
|
+
def restore(self, saved_rgb=None):
|
|
303
|
+
if saved_rgb is not None:
|
|
304
|
+
self.set_imgui_tint(*saved_rgb)
|
|
305
|
+
else:
|
|
306
|
+
self.set_imgui_tint(*self.saved_rgb)
|
|
307
|
+
|
|
308
|
+
def restore_style(self, saved_rgb=None):
|
|
309
|
+
"""Restore the previously saved style and colors"""
|
|
310
|
+
|
|
311
|
+
if saved_rgb is not None:
|
|
312
|
+
self.set_imgui_tint(*saved_rgb)
|
|
313
|
+
else:
|
|
314
|
+
self.set_imgui_tint(*self.saved_rgb)
|
|
315
|
+
# if self.saved_colors is None or self.saved_style is None:
|
|
316
|
+
# print("Warning: No style saved to restore")
|
|
317
|
+
# # Print stack trace
|
|
318
|
+
# import traceback
|
|
319
|
+
# traceback.print_stack()
|
|
320
|
+
# return False
|
|
321
|
+
#
|
|
322
|
+
# style = imgui.get_style()
|
|
323
|
+
# # Restore colors
|
|
324
|
+
# for i, color in self.saved_colors.items():
|
|
325
|
+
# style.colors[i] = color
|
|
326
|
+
#
|
|
327
|
+
# # Restore other style variables
|
|
328
|
+
# style.alpha = self.saved_style['alpha']
|
|
329
|
+
# style.window_padding = self.saved_style['window_padding']
|
|
330
|
+
# style.window_rounding = self.saved_style['window_rounding']
|
|
331
|
+
# style.frame_padding = self.saved_style['frame_padding']
|
|
332
|
+
# style.frame_rounding = self.saved_style['frame_rounding']
|
|
333
|
+
# style.item_spacing = self.saved_style['item_spacing']
|
|
334
|
+
# style.item_inner_spacing = self.saved_style['item_inner_spacing']
|
|
335
|
+
# style.touch_extra_padding = self.saved_style['touch_extra_padding']
|
|
336
|
+
# style.indent_spacing = self.saved_style['indent_spacing']
|
|
337
|
+
# style.scrollbar_size = self.saved_style['scrollbar_size']
|
|
338
|
+
# style.grab_min_size = self.saved_style['grab_min_size']
|
|
339
|
+
# return True
|
|
340
|
+
|
|
341
|
+
def tint_gold(self):
|
|
342
|
+
self.save_style()
|
|
343
|
+
self.set_imgui_tint(0.6, 0.5, 0.3)
|
|
344
|
+
|
|
345
|
+
def tint_default(self):
|
|
346
|
+
self.save_style()
|
|
347
|
+
self.set_imgui_tint(0.3, 0.6, 0.7)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def get_tint(self):
|
|
351
|
+
"""
|
|
352
|
+
Returns the current tint color as an RGB tuple.
|
|
353
|
+
Returns:
|
|
354
|
+
Tuple of (r, g, b) values in the range [0.0, 1.0]
|
|
355
|
+
"""
|
|
356
|
+
return self.current_rgb
|
|
357
|
+
|
|
358
|
+
def push_tint_fields(self, r, g, b, a=1.0):
|
|
359
|
+
"""set_imgui_tint's COLOUR MATH without the imgui table apply: sets
|
|
360
|
+
`current_rgb` / `hsv` (what make_color_* and draw_bg read) and
|
|
361
|
+
returns the previous pair for pop_tint_fields. For draw-list code
|
|
362
|
+
that only needs draw_bg to colour from a tint (the editor's inline
|
|
363
|
+
widgets, ~40 a frame) — the 35-entry imgui table isn't consulted
|
|
364
|
+
there, and applying it twice per widget was half the widget's cost."""
|
|
365
|
+
prev = (self.current_rgb, self.hsv)
|
|
366
|
+
if a < 1.0 and self.current_rgb is not None:
|
|
367
|
+
pr, pg, pb = self.current_rgb
|
|
368
|
+
r = r * a + pr * (1.0 - a)
|
|
369
|
+
g = g * a + pg * (1.0 - a)
|
|
370
|
+
b = b * a + pb * (1.0 - a)
|
|
371
|
+
self.current_rgb = (r, g, b)
|
|
372
|
+
self.hsv = self._safe_rgb_to_hsv(r, g, b)
|
|
373
|
+
return prev
|
|
374
|
+
|
|
375
|
+
def pop_tint_fields(self, prev):
|
|
376
|
+
self.current_rgb, self.hsv = prev
|
|
377
|
+
|
|
378
|
+
def set_imgui_tint(self, r, g, b, a=1.0):
|
|
379
|
+
"""
|
|
380
|
+
Sets a global tint color for ImGui by adjusting all style colors based on a single RGB color.
|
|
381
|
+
Args:
|
|
382
|
+
r, g, b: RGB values between 0 and 1
|
|
383
|
+
"""
|
|
384
|
+
|
|
385
|
+
# No boot gate here: the studio's `set_root` used to be the first
|
|
386
|
+
# thing that armed this, and a @glfw_window app never called that, so
|
|
387
|
+
# setting tint was a no-op and the whole app painted from the black
|
|
388
|
+
# default (09-12). The colour math needs nothing; only the imgui
|
|
389
|
+
# style table below needs a check.
|
|
390
|
+
|
|
391
|
+
# A 4-component tint carries an alpha that controls how much of the
|
|
392
|
+
# current (parent) tint bleeds through. a=1.0 -> use the new color
|
|
393
|
+
# outright; a=0.0 -> keep the parent color in full force. Because every
|
|
394
|
+
# nested layer re-applies its tint through here, a low alpha lets the
|
|
395
|
+
# parent tint accumulate down the layer stack instead of washing out.
|
|
396
|
+
if a < 1.0 and self.current_rgb is not None:
|
|
397
|
+
pr, pg, pb = self.current_rgb
|
|
398
|
+
r = r * a + pr * (1.0 - a)
|
|
399
|
+
g = g * a + pg * (1.0 - a)
|
|
400
|
+
b = b * a + pb * (1.0 - a)
|
|
401
|
+
|
|
402
|
+
self.current_rgb = (r, g, b)
|
|
403
|
+
h, s, v = self._safe_rgb_to_hsv(r, g, b)
|
|
404
|
+
self.hsv = (h, s, v)
|
|
405
|
+
if imgui.get_current_context() is None:
|
|
406
|
+
return
|
|
407
|
+
style = imgui.get_style()
|
|
408
|
+
# The 35-entry table below is a pure function of the final (r, g, b)
|
|
409
|
+
# and the widget brightness cap, so it's memoized per tint: the
|
|
410
|
+
# editor's inline widgets push + restore a tint each (60+ widgets a
|
|
411
|
+
# frame on a Toggles-like file = ~2.8 ms of hsv conversion per frame).
|
|
412
|
+
from meltygui.core.runtime.toggles import Toggles
|
|
413
|
+
widget_max_b = Toggles.Style.widget_max_brightness
|
|
414
|
+
real_colors = style.colors
|
|
415
|
+
cache_key = (round(r, 5), round(g, 5), round(b, 5), widget_max_b)
|
|
416
|
+
cached = _TINT_TABLE_CACHE.get(cache_key)
|
|
417
|
+
if cached is not None:
|
|
418
|
+
for idx, col in cached:
|
|
419
|
+
real_colors[idx] = col
|
|
420
|
+
return
|
|
421
|
+
# Entries are recorded as the packer's bytes / 255 (hdr_color.style_color):
|
|
422
|
+
# imgui converts a style float4 to u32 by itself, and that conversion
|
|
423
|
+
# must reproduce pack_color's layout (the SDR bit, the 7-bit alpha).
|
|
424
|
+
colors = _TintTableRecorder()
|
|
425
|
+
|
|
426
|
+
def make_color(input, alpha=1.0):
|
|
427
|
+
value = input["value"]
|
|
428
|
+
saturation_scale = input["saturation"]
|
|
429
|
+
GlobalStyle.base_value = 0.3
|
|
430
|
+
value = (v * GlobalStyle.base_value) + value
|
|
431
|
+
alpha = input["alpha"]
|
|
432
|
+
|
|
433
|
+
modified_rgb = colorsys.hsv_to_rgb(h, scale_saturation(s, saturation_scale), value)
|
|
434
|
+
return (modified_rgb[0], modified_rgb[1], modified_rgb[2], alpha)
|
|
435
|
+
|
|
436
|
+
# Widget fills (buttons, frame backgrounds = text edits + drag/slider
|
|
437
|
+
# tracks, check.)) sit under the light COLOR_TEXT, so their
|
|
438
|
+
# brightness is capped (Toggles.Style.widget_max_brightness): a bright
|
|
439
|
+
# tint otherwise lifts them to the text's brightness and the widgets
|
|
440
|
+
# go blank. Function-level import: toggles.py imports this module.
|
|
441
|
+
from meltygui.core.runtime.toggles import Toggles
|
|
442
|
+
widget_max_b = Toggles.Style.widget_max_brightness
|
|
443
|
+
|
|
444
|
+
def make_widget_color(input):
|
|
445
|
+
cr, cg, cb, ca = make_color(input)
|
|
446
|
+
cr, cg, cb = cap_brightness(cr, cg, cb, widget_max_b)
|
|
447
|
+
return (cr, cg, cb, ca)
|
|
448
|
+
|
|
449
|
+
glb_cst = GlobalStyle.main_const
|
|
450
|
+
|
|
451
|
+
colors[imgui.COLOR_TEXT] = make_color(glb_cst["widget"]["text"]) # Nearly white text
|
|
452
|
+
colors[imgui.COLOR_TEXT_DISABLED] = make_color(glb_cst["widget"]["text_disabled"]) # Grayed out text
|
|
453
|
+
|
|
454
|
+
colors[imgui.COLOR_WINDOW_BACKGROUND] = make_color(glb_cst["window"]["background"]) # Dark background
|
|
455
|
+
colors[imgui.COLOR_CHILD_BACKGROUND] = make_color(glb_cst["window"]["child_bg"])
|
|
456
|
+
colors[imgui.COLOR_POPUP_BACKGROUND] = make_color(glb_cst["window"]["popup_bg"])
|
|
457
|
+
colors[imgui.COLOR_BORDER] = make_color(glb_cst["window"]["border"])
|
|
458
|
+
|
|
459
|
+
# Title
|
|
460
|
+
colors[imgui.COLOR_TITLE_BACKGROUND] = make_color(glb_cst["window"]["title_bg"])
|
|
461
|
+
colors[imgui.COLOR_TITLE_BACKGROUND_ACTIVE] = make_color(glb_cst["window"]["title_bg_active"])
|
|
462
|
+
colors[imgui.COLOR_TITLE_BACKGROUND_COLLAPSED] = make_color(glb_cst["window"]["title_bg_collapsed"])
|
|
463
|
+
|
|
464
|
+
# Headers
|
|
465
|
+
colors[imgui.COLOR_HEADER] = make_color(glb_cst["window"]["header"])
|
|
466
|
+
colors[imgui.COLOR_HEADER_HOVERED] = make_color(glb_cst["window"]["header_hovered"])
|
|
467
|
+
colors[imgui.COLOR_HEADER_ACTIVE] = make_color(glb_cst["window"]["header_active"])
|
|
468
|
+
|
|
469
|
+
colors[imgui.COLOR_RESIZE_GRIP] = make_color(glb_cst["widget"]["resize_grip"])
|
|
470
|
+
colors[imgui.COLOR_RESIZE_GRIP_HOVERED] = make_color(glb_cst["widget"]["resize_hovered"])
|
|
471
|
+
colors[imgui.COLOR_RESIZE_GRIP_ACTIVE] = make_color(glb_cst["widget"]["resize_active"])
|
|
472
|
+
|
|
473
|
+
# Buttons
|
|
474
|
+
colors[imgui.COLOR_BUTTON] = make_widget_color(glb_cst["widget"]["button"])
|
|
475
|
+
colors[imgui.COLOR_BUTTON_HOVERED] = make_widget_color(glb_cst["widget"]["button_hovered"])
|
|
476
|
+
colors[imgui.COLOR_BUTTON_ACTIVE] = make_widget_color(glb_cst["widget"]["button_active"])
|
|
477
|
+
|
|
478
|
+
# Frame backgrounds
|
|
479
|
+
colors[imgui.COLOR_FRAME_BACKGROUND] = make_widget_color(glb_cst["frame"]["frame_bg"])
|
|
480
|
+
colors[imgui.COLOR_FRAME_BACKGROUND_HOVERED] = make_widget_color(glb_cst["frame"]["frame_hovered"])
|
|
481
|
+
colors[imgui.COLOR_FRAME_BACKGROUND_ACTIVE] = make_widget_color(glb_cst["frame"]["frame_active"])
|
|
482
|
+
|
|
483
|
+
colors[imgui.COLOR_CHECK_MARK] = make_color(glb_cst["widget"]["check_mark"])
|
|
484
|
+
colors[imgui.COLOR_TEXT_SELECTED_BACKGROUND] = make_color(glb_cst["widget"]["text_selected_bg"])
|
|
485
|
+
# Tabs
|
|
486
|
+
colors[imgui.COLOR_TAB] = make_color(glb_cst["tab"]["tab"])
|
|
487
|
+
colors[imgui.COLOR_TAB_HOVERED] = make_color(glb_cst["tab"]["tab_hovered"])
|
|
488
|
+
colors[imgui.COLOR_TAB_ACTIVE] = make_color(glb_cst["tab"]["tab_active"])
|
|
489
|
+
|
|
490
|
+
# Borders and separators
|
|
491
|
+
colors[imgui.COLOR_SEPARATOR] = make_color(glb_cst["frame"]["separator"])
|
|
492
|
+
|
|
493
|
+
# Sliders, scrollbars
|
|
494
|
+
colors[imgui.COLOR_SLIDER_GRAB] = make_widget_color(glb_cst["widget"]["slider_grab"])
|
|
495
|
+
colors[imgui.COLOR_SLIDER_GRAB_ACTIVE] = make_widget_color(glb_cst["widget"]["slider_grab_active"])
|
|
496
|
+
colors[imgui.COLOR_SCROLLBAR_GRAB] = make_color(glb_cst["widget"]["scrollbar_grab"])
|
|
497
|
+
colors[imgui.COLOR_SCROLLBAR_GRAB_HOVERED] = make_color(glb_cst["widget"]["scrollbar_grab_hovered"])
|
|
498
|
+
colors[imgui.COLOR_SCROLLBAR_GRAB_ACTIVE] = make_color(glb_cst["widget"]["scrollbar_grab_active"])
|
|
499
|
+
|
|
500
|
+
table = colors.entries
|
|
501
|
+
for idx, col in table:
|
|
502
|
+
real_colors[idx] = col
|
|
503
|
+
if len(_TINT_TABLE_CACHE) > 512:
|
|
504
|
+
_TINT_TABLE_CACHE.clear() # bounded: a slider will muck many tints
|
|
505
|
+
_TINT_TABLE_CACHE[cache_key] = table
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
class _TintTableRecorder:
|
|
509
|
+
"""Stand-in for style.colors while set_imgui_tint computes a table:
|
|
510
|
+
records (index, color) so the result can be memoized and replayed."""
|
|
511
|
+
__slots__ = ("entries",)
|
|
512
|
+
|
|
513
|
+
def __init__(self):
|
|
514
|
+
self.entries = []
|
|
515
|
+
|
|
516
|
+
def __setitem__(self, idx, col):
|
|
517
|
+
self.entries.append((idx, style_color(*col)))
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
_TINT_TABLE_CACHE = globals().get("_TINT_TABLE_CACHE", {}) # (r, g, b, cap) → [(idx, color)]
|
|
521
|
+
# (current tint, r, g, b, sat, alpha, factor, cap) → rgba - make_color_rgb
|
|
522
|
+
_MAKE_COLOR_RGB_MEMO = globals().get("_MAKE_COLOR_RGB_MEMO", {})
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Start-up shortcuts for meltygui apps (app.py), each worth tens of
|
|
2
|
+
milliseconds on the first frame. All measured 2026-09-10 on Hyprland with an
|
|
3
|
+
NVIDIA GPU, originally in hdr-viewer's warm_start.py. MELTY_COLD=1 skips them.
|
|
4
|
+
|
|
5
|
+
prepare(cache) call BEFORE ``import glfw``
|
|
6
|
+
remember_glfw_library() call after it
|
|
7
|
+
cache_hinted_atlas(fm) wrap a meltygui FontManager before the renderer builds
|
|
8
|
+
"""
|
|
9
|
+
import hashlib
|
|
10
|
+
import os
|
|
11
|
+
import pathlib
|
|
12
|
+
|
|
13
|
+
# --- glfw shared library -------------------------------------------------------
|
|
14
|
+
# PyGLFW walks ~50 candidate paths and dlopens several before settling on the
|
|
15
|
+
# library (48 ms). PYGLFW_LIBRARY short-circuits that, so cache what it found.
|
|
16
|
+
|
|
17
|
+
def _glfw_cache_file(cache):
|
|
18
|
+
return cache / 'glfw-library'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _hint_glfw_library(cache):
|
|
22
|
+
if os.environ.get('PYGLFW_LIBRARY'):
|
|
23
|
+
return
|
|
24
|
+
try:
|
|
25
|
+
path = _glfw_cache_file(cache).read_text().strip()
|
|
26
|
+
except OSError:
|
|
27
|
+
return
|
|
28
|
+
if path and os.path.isfile(path):
|
|
29
|
+
os.environ['PYGLFW_LIBRARY'] = path
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def remember_glfw_library(cache):
|
|
33
|
+
import meltygui.core.windowing.window_api as glfw
|
|
34
|
+
path = getattr(getattr(glfw, '_glfw', None), '_name', None)
|
|
35
|
+
if not path or not os.path.isabs(path):
|
|
36
|
+
return
|
|
37
|
+
f = _glfw_cache_file(cache)
|
|
38
|
+
try:
|
|
39
|
+
if f.read_text().strip() == path:
|
|
40
|
+
return
|
|
41
|
+
except OSError:
|
|
42
|
+
pass
|
|
43
|
+
_write_atomic(f, path.encode())
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# --- cursor theme --------------------------------------------------------------
|
|
47
|
+
# libwayland-cursor parses EVERY file of the theme when glfw creates the window.
|
|
48
|
+
# Bibata's two animated cursors (wait, progress) are 33 KB each: 130 ms for
|
|
49
|
+
# something a viewer never shows. Point XCURSOR at a shadow theme that symlinks
|
|
50
|
+
# only the theme's small cursors (< 1 MB) and inherits nothing.
|
|
51
|
+
|
|
52
|
+
CURSOR_MAX_BYTES = 1_000_000
|
|
53
|
+
_DEFAULT_CURSOR_PATH = ('~/.icons', '~/.local/share/icons', '/usr/share/icons', '/usr/share/pixmaps')
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _cursor_theme_dir(name):
|
|
57
|
+
dirs = os.environ.get('XCURSOR_PATH', '').split(':') if os.environ.get('XCURSOR_PATH') else _DEFAULT_CURSOR_PATH
|
|
58
|
+
for d in dirs:
|
|
59
|
+
if not d:
|
|
60
|
+
continue
|
|
61
|
+
p = pathlib.Path(d).expanduser() / name / 'cursors'
|
|
62
|
+
if p.is_dir():
|
|
63
|
+
return p
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _shadow_cursor_theme(cache):
|
|
68
|
+
name = os.environ.get('XCURSOR_THEME') or 'default'
|
|
69
|
+
src = _cursor_theme_dir(name)
|
|
70
|
+
if src is None:
|
|
71
|
+
return
|
|
72
|
+
shadow_name = f'meltygui-{name}'
|
|
73
|
+
root = cache / 'cursor-themes'
|
|
74
|
+
theme = root / shadow_name
|
|
75
|
+
cursors = theme / 'cursors'
|
|
76
|
+
try:
|
|
77
|
+
stale = not cursors.is_dir() or cursors.stat().st_mtime < src.stat().st_mtime
|
|
78
|
+
if stale:
|
|
79
|
+
tmp = root / f'{shadow_name}.{os.getpid()}.tmp'
|
|
80
|
+
(tmp / 'cursors').mkdir(parents=True)
|
|
81
|
+
for entry in src.iterdir():
|
|
82
|
+
try:
|
|
83
|
+
if entry.is_file() and entry.stat().st_size < CURSOR_MAX_BYTES:
|
|
84
|
+
(tmp / 'cursors' / entry.name).symlink_to(entry)
|
|
85
|
+
except OSError:
|
|
86
|
+
pass
|
|
87
|
+
(tmp / 'index.theme').write_text(f'[Icon Theme]\nName={shadow_name}\n')
|
|
88
|
+
if theme.exists():
|
|
89
|
+
import shutil
|
|
90
|
+
shutil.rmtree(theme, ignore_errors=True)
|
|
91
|
+
os.rename(tmp, theme)
|
|
92
|
+
except OSError:
|
|
93
|
+
return
|
|
94
|
+
old_path = os.environ.get('XCURSOR_PATH')
|
|
95
|
+
os.environ['XCURSOR_PATH'] = str(root) + (':' + old_path if old_path else ':' + ':'.join(_DEFAULT_CURSOR_PATH))
|
|
96
|
+
os.environ['XCURSOR_THEME'] = shadow_name
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# --- export --------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
def prepare(cache):
|
|
102
|
+
"""Environment tweaks; must run before ``import glfw``. MELTY_COLD=1 skips them."""
|
|
103
|
+
if os.environ.get('MELTY_COLD'):
|
|
104
|
+
return
|
|
105
|
+
try:
|
|
106
|
+
cache.mkdir(parents=True, exist_ok=True)
|
|
107
|
+
except OSError:
|
|
108
|
+
return
|
|
109
|
+
_hint_glfw_library(cache)
|
|
110
|
+
_shadow_cursor_theme(cache)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def cache_hinted_atlas(font_mgr, cache):
|
|
114
|
+
"""FontManager.hint_atlas (FreeType re-rasterising every UI glyph, ~30 ms)
|
|
115
|
+
is a pure function of the stb atlas it is handed, so cache its output on
|
|
116
|
+
disk keyed by those pixels (and meltygui's fonts.py, which holds the algorithm)."""
|
|
117
|
+
if os.environ.get('MELTY_COLD'):
|
|
118
|
+
return
|
|
119
|
+
real = font_mgr.hint_atlas
|
|
120
|
+
try:
|
|
121
|
+
algo = str(os.stat(pathlib.Path(__file__).with_name('fonts.py')).st_mtime_ns).encode()
|
|
122
|
+
except OSError:
|
|
123
|
+
algo = b''
|
|
124
|
+
|
|
125
|
+
def hint_atlas(width, height, pixels):
|
|
126
|
+
key = hashlib.sha1(algo + b'|' + pixels).hexdigest()[:16]
|
|
127
|
+
f = cache / f'atlas-{width}x{height}-{key}.rgba'
|
|
128
|
+
try:
|
|
129
|
+
data = f.read_bytes()
|
|
130
|
+
if len(data) == width * height * 4:
|
|
131
|
+
return data
|
|
132
|
+
except OSError:
|
|
133
|
+
pass
|
|
134
|
+
data = real(width, height, pixels)
|
|
135
|
+
if data is not None:
|
|
136
|
+
_write_atomic(f, data)
|
|
137
|
+
return data
|
|
138
|
+
|
|
139
|
+
font_mgr.hint_atlas = hint_atlas
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _write_atomic(path, data):
|
|
143
|
+
try:
|
|
144
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
145
|
+
tmp = path.with_name(f'{path.name}.{os.getpid()}.tmp')
|
|
146
|
+
tmp.write_bytes(data)
|
|
147
|
+
os.replace(tmp, path)
|
|
148
|
+
except OSError:
|
|
149
|
+
pass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Shared framework windowing machinery."""
|