diff options
-rw-r--r-- | Userland/DevTools/GMLPlayground/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/DevTools/GMLPlayground/MainWidget.cpp | 305 | ||||
-rw-r--r-- | Userland/DevTools/GMLPlayground/MainWidget.h | 46 | ||||
-rw-r--r-- | Userland/DevTools/GMLPlayground/main.cpp | 288 |
4 files changed, 361 insertions, 279 deletions
diff --git a/Userland/DevTools/GMLPlayground/CMakeLists.txt b/Userland/DevTools/GMLPlayground/CMakeLists.txt index 05b55f8dc8..5ac8aec5ed 100644 --- a/Userland/DevTools/GMLPlayground/CMakeLists.txt +++ b/Userland/DevTools/GMLPlayground/CMakeLists.txt @@ -7,6 +7,7 @@ serenity_component( compile_gml(GMLPlaygroundWindow.gml GMLPlaygroundWindowGML.h gml_playground_window_gml) set(SOURCES + MainWidget.cpp main.cpp ) diff --git a/Userland/DevTools/GMLPlayground/MainWidget.cpp b/Userland/DevTools/GMLPlayground/MainWidget.cpp new file mode 100644 index 0000000000..02ed2d7802 --- /dev/null +++ b/Userland/DevTools/GMLPlayground/MainWidget.cpp @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2021, Julius Heijmen <julius.heijmen@gmail.com> + * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org> + * Copyright (c) 2022-2023, Sam Atkins <atkinssj@serenityos.org> + * Copyright (c) 2023, Karol Kosek <krkk@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "MainWidget.h" +#include <LibDesktop/Launcher.h> +#include <LibFileSystemAccessClient/Client.h> +#include <LibGUI/Application.h> +#include <LibGUI/GML/AutocompleteProvider.h> +#include <LibGUI/GML/Formatter.h> +#include <LibGUI/GML/SyntaxHighlighter.h> +#include <LibGUI/Icon.h> +#include <LibGUI/Menu.h> +#include <LibGUI/Menubar.h> +#include <LibGUI/MessageBox.h> +#include <LibGUI/Painter.h> +#include <LibGUI/RegularEditingEngine.h> +#include <LibGUI/Splitter.h> +#include <LibGUI/TextEditor.h> +#include <LibGUI/Toolbar.h> +#include <LibGUI/VimEditingEngine.h> +#include <Userland/DevTools/GMLPlayground/GMLPlaygroundWindowGML.h> + +namespace { + +class UnregisteredWidget final : public GUI::Widget { + C_OBJECT(UnregisteredWidget); + +private: + UnregisteredWidget(DeprecatedString const& class_name); + + virtual void paint_event(GUI::PaintEvent& event) override; + + DeprecatedString m_text; +}; + +UnregisteredWidget::UnregisteredWidget(DeprecatedString const& class_name) +{ + StringBuilder builder; + builder.append(class_name); + builder.append("\nnot registered"sv); + m_text = builder.to_deprecated_string(); +} + +void UnregisteredWidget::paint_event(GUI::PaintEvent& event) +{ + GUI::Painter painter(*this); + painter.add_clip_rect(event.rect()); + painter.fill_rect(event.rect(), Gfx::Color::DarkRed); + painter.draw_text(rect(), m_text, Gfx::TextAlignment::Center, Color::White); +} + +} + +ErrorOr<NonnullRefPtr<MainWidget>> MainWidget::try_create(GUI::Icon const& icon) +{ + auto main_widget = TRY(try_make_ref_counted<MainWidget>()); + TRY(main_widget->load_from_gml(gml_playground_window_gml)); + main_widget->m_icon = icon; + + main_widget->m_toolbar = main_widget->find_descendant_of_type_named<GUI::Toolbar>("toolbar"); + main_widget->m_splitter = main_widget->find_descendant_of_type_named<GUI::HorizontalSplitter>("splitter"); + main_widget->m_editor = main_widget->find_descendant_of_type_named<GUI::TextEditor>("text_editor"); + main_widget->m_preview_frame_widget = main_widget->find_descendant_of_type_named<GUI::Frame>("preview_frame"); + + main_widget->m_preview_window = TRY(GUI::Window::try_create()); + main_widget->m_preview_window->set_title("Preview - GML Playground"); + main_widget->m_preview_window->set_icon(icon.bitmap_for_size(16)); + main_widget->m_preview_window_widget = TRY(main_widget->m_preview_window->set_main_widget<GUI::Widget>()); + main_widget->m_preview_window_widget->set_fill_with_background_color(true); + + main_widget->m_preview = main_widget->m_preview_frame_widget; + + main_widget->m_editor->set_syntax_highlighter(make<GUI::GML::SyntaxHighlighter>()); + main_widget->m_editor->set_autocomplete_provider(make<GUI::GML::AutocompleteProvider>()); + main_widget->m_editor->set_should_autocomplete_automatically(true); + main_widget->m_editor->set_automatic_indentation_enabled(true); + main_widget->m_editor->set_ruler_visible(true); + + main_widget->m_editor->on_change = [main_widget = main_widget.ptr()] { + main_widget->m_preview->remove_all_children(); + // FIXME: Parsing errors happen while the user is typing. What should we do about them? + (void)main_widget->m_preview->load_from_gml(main_widget->m_editor->text(), [](DeprecatedString const& class_name) -> ErrorOr<NonnullRefPtr<Core::Object>> { + return UnregisteredWidget::try_create(class_name); + }); + }; + + main_widget->m_editor->on_modified_change = [main_widget = main_widget.ptr()](bool modified) { + main_widget->window()->set_modified(modified); + }; + + return main_widget; +} + +void MainWidget::update_title() +{ + window()->set_title(DeprecatedString::formatted("{}[*] - GML Playground", m_file_path.is_empty() ? "Untitled"sv : m_file_path.view())); +}; + +void MainWidget::load_file(FileSystemAccessClient::File file) +{ + auto buffer_or_error = file.stream().read_until_eof(); + if (buffer_or_error.is_error()) + return; + + m_editor->set_text(buffer_or_error.release_value()); + m_editor->set_focus(true); + + m_file_path = file.filename().to_deprecated_string(); + update_title(); + + GUI::Application::the()->set_most_recently_open_file(file.filename()); +}; + +ErrorOr<void> MainWidget::initialize_menubar(GUI::Window& window) +{ + auto file_menu = TRY(window.try_add_menu("&File"_short_string)); + + auto save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) { + auto response = FileSystemAccessClient::Client::the().save_file(&window, "Untitled", "gml"); + if (response.is_error()) + return; + + auto file = response.value().release_stream(); + if (auto result = m_editor->write_to_file(*file); result.is_error()) { + GUI::MessageBox::show(&window, DeprecatedString::formatted("Unable to save file: {}\n"sv, result.release_error()), "Error"sv, GUI::MessageBox::Type::Error); + return; + } + m_file_path = response.value().filename().to_deprecated_string(); + update_title(); + + GUI::Application::the()->set_most_recently_open_file(response.value().filename()); + }); + + m_save_action = GUI::CommonActions::make_save_action([&](auto&) { + if (m_file_path.is_empty()) { + save_as_action->activate(); + return; + } + auto response = FileSystemAccessClient::Client::the().request_file(&window, m_file_path, Core::File::OpenMode::Truncate | Core::File::OpenMode::Write); + if (response.is_error()) + return; + + auto file = response.value().release_stream(); + if (auto result = m_editor->write_to_file(*file); result.is_error()) { + GUI::MessageBox::show(&window, DeprecatedString::formatted("Unable to save file: {}\n"sv, result.release_error()), "Error"sv, GUI::MessageBox::Type::Error); + return; + } + update_title(); + }); + + auto open_action = GUI::CommonActions::make_open_action([&](auto&) { + if (window.is_modified()) { + auto result = GUI::MessageBox::ask_about_unsaved_changes(&window, m_file_path, m_editor->document().undo_stack().last_unmodified_timestamp()); + if (result == GUI::MessageBox::ExecResult::Yes) + m_save_action->activate(); + if (result != GUI::MessageBox::ExecResult::No && window.is_modified()) + return; + } + + auto response = FileSystemAccessClient::Client::the().open_file(&window); + if (response.is_error()) + return; + + load_file(response.release_value()); + }); + + TRY(file_menu->try_add_action(open_action)); + TRY(file_menu->try_add_action(*m_save_action)); + TRY(file_menu->try_add_action(save_as_action)); + TRY(file_menu->try_add_separator()); + + TRY(file_menu->add_recent_files_list([&](auto& action) { + if (window.is_modified()) { + auto result = GUI::MessageBox::ask_about_unsaved_changes(&window, m_file_path, m_editor->document().undo_stack().last_unmodified_timestamp()); + if (result == GUI::MessageBox::ExecResult::Yes) + m_save_action->activate(); + if (result != GUI::MessageBox::ExecResult::No && window.is_modified()) + return; + } + + auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(&window, action.text()); + if (response.is_error()) + return; + load_file(response.release_value()); + })); + + TRY(file_menu->try_add_action(GUI::CommonActions::make_quit_action([&](auto&) { + if (window.on_close_request() == GUI::Window::CloseRequestDecision::Close) + GUI::Application::the()->quit(); + }))); + + auto edit_menu = TRY(window.try_add_menu("&Edit"_short_string)); + TRY(edit_menu->try_add_action(m_editor->undo_action())); + TRY(edit_menu->try_add_action(m_editor->redo_action())); + TRY(edit_menu->try_add_separator()); + TRY(edit_menu->try_add_action(m_editor->cut_action())); + TRY(edit_menu->try_add_action(m_editor->copy_action())); + TRY(edit_menu->try_add_action(m_editor->paste_action())); + TRY(edit_menu->try_add_separator()); + TRY(edit_menu->try_add_action(m_editor->select_all_action())); + TRY(edit_menu->try_add_action(m_editor->go_to_line_action())); + TRY(edit_menu->try_add_separator()); + + auto format_gml_action = GUI::Action::create("&Format GML", { Mod_Ctrl | Mod_Shift, Key_I }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reformat.png"sv)), [&](auto&) { + auto formatted_gml_or_error = GUI::GML::format_gml(m_editor->text()); + if (!formatted_gml_or_error.is_error()) { + m_editor->replace_all_text_without_resetting_undo_stack(formatted_gml_or_error.release_value()); + } else { + GUI::MessageBox::show( + &window, + DeprecatedString::formatted("GML could not be formatted: {}", formatted_gml_or_error.error()), + "Error"sv, + GUI::MessageBox::Type::Error); + } + }); + TRY(edit_menu->try_add_action(format_gml_action)); + + auto vim_emulation_setting_action = GUI::Action::create_checkable("&Vim Emulation", { Mod_Ctrl | Mod_Shift | Mod_Alt, Key_V }, [&](auto& action) { + if (action.is_checked()) + m_editor->set_editing_engine(make<GUI::VimEditingEngine>()); + else + m_editor->set_editing_engine(make<GUI::RegularEditingEngine>()); + }); + vim_emulation_setting_action->set_checked(false); + TRY(edit_menu->try_add_action(vim_emulation_setting_action)); + + auto view_menu = TRY(window.try_add_menu("&View"_short_string)); + m_views_group.set_exclusive(true); + m_views_group.set_unchecking_allowed(false); + + auto view_frame_action = GUI::Action::create_checkable("&Frame", [&](auto&) { + dbgln("View switched to frame"); + m_preview = m_preview_frame_widget; + m_editor->on_change(); + m_preview_window->hide(); + m_preview_frame_widget->set_preferred_width(m_splitter->width() / 2); + m_preview_frame_widget->set_visible(true); + }); + view_menu->add_action(view_frame_action); + m_views_group.add_action(view_frame_action); + view_frame_action->set_checked(true); + + auto view_window_action = GUI::Action::create_checkable("&Window", [&](auto&) { + dbgln("View switched to window"); + m_preview = m_preview_window_widget; + m_editor->on_change(); + m_preview_window->resize(400, 300); + m_preview_window->show(); + m_preview_frame_widget->set_visible(false); + }); + view_menu->add_action(view_window_action); + m_views_group.add_action(view_window_action); + + m_preview_window->on_close = [&] { + view_frame_action->activate(); + }; + + auto help_menu = TRY(window.try_add_menu("&Help"_short_string)); + TRY(help_menu->try_add_action(GUI::CommonActions::make_command_palette_action(&window))); + TRY(help_menu->try_add_action(GUI::CommonActions::make_help_action([](auto&) { + Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man1/Applications/GMLPlayground.md"), "/bin/Help"); + }))); + TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("GML Playground", m_icon, &window))); + + (void)TRY(m_toolbar->try_add_action(open_action)); + (void)TRY(m_toolbar->try_add_action(*m_save_action)); + (void)TRY(m_toolbar->try_add_action(save_as_action)); + TRY(m_toolbar->try_add_separator()); + (void)TRY(m_toolbar->try_add_action(m_editor->cut_action())); + (void)TRY(m_toolbar->try_add_action(m_editor->copy_action())); + (void)TRY(m_toolbar->try_add_action(m_editor->paste_action())); + TRY(m_toolbar->try_add_separator()); + (void)TRY(m_toolbar->try_add_action(m_editor->undo_action())); + (void)TRY(m_toolbar->try_add_action(m_editor->redo_action())); + TRY(m_toolbar->try_add_separator()); + (void)TRY(m_toolbar->try_add_action(format_gml_action)); + + return {}; +} + +GUI::Window::CloseRequestDecision MainWidget::request_close() +{ + if (!window()->is_modified()) + return GUI::Window::CloseRequestDecision::Close; + + auto result = GUI::MessageBox::ask_about_unsaved_changes(window(), m_file_path, m_editor->document().undo_stack().last_unmodified_timestamp()); + if (result == GUI::MessageBox::ExecResult::Yes) { + m_save_action->activate(); + if (window()->is_modified()) + return GUI::Window::CloseRequestDecision::StayOpen; + return GUI::Window::CloseRequestDecision::Close; + } + + if (result == GUI::MessageBox::ExecResult::No) + return GUI::Window::CloseRequestDecision::Close; + + return GUI::Window::CloseRequestDecision::StayOpen; +} diff --git a/Userland/DevTools/GMLPlayground/MainWidget.h b/Userland/DevTools/GMLPlayground/MainWidget.h new file mode 100644 index 0000000000..8c3d687a63 --- /dev/null +++ b/Userland/DevTools/GMLPlayground/MainWidget.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2021, Julius Heijmen <julius.heijmen@gmail.com> + * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org> + * Copyright (c) 2022-2023, Sam Atkins <atkinssj@serenityos.org> + * Copyright (c) 2023, Karol Kosek <krkk@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibFileSystemAccessClient/Client.h> +#include <LibGUI/ActionGroup.h> +#include <LibGUI/Icon.h> +#include <LibGUI/Widget.h> +#include <LibGUI/Window.h> + +class MainWidget final : public GUI::Widget { + C_OBJECT_ABSTRACT(MainWidget) + +public: + static ErrorOr<NonnullRefPtr<MainWidget>> try_create(GUI::Icon const&); + ErrorOr<void> initialize_menubar(GUI::Window&); + GUI::Window::CloseRequestDecision request_close(); + + void load_file(FileSystemAccessClient::File); + void update_title(); + + GUI::TextEditor& editor() const { return *m_editor; } + +private: + RefPtr<GUI::Action> m_save_action; + RefPtr<GUI::TextEditor> m_editor; + RefPtr<GUI::Toolbar> m_toolbar; + RefPtr<GUI::Splitter> m_splitter; + + RefPtr<GUI::Frame> m_preview_frame_widget; + RefPtr<GUI::Window> m_preview_window; + RefPtr<GUI::Widget> m_preview_window_widget; + GUI::Widget* m_preview; + GUI::ActionGroup m_views_group; + + GUI::Icon m_icon; + DeprecatedString m_file_path; +}; diff --git a/Userland/DevTools/GMLPlayground/main.cpp b/Userland/DevTools/GMLPlayground/main.cpp index 93a71d9f0e..31285ad304 100644 --- a/Userland/DevTools/GMLPlayground/main.cpp +++ b/Userland/DevTools/GMLPlayground/main.cpp @@ -7,61 +7,16 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include "MainWidget.h" #include <AK/URL.h> #include <LibConfig/Client.h> #include <LibCore/ArgsParser.h> -#include <LibCore/System.h> #include <LibDesktop/Launcher.h> #include <LibFileSystemAccessClient/Client.h> -#include <LibGUI/ActionGroup.h> #include <LibGUI/Application.h> -#include <LibGUI/GML/AutocompleteProvider.h> -#include <LibGUI/GML/Formatter.h> -#include <LibGUI/GML/SyntaxHighlighter.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Menu.h> -#include <LibGUI/Menubar.h> -#include <LibGUI/MessageBox.h> -#include <LibGUI/Painter.h> -#include <LibGUI/RegularEditingEngine.h> -#include <LibGUI/Splitter.h> #include <LibGUI/TextEditor.h> -#include <LibGUI/Toolbar.h> -#include <LibGUI/VimEditingEngine.h> #include <LibGUI/Window.h> #include <LibMain/Main.h> -#include <Userland/DevTools/GMLPlayground/GMLPlaygroundWindowGML.h> - -namespace { - -class UnregisteredWidget final : public GUI::Widget { - C_OBJECT(UnregisteredWidget); - -private: - UnregisteredWidget(DeprecatedString const& class_name); - - virtual void paint_event(GUI::PaintEvent& event) override; - - DeprecatedString m_text; -}; - -UnregisteredWidget::UnregisteredWidget(DeprecatedString const& class_name) -{ - StringBuilder builder; - builder.append(class_name); - builder.append("\nnot registered"sv); - m_text = builder.to_deprecated_string(); -} - -void UnregisteredWidget::paint_event(GUI::PaintEvent& event) -{ - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.fill_rect(event.rect(), Gfx::Color::DarkRed); - painter.draw_text(rect(), m_text, Gfx::TextAlignment::Center, Color::White); -} - -} ErrorOr<int> serenity_main(Main::Arguments arguments) { @@ -90,254 +45,29 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) window->set_icon(app_icon.bitmap_for_size(16)); window->resize(800, 600); - auto main_widget = TRY(window->set_main_widget<GUI::Widget>()); - TRY(main_widget->load_from_gml(gml_playground_window_gml)); - - auto toolbar = main_widget->find_descendant_of_type_named<GUI::Toolbar>("toolbar"); - auto splitter = main_widget->find_descendant_of_type_named<GUI::HorizontalSplitter>("splitter"); - auto editor = main_widget->find_descendant_of_type_named<GUI::TextEditor>("text_editor"); - auto preview_frame_widget = main_widget->find_descendant_of_type_named<GUI::Frame>("preview_frame"); - - auto preview_window = TRY(GUI::Window::try_create()); - preview_window->set_title("Preview - GML Playground"); - preview_window->set_icon(app_icon.bitmap_for_size(16)); - auto preview_window_widget = TRY(preview_window->set_main_widget<GUI::Widget>()); - preview_window_widget->set_fill_with_background_color(true); - - GUI::Widget* preview = preview_frame_widget; - - editor->set_syntax_highlighter(make<GUI::GML::SyntaxHighlighter>()); - editor->set_autocomplete_provider(make<GUI::GML::AutocompleteProvider>()); - editor->set_should_autocomplete_automatically(true); - editor->set_automatic_indentation_enabled(true); - editor->set_ruler_visible(true); - - DeprecatedString file_path; - auto update_title = [&] { - window->set_title(DeprecatedString::formatted("{}[*] - GML Playground", file_path.is_empty() ? "Untitled"sv : file_path.view())); - }; - - editor->on_change = [&] { - preview->remove_all_children(); - // FIXME: Parsing errors happen while the user is typing. What should we do about them? - (void)preview->load_from_gml(editor->text(), [](DeprecatedString const& class_name) -> ErrorOr<NonnullRefPtr<Core::Object>> { - return UnregisteredWidget::try_create(class_name); - }); - }; - - editor->on_modified_change = [&](bool modified) { - window->set_modified(modified); - }; - - auto load_file = [&](auto file) { - auto buffer_or_error = file.stream().read_until_eof(); - if (buffer_or_error.is_error()) - return; - - editor->set_text(buffer_or_error.release_value()); - editor->set_focus(true); - file_path = file.filename().to_deprecated_string(); - update_title(); - - GUI::Application::the()->set_most_recently_open_file(file.filename()); - }; - - auto file_menu = TRY(window->try_add_menu("&File"_short_string)); - - auto save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) { - auto response = FileSystemAccessClient::Client::the().save_file(window, "Untitled", "gml"); - if (response.is_error()) - return; - - auto file = response.value().release_stream(); - if (auto result = editor->write_to_file(*file); result.is_error()) { - GUI::MessageBox::show(window, DeprecatedString::formatted("Unable to save file: {}\n"sv, result.release_error()), "Error"sv, GUI::MessageBox::Type::Error); - return; - } - file_path = response.value().filename().to_deprecated_string(); - update_title(); - - GUI::Application::the()->set_most_recently_open_file(response.value().filename()); - }); - - auto save_action = GUI::CommonActions::make_save_action([&](auto&) { - if (file_path.is_empty()) { - save_as_action->activate(); - return; - } - auto response = FileSystemAccessClient::Client::the().request_file(window, file_path, Core::File::OpenMode::Truncate | Core::File::OpenMode::Write); - if (response.is_error()) - return; - - auto file = response.value().release_stream(); - if (auto result = editor->write_to_file(*file); result.is_error()) { - GUI::MessageBox::show(window, DeprecatedString::formatted("Unable to save file: {}\n"sv, result.release_error()), "Error"sv, GUI::MessageBox::Type::Error); - return; - } - update_title(); - }); - - auto open_action = GUI::CommonActions::make_open_action([&](auto&) { - if (window->is_modified()) { - auto result = GUI::MessageBox::ask_about_unsaved_changes(window, file_path, editor->document().undo_stack().last_unmodified_timestamp()); - if (result == GUI::MessageBox::ExecResult::Yes) - save_action->activate(); - if (result != GUI::MessageBox::ExecResult::No && window->is_modified()) - return; - } - - auto response = FileSystemAccessClient::Client::the().open_file(window); - if (response.is_error()) - return; - - load_file(response.release_value()); - }); - - TRY(file_menu->try_add_action(open_action)); - TRY(file_menu->try_add_action(save_action)); - TRY(file_menu->try_add_action(save_as_action)); - TRY(file_menu->try_add_separator()); - - TRY(file_menu->add_recent_files_list([&](auto& action) { - if (window->is_modified()) { - auto result = GUI::MessageBox::ask_about_unsaved_changes(window, file_path, editor->document().undo_stack().last_unmodified_timestamp()); - if (result == GUI::MessageBox::ExecResult::Yes) - save_action->activate(); - if (result != GUI::MessageBox::ExecResult::No && window->is_modified()) - return; - } - - auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window, action.text()); - if (response.is_error()) - return; - load_file(response.release_value()); - })); - - TRY(file_menu->try_add_action(GUI::CommonActions::make_quit_action([&](auto&) { - if (window->on_close_request() == GUI::Window::CloseRequestDecision::Close) - app->quit(); - }))); - - auto edit_menu = TRY(window->try_add_menu("&Edit"_short_string)); - TRY(edit_menu->try_add_action(editor->undo_action())); - TRY(edit_menu->try_add_action(editor->redo_action())); - TRY(edit_menu->try_add_separator()); - TRY(edit_menu->try_add_action(editor->cut_action())); - TRY(edit_menu->try_add_action(editor->copy_action())); - TRY(edit_menu->try_add_action(editor->paste_action())); - TRY(edit_menu->try_add_separator()); - TRY(edit_menu->try_add_action(editor->select_all_action())); - TRY(edit_menu->try_add_action(editor->go_to_line_action())); - TRY(edit_menu->try_add_separator()); - - auto format_gml_action = GUI::Action::create("&Format GML", { Mod_Ctrl | Mod_Shift, Key_I }, TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/reformat.png"sv)), [&](auto&) { - auto formatted_gml_or_error = GUI::GML::format_gml(editor->text()); - if (!formatted_gml_or_error.is_error()) { - editor->replace_all_text_without_resetting_undo_stack(formatted_gml_or_error.release_value()); - } else { - GUI::MessageBox::show( - window, - DeprecatedString::formatted("GML could not be formatted: {}", formatted_gml_or_error.error()), - "Error"sv, - GUI::MessageBox::Type::Error); - } - }); - TRY(edit_menu->try_add_action(format_gml_action)); - - auto vim_emulation_setting_action = GUI::Action::create_checkable("&Vim Emulation", { Mod_Ctrl | Mod_Shift | Mod_Alt, Key_V }, [&](auto& action) { - if (action.is_checked()) - editor->set_editing_engine(make<GUI::VimEditingEngine>()); - else - editor->set_editing_engine(make<GUI::RegularEditingEngine>()); - }); - vim_emulation_setting_action->set_checked(false); - TRY(edit_menu->try_add_action(vim_emulation_setting_action)); - - auto view_menu = TRY(window->try_add_menu("&View"_short_string)); - GUI::ActionGroup views_group; - views_group.set_exclusive(true); - views_group.set_unchecking_allowed(false); - - auto view_frame_action = GUI::Action::create_checkable("&Frame", [&](auto&) { - dbgln("View switched to frame"); - preview = preview_frame_widget; - editor->on_change(); - preview_window->hide(); - preview_frame_widget->set_preferred_width(splitter->width() / 2); - preview_frame_widget->set_visible(true); - }); - view_menu->add_action(view_frame_action); - views_group.add_action(view_frame_action); - view_frame_action->set_checked(true); - - auto view_window_action = GUI::Action::create_checkable("&Window", [&](auto&) { - dbgln("View switched to window"); - preview = preview_window_widget; - editor->on_change(); - preview_window->resize(400, 300); - preview_window->show(); - preview_frame_widget->set_visible(false); - }); - view_menu->add_action(view_window_action); - views_group.add_action(view_window_action); - - preview_window->on_close = [&] { - view_frame_action->activate(); - }; - - auto help_menu = TRY(window->try_add_menu("&Help"_short_string)); - TRY(help_menu->try_add_action(GUI::CommonActions::make_command_palette_action(window))); - TRY(help_menu->try_add_action(GUI::CommonActions::make_help_action([](auto&) { - Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man1/Applications/GMLPlayground.md"), "/bin/Help"); - }))); - TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("GML Playground", app_icon, window))); - - (void)TRY(toolbar->try_add_action(open_action)); - (void)TRY(toolbar->try_add_action(save_action)); - (void)TRY(toolbar->try_add_action(save_as_action)); - TRY(toolbar->try_add_separator()); - (void)TRY(toolbar->try_add_action(editor->cut_action())); - (void)TRY(toolbar->try_add_action(editor->copy_action())); - (void)TRY(toolbar->try_add_action(editor->paste_action())); - TRY(toolbar->try_add_separator()); - (void)TRY(toolbar->try_add_action(editor->undo_action())); - (void)TRY(toolbar->try_add_action(editor->redo_action())); - TRY(toolbar->try_add_separator()); - (void)TRY(toolbar->try_add_action(format_gml_action)); - - window->on_close_request = [&] { - if (!window->is_modified()) - return GUI::Window::CloseRequestDecision::Close; - - auto result = GUI::MessageBox::ask_about_unsaved_changes(window, file_path, editor->document().undo_stack().last_unmodified_timestamp()); - if (result == GUI::MessageBox::ExecResult::Yes) { - save_action->activate(); - if (window->is_modified()) - return GUI::Window::CloseRequestDecision::StayOpen; - return GUI::Window::CloseRequestDecision::Close; - } + auto main_widget = TRY(window->set_main_widget<MainWidget>(app_icon)); - if (result == GUI::MessageBox::ExecResult::No) - return GUI::Window::CloseRequestDecision::Close; + TRY(main_widget->initialize_menubar(window)); - return GUI::Window::CloseRequestDecision::StayOpen; + window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision { + return main_widget->request_close(); }; window->show(); if (DeprecatedString(path).is_empty()) { - editor->set_text(R"~~~(@GUI::Frame { + main_widget->editor().set_text(R"~~~(@GUI::Frame { layout: @GUI::VerticalBoxLayout { } // Now add some widgets! } )~~~"sv); - editor->set_cursor(4, 28); // after "...widgets!" - update_title(); + main_widget->editor().set_cursor(4, 28); // after "...widgets!" + main_widget->update_title(); } else { auto file = TRY(FileSystemAccessClient::Client::the().request_file_read_only_approved(window, path)); - load_file(move(file)); + main_widget->load_file(move(file)); } return app->exec(); |