feat(term): implement clear() method

Clears all but the bottommost row of the terminal (including scrollback
buffer) and moves the bottommost row to the top.
This commit is contained in:
Leroy Hopson 2024-03-03 22:25:14 +13:00
parent fc03595e29
commit d00a31fb45
No known key found for this signature in database
GPG key ID: D2747312A6DB51AA
4 changed files with 82 additions and 12 deletions

View file

@ -75,8 +75,8 @@ void Terminal::_bind_methods()
ClassDB::bind_method(D_METHOD("get_copy_on_selection"), &Terminal::get_copy_on_selection);
ClassDB::add_property("Terminal", PropertyInfo(Variant::BOOL, "copy_on_selection"), "set_copy_on_selection", "get_copy_on_selection");
// Methods.
// Other methods.
ClassDB::bind_method(D_METHOD("clear"), &Terminal::clear);
ClassDB::bind_method(D_METHOD("write", "data"), &Terminal::write);
ClassDB::bind_method(D_METHOD("get_cursor_pos"), &Terminal::get_cursor_pos);
ClassDB::bind_method(D_METHOD("get_cell_size"), &Terminal::get_cell_size);
@ -642,6 +642,19 @@ double Terminal::get_blink_off_time() const
return blink_off_time;
}
void Terminal::clear() {
// Resize the terminal to a single row, forcing content above in to the scrollback buffer.
tsm_screen_resize(screen, cols, 1);
// Clear the scrollback buffer (hence clearing the content that was above).
tsm_screen_clear_sb(screen);
// Resize the screen to its original size.
tsm_screen_resize(screen, cols, rows);
refresh();
}
String Terminal::_copy_screen(ScreenCopyFunction func) {
char *out;
PackedByteArray data;

View file

@ -72,6 +72,8 @@ namespace godot
void set_blink_off_time(const double p_blink_off_time);
double get_blink_off_time() const;
void clear();
String copy_all();
String copy_selection();
void set_copy_on_selection(const bool p_enable);

View file

@ -12,7 +12,7 @@ func get_described_class():
func pick_cell_color(cell := Vector2i(0, 0)) -> Color:
var cell_size = subject.get_cell_size()
var pixelv = Vector2(cell) * cell_size + (cell_size / 2)
return get_viewport().get_texture().get_image().get_pixelv(cell_size / 2)
return get_viewport().get_texture().get_image().get_pixelv(pixelv)
func before_each():
@ -27,9 +27,20 @@ func before_each():
class TestRendering:
extends RenderingTest
# Fill the terminal with colored blocks.
func fill_color(color = Color.BLACK, rows: int = subject.get_rows()):
subject.write("\u001b[38;2;%d;%d;%dm" % [color.r8, color.g8, color.b8])
subject.write("".repeat(subject.get_cols() * rows))
func test_render():
fill_color(Color.BLUE)
await wait_for_signal(subject.draw, 3)
await wait_frames(15)
var cell_color = pick_cell_color()
assert_eq(cell_color, Color.BLUE)
func test_update():
subject.write("\u001b[38;2;255;0;0m")
subject.write("".repeat(subject.get_cols() * subject.get_rows()))
fill_color(Color.RED)
await get_tree().physics_frame
subject.queue_redraw()
await wait_for_signal(subject.draw, 3)
@ -37,6 +48,30 @@ class TestRendering:
var cell_color = pick_cell_color(Vector2i(0, 0))
assert_eq(cell_color, Color.RED)
func test_clear_clears_all_but_the_first_row():
await wait_frames(15)
var cell = Vector2i(0, 1) # Pick a cell not on the first row.
var original_color = pick_cell_color(cell)
call_deferred("fill_color", Color.CYAN)
await wait_for_signal(subject.draw, 3)
await wait_frames(15)
subject.clear()
await wait_for_signal(subject.draw, 3)
await wait_frames(15)
var cell_color = pick_cell_color(cell)
assert_eq(cell_color, original_color)
func test_clear_keeps_the_last_row():
fill_color(Color.GREEN)
fill_color(Color.ORANGE, 1)
await wait_for_signal(subject.draw, 3)
await wait_frames(15)
subject.clear()
await wait_for_signal(subject.draw, 3)
await wait_frames(15)
var cell_color = pick_cell_color(Vector2i(0, 0))
assert_eq(cell_color, Color.ORANGE)
class TestKeyPressed:
extends RenderingTest

View file

@ -13,6 +13,13 @@ func before_each():
subject.size = Vector2(400, 200)
# Helper function to fill the screen with the given character.
func fill_screen(char: String = "A") -> String:
var result = char.repeat(subject.get_cols() * subject.get_rows())
subject.write(result)
return result
class TestInterface:
extends TerminalTest
@ -44,8 +51,7 @@ class TestInterface:
# Methods.
# TODO: Implement clear() method.
func xtest_has_method_clear():
func test_has_method_clear():
assert_has_method_with_return_type("clear", TYPE_NIL)
func test_has_method_copy_all():
@ -196,11 +202,6 @@ class TestWrite:
class TestCopy:
extends TerminalTest
func fill_screen(char: String = "A") -> String:
var result = char.repeat(subject.get_cols() * subject.get_rows())
subject.write(result)
return result
func test_copy_all_copies_the_entire_screen():
var text = fill_screen()
# The text will be wrapped over multiple lines and copy_all() preserves
@ -220,3 +221,22 @@ class TestCopy:
var text = "アイウエオカキクケコサシスセソ"
subject.write(text)
assert_string_contains(subject.copy_all(), text)
class TestClear:
extends TerminalTest
func test_clear_an_empty_screen_changes_nothing():
var empty_screen = subject.copy_all()
subject.clear()
var screen_after = subject.copy_all()
assert_eq(screen_after, empty_screen)
func test_clear_when_screen_is_full_clears_all_but_the_bottommost_row():
fill_screen()
var final_line = "THIS SHOULDN'T BE CLEARED"
subject.write(final_line)
subject.clear()
var screen_after = subject.copy_all()
var expected = final_line + "\n".repeat(subject.get_rows())
assert_eq(screen_after, expected)