opuntiaOS - an operating system targeting x86 and ARMv7
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#pragma once 10#include "../../../shared/MessageContent/MenuBar.h" 11#include "../../Managers/CursorManager.h" 12#include "MenuItem.h" 13#include "Widgets/BaseWidget.h" 14#include <libfoundation/EventLoop.h> 15#include <libfoundation/Logger.h> 16#include <libg/Context.h> 17#include <libg/PixelBitmap.h> 18#include <libg/Point.h> 19#include <vector> 20 21namespace WinServer { 22 23struct PopupContext { 24 25 void discard() { opened = false; } 26 27 bool opened { false }; 28 int invoker_id { 0 }; 29}; 30 31class WindowManager; 32 33class MenuBar { 34 friend WindowManager; 35 36public: 37 inline static MenuBar& the() 38 { 39 extern MenuBar* s_WinServer_MenuBar_the; 40 return *s_WinServer_MenuBar_the; 41 } 42 43 MenuBar(); 44 45 static constexpr size_t height() { return 24; } 46 static constexpr size_t padding() { return 8; } 47 static constexpr size_t menubar_content_offset() { return padding() + 2; } 48 static constexpr int popup_x_offset() { return -8; } 49 static constexpr int text_y_offset() { return 2; } 50 51 void set_background_color(const LG::Color& clr) { m_background_color = clr; } 52 void set_style(StatusBarStyle style); 53 54 size_t width() const { return m_bounds.width(); } 55 LG::Rect& bounds() { return m_bounds; } 56 const LG::Rect& bounds() const { return m_bounds; } 57 58 template <class T, class... Args> 59 T& add_widget(Args&&... args) 60 { 61 T* widget = new T(args...); 62 m_widgets.push_back(widget); 63 return *widget; 64 } 65 66 inline LFoundation::EventLoop& event_loop() { return LFoundation::EventLoop::the(); } 67 void invalidate_widget(BaseWidget* wg); 68 69 inline bool is_hovered() const { return m_hovered; } 70 inline PopupContext& popup_context() { return m_popup_context; } 71 inline const PopupContext& popup_context() const { return m_popup_context; } 72 inline LG::Rect menubar_panel_bounds() { return LG::Rect(menubar_content_offset() + popup_x_offset(), text_y_offset(), menubar_panel_width(*m_menubar_content) - popup_x_offset(), height() - 3); } 73 74 void draw_panel_items(LG::Context& ctx); 75 void draw_widgets(LG::Context& ctx); 76 [[gnu::always_inline]] inline void draw(LG::Context& ctx) 77 { 78 ctx.set_fill_color(m_background_color); 79 ctx.fill({ 0, 0, MenuBar::width(), MenuBar::height() }); 80 81 ctx.set_fill_color(LG::Color::LightSystemOpaque); 82 if (m_menubar_content) { 83 draw_panel_items(ctx); 84 } 85 86 draw_widgets(ctx); 87 } 88 89 inline void on_mouse_move(const CursorManager& cursor_manager) { m_hovered = true; } 90 inline void on_mouse_leave(const CursorManager& cursor_manager) { m_hovered = false; } 91 92 void on_mouse_status_change(const CursorManager& cursor_manager); 93 94 inline std::vector<MenuDir>* menubar_content() const { return m_menubar_content; } 95 96 void set_menubar_content(std::vector<MenuDir>* mc); 97 void set_menubar_content(std::vector<MenuDir>* mc, Compositor& compositor); 98 99 inline void popup_will_be_shown(int invoker_id) 100 { 101 auto& content = *m_menubar_content; 102 popup_context().invoker_id = invoker_id; 103 m_popup.show({ (int)panel_item_start_offset(invoker_id) + popup_x_offset(), height() + 2 }, content[invoker_id].items()); 104 } 105 106 inline void popup_will_be_closed() 107 { 108 popup_context().discard(); 109 m_popup.hide(); 110 } 111 112private: 113 // Widgets 114 MenuItemAnswer widget_recieve_mouse_status_change(const CursorManager& cursor_manager, size_t wind); 115 size_t widget_start_offset(size_t index); 116 int find_widget(int x, int y); 117 118 // MenuBar Panel 119 void invalidate_menubar_panel(); 120 void invalidate_menubar_panel(Compositor& compositor); 121 MenuItemAnswer panel_item_recieve_mouse_status_change(const CursorManager& cursor_manager, size_t ind); 122 static inline size_t menubar_panel_width(const std::vector<MenuDir>& items) 123 { 124 size_t width = 0; 125 for (int ind = 0; ind < items.size(); ind++) { 126 width += padding(); 127 width += items[ind].width(); 128 } 129 return width; 130 } 131 132 size_t panel_item_start_offset(size_t index); 133 int find_menubar_panel_item(int x, int y); 134 135 LG::Rect m_bounds; 136 std::vector<MenuDir>* m_menubar_content { nullptr }; 137 std::vector<BaseWidget*> m_widgets; 138 Popup& m_popup; 139 PopupContext m_popup_context; 140 LG::Color m_background_color; 141 LG::Color m_text_color; 142 143 bool m_hovered { false }; 144}; 145 146// Implementation 147 148inline void MenuBar::draw_panel_items(LG::Context& ctx) 149{ 150 if (!m_menubar_content) { 151 return; 152 } 153 154 auto offset = ctx.draw_offset(); 155 size_t start_offset = menubar_content_offset(); 156 auto& content = *m_menubar_content; 157 158 for (int ind = 0; ind < content.size(); ind++) { 159 ctx.set_fill_color(m_text_color); 160 ctx.set_draw_offset(LG::Point<int>(start_offset, text_y_offset())); 161 content[ind].draw(ctx); 162 start_offset += content[ind].width(); 163 start_offset += padding(); 164 } 165 166 ctx.set_draw_offset(offset); 167} 168 169inline void MenuBar::draw_widgets(LG::Context& ctx) 170{ 171 auto offset = ctx.draw_offset(); 172 size_t start_offset = MenuBar::width() - padding(); 173 174 for (int wind = m_widgets.size() - 1; wind >= 0; wind--) { 175 ctx.set_fill_color(m_text_color); 176 start_offset -= padding(); 177 start_offset -= m_widgets[wind]->width(); 178 ctx.set_draw_offset(LG::Point<int>(start_offset, text_y_offset())); 179 m_widgets[wind]->draw(ctx); 180 } 181 182 ctx.set_draw_offset(offset); 183} 184 185inline void MenuBar::on_mouse_status_change(const CursorManager& cursor_manager) 186{ 187 // Checking recievers 188 int target_widget = find_widget(cursor_manager.x(), cursor_manager.y()); 189 if (target_widget >= 0) { 190 widget_recieve_mouse_status_change(cursor_manager, (size_t)target_widget); 191 return; 192 } 193 194 int target_panel_item = find_menubar_panel_item(cursor_manager.x(), cursor_manager.y()); 195 if (target_panel_item >= 0) { 196 panel_item_recieve_mouse_status_change(cursor_manager, (size_t)target_panel_item); 197 return; 198 } 199} 200 201inline void MenuBar::set_menubar_content(std::vector<MenuDir>* mc) 202{ 203 invalidate_menubar_panel(); 204 m_menubar_content = mc; 205 invalidate_menubar_panel(); 206 popup_will_be_closed(); 207} 208 209inline void MenuBar::set_menubar_content(std::vector<MenuDir>* mc, Compositor& compositor) 210{ 211 invalidate_menubar_panel(compositor); 212 m_menubar_content = mc; 213 invalidate_menubar_panel(compositor); 214 popup_will_be_closed(); 215} 216 217inline size_t MenuBar::widget_start_offset(size_t index) 218{ 219 size_t start_offset = MenuBar::width() - padding(); 220 for (int wind = m_widgets.size() - 1; wind >= (int)index; wind--) { 221 start_offset -= padding(); 222 start_offset -= m_widgets[wind]->width(); 223 } 224 return start_offset; 225} 226 227inline int MenuBar::find_widget(int x, int y) 228{ 229 size_t start_offset = MenuBar::width() - padding(); 230 for (int wind = m_widgets.size() - 1; wind >= 0; wind--) { 231 start_offset -= padding(); 232 int end_offset = start_offset; 233 start_offset -= m_widgets[wind]->width(); 234 if (start_offset <= x && x <= end_offset) { 235 return wind; 236 } 237 } 238 return -1; 239} 240 241inline size_t MenuBar::panel_item_start_offset(size_t index) 242{ 243 if (!m_menubar_content) { 244 return 0; 245 } 246 247 auto& content = *m_menubar_content; 248 size_t start_offset = menubar_content_offset(); 249 for (int ind = 0; ind < index; ind++) { 250 start_offset += padding(); 251 start_offset += content[ind].width(); 252 } 253 return start_offset; 254} 255 256inline int MenuBar::find_menubar_panel_item(int x, int y) 257{ 258 if (!m_menubar_content) { 259 return -1; 260 } 261 262 auto& content = *m_menubar_content; 263 size_t start_offset = menubar_content_offset(); 264 for (int ind = 0; ind < content.size(); ind++) { 265 int end_offset = start_offset + content[ind].width(); 266 if (start_offset <= x && x <= end_offset) { 267 return ind; 268 } 269 start_offset = end_offset + padding(); 270 } 271 return -1; 272} 273 274} // namespace WinServer