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