opuntiaOS - an operating system targeting x86 and ARMv7
at master 4.5 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 <libg/Color.h> 10#include <libui/Context.h> 11#include <libui/ScrollView.h> 12#include <utility> 13 14namespace UI { 15 16ScrollView::ScrollView(View* superview, const LG::Rect& frame) 17 : View(superview, frame) 18{ 19} 20 21ScrollView::ScrollView(View* superview, Window* window, const LG::Rect& frame) 22 : View(superview, window, frame) 23{ 24} 25 26void ScrollView::display(const LG::Rect& rect) 27{ 28 LG::Context ctx = graphics_current_context(); 29 ctx.add_clip(rect); 30 31 display_scroll_indicators(ctx); 32} 33 34void ScrollView::did_scroll(int n_x, int n_y) 35{ 36 int x = content_offset().x(); 37 int y = content_offset().y(); 38 int max_x = std::max(0, (int)content_size().width() - (int)bounds().width()); 39 int max_y = std::max(0, (int)content_size().height() - (int)bounds().height()); 40 content_offset().set_x(std::max(0, std::min(x + n_x, max_x))); 41 content_offset().set_y(std::max(0, std::min(y + n_y, max_y))); 42 set_needs_display(); 43} 44 45void ScrollView::mouse_wheel_event(int wheel_data) 46{ 47 did_scroll(0, wheel_data * 10); 48} 49 50std::optional<LG::Point<int>> ScrollView::subview_location(const View& subview) const 51{ 52 auto frame_origin = subview.frame().origin(); 53 frame_origin.offset_by(-m_content_offset); 54 return frame_origin; 55} 56 57std::optional<View*> ScrollView::subview_at(const LG::Point<int>& point) const 58{ 59 for (int i = subviews().size() - 1; i >= 0; --i) { 60 auto frame = subviews()[i]->frame(); 61 frame.offset_by(-m_content_offset); 62 if (frame.contains(point)) { 63 return subviews()[i]; 64 } 65 } 66 return {}; 67} 68 69void ScrollView::receive_mouse_move_event(MouseEvent& event) 70{ 71 auto location = LG::Point<int>(event.x(), event.y()); 72 if (!is_hovered()) { 73 mouse_entered(location); 74 } 75 76 foreach_subview([&](View& subview) -> bool { 77 auto frame = subview.frame(); 78 frame.offset_by(-m_content_offset); 79 bool event_hits_subview = frame.contains(event.x(), event.y()); 80 if (subview.is_hovered() && !event_hits_subview) { 81 LG::Point<int> point(event.x(), event.y()); 82 point.offset_by(-frame.origin()); 83 MouseLeaveEvent mle(point.x(), point.y()); 84 subview.receive_mouse_leave_event(mle); 85 } else if (event_hits_subview) { 86 LG::Point<int> point(event.x(), event.y()); 87 point.offset_by(-frame.origin()); 88 MouseEvent me(point.x(), point.y()); 89 subview.receive_mouse_move_event(me); 90 } 91 return true; 92 }); 93 94 mouse_moved(location); 95 Responder::receive_mouse_move_event(event); 96} 97 98void ScrollView::receive_display_event(DisplayEvent& event) 99{ 100 event.bounds().intersect(bounds()); 101 display(event.bounds()); 102 foreach_subview([&](View& subview) -> bool { 103 auto bounds = event.bounds(); 104 auto frame = subview.frame(); 105 frame.offset_by(-m_content_offset); 106 bounds.intersect(frame); 107 if (!bounds.empty()) { 108 graphics_push_context(Context(subview, frame, Context::RelativeToCurrentContext::Yes)); 109 bounds.origin().offset_by(-frame.origin()); 110 DisplayEvent own_event(bounds); 111 subview.receive_display_event(own_event); 112 graphics_pop_context(); 113 } 114 return true; 115 }); 116 did_display(event.bounds()); 117 118 if (!has_superview()) { 119 // Only superview sends invalidate_message to server. 120 bool success = send_invalidate_message_to_server(event.bounds()); 121 } 122 123 Responder::receive_display_event(event); 124} 125 126void ScrollView::recalc_content_props() 127{ 128 int max_width = 0; 129 int max_height = 0; 130 131 for (auto* view : subviews()) { 132 max_width = std::max(max_width, view->frame().max_x()); 133 max_height = std::max(max_height, view->frame().max_y()); 134 } 135 136 m_content_size.set_width(max_width); 137 m_content_size.set_height(max_height); 138} 139 140void ScrollView::display_scroll_indicators(LG::Context& ctx) 141{ 142 float ratio = (float)bounds().height() / (float)content_size().height(); 143 int line_height = bounds().height() * ratio; 144 int start_y = content_offset().y() * ratio; 145 int start_x = bounds().max_x() - 6; 146 ctx.set_fill_color(LG::Color(30, 30, 30, 100)); 147 ctx.fill(LG::Rect(start_x, start_y, 4, line_height)); 148} 149 150} // namespace UI