/* * Copyright (C) 2020-2022 The opuntiaOS Project Authors. * + Contributed by Nikita Melekhin * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #pragma once #include "../../../shared/MessageContent/MenuBar.h" #include "../../Managers/CursorManager.h" #include "MenuItem.h" #include "Widgets/BaseWidget.h" #include #include #include #include #include #include namespace WinServer { struct PopupContext { void discard() { opened = false; } bool opened { false }; int invoker_id { 0 }; }; class WindowManager; class MenuBar { friend WindowManager; public: inline static MenuBar& the() { extern MenuBar* s_WinServer_MenuBar_the; return *s_WinServer_MenuBar_the; } MenuBar(); static constexpr size_t height() { return 24; } static constexpr size_t padding() { return 8; } static constexpr size_t menubar_content_offset() { return padding() + 2; } static constexpr int popup_x_offset() { return -8; } static constexpr int text_y_offset() { return 2; } void set_background_color(const LG::Color& clr) { m_background_color = clr; } void set_style(StatusBarStyle style); size_t width() const { return m_bounds.width(); } LG::Rect& bounds() { return m_bounds; } const LG::Rect& bounds() const { return m_bounds; } template T& add_widget(Args&&... args) { T* widget = new T(args...); m_widgets.push_back(widget); return *widget; } inline LFoundation::EventLoop& event_loop() { return LFoundation::EventLoop::the(); } void invalidate_widget(BaseWidget* wg); inline bool is_hovered() const { return m_hovered; } inline PopupContext& popup_context() { return m_popup_context; } inline const PopupContext& popup_context() const { return m_popup_context; } 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); } void draw_panel_items(LG::Context& ctx); void draw_widgets(LG::Context& ctx); [[gnu::always_inline]] inline void draw(LG::Context& ctx) { ctx.set_fill_color(m_background_color); ctx.fill({ 0, 0, MenuBar::width(), MenuBar::height() }); ctx.set_fill_color(LG::Color::LightSystemOpaque); if (m_menubar_content) { draw_panel_items(ctx); } draw_widgets(ctx); } inline void on_mouse_move(const CursorManager& cursor_manager) { m_hovered = true; } inline void on_mouse_leave(const CursorManager& cursor_manager) { m_hovered = false; } void on_mouse_status_change(const CursorManager& cursor_manager); inline std::vector* menubar_content() const { return m_menubar_content; } void set_menubar_content(std::vector* mc); void set_menubar_content(std::vector* mc, Compositor& compositor); inline void popup_will_be_shown(int invoker_id) { auto& content = *m_menubar_content; popup_context().invoker_id = invoker_id; m_popup.show({ (int)panel_item_start_offset(invoker_id) + popup_x_offset(), height() + 2 }, content[invoker_id].items()); } inline void popup_will_be_closed() { popup_context().discard(); m_popup.hide(); } private: // Widgets MenuItemAnswer widget_recieve_mouse_status_change(const CursorManager& cursor_manager, size_t wind); size_t widget_start_offset(size_t index); int find_widget(int x, int y); // MenuBar Panel void invalidate_menubar_panel(); void invalidate_menubar_panel(Compositor& compositor); MenuItemAnswer panel_item_recieve_mouse_status_change(const CursorManager& cursor_manager, size_t ind); static inline size_t menubar_panel_width(const std::vector& items) { size_t width = 0; for (int ind = 0; ind < items.size(); ind++) { width += padding(); width += items[ind].width(); } return width; } size_t panel_item_start_offset(size_t index); int find_menubar_panel_item(int x, int y); LG::Rect m_bounds; std::vector* m_menubar_content { nullptr }; std::vector m_widgets; Popup& m_popup; PopupContext m_popup_context; LG::Color m_background_color; LG::Color m_text_color; bool m_hovered { false }; }; // Implementation inline void MenuBar::draw_panel_items(LG::Context& ctx) { if (!m_menubar_content) { return; } auto offset = ctx.draw_offset(); size_t start_offset = menubar_content_offset(); auto& content = *m_menubar_content; for (int ind = 0; ind < content.size(); ind++) { ctx.set_fill_color(m_text_color); ctx.set_draw_offset(LG::Point(start_offset, text_y_offset())); content[ind].draw(ctx); start_offset += content[ind].width(); start_offset += padding(); } ctx.set_draw_offset(offset); } inline void MenuBar::draw_widgets(LG::Context& ctx) { auto offset = ctx.draw_offset(); size_t start_offset = MenuBar::width() - padding(); for (int wind = m_widgets.size() - 1; wind >= 0; wind--) { ctx.set_fill_color(m_text_color); start_offset -= padding(); start_offset -= m_widgets[wind]->width(); ctx.set_draw_offset(LG::Point(start_offset, text_y_offset())); m_widgets[wind]->draw(ctx); } ctx.set_draw_offset(offset); } inline void MenuBar::on_mouse_status_change(const CursorManager& cursor_manager) { // Checking recievers int target_widget = find_widget(cursor_manager.x(), cursor_manager.y()); if (target_widget >= 0) { widget_recieve_mouse_status_change(cursor_manager, (size_t)target_widget); return; } int target_panel_item = find_menubar_panel_item(cursor_manager.x(), cursor_manager.y()); if (target_panel_item >= 0) { panel_item_recieve_mouse_status_change(cursor_manager, (size_t)target_panel_item); return; } } inline void MenuBar::set_menubar_content(std::vector* mc) { invalidate_menubar_panel(); m_menubar_content = mc; invalidate_menubar_panel(); popup_will_be_closed(); } inline void MenuBar::set_menubar_content(std::vector* mc, Compositor& compositor) { invalidate_menubar_panel(compositor); m_menubar_content = mc; invalidate_menubar_panel(compositor); popup_will_be_closed(); } inline size_t MenuBar::widget_start_offset(size_t index) { size_t start_offset = MenuBar::width() - padding(); for (int wind = m_widgets.size() - 1; wind >= (int)index; wind--) { start_offset -= padding(); start_offset -= m_widgets[wind]->width(); } return start_offset; } inline int MenuBar::find_widget(int x, int y) { size_t start_offset = MenuBar::width() - padding(); for (int wind = m_widgets.size() - 1; wind >= 0; wind--) { start_offset -= padding(); int end_offset = start_offset; start_offset -= m_widgets[wind]->width(); if (start_offset <= x && x <= end_offset) { return wind; } } return -1; } inline size_t MenuBar::panel_item_start_offset(size_t index) { if (!m_menubar_content) { return 0; } auto& content = *m_menubar_content; size_t start_offset = menubar_content_offset(); for (int ind = 0; ind < index; ind++) { start_offset += padding(); start_offset += content[ind].width(); } return start_offset; } inline int MenuBar::find_menubar_panel_item(int x, int y) { if (!m_menubar_content) { return -1; } auto& content = *m_menubar_content; size_t start_offset = menubar_content_offset(); for (int ind = 0; ind < content.size(); ind++) { int end_offset = start_offset + content[ind].width(); if (start_offset <= x && x <= end_offset) { return ind; } start_offset = end_offset + padding(); } return -1; } } // namespace WinServer