|
@ -22,9 +22,6 @@ func _ready():
|
||||||
if not editor_settings:
|
if not editor_settings:
|
||||||
return
|
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
|
# Get colors from TextEdit theme. Created using the default (Adaptive) theme
|
||||||
# for reference, but will probably cause strange results if using another 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.
|
# better to use a dedicated terminal theme, rather than relying on this.
|
||||||
|
|
|
@ -209,6 +209,7 @@ void Terminal::_notification(int what)
|
||||||
case NOTIFICATION_FOCUS_ENTER:
|
case NOTIFICATION_FOCUS_ENTER:
|
||||||
case NOTIFICATION_FOCUS_EXIT:
|
case NOTIFICATION_FOCUS_EXIT:
|
||||||
{
|
{
|
||||||
|
set_shader_parameters("has_focus", has_focus());
|
||||||
refresh();
|
refresh();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -275,6 +276,9 @@ int Terminal::_draw_cb(struct tsm_screen *con,
|
||||||
attr_flags |= AttrFlag::INVERSE;
|
attr_flags |= AttrFlag::INVERSE;
|
||||||
if (attr->blink)
|
if (attr->blink)
|
||||||
attr_flags |= AttrFlag::BLINK;
|
attr_flags |= AttrFlag::BLINK;
|
||||||
|
if (term->cursor_position.x == posx && term->cursor_position.y == posy) {
|
||||||
|
attr_flags |= AttrFlag::CURSOR;
|
||||||
|
}
|
||||||
|
|
||||||
// Collect colors.
|
// Collect colors.
|
||||||
Color fgcol = std::min(attr->fccode, (int8_t)TSM_COLOR_FOREGROUND) >= 0
|
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_image = Image::create(std::max(cols, 1u), std::max(rows, 1u), false, Image::FORMAT_L8);
|
||||||
attr_texture->set_image(attr_image);
|
attr_texture->set_image(attr_image);
|
||||||
|
|
||||||
update_shader_parameters(back_material);
|
set_shader_parameters();
|
||||||
update_shader_parameters(fore_material);
|
|
||||||
|
|
||||||
if (force || prev_cols != cols || prev_rows != rows)
|
if (force || prev_cols != cols || prev_rows != rows)
|
||||||
emit_signal("size_changed", Vector2i(cols, rows));
|
emit_signal("size_changed", Vector2i(cols, rows));
|
||||||
|
@ -480,13 +483,18 @@ void Terminal::update_sizes(bool force)
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::update_shader_parameters(Ref<ShaderMaterial> material)
|
void Terminal::set_shader_parameters(const String ¶m, const Variant &value)
|
||||||
{
|
{
|
||||||
material->set_shader_parameter("cols", cols);
|
if (param.is_empty()) {
|
||||||
material->set_shader_parameter("rows", rows);
|
set_shader_parameters("cols", cols);
|
||||||
material->set_shader_parameter("size", size);
|
set_shader_parameters("rows", rows);
|
||||||
material->set_shader_parameter("cell_size", cell_size);
|
set_shader_parameters("size", size);
|
||||||
material->set_shader_parameter("grid_size", Vector2(cols * cell_size.x, rows * cell_size.y));
|
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() {
|
void Terminal::initialize_rendering() {
|
||||||
|
@ -614,6 +622,7 @@ void Terminal::draw_screen() {
|
||||||
}
|
}
|
||||||
|
|
||||||
rs->canvas_item_clear(char_canvas_item);
|
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);
|
framebuffer_age = tsm_screen_draw(screen, Terminal::_draw_cb, this);
|
||||||
attr_texture->update(attr_image);
|
attr_texture->update(attr_image);
|
||||||
back_texture->update(back_image);
|
back_texture->update(back_image);
|
||||||
|
@ -752,8 +761,7 @@ void Terminal::set_inverse_mode(const int mode) {
|
||||||
inverse_mode = static_cast<InverseMode>(mode);
|
inverse_mode = static_cast<InverseMode>(mode);
|
||||||
|
|
||||||
bool inverse_enabled = inverse_mode == InverseMode::INVERSE_MODE_INVERT;
|
bool inverse_enabled = inverse_mode == InverseMode::INVERSE_MODE_INVERT;
|
||||||
back_material->set_shader_parameter("inverse_enabled", inverse_enabled);
|
set_shader_parameters("inverse_enabled", inverse_enabled);
|
||||||
fore_material->set_shader_parameter("inverse_enabled", inverse_enabled);
|
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ namespace godot
|
||||||
{
|
{
|
||||||
INVERSE = 1 << 0,
|
INVERSE = 1 << 0,
|
||||||
BLINK = 1 << 1,
|
BLINK = 1 << 1,
|
||||||
|
CURSOR = 1 << 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum InverseMode {
|
enum InverseMode {
|
||||||
|
@ -130,6 +131,7 @@ namespace godot
|
||||||
double font_offset;
|
double font_offset;
|
||||||
Vector2 size;
|
Vector2 size;
|
||||||
Vector2 cell_size;
|
Vector2 cell_size;
|
||||||
|
Vector2i cursor_position;
|
||||||
|
|
||||||
Ref<Image> attr_image;
|
Ref<Image> attr_image;
|
||||||
Ref<ImageTexture> attr_texture;
|
Ref<ImageTexture> attr_texture;
|
||||||
|
@ -157,7 +159,7 @@ namespace godot
|
||||||
void initialize_rendering();
|
void initialize_rendering();
|
||||||
void update_theme();
|
void update_theme();
|
||||||
void update_sizes(bool force = false);
|
void update_sizes(bool force = false);
|
||||||
void update_shader_parameters(Ref<ShaderMaterial> material);
|
void set_shader_parameters(const String &name = "", const Variant &value = nullptr);
|
||||||
bool redraw_requested = false;
|
bool redraw_requested = false;
|
||||||
void _on_frame_post_draw();
|
void _on_frame_post_draw();
|
||||||
void draw_screen();
|
void draw_screen();
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#define FLAG_INVERSE 1 << 0
|
#define FLAG_INVERSE 1 << 0
|
||||||
#define FLAG_BLINK 1 << 1
|
#define FLAG_BLINK 1 << 1
|
||||||
|
#define FLAG_CURSOR 1 << 2
|
||||||
|
|
||||||
#define transparent vec4(0)
|
#define transparent vec4(0)
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ uniform vec2 grid_size;
|
||||||
|
|
||||||
uniform sampler2D attributes;
|
uniform sampler2D attributes;
|
||||||
uniform bool inverse_enabled = true;
|
uniform bool inverse_enabled = true;
|
||||||
|
uniform bool has_focus = false;
|
||||||
|
|
||||||
#ifdef BACKGROUND
|
#ifdef BACKGROUND
|
||||||
uniform vec4 background_color;
|
uniform vec4 background_color;
|
||||||
|
@ -48,17 +50,21 @@ void fragment() {
|
||||||
color = texture(TEXTURE, UV);
|
color = texture(TEXTURE, UV);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (has_attribute(sample_uv, FLAG_INVERSE) && inverse_enabled) {
|
bool unfocused_cursor = !has_focus && has_attribute(sample_uv, FLAG_CURSOR);
|
||||||
|
|
||||||
#ifdef BACKGROUND
|
#ifdef BACKGROUND
|
||||||
|
if (has_attribute(sample_uv, FLAG_INVERSE) && inverse_enabled) {
|
||||||
vec4 bg_color = textureLod(screen_texture, SCREEN_UV, 0.0);
|
vec4 bg_color = textureLod(screen_texture, SCREEN_UV, 0.0);
|
||||||
vec4 target_color;
|
vec4 target_color;
|
||||||
target_color.a = color.a + (1.0 - color.a) * bg_color.a;
|
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);
|
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);
|
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
|
#ifdef FOREGROUND
|
||||||
if (has_attribute(sample_uv, FLAG_BLINK)) {
|
if (has_attribute(sample_uv, FLAG_BLINK)) {
|
||||||
|
@ -70,9 +76,18 @@ void fragment() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(FOREGROUND) || defined(BACKGROUND)
|
#ifdef BACKGROUND
|
||||||
COLOR = color;
|
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
|
#endif
|
||||||
|
|
||||||
|
COLOR = color;
|
||||||
} else { // Outside the grid.
|
} else { // Outside the grid.
|
||||||
COLOR = transparent;
|
COLOR = transparent;
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 984 B After Width: | Height: | Size: 992 B |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 517 B |
BIN
test/visual_regression/baseline/empty_focused.png
Normal file
After Width: | Height: | Size: 517 B |
BIN
test/visual_regression/baseline/hollow_cursor.png
Normal file
After Width: | Height: | Size: 810 B |
Before Width: | Height: | Size: 548 B After Width: | Height: | Size: 550 B |
Before Width: | Height: | Size: 560 B After Width: | Height: | Size: 561 B |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.8 KiB |
|
@ -51,6 +51,16 @@ class TestVisualRegression:
|
||||||
await wait_frames(30)
|
await wait_frames(30)
|
||||||
assert_match("empty")
|
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():
|
func test_default_theme():
|
||||||
# Print every background color.
|
# Print every background color.
|
||||||
for i in range(8):
|
for i in range(8):
|
||||||
|
|