diff --git a/DevTools/HackStudio/Editor.cpp b/DevTools/HackStudio/Editor.cpp new file mode 100644 index 00000000000..073658fecfc --- /dev/null +++ b/DevTools/HackStudio/Editor.cpp @@ -0,0 +1,9 @@ +#include "Editor.h" +#include "EditorWrapper.h" + +void Editor::focusin_event(CEvent& event) +{ + if (on_focus) + on_focus(); + GTextEditor::focusin_event(event); +} diff --git a/DevTools/HackStudio/Editor.h b/DevTools/HackStudio/Editor.h new file mode 100644 index 00000000000..349984eaaba --- /dev/null +++ b/DevTools/HackStudio/Editor.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class Editor final : public GTextEditor { + C_OBJECT(Editor) +public: + virtual ~Editor() override {} + + Function on_focus; + +private: + virtual void focusin_event(CEvent& event) override; + + Editor(GWidget* parent) + : GTextEditor(GTextEditor::MultiLine, parent) + { + } +}; diff --git a/DevTools/HackStudio/EditorWrapper.cpp b/DevTools/HackStudio/EditorWrapper.cpp new file mode 100644 index 00000000000..4192e83f153 --- /dev/null +++ b/DevTools/HackStudio/EditorWrapper.cpp @@ -0,0 +1,62 @@ +#include "EditorWrapper.h" +#include "Editor.h" +#include +#include +#include +#include + +extern RefPtr g_current_editor_wrapper; + +EditorWrapper::EditorWrapper(GWidget* parent) + : GWidget(parent) +{ + set_layout(make(Orientation::Vertical)); + + auto label_wrapper = GWidget::construct(this); + label_wrapper->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed); + label_wrapper->set_preferred_size(0, 16); + label_wrapper->set_fill_with_background_color(true); + label_wrapper->set_layout(make(Orientation::Horizontal)); + + m_filename_label = GLabel::construct("(Untitled)", label_wrapper); + m_filename_label->set_font(Font::default_bold_font()); + m_filename_label->set_text_alignment(TextAlignment::CenterLeft); + m_filename_label->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed); + m_filename_label->set_preferred_size(0, 16); + + m_cursor_label = GLabel::construct("(Cursor)", label_wrapper); + m_cursor_label->set_text_alignment(TextAlignment::CenterRight); + m_cursor_label->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed); + m_cursor_label->set_preferred_size(0, 16); + + m_editor = Editor::construct(this); + m_editor->set_ruler_visible(true); + m_editor->set_line_wrapping_enabled(true); + m_editor->set_automatic_indentation_enabled(true); + + m_editor->on_cursor_change = [this] { + m_cursor_label->set_text(String::format("Line: %d, Column: %d", m_editor->cursor().line() + 1, m_editor->cursor().column())); + }; + + m_editor->on_focus = [this] { + g_current_editor_wrapper = this; + }; + + m_editor->add_custom_context_menu_action(GAction::create( + "Go to line...", { Mod_Ctrl, Key_L }, GraphicsBitmap::load_from_file("/res/icons/16x16/go-forward.png"), [this](auto&) { + auto input_box = GInputBox::construct("Line:", "Go to line", window()); + auto result = input_box->exec(); + if (result == GInputBox::ExecOK) { + bool ok; + auto line_number = input_box->text_value().to_uint(ok); + if (ok) { + m_editor->set_cursor(line_number - 1, 0); + } + } + }, + m_editor)); +} + +EditorWrapper::~EditorWrapper() +{ +} diff --git a/DevTools/HackStudio/EditorWrapper.h b/DevTools/HackStudio/EditorWrapper.h new file mode 100644 index 00000000000..b0b932525f9 --- /dev/null +++ b/DevTools/HackStudio/EditorWrapper.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class GLabel; +class Editor; + +class EditorWrapper : public GWidget { + C_OBJECT(EditorWrapper) +public: + virtual ~EditorWrapper() override; + + Editor& editor() { return *m_editor; } + const Editor& editor() const { return *m_editor; } + + GLabel& filename_label() { return *m_filename_label; } + +private: + explicit EditorWrapper(GWidget* parent = nullptr); + + RefPtr m_filename_label; + RefPtr m_cursor_label; + RefPtr m_editor; +}; diff --git a/DevTools/HackStudio/FindInFilesWidget.cpp b/DevTools/HackStudio/FindInFilesWidget.cpp index 666f84fc511..ec89f8866e1 100644 --- a/DevTools/HackStudio/FindInFilesWidget.cpp +++ b/DevTools/HackStudio/FindInFilesWidget.cpp @@ -5,7 +5,7 @@ #include #include -extern GTextEditor& main_editor(); +extern GTextEditor& current_editor(); extern void open_file(const String&); extern OwnPtr g_project; @@ -71,8 +71,8 @@ FindInFilesWidget::FindInFilesWidget(GWidget* parent) int line_number = parts[1].to_int(ok); ASSERT(ok); open_file(parts[0]); - main_editor().set_cursor(line_number - 1, 0); - main_editor().set_focus(true); + current_editor().set_cursor(line_number - 1, 0); + current_editor().set_focus(true); }; m_button->on_click = [this](auto&) { diff --git a/DevTools/HackStudio/Makefile b/DevTools/HackStudio/Makefile index fa6e23fe761..f5e17f115b6 100644 --- a/DevTools/HackStudio/Makefile +++ b/DevTools/HackStudio/Makefile @@ -7,6 +7,8 @@ OBJS = \ FindInFilesWidget.o \ ProcessStateWidget.o \ CppLexer.o \ + Editor.o \ + EditorWrapper.o \ main.o APP = HackStudio diff --git a/DevTools/HackStudio/main.cpp b/DevTools/HackStudio/main.cpp index c1b78fd77cf..9ab890e34dd 100644 --- a/DevTools/HackStudio/main.cpp +++ b/DevTools/HackStudio/main.cpp @@ -1,4 +1,6 @@ #include "CppLexer.h" +#include "Editor.h" +#include "EditorWrapper.h" #include "FindInFilesWidget.h" #include "Project.h" #include "TerminalWrapper.h" @@ -10,12 +12,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include @@ -25,16 +27,28 @@ #include #include +RefPtr g_current_editor_wrapper; + String g_currently_open_file; OwnPtr g_project; RefPtr g_window; RefPtr g_project_list_view; -RefPtr g_text_editor; -GTextEditor& main_editor() +void add_new_editor(GWidget& parent) { - ASSERT(g_text_editor); - return *g_text_editor; + auto wrapper = EditorWrapper::construct(&parent); + g_current_editor_wrapper = wrapper; +} + +EditorWrapper& current_editor_wrapper() +{ + ASSERT(g_current_editor_wrapper); + return *g_current_editor_wrapper; +} + +Editor& current_editor() +{ + return current_editor_wrapper().editor(); } static void build(TerminalWrapper&); @@ -72,10 +86,8 @@ int main(int argc, char** argv) g_project_list_view->set_preferred_size(200, 0); auto inner_splitter = GSplitter::construct(Orientation::Vertical, outer_splitter); - g_text_editor = GTextEditor::construct(GTextEditor::MultiLine, inner_splitter); - g_text_editor->set_ruler_visible(true); - g_text_editor->set_line_wrapping_enabled(true); - g_text_editor->set_automatic_indentation_enabled(true); + add_new_editor(inner_splitter); + add_new_editor(inner_splitter); auto new_action = GAction::create("Add new file to project...", { Mod_Ctrl, Key_N }, GraphicsBitmap::load_from_file("/res/icons/16x16/new.png"), [&](const GAction&) { auto input_box = GInputBox::construct("Enter name of new file:", "Add new file to project", g_window); @@ -110,19 +122,20 @@ int main(int argc, char** argv) auto save_action = GAction::create("Save", { Mod_Ctrl, Key_S }, GraphicsBitmap::load_from_file("/res/icons/16x16/save.png"), [&](auto&) { if (g_currently_open_file.is_empty()) return; - g_text_editor->write_to_file(g_currently_open_file); + current_editor().write_to_file(g_currently_open_file); }); toolbar->add_action(new_action); toolbar->add_action(add_existing_file_action); toolbar->add_action(save_action); toolbar->add_separator(); - toolbar->add_action(g_text_editor->cut_action()); - toolbar->add_action(g_text_editor->copy_action()); - toolbar->add_action(g_text_editor->paste_action()); + + toolbar->add_action(GCommonActions::make_cut_action([&](auto&) { current_editor().cut_action().activate(); })); + toolbar->add_action(GCommonActions::make_copy_action([&](auto&) { current_editor().copy_action().activate(); })); + toolbar->add_action(GCommonActions::make_paste_action([&](auto&) { current_editor().paste_action().activate(); })); toolbar->add_separator(); - toolbar->add_action(g_text_editor->undo_action()); - toolbar->add_action(g_text_editor->redo_action()); + toolbar->add_action(GCommonActions::make_undo_action([&](auto&) { current_editor().undo_action().activate(); })); + toolbar->add_action(GCommonActions::make_redo_action([&](auto&) { current_editor().redo_action().activate(); })); toolbar->add_separator(); g_project_list_view->on_activation = [&](auto& index) { @@ -156,26 +169,6 @@ int main(int argc, char** argv) auto terminal_wrapper = TerminalWrapper::construct(nullptr); tab_widget->add_widget("Console", terminal_wrapper); - auto statusbar = GStatusBar::construct(widget); - - g_text_editor->on_cursor_change = [&] { - statusbar->set_text(String::format("Line: %d, Column: %d", g_text_editor->cursor().line() + 1, g_text_editor->cursor().column())); - }; - - g_text_editor->add_custom_context_menu_action(GAction::create( - "Go to line...", { Mod_Ctrl, Key_L }, GraphicsBitmap::load_from_file("/res/icons/16x16/go-forward.png"), [&](auto&) { - auto input_box = GInputBox::construct("Line:", "Go to line", g_window); - auto result = input_box->exec(); - if (result == GInputBox::ExecOK) { - bool ok; - auto line_number = input_box->text_value().to_uint(ok); - if (ok) { - g_text_editor->set_cursor(line_number - 1, 0); - } - } - }, - g_text_editor)); - auto menubar = make(); auto app_menu = make("HackStudio"); app_menu->add_action(save_action); @@ -274,7 +267,7 @@ static TextStyle style_for_token_type(CppToken::Type type) static void rehighlight() { - auto text = g_text_editor->text(); + auto text = current_editor().text(); CppLexer lexer(text); auto tokens = lexer.lex(); @@ -291,8 +284,8 @@ static void rehighlight() span.font = style.font; spans.append(span); } - g_text_editor->set_spans(spans); - g_text_editor->update(); + current_editor().set_spans(spans); + current_editor().update(); } void open_file(const String& filename) @@ -303,18 +296,20 @@ void open_file(const String& filename) return; } auto contents = file->read_all(); - g_text_editor->set_text(contents); + current_editor().set_text(contents); if (filename.ends_with(".cpp")) { - g_text_editor->on_change = [] { rehighlight(); }; + current_editor().on_change = [] { rehighlight(); }; rehighlight(); } else { - g_text_editor->on_change = nullptr; + current_editor().on_change = nullptr; } g_currently_open_file = filename; g_window->set_title(String::format("%s - HackStudio", g_currently_open_file.characters())); g_project_list_view->update(); - g_text_editor->set_focus(true); + current_editor_wrapper().filename_label().set_text(filename); + + current_editor().set_focus(true); }