summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorLucas CHOLLET <lucas.chollet@free.fr>2022-06-07 22:32:35 +0200
committerLinus Groh <mail@linusgroh.de>2022-07-19 10:49:38 +0100
commit7a8104e79b9c8b4bb63d9ad8386ed73125ca07a5 (patch)
tree9c6a6f18eb64dd3734769c6d5ec5ba9c10b9488d /Userland
parentcf693136e25eb6bbee6f0cecf229cc56820c8808 (diff)
downloadserenity-7a8104e79b9c8b4bb63d9ad8386ed73125ca07a5.zip
LibGUI: Add MoveLineUpOrDownCommand
This allows lines moved by Ctrl+Shift+[Up, Down] to be registered as a command, i.e. cancellable by Ctrl+Z. This patch also introduces the usage of TextDocument::[take, insert]_line. Those functions forward changes to the visual lines and then avoid some data mismatch. Co-authored-by: Jorropo <jorropo.pgm@gmail.com>
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibGUI/EditingEngine.cpp153
-rw-r--r--Userland/Libraries/LibGUI/EditingEngine.h25
-rw-r--r--Userland/Libraries/LibGUI/TextEditor.h6
3 files changed, 137 insertions, 47 deletions
diff --git a/Userland/Libraries/LibGUI/EditingEngine.cpp b/Userland/Libraries/LibGUI/EditingEngine.cpp
index 5f2d6cfc0d..028a7bb24e 100644
--- a/Userland/Libraries/LibGUI/EditingEngine.cpp
+++ b/Userland/Libraries/LibGUI/EditingEngine.cpp
@@ -259,8 +259,11 @@ EditingEngine::DidMoveALine EditingEngine::move_one_up(KeyEvent const& event)
{
if (m_editor->cursor().line() > 0 || m_editor->is_wrapping_enabled()) {
if (event.ctrl() && event.shift()) {
- move_selected_lines_up();
- return DidMoveALine::Yes;
+ if (MoveLineUpOrDownCommand::valid_operation(*this, VerticalDirection::Up)) {
+ m_editor->execute<MoveLineUpOrDownCommand>(Badge<EditingEngine> {}, event, *this);
+ return DidMoveALine::Yes;
+ }
+ return DidMoveALine::No;
}
TextPosition new_cursor;
if (m_editor->is_wrapping_enabled()) {
@@ -280,8 +283,11 @@ EditingEngine::DidMoveALine EditingEngine::move_one_down(KeyEvent const& event)
{
if (m_editor->cursor().line() < (m_editor->line_count() - 1) || m_editor->is_wrapping_enabled()) {
if (event.ctrl() && event.shift()) {
- move_selected_lines_down();
- return DidMoveALine::Yes;
+ if (MoveLineUpOrDownCommand::valid_operation(*this, VerticalDirection::Down)) {
+ m_editor->execute<MoveLineUpOrDownCommand>(Badge<EditingEngine> {}, event, *this);
+ return DidMoveALine::Yes;
+ }
+ return DidMoveALine::No;
}
TextPosition new_cursor;
if (m_editor->is_wrapping_enabled()) {
@@ -353,6 +359,11 @@ void EditingEngine::move_to_last_line()
m_editor->set_cursor(m_editor->line_count() - 1, m_editor->lines()[m_editor->line_count() - 1].length());
};
+void EditingEngine::get_selection_line_boundaries(Badge<MoveLineUpOrDownCommand>, size_t& first_line, size_t& last_line)
+{
+ get_selection_line_boundaries(first_line, last_line);
+}
+
void EditingEngine::get_selection_line_boundaries(size_t& first_line, size_t& last_line)
{
auto selection = m_editor->normalized_selection();
@@ -367,67 +378,119 @@ void EditingEngine::get_selection_line_boundaries(size_t& first_line, size_t& la
last_line -= 1;
}
-void EditingEngine::move_selected_lines_up()
+void EditingEngine::delete_char()
{
if (!m_editor->is_editable())
return;
- size_t first_line;
- size_t last_line;
- get_selection_line_boundaries(first_line, last_line);
+ m_editor->do_delete();
+};
- if (first_line == 0)
+void EditingEngine::delete_line()
+{
+ if (!m_editor->is_editable())
return;
+ m_editor->delete_current_line();
+};
- auto& lines = m_editor->document().lines();
- lines.insert((int)last_line, lines.take((int)first_line - 1));
- m_editor->set_cursor({ m_editor->cursor().line() - 1, m_editor->cursor().column() });
+MoveLineUpOrDownCommand::MoveLineUpOrDownCommand(TextDocument& document, KeyEvent event, EditingEngine& engine)
+ : TextDocumentUndoCommand(document)
+ , m_event(move(event))
+ , m_direction(key_code_to_vertical_direction(m_event.key()))
+ , m_engine(engine)
+ , m_selection(m_engine.editor().selection())
+ , m_cursor(m_engine.editor().cursor())
+{
+}
- if (m_editor->has_selection()) {
- m_editor->selection().start().set_line(m_editor->selection().start().line() - 1);
- m_editor->selection().end().set_line(m_editor->selection().end().line() - 1);
- }
+void MoveLineUpOrDownCommand::redo()
+{
+ move_lines(m_direction);
+}
- m_editor->did_change();
- m_editor->update();
+void MoveLineUpOrDownCommand::undo()
+{
+ move_lines(!m_direction);
}
-void EditingEngine::move_selected_lines_down()
+bool MoveLineUpOrDownCommand::merge_with(GUI::Command const&)
{
- if (!m_editor->is_editable())
- return;
- size_t first_line;
- size_t last_line;
- get_selection_line_boundaries(first_line, last_line);
+ return false;
+}
- auto& lines = m_editor->document().lines();
- VERIFY(lines.size() != 0);
- if (last_line >= lines.size() - 1)
- return;
+String MoveLineUpOrDownCommand::action_text() const
+{
+ return "Move a line";
+}
+
+bool MoveLineUpOrDownCommand::valid_operation(EditingEngine& engine, VerticalDirection direction)
+{
- lines.insert((int)first_line, lines.take((int)last_line + 1));
- m_editor->set_cursor({ m_editor->cursor().line() + 1, m_editor->cursor().column() });
+ VERIFY(engine.editor().line_count() != 0);
- if (m_editor->has_selection()) {
- m_editor->selection().start().set_line(m_editor->selection().start().line() + 1);
- m_editor->selection().end().set_line(m_editor->selection().end().line() + 1);
- }
+ auto const& selection = engine.editor().selection().normalized();
+ if (selection.is_valid()) {
+ if ((direction == VerticalDirection::Up && selection.start().line() == 0) || (direction == VerticalDirection::Down && selection.end().line() >= engine.editor().line_count() - 1))
+ return false;
+ } else {
+ size_t first_line;
+ size_t last_line;
+ engine.get_selection_line_boundaries(Badge<MoveLineUpOrDownCommand> {}, first_line, last_line);
- m_editor->did_change();
- m_editor->update();
+ if ((direction == VerticalDirection::Up && first_line == 0) || (direction == VerticalDirection::Down && last_line >= engine.editor().line_count() - 1))
+ return false;
+ }
+ return true;
}
-void EditingEngine::delete_char()
+TextRange MoveLineUpOrDownCommand::retrieve_selection(VerticalDirection direction)
{
- if (!m_editor->is_editable())
- return;
- m_editor->do_delete();
-};
+ if (direction == m_direction)
+ return m_selection;
-void EditingEngine::delete_line()
+ auto const offset_selection = [this](auto const offset) {
+ auto tmp = m_selection;
+ tmp.start().set_line(tmp.start().line() + offset);
+ tmp.end().set_line(tmp.end().line() + offset);
+
+ return tmp;
+ };
+
+ if (direction == VerticalDirection::Up)
+ return offset_selection(1);
+ if (direction == VerticalDirection::Down)
+ return offset_selection(-1);
+ VERIFY_NOT_REACHED();
+}
+
+void MoveLineUpOrDownCommand::move_lines(VerticalDirection direction)
{
- if (!m_editor->is_editable())
+ if (m_event.shift() && m_selection.is_valid()) {
+ m_engine.editor().set_selection(retrieve_selection(direction));
+ m_engine.editor().did_update_selection();
+ }
+
+ if (!m_engine.editor().is_editable())
return;
- m_editor->delete_current_line();
-};
+
+ size_t first_line;
+ size_t last_line;
+ m_engine.get_selection_line_boundaries(Badge<MoveLineUpOrDownCommand> {}, first_line, last_line);
+
+ auto const offset = direction == VerticalDirection::Up ? -1 : 1;
+ auto const insertion_index = direction == VerticalDirection::Up ? last_line : first_line;
+ auto const moved_line_index = offset + (direction != VerticalDirection::Up ? last_line : first_line);
+
+ auto moved_line = m_document.take_line(moved_line_index);
+ m_document.insert_line(insertion_index, move(moved_line));
+
+ m_engine.editor().set_cursor({ m_engine.editor().cursor().line() + offset, m_engine.editor().cursor().column() });
+ if (m_engine.editor().has_selection()) {
+ m_engine.editor().selection().start().set_line(m_engine.editor().selection().start().line() + offset);
+ m_engine.editor().selection().end().set_line(m_engine.editor().selection().end().line() + offset);
+ }
+
+ m_engine.editor().did_change();
+ m_engine.editor().update();
+}
}
diff --git a/Userland/Libraries/LibGUI/EditingEngine.h b/Userland/Libraries/LibGUI/EditingEngine.h
index 13c71a3dca..d6dacb1205 100644
--- a/Userland/Libraries/LibGUI/EditingEngine.h
+++ b/Userland/Libraries/LibGUI/EditingEngine.h
@@ -22,6 +22,8 @@ enum EngineType {
Vim,
};
+class MoveLineUpOrDownCommand;
+
class EditingEngine {
AK_MAKE_NONCOPYABLE(EditingEngine);
AK_MAKE_NONMOVABLE(EditingEngine);
@@ -45,6 +47,8 @@ public:
bool is_regular() const { return engine_type() == EngineType::Regular; }
bool is_vim() const { return engine_type() == EngineType::Vim; }
+ void get_selection_line_boundaries(Badge<MoveLineUpOrDownCommand>, size_t& first_line, size_t& last_line);
+
protected:
EditingEngine() = default;
@@ -80,10 +84,27 @@ protected:
void delete_char();
virtual EngineType engine_type() const = 0;
+};
+
+class MoveLineUpOrDownCommand : public TextDocumentUndoCommand {
+public:
+ MoveLineUpOrDownCommand(TextDocument&, KeyEvent event, EditingEngine&);
+ virtual void undo() override;
+ virtual void redo() override;
+ bool merge_with(GUI::Command const&) override;
+ String action_text() const override;
+
+ static bool valid_operation(EditingEngine& engine, VerticalDirection direction);
private:
- void move_selected_lines_up();
- void move_selected_lines_down();
+ void move_lines(VerticalDirection);
+ TextRange retrieve_selection(VerticalDirection);
+
+ KeyEvent m_event;
+ VerticalDirection m_direction;
+ EditingEngine& m_engine;
+ TextRange m_selection;
+ TextPosition m_cursor;
};
}
diff --git a/Userland/Libraries/LibGUI/TextEditor.h b/Userland/Libraries/LibGUI/TextEditor.h
index b5b8dd374f..547ac759a3 100644
--- a/Userland/Libraries/LibGUI/TextEditor.h
+++ b/Userland/Libraries/LibGUI/TextEditor.h
@@ -229,6 +229,12 @@ public:
virtual Optional<UISize> calculated_min_size() const override;
+ template<class T, class... Args>
+ inline void execute(Badge<EditingEngine>, Args&&... args)
+ {
+ execute<T>(forward<Args>(args)...);
+ }
+
protected:
explicit TextEditor(Type = Type::MultiLine);