mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 05:57:45 +00:00
WindowServer: Add basic virtual desktop support
This creates a 2-dimensional array of WindowStack instances, one for each virtual desktop. The main desktop 0,0 is the main desktop, which is the desktop used for all stationary windows (e.g. taskbar, desktop). When adding windows to a desktop, stationary windows are always added to the main desktop. When composing the desktop, there are usually two WindowStacks involved. For stationary windows, the main desktop will be traversed, and for everything else the current virtual desktop will be iterated. Iteration is interweaved to preserve the correct order. During the transition animation, two WindowStacks will be iterated at the same time.
This commit is contained in:
parent
944e5cfb35
commit
584b144953
11 changed files with 900 additions and 213 deletions
|
@ -90,17 +90,36 @@ public:
|
|||
void start_dnd_drag(ClientConnection&, String const& text, Gfx::Bitmap const*, Core::MimeData const&);
|
||||
void end_dnd_drag();
|
||||
|
||||
Window* active_window() { return m_window_stack.active_window(); }
|
||||
Window const* active_window() const { return m_window_stack.active_window(); }
|
||||
Window* active_input_window() { return m_active_input_window.ptr(); }
|
||||
Window const* active_input_window() const { return m_active_input_window.ptr(); }
|
||||
Window* active_window()
|
||||
{
|
||||
VERIFY(m_current_window_stack);
|
||||
return m_current_window_stack->active_window();
|
||||
}
|
||||
Window const* active_window() const
|
||||
{
|
||||
VERIFY(m_current_window_stack);
|
||||
return m_current_window_stack->active_window();
|
||||
}
|
||||
|
||||
Window* active_input_window()
|
||||
{
|
||||
VERIFY(m_current_window_stack);
|
||||
return m_current_window_stack->active_input_window();
|
||||
}
|
||||
Window const* active_input_window() const
|
||||
{
|
||||
VERIFY(m_current_window_stack);
|
||||
return m_current_window_stack->active_input_window();
|
||||
}
|
||||
|
||||
ClientConnection const* active_client() const;
|
||||
|
||||
Window* window_with_active_menu() { return m_window_with_active_menu; }
|
||||
Window const* window_with_active_menu() const { return m_window_with_active_menu; }
|
||||
void set_window_with_active_menu(Window*);
|
||||
|
||||
Window const* highlight_window() const { return m_window_stack.highlight_window(); }
|
||||
Window* highlight_window() { return m_highlight_window; }
|
||||
Window const* highlight_window() const { return m_highlight_window; }
|
||||
void set_highlight_window(Window*);
|
||||
|
||||
void move_to_front_and_make_active(Window&);
|
||||
|
@ -223,6 +242,7 @@ public:
|
|||
return f(window, true);
|
||||
}
|
||||
}
|
||||
bool is_window_in_modal_stack(Window& window_in_modal_stack, Window& other_window);
|
||||
|
||||
Gfx::IntPoint get_recommended_window_position(Gfx::IntPoint const& desired);
|
||||
|
||||
|
@ -231,13 +251,61 @@ public:
|
|||
void reevaluate_hovered_window(Window* = nullptr);
|
||||
Window* hovered_window() const { return m_hovered_window.ptr(); }
|
||||
|
||||
WindowStack& window_stack() { return m_window_stack; }
|
||||
void switch_to_window_stack(WindowStack&, Window* = nullptr);
|
||||
|
||||
size_t window_stack_rows() const { return m_window_stacks.size(); }
|
||||
size_t window_stack_columns() const { return m_window_stacks[0].size(); }
|
||||
|
||||
WindowStack& current_window_stack()
|
||||
{
|
||||
VERIFY(m_current_window_stack);
|
||||
return *m_current_window_stack;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
IterationDecision for_each_window_stack(F f)
|
||||
{
|
||||
for (auto& row : m_window_stacks) {
|
||||
for (auto& stack : row) {
|
||||
IterationDecision decision = f(stack);
|
||||
if (decision != IterationDecision::Continue)
|
||||
return decision;
|
||||
}
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
WindowStack& window_stack_for_window(Window&);
|
||||
|
||||
static constexpr bool is_stationary_window_type(WindowType window_type)
|
||||
{
|
||||
switch (window_type) {
|
||||
case WindowType::Normal:
|
||||
case WindowType::ToolWindow:
|
||||
case WindowType::Tooltip:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void did_switch_window_stack(Badge<Compositor>, WindowStack&, WindowStack&);
|
||||
|
||||
template<typename Callback>
|
||||
IterationDecision for_each_visible_window_from_back_to_front(Callback, WindowStack* = nullptr);
|
||||
template<typename Callback>
|
||||
IterationDecision for_each_visible_window_from_front_to_back(Callback, WindowStack* = nullptr);
|
||||
|
||||
MultiScaleBitmaps const* overlay_rect_shadow() const { return m_overlay_rect_shadow.ptr(); }
|
||||
|
||||
private:
|
||||
RefPtr<Cursor> get_cursor(String const& name);
|
||||
|
||||
void notify_new_active_window(Window&);
|
||||
void notify_new_active_input_window(Window&);
|
||||
void notify_previous_active_window(Window&);
|
||||
void notify_previous_active_input_window(Window&);
|
||||
|
||||
void process_mouse_event(MouseEvent&);
|
||||
void process_event_for_doubleclick(Window& window, MouseEvent& event);
|
||||
bool process_ongoing_window_resize(MouseEvent const&);
|
||||
|
@ -260,6 +328,8 @@ private:
|
|||
|
||||
void do_move_to_front(Window&, bool, bool);
|
||||
|
||||
[[nodiscard]] static WindowStack& get_rendering_window_stacks(WindowStack*&);
|
||||
|
||||
RefPtr<Cursor> m_hidden_cursor;
|
||||
RefPtr<Cursor> m_arrow_cursor;
|
||||
RefPtr<Cursor> m_hand_cursor;
|
||||
|
@ -279,7 +349,9 @@ private:
|
|||
|
||||
RefPtr<MultiScaleBitmaps> m_overlay_rect_shadow;
|
||||
|
||||
WindowStack m_window_stack;
|
||||
// Setup 2 rows 1 column by default
|
||||
NonnullOwnPtrVector<NonnullOwnPtrVector<WindowStack, 3>, 2> m_window_stacks;
|
||||
WindowStack* m_current_window_stack { nullptr };
|
||||
|
||||
struct DoubleClickInfo {
|
||||
struct ClickMetadata {
|
||||
|
@ -317,8 +389,7 @@ private:
|
|||
bool m_previous_event_was_super_keydown { false };
|
||||
|
||||
WeakPtr<Window> m_hovered_window;
|
||||
WeakPtr<Window> m_active_input_window;
|
||||
WeakPtr<Window> m_active_input_tracking_window;
|
||||
WeakPtr<Window> m_highlight_window;
|
||||
WeakPtr<Window> m_window_with_active_menu;
|
||||
|
||||
OwnPtr<WindowGeometryOverlay> m_geometry_overlay;
|
||||
|
@ -349,8 +420,85 @@ private:
|
|||
String m_dnd_text;
|
||||
|
||||
RefPtr<Core::MimeData> m_dnd_mime_data;
|
||||
|
||||
WindowStack* m_switching_to_window_stack { nullptr };
|
||||
Vector<WeakPtr<Window>, 4> m_carry_window_to_new_stack;
|
||||
};
|
||||
|
||||
template<typename Callback>
|
||||
inline IterationDecision WindowManager::for_each_visible_window_from_back_to_front(Callback callback, WindowStack* specific_stack)
|
||||
{
|
||||
auto* window_stack = specific_stack;
|
||||
WindowStack* transitioning_to_window_stack = nullptr;
|
||||
if (!window_stack)
|
||||
window_stack = &get_rendering_window_stacks(transitioning_to_window_stack);
|
||||
auto for_each_window = [&]<WindowType window_type>() {
|
||||
if constexpr (is_stationary_window_type(window_type)) {
|
||||
auto& stationary_stack = window_stack->stationary_window_stack();
|
||||
return stationary_stack.for_each_visible_window_of_type_from_back_to_front(window_type, callback);
|
||||
} else {
|
||||
auto decision = window_stack->for_each_visible_window_of_type_from_back_to_front(window_type, callback);
|
||||
if (decision == IterationDecision::Continue && transitioning_to_window_stack)
|
||||
decision = transitioning_to_window_stack->for_each_visible_window_of_type_from_back_to_front(window_type, callback);
|
||||
return decision;
|
||||
}
|
||||
};
|
||||
if (for_each_window.template operator()<WindowType::Desktop>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::Normal>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::ToolWindow>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::Taskbar>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::AppletArea>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::Notification>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::Tooltip>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::Menu>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
return for_each_window.template operator()<WindowType::WindowSwitcher>();
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
inline IterationDecision WindowManager::for_each_visible_window_from_front_to_back(Callback callback, WindowStack* specific_stack)
|
||||
{
|
||||
auto* window_stack = specific_stack;
|
||||
WindowStack* transitioning_to_window_stack = nullptr;
|
||||
if (!window_stack)
|
||||
window_stack = &get_rendering_window_stacks(transitioning_to_window_stack);
|
||||
auto for_each_window = [&]<WindowType window_type>() {
|
||||
if constexpr (is_stationary_window_type(window_type)) {
|
||||
auto& stationary_stack = window_stack->stationary_window_stack();
|
||||
return stationary_stack.for_each_visible_window_of_type_from_front_to_back(window_type, callback);
|
||||
} else {
|
||||
auto decision = window_stack->for_each_visible_window_of_type_from_front_to_back(window_type, callback);
|
||||
if (decision == IterationDecision::Continue && transitioning_to_window_stack)
|
||||
decision = transitioning_to_window_stack->for_each_visible_window_of_type_from_front_to_back(window_type, callback);
|
||||
return decision;
|
||||
}
|
||||
};
|
||||
if (for_each_window.template operator()<WindowType::WindowSwitcher>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::Menu>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::Tooltip>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::Notification>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::AppletArea>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::Taskbar>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::ToolWindow>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
if (for_each_window.template operator()<WindowType::Normal>() == IterationDecision::Break)
|
||||
return IterationDecision::Break;
|
||||
return for_each_window.template operator()<WindowType::Desktop>();
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void WindowManager::for_each_window_manager(Callback callback)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue