Serenity Operating System
at master 179 lines 5.7 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#pragma once 8 9#include <AK/DeprecatedString.h> 10#include <AK/WeakPtr.h> 11#include <LibCore/Object.h> 12#include <LibGfx/Font/Font.h> 13#include <LibGfx/Font/FontDatabase.h> 14#include <LibGfx/Forward.h> 15#include <LibGfx/Rect.h> 16#include <WindowServer/Cursor.h> 17#include <WindowServer/Event.h> 18#include <WindowServer/MenuItem.h> 19 20namespace WindowServer { 21 22class ConnectionFromClient; 23class Menubar; 24class Window; 25 26class Menu final : public Core::Object { 27 C_OBJECT(Menu); 28 29public: 30 virtual ~Menu() override = default; 31 32 ConnectionFromClient* client() { return m_client; } 33 ConnectionFromClient const* client() const { return m_client; } 34 int menu_id() const { return m_menu_id; } 35 36 bool is_open() const; 37 38 u32 alt_shortcut_character() const { return m_alt_shortcut_character; } 39 40 bool is_empty() const { return m_items.is_empty(); } 41 size_t item_count() const { return m_items.size(); } 42 MenuItem const& item(size_t index) const { return *m_items.at(index); } 43 MenuItem& item(size_t index) { return *m_items.at(index); } 44 45 MenuItem* item_by_identifier(unsigned identifier) 46 { 47 MenuItem* found_item = nullptr; 48 for_each_item([&](auto& item) { 49 if (item.identifier() == identifier) { 50 found_item = &item; 51 return IterationDecision::Break; 52 } 53 return IterationDecision::Continue; 54 }); 55 return found_item; 56 } 57 58 void update_alt_shortcuts_for_items(); 59 void add_item(NonnullOwnPtr<MenuItem>); 60 61 DeprecatedString const& name() const { return m_name; } 62 63 template<typename Callback> 64 IterationDecision for_each_item(Callback callback) 65 { 66 for (auto& item : m_items) { 67 IterationDecision decision = callback(*item); 68 if (decision != IterationDecision::Continue) 69 return decision; 70 } 71 return IterationDecision::Continue; 72 } 73 74 Gfx::IntRect rect_in_window_menubar() const { return m_rect_in_window_menubar; } 75 void set_rect_in_window_menubar(Gfx::IntRect const& rect) { m_rect_in_window_menubar = rect; } 76 77 Gfx::IntPoint unadjusted_position() const { return m_unadjusted_position; } 78 void set_unadjusted_position(Gfx::IntPoint position) { m_unadjusted_position = position; } 79 80 Window* menu_window() { return m_menu_window.ptr(); } 81 Window& ensure_menu_window(Gfx::IntPoint); 82 83 // Invalidates the menu window so that it gets rebuilt the next time it's showed. 84 void invalidate_menu_window(); 85 86 Window* window_menu_of() { return m_window_menu_of; } 87 void set_window_menu_of(Window& window) { m_window_menu_of = window; } 88 bool is_window_menu_open() const { return m_is_window_menu_open; } 89 void set_window_menu_open(bool is_open) { m_is_window_menu_open = is_open; } 90 91 bool activate_default(); 92 93 int content_width() const; 94 95 int item_height() const; 96 static constexpr int frame_thickness() { return 2; } 97 static constexpr int horizontal_padding() { return left_padding() + right_padding(); } 98 static constexpr int left_padding() { return 14; } 99 static constexpr int right_padding() { return 14; } 100 101 void draw(); 102 void draw(MenuItem const&, bool = false); 103 Gfx::Font const& font() const; 104 105 MenuItem* item_with_identifier(unsigned); 106 bool remove_item_with_identifier(unsigned); 107 void redraw(); 108 void redraw(MenuItem const&); 109 110 MenuItem* hovered_item() const; 111 int hovered_item_index() const { return m_hovered_item_index; }; 112 113 void set_hovered_index(int index, bool make_input = false); 114 115 void clear_hovered_item(); 116 117 Function<void(MenuItem&)> on_item_activation; 118 119 void close(); 120 121 void set_visible(bool); 122 123 void popup(Gfx::IntPoint); 124 void do_popup(Gfx::IntPoint, bool make_input, bool as_submenu = false); 125 void open_button_menu(Gfx::IntPoint position, Gfx::IntRect const& button_rect); 126 127 bool is_menu_ancestor_of(Menu const&) const; 128 129 void redraw_if_theme_changed(); 130 131 bool is_scrollable() const { return m_scrollable; } 132 int scroll_offset() const { return m_scroll_offset; } 133 134 void descend_into_submenu_at_hovered_item(); 135 void open_hovered_item(bool leave_menu_open); 136 137 Vector<size_t> const* items_with_alt_shortcut(u32 alt_shortcut) const; 138 139private: 140 Menu(ConnectionFromClient*, int menu_id, DeprecatedString name); 141 142 virtual void event(Core::Event&) override; 143 144 void handle_mouse_move_event(MouseEvent const&); 145 size_t visible_item_count() const; 146 Gfx::IntRect stripe_rect(); 147 148 int item_index_at(Gfx::IntPoint); 149 static constexpr int padding_between_text_and_shortcut() { return 50; } 150 void did_activate(MenuItem&, bool leave_menu_open); 151 void update_for_new_hovered_item(bool make_input = false); 152 153 void start_activation_animation(MenuItem&); 154 155 ConnectionFromClient* m_client { nullptr }; 156 int m_menu_id { 0 }; 157 DeprecatedString m_name; 158 u32 m_alt_shortcut_character { 0 }; 159 Gfx::IntRect m_rect_in_window_menubar; 160 Gfx::IntPoint m_unadjusted_position; 161 Vector<NonnullOwnPtr<MenuItem>> m_items; 162 RefPtr<Window> m_menu_window; 163 164 WeakPtr<Window> m_window_menu_of; 165 bool m_is_window_menu_open = { false }; 166 Gfx::IntPoint m_last_position_in_hover; 167 int m_theme_index_at_last_paint { -1 }; 168 int m_hovered_item_index { -1 }; 169 170 bool m_scrollable { false }; 171 int m_scroll_offset { 0 }; 172 int m_max_scroll_offset { 0 }; 173 174 HashMap<u32, Vector<size_t>> m_alt_shortcut_character_to_item_indices; 175}; 176 177u32 find_ampersand_shortcut_character(StringView); 178 179}