summaryrefslogtreecommitdiff
path: root/Applications/Spreadsheet/HelpWindow.cpp
diff options
context:
space:
mode:
authorAnotherTest <ali.mpfard@gmail.com>2020-08-24 20:05:20 +0430
committerAndreas Kling <kling@serenityos.org>2020-08-24 18:21:33 +0200
commit3a07f6e345af4fc42fab3b07d0c88c45a6abed73 (patch)
tree2f7eb81a00148820bd5b7a68426f6c60e5f35230 /Applications/Spreadsheet/HelpWindow.cpp
parent12cf3e13c0fd7a1b68251d605d5a29d1e77e2526 (diff)
downloadserenity-3a07f6e345af4fc42fab3b07d0c88c45a6abed73.zip
Spreadsheet: Document runtime functions and add a help window
...that can automatically generate documentation pages from the objects.
Diffstat (limited to 'Applications/Spreadsheet/HelpWindow.cpp')
-rw-r--r--Applications/Spreadsheet/HelpWindow.cpp180
1 files changed, 180 insertions, 0 deletions
diff --git a/Applications/Spreadsheet/HelpWindow.cpp b/Applications/Spreadsheet/HelpWindow.cpp
new file mode 100644
index 0000000000..f016abffa8
--- /dev/null
+++ b/Applications/Spreadsheet/HelpWindow.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * 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 "HelpWindow.h"
+#include <LibGUI/BoxLayout.h>
+#include <LibGUI/Frame.h>
+#include <LibGUI/ListView.h>
+#include <LibGUI/Model.h>
+#include <LibGUI/Splitter.h>
+#include <LibMarkdown/Document.h>
+#include <LibWeb/Layout/LayoutNode.h>
+#include <LibWeb/OutOfProcessWebView.h>
+
+namespace Spreadsheet {
+
+class HelpListModel final : public GUI::Model {
+public:
+ static NonnullRefPtr<HelpListModel> create() { return adopt(*new HelpListModel); }
+
+ virtual ~HelpListModel() override { }
+
+ virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_keys.size(); }
+ virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return 1; }
+ virtual void update() override { }
+
+ virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role = GUI::ModelRole::Display) const override
+ {
+ if (role == GUI::ModelRole::Display) {
+ return key(index);
+ }
+
+ return {};
+ }
+
+ String key(const GUI::ModelIndex& index) const { return m_keys[index.row()]; }
+
+ void set_from(const JsonObject& object)
+ {
+ m_keys.clear();
+ object.for_each_member([this](auto& name, auto&) {
+ m_keys.append(name);
+ });
+ did_update();
+ }
+
+private:
+ HelpListModel()
+ {
+ }
+
+ Vector<String> m_keys;
+};
+
+RefPtr<HelpWindow> HelpWindow::s_the { nullptr };
+
+HelpWindow::HelpWindow(GUI::Window* parent)
+ : GUI::Window(parent)
+{
+ resize(530, 365);
+ set_title("Spreadsheet Functions Help");
+
+ auto& widget = set_main_widget<GUI::Widget>();
+ widget.set_layout<GUI::VerticalBoxLayout>().set_margins({ 4, 4, 4, 4 });
+ widget.set_fill_with_background_color(true);
+
+ auto& splitter = widget.add<GUI::HorizontalSplitter>();
+ auto& left_frame = splitter.add<GUI::Frame>();
+ left_frame.set_layout<GUI::VerticalBoxLayout>().set_margins({ 0, 0, 0, 0 });
+ left_frame.set_preferred_size(100, 0);
+ left_frame.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill);
+ auto& list_view = left_frame.add<GUI::ListView>();
+ m_listview = &list_view;
+ list_view.set_model(HelpListModel::create());
+
+ // FIXME: This should be in the Web namespace!
+ m_webview = &splitter.add<OutOfProcessWebView>();
+
+ list_view.on_activation = [this](auto& index) {
+ if (!m_webview)
+ return;
+
+ m_webview->load(URL::create_with_data("text/html", render(index)));
+ };
+}
+
+String HelpWindow::render(const GUI::ModelIndex& index)
+{
+ auto key = static_cast<HelpListModel*>(m_listview->model())->key(index);
+ auto doc_option = m_docs.get(key);
+ ASSERT(doc_option.is_object());
+
+ auto& doc = doc_option.as_object();
+
+ auto name = doc.get("name").to_string();
+ auto argc = doc.get("argc").to_u32(0);
+ auto argnames_value = doc.get("argnames");
+ ASSERT(argnames_value.is_array());
+ auto& argnames = argnames_value.as_array();
+
+ auto docstring = doc.get("doc").to_string();
+ auto examples_value = doc.get_or("examples", JsonObject {});
+ ASSERT(examples_value.is_object());
+ auto& examples = examples_value.as_object();
+
+ StringBuilder markdown_builder;
+
+ markdown_builder.append("# NAME\n`");
+ markdown_builder.append(name);
+ markdown_builder.append("`\n\n");
+
+ markdown_builder.append("# ARGUMENTS\n");
+ if (argc > 0)
+ markdown_builder.appendf("%d required argument%s: \n", argc, argc > 1 ? "s" : "");
+ else
+ markdown_builder.appendf("No required arguments.\n");
+
+ for (size_t i = 0; i < argc; ++i)
+ markdown_builder.appendf("- `%s`\n", argnames.at(i).to_string().characters());
+
+ if (argc > 0)
+ markdown_builder.append("\n");
+
+ if ((size_t)argnames.size() > argc) {
+ auto opt_count = argnames.size() - argc;
+ markdown_builder.appendf("%d optional argument%s: \n", opt_count, opt_count > 1 ? "s" : "");
+ for (size_t i = argc; i < (size_t)argnames.size(); ++i)
+ markdown_builder.appendf("- `%s`\n", argnames.at(i).to_string().characters());
+ markdown_builder.append("\n");
+ }
+
+ markdown_builder.append("# DESCRIPTION\n");
+ markdown_builder.append(docstring);
+ markdown_builder.append("\n\n");
+
+ if (!examples.is_empty()) {
+ markdown_builder.append("# EXAMPLES\n");
+ examples.for_each_member([&](auto& text, auto& description_value) {
+ markdown_builder.appendf("- %s\n\n```js\n%s\n```\n", description_value.to_string().characters(), text.characters());
+ });
+ }
+
+ auto document = Markdown::Document::parse(markdown_builder.string_view());
+ return document->render_to_html();
+}
+
+void HelpWindow::set_docs(JsonObject&& docs)
+{
+ m_docs = move(docs);
+ static_cast<HelpListModel*>(m_listview->model())->set_from(m_docs);
+ m_listview->update();
+}
+
+HelpWindow::~HelpWindow()
+{
+}
+
+}