mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-08-16 17:00:35 +03:00
LibGUI+WindowServer: Notify Windows on input preemption
Previously Menus set themselves as active input solely to make sure CaptureInput modals would close, but this is a functional half-truth. Menus don't actually use the active input role; they preempt normal Windows during event handling instead. Now the active input window is notified on preemption and Menus can remain outside the active input concept. This lets us make more granular choices about modal behavior. For now, the only thing clients care about is menu preemption on popup. Fixes windows which close on changes to active input closing on their own context menus.
This commit is contained in:
parent
6c35aac617
commit
463aff827e
Notes:
sideshowbarker
2024-07-17 07:20:19 +09:00
Author: https://github.com/thankyouverycool Commit: https://github.com/SerenityOS/serenity/commit/463aff827e Pull-request: https://github.com/SerenityOS/serenity/pull/15157
@ -117,6 +117,7 @@ ComboBox::ComboBox()
|
||||
}
|
||||
m_open_button->set_enabled(true);
|
||||
};
|
||||
m_list_window->on_input_preemption = [this](auto) { close(); };
|
||||
|
||||
m_list_view = m_list_window->set_main_widget<ListView>();
|
||||
m_list_view->horizontal_scrollbar().set_visible(false);
|
||||
|
@ -227,6 +227,11 @@ CommandPalette::CommandPalette(GUI::Window& parent_window, ScreenPosition screen
|
||||
if (!is_active_window)
|
||||
close();
|
||||
};
|
||||
|
||||
on_input_preemption = [this](InputPreemptor preemptor) {
|
||||
if (preemptor != InputPreemptor::ContextMenu)
|
||||
close();
|
||||
};
|
||||
}
|
||||
|
||||
void CommandPalette::collect_actions(GUI::Window& parent_window)
|
||||
|
@ -401,6 +401,12 @@ void ConnectionToWindowServer::window_state_changed(i32 window_id, bool minimize
|
||||
window->notify_state_changed({}, minimized, maximized, occluded);
|
||||
}
|
||||
|
||||
void ConnectionToWindowServer::window_input_preempted(i32 window_id, i32 preemptor)
|
||||
{
|
||||
if (auto* window = Window::from_window_id(window_id))
|
||||
window->notify_input_preempted({}, static_cast<InputPreemptor>(preemptor));
|
||||
}
|
||||
|
||||
void ConnectionToWindowServer::display_link_notification()
|
||||
{
|
||||
if (m_display_link_notification_pending)
|
||||
|
@ -54,6 +54,7 @@ private:
|
||||
virtual void update_system_fonts(String const&, String const&, String const&) override;
|
||||
virtual void update_system_effects(Vector<bool> const&) override;
|
||||
virtual void window_state_changed(i32, bool, bool, bool) override;
|
||||
virtual void window_input_preempted(i32, i32) override;
|
||||
virtual void display_link_notification() override;
|
||||
virtual void track_mouse_move(Gfx::IntPoint const&) override;
|
||||
virtual void ping() override;
|
||||
|
@ -1110,6 +1110,12 @@ void Window::notify_state_changed(Badge<ConnectionToWindowServer>, bool minimize
|
||||
}
|
||||
}
|
||||
|
||||
void Window::notify_input_preempted(Badge<ConnectionToWindowServer>, InputPreemptor preemptor)
|
||||
{
|
||||
if (on_input_preemption)
|
||||
on_input_preemption(preemptor);
|
||||
}
|
||||
|
||||
Action* Window::action_for_shortcut(Shortcut const& shortcut)
|
||||
{
|
||||
Action* found_action = nullptr;
|
||||
|
@ -91,6 +91,7 @@ public:
|
||||
Function<CloseRequestDecision()> on_close_request;
|
||||
Function<void(bool is_active_input)> on_active_input_change;
|
||||
Function<void(bool is_active_window)> on_active_window_change;
|
||||
Function<void(InputPreemptor)> on_input_preemption;
|
||||
|
||||
int x() const { return rect().x(); }
|
||||
int y() const { return rect().y(); }
|
||||
@ -197,6 +198,7 @@ public:
|
||||
static void for_each_window(Badge<ConnectionToWindowServer>, Function<void(Window&)>);
|
||||
static void update_all_windows(Badge<ConnectionToWindowServer>);
|
||||
void notify_state_changed(Badge<ConnectionToWindowServer>, bool minimized, bool maximized, bool occluded);
|
||||
void notify_input_preempted(Badge<ConnectionToWindowServer>, InputPreemptor);
|
||||
|
||||
virtual bool is_visible_for_timer_purposes() const override { return m_visible_for_timer_purposes; }
|
||||
|
||||
|
@ -11,5 +11,6 @@
|
||||
namespace GUI {
|
||||
|
||||
using WindowMode = WindowServer::WindowMode;
|
||||
using InputPreemptor = WindowServer::InputPreemptor;
|
||||
|
||||
}
|
||||
|
@ -331,17 +331,14 @@ void MenuManager::open_menu(Menu& menu, bool as_current_menu)
|
||||
|
||||
void MenuManager::clear_current_menu()
|
||||
{
|
||||
Menu* previous_current_menu = m_current_menu;
|
||||
m_current_menu = nullptr;
|
||||
if (previous_current_menu) {
|
||||
// When closing the last menu, restore the previous active input window
|
||||
if (m_current_menu) {
|
||||
auto& wm = WindowManager::the();
|
||||
wm.restore_active_input_window(m_previous_input_window);
|
||||
if (auto* window = wm.window_with_active_menu()) {
|
||||
window->invalidate_menubar();
|
||||
}
|
||||
wm.set_window_with_active_menu(nullptr);
|
||||
}
|
||||
m_current_menu = nullptr;
|
||||
}
|
||||
|
||||
void MenuManager::set_current_menu(Menu* menu)
|
||||
@ -356,19 +353,17 @@ void MenuManager::set_current_menu(Menu* menu)
|
||||
return;
|
||||
}
|
||||
|
||||
Menu* previous_current_menu = m_current_menu;
|
||||
m_current_menu = menu;
|
||||
|
||||
auto& wm = WindowManager::the();
|
||||
if (!previous_current_menu) {
|
||||
// When opening the first menu, store the current active input window
|
||||
if (auto* active_input = wm.active_input_window())
|
||||
m_previous_input_window = *active_input;
|
||||
else
|
||||
m_previous_input_window = nullptr;
|
||||
if (auto* window = wm.active_input_window()) {
|
||||
InputPreemptor preemptor { InputPreemptor::OtherMenu };
|
||||
if (window->rect().contains(m_current_menu->unadjusted_position()))
|
||||
preemptor = InputPreemptor::ContextMenu;
|
||||
else if (!m_current_menu->rect_in_window_menubar().is_null())
|
||||
preemptor = InputPreemptor::MenubarMenu;
|
||||
wm.notify_input_preempted(*window, preemptor);
|
||||
}
|
||||
|
||||
wm.set_active_input_window(m_current_menu->menu_window());
|
||||
}
|
||||
|
||||
Menu* MenuManager::previous_menu(Menu* current)
|
||||
|
@ -58,7 +58,6 @@ private:
|
||||
void refresh();
|
||||
|
||||
WeakPtr<Menu> m_current_menu;
|
||||
WeakPtr<Window> m_previous_input_window;
|
||||
Vector<WeakPtr<Menu>> m_open_menu_stack;
|
||||
|
||||
int m_theme_index { 0 };
|
||||
|
@ -20,6 +20,7 @@ endpoint WindowClient
|
||||
window_activated(i32 window_id) =|
|
||||
window_deactivated(i32 window_id) =|
|
||||
window_state_changed(i32 window_id, bool minimized, bool maximized, bool occluded) =|
|
||||
window_input_preempted(i32 window_id, i32 preemptor) =|
|
||||
window_close_request(i32 window_id) =|
|
||||
window_resized(i32 window_id, Gfx::IntRect new_rect) =|
|
||||
|
||||
|
@ -642,6 +642,12 @@ void WindowManager::notify_progress_changed(Window& window)
|
||||
tell_wms_window_state_changed(window);
|
||||
}
|
||||
|
||||
void WindowManager::notify_input_preempted(Window& window, InputPreemptor preemptor)
|
||||
{
|
||||
if (window.client())
|
||||
window.client()->async_window_input_preempted(window.window_id(), (i32)preemptor);
|
||||
}
|
||||
|
||||
void WindowManager::pick_new_active_window(Window* previous_active)
|
||||
{
|
||||
Window* desktop = nullptr;
|
||||
|
@ -87,6 +87,7 @@ public:
|
||||
void notify_occlusion_state_changed(Window&);
|
||||
void notify_progress_changed(Window&);
|
||||
void notify_modified_changed(Window&);
|
||||
void notify_input_preempted(Window&, InputPreemptor = InputPreemptor::Other);
|
||||
|
||||
Gfx::IntRect tiled_window_rect(Window const&, WindowTileType tile_type = WindowTileType::Maximized, bool relative_to_window_screen = false) const;
|
||||
|
||||
|
@ -24,4 +24,13 @@ enum class WindowMode {
|
||||
_Count,
|
||||
};
|
||||
|
||||
// InputPreemptors are Objects which take input precedence over the active input
|
||||
// window without changing its activity state or joining its modal chain
|
||||
enum class InputPreemptor {
|
||||
ContextMenu = 0,
|
||||
MenubarMenu,
|
||||
OtherMenu,
|
||||
Other,
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user