mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-24 02:12:09 -05:00
LibGUI: Indent selected text on tab press
If selected text is less than a whole line, usual delete/replace takes place. Otherwise, if the selected text is a whole line or spans multiple lines, the selection will be indented.
This commit is contained in:
parent
e98f8f20df
commit
00f51d42d2
4 changed files with 80 additions and 2 deletions
|
@ -932,6 +932,33 @@ String ReplaceAllTextCommand::action_text() const
|
|||
return m_action_text;
|
||||
}
|
||||
|
||||
IndentSelection::IndentSelection(TextDocument& document, size_t tab_width, TextRange const& range)
|
||||
: TextDocumentUndoCommand(document)
|
||||
, m_tab_width(tab_width)
|
||||
, m_range(range)
|
||||
{
|
||||
}
|
||||
|
||||
void IndentSelection::redo()
|
||||
{
|
||||
auto const tab = String::repeated(' ', m_tab_width);
|
||||
|
||||
for (size_t i = m_range.start().line(); i <= m_range.end().line(); i++) {
|
||||
m_document.insert_at({ i, 0 }, tab, m_client);
|
||||
}
|
||||
|
||||
m_document.set_all_cursors(m_range.start());
|
||||
}
|
||||
|
||||
void IndentSelection::undo()
|
||||
{
|
||||
for (size_t i = m_range.start().line(); i <= m_range.end().line(); i++) {
|
||||
m_document.remove({ { i, 0 }, { i, m_tab_width } });
|
||||
}
|
||||
|
||||
m_document.set_all_cursors(m_range.start());
|
||||
}
|
||||
|
||||
TextPosition TextDocument::insert_at(TextPosition const& position, StringView text, Client const* client)
|
||||
{
|
||||
TextPosition cursor = position;
|
||||
|
|
|
@ -259,4 +259,16 @@ private:
|
|||
String m_action_text;
|
||||
};
|
||||
|
||||
class IndentSelection : public TextDocumentUndoCommand {
|
||||
public:
|
||||
IndentSelection(TextDocument&, size_t tab_width, TextRange const&);
|
||||
virtual void undo() override;
|
||||
virtual void redo() override;
|
||||
TextRange const& range() const { return m_range; }
|
||||
|
||||
private:
|
||||
size_t m_tab_width { 0 };
|
||||
TextRange m_range;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -867,6 +867,15 @@ void TextEditor::keydown_event(KeyEvent& event)
|
|||
return;
|
||||
}
|
||||
|
||||
if (event.key() == KeyCode::Key_Tab) {
|
||||
if (has_selection()) {
|
||||
if (is_indenting_selection()) {
|
||||
indent_selection();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event.key() == KeyCode::Key_Delete) {
|
||||
if (!is_editable())
|
||||
return;
|
||||
|
@ -952,6 +961,33 @@ void TextEditor::keydown_event(KeyEvent& event)
|
|||
event.ignore();
|
||||
}
|
||||
|
||||
bool TextEditor::is_indenting_selection()
|
||||
{
|
||||
auto const selection_start = m_selection.start() > m_selection.end() ? m_selection.end() : m_selection.start();
|
||||
auto const selection_end = m_selection.end() > m_selection.start() ? m_selection.end() : m_selection.start();
|
||||
auto const whole_line_selected = selection_end.column() - selection_start.column() >= current_line().length() - current_line().first_non_whitespace_column();
|
||||
auto const on_same_line = selection_start.line() == selection_end.line();
|
||||
|
||||
if (has_selection() && (whole_line_selected || !on_same_line)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TextEditor::indent_selection()
|
||||
{
|
||||
auto const selection_start = m_selection.start() > m_selection.end() ? m_selection.end() : m_selection.start();
|
||||
auto const selection_end = m_selection.end() > m_selection.start() ? m_selection.end() : m_selection.start();
|
||||
|
||||
if (is_indenting_selection()) {
|
||||
execute<IndentSelection>(m_soft_tab_width, TextRange(selection_start, selection_end));
|
||||
m_selection.set_start({ selection_start.line(), selection_start.column() + m_soft_tab_width });
|
||||
m_selection.set_end({ selection_end.line(), selection_end.column() + m_soft_tab_width });
|
||||
set_cursor({ m_cursor.line(), m_cursor.column() + m_soft_tab_width });
|
||||
}
|
||||
}
|
||||
|
||||
void TextEditor::delete_previous_word()
|
||||
{
|
||||
TextRange to_erase(document().first_word_before(m_cursor, true), m_cursor);
|
||||
|
@ -1444,7 +1480,7 @@ void TextEditor::insert_at_cursor_or_replace_selection(StringView text)
|
|||
{
|
||||
ReflowDeferrer defer(*this);
|
||||
VERIFY(is_editable());
|
||||
if (has_selection())
|
||||
if (has_selection() && !is_indenting_selection())
|
||||
delete_selection();
|
||||
|
||||
// Check if adding a newline leaves the previous line as just whitespace.
|
||||
|
@ -1453,6 +1489,7 @@ void TextEditor::insert_at_cursor_or_replace_selection(StringView text)
|
|||
&& clear_length > 0
|
||||
&& current_line().leading_spaces() == clear_length;
|
||||
|
||||
if (!is_indenting_selection())
|
||||
execute<InsertTextCommand>(text, m_cursor);
|
||||
|
||||
if (should_clear_last_line) { // If it does leave just whitespace, clear it.
|
||||
|
|
|
@ -151,6 +151,8 @@ public:
|
|||
void select_current_line();
|
||||
virtual void undo();
|
||||
virtual void redo();
|
||||
bool is_indenting_selection();
|
||||
void indent_selection();
|
||||
|
||||
Function<void()> on_change;
|
||||
Function<void(bool modified)> on_modified_change;
|
||||
|
|
Loading…
Add table
Reference in a new issue