mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 15:27:42 +00:00
LibGUI+WindowServer: Flash menubar menu when using a keyboard shortcut
Briefly flash the menubar menu containing the keyboard shortcut action to give the user immediate visual feedback on their interaction with the system.
This commit is contained in:
parent
ed0f4bdfaf
commit
6c049ea4c4
11 changed files with 88 additions and 1 deletions
|
@ -135,6 +135,14 @@ void Action::activate(Core::Object* activator)
|
||||||
m_activator = nullptr;
|
m_activator = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Action::flash_menubar_menu()
|
||||||
|
{
|
||||||
|
if (auto* app = Application::the())
|
||||||
|
if (auto* window = app->active_window())
|
||||||
|
for (auto& menu_item : m_menu_items)
|
||||||
|
window->flash_menubar_menu_for(*menu_item);
|
||||||
|
}
|
||||||
|
|
||||||
void Action::register_button(Badge<Button>, Button& button)
|
void Action::register_button(Badge<Button>, Button& button)
|
||||||
{
|
{
|
||||||
m_buttons.set(&button);
|
m_buttons.set(&button);
|
||||||
|
|
|
@ -92,6 +92,7 @@ public:
|
||||||
Function<void(Action&)> on_activation;
|
Function<void(Action&)> on_activation;
|
||||||
|
|
||||||
void activate(Core::Object* activator = nullptr);
|
void activate(Core::Object* activator = nullptr);
|
||||||
|
void flash_menubar_menu();
|
||||||
|
|
||||||
bool is_enabled() const { return m_enabled; }
|
bool is_enabled() const { return m_enabled; }
|
||||||
void set_enabled(bool);
|
void set_enabled(bool);
|
||||||
|
|
|
@ -48,6 +48,7 @@ public:
|
||||||
bool is_default() const { return m_default; }
|
bool is_default() const { return m_default; }
|
||||||
void set_default(bool);
|
void set_default(bool);
|
||||||
|
|
||||||
|
int menu_id() const { return m_menu_id; }
|
||||||
void set_menu_id(Badge<Menu>, unsigned menu_id);
|
void set_menu_id(Badge<Menu>, unsigned menu_id);
|
||||||
void set_identifier(Badge<Menu>, unsigned identifier);
|
void set_identifier(Badge<Menu>, unsigned identifier);
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <LibGUI/Application.h>
|
#include <LibGUI/Application.h>
|
||||||
#include <LibGUI/Desktop.h>
|
#include <LibGUI/Desktop.h>
|
||||||
#include <LibGUI/Event.h>
|
#include <LibGUI/Event.h>
|
||||||
|
#include <LibGUI/MenuItem.h>
|
||||||
#include <LibGUI/Menubar.h>
|
#include <LibGUI/Menubar.h>
|
||||||
#include <LibGUI/Painter.h>
|
#include <LibGUI/Painter.h>
|
||||||
#include <LibGUI/Widget.h>
|
#include <LibGUI/Widget.h>
|
||||||
|
@ -1204,6 +1205,15 @@ Menu& Window::add_menu(String name)
|
||||||
return *menu;
|
return *menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Window::flash_menubar_menu_for(const MenuItem& menu_item)
|
||||||
|
{
|
||||||
|
auto menu_id = menu_item.menu_id();
|
||||||
|
if (menu_id < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WindowServerConnection::the().async_flash_menubar_menu(m_window_id, menu_id);
|
||||||
|
}
|
||||||
|
|
||||||
bool Window::is_modified() const
|
bool Window::is_modified() const
|
||||||
{
|
{
|
||||||
if (!m_window_id)
|
if (!m_window_id)
|
||||||
|
|
|
@ -211,6 +211,7 @@ public:
|
||||||
|
|
||||||
Menu& add_menu(String name);
|
Menu& add_menu(String name);
|
||||||
ErrorOr<NonnullRefPtr<Menu>> try_add_menu(String name);
|
ErrorOr<NonnullRefPtr<Menu>> try_add_menu(String name);
|
||||||
|
void flash_menubar_menu_for(const MenuItem&);
|
||||||
|
|
||||||
void flush_pending_paints_immediately();
|
void flush_pending_paints_immediately();
|
||||||
|
|
||||||
|
|
|
@ -172,6 +172,7 @@ void WindowServerConnection::key_down(i32 window_id, u32 code_point, u32 key, u3
|
||||||
|
|
||||||
if (auto* action = action_for_key_event(*window, *key_event)) {
|
if (auto* action = action_for_key_event(*window, *key_event)) {
|
||||||
if (action->is_enabled()) {
|
if (action->is_enabled()) {
|
||||||
|
action->flash_menubar_menu();
|
||||||
action->activate();
|
action->activate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,6 +192,40 @@ void ClientConnection::update_menu_item(i32 menu_id, i32 identifier, [[maybe_unu
|
||||||
menu_item->set_checked(checked);
|
menu_item->set_checked(checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientConnection::flash_menubar_menu(i32 window_id, i32 menu_id)
|
||||||
|
{
|
||||||
|
auto itw = m_windows.find(window_id);
|
||||||
|
if (itw == m_windows.end()) {
|
||||||
|
did_misbehave("FlashMenubarMenu: Bad window ID");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& window = *(*itw).value;
|
||||||
|
|
||||||
|
auto itm = m_menus.find(menu_id);
|
||||||
|
if (itm == m_menus.end()) {
|
||||||
|
did_misbehave("FlashMenubarMenu: Bad menu ID");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& menu = *(*itm).value;
|
||||||
|
|
||||||
|
if (window.menubar().flash_menu(&menu)) {
|
||||||
|
window.frame().invalidate_menubar();
|
||||||
|
|
||||||
|
if (m_flashed_menu_timer && m_flashed_menu_timer->is_active()) {
|
||||||
|
m_flashed_menu_timer->on_timeout();
|
||||||
|
m_flashed_menu_timer->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_flashed_menu_timer = Core::Timer::create_single_shot(75, [&window] {
|
||||||
|
window.menubar().flash_menu(nullptr);
|
||||||
|
window.frame().invalidate_menubar();
|
||||||
|
});
|
||||||
|
m_flashed_menu_timer->start();
|
||||||
|
} else if (m_flashed_menu_timer) {
|
||||||
|
m_flashed_menu_timer->restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ClientConnection::add_menu_separator(i32 menu_id)
|
void ClientConnection::add_menu_separator(i32 menu_id)
|
||||||
{
|
{
|
||||||
auto it = m_menus.find(menu_id);
|
auto it = m_menus.find(menu_id);
|
||||||
|
|
|
@ -97,6 +97,7 @@ private:
|
||||||
virtual void add_menu_item(i32, i32, i32, String const&, bool, bool, bool, bool, String const&, Gfx::ShareableBitmap const&, bool) override;
|
virtual void add_menu_item(i32, i32, i32, String const&, bool, bool, bool, bool, String const&, Gfx::ShareableBitmap const&, bool) override;
|
||||||
virtual void add_menu_separator(i32) override;
|
virtual void add_menu_separator(i32) override;
|
||||||
virtual void update_menu_item(i32, i32, i32, String const&, bool, bool, bool, bool, String const&) override;
|
virtual void update_menu_item(i32, i32, i32, String const&, bool, bool, bool, bool, String const&) override;
|
||||||
|
virtual void flash_menubar_menu(i32, i32) override;
|
||||||
virtual void create_window(i32, Gfx::IntRect const&, bool, bool, bool, bool, bool,
|
virtual void create_window(i32, Gfx::IntRect const&, bool, bool, bool, bool, bool,
|
||||||
bool, bool, bool, bool, bool, float, float, Gfx::IntSize const&, Gfx::IntSize const&, Gfx::IntSize const&,
|
bool, bool, bool, bool, bool, float, float, Gfx::IntSize const&, Gfx::IntSize const&, Gfx::IntSize const&,
|
||||||
Optional<Gfx::IntSize> const&, i32, String const&, i32, Gfx::IntRect const&) override;
|
Optional<Gfx::IntSize> const&, i32, String const&, i32, Gfx::IntRect const&) override;
|
||||||
|
@ -178,6 +179,7 @@ private:
|
||||||
HashMap<int, NonnullRefPtr<Window>> m_windows;
|
HashMap<int, NonnullRefPtr<Window>> m_windows;
|
||||||
HashMap<int, NonnullRefPtr<Menu>> m_menus;
|
HashMap<int, NonnullRefPtr<Menu>> m_menus;
|
||||||
|
|
||||||
|
RefPtr<Core::Timer> m_flashed_menu_timer;
|
||||||
RefPtr<Core::Timer> m_ping_timer;
|
RefPtr<Core::Timer> m_ping_timer;
|
||||||
|
|
||||||
bool m_has_display_link { false };
|
bool m_has_display_link { false };
|
||||||
|
|
|
@ -23,6 +23,26 @@ public:
|
||||||
layout_menu(menu, window_rect);
|
layout_menu(menu, window_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool flash_menu(Menu* flashed_submenu)
|
||||||
|
{
|
||||||
|
Menu* const old_flashed_menu = m_flashed_menu;
|
||||||
|
m_flashed_menu = nullptr;
|
||||||
|
|
||||||
|
if (flashed_submenu) {
|
||||||
|
for_each_menu([&](Menu& menu) {
|
||||||
|
if ((&menu) == flashed_submenu || menu.is_menu_ancestor_of(*flashed_submenu)) {
|
||||||
|
m_flashed_menu = &menu;
|
||||||
|
return IterationDecision::Break;
|
||||||
|
}
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (old_flashed_menu != m_flashed_menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu* flashed_menu() const { return m_flashed_menu; }
|
||||||
|
|
||||||
bool has_menus()
|
bool has_menus()
|
||||||
{
|
{
|
||||||
return !m_menus.is_empty();
|
return !m_menus.is_empty();
|
||||||
|
@ -40,6 +60,7 @@ private:
|
||||||
void layout_menu(Menu&, Gfx::IntRect window_rect);
|
void layout_menu(Menu&, Gfx::IntRect window_rect);
|
||||||
|
|
||||||
Vector<Menu&> m_menus;
|
Vector<Menu&> m_menus;
|
||||||
|
Menu* m_flashed_menu { nullptr };
|
||||||
|
|
||||||
// FIXME: This doesn't support removing menus from a menubar or inserting a
|
// FIXME: This doesn't support removing menus from a menubar or inserting a
|
||||||
// menu in the middle.
|
// menu in the middle.
|
||||||
|
|
|
@ -264,8 +264,15 @@ void WindowFrame::paint_menubar(Gfx::Painter& painter)
|
||||||
painter.translate(menubar_rect.location());
|
painter.translate(menubar_rect.location());
|
||||||
|
|
||||||
m_window.menubar().for_each_menu([&](Menu& menu) {
|
m_window.menubar().for_each_menu([&](Menu& menu) {
|
||||||
|
bool paint_as_flashed = ((&menu) == m_window.menubar().flashed_menu());
|
||||||
|
if (paint_as_flashed) {
|
||||||
|
auto flashed_rect = menu.rect_in_window_menubar();
|
||||||
|
flashed_rect.shrink(2, 2);
|
||||||
|
painter.fill_rect(flashed_rect, palette.selection());
|
||||||
|
}
|
||||||
|
|
||||||
auto text_rect = menu.rect_in_window_menubar();
|
auto text_rect = menu.rect_in_window_menubar();
|
||||||
Color text_color = palette.window_text();
|
Color text_color = (paint_as_flashed ? palette.selection_text() : palette.window_text());
|
||||||
auto is_open = menu.is_open();
|
auto is_open = menu.is_open();
|
||||||
if (is_open)
|
if (is_open)
|
||||||
text_rect.translate_by(1, 1);
|
text_rect.translate_by(1, 1);
|
||||||
|
|
|
@ -24,6 +24,7 @@ endpoint WindowServer
|
||||||
add_menu_separator(i32 menu_id) =|
|
add_menu_separator(i32 menu_id) =|
|
||||||
|
|
||||||
update_menu_item(i32 menu_id, i32 identifier, i32 submenu_id, [UTF8] String text, bool enabled, bool checkable, bool checked, bool is_default, [UTF8] String shortcut) =|
|
update_menu_item(i32 menu_id, i32 identifier, i32 submenu_id, [UTF8] String text, bool enabled, bool checkable, bool checked, bool is_default, [UTF8] String shortcut) =|
|
||||||
|
flash_menubar_menu(i32 window_id, i32 menu_id) =|
|
||||||
|
|
||||||
create_window(
|
create_window(
|
||||||
i32 window_id,
|
i32 window_id,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue