From d6a4adf6aaed5957a4c47ee3c0767b78d959ab20 Mon Sep 17 00:00:00 2001 From: Leroy Hopson Date: Sun, 14 Apr 2024 20:28:26 +1200 Subject: [PATCH] feat(terminal): and stylebox support Adds support for 'normal' and 'focus' Style Boxes to Terminal node. Changes default background color to transparent, with background to be set by StyleBox. If background color is not transparent, will draw a background color rect to cover the entire control over the top of any stylebox. This is consistent with the behavior of the TextEdit node with regards to theme colors and styleboxes. --- Justfile | 3 + .../terminal/editor_terminal.gd | 3 + .../terminal/editor_terminal.tscn | 8 +-- .../editor_plugins/terminal/terminal_panel.gd | 8 +-- .../terminal/terminal_panel.tscn | 10 ++- addons/godot_xterm/native/src/terminal.cpp | 66 ++++++++++++++++-- addons/godot_xterm/native/src/terminal.h | 6 ++ addons/godot_xterm/shaders/common.gdshaderinc | 15 ++-- docs/{_static => _build}/.gdignore | 0 .../images/button_live_demo.png.import | 34 +++++++++ .../button_view_documentation.png.import | 34 +++++++++ .../button_view_it_on_github.png.import | 34 +++++++++ docs/_static/images/diagram_flow.svg.import | 37 ++++++++++ docs/_static/images/diagram_pty.png.import | 34 +++++++++ docs/_static/images/diagram_pty.svg.import | 37 ++++++++++ docs/_static/images/icon.png.import | 34 +++++++++ docs/_static/images/logo.png.import | 34 +++++++++ docs/_static/images/logo.svg.import | 37 ++++++++++ .../images/screenshot_editor.png.import | 34 +++++++++ .../images/screenshot_retro_term.jpg.import | 34 +++++++++ .../images/screenshot_the_guest.png.import | 34 +++++++++ .../images/screenshot_xterminate.png.import | 34 +++++++++ docs/demo/.gdignore | 0 export_presets.cfg | 2 +- test/test_terminal.gd | 6 +- test/visual_regression/background.tscn | 13 ++++ .../baseline/blue_background.png | Bin 0 -> 479 bytes .../baseline/default_theme.png | Bin 822 -> 984 bytes test/visual_regression/baseline/emoji.png | Bin 11478 -> 11585 bytes test/visual_regression/baseline/empty.png | Bin 368 -> 503 bytes .../baseline/solid_invert_selection.png | Bin 0 -> 548 bytes .../baseline/solid_swap_selection.png | Bin 0 -> 560 bytes .../baseline/transparency.png | Bin 4019 -> 12398 bytes .../baseline/transparent_invert_selection.png | Bin 0 -> 7072 bytes .../baseline/transparent_swap_selection.png | Bin 0 -> 6572 bytes .../test_visual_regression.gd | 38 ++++++++++ theme_map.gd | 22 ------ themes/demo.tres | 5 +- themes/demo_full.tres | 4 +- themes/normal.stylebox | Bin 0 -> 443 bytes 40 files changed, 608 insertions(+), 52 deletions(-) rename docs/{_static => _build}/.gdignore (100%) create mode 100644 docs/_static/images/button_live_demo.png.import create mode 100644 docs/_static/images/button_view_documentation.png.import create mode 100644 docs/_static/images/button_view_it_on_github.png.import create mode 100644 docs/_static/images/diagram_flow.svg.import create mode 100644 docs/_static/images/diagram_pty.png.import create mode 100644 docs/_static/images/diagram_pty.svg.import create mode 100644 docs/_static/images/icon.png.import create mode 100644 docs/_static/images/logo.png.import create mode 100644 docs/_static/images/logo.svg.import create mode 100644 docs/_static/images/screenshot_editor.png.import create mode 100644 docs/_static/images/screenshot_retro_term.jpg.import create mode 100644 docs/_static/images/screenshot_the_guest.png.import create mode 100644 docs/_static/images/screenshot_xterminate.png.import create mode 100644 docs/demo/.gdignore create mode 100644 test/visual_regression/background.tscn create mode 100644 test/visual_regression/baseline/blue_background.png create mode 100644 test/visual_regression/baseline/solid_invert_selection.png create mode 100644 test/visual_regression/baseline/solid_swap_selection.png create mode 100644 test/visual_regression/baseline/transparent_invert_selection.png create mode 100644 test/visual_regression/baseline/transparent_swap_selection.png delete mode 100644 theme_map.gd create mode 100644 themes/normal.stylebox diff --git a/Justfile b/Justfile index 0a1f8d9..7114a3b 100644 --- a/Justfile +++ b/Justfile @@ -27,5 +27,8 @@ test-all: test-rendering: {{godot}} --windowed --resolution 400x200 --position 0,0 -s addons/gut/gut_cmdln.gd -gtest=res://test/test_rendering.gd -gopacity=0 -gexit +test-visual: + {{godot}} --windowed --resolution 400x200 --position 0,0 -s addons/gut/gut_cmdln.gd -gdir=res://test/visual_regression/ -ginclude_subdirs=true -gopacity=0 -gexit + uninstall: {{godot}} --headless -s plug.gd uninstall diff --git a/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd b/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd index 437e2e7..089ec75 100644 --- a/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd +++ b/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd @@ -22,6 +22,9 @@ func _ready(): if not editor_settings: return + add_theme_stylebox_override("normal", get_theme_stylebox("normal", "Tree")) + add_theme_stylebox_override("focus", get_theme_stylebox("focus", "Tree")) + # Get colors from TextEdit theme. Created using the default (Adaptive) theme # for reference, but will probably cause strange results if using another theme # better to use a dedicated terminal theme, rather than relying on this. diff --git a/addons/godot_xterm/editor_plugins/terminal/editor_terminal.tscn b/addons/godot_xterm/editor_plugins/terminal/editor_terminal.tscn index 15a3ab8..be52423 100644 --- a/addons/godot_xterm/editor_plugins/terminal/editor_terminal.tscn +++ b/addons/godot_xterm/editor_plugins/terminal/editor_terminal.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=3 format=3 uid="uid://bkcyv0w3setep"] [ext_resource type="Script" path="res://addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd" id="1"] -[ext_resource type="Theme" uid="uid://0gk8swmcldbg" path="res://themes/demo.tres" id="1_h7osn"] +[ext_resource type="Theme" uid="uid://0gk8swmcldbg" path="res://themes/demo.tres" id="1_htj4w"] [node name="Terminal" type="Terminal"] anchors_preset = 15 @@ -12,12 +12,12 @@ grow_vertical = 2 size_flags_horizontal = 4 size_flags_vertical = 4 focus_mode = 1 -theme = ExtResource("1_h7osn") +theme = ExtResource("1_htj4w") script = ExtResource("1") [node name="PTY" type="PTY" parent="."] -cols = 128 -rows = 32 +cols = 126 +rows = 31 terminal_path = NodePath("..") [node name="Bell" type="AudioStreamPlayer" parent="."] diff --git a/addons/godot_xterm/editor_plugins/terminal/terminal_panel.gd b/addons/godot_xterm/editor_plugins/terminal/terminal_panel.gd index 0b71d1c..b705d22 100644 --- a/addons/godot_xterm/editor_plugins/terminal/terminal_panel.gd +++ b/addons/godot_xterm/editor_plugins/terminal/terminal_panel.gd @@ -46,9 +46,6 @@ var _tab_container_min_size func _ready(): - tab_container.add_theme_stylebox_override( - "panel", get_theme_stylebox("background", "EditorStyles") - ) _update_settings() @@ -140,8 +137,9 @@ func _on_AddButton_pressed(): func _on_Tabs_tab_changed(tab_index): - tab_container.current_tab = tab_index - tab_container.get_child(tab_index).grab_focus() + tab_container.call_deferred("set_current_tab", tab_index) + await get_tree().process_frame + tab_container.get_current_tab_control().grab_focus() func _on_Tabs_tab_close(tab_index): diff --git a/addons/godot_xterm/editor_plugins/terminal/terminal_panel.tscn b/addons/godot_xterm/editor_plugins/terminal/terminal_panel.tscn index 809869a..3af4427 100644 --- a/addons/godot_xterm/editor_plugins/terminal/terminal_panel.tscn +++ b/addons/godot_xterm/editor_plugins/terminal/terminal_panel.tscn @@ -1,8 +1,8 @@ -[gd_scene load_steps=4 format=3 uid="uid://cbxovnvw5o4mo"] +[gd_scene load_steps=5 format=3 uid="uid://cbxovnvw5o4mo"] [ext_resource type="Script" path="res://addons/godot_xterm/editor_plugins/terminal/terminal_panel.gd" id="1"] -[sub_resource type="Image" id="Image_4ja3e"] +[sub_resource type="Image" id="Image_knxas"] data = { "data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 0, 224, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", @@ -12,7 +12,10 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_q1uu0"] -image = SubResource("Image_4ja3e") +image = SubResource("Image_knxas") + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_osmrc"] +bg_color = Color(0.113329, 0.129458, 0.156802, 1) [node name="Panel" type="Panel"] anchors_preset = 15 @@ -59,6 +62,7 @@ item_3/id = 3 clip_contents = true layout_mode = 2 size_flags_vertical = 3 +theme_override_styles/panel = SubResource("StyleBoxFlat_osmrc") tabs_visible = false [node name="TerminalPopupMenu" type="PopupMenu" parent="VBoxContainer"] diff --git a/addons/godot_xterm/native/src/terminal.cpp b/addons/godot_xterm/native/src/terminal.cpp index 5ffb2e7..3470004 100644 --- a/addons/godot_xterm/native/src/terminal.cpp +++ b/addons/godot_xterm/native/src/terminal.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -204,6 +206,12 @@ void Terminal::_notification(int what) update_sizes(true); break; } + case NOTIFICATION_FOCUS_ENTER: + case NOTIFICATION_FOCUS_EXIT: + { + refresh(); + break; + } case NOTIFICATION_THEME_CHANGED: { update_theme(); @@ -277,8 +285,15 @@ int Terminal::_draw_cb(struct tsm_screen *con, ? term->palette[attr->bccode] : Color(attr->br / 255.0f, attr->bg / 255.0f, attr->bb / 255.0f); - if (attr->inverse && term->inverse_mode == InverseMode::INVERSE_MODE_SWAP) - std::swap(fgcol, bgcol); + if (attr->inverse && term->inverse_mode == InverseMode::INVERSE_MODE_SWAP) { + std::swap(fgcol.r, bgcol.r); + std::swap(fgcol.g, bgcol.g); + std::swap(fgcol.b, bgcol.b); + bgcol.a = term->palette[TSM_COLOR_BACKGROUND].a; + } + + if (bgcol == term->palette[TSM_COLOR_BACKGROUND]) + bgcol.a = 0; // Update images (accounting for ultra-wide characters). for (int i = 0; i < width && (posx + i) < term->cols; i++) { @@ -425,6 +440,10 @@ void Terminal::update_sizes(bool force) unsigned int prev_rows = rows; size = get_size(); + size.x -= (style_normal->get_margin(SIDE_LEFT) + style_normal->get_margin(SIDE_RIGHT)); + size.y -= (style_normal->get_margin(SIDE_TOP) + style_normal->get_margin(SIDE_BOTTOM)); + size.x = std::max(size.x, 1.0f); + size.y = std::max(size.y, 1.0f); Ref font = fonts[FontType::NORMAL]; font_size = get_theme_font_size("font_size"); @@ -439,6 +458,10 @@ void Terminal::update_sizes(bool force) if (!force && size == prev_size && font_size == prev_font_size && cell_size == prev_cell_size && cols == prev_cols && rows == prev_rows) return; + Transform2D transform = Transform2D(0, Vector2(style_normal->get_margin(SIDE_LEFT), style_normal->get_margin(SIDE_TOP))); + rs->canvas_item_set_transform(back_canvas_item, transform); + rs->canvas_item_set_transform(fore_canvas_item, transform); + tsm_screen_resize(screen, cols, rows); rs->viewport_set_size(viewport, size.x, size.y); @@ -472,6 +495,12 @@ void Terminal::initialize_rendering() { rs = RenderingServer::get_singleton(); attr_texture.instantiate(); + // StyleBox. + + style_canvas_item = rs->canvas_item_create(); + rs->canvas_item_set_parent(style_canvas_item, get_canvas_item()); + rs->canvas_item_set_draw_behind_parent(style_canvas_item, true); + // Background. back_texture.instantiate(); @@ -533,7 +562,6 @@ void Terminal::update_theme() { tsm_vte_color color = static_cast(i); palette[color] = get_theme_color(String(COLOR_NAMES[i])); } - back_material->set_shader_parameter("background_color", palette[TSM_COLOR_BACKGROUND]); // Update fonts. for (int i = FontType::NORMAL; i <= FontType::BOLD_ITALICS; i++) { @@ -541,6 +569,17 @@ void Terminal::update_theme() { fonts[type] = has_theme_font(FONT_TYPES[type]) ? get_theme_font(FONT_TYPES[type]) : get_theme_font(FONT_TYPES[FontType::NORMAL]); } + // Update styles. + style_normal = get_theme_stylebox("normal"); + style_focus = get_theme_stylebox("focus"); + + if (dynamic_cast(style_normal.ptr()) != nullptr) { + // Blend the background color with the style box's background color to get the "true" background color. + Color style_background_color = style_normal->get("bg_color"); + palette[TSM_COLOR_BACKGROUND] = style_background_color.blend(palette[TSM_COLOR_BACKGROUND]); + } + back_material->set_shader_parameter("background_color", palette[TSM_COLOR_BACKGROUND]); + refresh(); } @@ -557,9 +596,18 @@ void Terminal::draw_screen() { rs->viewport_set_clear_mode(viewport, RenderingServer::ViewportClearMode::VIEWPORT_CLEAR_ONLY_NEXT_FRAME); + Color bgcol = palette[TSM_COLOR_BACKGROUND]; + + rs->canvas_item_clear(style_canvas_item); + style_normal->draw(style_canvas_item, get_rect()); + if (has_focus()) + style_focus->draw(style_canvas_item, get_rect()); + if (get_theme_color("background_color").a > 0) + rs->canvas_item_add_rect(style_canvas_item, get_rect(), bgcol); + rs->canvas_item_clear(back_canvas_item); - rs->canvas_item_add_rect(back_canvas_item, rect, palette[TSM_COLOR_BACKGROUND]); - back_image->fill(palette[TSM_COLOR_BACKGROUND]); + rs->canvas_item_add_rect(back_canvas_item, rect, bgcol); + back_image->fill(bgcol); rs->canvas_item_clear(fore_canvas_item); rs->canvas_item_add_texture_rect(fore_canvas_item, rect, rs->viewport_get_texture(viewport)); @@ -577,6 +625,9 @@ void Terminal::refresh() { } void Terminal::cleanup_rendering() { + // StyleBox. + rs->free_rid(style_canvas_item); + // Background. rs->free_rid(back_canvas_item); @@ -846,7 +897,7 @@ void Terminal::set_default_theme_items() { // Default colors and font sizes from CodeEdit, TextEdit, et al. // A comment on the translucency of the default background color: https://github.com/godotengine/godot/pull/51159#issuecomment-891127783. - default_theme->set_theme_item(Theme::DATA_TYPE_COLOR, "background_color", "Terminal", Color(0.1, 0.1, 0.1, 0.6)); + default_theme->set_theme_item(Theme::DATA_TYPE_COLOR, "background_color", "Terminal", Color(0.0, 0.0, 0.0, 0.0)); default_theme->set_theme_item(Theme::DATA_TYPE_COLOR, "foreground_color", "Terminal", Color(0.875, 0.875, 0.875, 1)); default_theme->set_theme_item(Theme::DATA_TYPE_FONT_SIZE, "font_size", "Terminal", 16); @@ -879,4 +930,7 @@ void Terminal::set_default_theme_items() { default_theme->set_theme_item(Theme::DATA_TYPE_FONT, FONT_TYPES[type], "Terminal", default_font); } } + + default_theme->set_theme_item(Theme::DATA_TYPE_STYLEBOX, "normal", "Terminal", default_theme->get_theme_item(Theme::DATA_TYPE_STYLEBOX, "normal", "TextEdit")); + default_theme->set_theme_item(Theme::DATA_TYPE_STYLEBOX, "focus", "Terminal", default_theme->get_theme_item(Theme::DATA_TYPE_STYLEBOX, "focus", "TextEdit")); } diff --git a/addons/godot_xterm/native/src/terminal.h b/addons/godot_xterm/native/src/terminal.h index 949c4aa..cda5df1 100644 --- a/addons/godot_xterm/native/src/terminal.h +++ b/addons/godot_xterm/native/src/terminal.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -133,6 +134,11 @@ namespace godot Ref attr_image; Ref attr_texture; + // StyleBox. + Ref style_normal; + Ref style_focus; + RID style_canvas_item; + // Background. Ref back_image; Ref back_texture; diff --git a/addons/godot_xterm/shaders/common.gdshaderinc b/addons/godot_xterm/shaders/common.gdshaderinc index 62249b6..c0e6df5 100644 --- a/addons/godot_xterm/shaders/common.gdshaderinc +++ b/addons/godot_xterm/shaders/common.gdshaderinc @@ -18,6 +18,7 @@ uniform bool inverse_enabled = true; #ifdef BACKGROUND uniform vec4 background_color; uniform sampler2D background_colors; +uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest; #endif #ifdef FOREGROUND @@ -48,7 +49,15 @@ void fragment() { #endif if (has_attribute(sample_uv, FLAG_INVERSE) && inverse_enabled) { - color = vec4(1.0 - color.rgb, color.a); +#ifdef BACKGROUND + vec4 bg_color = textureLod(screen_texture, SCREEN_UV, 0.0); + vec4 target_color; + target_color.a = color.a + (1.0 - color.a) * bg_color.a; + target_color.rgb = 1.0 / target_color.a * (color.a * color.rgb + (1.0 - color.a) * bg_color.a * bg_color.rgb); +#else + vec4 target_color = color; +#endif + color = vec4(1.0 - target_color.rgb, target_color.a); } #ifdef FOREGROUND @@ -65,10 +74,6 @@ void fragment() { COLOR = color; #endif } else { // Outside the grid. -#ifdef BACKGROUND - COLOR = background_color; -#elif defined(FOREGROUND) COLOR = transparent; -#endif } } \ No newline at end of file diff --git a/docs/_static/.gdignore b/docs/_build/.gdignore similarity index 100% rename from docs/_static/.gdignore rename to docs/_build/.gdignore diff --git a/docs/_static/images/button_live_demo.png.import b/docs/_static/images/button_live_demo.png.import new file mode 100644 index 0000000..034d0b8 --- /dev/null +++ b/docs/_static/images/button_live_demo.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2701o6oeug74" +path="res://.godot/imported/button_live_demo.png-c4135c006c49c133ba207164b5507208.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/button_live_demo.png" +dest_files=["res://.godot/imported/button_live_demo.png-c4135c006c49c133ba207164b5507208.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/docs/_static/images/button_view_documentation.png.import b/docs/_static/images/button_view_documentation.png.import new file mode 100644 index 0000000..2ac79df --- /dev/null +++ b/docs/_static/images/button_view_documentation.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b3ng2bbvkcxqg" +path="res://.godot/imported/button_view_documentation.png-36b4e7350e1938a5364654dc5a6641e5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/button_view_documentation.png" +dest_files=["res://.godot/imported/button_view_documentation.png-36b4e7350e1938a5364654dc5a6641e5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/docs/_static/images/button_view_it_on_github.png.import b/docs/_static/images/button_view_it_on_github.png.import new file mode 100644 index 0000000..3db1f27 --- /dev/null +++ b/docs/_static/images/button_view_it_on_github.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c7rj0vssdtd0b" +path="res://.godot/imported/button_view_it_on_github.png-e0984ad68204453e4f7eca81ea6f9f3d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/button_view_it_on_github.png" +dest_files=["res://.godot/imported/button_view_it_on_github.png-e0984ad68204453e4f7eca81ea6f9f3d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/docs/_static/images/diagram_flow.svg.import b/docs/_static/images/diagram_flow.svg.import new file mode 100644 index 0000000..4182c68 --- /dev/null +++ b/docs/_static/images/diagram_flow.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bt0w1bpmrlvg6" +path="res://.godot/imported/diagram_flow.svg-deb966afe34c618af795399045a53c68.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/diagram_flow.svg" +dest_files=["res://.godot/imported/diagram_flow.svg-deb966afe34c618af795399045a53c68.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/docs/_static/images/diagram_pty.png.import b/docs/_static/images/diagram_pty.png.import new file mode 100644 index 0000000..9a13624 --- /dev/null +++ b/docs/_static/images/diagram_pty.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bntcy0kljx1pk" +path="res://.godot/imported/diagram_pty.png-c8b3e1262155f7bb11780ceae74c3a89.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/diagram_pty.png" +dest_files=["res://.godot/imported/diagram_pty.png-c8b3e1262155f7bb11780ceae74c3a89.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/docs/_static/images/diagram_pty.svg.import b/docs/_static/images/diagram_pty.svg.import new file mode 100644 index 0000000..59567da --- /dev/null +++ b/docs/_static/images/diagram_pty.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cjdx1bbrl2dqx" +path="res://.godot/imported/diagram_pty.svg-870dd178a8c99c5215b512d2c8798749.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/diagram_pty.svg" +dest_files=["res://.godot/imported/diagram_pty.svg-870dd178a8c99c5215b512d2c8798749.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/docs/_static/images/icon.png.import b/docs/_static/images/icon.png.import new file mode 100644 index 0000000..6fe43ca --- /dev/null +++ b/docs/_static/images/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://2em8p7vcmp33" +path="res://.godot/imported/icon.png-2ad8615e0fbd4a5bd3357340a1dec3aa.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/icon.png" +dest_files=["res://.godot/imported/icon.png-2ad8615e0fbd4a5bd3357340a1dec3aa.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/docs/_static/images/logo.png.import b/docs/_static/images/logo.png.import new file mode 100644 index 0000000..1476b9e --- /dev/null +++ b/docs/_static/images/logo.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dx8rrrcro3wv4" +path="res://.godot/imported/logo.png-6572eef03017dd6311ce850698a2dea9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/logo.png" +dest_files=["res://.godot/imported/logo.png-6572eef03017dd6311ce850698a2dea9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/docs/_static/images/logo.svg.import b/docs/_static/images/logo.svg.import new file mode 100644 index 0000000..e8a9289 --- /dev/null +++ b/docs/_static/images/logo.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bx6o0qcekeeoh" +path="res://.godot/imported/logo.svg-328426fb4e56bc33b269fdd59f642f34.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/logo.svg" +dest_files=["res://.godot/imported/logo.svg-328426fb4e56bc33b269fdd59f642f34.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/docs/_static/images/screenshot_editor.png.import b/docs/_static/images/screenshot_editor.png.import new file mode 100644 index 0000000..b8df614 --- /dev/null +++ b/docs/_static/images/screenshot_editor.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d315jbwmtfwhi" +path="res://.godot/imported/screenshot_editor.png-c9da5cc4c087248bfd11d1ae567b2d56.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/screenshot_editor.png" +dest_files=["res://.godot/imported/screenshot_editor.png-c9da5cc4c087248bfd11d1ae567b2d56.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/docs/_static/images/screenshot_retro_term.jpg.import b/docs/_static/images/screenshot_retro_term.jpg.import new file mode 100644 index 0000000..3515b32 --- /dev/null +++ b/docs/_static/images/screenshot_retro_term.jpg.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bewlk02k0excw" +path="res://.godot/imported/screenshot_retro_term.jpg-ac0139b857a49cabe0e6d99e21494eb0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/screenshot_retro_term.jpg" +dest_files=["res://.godot/imported/screenshot_retro_term.jpg-ac0139b857a49cabe0e6d99e21494eb0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/docs/_static/images/screenshot_the_guest.png.import b/docs/_static/images/screenshot_the_guest.png.import new file mode 100644 index 0000000..3ae1ee9 --- /dev/null +++ b/docs/_static/images/screenshot_the_guest.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://0iq01v30huje" +path="res://.godot/imported/screenshot_the_guest.png-26bd55ff36509f34b0d3608706091443.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/screenshot_the_guest.png" +dest_files=["res://.godot/imported/screenshot_the_guest.png-26bd55ff36509f34b0d3608706091443.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/docs/_static/images/screenshot_xterminate.png.import b/docs/_static/images/screenshot_xterminate.png.import new file mode 100644 index 0000000..879b903 --- /dev/null +++ b/docs/_static/images/screenshot_xterminate.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://i5i2woh5t4w5" +path="res://.godot/imported/screenshot_xterminate.png-195255f90e96125f75605ca3c708e423.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://docs/_static/images/screenshot_xterminate.png" +dest_files=["res://.godot/imported/screenshot_xterminate.png-195255f90e96125f75605ca3c708e423.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/docs/demo/.gdignore b/docs/demo/.gdignore new file mode 100644 index 0000000..e69de29 diff --git a/export_presets.cfg b/export_presets.cfg index 3e6e76a..f47f950 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -6,7 +6,7 @@ runnable=true dedicated_server=false custom_features="" export_filter="resources" -export_files=PackedStringArray("res://addons/godot_xterm/shaders/foreground.gdshader", "res://addons/godot_xterm/native/godot-xterm.gdextension", "res://addons/godot_xterm/shaders/background.gdshader", "res://addons/godot_xterm/shaders/common.gdshaderinc", "res://examples/web_console/web_console.gd", "res://examples/web_console/web_console.tscn", "res://examples/menu/menu.gd", "res://examples/menu/menu.tscn", "res://examples/asciicast/asciicast.tscn", "res://examples/asciicast/example.cast", "res://addons/godot_xterm/util/tput.gd", "res://examples/terminal/terminal.tscn", "res://examples/terminal/terminal.gd", "res://addons/godot_xterm/themes/fonts/regular.tres", "res://addons/godot_xterm/themes/fonts/jet_brains_mono/jet_brains_mono_nl-regular-2.304.ttf", "res://themes/demo.tres") +export_files=PackedStringArray("res://addons/godot_xterm/shaders/foreground.gdshader", "res://addons/godot_xterm/native/godot-xterm.gdextension", "res://addons/godot_xterm/shaders/background.gdshader", "res://addons/godot_xterm/shaders/common.gdshaderinc", "res://examples/web_console/web_console.gd", "res://examples/web_console/web_console.tscn", "res://examples/menu/menu.gd", "res://examples/menu/menu.tscn", "res://examples/asciicast/asciicast.tscn", "res://examples/asciicast/example.cast", "res://addons/godot_xterm/util/tput.gd", "res://examples/terminal/terminal.tscn", "res://examples/terminal/terminal.gd", "res://addons/godot_xterm/themes/fonts/regular.tres", "res://addons/godot_xterm/themes/fonts/jet_brains_mono/jet_brains_mono_nl-regular-2.304.ttf", "res://themes/demo.tres", "res://themes/normal.stylebox") include_filter="addons/godot_xterm/native/bin/*.wasm" exclude_filter="" export_path="docs/demo/index.html" diff --git a/test/test_terminal.gd b/test/test_terminal.gd index ea3e0a8..efca752 100644 --- a/test/test_terminal.gd +++ b/test/test_terminal.gd @@ -264,7 +264,7 @@ class TestSelect: func before_each(): super.before_each() text_edit = TextEdit.new() - text_edit.text = "0123456789\nABCDEFGHIJ\n)!@#$%^&*(\n\n\n\n\n\n\n" + text_edit.text = "0123456789\nABCDEFGHIJ\n)!@#$%^&*(\n\n\n\n\n\n" add_child_autofree(text_edit) subject.write("0123456789\r\nABCDEFGHIJ\r\n)!@#$%^&*(") @@ -306,7 +306,7 @@ class TestSelect: assert_select_eq([-2, 0, 1, 10], "0123456789\nABCDEFGHIJ") func test_select_exceeds_row_bounds(): - assert_select_eq([1, 5, 999, 999], "FGHIJ\n)!@#$%^&*(\n\n\n\n\n\n\n") + assert_select_eq([1, 5, 999, 999], "FGHIJ\n)!@#$%^&*(\n\n\n\n\n\n") func test_wide_bounds(): - assert_select_eq([-999, -999, 999, 999], "0123456789\nABCDEFGHIJ\n)!@#$%^&*(\n\n\n\n\n\n\n") + assert_select_eq([-999, -999, 999, 999], "0123456789\nABCDEFGHIJ\n)!@#$%^&*(\n\n\n\n\n\n") diff --git a/test/visual_regression/background.tscn b/test/visual_regression/background.tscn new file mode 100644 index 0000000..088c64e --- /dev/null +++ b/test/visual_regression/background.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=2 format=3 uid="uid://bk450yhh0sx2f"] + +[ext_resource type="Texture2D" uid="uid://2em8p7vcmp33" path="res://docs/_static/images/icon.png" id="1_osuf4"] + +[node name="Background" type="TextureRect"] +z_index = -1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("1_osuf4") +stretch_mode = 1 diff --git a/test/visual_regression/baseline/blue_background.png b/test/visual_regression/baseline/blue_background.png new file mode 100644 index 0000000000000000000000000000000000000000..054b9865aef5b83b156d724f5f56b6d76ac361ca GIT binary patch literal 479 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 QhbJ147FQ6f6fq3?A00fDV*mgE literal 11478 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 diff --git a/test/visual_regression/baseline/empty.png b/test/visual_regression/baseline/empty.png index fc9d98d6ce23e9b0014d552585547fb9d6109783..921af00c2ab78ca36cf94db7521a548d4cb3814c 100644 GIT binary patch literal 503 zcmeAS@N?(olHy`uVBq!ia0vp^CxAGGg9%9bJb4iVq!^2X+?^QKos)S9WN-9zaSW-L z^Y*IYx<>{Kt`}9>)IvmbnAf++Zm=^u;^g4u*%j}d;$FTehpwRWv-r*6jDz4zZ=yJc|x^R46?vnuDY+bitP zi=1}%{`1>!zxCg~{@Qd;`&)@?atF*fyEzgC6?K$aTw6AD2#5(DQgVuL;lMBGQg1i^ z`n>~el^coGLA=>CSJ{y`YuQ%w4W^e(-cC!cw2}M$ci;QpTfcv}pS`x$Zhp|Ib=Ru) z-`{`#{jJG2I)c2`mcH1v?wVEK<6G4?nhIsI*T!D|%gEv7WF7jiu>cs844$rjF6*2U FngAp>#1Q}h literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^CxAGGg9%9bJb4iVq!^2X+?^QKos)S9WNUf4IEGZr zd3$9eZ-apVi=&Ud-sQbJe6$R1a&ewrGCyX+uZ`dLox2vLA8xU8zuWO>@wbmJfM>cgwAUXgF)HIrF(Fe+NzaPK$ z?#+dB{pUYVcTax(CO`g5D9~!LdF?lM_!X7CIClJbvhe;Ny+m6Bc23e;`}eEwzJ4CF zYwqiUUtb?xfB5FMrMb~=k>mS!^w@BDa&)(B@E9oK4~v%R^!1X#ehYxn&fw|l=d#Wz Gp$Pz5;rtK) literal 0 HcmV?d00001 diff --git a/test/visual_regression/baseline/solid_swap_selection.png b/test/visual_regression/baseline/solid_swap_selection.png new file mode 100644 index 0000000000000000000000000000000000000000..4179708f27ea63d38d83c92213e4bd2b3dce8680 GIT binary patch literal 560 zcmeAS@N?(olHy`uVBq!ia0vp^CxAGGg9%9bJb4iVq!^2X+?^QKos)S9WWVxsaSW-L z^Y*r3-em@nhQ!?_&JzW=gV@Ca{0Gj_f0%I_P2rd^5De_@5okt`ug|gzlVR^&--j|{2!sx z;_5^^k*6CRV_#)c{36Zo_-Xm?_Ok2i&doXTLE+l|ijF&6o*dmRpr9ET;t$*T53~4| TNTy~2lK_LKtDnm{r-UW|i1PPi literal 0 HcmV?d00001 diff --git a/test/visual_regression/baseline/transparency.png b/test/visual_regression/baseline/transparency.png index c861e6c360380d8dcca36054a60835506cf17ddd..b32e6ba5aa49c89b3f0311011fc5d0284fb19969 100644 GIT binary patch literal 12398 zcmYkDWmFtNw`g$-2~KcA(BN(X26uON3+@hsB)Ge~yAxc3ySux)4Klp>?tS;YJ8RAK zkLjxJuBvsa>g?TNit-YlQ3z0=prAfWNs1~%j>C|zDH06iS9bqc7jl4hR+bQks-7e| zfr9!5B_%4P>XCWTY2iYokzNcEH7#_9V%@>q#DEe~kWQ48P81EJ4HNSWD_6#m*-&Y+ zEsk;XDsDMC&rF-^WLsvoWRt-~(T)B{DCh!Z?PUnPd>OG?| z&=e*O3NyG^Qab2qKPEDi5M!#-k$iWPsmmL%GhsHCS}jSjd*kuu2jnJ-9xx8icQ~U! z3Z#&-YFf2qPnV@?((c@5lzHP)TpDL8RIjvTKXq!QqN1|1H9w61hJ+h2VAoQiTHc79 z{0yvVqXnA|pdbXjzrW8|V$(1(R##QIaubOcI93;|6Ac)%qgUK1LhmU2lZxWlZYvuyS+ve{~R41weusvRh5t!+U)e!V@#n5|MTZh znWxTfH*y=T-@+WH4m-ic#YK`?Yh$C!)8)p>%1TSDABmZ{d9`*^c}#Ai=r*JkiZOc&S{Vgl9Fial356%5}f1QU?5`?jLaCtwqtQ zsjaQei{*ex>^nyH7TWo8or#q#JuV?09#Y7^7Ft-g!Wm2Bv@md}e$7^^^O1E;CnpO_ zTv8IetgNg9$D42XSOO3D`eb+irl9;W?(z9U$@#bnMDiY-+bD^CiFbjn)+y=&^lF zXwZIqA2Yc1;0w<;)n*%&4SQ-EJMemcSQ;7{vR7i9rEBb^|9CyP`6wzX5)%{S=jYe2 z(NMw`QwooWC|RZabo*qV%O80H$d+kux-b%-PX*HrnL;UWP-k0MZn1Pmc;T$1IA#^z z3HY?ST;{!!3>N&}+P@cmQ?GD^bSP)W=UNMkjGCa2WSS%(q>(^A0^hi(TYl`W8_XQ4 zgdje0;o~nA6S;W6XFXxm8=sK12P6}(Ke$^`;}+j=bH9En2bdf3Z}PdAB$80I8)*kV zhFtHHi;uL*t_Z3Aa6h@0=Di{}r7DOxM~G?fB)*KXFR!Y)I61ii`P^iC+S*>s>m#C( z=w6r8^krtiXr&@x?DyxlGSw+x38QRJAwUB;tpH#fFRpqpDJ~;oE}iq6Litv8MMVVD z<6Bg5)#oNJn+0#$goM@ARlkO#Bv%WPZsj|t^MqsoVUC`1jnS_o$m>I>&=dz>LcIa0DdEij~)$98LU>Udea-@hot%zGwk zLg4DTHPStsrq$kC=g*JIu#^w~f!7OCQMyg?j9Um7Z z;P=q$y@O2r!rr81evyy|flK~?ON!5gmhZ46Zybj#P&I#I+zSN$jc%c{&NUNfWgVeW zySZ4*fnW%=e*gwk!(iRX;9yi*C#I$lzsE_Q8XHTr{Jbc_ftRLNHD}cx34;M2dB>WW ze#nC52JX2Nypz(88)}>V^Pv_li2NcaU+X~2k7U&P`(d9@OV$lr#miaeGI@#io|(tC z)ZO^IcO|8$Yg>&DBuJa2;*HNEif#>tiD~^PjV}rnzB46r7 zd=B;-gkL&bT9F2(%ZIt@JF%rG(8bi5%Ud&@i~^Nzr|M>8#1MY2fdI{lKGbJ5(*lEl zWY!&{0YoGgC^+q$qIYIG3QnBs18u9wj_c1yh+&}N8)n^XzRDFFgxzbw4jlX?o3nf< z4o(spsx_$VBwY7MAc%qmK1%k28BlSg1PuHTArG?4=av6pd89(!!VFh3-0j8m*UQ

zG#pU2^kpC9HBcKdI)$e>q>1mv1!Hr6bA(8v0_F1_NosNxgdJ^|IKovg5M#;mvHPu@ zHqxkze}XzA>@*H068>g&80Yy;ND|YwK`GIka4{%m?znGq6r=qU|GeN0obMwx5|j!P3gs6 z7_`PQ#KZEh}+mrUcK|ea>i3?iiyil{8y>|FPN8LUW&s}Bg>Uu5Cm4ULX zA8t}V$??}xN8H7*UOm7uR5DT=!=1^U=Nb?1a!=!-zaosRs2=7fcwdz`eJGqJj88w! zkPyu=H=^O|3<(*y8qKus!%v=muck}#wnDTYZ_wzzATk8+_C|`%15!JU$>Uz*ttTZ9 zaLnI+we<~Sb?ErXDFs$(yF&kqYusX}z}Ie$M9YtGjPZ9V@IfoldQS$W?ic2B323v- z0+TTJMM30Z76e6&qe@%2w^PY@1EHVU+ZtoEAwxu0a1$#c(JjL4w9QGo z9MJb(9t{E6(&j+>>tN6Bw9xT3U8p8*Ht1_tQYwz_XNtPNyctVsve=IITk?RKVLebN zOjLoclCy-1S;FFL&z{-O_@QwYLoZp+*EB z#W2_bF_#Bv4d)Dnd(g%Nx05Pv%$~E{A7Or9-1I)(B`$UbiVrB9(bQiLczsF607-Kx^g6o~b}bw|6F{OrrD>V`P*blO)uL%!O^_J9Y8^8lyQ~@7kIy z8-6j|d1pR>FD^=!#mg{?rg{C6wK?f%B}HYittG+YUmrviQ&HFz+z#hWlESn(2GHQ@ z(M?90t2W`$IjNt;(&mS9zQcqy`($=ou&pw$A$q*SqX-PL_y74iY&Eb7uV{Ig{hdJX z{Hg0(6F#5t3aH-;$V+&t3X*9U?cUZw=@P^Cc$9DBbT)+jDF4IzYFa>9WOEm# zN#EGUvyRNHEaL{tlL946JA=JQAJ2DVbMx!FpKCtZ`II75R7g{6WvvQpsKOL*Vt{;m z%*b#fb0~boXEwO95NHft--kE>hjsCtWIhN!=u;3@5LOfE$Y8^m1N?I>`~)1C%x)Wu z-|KhhMAb|{^?O#>BiuCjTZ$WH!T?je8((h@Pp^mkZ3hWrtjFFkiaj^TiYJDcx-^e3 zV-~kk?|2YN3u>GoyF%)E>Va5G70%2tW=j=JCm;ADg9>G~FozcS>qYLkGVkdU?&Iiz z-dY^v8qcGVsDR*`@i$}HYkPf(Z=-piko|TWGsH(M=Y|u zORjB2>&&U*fWq;uiyzTCp zJ#xm3N35vZLN2anRhWYf)l{OB&!9?lR|GuyB!PbPW_6L@Fu@$Bnh)?LY}!96MNoQDB31JN4zJ7iS$BY7UBnbhasJY|8kecw6UX z)Ph7h`I>vU6)rd}Lm9*r{jG$L+-8Cv7<0+MzL@*P(7uqb=#NWtwlotg;V%yavf$3J za3;(QuPfomZNPAmmHIMhXNl+yy3;+*IWSV*PxH^eew`>}aBE{x`|_{rD}zl(&#yh{ zz`oTisufgl{3p=`7keS~xE1*u7J?jU;9Jl}*w9qs?G$GHba!_2D@H*of~PUsDNPT` zPfGP*Q=*cY_e3@~uhSnZ3F|HQGREoso-%s3$(OrmR)j}mn}rm)Z%{z>7DhImLK?Q;Mg+Y-ax*v+TCLu2-!5WJ`W(Ny_T+~ z=%zWdfV$tH4iJ@}&sMYn7llOz$}eV4H;w&S)-2Xgm!n4={b-$hZ}^C9NKf)>EDs&6 zt!+v0t#l#I0ZK6AzP@QzCKKvu95)i`$BKKEy;}czyxK0w8pvmUcACTI`>duv*Q7j4 z5u+tJt&H4kdb(#&+B3$2qZJNvjIjUuZP0){UFM#fXcrH*Jizyz!cC#S>OrKTth!QX zVnKI;wxtR)5YO+qF17n3QD&a3F) zpnFuQ15CW&zuleT-cA)|jC^eWBH@#}7r!g1(JU*zI~HDe-bMN>Kg#OSOmJbkbYGz# zeUF~?r2c6L51E27lHyhsr$$%PFh$HN>?8TE#NGrxf8?W0?62++wzfno`$~~U;vZ*t z8-`-q1S6m^%hGUj{GX{NJcgSsCom7)qUy(bu72f=cR3qgBtvF}Hla?$-}C*XPu$e6 zsqP2VgjGuglwh67oG#-ZDDMef0@^r_TI%_Y;j-rDy}dFk2-&5^%R@s|qQ*Ycvh;RG zI37;;FMsrWzD{F)ytN^}Ij0k9tLS!0(=T}!tK!eU{Y_3CUrWF}^YYZg3;`QQj+|>1+1d}Mijcvc) zAQ8{q|1RM%&j)L4mPcGah=j3Vd^DiCkWEonDJ@1jUJPW(v*_9ViJ|oW7638^GqvGm z*q~ijW}54^a#l^5mXnbOc{FAkCdOzVHeQol%CEx8RM|)!5!HJz+?1SB-BI&G1*vaQ zSF|IH>r3JOtohBlr4X5ne|?kUR^NOx4*#WUsG!dse?q5Jg`{xPXO;A~vgl6}ay}Q7 zDJcK^-7}z=8~K<(QO~&ZLhh~sAYd(hQ}@enWA?wxu_>Az-Kk`x6Q6t+vm{^z^gDal z#%P!mOtxbdug$nDLNK#(RSz=a4qhC2}e(}bLhi(&rrs>2?y zTQHVCI{6h}SCmcL@FHI$ZTcr>O|3bJ<^EcG?UT~qqAN)j6dK_$?N&S-Jvdw&5f986 zWi#Ju^iAcy=%Cb|tSI$1W3CQjz6JVA$(1Duv*iocguEC{exY=CPc0oblricn)ywWZ zB?^9*dHW;@6RfU+ii|ev{+D(J{)%G1TzSkUJxU6vtDPp135@wzAv6sIciq9SJ(h)soM1h;sdE zalOMG^73~wO59wc@90J9$!3ssPk)~ip+6v;3$hou`_~u)S&0p(W&M%fo)>hIYtFWL z$NYea_ZP&~63DWeC0yeB4ihkLIuIVStC(v-E?r-YP~_56>lLgi%8?wWyT@o zk3(r#(t}znKz;h2RwBuvU>g|%Sn}nejFqw07kU8nv)al3X2(TURl{5M30;!cw`5@+DHfCf4LY*i6j~nlV5t|q9Dtu?I(a2SMmO1yAv8GFCp9Q~ ze-LXKtI3g!bWIQyqolP`%SVu+6~o3!Lh6UZ$w$KMi_ESK#DqtOL(%D%Pn>b>(~OH> z$+Mj8EEkiCl7`uLa2R|-5<-WM!c{b$VfUUSOQH$y^C7i26N?^U5#o?&;r?S#fKjse zN93ekMdqymL@9VRSQ57U<^>Hs4fh+TLLKm{OCmh1CvRcnXiNg^Bay$_bP)4iTz=B4 zb|`|_HT^Ew$WI%YOotFbBL+Rl;L{+a->xwYKEgAcP~`{vh|3Fk?8lfDD6O~&CA<=S z{t8nim!}#%{uwRZ`U(mG_^Rz-x!Ry(&_RC`aO7Y2LVnN1z-p3F!D&~v$*mAr3{FOq zznXg&oUfF3Hp|xwznY)`ItBHW(lz&YZC2I>$4e`g7 z#;4}+w|g;5^Fz{m#*oL6M|4_$N~Vq10ibGeRprChfpaP&m5 z+S!8!BYeS}?{fYoi?7kbahk}8T3{|yWu(^-g$ja3fxWf@a4tPl>-gyDz29y!{BA1w zDoWGFLjdSs{dj#NqA!T9xobcH>=R4}*m>0nu*@E$cr&3E$-=7o`R7Qan8?$cH%$_y z8j+YTz#q;yL}SjydXtFd?fB$23MXVyuhbXi1^g;*6SS{rH4idv6!k@R*OoJn9yPpS z&thF#kGt&Q(vf+KSQ5@^Bg}gYPaPC7moT2;z=XdaOe^_v-w5>*SU4BlRf{5qeBZPM zH#}esX|I<~jmlI>i?U2_7M_X_8WcgBWG451Hi9gNGZp>Q;J(pm$r_@zFKfG3LXf(7 z#~!{hiqobY0SkWQ5XsUbnI1{`TkuWLxB%@VCY2aNX$9v`n|S^NY%Z>h*ySZZe=R$E z6o#K8ooxII*PSS`Rp{8pnWpAy?UKfsWX;XXnNDRAu&F+F8@0Ngzb#m@sTBR~#al=8 zR~=iXZbtHRi3=>qUT{ELF^OY!G`(IFFdd0@%aBj()x{N4aY5u=0Sw4j&3Q9=8= zK3LdlNTaE=1-xV@mt!0oxh>L5%`SmKkv(QndBIjU5e ze9wbk#UzHsHEZmIgv}S$?b(&aEATp2Gihb23F{lr$=HHEo&0{S4CeZ#3WIAn~EQX3n4` zu;8{6QO&%<{DV49P%v4T`NZb3MdgF7KD~x}lgcLlP5>rt9Rx3U7R<7ddQDxz>7%{j zQ~B3>FZOdlF*OZ9P+KFvv5gGp>@X2nZya?Bb6D#p(!HlzL4i8aFeZCKij{Vm!}NO7 z{qu=Fb9xbbwRS~OsjAcCaZ#tJCfPtSlsH`7Zc!F#H9`(rQoSqJq)1l`q&GBLY?pWbp10wSUp?C`jxAthW>uTL>eL>ZYi zFT)0u;7Za$C{>=m-+!_x?E`jM@`$NFZhag*ei!7q*1-YSI41#{b#{f%8Gz5f zC9y(EEcBjtgQz#$-F|Hds9b;`oqG=MH^o$O^w8hbjuE3xuLalgund%9kD_;ZN`g)Hl~RO&!W)oE2jZW9|)9f~dcXcfHN5*k3#}SWsxjZn2SF`MUF6jwdGi zqk;Y@3^pnWp~HzO4Kn#%*jB}O4Vf-9ep9(2z`?@^i`u% z&As>ykavfKL=%n~G4bTj<8~}lr&fA5+k}&AddpEnsL|nsdVPSQJ~LC5_>N@-;6KB1 z^iajR-E<#%C6b=Q!Ul?oe^nyZdvdr8sejmDNs>l~gT~&Hw6I>AE}s%MB92m^+`ERl zq$Ri_Df4+v+>;*8Ec;Xy+1YfvE3;)63HvE9w-ld213WN7D-wM$XQr{KlU+I)PexM( zb~P=j`3o$Vwj~A@8@1j~9>ihP_&KqsRixK4DPAKOV}C0y+6x$o;L2mRN3GpKp=2*k zELn3OC%!vY#J-&}vbpsysSgU^2s>u!nT8~WtpI|dVwQwI2n#G)Rh5@ zj4DvDh*My;-c?>?*pidT4HGS*1pX&8>KBYB#1(uI+wDnB`I3fZz%)!6LF;JX|I1s7 z=wnGxHZp!jXu*CaDoVn4#M_7Vi^AJvVnJN2bI2-Bv`?9A|D7)-m`HkVM{})C<8K=DXEMwi7hiv_IPw6Kyk+? z(S^PB4S_1krE641dz({|P;`1vY8vrr;TgwtcdFfMC=x_kgYlcPPs1Z@7j3Fsrne)3 z5D*EKBwcizSiUvDt~Dc^WoE5$(muvyk!pw}MOVtkOI{v9GL~*5g)1|}TJEzFH)%S@ zZzH@XHILfkz!CK_aQ^dQJ}aCAOM@?7$EuLHQy)MJzlT#n&R6Q5hqDkW`aMo_3~mi? z0dc~}R$UzzRem5OpkL=FiOwOfS$BNoDb{fQogd?s@%z2qhAL4ue!ZC~q_InSKje(A zPCpcUiRP_1(8EL#5D=tgWcEht>+2B^k3z*+YYu6pSj3MuP^-QZ0Czt@VTj!B@G5Af zA%vbgpXIB)KS!EziQKn|2Q~_bA!Or(Oaiho*VVkldCkE-3lNzlmO}1N(+q! zb=*YmkDW`#RGyr}2UAmN0)^YfobZ2G(pwi-#farQ(EYh4e zwtFq(UP`q^M0QTjP2Z=Dn?$zJkJUu&cw#h)y&Yz{FACZdq5s$#QLYHG9-=Ehbll${ zMdkR#qN1Yw^-Ja~^UnH2+9-Hg50dlD=r9lUctmxwlW`g-l_?S~c-5 zrB<{R)+00t>gZ&wow3sOJ;cx3bYiWe?*Ogfk(x+`c6aJPVwVH2{)LNn zw*^zLYI_tMJhoufdc!Xil@TKNVEzgnw=T`JD^8f4^^LsvKg4ab&+6Tbr#il#7P*yP z5&R@Cj~+@boaL;(7q7RMz2UIJQM`9&L!IRf!tNIC-rnAxo;(es05wuuE5vMc3vuPt zZVKSL;+}cSNuFz%{YX{x%~tiL4;Z8!VE{{1Lug6kcD1R;rJ(f-Th5Oiph)^tecd;= zW5cO$Yi^DlDNw#<>9{X&`|9%2&fY#{aaGY|oe2sGo#;Osznu2FFdEh#wEDU;0!sl+x{Zy>tsG=orehn9cAdbM1*o>xA@!ejk~Af> zTl>b*ot&&}=1+G<^kwv^wM1I+2F?d;V%jrq%|*LNOGu6wEfv>#rajwN1MPsA=`xMr zr%T6QY8o-1pXow&%NC>t_!P=3`F|rJ`~z~UlC1|$Wz>cE-d7jyIY7H5RIum$XY@^# z-NX%kz_W~liVE1?;B}gfxm;OPU5me~%CY1v14`oQU8rNLsU8uYy9=y7_7;Hrd7 zgWqsk@Tr@tJgeCr#hW-go3Ey9jd!#D*n`{OfOy4V1{dd`4_QVvF=oG+E2m3$j#U7!b2c6lW)B>=)Bli^ti7KS)?7logF*fqMel*d zYULQJM2va8Is^E{hw}`K`S(h{hp#vnkYQwIl9q%xpt0pUaN!n_uW9G752V*yKLJ z53jLwMr;%cDQIbFX<+LGyryAm@O_!;DnBfxfdwjdH+%T(j96Zs(SNQm9tv+a$yXLg zZ+-UQ?t$z9kZq46O|~7hj($5=epov1gb4hg;zc38fu0DI9WZ-Ov!ExBV{uMcyQ@gJ z&~X|a8~ekRLP@XPYgpUe4In{nDJ?B+ZeHARNR=(d@0FQKL?x*_dAWmiFE!D4hkhk^ zj2bZ3uW9M-?yjq=D=9&Yr5M?9ICg2n|M7JMP(&7S)g+hF%#~Pu(dEd-vMCDjQ9vLj z+gtag-z4v9hc*y%CY*HO#6@0Kb{wgKXuKx_!tQ1r0mWqMopqY^XDjZ(a6+}3^lt6G z)%DpCfGUI~hXY5>j>sFUE(5(7JNl1_B^$6z#+zPK9`Q$N>JV!ZFufp0x@dG_Vq$dk z)~iz>hY2%Gv_MHrNq4N8KY8-uoNy}g|A{`ue|IbxCkApjZ*(0!>?-|$Z(I^f<2jo_ z?Ad8$^f;NBOv#gPZP~sxT5QjAR@K~mEkU^dfIlqT=GP-8p0RAKsGz&-Ad^ydbgZM0%F4>J?|ygtmgx63a*@jow6G|lkb=a)o01v# z>bI}|m|z%F=m?@#FCN$UH2fh-PLp-Y*g-tSuhFS~^9HH-eHY#>zt@YEn&CBRN56~- z+$iy^$=@0M|6N$@dt6u;vf)rPv5K`xo#tvxZi_TQ6crfb>-5<}j^E2k1XGt~o#wh7 zmtmczzwhg#+(P+UCh%x7tETgDwZC5!vSPW;qWc)QmLZ9eP)dYE`m+D=acbsl_kzc) znl3jx*UTHBArAs^aUhLDl^}%uw=~Jpx{Rx~?oiOv6kAuOT21 zNQH3sDrE)pza$Q2)4|fgTq-K8P}bvuA(17xe}1CRH%^nNv&^7Y9ujsV#t~^&Ws+Fy zLR!7l3Wvx9*twovl1P={?dM(hP=Nk+oFs+o&1-*7sixjS@05DB+ZpU?X?g2UOK-%9 zeIE)ELWCkmRes~Rn zC#KmdJsINUG0U3bKE6F=k-H@VhmV*lR<6aI=qJi1u-UKspy0*MyAxu@Q>!Vod>uwb zZ$6}?;Hz`4!RlZKQo*1Igfx{YW1?a!(t6P++%C`Tc<514FZko&Um^kaM>4RM?X-<&ZfDNf`z>Bh(q}Eb{C;o#4&AANxHJv;|JVpT_-m3`lVwL|UBB$37MzE^Mcw5bA8iw9nBBZw z-co;8$)qJ*SDC?0%1fZCc5SQs-FHPjtDt;X=WwY|fRD3r-pc zq3$f@p(&Y?wS)#ZswNzcdk|#{vprQ7&J2{%r7ki54bGGqw#r2%qpWp`Gu&;gntepc@D%vbB zkAW9Do7q(oi1%-ODmVqJd-Y@R{lnkHj=IDUk_bp(#22-?7?Fz~t|pi_W^MDDr0k~= zp~Ke~GDqaSQbXOAK5$2zTTlPRv3s;TocR?2?^9h1gr}7r#$kWmU3pt7xZ>?bLSn5? zt;{h2u_`8r4em2%k{P5wdFgs_8IdD=MW9BYwgS9TTV}Rk6Kv=@!I*lm94a43R(3mM z)pYfbk*z2ElU5_EC&bhK(;mNmr;$>}s}m2fZ8v2kKG)x-)pw$L4#06sR@fv2RQ4F{ zz8_M2JRG-AGQm93u^+T5U_ZK;Mw0wEA!3Z_ZQMLLSFBYTW z>F(?N32%-Ul|A`8b`ZkLJui3W3U?~E;GSgwC*5u8s_`Kgwk^+L2qBwo=}^rdMu3`)E4x?tu`~qI5fp0o}B+2y%ArA7k4q zr-dMh`1HThBiC8 z3#-f8{btlPx;;EA-JNuR70XVN|AN2wI5JAdIWAwP%WIkJqLYVB&2iaR%?Um7!;MHu zTQI|JgxFZ%1=0Tt=%Sv7e`|Dgp$TMukkRXS2YsD?@(0Qj+FFm6Tw#vsmQ{-(Z~&?) zsi+w`-Z%H3lp=zO8!SF`dNJHUkoG@-#XwuE@r536&wK(MKD%nL{NUYOT;;s_f4J_% z`-N|u_O3!j#vtTeCq!-lXoP92zuqPK&r)v47)^syu5~e+qUvEmvZ<1QcJ#vw)t1hG zpT6ZHY~aGexk9q8#WV5O$yPgOXwXMnoM$G*{LDWlA>qp)Zy0q&DqZaK_x}(!t^{CL z_(4o<35Ee&Pvy=4j^<%h6VIA>O8G4+2?I6WK|wOlgmOrG!efszoes*OWDECyV_ha@ z=vDt14W{WM=wX&?3G2V)XliY!`w-e1utHk(X`D|ZN;-EYiKeU(Wgxx)%KtT@<_V{^ zA6W7H^-?Pu!Sms}wbQb)zuZ(bN^{aQ=U^=<5KgG4P#zT?1Kx{&9*x@96gng<9JLvn zDzfhYzjD%Od8(FaW*|z2QqhoVNYFD};}1=3MY0O4uH#(iv81@nTMO(*zASEdUn|*Z z2u)KxeS|O@C2sN>)SFU=(MxA0X|b=5wgh?gDZMI8Nqj-W@M1!h#dr3L#Tn_ai~_PO zCM7Wh@}(V$#(v-(sHP9MSa67&Sc50=*5N5X9z;%bto=1Da1&~VUE;+fiCzZ6|XhsF~m!42-&HOh1SmVJa$O{BeQeyI=)xw5>{|8K&>8bz# literal 4019 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 CZMP}_ diff --git a/test/visual_regression/baseline/transparent_invert_selection.png b/test/visual_regression/baseline/transparent_invert_selection.png new file mode 100644 index 0000000000000000000000000000000000000000..523e3c0e892fbe2b2114dd76bf1fc224d4fc3002 GIT binary patch literal 7072 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 F^?#^e>FfXi literal 0 HcmV?d00001 diff --git a/test/visual_regression/baseline/transparent_swap_selection.png b/test/visual_regression/baseline/transparent_swap_selection.png new file mode 100644 index 0000000000000000000000000000000000000000..571b00190657a93d76825a8962e64865f1555f97 GIT binary patch literal 6572 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 E1-S?AQvd(} literal 0 HcmV?d00001 diff --git a/test/visual_regression/test_visual_regression.gd b/test/visual_regression/test_visual_regression.gd index 717fd64..ec6ddf3 100644 --- a/test/visual_regression/test_visual_regression.gd +++ b/test/visual_regression/test_visual_regression.gd @@ -88,6 +88,8 @@ class TestVisualRegression: assert_match("default_theme") func test_transparency(): + subject.add_child(preload("./background.tscn").instantiate()) + subject.add_theme_stylebox_override("normal", StyleBoxEmpty.new()) subject.add_theme_color_override("foreground_color", Color(0, 1, 0, 0.5)) subject.add_theme_color_override("background_color", Color(1, 0, 0, 0.5)) subject.write("bg red, 50% transparency\r\n") @@ -104,3 +106,39 @@ class TestVisualRegression: subject.write("šŸ‘ˆšŸ¤®šŸ‘‰šŸ’©šŸ‘ƒšŸ˜šŸ¤„šŸ˜¤šŸ™šŸ¤Ÿ") await wait_frames(30) assert_match("emoji") + + func test_solid_invert_selection(): + subject.write("ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ\r\nā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ") + subject.add_theme_color_override("background_color", Color.ORANGE) + subject.inverse_mode = Terminal.INVERSE_MODE_INVERT + subject.select(0, 5, 1, 6) + await wait_frames(30) + assert_match("solid_invert_selection") + + func test_solid_swap_selection(): + subject.write("ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ\r\nā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ") + subject.add_theme_color_override("background_color", Color.ORANGE) + subject.inverse_mode = Terminal.INVERSE_MODE_SWAP + subject.select(0, 5, 1, 6) + await wait_frames(30) + assert_match("solid_swap_selection") + + func test_transparent_invert_selection(): + subject.add_child(preload("./background.tscn").instantiate()) + subject.write("ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ\r\nā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ") + subject.add_theme_color_override("background_color", Color.TRANSPARENT) + subject.add_theme_stylebox_override("normal", StyleBoxEmpty.new()) + subject.inverse_mode = Terminal.INVERSE_MODE_INVERT + subject.select(0, 5, 1, 6) + await wait_frames(30) + assert_match("transparent_invert_selection") + + func test_transparent_swap_selection(): + subject.add_child(preload("./background.tscn").instantiate()) + subject.write("ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ\r\nā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ") + subject.add_theme_color_override("background_color", Color.TRANSPARENT) + subject.add_theme_stylebox_override("normal", StyleBoxEmpty.new()) + subject.inverse_mode = Terminal.INVERSE_MODE_SWAP + subject.select(0, 5, 1, 6) + await wait_frames(30) + assert_match("transparent_swap_selection") diff --git a/theme_map.gd b/theme_map.gd deleted file mode 100644 index 5efeb26..0000000 --- a/theme_map.gd +++ /dev/null @@ -1,22 +0,0 @@ -extends Node - -var map = { - "background_color": "highlighting/background_color", - "foreground_color": "highlighting/text_color", - "ansi_0_color": "highlighting/caret_background_color", - "ansi_1_color": "highlighting/brace_mismatch_color", - "ansi_2_color": "gdscript/node_reference_color", - "ansi_3_color": "highlighting/executing_line_color", - "ansi_4_color": "highlighting/bookmark_color", - "ansi_5_color": "highlighting/control_flow_keyword_color", - "ansi_6_color": "highlighting/engine_type_color", - "ansi_7_color": "highlighting/comment_color", - "ansi_8_color": "highlighting/completion_background_color", - "ansi_9_color": "highlighting/keyword_color", - "ansi_10_color": "highlighting/base_type_color", - "ansi_11_color": "highlighting/string_color", - "ansi_12_color": "highlighting/function_color", - "ansi_13_color": "gdscript/global_function_color", - "ansi_14_color": "gdscript/function_definition_color", - "ansi_15_color": "highlighting/caret_color", -} diff --git a/themes/demo.tres b/themes/demo.tres index 0d1dfff..a6cca3c 100644 --- a/themes/demo.tres +++ b/themes/demo.tres @@ -1,6 +1,7 @@ -[gd_resource type="Theme" load_steps=2 format=3 uid="uid://0gk8swmcldbg"] +[gd_resource type="Theme" load_steps=3 format=3 uid="uid://0gk8swmcldbg"] [ext_resource type="FontVariation" uid="uid://vmgmcu8gc6nt" path="res://addons/godot_xterm/themes/fonts/regular.tres" id="1_aigbn"] +[ext_resource type="StyleBox" uid="uid://cxaclm5pavuv6" path="res://themes/normal.stylebox" id="1_bj7pu"] [resource] default_font = ExtResource("1_aigbn") @@ -20,6 +21,6 @@ Terminal/colors/ansi_6_color = Color(0.560784, 1, 0.858824, 1) Terminal/colors/ansi_7_color = Color(0.803922, 0.811765, 0.823529, 0.501961) Terminal/colors/ansi_8_color = Color(0.211765, 0.239216, 0.290196, 1) Terminal/colors/ansi_9_color = Color(1, 0.439216, 0.521569, 1) -Terminal/colors/background_color = Color(0.113725, 0.133333, 0.160784, 1) Terminal/colors/foreground_color = Color(0.803922, 0.811765, 0.823529, 1) Terminal/font_sizes/font_size = 14 +Terminal/styles/normal = ExtResource("1_bj7pu") diff --git a/themes/demo_full.tres b/themes/demo_full.tres index 65dfb64..f5f2d28 100644 --- a/themes/demo_full.tres +++ b/themes/demo_full.tres @@ -1,10 +1,11 @@ -[gd_resource type="Theme" load_steps=6 format=3 uid="uid://c7vj5543645y4"] +[gd_resource type="Theme" load_steps=7 format=3 uid="uid://c7vj5543645y4"] [ext_resource type="FontVariation" uid="uid://bic8m0k54oyy5" path="res://themes/fonts/bold.tres" id="1_0fs6o"] [ext_resource type="FontVariation" uid="uid://ddjoc6kuuq1bh" path="res://themes/fonts/bold_italic.tres" id="2_vxvx6"] [ext_resource type="FontVariation" uid="uid://bvoyrkgr4mipq" path="res://themes/fonts/italic.tres" id="3_0wjcg"] [ext_resource type="FontVariation" uid="uid://ckq73bs2fwsie" path="res://themes/fonts/regular.tres" id="4_ldymn"] [ext_resource type="FontVariation" uid="uid://vmgmcu8gc6nt" path="res://addons/godot_xterm/themes/fonts/regular.tres" id="5_s2il6"] +[ext_resource type="StyleBox" uid="uid://cxaclm5pavuv6" path="res://themes/normal.stylebox" id="5_u76ir"] [resource] default_font = ExtResource("5_s2il6") @@ -31,3 +32,4 @@ Terminal/fonts/bold_font = ExtResource("1_0fs6o") Terminal/fonts/bold_italics_font = ExtResource("2_vxvx6") Terminal/fonts/italics_font = ExtResource("3_0wjcg") Terminal/fonts/normal_font = ExtResource("4_ldymn") +Terminal/styles/normal = ExtResource("5_u76ir") diff --git a/themes/normal.stylebox b/themes/normal.stylebox new file mode 100644 index 0000000000000000000000000000000000000000..09f4790d54e23c62ecbb24011c4f3d18271c2541 GIT binary patch literal 443 zcmV;s0Yv^%Q$s@n000005C8yx0{{S{0RR9fwJ-f(fC9}702b3hHc+J~9d%}EF6gQn zU(+v~r@G+&tbUv2v5WB;R(1peqt2JWZc*wnh8qZOCHvqfP-Z04@MD0F|rM zYPRhbUA3`k#iO0d|2O{|o?*fWxe%}s;D6-pq6KLwG?p6lpYnh5f8f9553DVVKYbYIc}_yHGfhgMZsJWu6{6UtdH07Of_Z|AJB&*(eZm6OaMFv z9v=~>(}0O&0^}!%Xii#6W2xsubQrJfOvUu-P6vYXUqA%8DzGG+4n`L~AaEin;JTpgNMe@GibCG{tFJU zoOc