diff --git a/Applications/Browser/BrowserWindow.gml b/Applications/Browser/BrowserWindow.gml new file mode 100644 index 00000000000..f6ec7ef1afe --- /dev/null +++ b/Applications/Browser/BrowserWindow.gml @@ -0,0 +1,15 @@ +@GUI::Widget { + name: "browser" + fill_with_background_color: true + + layout: @GUI::VerticalBoxLayout { + spacing: 2 + } + + @GUI::TabWidget { + name: "tab_widget" + container_padding: 0 + uniform_tabs: true + text_alignment: "CenterLeft" + } +} diff --git a/Applications/Browser/BrowserWindow.json b/Applications/Browser/BrowserWindow.json deleted file mode 100644 index ea3d6c16758..00000000000 --- a/Applications/Browser/BrowserWindow.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "browser", - "fill_with_background_color": true, - - "layout": { - "class": "GUI::VerticalBoxLayout", - "spacing": 2 - }, - - "children": [ - { - "class": "GUI::TabWidget", - "name": "tab_widget", - "container_padding": 0, - "uniform_tabs": true, - "text_alignment": "CenterLeft" - } - ] -} diff --git a/Applications/Browser/CMakeLists.txt b/Applications/Browser/CMakeLists.txt index 048ff35bac2..8594c00d614 100644 --- a/Applications/Browser/CMakeLists.txt +++ b/Applications/Browser/CMakeLists.txt @@ -1,5 +1,5 @@ -compile_json_gui(BrowserWindow.json BrowserWindowUI.h browser_window_ui_json) -compile_json_gui(Tab.json TabUI.h tab_ui_json) +compile_gml(BrowserWindow.gml BrowserWindowGML.h browser_window_gml) +compile_gml(Tab.gml TabGML.h tab_gml) set(SOURCES BookmarksBarWidget.cpp @@ -11,8 +11,8 @@ set(SOURCES main.cpp Tab.cpp WindowActions.cpp - BrowserWindowUI.h - TabUI.h + BrowserWindowGML.h + TabGML.h ) serenity_bin(Browser) diff --git a/Applications/Browser/Tab.cpp b/Applications/Browser/Tab.cpp index abd5f1c1d97..73e88b761a3 100644 --- a/Applications/Browser/Tab.cpp +++ b/Applications/Browser/Tab.cpp @@ -32,7 +32,7 @@ #include "InspectorWidget.h" #include "WindowActions.h" #include -#include +#include #include #include #include @@ -88,7 +88,7 @@ static void start_download(const URL& url) Tab::Tab(Type type) : m_type(type) { - load_from_json(tab_ui_json); + load_from_gml(tab_gml); m_toolbar_container = static_cast(*find_descendant_by_name("toolbar_container")); auto& toolbar = static_cast(*find_descendant_by_name("toolbar")); diff --git a/Applications/Browser/Tab.gml b/Applications/Browser/Tab.gml new file mode 100644 index 00000000000..24c7456d776 --- /dev/null +++ b/Applications/Browser/Tab.gml @@ -0,0 +1,22 @@ +@GUI::Widget { + layout: @GUI::VerticalBoxLayout { + } + + @GUI::ToolBarContainer { + name: "toolbar_container" + + @GUI::ToolBar { + name: "toolbar" + } + } + + @GUI::Widget { + name: "webview_container" + layout: @GUI::VerticalBoxLayout { + } + } + + @GUI::StatusBar { + name: "statusbar" + } +} diff --git a/Applications/Browser/Tab.json b/Applications/Browser/Tab.json deleted file mode 100644 index 2f1ef0a0eae..00000000000 --- a/Applications/Browser/Tab.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "layout": { - "class": "GUI::VerticalBoxLayout" - }, - - "children": [ - { - "class": "GUI::ToolBarContainer", - "name": "toolbar_container", - "children": [ - { - "class": "GUI::ToolBar", - "name": "toolbar" - } - ] - }, - { - "class": "GUI::Widget", - "name": "webview_container", - "layout": { - "class": "GUI::VerticalBoxLayout" - } - }, - { - "class": "GUI::StatusBar", - "name": "statusbar" - } - ] -} diff --git a/Applications/Browser/main.cpp b/Applications/Browser/main.cpp index 7fe4e5f94dc..3624ca60767 100644 --- a/Applications/Browser/main.cpp +++ b/Applications/Browser/main.cpp @@ -30,7 +30,7 @@ #include "Tab.h" #include "WindowActions.h" #include -#include +#include #include #include #include @@ -138,7 +138,7 @@ int main(int argc, char** argv) window->set_title("Browser"); auto& widget = window->set_main_widget(); - widget.load_from_json(browser_window_ui_json); + widget.load_from_gml(browser_window_gml); auto& tab_widget = static_cast(*widget.find_descendant_by_name("tab_widget")); diff --git a/Applications/Spreadsheet/CMakeLists.txt b/Applications/Spreadsheet/CMakeLists.txt index 9ba555450d6..ea0054dc7b1 100644 --- a/Applications/Spreadsheet/CMakeLists.txt +++ b/Applications/Spreadsheet/CMakeLists.txt @@ -1,5 +1,5 @@ -compile_json_gui(CondFormatting.json CondFormattingUI.h cond_fmt_ui_json) -compile_json_gui(CondView.json CondFormattingViewUI.h cond_fmt_view_ui_json) +compile_gml(CondFormatting.gml CondFormattingGML.h cond_fmt_gml) +compile_gml(CondView.gml CondFormattingViewGML.h cond_fmt_view_gml) set(SOURCES Cell.cpp @@ -11,8 +11,8 @@ set(SOURCES CellType/String.cpp CellType/Type.cpp CellTypeDialog.cpp - CondFormattingUI.h - CondFormattingViewUI.h + CondFormattingGML.h + CondFormattingViewGML.h HelpWindow.cpp JSIntegration.cpp Readers/XSV.cpp diff --git a/Applications/Spreadsheet/CellTypeDialog.cpp b/Applications/Spreadsheet/CellTypeDialog.cpp index bd264a34d65..38d183faa7f 100644 --- a/Applications/Spreadsheet/CellTypeDialog.cpp +++ b/Applications/Spreadsheet/CellTypeDialog.cpp @@ -28,8 +28,8 @@ #include "Cell.h" #include "Spreadsheet.h" #include -#include -#include +#include +#include #include #include #include @@ -356,7 +356,7 @@ void CellTypeDialog::setup_tabs(GUI::TabWidget& tabs, const Vector& po } auto& conditional_fmt_tab = tabs.add_tab("Conditional Format"); - conditional_fmt_tab.load_from_json(cond_fmt_ui_json); + conditional_fmt_tab.load_from_gml(cond_fmt_gml); { auto& view = static_cast(*conditional_fmt_tab.find_descendant_by_name("conditions_view")); view.set_formats(&m_conditional_formats); @@ -429,7 +429,7 @@ CellTypeMetadata CellTypeDialog::metadata() const ConditionView::ConditionView(ConditionalFormat& fmt) : m_format(fmt) { - load_from_json(cond_fmt_view_ui_json); + load_from_gml(cond_fmt_view_gml); auto& fg_input = *static_cast(find_descendant_by_name("foreground_input")); auto& bg_input = *static_cast(find_descendant_by_name("background_input")); diff --git a/Applications/Spreadsheet/CondFormatting.gml b/Applications/Spreadsheet/CondFormatting.gml new file mode 100644 index 00000000000..6ef95e5cdbc --- /dev/null +++ b/Applications/Spreadsheet/CondFormatting.gml @@ -0,0 +1,41 @@ +@GUI::Widget { + name: "main" + fill_with_background_color: true + + layout: @GUI::VerticalBoxLayout { + spacing: 4 + } + + @Spreadsheet::ConditionsView { + name: "conditions_view" + } + + @GUI::Widget { + vertical_size_policy: "Fixed" + horizontal_size_policy: "Fill" + preferred_width: 0 + preferred_height: 20 + + layout: @GUI::HorizontalBoxLayout { + spacing: 10 + } + + @GUI::Button { + name: "add_button" + text: "Add" + horizontal_size_policy: "Fixed" + vertical_size_policy: "Fixed" + preferred_width: 100 + preferred_height: 20 + } + + @GUI::Button { + name: "remove_button" + text: "Remove" + horizontal_size_policy: "Fixed" + vertical_size_policy: "Fixed" + preferred_width: 100 + preferred_height: 20 + } + } +} diff --git a/Applications/Spreadsheet/CondFormatting.json b/Applications/Spreadsheet/CondFormatting.json deleted file mode 100644 index c5b10d6b0ff..00000000000 --- a/Applications/Spreadsheet/CondFormatting.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "main", - "fill_with_background_color": true, - - "layout": { - "class": "GUI::VerticalBoxLayout", - "spacing": 4 - }, - - "children": [ - { - "class": "Spreadsheet::ConditionsView", - "name": "conditions_view" - }, - { - "class": "GUI::Widget", - "vertical_size_policy": "Fixed", - "horizontal_size_policy": "Fill", - "preferred_width": 0, - "preferred_height": 20, - "layout": { - "class": "GUI::HorizontalBoxLayout", - "spacing": 10 - }, - "children": [ - { - "class": "GUI::Button", - "name": "add_button", - "text": "Add", - "horizontal_size_policy": "Fixed", - "vertical_size_policy": "Fixed", - "preferred_width": 100, - "preferred_height": 20 - }, - { - "class": "GUI::Button", - "name": "remove_button", - "text": "Remove", - "horizontal_size_policy": "Fixed", - "vertical_size_policy": "Fixed", - "preferred_width": 100, - "preferred_height": 20 - } - ] - } - ] -} diff --git a/Applications/Spreadsheet/CondView.gml b/Applications/Spreadsheet/CondView.gml new file mode 100644 index 00000000000..d76bd8c734f --- /dev/null +++ b/Applications/Spreadsheet/CondView.gml @@ -0,0 +1,76 @@ +@GUI::Widget { + layout: @GUI::VerticalBoxLayout { + spacing: 2 + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 10 + } + + vertical_size_policy: "Fixed" + preferred_height: 25 + + @GUI::Label { + text: "if..." + horizontal_size_policy: "Fixed" + vertical_size_policy: "Fixed" + preferred_width: 40 + preferred_height: 25 + } + + @GUI::TextEditor { + name: "formula_editor" + horizontal_size_policy: "Fill" + vertical_size_policy: "Fixed" + preferred_height: 25 + tooltip: "Use 'value' to refer to the current cell's value" + } + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 10 + } + + vertical_size_policy: "Fixed" + preferred_height: 25 + + @GUI::Label { + text: "Foreground..." + horizontal_size_policy: "Fixed" + vertical_size_policy: "Fixed" + preferred_width: 150 + preferred_height: 25 + } + + @GUI::ColorInput { + name: "foreground_input" + vertical_size_policy: "Fixed" + preferred_height: 25 + } + } + + @GUI::Widget { + layout: @GUI::HorizontalBoxLayout { + spacing: 10 + } + + vertical_size_policy: "Fixed" + preferred_height: 25 + + @GUI::Label { + text: "Background..." + horizontal_size_policy: "Fixed" + vertical_size_policy: "Fixed" + preferred_width: 150 + preferred_height: 25 + } + + @GUI::ColorInput { + name: "background_input" + vertical_size_policy: "Fixed" + preferred_height: 25 + } + } +} diff --git a/Applications/Spreadsheet/CondView.json b/Applications/Spreadsheet/CondView.json deleted file mode 100644 index f4cdac362ba..00000000000 --- a/Applications/Spreadsheet/CondView.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "class": "GUI::Widget", - "layout": { - "class": "GUI::VerticalBoxLayout", - "spacing": 2 - }, - "children": [ - { - "class": "GUI::Widget", - "layout": { - "class": "GUI::HorizontalBoxLayout", - "spacing": 10 - }, - "vertical_size_policy": "Fixed", - "preferred_height": 25, - "children": [ - { - "class": "GUI::Label", - "name": "if_label", - "horizontal_size_policy": "Fixed", - "vertical_size_policy": "Fixed", - "text": "if...", - "preferred_width": 40, - "preferred_height": 25 - }, - { - "class": "GUI::TextEditor", - "name": "formula_editor", - "horizontal_size_policy": "Fill", - "vertical_size_policy": "Fixed", - "tooltip": "Use 'value' to refer to the current cell's value", - "preferred_height": 25 - } - ] - }, - { - "class": "GUI::Widget", - "layout": { - "class": "GUI::HorizontalBoxLayout", - "spacing": 10 - }, - "vertical_size_policy": "Fixed", - "preferred_height": 25, - "children": [ - { - "class": "GUI::Label", - "name": "fg_color_label", - "horizontal_size_policy": "Fixed", - "vertical_size_policy": "Fixed", - "text": "Foreground...", - "preferred_width": 150, - "preferred_height": 25 - }, - { - "class": "GUI::ColorInput", - "name": "foreground_input", - "horizontal_size_policy": "Fill", - "vertical_size_policy": "Fixed", - "preferred_height": 25, - "preferred_width": 25 - } - ] - }, - { - "class": "GUI::Widget", - "layout": { - "class": "GUI::HorizontalBoxLayout", - "spacing": 10 - }, - "vertical_size_policy": "Fixed", - "preferred_height": 25, - "children": [ - { - "class": "GUI::Label", - "name": "bg_color_label", - "horizontal_size_policy": "Fixed", - "vertical_size_policy": "Fixed", - "text": "Background...", - "preferred_width": 150, - "preferred_height": 25 - }, - { - "class": "GUI::ColorInput", - "name": "background_input", - "horizontal_size_policy": "Fill", - "vertical_size_policy": "Fixed", - "preferred_height": 25, - "preferred_width": 25 - } - ] - } - ] -} diff --git a/Applications/TextEditor/CMakeLists.txt b/Applications/TextEditor/CMakeLists.txt index eadf6decfbc..897a77f287e 100644 --- a/Applications/TextEditor/CMakeLists.txt +++ b/Applications/TextEditor/CMakeLists.txt @@ -1,9 +1,9 @@ -compile_json_gui(MainWindow.json MainWindowUI.h main_window_ui_json) +compile_gml(MainWindow.gml MainWindowGML.h main_window_gml) set(SOURCES main.cpp TextEditorWidget.cpp - MainWindowUI.h + MainWindowGML.h ) serenity_bin(TextEditor) diff --git a/Applications/TextEditor/MainWindow.gml b/Applications/TextEditor/MainWindow.gml new file mode 100644 index 00000000000..d766a57e0a6 --- /dev/null +++ b/Applications/TextEditor/MainWindow.gml @@ -0,0 +1,105 @@ +@GUI::Widget { + name: "main" + fill_with_background_color: true + + layout: @GUI::VerticalBoxLayout { + spacing: 2 + } + + @GUI::ToolBarContainer { + @GUI::ToolBar { + name: "toolbar" + } + } + + @GUI::HorizontalSplitter { + @GUI::TextEditor { + name: "editor" + } + + @Web::OutOfProcessWebView { + name: "webview" + visible: false + } + } + + @GUI::Widget { + name: "find_replace_widget" + visible: false + fill_with_background_color: true + horizontal_size_policy: "Fill" + vertical_size_policy: "Fixed" + preferred_height: 48 + + layout: @GUI::VerticalBoxLayout { + margins: [2, 2, 2, 4] + } + + @GUI::Widget { + name: "find_widget" + fill_with_background_color: true + horizontal_size_policy: "Fill" + vertical_size_policy: "Fixed" + preferred_height: 22 + + layout: @GUI::HorizontalBoxLayout { + } + + @GUI::Button { + name: "find_previous_button" + text: "Find previous" + horizontal_size_policy: "Fixed" + vertical_size_policy: "Fill" + preferred_width: 150 + } + + @GUI::Button { + name: "find_next_button" + text: "Find next" + horizontal_size_policy: "Fixed" + vertical_size_policy: "Fill" + preferred_width: 150 + } + } + + @GUI::Widget { + name: "replace_widget" + fill_with_background_color: true + horizontal_size_policy: "Fill" + vertical_size_policy: "Fixed" + preferred_height: 22 + + layout: @GUI::HorizontalBoxLayout { + } + + @GUI::Button { + name: "replace_previous_button" + text: "Replace previous" + horizontal_size_policy: "Fixed" + vertical_size_policy: "Fill" + preferred_width: 100 + } + + @GUI::Button { + name: "replace_next_button" + text: "Replace next" + horizontal_size_policy: "Fixed" + vertical_size_policy: "Fill" + preferred_width: 100 + } + + @GUI::Button { + name: "replace_all_button" + text: "Replace all" + horizontal_size_policy: "Fixed" + vertical_size_policy: "Fill" + preferred_width: 100 + } + } + } + + @GUI::StatusBar { + name: "statusbar" + } +} + diff --git a/Applications/TextEditor/MainWindow.json b/Applications/TextEditor/MainWindow.json deleted file mode 100644 index 899dc1befe8..00000000000 --- a/Applications/TextEditor/MainWindow.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "name": "main", - "fill_with_background_color": true, - - "layout": { - "class": "GUI::VerticalBoxLayout", - "spacing": 2 - }, - - "children": [ - { - "class": "GUI::ToolBarContainer", - "children": [ - { - "class": "GUI::ToolBar", - "name": "toolbar" - } - ] - }, - { - "class": "GUI::HorizontalSplitter", - "children": [ - { - "class": "GUI::TextEditor", - "name": "editor" - }, - { - "class": "Web::OutOfProcessWebView", - "name": "webview", - "visible": false - } - ] - }, - { - "class": "GUI::Widget", - "name": "find_replace_widget", - "visible": false, - "fill_with_background_color": true, - "horizontal_size_policy": "Fill", - "vertical_size_policy": "Fixed", - "preferred_height": 48, - "layout": { - "class": "GUI::VerticalBoxLayout", - "margins": [ 2, 2, 2, 4 ] - }, - "children": [ - { - "class": "GUI::Widget", - "name": "find_widget", - "fill_with_background_color": true, - "horizontal_size_policy": "Fill", - "vertical_size_policy": "Fixed", - "preferred_height": 22, - "layout": { - "class": "GUI::HorizontalBoxLayout" - }, - "children": [ - { - "class": "GUI::Button", - "name": "find_previous_button", - "text": "Find previous", - "horizontal_size_policy": "Fixed", - "vertical_size_policy": "Fill", - "preferred_width": 150 - }, - { - "class": "GUI::Button", - "name": "find_next_button", - "text": "Find next", - "horizontal_size_policy": "Fixed", - "vertical_size_policy": "Fill", - "preferred_width": 150 - } - ] - }, - { - "class": "GUI::Widget", - "name": "replace_widget", - "fill_with_background_color": true, - "horizontal_size_policy": "Fill", - "vertical_size_policy": "Fixed", - "preferred_height": 22, - "layout": { - "class": "GUI::HorizontalBoxLayout" - }, - "children": [ - { - "class": "GUI::Button", - "name": "replace_previous_button", - "text": "Replace previous", - "horizontal_size_policy": "Fixed", - "vertical_size_policy": "Fill", - "preferred_width": 100 - }, - { - "class": "GUI::Button", - "name": "replace_next_button", - "text": "Replace next", - "horizontal_size_policy": "Fixed", - "vertical_size_policy": "Fill", - "preferred_width": 100 - }, - { - "class": "GUI::Button", - "name": "replace_all_button", - "text": "Replace all", - "horizontal_size_policy": "Fixed", - "vertical_size_policy": "Fill", - "preferred_width": 100 - } - ] - } - ] - }, - { - "class": "GUI::StatusBar", - "name": "statusbar" - } - ] -} diff --git a/Applications/TextEditor/TextEditorWidget.cpp b/Applications/TextEditor/TextEditorWidget.cpp index 9d8e6e9e4b9..6325a1a0a0a 100644 --- a/Applications/TextEditor/TextEditorWidget.cpp +++ b/Applications/TextEditor/TextEditorWidget.cpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -61,7 +61,7 @@ TextEditorWidget::TextEditorWidget() { - load_from_json(main_window_ui_json); + load_from_gml(main_window_gml); auto& toolbar = static_cast(*find_descendant_by_name("toolbar")); diff --git a/CMakeLists.txt b/CMakeLists.txt index 4babb9f3f8c..67bf67fc910 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,7 +164,7 @@ function(serenity_bin target_name) serenity_generated_sources(${target_name}) endfunction() -function(compile_json_gui source output string_name) +function(compile_gml source output string_name) set(source ${CMAKE_CURRENT_SOURCE_DIR}/${source}) add_custom_command( OUTPUT ${output} @@ -177,6 +177,7 @@ function(compile_json_gui source output string_name) add_custom_target(generate_${output_name} DEPENDS ${output}) endfunction() + function(compile_ipc source output) set(source ${CMAKE_CURRENT_SOURCE_DIR}/${source}) add_custom_command( diff --git a/Libraries/LibGUI/CMakeLists.txt b/Libraries/LibGUI/CMakeLists.txt index cc5e199f366..ce7dcc7ce3d 100644 --- a/Libraries/LibGUI/CMakeLists.txt +++ b/Libraries/LibGUI/CMakeLists.txt @@ -30,6 +30,7 @@ set(SOURCES FileSystemModel.cpp FilteringProxyModel.cpp Frame.cpp + GMLParser.cpp GroupBox.cpp HeaderView.cpp INILexer.cpp diff --git a/Libraries/LibGUI/GMLParser.cpp b/Libraries/LibGUI/GMLParser.cpp new file mode 100644 index 00000000000..8b1f3e9eb2c --- /dev/null +++ b/Libraries/LibGUI/GMLParser.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +namespace GUI { + +static bool is_valid_class_name_character(char ch) +{ + return isalpha(ch) || ch == ':'; +} + +static bool is_valid_property_name_character(char ch) +{ + return isalpha(ch) || ch == '_'; +} + +static void swallow_whitespace(GenericLexer& scanner) +{ + scanner.consume_while([](auto ch) { return isspace(ch); }); +} + +static JsonValue parse_core_object(GenericLexer& scanner) +{ + JsonObject object; + JsonArray children; + + // '@Foo' means new Core::Object of class Foo + if (!scanner.consume_specific('@')) + ASSERT_NOT_REACHED(); + + auto class_name = scanner.consume_while([](auto ch) { return is_valid_class_name_character(ch); }); + object.set("class", JsonValue(class_name)); + + swallow_whitespace(scanner); + + if (!scanner.consume_specific('{')) + ASSERT_NOT_REACHED(); + + swallow_whitespace(scanner); + + for (;;) { + swallow_whitespace(scanner); + + if (scanner.peek() == '}') { + // End of object + break; + } + + if (scanner.peek() == '@') { + // It's a child object. + auto value = parse_core_object(scanner); + ASSERT(value.is_object()); + children.append(move(value)); + } else { + // It's a property. + auto property_name = scanner.consume_while([](auto ch) { return is_valid_property_name_character(ch); }); + swallow_whitespace(scanner); + + ASSERT(!property_name.is_empty()); + + if (!scanner.consume_specific(':')) + ASSERT_NOT_REACHED(); + + swallow_whitespace(scanner); + + JsonValue value; + if (scanner.peek() == '@') { + value = parse_core_object(scanner); + ASSERT(value.is_object()); + } else { + auto value_string = scanner.consume_line(); + value = JsonValue::from_string(value_string).release_value(); + } + object.set(property_name, move(value)); + } + } + + if (!scanner.consume_specific('}')) + ASSERT_NOT_REACHED(); + + if (!children.is_empty()) + object.set("children", move(children)); + + return object; +} + +JsonValue parse_gml(const StringView& string) +{ + GenericLexer scanner(string); + auto root = parse_core_object(scanner); + + if (root.is_null()) + return JsonValue(); + + return root; +} + +} diff --git a/Libraries/LibGUI/GMLParser.h b/Libraries/LibGUI/GMLParser.h new file mode 100644 index 00000000000..e6a419551de --- /dev/null +++ b/Libraries/LibGUI/GMLParser.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace GUI { + +JsonValue parse_gml(const StringView&); + +} diff --git a/Libraries/LibGUI/Widget.cpp b/Libraries/LibGUI/Widget.cpp index 2406e2119e0..a78da4ac5aa 100644 --- a/Libraries/LibGUI/Widget.cpp +++ b/Libraries/LibGUI/Widget.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -926,18 +927,11 @@ void Widget::set_override_cursor(Gfx::StandardCursor cursor) window->update_cursor({}); } -bool Widget::load_from_json(const StringView& json_string) +bool Widget::load_from_gml(const StringView& gml_string) { - auto json_value = JsonValue::from_string(json_string); - if (!json_value.has_value()) { - dbg() << "load_from_json parse failed: _" << json_string << "_"; - return false; - } - if (!json_value.value().is_object()) { - dbg() << "load_from_json parse non-object"; - return false; - } - return load_from_json(json_value.value().as_object()); + auto value = parse_gml(gml_string); + ASSERT(value.is_object()); + return load_from_json(value.as_object()); } bool Widget::load_from_json(const JsonObject& json) diff --git a/Libraries/LibGUI/Widget.h b/Libraries/LibGUI/Widget.h index ec8e5b5f7cf..162c46039ee 100644 --- a/Libraries/LibGUI/Widget.h +++ b/Libraries/LibGUI/Widget.h @@ -300,8 +300,8 @@ public: Gfx::StandardCursor override_cursor() const { return m_override_cursor; } void set_override_cursor(Gfx::StandardCursor); - bool load_from_json(const StringView&); - bool load_from_json(const JsonObject&); + bool load_from_gml(const StringView&); + Widget* find_child_by_name(const String&); Widget* find_descendant_by_name(const String&); @@ -349,6 +349,8 @@ private: void show_tooltip(); + bool load_from_json(const JsonObject&); + Window* m_window { nullptr }; RefPtr m_layout; diff --git a/Meta/refresh-serenity-qtcreator.sh b/Meta/refresh-serenity-qtcreator.sh index c87ecd351ed..58750c80322 100755 --- a/Meta/refresh-serenity-qtcreator.sh +++ b/Meta/refresh-serenity-qtcreator.sh @@ -11,4 +11,4 @@ fi cd "$SERENITY_ROOT" -find . \( -name Base -o -name Patches -o -name Ports -o -name Root -o -name Toolchain -o -name Build \) -prune -o \( -name '*.ipc' -or -name '*.cpp' -or -name '*.idl' -or -name '*.c' -or -name '*.h' -or -name '*.S' -or -name '*.css' -or -name '*.json' \) -print > serenity.files +find . \( -name Base -o -name Patches -o -name Ports -o -name Root -o -name Toolchain -o -name Build \) -prune -o \( -name '*.ipc' -or -name '*.cpp' -or -name '*.idl' -or -name '*.c' -or -name '*.h' -or -name '*.S' -or -name '*.css' -or -name '*.json' -or -name '*.gml' \) -print > serenity.files