mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 12:47:35 +00:00
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!
This commit is contained in:
parent
0443cd4eed
commit
71a123148a
11 changed files with 457 additions and 0 deletions
|
@ -27,5 +27,6 @@ alias ws=WebServer
|
|||
alias sl=Solitaire
|
||||
alias wv=WebView
|
||||
alias ue=UserspaceEmulator
|
||||
alias welcome=Serendipity
|
||||
|
||||
alias ll='ls -l'
|
||||
|
|
10
Base/home/anon/Documents/tips.txt
Normal file
10
Base/home/anon/Documents/tips.txt
Normal file
|
@ -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
Normal file
BIN
Base/res/graphics/welcome-serendipity.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 625 B |
BIN
Base/res/icons/16x16/app-serendipity.png
Normal file
BIN
Base/res/icons/16x16/app-serendipity.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 229 B |
BIN
Base/res/icons/32x32/app-serendipity.png
Normal file
BIN
Base/res/icons/32x32/app-serendipity.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 678 B |
|
@ -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
Userland/Applications/Serendipity/CMakeLists.txt
Normal file
11
Userland/Applications/Serendipity/CMakeLists.txt
Normal file
|
@ -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
Userland/Applications/Serendipity/SerendipityWidget.cpp
Normal file
171
Userland/Applications/Serendipity/SerendipityWidget.cpp
Normal file
|
@ -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
Userland/Applications/Serendipity/SerendipityWidget.h
Normal file
53
Userland/Applications/Serendipity/SerendipityWidget.h
Normal file
|
@ -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
Userland/Applications/Serendipity/SerendipityWindow.gml
Normal file
133
Userland/Applications/Serendipity/SerendipityWindow.gml
Normal file
|
@ -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
Userland/Applications/Serendipity/main.cpp
Normal file
77
Userland/Applications/Serendipity/main.cpp
Normal file
|
@ -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();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue