mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 15:57:35 +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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
m_buttons.set(&button);
|
||||
|
|
|
@ -92,6 +92,7 @@ public:
|
|||
Function<void(Action&)> on_activation;
|
||||
|
||||
void activate(Core::Object* activator = nullptr);
|
||||
void flash_menubar_menu();
|
||||
|
||||
bool is_enabled() const { return m_enabled; }
|
||||
void set_enabled(bool);
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
bool is_default() const { return m_default; }
|
||||
void set_default(bool);
|
||||
|
||||
int menu_id() const { return m_menu_id; }
|
||||
void set_menu_id(Badge<Menu>, unsigned menu_id);
|
||||
void set_identifier(Badge<Menu>, unsigned identifier);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/Desktop.h>
|
||||
#include <LibGUI/Event.h>
|
||||
#include <LibGUI/MenuItem.h>
|
||||
#include <LibGUI/Menubar.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
|
@ -1204,6 +1205,15 @@ Menu& Window::add_menu(String name)
|
|||
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
|
||||
{
|
||||
if (!m_window_id)
|
||||
|
|
|
@ -211,6 +211,7 @@ public:
|
|||
|
||||
Menu& add_menu(String name);
|
||||
ErrorOr<NonnullRefPtr<Menu>> try_add_menu(String name);
|
||||
void flash_menubar_menu_for(const MenuItem&);
|
||||
|
||||
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 (action->is_enabled()) {
|
||||
action->flash_menubar_menu();
|
||||
action->activate();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -192,6 +192,40 @@ void ClientConnection::update_menu_item(i32 menu_id, i32 identifier, [[maybe_unu
|
|||
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)
|
||||
{
|
||||
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_separator(i32) 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,
|
||||
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;
|
||||
|
@ -178,6 +179,7 @@ private:
|
|||
HashMap<int, NonnullRefPtr<Window>> m_windows;
|
||||
HashMap<int, NonnullRefPtr<Menu>> m_menus;
|
||||
|
||||
RefPtr<Core::Timer> m_flashed_menu_timer;
|
||||
RefPtr<Core::Timer> m_ping_timer;
|
||||
|
||||
bool m_has_display_link { false };
|
||||
|
|
|
@ -23,6 +23,26 @@ public:
|
|||
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()
|
||||
{
|
||||
return !m_menus.is_empty();
|
||||
|
@ -40,6 +60,7 @@ private:
|
|||
void layout_menu(Menu&, Gfx::IntRect window_rect);
|
||||
|
||||
Vector<Menu&> m_menus;
|
||||
Menu* m_flashed_menu { nullptr };
|
||||
|
||||
// FIXME: This doesn't support removing menus from a menubar or inserting a
|
||||
// menu in the middle.
|
||||
|
|
|
@ -264,8 +264,15 @@ void WindowFrame::paint_menubar(Gfx::Painter& painter)
|
|||
painter.translate(menubar_rect.location());
|
||||
|
||||
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();
|
||||
Color text_color = palette.window_text();
|
||||
Color text_color = (paint_as_flashed ? palette.selection_text() : palette.window_text());
|
||||
auto is_open = menu.is_open();
|
||||
if (is_open)
|
||||
text_rect.translate_by(1, 1);
|
||||
|
|
|
@ -24,6 +24,7 @@ endpoint WindowServer
|
|||
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) =|
|
||||
flash_menubar_menu(i32 window_id, i32 menu_id) =|
|
||||
|
||||
create_window(
|
||||
i32 window_id,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue