Преглед изворни кода

Serendipity: A new welcome app

Provides the basic Help+ReadMe care package to new users and some
interesting tips to get started. Feel free to add more!
thankyouverycool пре 4 година
родитељ
комит
71a123148a

+ 1 - 0
Base/etc/shellrc

@@ -27,5 +27,6 @@ alias ws=WebServer
 alias sl=Solitaire
 alias wv=WebView
 alias ue=UserspaceEmulator
+alias welcome=Serendipity
 
 alias ll='ls -l'

+ 10 - 0
Base/home/anon/Documents/tips.txt

@@ -0,0 +1,10 @@
+# SerenityOS Tips
+# Did you know...
+Pressing Ctrl+Alt+Space at an insertion caret pops up the emoji picker.
+Holding Super while scrolling the mouse wheel over a window adjusts its transparency.
+Browser has built-in ad blocking. Filter content by adding new domains to ~/.config/BrowserContentFiltering.txt
+Default file and protocol associations can be changed in ~/.config/LaunchServer.ini
+Text Editor has multiple viewing modes; edit and preview HTML and Markdown in real time.
+It can help to get a second pair of $ Eyes on a problem. Or fifty: $ Eyes -n 100
+Highlighted text in Terminal can be launched or right-clicked for more context.
+Focus can be cycled between windows by pressing and holding Super+Tab. Shift reverses the order.

BIN
Base/res/graphics/welcome-serendipity.png


BIN
Base/res/icons/16x16/app-serendipity.png


BIN
Base/res/icons/32x32/app-serendipity.png


+ 1 - 0
Userland/Applications/CMakeLists.txt

@@ -17,6 +17,7 @@ add_subdirectory(Piano)
 add_subdirectory(PixelPaint)
 add_subdirectory(QuickShow)
 add_subdirectory(Run)
+add_subdirectory(Serendipity)
 add_subdirectory(SoundPlayer)
 add_subdirectory(SpaceAnalyzer)
 add_subdirectory(Spreadsheet)

+ 11 - 0
Userland/Applications/Serendipity/CMakeLists.txt

@@ -0,0 +1,11 @@
+compile_gml(SerendipityWindow.gml SerendipityWindowGML.h serendipity_window_gml)
+
+set(SOURCES
+    SerendipityWindowGML.h
+    SerendipityWidget.cpp
+    SerendipityWidget.h
+    main.cpp
+)
+
+serenity_app(Serendipity ICON app-serendipity)
+target_link_libraries(Serendipity LibGUI LibWeb)

+ 171 - 0
Userland/Applications/Serendipity/SerendipityWidget.cpp

@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2021, 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 "SerendipityWidget.h"
+#include <Applications/Serendipity/SerendipityWindowGML.h>
+#include <LibCore/File.h>
+#include <LibGUI/Application.h>
+#include <LibGUI/Button.h>
+#include <LibGUI/CheckBox.h>
+#include <LibGUI/Label.h>
+#include <LibGUI/Layout.h>
+#include <LibGUI/SeparatorWidget.h>
+#include <LibGfx/FontDatabase.h>
+#include <LibGfx/Painter.h>
+#include <LibGfx/Palette.h>
+#include <LibMarkdown/Document.h>
+#include <LibWeb/OutOfProcessWebView.h>
+#include <serenity.h>
+#include <spawn.h>
+#include <time.h>
+
+SerendipityWidget::SerendipityWidget()
+{
+    load_from_gml(serendipity_window_gml);
+
+    auto& banner_label = *find_descendant_of_type_named<GUI::Label>("banner_label");
+    banner_label.set_icon(Gfx::Bitmap::load_from_file("/res/graphics/welcome-serendipity.png"));
+
+    auto& navigation_column = *find_descendant_of_type_named<GUI::Widget>("navigation_column");
+    navigation_column.layout()->add_spacer();
+
+    auto& nav_separator = navigation_column.add<GUI::SeparatorWidget>(Gfx::Orientation::Horizontal);
+    nav_separator.set_max_height(2);
+
+    auto& tip_frame = *find_descendant_of_type_named<GUI::Frame>("tip_frame");
+    auto palette = tip_frame.palette();
+    palette.set_color(Gfx::ColorRole::Base, Color::from_rgb(0xffffe1));
+    tip_frame.set_palette(palette);
+    tip_frame.set_background_role(Gfx::ColorRole::Base);
+    tip_frame.set_fill_with_background_color(true);
+
+    auto& light_bulb_label = *find_descendant_of_type_named<GUI::Label>("light_bulb_label");
+    light_bulb_label.set_icon(Gfx::Bitmap::load_from_file("/res/icons/32x32/app-serendipity.png"));
+
+    auto& did_you_know_label = *find_descendant_of_type_named<GUI::Label>("did_you_know_label");
+    did_you_know_label.set_font(Gfx::BitmapFont::load_from_file("/res/fonts/KaticaBold12.font"));
+
+    m_web_view = *find_descendant_of_type_named<Web::OutOfProcessWebView>("web_view");
+
+    m_tip_label = *find_descendant_of_type_named<GUI::Label>("tip_label");
+    m_tip_label->set_font(Gfx::BitmapFont::load_from_file("/res/fonts/KaticaRegular12.font"));
+
+    m_next_button = *find_descendant_of_type_named<GUI::Button>("next_button");
+    m_next_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"));
+    m_next_button->on_click = [&]() {
+        if (!tip_frame.is_visible()) {
+            m_web_view->set_visible(false);
+            tip_frame.set_visible(true);
+        }
+        if (m_tips.is_empty())
+            return;
+        m_initial_tip_index++;
+        if (m_initial_tip_index >= m_tips.size())
+            m_initial_tip_index = 0;
+        m_tip_label->set_text(m_tips[m_initial_tip_index]);
+    };
+
+    m_help_button = *find_descendant_of_type_named<GUI::Button>("help_button");
+    m_help_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/book-open.png"));
+    m_help_button->on_click = []() {
+        pid_t pid;
+        const char* argv[] = { "Help", nullptr };
+        if ((errno = posix_spawn(&pid, "/bin/Help", nullptr, nullptr, const_cast<char**>(argv), environ))) {
+            perror("posix_spawn");
+        } else {
+            if (disown(pid) < 0)
+                perror("disown");
+        }
+    };
+
+    m_new_button = *find_descendant_of_type_named<GUI::Button>("new_button");
+    m_new_button->on_click = [&]() {
+        m_web_view->set_visible(!m_web_view->is_visible());
+        tip_frame.set_visible(!tip_frame.is_visible());
+    };
+
+    m_close_button = *find_descendant_of_type_named<GUI::Button>("close_button");
+    m_close_button->on_click = []() {
+        GUI::Application::the()->quit();
+    };
+
+    open_and_parse_readme_file();
+    open_and_parse_tips_file();
+    srand(time(nullptr));
+    set_random_tip();
+}
+
+SerendipityWidget::~SerendipityWidget()
+{
+}
+
+void SerendipityWidget::open_and_parse_tips_file()
+{
+    auto file = Core::File::construct("/home/anon/Documents/tips.txt");
+    if (!file->open(Core::IODevice::ReadOnly)) {
+        m_tip_label->set_text("~/Documents/tips.txt has gone missing!");
+        return;
+    }
+
+    while (file->can_read_line()) {
+        auto line = file->read_line();
+        auto* ch = line.characters();
+        switch (*ch) {
+        case '\n':
+        case '\r':
+        case '\0':
+        case '#':
+            continue;
+        }
+        m_tips.append(line);
+    }
+}
+
+void SerendipityWidget::open_and_parse_readme_file()
+{
+    auto file = Core::File::construct("/home/anon/ReadMe.md");
+    if (!file->open(Core::IODevice::ReadOnly))
+        return;
+
+    auto document = Markdown::Document::parse(file->read_all());
+    if (document) {
+        auto html = document->render_to_html();
+        m_web_view->load_html(html, URL::create_with_file_protocol("/home/anon/ReadMe.md"));
+    }
+}
+
+void SerendipityWidget::set_random_tip()
+{
+    if (m_tips.is_empty())
+        return;
+
+    size_t n;
+    do
+        n = rand();
+    while (n >= m_tips.size());
+    m_initial_tip_index = n;
+    m_tip_label->set_text(m_tips[n]);
+}

+ 53 - 0
Userland/Applications/Serendipity/SerendipityWidget.h

@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+#pragma once
+
+#include <LibGUI/Widget.h>
+#include <LibWeb/OutOfProcessWebView.h>
+
+class SerendipityWidget final : public GUI::Widget {
+    C_OBJECT(SerendipityWidget)
+public:
+    virtual ~SerendipityWidget() override;
+
+private:
+    SerendipityWidget();
+
+    void set_random_tip();
+    void open_and_parse_tips_file();
+    void open_and_parse_readme_file();
+
+    RefPtr<GUI::Button> m_close_button;
+    RefPtr<GUI::Button> m_next_button;
+    RefPtr<GUI::Button> m_help_button;
+    RefPtr<GUI::Button> m_new_button;
+    RefPtr<GUI::Label> m_tip_label;
+    RefPtr<Web::OutOfProcessWebView> m_web_view;
+
+    size_t m_initial_tip_index { 0 };
+    Vector<String> m_tips;
+};

+ 133 - 0
Userland/Applications/Serendipity/SerendipityWindow.gml

@@ -0,0 +1,133 @@
+@GUI::Widget {
+    fill_with_background_color: true
+    layout: @GUI::VerticalBoxLayout {
+        margins: [8, 8, 8, 8]
+    }
+
+    @GUI::Widget {
+        fixed_height: 30
+        layout: @GUI::HorizontalBoxLayout {
+            margins: [4, 0, 0, 0]
+        }
+
+        @GUI::Label {
+            name: "banner_label"
+            fixed_width: 251
+        }
+
+        @GUI::Widget {
+        }
+    }
+
+    @GUI::Widget {
+        fixed_height: 160
+        layout: @GUI::HorizontalBoxLayout {
+        }
+
+        @GUI::Frame {
+            name: "tip_frame"
+            fixed_width: 340
+            fixed_height: 160
+            layout: @GUI::HorizontalBoxLayout {
+                margins: [0, 0, 16, 0]
+            }
+
+            @GUI::Widget {
+                fixed_width: 60
+                layout: @GUI::VerticalBoxLayout {
+                }
+
+                @GUI::Label {
+                    name: "light_bulb_label"
+                    fixed_height: 60
+                }
+
+                @GUI::Widget {
+                }
+            }
+
+            @GUI::Widget {
+                layout: @GUI::VerticalBoxLayout {
+                }
+
+                @GUI::Label {
+                    fixed_height: 60
+                    name: "did_you_know_label"
+                    text: "Did you know..."
+                    text_alignment: "CenterLeft"
+                }
+
+                @GUI::Label {
+                    name: "tip_label"
+                    text_alignment: "TopLeft"
+                    word_wrap: true
+                }
+            }
+        }
+
+        @Web::OutOfProcessWebView {
+            name: "web_view"
+            fixed_width: 340
+            fixed_height: 160
+            visible: false
+        }
+
+        @GUI::Widget {
+        }
+
+        @GUI::Widget {
+            name: "navigation_column"
+            fixed_width: 120
+            fixed_height: 160
+            layout: @GUI::VerticalBoxLayout {
+            }
+
+            @GUI::Button {
+                name: "new_button"
+                text: "What's New"
+                fixed_width: 115
+                fixed_height: 24
+            }
+
+            @GUI::Button {
+                name: "help_button"
+                text: "Help Contents"
+                fixed_width: 115
+                fixed_height: 24
+            }
+
+            @GUI::Button {
+                name: "next_button"
+                text: "Next Tip"
+                fixed_width: 115
+                fixed_height: 24
+            }
+
+        }
+    }
+
+    @GUI::Widget {
+    }
+
+    @GUI::Widget {
+        fixed_height: 24
+        layout: @GUI::HorizontalBoxLayout {
+        }
+
+        @GUI::CheckBox{
+            name: "startup_checkbox"
+            text: "Show this Welcome Screen next time SerenityOS starts"
+            fixed_width: 315
+        }
+
+        @GUI::Widget {
+        }
+
+        @GUI::Button {
+            name: "close_button"
+            text: "Close"
+            fixed_width: 115
+            fixed_height: 24
+        }
+    }
+}

+ 77 - 0
Userland/Applications/Serendipity/main.cpp

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2021, 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 "SerendipityWidget.h"
+#include <LibGUI/Application.h>
+#include <LibGUI/Window.h>
+
+int main(int argc, char** argv)
+{
+    auto app = GUI::Application::construct(argc, argv);
+
+    if (pledge("stdio recvfd sendfd rpath unix proc accept exec", nullptr) < 0) {
+        perror("pledge");
+        return 1;
+    }
+
+    if (unveil("/res", "r") < 0) {
+        perror("unveil");
+        return 1;
+    }
+
+    if (unveil("/home", "r") < 0) {
+        perror("unveil");
+        return 1;
+    }
+
+    if (unveil("/tmp/portal/webcontent", "rw") < 0) {
+        perror("unveil");
+        return 1;
+    }
+
+    if (unveil("/bin/Help", "x") < 0) {
+        perror("unveil");
+        return 1;
+    }
+
+    if (unveil(nullptr, nullptr) < 0) {
+        perror("unveil");
+        return 1;
+    }
+
+    auto window = GUI::Window::construct();
+    window->resize(480, 250);
+    window->center_on_screen();
+
+    window->set_title("Welcome");
+    window->set_resizable(false);
+    window->set_window_type(GUI::WindowType::ToolWindow);
+    window->set_main_widget<SerendipityWidget>();
+
+    window->show();
+
+    return app->exec();
+}