1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 20:37:35 +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.
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 max_selection_row = max(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());
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();
}
@ -491,23 +491,16 @@ void TerminalWidget::set_opacity(u8 new_opacity)
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
{
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
@ -516,6 +509,8 @@ bool TerminalWidget::selection_contains(const VT::Position& position) const
return false;
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 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());
@ -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 >= 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
@ -564,8 +560,7 @@ void TerminalWidget::doubleclick_event(GUI::MouseEvent& event)
end_column = column;
}
m_selection_start = { position.row(), start_column };
m_selection_end = { position.row(), end_column };
m_selection.set({ position.row(), start_column }, { position.row(), end_column });
update_copy_action();
}
GUI::Frame::doubleclick_event(event);
@ -631,11 +626,9 @@ void TerminalWidget::mousedown_event(GUI::MouseEvent& event)
int end_column = m_terminal.columns() - 1;
auto position = buffer_position_at(event.position());
m_selection_start = { position.row(), start_column };
m_selection_end = { position.row(), end_column };
m_selection.set({ position.row(), start_column }, { position.row(), end_column });
} else {
m_selection_start = buffer_position_at(event.position());
m_selection_end = {};
m_selection.set(buffer_position_at(event.position()), {});
}
if (m_alt_key_held)
m_rectangle_selection = true;
@ -700,9 +693,9 @@ void TerminalWidget::mousemove_event(GUI::MouseEvent& event)
else
m_auto_scroll_direction = AutoScrollDirection::None;
auto old_selection_end = m_selection_end;
m_selection_end = position;
if (old_selection_end != m_selection_end) {
VT::Position old_selection_end = m_selection.end();
m_selection.set_end(position);
if (old_selection_end != m_selection.end()) {
update_copy_action();
update();
}
@ -744,8 +737,10 @@ void TerminalWidget::set_scroll_length(int length)
String TerminalWidget::selected_text() const
{
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) {
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
{
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
{
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()

View file

@ -34,6 +34,7 @@
#include <LibGUI/Frame.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Rect.h>
#include <LibVT/Range.h>
#include <LibVT/Terminal.h>
class TerminalWidget final : public GUI::Frame
@ -76,9 +77,9 @@ public:
bool has_selection() const;
bool selection_contains(const VT::Position&) 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 normalized_selection_start() const;
VT::Position normalized_selection_end() const;
void scroll_to_bottom();
@ -146,8 +147,7 @@ private:
VT::Terminal m_terminal;
VT::Position m_selection_start;
VT::Position m_selection_end;
VT::Range m_selection;
String m_hovered_href;
String m_hovered_href_id;