diff --git a/addons/godot_xterm/native/src/terminal.cpp b/addons/godot_xterm/native/src/terminal.cpp index 44486f3..ba90e85 100644 --- a/addons/godot_xterm/native/src/terminal.cpp +++ b/addons/godot_xterm/native/src/terminal.cpp @@ -286,9 +286,15 @@ void Terminal::_register_methods() { register_method("sb_up", &Terminal::sb_up); register_method("sb_down", &Terminal::sb_down); + register_method("start_selection", &Terminal::start_selection); + register_method("select_to_pointer", &Terminal::select_to_pointer); + register_method("reset_selection", &Terminal::reset_selection); + register_method("_update_theme", &Terminal::update_theme); register_method("_update_size", &Terminal::update_theme); + register_property("cell_size", &Terminal::cell_size, + Vector2(0, 0)); register_property("rows", &Terminal::rows, 24); register_property("cols", &Terminal::cols, 80); register_property("update_mode", &Terminal::update_mode, @@ -547,11 +553,10 @@ void Terminal::update_size() { // Recalculates the cell_size and number of cols/rows based on font size and // the Control's rect_size. - Ref fontref = fontmap.count("Regular") - ? fontmap["Regular"] - : has_font("Regular", "Terminal") - ? get_font("Regular", "Terminal") - : get_font(""); + Ref fontref = fontmap.count("Regular") ? fontmap["Regular"] + : has_font("Regular", "Terminal") + ? get_font("Regular", "Terminal") + : get_font(""); cell_size = fontref->get_string_size("W"); rows = std::max(2, (int)floor(get_rect().size.y / cell_size.y)); @@ -577,4 +582,19 @@ void Terminal::sb_up(int num) { void Terminal::sb_down(int num) { tsm_screen_sb_down(screen, num); update(); +} + +void Terminal::start_selection(Vector2 position) { + tsm_screen_selection_start(screen, position.x, position.y); + update(); +} + +void Terminal::select_to_pointer(Vector2 position) { + tsm_screen_selection_target(screen, position.x, position.y); + update(); +} + +void Terminal::reset_selection() { + tsm_screen_selection_reset(screen); + update(); } \ No newline at end of file diff --git a/addons/godot_xterm/native/src/terminal.h b/addons/godot_xterm/native/src/terminal.h index c4dbb9e..c5ea690 100644 --- a/addons/godot_xterm/native/src/terminal.h +++ b/addons/godot_xterm/native/src/terminal.h @@ -29,7 +29,6 @@ private: static void _populate_key_list(); static uint32_t mapkey(std::pair key); - Vector2 cell_size; std::map palette = {}; std::map> fontmap = {}; @@ -59,6 +58,10 @@ public: void sb_up(int num); void sb_down(int num); + void start_selection(Vector2 position); + void select_to_pointer(Vector2 position); + void reset_selection(); + enum UpdateMode { DISABLED, AUTO, @@ -66,6 +69,7 @@ public: ALL_NEXT_FRAME, }; + Vector2 cell_size; int rows; int cols; int update_mode; diff --git a/addons/godot_xterm/nodes/terminal/terminal.gd b/addons/godot_xterm/nodes/terminal/terminal.gd index 90d42ae..8c3affb 100644 --- a/addons/godot_xterm/nodes/terminal/terminal.gd +++ b/addons/godot_xterm/nodes/terminal/terminal.gd @@ -21,6 +21,11 @@ enum UpdateMode { ALL_NEXT_FRAME, } +enum SelectionMode { + NONE, + POINTER, +} + export (UpdateMode) var update_mode = UpdateMode.AUTO setget set_update_mode var cols = 2 @@ -30,6 +35,11 @@ var _viewport: Viewport = preload("./viewport.tscn").instance() var _native_terminal: Control = _viewport.get_node("Terminal") var _screen := TextureRect.new() var _visibility_notifier := VisibilityNotifier2D.new() + +var _selecting := false +var _selecting_mode: int = SelectionMode.NONE +var _selection_timer := Timer.new() + var _dirty := false var buffer := StreamPeerBuffer.new() @@ -61,6 +71,14 @@ func write(data) -> void: _native_terminal.update() +func copy_selection() -> String: + return _native_terminal.copy_selection() + + +func copy_all() -> String: + return _native_terminal.copy_all() + + func _ready(): if theme: _native_terminal.theme = theme @@ -79,9 +97,13 @@ func _ready(): _visibility_notifier.connect("screen_entered", self, "_refresh") + _selection_timer.wait_time = 0.05 + _selection_timer.connect("timeout", self, "_on_selection_held") + add_child(_viewport) add_child(_screen) add_child(_visibility_notifier) + add_child(_selection_timer) _refresh() @@ -94,6 +116,7 @@ func _refresh(): func _gui_input(event): _native_terminal._gui_input(event) _handle_mouse_wheel(event) + _handle_selection(event) func _handle_mouse_wheel(event: InputEventMouseButton): @@ -117,6 +140,41 @@ func _handle_mouse_wheel(event: InputEventMouseButton): _native_terminal.sb_down(3 * event.factor) +func _handle_selection(event: InputEventMouse): + if event is InputEventMouseButton: + if not event or not event.is_pressed() or not event.button_index == BUTTON_LEFT: + return + + if _selecting: + _selecting = false + _selecting_mode = SelectionMode.NONE + _native_terminal.reset_selection() + + # Single-click select pointer. + _selecting = false + _selecting_mode = SelectionMode.POINTER + + elif event is InputEventMouseMotion: + if ( + event.button_mask & BUTTON_MASK_LEFT + and _selecting_mode != SelectionMode.NONE + and not _selecting + ): + _selecting = true + _native_terminal.start_selection(_mouse_to_cell(event.position)) + _selection_timer.start() + + +func _on_selection_held() -> void: + if not Input.is_mouse_button_pressed(BUTTON_LEFT) or _selecting_mode == SelectionMode.NONE: + _selection_timer.stop() + return + + var position: Vector2 = _mouse_to_cell(get_local_mouse_position()) + _native_terminal.select_to_pointer(position) + _selection_timer.start() + + func _notification(what: int) -> void: match what: NOTIFICATION_RESIZED: @@ -146,6 +204,10 @@ func _on_bell(): emit_signal("bell") +func _mouse_to_cell(pos: Vector2) -> Vector2: + return Vector2(pos / _native_terminal.cell_size) + + func _set_size_warning(value): if value: push_warning(