opuntiaOS - an operating system targeting x86 and ARMv7
at master 6.8 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/EventLoop.h> 10#include <libg/Color.h> 11#include <libui/Context.h> 12#include <libui/View.h> 13 14namespace UI { 15 16View::View(View* superview, const LG::Rect& frame) 17 : m_frame(frame) 18 , m_superview(superview) 19 , m_window(superview ? superview->window() : nullptr) 20 , m_bounds(0, 0, frame.width(), frame.height()) 21{ 22} 23 24View::View(View* superview, Window* window, const LG::Rect& frame) 25 : m_frame(frame) 26 , m_superview(superview) 27 , m_window(window) 28 , m_bounds(0, 0, frame.width(), frame.height()) 29{ 30} 31 32void View::remove_from_superview() 33{ 34} 35 36std::optional<View*> View::subview_at(const LG::Point<int>& point) const 37{ 38 for (int i = subviews().size() - 1; i >= 0; --i) { 39 if (subviews()[i]->frame().contains(point)) { 40 return subviews()[i]; 41 } 42 } 43 return {}; 44} 45 46View& View::hit_test(const LG::Point<int>& point) 47{ 48 auto subview = subview_at(point); 49 if (subview.has_value()) { 50 return subview.value()->hit_test(point - subview.value()->frame().origin()); 51 } 52 return *this; 53} 54 55LG::Rect View::frame_in_window() 56{ 57 View* view = this; 58 auto rect = frame(); 59 while (view->has_superview()) { 60 view = view->superview(); 61 rect.offset_by(view->frame().origin()); 62 } 63 return rect; 64} 65 66void View::layout_subviews() 67{ 68 // TODO: Apply topsort to find the right order. 69 for (const auto& constraint : m_constrints) { 70 constraint_interpreter(constraint); 71 } 72} 73 74std::optional<LG::Point<int>> View::subview_location(const View& subview) const 75{ 76 return subview.frame().origin(); 77} 78 79void View::set_needs_display(const LG::Rect& rect) 80{ 81 auto display_rect = rect; 82 display_rect.intersect(bounds()); 83 if (has_superview()) { 84 auto location = superview()->subview_location(*this); 85 display_rect.offset_by(location.value()); 86 superview()->set_needs_display(display_rect); 87 } else { 88 send_display_message_to_self(*window(), display_rect); 89 } 90} 91 92void View::display(const LG::Rect& rect) 93{ 94 LG::Context ctx = graphics_current_context(); 95 ctx.set_fill_color(background_color()); 96 ctx.fill_rounded(rect, layer().corner_mask()); 97} 98 99void View::did_display(const LG::Rect& rect) 100{ 101} 102 103void View::mouse_moved(const LG::Point<int>& location) 104{ 105} 106 107void View::mouse_entered(const LG::Point<int>& location) 108{ 109 set_hovered(true); 110 set_needs_display(); 111} 112 113void View::mouse_exited() 114{ 115 set_hovered(false); 116 set_active(false); 117 set_needs_display(); 118} 119 120void View::mouse_down(const LG::Point<int>& location) 121{ 122 m_active = true; 123 set_needs_display(); 124} 125 126void View::mouse_up() 127{ 128 m_active = false; 129 set_needs_display(); 130} 131 132void View::mouse_wheel_event(int wheel_data) 133{ 134} 135 136void View::receive_mouse_move_event(MouseEvent& event) 137{ 138 auto location = LG::Point<int>(event.x(), event.y()); 139 if (!is_hovered()) { 140 mouse_entered(location); 141 } 142 143 foreach_subview([&](View& subview) -> bool { 144 bool event_hits_subview = subview.frame().contains(event.x(), event.y()); 145 if (subview.is_hovered() && !event_hits_subview) { 146 LG::Point<int> point(event.x(), event.y()); 147 point.offset_by(-subview.frame().origin()); 148 MouseLeaveEvent mle(point.x(), point.y()); 149 subview.receive_mouse_leave_event(mle); 150 } else if (event_hits_subview) { 151 LG::Point<int> point(event.x(), event.y()); 152 point.offset_by(-subview.frame().origin()); 153 MouseEvent me(point.x(), point.y()); 154 subview.receive_mouse_move_event(me); 155 } 156 return true; 157 }); 158 159 mouse_moved(location); 160 Responder::receive_mouse_move_event(event); 161} 162 163void View::receive_mouse_action_event(MouseActionEvent& event) 164{ 165 if (event.type() == MouseActionType::LeftMouseButtonPressed) { 166 mouse_down(LG::Point<int>(event.x(), event.y())); 167 } else if (event.type() == MouseActionType::LeftMouseButtonReleased) { 168 mouse_up(); 169 } 170 171 Responder::receive_mouse_action_event(event); 172} 173 174void View::receive_mouse_leave_event(MouseLeaveEvent& event) 175{ 176 if (!is_hovered()) { 177 return; 178 } 179 180 foreach_subview([&](View& subview) -> bool { 181 if (subview.is_hovered()) { 182 LG::Point<int> point(event.x(), event.y()); 183 point.offset_by(-subview.frame().origin()); 184 MouseLeaveEvent mle(point.x(), point.y()); 185 subview.receive_mouse_leave_event(mle); 186 } 187 return true; 188 }); 189 190 mouse_exited(); 191 Responder::receive_mouse_leave_event(event); 192} 193 194void View::receive_mouse_wheel_event(MouseWheelEvent& event) 195{ 196 bool found = false; 197 foreach_subview([&](View& subview) -> bool { 198 if (subview.is_hovered()) { 199 LG::Point<int> point(event.x(), event.y()); 200 point.offset_by(-subview.frame().origin()); 201 MouseWheelEvent mwe(point.x(), point.y(), event.wheel_data()); 202 subview.receive_mouse_wheel_event(mwe); 203 found = true; 204 } 205 return true; 206 }); 207 208 if (!found) { 209 mouse_wheel_event(event.wheel_data()); 210 } 211} 212 213void View::receive_keyup_event(KeyUpEvent&) 214{ 215} 216 217void View::receive_keydown_event(KeyDownEvent&) 218{ 219} 220 221void View::receive_display_event(DisplayEvent& event) 222{ 223 event.bounds().intersect(bounds()); 224 display(event.bounds()); 225 foreach_subview([&](View& subview) -> bool { 226 auto bounds = event.bounds(); 227 if (bounds.intersects(subview.frame())) { 228 graphics_push_context(Context(subview, Context::RelativeToCurrentContext::Yes)); 229 bounds.offset_by(-subview.frame().origin()); 230 DisplayEvent own_event(bounds); 231 subview.receive_display_event(own_event); 232 graphics_pop_context(); 233 } 234 return true; 235 }); 236 did_display(event.bounds()); 237 238 if (!has_superview()) { 239 // Only superview sends invalidate_message to server. 240 bool success = send_invalidate_message_to_server(event.bounds()); 241 } 242 243 Responder::receive_display_event(event); 244} 245 246bool View::receive_layout_event(const LayoutEvent& event, bool force_layout_if_not_target) 247{ 248 bool need_to_layout = (this == event.target()) | force_layout_if_not_target; 249 if (need_to_layout) { 250 layout_subviews(); 251 } 252 253 foreach_subview([&](View& subview) -> bool { 254 bool found_target = subview.receive_layout_event(event, need_to_layout); 255 return need_to_layout || !found_target; 256 }); 257 258 return need_to_layout; 259} 260 261} // namespace UI