feat(term): add select method

Adds select() method to Terminal. Method behaves the same way as
TextEdit's select method.
This commit is contained in:
Leroy Hopson 2024-04-26 13:58:49 +12:00 committed by Leroy Hopson
parent 43303a51bf
commit dd118d72f3
5 changed files with 112 additions and 8 deletions

View file

@ -3,6 +3,7 @@
#include "terminal.h" #include "terminal.h"
#include <algorithm>
#include <godot_cpp/classes/control.hpp> #include <godot_cpp/classes/control.hpp>
#include <godot_cpp/classes/font.hpp> #include <godot_cpp/classes/font.hpp>
#include <godot_cpp/classes/image_texture.hpp> #include <godot_cpp/classes/image_texture.hpp>
@ -70,6 +71,9 @@ void Terminal::_bind_methods()
ClassDB::bind_method(D_METHOD("set_blink_off_time", "time"), &Terminal::set_blink_off_time); ClassDB::bind_method(D_METHOD("set_blink_off_time", "time"), &Terminal::set_blink_off_time);
ClassDB::add_property("Terminal", PropertyInfo(Variant::FLOAT, "blink_off_time"), "set_blink_off_time", "get_blink_off_time"); ClassDB::add_property("Terminal", PropertyInfo(Variant::FLOAT, "blink_off_time"), "set_blink_off_time", "get_blink_off_time");
// Selection.
ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column"), &Terminal::select);
// Copying. // Copying.
ClassDB::bind_method(D_METHOD("copy_all"), &Terminal::copy_all); ClassDB::bind_method(D_METHOD("copy_all"), &Terminal::copy_all);
ClassDB::bind_method(D_METHOD("copy_selection"), &Terminal::copy_selection); ClassDB::bind_method(D_METHOD("copy_selection"), &Terminal::copy_selection);
@ -657,6 +661,26 @@ String Terminal::_copy_screen(ScreenCopyFunction func) {
return data.get_string_from_utf8(); return data.get_string_from_utf8();
} }
void Terminal::select(const int p_from_line, const int p_from_column, const int p_to_line, const int p_to_column) {
int from_line = std::clamp((int)p_from_line, 0, (int)rows);
int from_column = std::clamp((int)p_from_column, 0, (int)cols);
int to_line = std::clamp((int)p_to_line, 0, (int)rows);
int to_column = std::clamp((int)p_to_column, 0, (int)cols);
if (from_line > to_line) {
std::swap(to_line, from_line);
std::swap(to_column, from_column);
} else if ((from_line == to_line) && (from_column > to_column)) {
std::swap(to_column, from_column);
}
to_column -= 1;
tsm_screen_selection_reset(screen);
tsm_screen_selection_start(screen, from_column, from_line);
tsm_screen_selection_target(screen, to_column, to_line);
}
String Terminal::copy_all() { String Terminal::copy_all() {
return _copy_screen(&tsm_screen_copy_all); return _copy_screen(&tsm_screen_copy_all);
} }

View file

@ -74,6 +74,8 @@ namespace godot
void clear(); void clear();
void select(const int p_from_line, const int p_from_column, const int p_to_line, const int p_to_column);
String copy_all(); String copy_all();
String copy_selection(); String copy_selection();
void set_copy_on_selection(const bool p_enable); void set_copy_on_selection(const bool p_enable);

View file

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased](https://github.com/lihop/godot-xterm/compare/v2.2.0...HEAD) ## [Unreleased](https://github.com/lihop/godot-xterm/compare/v2.2.0...HEAD)
### Added
- Added select() method to Terminal.
### Changed ### Changed
- Custom export templates are no longer required when exporting to HTML5 from Godot v3.5.x. - Custom export templates are no longer required when exporting to HTML5 from Godot v3.5.x.

View file

@ -43,12 +43,13 @@ For example if the string `"\u001b[38;2;0;255;0;mA"` was written to the terminal
## Methods ## Methods
| Returns | Signature | | Returns | Signature |
| ---------- | ------------------------------------------------------------------- | | ---------- | ------------------------------------------------------------------------------------------------------------ |
| void | [clear](#mthd-clear) **( )** | | void | [clear](#mthd-clear) **( )** |
| {{String}} | [copy_all](#mthd-copy_all) **( )** | | {{String}} | [copy_all](#mthd-copy_all) **( )** |
| {{String}} | [copy_selection](#mthd-copy_selection) **( )** | | {{String}} | [copy_selection](#mthd-copy_selection) **( )** |
| {{int}} | [get_cols](#mthd-get_cols) **( )** | | {{int}} | [get_cols](#mthd-get_cols) **( )** |
| {{int}} | [get_rows](#mthd-get_rows) **( )** | | {{int}} | [get_rows](#mthd-get_rows) **( )** |
| void | [select](#mthd-select) **(** {{int}} from_line, {{int}} from_column, {{int}} to_line {{int}} to_column **)** |
| void | [write](#mthd-write) **(** {{String}}\|{{PoolByteArray}} data **)** | | void | [write](#mthd-write) **(** {{String}}\|{{PoolByteArray}} data **)** |
## Signals ## Signals
@ -189,6 +190,12 @@ Returns the height of the terminal in characters.
When using a monospace font, this is the number of visible characters that can fit from the top of the terminal to the bottom in a single column. When using a monospace font, this is the number of visible characters that can fit from the top of the terminal to the bottom in a single column.
It will automatically update according to the terminal's rect_size and theme's font size. It will automatically update according to the terminal's rect_size and theme's font size.
<hr id="mthd-select" />
void **select** **(** {{int}} from_line, {{int}} from_column, {{int}} to_line, {{int}} to_column **)**
Perform selection, from line/column to line/column.
<hr id="mthd-write" /> <hr id="mthd-write" />
void **write** **(** {{String}}\|{{PoolByteArray}} data **)** void **write** **(** {{String}}\|{{PoolByteArray}} data **)**

View file

@ -243,3 +243,70 @@ class TestClear:
var screen_after = subject.copy_all() var screen_after = subject.copy_all()
var expected = final_line + "\n".repeat(subject.get_rows()) var expected = final_line + "\n".repeat(subject.get_rows())
assert_eq(screen_after, expected) assert_eq(screen_after, expected)
class TestSelect:
extends TerminalTest
# Use the behavior of TextEdit's select() method as a reference.
var text_edit: TextEdit
func assert_select_eq(argv, expected):
text_edit.callv("select", argv)
subject.callv("select", argv)
assert_eq(
expected,
text_edit.get_selected_text(),
"expected does not match reference implementation"
)
assert_eq(subject.copy_selection(), expected)
func before_each():
super.before_each()
text_edit = TextEdit.new()
text_edit.text = "0123456789\nABCDEFGHIJ\n)!@#$%^&*(\n\n\n\n\n\n\n"
add_child_autofree(text_edit)
subject.write("0123456789\r\nABCDEFGHIJ\r\n)!@#$%^&*(")
func test_select_nothing():
assert_select_eq([0, 0, 0, 0], "")
func test_select_first_character():
assert_select_eq([0, 0, 0, 1], "0")
func test_select_last_character():
assert_select_eq([2, 9, 2, 10], "(")
func test_select_reverse_column():
assert_select_eq([0, 6, 0, 1], "12345")
func test_select_preceeds_column_bounds():
assert_select_eq([0, -2, 0, -1], "")
assert_select_eq([0, -2, 0, 0], "")
assert_select_eq([0, -2, 0, 1], "0")
func test_select_exceeds_column_bounds():
assert_select_eq([0, 5, 0, 999], "56789")
func test_select_first_row():
assert_select_eq([0, 0, 0, 10], "0123456789")
func test_select_second_row():
assert_select_eq([1, 0, 1, 10], "ABCDEFGHIJ")
func test_select_multiple_rows():
assert_select_eq([0, 0, 1, 10], "0123456789\nABCDEFGHIJ")
func test_select_rows_reverse():
assert_select_eq([1, 5, 0, 0], "0123456789\nABCDE")
func test_select_preceeds_row_bounds():
assert_select_eq([-2, 0, -1, 10], "0123456789")
assert_select_eq([-2, 0, 0, 10], "0123456789")
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")
func test_wide_bounds():
assert_select_eq([-999, -999, 999, 999], "0123456789\nABCDEFGHIJ\n)!@#$%^&*(\n\n\n\n\n\n\n")