Serenity Operating System
at hosted 265 lines 9.2 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/Optional.h> 28#include <AK/String.h> 29#include <AK/StringBuilder.h> 30#include <AK/Vector.h> 31#include <LibCore/File.h> 32#include <LibGUI/Application.h> 33#include <LibGUI/BoxLayout.h> 34#include <LibGUI/Button.h> 35#include <LibGUI/Desktop.h> 36#include <LibGUI/Label.h> 37#include <LibGUI/MessageBox.h> 38#include <LibGUI/StackWidget.h> 39#include <LibGUI/Window.h> 40#include <LibGfx/Bitmap.h> 41#include <LibGfx/Font.h> 42#include <stdio.h> 43#include <unistd.h> 44 45#include "BackgroundWidget.h" 46#include "TextWidget.h" 47#include "UnuncheckableButton.h" 48 49struct ContentPage { 50 String menu_name; 51 String title; 52 String icon = String::empty(); 53 Vector<String> content; 54}; 55 56Optional<Vector<ContentPage>> parse_welcome_file(const String& path) 57{ 58 const auto error = Optional<Vector<ContentPage>>(); 59 auto file = Core::File::construct(path); 60 61 if (!file->open(Core::IODevice::ReadOnly)) 62 return error; 63 64 Vector<ContentPage> pages; 65 StringBuilder current_output_line; 66 bool started = false; 67 ContentPage current; 68 while (true) { 69 auto buffer = file->read_line(4096); 70 if (buffer.is_null()) { 71 if (file->error()) { 72 file->close(); 73 return error; 74 } 75 76 break; 77 } 78 79 auto line = String((char*)buffer.data()); 80 if (line.length() > 1) 81 line = line.substring(0, line.length() - 1); // remove newline 82 switch (line[0]) { 83 case '*': 84 dbg() << "menu_item line:\t" << line; 85 if (started) 86 pages.append(current); 87 else 88 started = true; 89 90 current = {}; 91 current.menu_name = line.substring(2, line.length() - 2); 92 break; 93 case '$': 94 dbg() << "icon line: \t" << line; 95 current.icon = line.substring(2, line.length() - 2); 96 break; 97 case '>': 98 dbg() << "title line:\t" << line; 99 current.title = line.substring(2, line.length() - 2); 100 break; 101 case '\n': 102 dbg() << "newline"; 103 104 if (!current_output_line.to_string().is_empty()) 105 current.content.append(current_output_line.to_string()); 106 current_output_line.clear(); 107 break; 108 case '#': 109 dbg() << "comment line:\t" << line; 110 break; 111 default: 112 dbg() << "content line:\t" << line; 113 if (current_output_line.length() != 0) 114 current_output_line.append(' '); 115 current_output_line.append(line); 116 break; 117 } 118 } 119 120 if (started) { 121 current.content.append(current_output_line.to_string()); 122 pages.append(current); 123 } 124 125 file->close(); 126 return pages; 127} 128 129int main(int argc, char** argv) 130{ 131#ifdef __serenity__ 132 if (pledge("stdio shared_buffer rpath unix cpath fattr", nullptr) < 0) { 133 perror("pledge"); 134 return 1; 135 } 136#endif 137 138 GUI::Application app(argc, argv); 139 140#ifdef __serenity__ 141 if (pledge("stdio shared_buffer rpath", nullptr) < 0) { 142 perror("pledge"); 143 return 1; 144 } 145#endif 146 147 if (unveil("/res", "r") < 0) { 148 perror("unveil"); 149 return 1; 150 } 151 152 unveil(nullptr, nullptr); 153 154 Optional<Vector<ContentPage>> _pages = parse_welcome_file("/res/welcome.txt"); 155 if (!_pages.has_value()) { 156 GUI::MessageBox::show("Could not open Welcome file.", "Welcome", GUI::MessageBox::Type::Error, GUI::MessageBox::InputType::OK, nullptr); 157 return 1; 158 } 159 auto pages = _pages.value(); 160 161 auto window = GUI::Window::construct(); 162 window->set_title("Welcome"); 163 Gfx::Rect window_rect { 0, 0, 640, 360 }; 164 window_rect.center_within(GUI::Desktop::the().rect()); 165 window->set_resizable(true); 166 window->set_rect(window_rect); 167 168 auto& background = window->set_main_widget<BackgroundWidget>(); 169 background.set_fill_with_background_color(false); 170 background.set_layout<GUI::VerticalBoxLayout>(); 171 background.layout()->set_margins({ 16, 8, 16, 8 }); 172 background.layout()->set_spacing(8); 173 background.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fill); 174 175 // 176 // header 177 // 178 179 auto& header = background.add<GUI::Label>(); 180 header.set_font(Gfx::Font::load_from_file("/res/fonts/PebbletonBold11.font")); 181 header.set_text("Welcome to SerenityOS!"); 182 header.set_text_alignment(Gfx::TextAlignment::CenterLeft); 183 header.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); 184 header.set_preferred_size(0, 30); 185 186 // 187 // main section 188 // 189 190 auto& main_section = background.add<GUI::Widget>(); 191 main_section.set_layout<GUI::HorizontalBoxLayout>(); 192 main_section.layout()->set_margins({ 0, 0, 0, 0 }); 193 main_section.layout()->set_spacing(8); 194 main_section.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fill); 195 196 auto& menu = main_section.add<GUI::Widget>(); 197 menu.set_layout<GUI::VerticalBoxLayout>(); 198 menu.layout()->set_margins({ 0, 0, 0, 0 }); 199 menu.layout()->set_spacing(4); 200 menu.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill); 201 menu.set_preferred_size(100, 0); 202 203 auto& stack = main_section.add<GUI::StackWidget>(); 204 stack.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fill); 205 206 bool first = true; 207 for (auto& page : pages) { 208 auto& content = stack.add<GUI::Widget>(); 209 content.set_layout<GUI::VerticalBoxLayout>(); 210 content.layout()->set_margins({ 0, 0, 0, 0 }); 211 content.layout()->set_spacing(8); 212 content.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fill); 213 214 auto& title_box = content.add<GUI::Widget>(); 215 title_box.set_layout<GUI::HorizontalBoxLayout>(); 216 title_box.layout()->set_spacing(4); 217 title_box.set_preferred_size(0, 16); 218 title_box.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); 219 220 if (!page.icon.is_empty()) { 221 auto& icon = title_box.add<GUI::Label>(); 222 icon.set_icon(Gfx::Bitmap::load_from_file(page.icon)); 223 icon.set_preferred_size(16, 16); 224 icon.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); 225 } 226 227 auto& content_title = title_box.add<GUI::Label>(); 228 content_title.set_font(Gfx::Font::default_bold_font()); 229 content_title.set_text(page.title); 230 content_title.set_text_alignment(Gfx::TextAlignment::CenterLeft); 231 content_title.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); 232 content_title.set_preferred_size(0, 10); 233 234 for (auto& paragraph : page.content) { 235 auto& content_text = content.add<TextWidget>(); 236 content_text.set_font(Gfx::Font::default_font()); 237 content_text.set_text(paragraph); 238 content_text.set_text_alignment(Gfx::TextAlignment::TopLeft); 239 content_text.set_line_height(12); 240 content_text.wrap_and_set_height(); 241 } 242 243 auto& menu_option = menu.add<UnuncheckableButton>(); 244 menu_option.set_font(Gfx::Font::default_font()); 245 menu_option.set_text(page.menu_name); 246 menu_option.set_text_alignment(Gfx::TextAlignment::CenterLeft); 247 menu_option.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); 248 menu_option.set_preferred_size(0, 20); 249 menu_option.set_checkable(true); 250 menu_option.set_exclusive(true); 251 252 if (first) 253 menu_option.set_checked(true); 254 255 menu_option.on_click = [content = &content, &stack] { 256 stack.set_active_widget(content); 257 content->invalidate_layout(); 258 }; 259 260 first = false; 261 } 262 263 window->show(); 264 return app.exec(); 265}