mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 09:30:24 +00:00
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.
This commit is contained in:
parent
e11ec20650
commit
bd6d0d2295
Notes:
sideshowbarker
2024-07-18 22:38:32 +09:00
Author: https://github.com/supex0fan Commit: https://github.com/SerenityOS/serenity/commit/bd6d0d22958 Pull-request: https://github.com/SerenityOS/serenity/pull/5126 Reviewed-by: https://github.com/awesomekling
10 changed files with 171 additions and 20 deletions
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -84,5 +84,6 @@
|
|||
|
||||
@GUI::StatusBar {
|
||||
name: "statusbar"
|
||||
label_count: 3
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -30,6 +30,11 @@
|
|||
|
||||
namespace GUI {
|
||||
|
||||
RegularEditingEngine::RegularEditingEngine()
|
||||
{
|
||||
m_editing_engine_type = EditingEngineType::Regular;
|
||||
}
|
||||
|
||||
CursorWidth RegularEditingEngine::cursor_width() const
|
||||
{
|
||||
return CursorWidth::NARROW;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue