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,118 @@
|
|
|
1
|
+
"""Query view functions and supporting definitions."""
|
|
2
|
+
from meltygui.core.core_render import render_func
|
|
3
|
+
from meltygui.core.rendering.render_funcs import RenderFuncs
|
|
4
|
+
from meltygui.state.query_state import MCPQueryState
|
|
5
|
+
import meltygui_imgui as imgui
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@render_func(tint=(0.20, 0.60, 0.90), use_cache=True, selectable=False,
|
|
9
|
+
show_bg=False)
|
|
10
|
+
def draw_mcp_query(input_value=None, draw_state=None,
|
|
11
|
+
panel_state: MCPQueryState = None):
|
|
12
|
+
# ── knobs ────────────────────────────────────────────────────────────
|
|
13
|
+
# [tint=(0.95, 0.75, 0.20)]
|
|
14
|
+
from meltygui.view.header_view import flat_button
|
|
15
|
+
from meltygui.core.automation.query_core import _TOOL_ARGS
|
|
16
|
+
from meltygui.core.automation.query_core import _pointer
|
|
17
|
+
from meltygui.core.automation.query_core import _run
|
|
18
|
+
from meltygui.core.automation.query_core import _seed_args
|
|
19
|
+
|
|
20
|
+
button_height = 26 # toolbar buttons; the rows size off this
|
|
21
|
+
# [tint=(0.95, 0.75, 0.20)]
|
|
22
|
+
json_height = 420 # the JSON pane's own scroll box
|
|
23
|
+
# [tint=(0.95, 0.75, 0.20)]
|
|
24
|
+
corner_radius = 5.0
|
|
25
|
+
# [tint=(0.55, 0.80, 0.45)]
|
|
26
|
+
tool_color = (0.16, 0.42, 0.62) # unselected tool buttons + Run
|
|
27
|
+
# [tint=(0.85, 0.45, 0.35)]
|
|
28
|
+
live_color = (0.62, 0.30, 0.16) # follow pointer, while it is on
|
|
29
|
+
icon_run = "" # fa bolt
|
|
30
|
+
icon_tree = "" # fa sitemap
|
|
31
|
+
icon_json = "" # fa code
|
|
32
|
+
icon_pointer = "" # fa mouse-pointer
|
|
33
|
+
|
|
34
|
+
_seed_args(panel_state)
|
|
35
|
+
tool = panel_state.tool
|
|
36
|
+
args = panel_state.args[tool]
|
|
37
|
+
rerun = False
|
|
38
|
+
|
|
39
|
+
# ── one button per collector ─────────────────────────────────────────
|
|
40
|
+
# A selected button is the same colour, lit: same shape, no second color
|
|
41
|
+
# to keep in sync.
|
|
42
|
+
for name in _TOOL_ARGS:
|
|
43
|
+
selected = name == tool
|
|
44
|
+
if flat_button(name, draw_state, f"mcp_tool_{name}", height=button_height,
|
|
45
|
+
color=tool_color, corner_radius=corner_radius,
|
|
46
|
+
tint_value=0.42 if selected else 0.14,
|
|
47
|
+
max_bg_brightness=0.55 if selected else 0.22,
|
|
48
|
+
text_value=1.15 if selected else 0.85):
|
|
49
|
+
panel_state.tool = tool = name
|
|
50
|
+
args = panel_state.args[tool]
|
|
51
|
+
rerun = True
|
|
52
|
+
imgui.same_line()
|
|
53
|
+
|
|
54
|
+
# ── run + how to read the answer ─────────────────────────────────────
|
|
55
|
+
if flat_button(f"{icon_run} Run", draw_state, "mcp_run", height=button_height,
|
|
56
|
+
color=tool_color, corner_radius=corner_radius, tint_value=0.30):
|
|
57
|
+
rerun = True
|
|
58
|
+
imgui.same_line()
|
|
59
|
+
view_label = f"{icon_tree} Tree" if panel_state.as_json else f"{icon_json} JSON"
|
|
60
|
+
if flat_button(view_label, draw_state, "mcp_view_mode", height=button_height,
|
|
61
|
+
color=tool_color, corner_radius=corner_radius, tint_value=0.20):
|
|
62
|
+
panel_state.as_json = not panel_state.as_json
|
|
63
|
+
if tool == "hit_test":
|
|
64
|
+
imgui.same_line()
|
|
65
|
+
following = panel_state.follow_pointer
|
|
66
|
+
if flat_button(f"{icon_pointer} follow pointer", draw_state, "mcp_follow",
|
|
67
|
+
height=button_height, corner_radius=corner_radius,
|
|
68
|
+
color=live_color if following else tool_color,
|
|
69
|
+
tint_value=0.45 if following else 0.18,
|
|
70
|
+
max_bg_brightness=0.60 if following else 0.22):
|
|
71
|
+
panel_state.follow_pointer = following = not following
|
|
72
|
+
rerun = True
|
|
73
|
+
# Following the pointer means re-collecting wherever it goes, which
|
|
74
|
+
# is the `live=True` contract: the body has to run every frame to see
|
|
75
|
+
# a pointer that is nowhere near this window. The collect itself is
|
|
76
|
+
# skipped while the pointer holds steady, so a parked mouse costs one
|
|
77
|
+
# immediate run and nothing else. Off by default - rule 12.
|
|
78
|
+
if following:
|
|
79
|
+
draw_state.invalidate()
|
|
80
|
+
pointer = _pointer()
|
|
81
|
+
if pointer != (args["x"], args["y"]):
|
|
82
|
+
args["x"], args["y"] = pointer
|
|
83
|
+
rerun = True
|
|
84
|
+
|
|
85
|
+
# ── arguments: the dict IS the form ──────────────────────────────────
|
|
86
|
+
changed, _ = RenderFuncs.draw_collection(
|
|
87
|
+
args, name=f"{tool} arguments", width=draw_state.content_width,
|
|
88
|
+
show_bg=True, show_add_delete=False, initial={"expanded": True})
|
|
89
|
+
|
|
90
|
+
# Re-collect on any edit, and whenever the arguments differ from the ones
|
|
91
|
+
# the held answer was collected with (a tool switch, a hot-swapped
|
|
92
|
+
# default). The signature is a repr of a tuple of scalars - never a
|
|
93
|
+
# digest of the ANSWER, which is the expensive half.
|
|
94
|
+
signature = repr((tool, sorted(args.items(), key=lambda item: item[0])))
|
|
95
|
+
if rerun or changed or signature != panel_state._signature:
|
|
96
|
+
_run(panel_state, tool, args)
|
|
97
|
+
panel_state._signature = signature
|
|
98
|
+
|
|
99
|
+
# ── the answer ───────────────────────────────────────────────────────
|
|
100
|
+
if panel_state._stamp:
|
|
101
|
+
imgui.text_disabled(panel_state._stamp)
|
|
102
|
+
if panel_state._error:
|
|
103
|
+
RenderFuncs.draw_text(panel_state._error, name="collector raised",
|
|
104
|
+
show_name=True, focusable=False, wrap=True,
|
|
105
|
+
autocomplete=False, show_jump_bar=False,
|
|
106
|
+
show_file_header=False)
|
|
107
|
+
if panel_state.as_json:
|
|
108
|
+
# focusable=False: this is the tool's output, not a buffer to type in.
|
|
109
|
+
RenderFuncs.draw_text(panel_state._json, name=f"{tool} json",
|
|
110
|
+
show_name=True, height=json_height, focusable=False,
|
|
111
|
+
autocomplete=False, show_jump_bar=False,
|
|
112
|
+
show_file_header=False)
|
|
113
|
+
else:
|
|
114
|
+
RenderFuncs.draw_collection(panel_state._result, name=f"{tool} result",
|
|
115
|
+
width=draw_state.content_width, show_bg=True,
|
|
116
|
+
show_add_delete=False,
|
|
117
|
+
initial={"expanded": True})
|
|
118
|
+
return False, None
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"""Search view functions and supporting definitions."""
|
|
2
|
+
from meltygui.hdr_color import pack_color
|
|
3
|
+
from meltygui.core.melty import Melty
|
|
4
|
+
from meltygui.core.core_render import render_func
|
|
5
|
+
from meltygui.core.runtime.toggles import Toggles
|
|
6
|
+
import meltygui_imgui as imgui
|
|
7
|
+
import math
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@render_func(use_cache=True, layer_offset=1, searchable=False)
|
|
11
|
+
def draw_search(input_value=None, draw_state=None, unique=0):
|
|
12
|
+
"""Floating find pill for searchable views that have no header. Rendered
|
|
13
|
+
as a Mode.WINDOW_CLEAN from core_render when search is active, pinned to
|
|
14
|
+
the owning view's bottom-right corner (the file browser's search pill):
|
|
15
|
+
one row — the search icon, the query box, "n of m" (or "no match"), and
|
|
16
|
+
the close button. Up / Down, Enter / Shift+Enter step the matches (the
|
|
17
|
+
row has no arrow buttons), Esc closes. State — search_text, count,
|
|
18
|
+
current index — lives on the owning view's draw_state (search_owner)."""
|
|
19
|
+
from meltygui.core.windowing.glfw_utils import request_render
|
|
20
|
+
import meltygui.core.windowing.window_api as glfw
|
|
21
|
+
|
|
22
|
+
owner = input_value
|
|
23
|
+
# draw_search(owner, unique=owner._tile_id, draw_state=draw_state)
|
|
24
|
+
search_ds = owner
|
|
25
|
+
regrab_focus = True
|
|
26
|
+
|
|
27
|
+
from meltygui.view.text_view import draw_text
|
|
28
|
+
# Re-grab gated on this search still being active (focused_ds is the owner):
|
|
29
|
+
# a deliberate click away clears focused_ds (via clear_focus) so the box
|
|
30
|
+
# releases focus and stays open-but-unfocused, while a spurious clear during
|
|
31
|
+
# typing leaves focused_ds intact so the box reclaims focus.
|
|
32
|
+
focus_search = ((not search_ds._search_was_active)
|
|
33
|
+
or (regrab_focus and Melty.text_focused_ds is None
|
|
34
|
+
and Melty.focused_ds is search_ds)
|
|
35
|
+
or search_ds._search_focus_pending)
|
|
36
|
+
# One-shot open/Ctrl+F frame (NOT the spurious-clear regrab): select the
|
|
37
|
+
# whole term so typing replaces, and a single delete clears it.
|
|
38
|
+
focus_fresh = ((not search_ds._search_was_active)
|
|
39
|
+
or search_ds._search_focus_pending)
|
|
40
|
+
search_ds._search_focus_pending = False
|
|
41
|
+
search_ds._search_was_active = True
|
|
42
|
+
search_icon = ""
|
|
43
|
+
imgui.align_text_to_frame_padding()
|
|
44
|
+
imgui.text(search_icon)
|
|
45
|
+
imgui.same_line()
|
|
46
|
+
x_width = 30
|
|
47
|
+
count_width = 84
|
|
48
|
+
box_width, _pill_w = search_pill_layout(search_ds.search_text, search_ds.width)
|
|
49
|
+
|
|
50
|
+
# Laid out left to right at fixed offsets (never off the pill's own
|
|
51
|
+
# width - core_render sizes the window to search_pill_layout too). The
|
|
52
|
+
# box draws no background of its own: the query sits flat on the pill.
|
|
53
|
+
box_left, row_top = imgui.get_cursor_screen_pos()
|
|
54
|
+
_box = draw_text(search_ds.search_text, searchable=False, is_search_box=True,
|
|
55
|
+
width=box_width, max_width=box_width, show_bg=False,
|
|
56
|
+
shadow=False, name=search_icon + str(unique),
|
|
57
|
+
with_header_end=None, wrap=False, z_offset=-1, single_line=True,
|
|
58
|
+
with_footer=None, tint=search_ds.tint,
|
|
59
|
+
show_name=False, show_header=False,
|
|
60
|
+
request_focus=focus_search, select_all_on_focus=focus_fresh,
|
|
61
|
+
return_extras=True)
|
|
62
|
+
search_change, new_search = _box[0], _box[1]
|
|
63
|
+
_box_ds = _box[2] if len(_box) > 2 else None
|
|
64
|
+
# While the find box holds text focus, mark this search as the active one so
|
|
65
|
+
# Enter/arrow nav goes here - including after clicking back into the box.
|
|
66
|
+
if _box_ds is not None and Melty.text_focused_ds is _box_ds:
|
|
67
|
+
Melty.focused_ds = search_ds
|
|
68
|
+
if search_change:
|
|
69
|
+
search_ds.search_text = new_search
|
|
70
|
+
# Re-render the owner's whole subtree so every child view recomputes its
|
|
71
|
+
# matches against the new term and the combined index stays in sync.
|
|
72
|
+
Melty.cache.invalidate_up(search_ds._tile_id, force=True, max_depth=12)
|
|
73
|
+
request_render()
|
|
74
|
+
# The count, on the same row: "n of m" while there are matches, "no
|
|
75
|
+
# match" for a term that finds nothing, populated by the owner's pre-body
|
|
76
|
+
# search walk (core_render). The keys below step the current match.
|
|
77
|
+
total = search_ds.text_search_count
|
|
78
|
+
imgui.same_line()
|
|
79
|
+
count_left = box_left + box_width + 10
|
|
80
|
+
imgui.set_cursor_screen_pos((count_left, imgui.get_cursor_screen_pos()[1]))
|
|
81
|
+
imgui.align_text_to_frame_padding()
|
|
82
|
+
if total > 0:
|
|
83
|
+
imgui.text_colored(f"{search_ds.text_search_current + 1} of {total}",
|
|
84
|
+
0.62, 0.68, 0.76, 1.0)
|
|
85
|
+
elif search_ds.search_text:
|
|
86
|
+
imgui.text_colored("no match", 0.95, 0.55, 0.5, 1.0)
|
|
87
|
+
else:
|
|
88
|
+
imgui.text_colored("", 0.62, 0.68, 0.76, 1.0)
|
|
89
|
+
|
|
90
|
+
imgui.same_line()
|
|
91
|
+
fa_x_icon = ""
|
|
92
|
+
|
|
93
|
+
# The close button: a glyph, dim until hovered, with a click rect on
|
|
94
|
+
# the pill (no button chrome).
|
|
95
|
+
close_left = count_left + count_width
|
|
96
|
+
row_h = max(imgui.get_frame_height(), 24.0)
|
|
97
|
+
close_rect = (close_left, row_top, close_left + x_width, row_top + row_h)
|
|
98
|
+
_mx, _my = imgui.get_mouse_pos()
|
|
99
|
+
_close_hover = (close_rect[0] <= _mx < close_rect[2] and close_rect[1] <= _my < close_rect[3]
|
|
100
|
+
and draw_state._bounding_hovered)
|
|
101
|
+
_glyph_w = imgui.calc_text_size(fa_x_icon).x
|
|
102
|
+
imgui.get_window_draw_list().add_text(
|
|
103
|
+
close_left + (x_width - _glyph_w) * 0.5, row_top + (row_h - imgui.get_font_size()) * 0.5,
|
|
104
|
+
pack_color(1.0, 1.0, 1.0, 0.9 if _close_hover else 0.35), fa_x_icon)
|
|
105
|
+
imgui.set_cursor_screen_pos((close_left, row_top))
|
|
106
|
+
imgui.dummy(x_width, row_h)
|
|
107
|
+
if draw_state.on_action("left_mouse_down", view_id=f"find_close{unique}",
|
|
108
|
+
rect=close_rect, priority_delta=4) is not None:
|
|
109
|
+
search_ds.search_active = False
|
|
110
|
+
search_ds._search_was_active = False
|
|
111
|
+
# Keep search_text so reopening the find bar restores the last query.
|
|
112
|
+
Melty.text_focused_ds = None
|
|
113
|
+
request_render()
|
|
114
|
+
|
|
115
|
+
if total > 0:
|
|
116
|
+
nav = 0
|
|
117
|
+
# Enter / Down = find next, Shift+Enter / Up = find prev, Ctrl+Enter =
|
|
118
|
+
# "click" the selected result - but only while the FIND BOX (not the
|
|
119
|
+
# underlying editor) holds text focus, so Enter still inserts newlines
|
|
120
|
+
# when you click into the editor. We gate on the box holding text focus
|
|
121
|
+
# directly rather than on `focused_ds is search_ds`: clicking back into
|
|
122
|
+
# the box runs clear_focus, which nulls focused_ds (the searched view
|
|
123
|
+
# isn't under the mouse to be protected), so keying off focused_ds
|
|
124
|
+
# silently dropped Enter-nav after a mouse refocus. text_focused_ds is
|
|
125
|
+
# set straight by the box's own key handler, so it survives that.
|
|
126
|
+
# The find box is single-line, so Up/Down don't move its cursor and are
|
|
127
|
+
# free for stepping matches. Drained from the frame-callback key queue
|
|
128
|
+
# (not imgui.is_key_pressed) so it isn't dropped on slow frames.
|
|
129
|
+
if _box_ds is not None and Melty.text_focused_ds is _box_ds:
|
|
130
|
+
if any(k == glfw.KEY_DOWN for k, _ in Melty.frame_key_events):
|
|
131
|
+
nav = 1
|
|
132
|
+
elif any(k == glfw.KEY_UP for k, _ in Melty.frame_key_events):
|
|
133
|
+
nav = -1
|
|
134
|
+
# Enter steps to the next match, Shift+Enter to the previous, and
|
|
135
|
+
# HOLDING Enter rapid-fires - imgui's synthesized auto-repeat
|
|
136
|
+
# (io.key_repeat_delay/rate) is read here because GLFW's REPEAT events
|
|
137
|
+
# don't reach the key queue on every platform (Wayland); keep the loop
|
|
138
|
+
# rendering while it is held so that cadence is sampled. Ctrl+Enter
|
|
139
|
+
# "clicks" the selected result and stays single-shot (from the queue).
|
|
140
|
+
_enter = [m for k, m in Melty.frame_key_events
|
|
141
|
+
if k in (glfw.KEY_ENTER, glfw.KEY_KP_ENTER)]
|
|
142
|
+
_enter_repeat = (imgui.is_key_pressed(glfw.KEY_ENTER, repeat=True)
|
|
143
|
+
or imgui.is_key_pressed(glfw.KEY_KP_ENTER, repeat=True))
|
|
144
|
+
if imgui.is_key_down(glfw.KEY_ENTER) or imgui.is_key_down(glfw.KEY_KP_ENTER):
|
|
145
|
+
request_render()
|
|
146
|
+
if _enter or _enter_repeat:
|
|
147
|
+
if _enter and (_enter[-1] & glfw.MOD_CONTROL):
|
|
148
|
+
# "Click" the selected result: hit-test the BVH at the center
|
|
149
|
+
# of the current match's rect and send a mouse down to the
|
|
150
|
+
# front-most view there (the actual target, e.g. a managed
|
|
151
|
+
# window's name label) - exactly what a real click resolves
|
|
152
|
+
# to. The view's own click handling does the rest (toggle a
|
|
153
|
+
# window, focus an input, ...). Queued + the view invalidated so
|
|
154
|
+
# it re-renders and reads the click next frame (the find UI
|
|
155
|
+
# renders too late to inject for this frame).
|
|
156
|
+
from meltygui.core.automation.search_core import search_activate_target
|
|
157
|
+
from meltygui.core.input.input_handler import InputEvent
|
|
158
|
+
_target = search_activate_target(Melty.search_current_node)
|
|
159
|
+
if _target is not None and _target.width and _target.height:
|
|
160
|
+
_cx = _target.abs_left + _target.width / 2.0
|
|
161
|
+
_cy = _target.abs_top + _target.height / 2.0
|
|
162
|
+
_hits = [h for h in Melty.bvh_query(_cx, _cy)
|
|
163
|
+
if h._tile_id is not None and not h.just_shadow]
|
|
164
|
+
_top = _hits[0] if _hits else _target
|
|
165
|
+
Melty.search_click_pending = (
|
|
166
|
+
_top._tile_id,
|
|
167
|
+
InputEvent("left_mouse", "down", tile_id=_top._tile_id, x=_cx, y=_cy))
|
|
168
|
+
# Re-render the owner's subtree next frame (same as nav) so
|
|
169
|
+
# the top view actually re-runs and reads the injection.
|
|
170
|
+
Melty.cache.invalidate_up(search_ds._tile_id, force=True, max_depth=12)
|
|
171
|
+
request_render()
|
|
172
|
+
elif not imgui.get_io().key_ctrl:
|
|
173
|
+
nav = -1 if imgui.get_io().key_shift else 1
|
|
174
|
+
|
|
175
|
+
if nav != 0:
|
|
176
|
+
# total is the combined count across all views; stepping wraps over
|
|
177
|
+
# the whole result set. Flag a scroll and invalidate the owner's
|
|
178
|
+
# subtree so every child view recomputes and the one holding the new
|
|
179
|
+
# global-current match scrolls to it.
|
|
180
|
+
search_ds.text_search_current = (search_ds.text_search_current + nav) % total
|
|
181
|
+
search_ds._search_nav_pending = True
|
|
182
|
+
Melty.cache.invalidate_up(search_ds._tile_id, force=True, max_depth=12)
|
|
183
|
+
request_render()
|
|
184
|
+
elif search_ds.search_text:
|
|
185
|
+
# Enter with the find box focused force-recomputes the result set. "No
|
|
186
|
+
# results" can be stale - the searched views may have been rebuilt since
|
|
187
|
+
# the count was last done (e.g. a fresh load from disk) - so re-run the
|
|
188
|
+
# sub-view walk on demand rather than leaving it stuck at zero. Flagging
|
|
189
|
+
# _search_nav_pending makes the owner's pre-body walk re-count next frame
|
|
190
|
+
# (same key source + box-focus gate as the nav block above).
|
|
191
|
+
if _box_ds is not None and Melty.text_focused_ds is _box_ds:
|
|
192
|
+
if any(k in (glfw.KEY_ENTER, glfw.KEY_KP_ENTER)
|
|
193
|
+
for k, _ in Melty.frame_key_events):
|
|
194
|
+
search_ds._search_nav_pending = True
|
|
195
|
+
Melty.cache.invalidate_up(search_ds._tile_id, force=True, max_depth=12)
|
|
196
|
+
request_render()
|
|
197
|
+
else:
|
|
198
|
+
imgui.align_text_to_frame_padding()
|
|
199
|
+
imgui.text_colored("", 0.74, 0.5, 0.5, 1.0)
|
|
200
|
+
|
|
201
|
+
if not input_value.search_active:
|
|
202
|
+
draw_state.closed = True
|
|
203
|
+
|
|
204
|
+
return False, input_value
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def draw_search_highlight_multi(draw_list, segs, *, current):
|
|
208
|
+
"""Highlight a match made of per-line segments (a multi-line search term).
|
|
209
|
+
One glow radiates around the segments' BOUNDING box — per-segment glows
|
|
210
|
+
overlap into an unreadable blob — while each segment keeps its own thin
|
|
211
|
+
outline so the exact matched text stays delineated."""
|
|
212
|
+
|
|
213
|
+
if len(segs) == 1:
|
|
214
|
+
x0, y0, x1, y1 = segs[0]
|
|
215
|
+
draw_search_highlight(draw_list, x0, y0, x1, y1, current=current)
|
|
216
|
+
return
|
|
217
|
+
# Same pixel-snap as draw_search_highlight - see the comment there.
|
|
218
|
+
segs = [(round(x0), round(y0), round(x1), round(y1))
|
|
219
|
+
for (x0, y0, x1, y1) in segs]
|
|
220
|
+
spec = (Toggles.SearchSettings.ActiveElement if current
|
|
221
|
+
else Toggles.SearchSettings.InactiveElements)
|
|
222
|
+
bx0 = min(s[0] for s in segs)
|
|
223
|
+
bx1 = max(s[2] for s in segs)
|
|
224
|
+
_draw_glow(draw_list, bx0, segs[0][1], bx1, segs[-1][3], spec)
|
|
225
|
+
a = float(spec.outline_alpha)
|
|
226
|
+
if a > 0.0:
|
|
227
|
+
oc = spec.outline_color
|
|
228
|
+
col = pack_color(oc[0], oc[1], oc[2], a)
|
|
229
|
+
for x0, y0, x1, y1 in segs:
|
|
230
|
+
draw_list.add_rect(x0, y0, x1, y1, col,
|
|
231
|
+
rounding=float(spec.cutout_radius),
|
|
232
|
+
thickness=float(spec.outline_thickness))
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def draw_search_highlight(draw_list, x0, y0, x1, y1, *, current, rounding=0.0):
|
|
236
|
+
"""Highlight a search match with its glow (rect cut out) plus an optional
|
|
237
|
+
thin outline. The current match uses the ActiveElement spec; the rest use
|
|
238
|
+
InactiveElements, so both states are tuned independently."""
|
|
239
|
+
|
|
240
|
+
# Pixel-snap: the rect usually derives from the live flow cursor at bake
|
|
241
|
+
# time, and sub-pixel drift between bakes makes the gradient triangles
|
|
242
|
+
# shimmer (glyphs don't - they're pixel-snapped). Rounding pins the whole
|
|
243
|
+
# halo to the pixel grid, so successive bakes bake identically.
|
|
244
|
+
x0, y0, x1, y1 = round(x0), round(y0), round(x1), round(y1)
|
|
245
|
+
spec = (Toggles.SearchSettings.ActiveElement if current
|
|
246
|
+
else Toggles.SearchSettings.InactiveElements)
|
|
247
|
+
_draw_glow(draw_list, x0, y0, x1, y1, spec)
|
|
248
|
+
a = float(spec.outline_alpha)
|
|
249
|
+
if a > 0.0:
|
|
250
|
+
oc = spec.outline_color
|
|
251
|
+
draw_list.add_rect(x0, y0, x1, y1, pack_color(oc[0], oc[1], oc[2], a),
|
|
252
|
+
rounding=max(float(rounding), float(spec.cutout_radius)),
|
|
253
|
+
thickness=float(spec.outline_thickness))
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def search_pill_layout(term, owner_width=None):
|
|
258
|
+
"""The find pill's geometry for `term`: ``(box_width, window_width)``.
|
|
259
|
+
The query box grows with the term (a long term stays readable instead
|
|
260
|
+
of scrolling inside a fixed box) from a 200px floor up to what the
|
|
261
|
+
owning view's width leaves for it (`owner_width` minus the pill's
|
|
262
|
+
other parts and a margin), and the window is the row: pad, icon, box,
|
|
263
|
+
gap, count, close, pad. Shared by draw_search (the row) and
|
|
264
|
+
core_render (the window it floats in), so the two always agree."""
|
|
265
|
+
text_w = imgui.calc_text_size(term or "").x
|
|
266
|
+
cap = 520.0 if owner_width is None else max(200.0, owner_width - 220.0)
|
|
267
|
+
box = max(200.0, min(cap, text_w + 40.0))
|
|
268
|
+
return box, box + 158.0
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _perimeter_samples(x0, y0, x1, y1, cseg, r):
|
|
272
|
+
"""Walk a rounded-rect perimeter clockwise as (base_x, base_y, nx, ny).
|
|
273
|
+
|
|
274
|
+
`r` is the cutout corner radius. The corners are quarter-arc fans of `cseg`
|
|
275
|
+
segments centred on the rect's inset corners, so each sample already sits on
|
|
276
|
+
the rounded cutout; offsetting it by ``base + normal * d`` grows the rounded
|
|
277
|
+
rect by `d` (the corner radius becomes ``r + d``). The straight edges need no
|
|
278
|
+
interior samples: the single quad spanning one corner's exit sample to the
|
|
279
|
+
next corner's entry sample (same outward normal) is geometrically exact.
|
|
280
|
+
"""
|
|
281
|
+
r = max(0.0, min(r, (x1 - x0) * 0.5, (y1 - y0) * 0.5))
|
|
282
|
+
half = math.pi / 2.0
|
|
283
|
+
# (inset corner centre, start angle, end angle) - screen space, y points
|
|
284
|
+
# down, so angle 0 = +x (right), +pi/2 = down, -pi/2 = up.
|
|
285
|
+
corners = (
|
|
286
|
+
((x1 - r, y0 + r), -half, 0.0), # top-right: up -> right
|
|
287
|
+
((x1 - r, y1 - r), 0.0, half), # bottom-right: right -> down
|
|
288
|
+
((x0 + r, y1 - r), half, math.pi), # bottom-left: down -> left
|
|
289
|
+
((x0 + r, y0 + r), math.pi, math.pi + half), # top-left: left -> up
|
|
290
|
+
)
|
|
291
|
+
out = []
|
|
292
|
+
for (cx, cy), a0, a1 in corners:
|
|
293
|
+
for s in range(cseg + 1):
|
|
294
|
+
ang = a0 + (a1 - a0) * (s / cseg)
|
|
295
|
+
nx, ny = math.cos(ang), math.sin(ang)
|
|
296
|
+
out.append((cx + nx * r, cy + ny * r, nx, ny))
|
|
297
|
+
return out
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _draw_glow(draw_list, x0, y0, x1, y1, spec):
|
|
301
|
+
"""Radial gradient halo around (x0,y0,x1,y1) with a rounded rect cut out."""
|
|
302
|
+
falloff = float(spec.falloff)
|
|
303
|
+
opacity = float(spec.opacity)
|
|
304
|
+
if falloff <= 0.0 or opacity <= 0.0:
|
|
305
|
+
return
|
|
306
|
+
|
|
307
|
+
cr, cg, cb = spec.gradient_color
|
|
308
|
+
rings = max(1, int(spec.rings))
|
|
309
|
+
exp = float(spec.falloff_exp)
|
|
310
|
+
pad = float(spec.inner_pad)
|
|
311
|
+
cseg = max(1, int(spec.corner_segments))
|
|
312
|
+
radius = float(spec.cutout_radius)
|
|
313
|
+
|
|
314
|
+
samples = _perimeter_samples(x0 - pad, y0 - pad, x1 + pad, y1 + pad, cseg, radius)
|
|
315
|
+
n = len(samples)
|
|
316
|
+
|
|
317
|
+
# Anti-aliased fill feathers every triangle edge, which leaves visible seams
|
|
318
|
+
# where the gradient triangles meet; turn it off for a clean radial gradient
|
|
319
|
+
# (same trick the swoosh ribbon uses), then restore the previous flags.
|
|
320
|
+
flags = draw_list.flags
|
|
321
|
+
draw_list.flags = flags & ~imgui.DRAW_LIST_ANTI_ALIASED_FILL
|
|
322
|
+
try:
|
|
323
|
+
inner = [(bx, by) for (bx, by, _nx, _ny) in samples] # cutout edge, d=0
|
|
324
|
+
a_inner = opacity
|
|
325
|
+
for k in range(1, rings + 1):
|
|
326
|
+
t = k / rings
|
|
327
|
+
d = t * falloff
|
|
328
|
+
a_outer = opacity * (1.0 - t) ** exp
|
|
329
|
+
outer = [(bx + nx * d, by + ny * d) for (bx, by, nx, ny) in samples]
|
|
330
|
+
col = pack_color(cr, cg, cb, (a_inner + a_outer) * 0.5)
|
|
331
|
+
for i in range(n):
|
|
332
|
+
j = i + 1 if i + 1 < n else 0
|
|
333
|
+
ix0, iy0 = inner[i]
|
|
334
|
+
ix1, iy1 = inner[j]
|
|
335
|
+
ox0, oy0 = outer[i]
|
|
336
|
+
ox1, oy1 = outer[j]
|
|
337
|
+
draw_list.add_triangle_filled(ix0, iy0, ix1, iy1, ox1, oy1, col)
|
|
338
|
+
draw_list.add_triangle_filled(ix0, iy0, ox1, oy1, ox0, oy0, col)
|
|
339
|
+
inner = outer
|
|
340
|
+
a_inner = a_outer
|
|
341
|
+
finally:
|
|
342
|
+
draw_list.flags = flags
|