opuntiaOS - an operating system targeting x86 and ARMv7
at master 7.6 kB view raw
1/* 2 * Copyright (C) 2020-2022 The opuntiaOS Project Authors. 3 * + Contributed by Nikita Melekhin <nimelehin@gmail.com> 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9#include <libfoundation/Memory.h> 10#include <libui/App.h> 11#include <libui/Connection.h> 12#include <libui/Context.h> 13#include <libui/Window.h> 14 15namespace UI { 16 17Window::Window(const std::string& title, const LG::Size& size, WindowType type) 18 : m_bounds(0, 0, size.width(), size.height()) 19 , m_buffer(size_t(size.width() * size.height())) 20 , m_bitmap() 21 , m_title(title) 22 , m_type(type) 23 , m_status_bar_style() 24{ 25 m_id = Connection::the().new_window(*this); 26 m_menubar.set_host_window_id(m_id); 27 m_popup.set_host_window_id(m_id); 28 m_bitmap = LG::PixelBitmap(m_buffer.data(), bounds().width(), bounds().height()); 29 App::the().set_window(this); 30} 31 32Window::Window(const std::string& title, const LG::Size& size, const std::string& icon_path) 33 : m_bounds(0, 0, size.width(), size.height()) 34 , m_buffer(size_t(size.width() * size.height())) 35 , m_bitmap() 36 , m_title(title) 37 , m_icon_path(icon_path) 38 , m_status_bar_style() 39{ 40 m_id = Connection::the().new_window(*this); 41 m_menubar.set_host_window_id(m_id); 42 m_popup.set_host_window_id(m_id); 43 m_bitmap = LG::PixelBitmap(m_buffer.data(), bounds().width(), bounds().height()); 44 App::the().set_window(this); 45} 46 47Window::Window(const std::string& title, const LG::Size& size, const std::string& icon_path, const StatusBarStyle& style) 48 : m_bounds(0, 0, size.width(), size.height()) 49 , m_buffer(size_t(size.width() * size.height())) 50 , m_bitmap() 51 , m_title(title) 52 , m_icon_path(icon_path) 53 , m_status_bar_style(style) 54{ 55 m_id = Connection::the().new_window(*this); 56 m_menubar.set_host_window_id(m_id); 57 m_popup.set_host_window_id(m_id); 58 m_bitmap = LG::PixelBitmap(m_buffer.data(), bounds().width(), bounds().height()); 59 App::the().set_window(this); 60} 61 62bool Window::set_title(const std::string& title) 63{ 64 m_title = title; 65 SetTitleMessage msg(Connection::the().key(), id(), title); 66 return App::the().connection().send_async_message(msg); 67} 68 69bool Window::set_status_bar_style(StatusBarStyle style) 70{ 71 m_status_bar_style = style; 72 SetBarStyleMessage msg(Connection::the().key(), id(), style.color().u32(), style.flags()); 73 return App::the().connection().send_async_message(msg); 74} 75 76bool Window::did_buffer_change() 77{ 78 m_bitmap.set_data(buffer().data()); 79 m_bitmap.set_size({ bounds().width(), bounds().height() }); 80 81 // If we have a superview, we also have a context for the superview. 82 // This context should be updated, since the superview is resized. 83 if (m_superview) { 84 graphics_pop_context(); 85 graphics_push_context(Context(*m_superview)); 86 } 87 88 SetBufferMessage msg(Connection::the().key(), id(), buffer().id(), bitmap().format(), bounds()); 89 return App::the().connection().send_async_message(msg); 90} 91 92bool Window::did_format_change() 93{ 94 if (bitmap().format() == LG::PixelBitmapFormat::RGBA) { 95 // Set full bitmap as opaque, to mix colors correctly. 96 fill_with_opaque(bounds()); 97 } 98 99 return did_buffer_change(); 100} 101 102void Window::receive_event(std::unique_ptr<LFoundation::Event> event) 103{ 104 if (event->type() == Event::Type::MouseEvent) { 105 if (m_superview) { 106 MouseEvent& own_event = *(MouseEvent*)event.get(); 107 m_superview->receive_mouse_move_event(own_event); 108 } 109 } 110 111 if (event->type() == Event::Type::MouseActionEvent) { 112 if (m_superview) { 113 MouseActionEvent& own_event = *(MouseActionEvent*)event.get(); 114 auto& view = m_superview->hit_test({ (int)own_event.x(), (int)own_event.y() }); 115 view.receive_mouse_action_event(own_event); 116 } 117 } 118 119 if (event->type() == Event::Type::MouseLeaveEvent) { 120 if (m_superview) { 121 MouseLeaveEvent& own_event = *(MouseLeaveEvent*)event.get(); 122 m_superview->receive_mouse_leave_event(own_event); 123 } 124 } 125 126 if (event->type() == Event::Type::MouseWheelEvent) { 127 if (m_superview) { 128 MouseWheelEvent& own_event = *(MouseWheelEvent*)event.get(); 129 m_superview->receive_mouse_wheel_event(own_event); 130 } 131 } 132 133 if (event->type() == Event::Type::KeyUpEvent) { 134 if (m_focused_view) { 135 KeyUpEvent& own_event = *(KeyUpEvent*)event.get(); 136 m_focused_view->receive_keyup_event(own_event); 137 } 138 } 139 140 if (event->type() == Event::Type::KeyDownEvent) { 141 if (m_focused_view) { 142 KeyDownEvent& own_event = *(KeyDownEvent*)event.get(); 143 m_focused_view->receive_keydown_event(own_event); 144 } 145 } 146 147 if (event->type() == Event::Type::DisplayEvent) { 148 if (m_superview) { 149 DisplayEvent& own_event = *(DisplayEvent*)event.get(); 150 151 // If the window is in RGBA mode, we have to fill this rect 152 // with opaque color before superview will mix it's color on 153 // top of bitmap. 154 if (bitmap().format() == LG::PixelBitmapFormat::RGBA) { 155 fill_with_opaque(own_event.bounds()); 156 } 157 158 m_superview->receive_display_event(own_event); 159 } 160 } 161 162 if (event->type() == Event::Type::LayoutEvent) { 163 if (m_superview) { 164 LayoutEvent& own_event = *(LayoutEvent*)event.get(); 165 m_superview->receive_layout_event(own_event); 166 } 167 } 168 169 if (event->type() == Event::Type::MenuBarActionEvent) { 170 MenuBarActionEvent& own_event = *(MenuBarActionEvent*)event.get(); 171 for (Menu& menu : menubar().menus()) { 172 if (menu.menu_id() == own_event.menu_id()) { 173 if (own_event.item_id() < menu.items().size()) [[likely]] { 174 menu.items()[own_event.item_id()].invoke(); 175 } 176 } 177 } 178 } 179 180 if (event->type() == Event::Type::PopupActionEvent) { 181 PopupActionEvent& own_event = *(PopupActionEvent*)event.get(); 182 auto& menu = popup_manager().menu(); 183 if (own_event.item_id() < menu.items().size()) [[likely]] { 184 menu.items()[own_event.item_id()].invoke(); 185 } 186 } 187 188 if (event->type() == Event::Type::ResizeEvent) { 189 ResizeEvent& own_event = *(ResizeEvent*)event.get(); 190 resize(own_event); 191 } 192} 193 194void Window::resize(ResizeEvent& resize_event) 195{ 196 m_bounds = resize_event.bounds(); 197 198 if (m_superview) [[likely]] { 199 m_superview->frame() = resize_event.bounds(); 200 m_superview->bounds() = resize_event.bounds(); 201 m_superview->set_needs_layout(); 202 } 203 204 size_t new_size = resize_event.bounds().width() * resize_event.bounds().height(); 205 if (m_buffer.size() != new_size) [[likely]] { 206 m_buffer.resize(resize_event.bounds().width() * resize_event.bounds().height()); 207 did_buffer_change(); 208 } 209} 210 211void Window::setup_superview() 212{ 213 graphics_push_context(Context(*m_superview)); 214} 215 216void Window::fill_with_opaque(const LG::Rect& rect) 217{ 218 const auto color = LG::Color(0, 0, 0, 0).u32(); 219 auto draw_bounds = rect; 220 draw_bounds.intersect(bounds()); 221 if (draw_bounds.empty()) { 222 return; 223 } 224 225 int min_x = draw_bounds.min_x(); 226 int min_y = draw_bounds.min_y(); 227 int max_x = draw_bounds.max_x(); 228 int max_y = draw_bounds.max_y(); 229 int len_x = max_x - min_x + 1; 230 for (int y = min_y; y <= max_y; y++) { 231 LFoundation::fast_set((uint32_t*)&m_bitmap[y][min_x], color, len_x); 232 } 233} 234 235} // namespace UI