1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 05:37:43 +00:00

LibVT: Create VT::Range and use it to replace selection start / end

Based on GUI::TextRange, This is both a bit more expressive and will
eventually be used for searching within the terminal
This commit is contained in:
Idan Horowitz 2020-12-28 02:24:37 +02:00 committed by Andreas Kling
parent 6efdabfc6f
commit 6446135681
3 changed files with 120 additions and 36 deletions

87
Libraries/LibVT/Range.h Normal file
View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibVT/Position.h>
namespace VT {
class Range {
public:
Range() { }
Range(const VT::Position& start, const VT::Position& end)
: m_start(start)
, m_end(end)
{
}
bool is_valid() const { return m_start.is_valid() && m_end.is_valid(); }
void clear()
{
m_start = {};
m_end = {};
}
VT::Position& start() { return m_start; }
VT::Position& end() { return m_end; }
const VT::Position& start() const { return m_start; }
const VT::Position& end() const { return m_end; }
Range normalized() const { return Range(normalized_start(), normalized_end()); }
void set_start(const VT::Position& position) { m_start = position; }
void set_end(const VT::Position& position) { m_end = position; }
void set(const VT::Position& start, const VT::Position& end)
{
m_start = start;
m_end = end;
}
bool operator==(const Range& other) const
{
return m_start == other.m_start && m_end == other.m_end;
}
bool contains(const VT::Position& position) const
{
if (!(position.row() > m_start.row() || (position.row() == m_start.row() && position.column() >= m_start.column())))
return false;
if (!(position.row() < m_end.row() || (position.row() == m_end.row() && position.column() <= m_end.column())))
return false;
return true;
}
private:
VT::Position normalized_start() const { return m_start < m_end ? m_start : m_end; }
VT::Position normalized_end() const { return m_start < m_end ? m_end : m_start; }
VT::Position m_start;
VT::Position m_end;
};
};

View file

@ -255,11 +255,11 @@ void TerminalWidget::keydown_event(GUI::KeyEvent& event)
// Clear the selection if we type in/behind it. // Clear the selection if we type in/behind it.
auto future_cursor_column = (event.key() == KeyCode::Key_Backspace) ? m_terminal.cursor_column() - 1 : m_terminal.cursor_column(); auto future_cursor_column = (event.key() == KeyCode::Key_Backspace) ? m_terminal.cursor_column() - 1 : m_terminal.cursor_column();
auto min_selection_row = min(m_selection_start.row(), m_selection_end.row()); auto min_selection_row = min(m_selection.start().row(), m_selection.end().row());
auto max_selection_row = max(m_selection_start.row(), m_selection_end.row()); auto max_selection_row = max(m_selection.start().row(), m_selection.end().row());
if (future_cursor_column <= last_selection_column_on_row(m_terminal.cursor_row()) && m_terminal.cursor_row() >= min_selection_row && m_terminal.cursor_row() <= max_selection_row) { if (future_cursor_column <= last_selection_column_on_row(m_terminal.cursor_row()) && m_terminal.cursor_row() >= min_selection_row && m_terminal.cursor_row() <= max_selection_row) {
m_selection_end = {}; m_selection.set_end({});
update_copy_action(); update_copy_action();
update(); update();
} }
@ -491,23 +491,16 @@ void TerminalWidget::set_opacity(u8 new_opacity)
force_repaint(); force_repaint();
} }
VT::Position TerminalWidget::normalized_selection_start() const
{
if (m_selection_start < m_selection_end)
return m_selection_start;
return m_selection_end;
}
VT::Position TerminalWidget::normalized_selection_end() const
{
if (m_selection_start < m_selection_end)
return m_selection_end;
return m_selection_start;
}
bool TerminalWidget::has_selection() const bool TerminalWidget::has_selection() const
{ {
return m_selection_start.is_valid() && m_selection_end.is_valid(); return m_selection.is_valid();
}
void TerminalWidget::set_selection(const VT::Range& selection)
{
m_selection = selection;
update_copy_action();
update();
} }
bool TerminalWidget::selection_contains(const VT::Position& position) const bool TerminalWidget::selection_contains(const VT::Position& position) const
@ -516,6 +509,8 @@ bool TerminalWidget::selection_contains(const VT::Position& position) const
return false; return false;
if (m_rectangle_selection) { if (m_rectangle_selection) {
auto m_selection_start = m_selection.start();
auto m_selection_end = m_selection.end();
auto min_selection_column = min(m_selection_start.column(), m_selection_end.column()); auto min_selection_column = min(m_selection_start.column(), m_selection_end.column());
auto max_selection_column = max(m_selection_start.column(), m_selection_end.column()); auto max_selection_column = max(m_selection_start.column(), m_selection_end.column());
auto min_selection_row = min(m_selection_start.row(), m_selection_end.row()); auto min_selection_row = min(m_selection_start.row(), m_selection_end.row());
@ -524,7 +519,8 @@ bool TerminalWidget::selection_contains(const VT::Position& position) const
return position.column() >= min_selection_column && position.column() <= max_selection_column && position.row() >= min_selection_row && position.row() <= max_selection_row; return position.column() >= min_selection_column && position.column() <= max_selection_column && position.row() >= min_selection_row && position.row() <= max_selection_row;
} }
return position >= normalized_selection_start() && position <= normalized_selection_end(); auto normalized_selection = m_selection.normalized();
return position >= normalized_selection.start() && position <= normalized_selection.end();
} }
VT::Position TerminalWidget::buffer_position_at(const Gfx::IntPoint& position) const VT::Position TerminalWidget::buffer_position_at(const Gfx::IntPoint& position) const
@ -564,8 +560,7 @@ void TerminalWidget::doubleclick_event(GUI::MouseEvent& event)
end_column = column; end_column = column;
} }
m_selection_start = { position.row(), start_column }; m_selection.set({ position.row(), start_column }, { position.row(), end_column });
m_selection_end = { position.row(), end_column };
update_copy_action(); update_copy_action();
} }
GUI::Frame::doubleclick_event(event); GUI::Frame::doubleclick_event(event);
@ -631,11 +626,9 @@ void TerminalWidget::mousedown_event(GUI::MouseEvent& event)
int end_column = m_terminal.columns() - 1; int end_column = m_terminal.columns() - 1;
auto position = buffer_position_at(event.position()); auto position = buffer_position_at(event.position());
m_selection_start = { position.row(), start_column }; m_selection.set({ position.row(), start_column }, { position.row(), end_column });
m_selection_end = { position.row(), end_column };
} else { } else {
m_selection_start = buffer_position_at(event.position()); m_selection.set(buffer_position_at(event.position()), {});
m_selection_end = {};
} }
if (m_alt_key_held) if (m_alt_key_held)
m_rectangle_selection = true; m_rectangle_selection = true;
@ -700,9 +693,9 @@ void TerminalWidget::mousemove_event(GUI::MouseEvent& event)
else else
m_auto_scroll_direction = AutoScrollDirection::None; m_auto_scroll_direction = AutoScrollDirection::None;
auto old_selection_end = m_selection_end; VT::Position old_selection_end = m_selection.end();
m_selection_end = position; m_selection.set_end(position);
if (old_selection_end != m_selection_end) { if (old_selection_end != m_selection.end()) {
update_copy_action(); update_copy_action();
update(); update();
} }
@ -744,8 +737,10 @@ void TerminalWidget::set_scroll_length(int length)
String TerminalWidget::selected_text() const String TerminalWidget::selected_text() const
{ {
StringBuilder builder; StringBuilder builder;
auto start = normalized_selection_start();
auto end = normalized_selection_end(); auto normalized_selection = m_selection.normalized();
auto start = normalized_selection.start();
auto end = normalized_selection.end();
for (int row = start.row(); row <= end.row(); ++row) { for (int row = start.row(); row <= end.row(); ++row) {
int first_column = first_selection_column_on_row(row); int first_column = first_selection_column_on_row(row);
@ -774,12 +769,14 @@ String TerminalWidget::selected_text() const
int TerminalWidget::first_selection_column_on_row(int row) const int TerminalWidget::first_selection_column_on_row(int row) const
{ {
return row == normalized_selection_start().row() || m_rectangle_selection ? normalized_selection_start().column() : 0; auto normalized_selection_start = m_selection.normalized().start();
return row == normalized_selection_start.row() || m_rectangle_selection ? normalized_selection_start.column() : 0;
} }
int TerminalWidget::last_selection_column_on_row(int row) const int TerminalWidget::last_selection_column_on_row(int row) const
{ {
return row == normalized_selection_end().row() || m_rectangle_selection ? normalized_selection_end().column() : m_terminal.columns() - 1; auto normalized_selection_end = m_selection.normalized().end();
return row == normalized_selection_end.row() || m_rectangle_selection ? normalized_selection_end.column() : m_terminal.columns() - 1;
} }
void TerminalWidget::terminal_history_changed() void TerminalWidget::terminal_history_changed()

View file

@ -34,6 +34,7 @@
#include <LibGUI/Frame.h> #include <LibGUI/Frame.h>
#include <LibGfx/Bitmap.h> #include <LibGfx/Bitmap.h>
#include <LibGfx/Rect.h> #include <LibGfx/Rect.h>
#include <LibVT/Range.h>
#include <LibVT/Terminal.h> #include <LibVT/Terminal.h>
class TerminalWidget final : public GUI::Frame class TerminalWidget final : public GUI::Frame
@ -76,9 +77,9 @@ public:
bool has_selection() const; bool has_selection() const;
bool selection_contains(const VT::Position&) const; bool selection_contains(const VT::Position&) const;
String selected_text() const; String selected_text() const;
VT::Range normalized_selection() const { return m_selection.normalized(); }
void set_selection(const VT::Range& selection);
VT::Position buffer_position_at(const Gfx::IntPoint&) const; VT::Position buffer_position_at(const Gfx::IntPoint&) const;
VT::Position normalized_selection_start() const;
VT::Position normalized_selection_end() const;
void scroll_to_bottom(); void scroll_to_bottom();
@ -146,8 +147,7 @@ private:
VT::Terminal m_terminal; VT::Terminal m_terminal;
VT::Position m_selection_start; VT::Range m_selection;
VT::Position m_selection_end;
String m_hovered_href; String m_hovered_href;
String m_hovered_href_id; String m_hovered_href_id;