diff options
author | Zac <zacary.gillerat@connect.qut.edu.au> | 2021-01-27 15:49:29 +1000 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-02-02 16:08:20 +0100 |
commit | bd6d0d229581e830c7a69cdb29252b9f63e0434a (patch) | |
tree | 84a2d3e93e80de3de0dda826632301664e3d9995 /Userland | |
parent | e11ec20650e5182f47675210dcd71e7301c34905 (diff) | |
download | serenity-bd6d0d229581e830c7a69cdb29252b9f63e0434a.zip |
TextEditor: Add vim status indicators to the statusbar
When using the VimEditingEngine in the TextEditor, vim's mode and the
previous key are shown in the editor's statusbar.
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Applications/TextEditor/TextEditorWidget.cpp | 20 | ||||
-rw-r--r-- | Userland/Applications/TextEditor/TextEditorWidget.h | 3 | ||||
-rw-r--r-- | Userland/Applications/TextEditor/TextEditorWindow.gml | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/EditingEngine.h | 9 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/RegularEditingEngine.cpp | 5 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/RegularEditingEngine.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/TextEditor.cpp | 47 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/TextEditor.h | 8 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/VimEditingEngine.cpp | 37 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/VimEditingEngine.h | 59 |
10 files changed, 171 insertions, 20 deletions
diff --git a/Userland/Applications/TextEditor/TextEditorWidget.cpp b/Userland/Applications/TextEditor/TextEditorWidget.cpp index 9d31b106f8..1bd7ac19aa 100644 --- a/Userland/Applications/TextEditor/TextEditorWidget.cpp +++ b/Userland/Applications/TextEditor/TextEditorWidget.cpp @@ -39,11 +39,13 @@ #include <LibGUI/BoxLayout.h> #include <LibGUI/Button.h> #include <LibGUI/CppSyntaxHighlighter.h> +#include <LibGUI/EditingEngine.h> #include <LibGUI/FilePicker.h> #include <LibGUI/FontPicker.h> #include <LibGUI/GMLSyntaxHighlighter.h> #include <LibGUI/INISyntaxHighlighter.h> #include <LibGUI/JSSyntaxHighlighter.h> +#include <LibGUI/Label.h> #include <LibGUI/Menu.h> #include <LibGUI/MenuBar.h> #include <LibGUI/MessageBox.h> @@ -57,6 +59,7 @@ #include <LibGUI/ToolBarContainer.h> #include <LibGUI/VimEditingEngine.h> #include <LibGfx/Font.h> +#include <LibGfx/FontDatabase.h> #include <LibMarkdown/Document.h> #include <LibWeb/OutOfProcessWebView.h> #include <string.h> @@ -301,6 +304,21 @@ TextEditorWidget::TextEditorWidget() m_statusbar = *find_descendant_of_type_named<GUI::StatusBar>("statusbar"); + m_statusbar->label(m_vim_mode_statusbar_index)->set_visible(m_editor->editing_engine()->type() == GUI::EditingEngineType::Vim); + m_statusbar->label(m_vim_mode_statusbar_index)->set_font(Gfx::FontDatabase::default_bold_font()); + + m_statusbar->label(m_vim_previous_keys_statusbar_index)->set_visible(m_editor->editing_engine()->type() == GUI::EditingEngineType::Vim); + + m_editor->on_vim_statusbar_messages_changed = [this] { + m_statusbar->set_text(m_vim_mode_statusbar_index, m_editor->vim_mode_statusbar_message()); + m_statusbar->set_text(m_vim_previous_keys_statusbar_index, m_editor->vim_previous_keys_statusbar_message()); + }; + + m_editor->on_editing_engine_changed = [this]() { + m_statusbar->label(m_vim_mode_statusbar_index)->set_visible(m_editor->editing_engine()->type() == GUI::EditingEngineType::Vim); + m_statusbar->label(m_vim_previous_keys_statusbar_index)->set_visible(m_editor->editing_engine()->type() == GUI::EditingEngineType::Vim); + }; + m_editor->on_cursor_change = [this] { update_statusbar_cursor_position(); }; m_new_action = GUI::Action::create("New", { Mod_Ctrl, Key_N }, Gfx::Bitmap::load_from_file("/res/icons/16x16/new.png"), [this](const GUI::Action&) { @@ -666,5 +684,5 @@ void TextEditorWidget::update_statusbar_cursor_position() { StringBuilder builder; builder.appendff("Line: {}, Column: {}", m_editor->cursor().line() + 1, m_editor->cursor().column()); - m_statusbar->set_text(builder.to_string()); + m_statusbar->set_text(m_cursor_position_statusbar_index, builder.to_string()); } diff --git a/Userland/Applications/TextEditor/TextEditorWidget.h b/Userland/Applications/TextEditor/TextEditorWidget.h index 763087b9e4..28c4a9c3fd 100644 --- a/Userland/Applications/TextEditor/TextEditorWidget.h +++ b/Userland/Applications/TextEditor/TextEditorWidget.h @@ -91,6 +91,9 @@ private: RefPtr<GUI::Action> m_html_preview_action; RefPtr<GUI::StatusBar> m_statusbar; + const int m_cursor_position_statusbar_index = 0; + const int m_vim_mode_statusbar_index = 1; + const int m_vim_previous_keys_statusbar_index = 2; RefPtr<GUI::TextBox> m_find_textbox; RefPtr<GUI::TextBox> m_replace_textbox; diff --git a/Userland/Applications/TextEditor/TextEditorWindow.gml b/Userland/Applications/TextEditor/TextEditorWindow.gml index 433d01abff..b13865f697 100644 --- a/Userland/Applications/TextEditor/TextEditorWindow.gml +++ b/Userland/Applications/TextEditor/TextEditorWindow.gml @@ -84,5 +84,6 @@ @GUI::StatusBar { name: "statusbar" + label_count: 3 } } diff --git a/Userland/Libraries/LibGUI/EditingEngine.h b/Userland/Libraries/LibGUI/EditingEngine.h index a9eb525acf..3da65d2f09 100644 --- a/Userland/Libraries/LibGUI/EditingEngine.h +++ b/Userland/Libraries/LibGUI/EditingEngine.h @@ -37,6 +37,11 @@ enum CursorWidth { WIDE }; +enum EditingEngineType { + Regular, + Vim +}; + class EditingEngine { AK_MAKE_NONCOPYABLE(EditingEngine); AK_MAKE_NONMOVABLE(EditingEngine); @@ -51,11 +56,15 @@ public: virtual bool on_key(const KeyEvent& event); + EditingEngineType type() const { return m_editing_engine_type; } + protected: EditingEngine() { } WeakPtr<TextEditor> m_editor; + EditingEngineType m_editing_engine_type; + void move_one_left(const KeyEvent& event); void move_one_right(const KeyEvent& event); void move_one_up(const KeyEvent& event); diff --git a/Userland/Libraries/LibGUI/RegularEditingEngine.cpp b/Userland/Libraries/LibGUI/RegularEditingEngine.cpp index ffcbe941cd..653d6da7bb 100644 --- a/Userland/Libraries/LibGUI/RegularEditingEngine.cpp +++ b/Userland/Libraries/LibGUI/RegularEditingEngine.cpp @@ -30,6 +30,11 @@ namespace GUI { +RegularEditingEngine::RegularEditingEngine() +{ + m_editing_engine_type = EditingEngineType::Regular; +} + CursorWidth RegularEditingEngine::cursor_width() const { return CursorWidth::NARROW; diff --git a/Userland/Libraries/LibGUI/RegularEditingEngine.h b/Userland/Libraries/LibGUI/RegularEditingEngine.h index 87e51b08d8..c91250333d 100644 --- a/Userland/Libraries/LibGUI/RegularEditingEngine.h +++ b/Userland/Libraries/LibGUI/RegularEditingEngine.h @@ -33,6 +33,8 @@ namespace GUI { class RegularEditingEngine final : public EditingEngine { public: + RegularEditingEngine(); + virtual CursorWidth cursor_width() const override; virtual bool on_key(const KeyEvent& event) override; diff --git a/Userland/Libraries/LibGUI/TextEditor.cpp b/Userland/Libraries/LibGUI/TextEditor.cpp index 55be2949e6..20615bcdce 100644 --- a/Userland/Libraries/LibGUI/TextEditor.cpp +++ b/Userland/Libraries/LibGUI/TextEditor.cpp @@ -40,6 +40,7 @@ #include <LibGUI/ScrollBar.h> #include <LibGUI/SyntaxHighlighter.h> #include <LibGUI/TextEditor.h> +#include <LibGUI/VimEditingEngine.h> #include <LibGUI/Window.h> #include <LibGfx/Bitmap.h> #include <LibGfx/Font.h> @@ -1625,6 +1626,52 @@ void TextEditor::set_editing_engine(OwnPtr<EditingEngine> editing_engine) update_cursor(); stop_timer(); start_timer(500); + + if (on_editing_engine_changed) + on_editing_engine_changed(); + + if (m_editing_engine->type() == EditingEngineType::Vim) { + VimEditingEngine* vim = dynamic_cast<VimEditingEngine*>(m_editing_engine.ptr()); + vim->on_mode_change = [&](VimMode mode) { + switch (mode) { + case Normal: + m_vim_mode_statusbar_message = {}; + break; + case Insert: + m_vim_mode_statusbar_message = "-- INSERT --"; + break; + case Visual: + m_vim_mode_statusbar_message = "-- VISUAL --"; + break; + default: + dbgln("Unhandled vim mode"); + m_vim_mode_statusbar_message = {}; + } + if (on_vim_statusbar_messages_changed) + on_vim_statusbar_messages_changed(); + }; + + // FIXME: Update this method to take multiple previous keys when that is implemented + vim->on_previous_keys_change = [&](const VimEditingEngine::PreviousKey& event, bool has_previous_key) { + if (has_previous_key) { + StringBuilder sb = StringBuilder(1); + sb.append_code_point(event.code_point); + m_vim_previous_keys_statusbar_message = sb.to_string(); + } else { + m_vim_previous_keys_statusbar_message = {}; + } + if (on_vim_statusbar_messages_changed) + on_vim_statusbar_messages_changed(); + }; + } else { + m_vim_mode_statusbar_message = {}; + m_vim_previous_keys_statusbar_message = {}; + if (on_vim_statusbar_messages_changed) + on_vim_statusbar_messages_changed(); + if (on_editing_engine_changed) { + on_editing_engine_changed(); + } + } } int TextEditor::line_height() const diff --git a/Userland/Libraries/LibGUI/TextEditor.h b/Userland/Libraries/LibGUI/TextEditor.h index 956e3cfcce..0bc742b80e 100644 --- a/Userland/Libraries/LibGUI/TextEditor.h +++ b/Userland/Libraries/LibGUI/TextEditor.h @@ -150,6 +150,8 @@ public: Function<void()> on_down_pressed; Function<void()> on_pageup_pressed; Function<void()> on_pagedown_pressed; + Function<void()> on_vim_statusbar_messages_changed; + Function<void()> on_editing_engine_changed; Action& undo_action() { return *m_undo_action; } Action& redo_action() { return *m_redo_action; } @@ -195,6 +197,9 @@ public: void delete_text_range(TextRange); + String vim_mode_statusbar_message() const { return m_vim_mode_statusbar_message; } + String vim_previous_keys_statusbar_message() const { return m_vim_previous_keys_statusbar_message; } + protected: explicit TextEditor(Type = Type::MultiLine); @@ -351,6 +356,9 @@ private: Gfx::IntPoint m_last_mousemove_position; RefPtr<Gfx::Bitmap> m_icon; + + String m_vim_mode_statusbar_message {}; + String m_vim_previous_keys_statusbar_message {}; }; } diff --git a/Userland/Libraries/LibGUI/VimEditingEngine.cpp b/Userland/Libraries/LibGUI/VimEditingEngine.cpp index 60a6a2b21b..213e267d35 100644 --- a/Userland/Libraries/LibGUI/VimEditingEngine.cpp +++ b/Userland/Libraries/LibGUI/VimEditingEngine.cpp @@ -30,6 +30,11 @@ namespace GUI { +VimEditingEngine::VimEditingEngine() +{ + m_editing_engine_type = EditingEngineType::Vim; +} + CursorWidth VimEditingEngine::cursor_width() const { return m_vim_mode == VimMode::Insert ? CursorWidth::NARROW : CursorWidth::WIDE; @@ -101,19 +106,19 @@ bool VimEditingEngine::on_key_in_normal_mode(const KeyEvent& event) delete_to.set_column(delete_to.column() + 1); m_editor->delete_text_range(TextRange(m_editor->cursor(), delete_to).normalized()); } - m_previous_key = {}; + clear_previous_key(); } else if (m_previous_key == KeyCode::Key_G) { if (event.key() == KeyCode::Key_G) { move_to_first_line(); } else if (event.key() == KeyCode::Key_E) { move_to_end_of_previous_word(); } - m_previous_key = {}; + clear_previous_key(); } else if (m_previous_key == KeyCode::Key_Y) { if (event.key() == KeyCode::Key_Y) { yank(Line); } - m_previous_key = {}; + clear_previous_key(); } else if (m_previous_key == KeyCode::Key_C) { if (event.key() == KeyCode::Key_C) { // Needed because the code to replace the deleted line is called after delete_line() so @@ -169,7 +174,7 @@ bool VimEditingEngine::on_key_in_normal_mode(const KeyEvent& event) m_editor->delete_text_range(TextRange(adjusted_cursor, delete_to).normalized()); switch_to_insert_mode(); } - m_previous_key = {}; + clear_previous_key(); } else { // Handle first any key codes that are to be applied regardless of modifiers. switch (event.key()) { @@ -240,7 +245,7 @@ bool VimEditingEngine::on_key_in_normal_mode(const KeyEvent& event) move_to_beginning_of_previous_word(); break; case (KeyCode::Key_C): - m_previous_key = event.key(); + set_previous_key(event); break; case (KeyCode::Key_Backspace): case (KeyCode::Key_H): @@ -248,13 +253,13 @@ bool VimEditingEngine::on_key_in_normal_mode(const KeyEvent& event) move_one_left(event); break; case (KeyCode::Key_D): - m_previous_key = event.key(); + set_previous_key(event); break; case (KeyCode::Key_E): move_to_end_of_next_word(); break; case (KeyCode::Key_G): - m_previous_key = event.key(); + set_previous_key(event); break; case (KeyCode::Key_Down): case (KeyCode::Key_J): @@ -293,7 +298,7 @@ bool VimEditingEngine::on_key_in_normal_mode(const KeyEvent& event) switch_to_visual_mode(); break; case (KeyCode::Key_Y): - m_previous_key = event.key(); + set_previous_key(event); break; case (KeyCode::Key_P): put(event); @@ -316,7 +321,7 @@ bool VimEditingEngine::on_key_in_visual_mode(const KeyEvent& event) move_to_end_of_previous_word(); update_selection_on_cursor_move(); } - m_previous_key = {}; + clear_previous_key(); } else { // Handle first any key codes that are to be applied regardless of modifiers. switch (event.key()) { @@ -391,7 +396,7 @@ bool VimEditingEngine::on_key_in_visual_mode(const KeyEvent& event) update_selection_on_cursor_move(); break; case (KeyCode::Key_G): - m_previous_key = event.key(); + set_previous_key(event); break; case (KeyCode::Key_Down): case (KeyCode::Key_J): @@ -448,26 +453,32 @@ void VimEditingEngine::switch_to_normal_mode() { m_vim_mode = VimMode::Normal; m_editor->reset_cursor_blink(); - m_previous_key = {}; + clear_previous_key(); clear_visual_mode_data(); + if (on_mode_change) + on_mode_change(m_vim_mode); }; void VimEditingEngine::switch_to_insert_mode() { m_vim_mode = VimMode::Insert; m_editor->reset_cursor_blink(); - m_previous_key = {}; + clear_previous_key(); clear_visual_mode_data(); + if (on_mode_change) + on_mode_change(m_vim_mode); }; void VimEditingEngine::switch_to_visual_mode() { m_vim_mode = VimMode::Visual; m_editor->reset_cursor_blink(); - m_previous_key = {}; + clear_previous_key(); m_selection_start_position = m_editor->cursor(); m_editor->selection()->set(m_editor->cursor(), { m_editor->cursor().line(), m_editor->cursor().column() + 1 }); m_editor->did_update_selection(); + if (on_mode_change) + on_mode_change(m_vim_mode); } void VimEditingEngine::update_selection_on_cursor_move() diff --git a/Userland/Libraries/LibGUI/VimEditingEngine.h b/Userland/Libraries/LibGUI/VimEditingEngine.h index 007472d0f9..7af4e60677 100644 --- a/Userland/Libraries/LibGUI/VimEditingEngine.h +++ b/Userland/Libraries/LibGUI/VimEditingEngine.h @@ -30,20 +30,48 @@ namespace GUI { +enum VimMode { + Normal, + Insert, + Visual +}; + class VimEditingEngine final : public EditingEngine { public: + VimEditingEngine(); + virtual CursorWidth cursor_width() const override; virtual bool on_key(const KeyEvent& event) override; -private: - enum VimMode { - Normal, - Insert, - Visual + class PreviousKey { + public: + PreviousKey() = default; + PreviousKey(const KeyEvent& event) + : key(event.key()) + , code_point(event.code_point()) + { + } + + bool operator==(const KeyCode& key) const + { + return this->key == key; + } + + bool operator==(const u32& code_point) const + { + return this->code_point == code_point; + } + + KeyCode key {}; + u32 code_point {}; }; + Function<void(VimMode)> on_mode_change; + Function<void(const PreviousKey&, bool has_previous_key)> on_previous_keys_change; + +private: enum YankType { Line, Selection @@ -61,7 +89,26 @@ private: void update_selection_on_cursor_move(); void clear_visual_mode_data(); - KeyCode m_previous_key {}; + // FIXME Support multiple previous keys, this is a temporary measure. + PreviousKey m_previous_key {}; + bool has_previous_key { false }; + + void set_previous_key(PreviousKey event) + { + m_previous_key = event; + has_previous_key = true; + if (on_previous_keys_change) + on_previous_keys_change(m_previous_key, has_previous_key); + } + + void clear_previous_key() + { + m_previous_key = {}; + has_previous_key = false; + if (on_previous_keys_change) + on_previous_keys_change(m_previous_key, has_previous_key); + } + void switch_to_normal_mode(); void switch_to_insert_mode(); void switch_to_visual_mode(); |