diff --git a/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd b/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd index 089ec75..437e2e7 100644 --- a/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd +++ b/addons/godot_xterm/editor_plugins/terminal/editor_terminal.gd @@ -22,9 +22,6 @@ 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/native/src/terminal.cpp b/addons/godot_xterm/native/src/terminal.cpp index 3470004..91a28e1 100644 --- a/addons/godot_xterm/native/src/terminal.cpp +++ b/addons/godot_xterm/native/src/terminal.cpp @@ -209,6 +209,7 @@ void Terminal::_notification(int what) case NOTIFICATION_FOCUS_ENTER: case NOTIFICATION_FOCUS_EXIT: { + set_shader_parameters("has_focus", has_focus()); refresh(); break; } @@ -275,6 +276,9 @@ int Terminal::_draw_cb(struct tsm_screen *con, attr_flags |= AttrFlag::INVERSE; if (attr->blink) attr_flags |= AttrFlag::BLINK; + if (term->cursor_position.x == posx && term->cursor_position.y == posy) { + attr_flags |= AttrFlag::CURSOR; + } // Collect colors. Color fgcol = std::min(attr->fccode, (int8_t)TSM_COLOR_FOREGROUND) >= 0 @@ -471,8 +475,7 @@ void Terminal::update_sizes(bool force) attr_image = Image::create(std::max(cols, 1u), std::max(rows, 1u), false, Image::FORMAT_L8); attr_texture->set_image(attr_image); - update_shader_parameters(back_material); - update_shader_parameters(fore_material); + set_shader_parameters(); if (force || prev_cols != cols || prev_rows != rows) emit_signal("size_changed", Vector2i(cols, rows)); @@ -480,13 +483,18 @@ void Terminal::update_sizes(bool force) refresh(); } -void Terminal::update_shader_parameters(Ref material) +void Terminal::set_shader_parameters(const String ¶m, const Variant &value) { - material->set_shader_parameter("cols", cols); - material->set_shader_parameter("rows", rows); - material->set_shader_parameter("size", size); - material->set_shader_parameter("cell_size", cell_size); - material->set_shader_parameter("grid_size", Vector2(cols * cell_size.x, rows * cell_size.y)); + if (param.is_empty()) { + set_shader_parameters("cols", cols); + set_shader_parameters("rows", rows); + set_shader_parameters("size", size); + set_shader_parameters("cell_size", cell_size); + set_shader_parameters("grid_size", Vector2(cols * cell_size.x, rows * cell_size.y)); + } else { + back_material->set_shader_parameter(param, value); + fore_material->set_shader_parameter(param, value); + } } void Terminal::initialize_rendering() { @@ -614,6 +622,7 @@ void Terminal::draw_screen() { } rs->canvas_item_clear(char_canvas_item); + cursor_position = tsm_screen_get_flags(screen) & TSM_SCREEN_HIDE_CURSOR ? Vector2i(-1, -1) : get_cursor_pos(); framebuffer_age = tsm_screen_draw(screen, Terminal::_draw_cb, this); attr_texture->update(attr_image); back_texture->update(back_image); @@ -752,8 +761,7 @@ void Terminal::set_inverse_mode(const int mode) { inverse_mode = static_cast(mode); bool inverse_enabled = inverse_mode == InverseMode::INVERSE_MODE_INVERT; - back_material->set_shader_parameter("inverse_enabled", inverse_enabled); - fore_material->set_shader_parameter("inverse_enabled", inverse_enabled); + set_shader_parameters("inverse_enabled", inverse_enabled); refresh(); } diff --git a/addons/godot_xterm/native/src/terminal.h b/addons/godot_xterm/native/src/terminal.h index cda5df1..c488bf6 100644 --- a/addons/godot_xterm/native/src/terminal.h +++ b/addons/godot_xterm/native/src/terminal.h @@ -42,6 +42,7 @@ namespace godot { INVERSE = 1 << 0, BLINK = 1 << 1, + CURSOR = 1 << 2, }; enum InverseMode { @@ -130,6 +131,7 @@ namespace godot double font_offset; Vector2 size; Vector2 cell_size; + Vector2i cursor_position; Ref attr_image; Ref attr_texture; @@ -157,7 +159,7 @@ namespace godot void initialize_rendering(); void update_theme(); void update_sizes(bool force = false); - void update_shader_parameters(Ref material); + void set_shader_parameters(const String &name = "", const Variant &value = nullptr); bool redraw_requested = false; void _on_frame_post_draw(); void draw_screen(); diff --git a/addons/godot_xterm/shaders/common.gdshaderinc b/addons/godot_xterm/shaders/common.gdshaderinc index c0e6df5..5b76725 100644 --- a/addons/godot_xterm/shaders/common.gdshaderinc +++ b/addons/godot_xterm/shaders/common.gdshaderinc @@ -3,6 +3,7 @@ #define FLAG_INVERSE 1 << 0 #define FLAG_BLINK 1 << 1 +#define FLAG_CURSOR 1 << 2 #define transparent vec4(0) @@ -14,6 +15,7 @@ uniform vec2 grid_size; uniform sampler2D attributes; uniform bool inverse_enabled = true; +uniform bool has_focus = false; #ifdef BACKGROUND uniform vec4 background_color; @@ -48,17 +50,21 @@ void fragment() { color = texture(TEXTURE, UV); #endif - if (has_attribute(sample_uv, FLAG_INVERSE) && inverse_enabled) { + bool unfocused_cursor = !has_focus && has_attribute(sample_uv, FLAG_CURSOR); + #ifdef BACKGROUND + if (has_attribute(sample_uv, FLAG_INVERSE) && inverse_enabled) { 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); } +#else + if (has_attribute(sample_uv, FLAG_INVERSE) && inverse_enabled && !unfocused_cursor) { + color.rgb = vec3(1.0 - color.rgb); + } +#endif #ifdef FOREGROUND if (has_attribute(sample_uv, FLAG_BLINK)) { @@ -70,9 +76,18 @@ void fragment() { } #endif -#if defined(FOREGROUND) || defined(BACKGROUND) - COLOR = color; +#ifdef BACKGROUND + if (unfocused_cursor) { + // Draw hollow cursor when not focused. + bool isBorderX = (UV.x * size.x - float(cell_x) * cell_size.x) < 1.0 || (float(cell_x + 1) * cell_size.x - UV.x * size.x) < 1.0; + bool isBorderY = (UV.y * size.y - float(cell_y) * cell_size.y) < 1.0 || (float(cell_y + 1) * cell_size.y - UV.y * size.y) < 1.0; + if (!isBorderX && !isBorderY) { + color = transparent; + } + } #endif + + COLOR = color; } else { // Outside the grid. COLOR = transparent; } diff --git a/test/visual_regression/baseline/default_theme.png b/test/visual_regression/baseline/default_theme.png index b36279f..1ddad33 100644 Binary files a/test/visual_regression/baseline/default_theme.png and b/test/visual_regression/baseline/default_theme.png differ diff --git a/test/visual_regression/baseline/emoji.png b/test/visual_regression/baseline/emoji.png index 4aeee27..63d38f1 100644 Binary files a/test/visual_regression/baseline/emoji.png and b/test/visual_regression/baseline/emoji.png differ diff --git a/test/visual_regression/baseline/empty.png b/test/visual_regression/baseline/empty.png index 921af00..490153f 100644 Binary files a/test/visual_regression/baseline/empty.png and b/test/visual_regression/baseline/empty.png differ diff --git a/test/visual_regression/baseline/empty_focused.png b/test/visual_regression/baseline/empty_focused.png new file mode 100644 index 0000000..c57b015 Binary files /dev/null and b/test/visual_regression/baseline/empty_focused.png differ diff --git a/test/visual_regression/baseline/hollow_cursor.png b/test/visual_regression/baseline/hollow_cursor.png new file mode 100644 index 0000000..478c139 Binary files /dev/null and b/test/visual_regression/baseline/hollow_cursor.png differ diff --git a/test/visual_regression/baseline/solid_invert_selection.png b/test/visual_regression/baseline/solid_invert_selection.png index 16bff43..9ec06f8 100644 Binary files a/test/visual_regression/baseline/solid_invert_selection.png and b/test/visual_regression/baseline/solid_invert_selection.png differ diff --git a/test/visual_regression/baseline/solid_swap_selection.png b/test/visual_regression/baseline/solid_swap_selection.png index 4179708..963e365 100644 Binary files a/test/visual_regression/baseline/solid_swap_selection.png and b/test/visual_regression/baseline/solid_swap_selection.png differ diff --git a/test/visual_regression/baseline/transparency.png b/test/visual_regression/baseline/transparency.png index b32e6ba..6cf47b2 100644 Binary files a/test/visual_regression/baseline/transparency.png and b/test/visual_regression/baseline/transparency.png differ diff --git a/test/visual_regression/baseline/transparent_invert_selection.png b/test/visual_regression/baseline/transparent_invert_selection.png index 523e3c0..67d5e43 100644 Binary files a/test/visual_regression/baseline/transparent_invert_selection.png and b/test/visual_regression/baseline/transparent_invert_selection.png differ diff --git a/test/visual_regression/test_visual_regression.gd b/test/visual_regression/test_visual_regression.gd index ec6ddf3..3d32d66 100644 --- a/test/visual_regression/test_visual_regression.gd +++ b/test/visual_regression/test_visual_regression.gd @@ -51,6 +51,16 @@ class TestVisualRegression: await wait_frames(30) assert_match("empty") + func test_empty_focused(): + subject.grab_focus() + await wait_frames(30) + assert_match("empty_focused") + + func test_hollow_cursor(): + subject.write("W\b") + await wait_frames(30) + assert_match("hollow_cursor") + func test_default_theme(): # Print every background color. for i in range(8):