Serenity Operating System
1/*
2 * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include "WindowStack.h"
8#include "WindowManager.h"
9
10namespace WindowServer {
11
12WindowStack::WindowStack(unsigned row, unsigned column)
13 : m_row(row)
14 , m_column(column)
15{
16}
17
18void WindowStack::add(Window& window)
19{
20 VERIFY(!window.is_on_any_window_stack({}));
21 m_windows.append(window);
22 window.set_window_stack({}, this);
23
24 move_always_on_top_windows_to_front();
25}
26
27void WindowStack::add_to_back(Window& window)
28{
29 VERIFY(!window.is_on_any_window_stack({}));
30 m_windows.prepend(window);
31 window.set_window_stack({}, this);
32}
33
34void WindowStack::remove(Window& window)
35{
36 VERIFY(&window.window_stack() == this);
37 m_windows.remove(window);
38 window.set_window_stack({}, nullptr);
39 if (m_active_window == &window)
40 m_active_window = nullptr;
41}
42
43void WindowStack::move_to_front(Window& window)
44{
45 if (m_windows.last() != &window)
46 window.invalidate();
47
48 m_windows.remove(window);
49 m_windows.append(window);
50
51 move_always_on_top_windows_to_front();
52
53 if (window.is_always_on_top()) {
54 m_windows.remove(window);
55 m_windows.append(window);
56 window.invalidate();
57 }
58}
59
60void WindowStack::move_always_on_top_windows_to_front()
61{
62 Window::List always_on_top_list;
63 for (auto iterator = m_windows.begin(); iterator != m_windows.end(); ++iterator) {
64 auto& window = *iterator;
65 if (window.is_always_on_top()) {
66 m_windows.remove(window);
67 always_on_top_list.append(window);
68 iterator = m_windows.begin();
69 }
70 }
71
72 while (!always_on_top_list.is_empty()) {
73 auto& window = *always_on_top_list.begin();
74 always_on_top_list.remove(window);
75 m_windows.append(window);
76 window.invalidate();
77 }
78}
79
80void WindowStack::move_all_windows(WindowStack& new_window_stack, Vector<Window*, 32>& windows_moved, MoveAllWindowsTo move_to)
81{
82 VERIFY(this != &new_window_stack);
83
84 move_always_on_top_windows_to_front();
85
86 if (move_to == MoveAllWindowsTo::Front) {
87 while (auto* window = m_windows.take_first()) {
88 window->set_window_stack({}, nullptr);
89 new_window_stack.add(*window);
90 windows_moved.append(window);
91 }
92 } else {
93 while (auto* window = m_windows.take_last()) {
94 window->set_window_stack({}, nullptr);
95 new_window_stack.add_to_back(*window);
96 windows_moved.append(window);
97 }
98 }
99 m_active_window = nullptr;
100}
101
102Window* WindowStack::window_at(Gfx::IntPoint position, IncludeWindowFrame include_window_frame) const
103{
104 auto result = hit_test(position);
105 if (!result.has_value())
106 return nullptr;
107 if (include_window_frame == IncludeWindowFrame::No && result->is_frame_hit)
108 return nullptr;
109 return result->window;
110}
111
112Window* WindowStack::highlight_window() const
113{
114 if (auto* window = WindowManager::the().highlight_window(); window && &window->window_stack() == this)
115 return window;
116 return nullptr;
117}
118
119void WindowStack::set_active_window(Window* window)
120{
121 if (!window)
122 m_active_window = nullptr;
123 else
124 m_active_window = window->make_weak_ptr<Window>();
125}
126
127void WindowStack::set_all_occluded(bool occluded)
128{
129 for (auto& window : m_windows) {
130 if (!WindowManager::is_stationary_window_type(window.type()))
131 window.set_occluded(occluded);
132 }
133}
134
135Optional<HitTestResult> WindowStack::hit_test(Gfx::IntPoint position) const
136{
137 Optional<HitTestResult> result;
138 WindowManager::the().for_each_visible_window_from_front_to_back([&](Window& window) {
139 result = window.hit_test(position);
140 if (result.has_value())
141 return IterationDecision::Break;
142 return IterationDecision::Continue;
143 },
144 const_cast<WindowStack*>(this));
145 return result;
146}
147
148}