1/*
2 * Copyright (C) 2020-2022 The opuntiaOS Project Authors.
3 * + Contributed by Nikita Melekhin <nimelehin@gmail.com>
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "WindowFrame.h"
10#include "../../Components/Elements/Button.h"
11#include "../../Managers/WindowManager.h"
12#include "Window.h"
13#include <libg/Font.h>
14#include <libg/ImageLoaders/PNGLoader.h>
15#include <libg/Rect.h>
16#include <utility>
17
18#define CONTROL_PANEL_CLOSE 0x0
19#define CONTROL_PANEL_MAXIMIZE 0x1
20#define CONTROL_PANEL_MINIMIZE 0x2
21
22namespace WinServer::Desktop {
23
24static const uint32_t s_close_button_glyph_data[10] = {
25 0b1100000011,
26 0b1110000111,
27 0b0111001110,
28 0b0011111100,
29 0b0001111000,
30 0b0001111000,
31 0b0011111100,
32 0b0111001110,
33 0b1110000111,
34 0b1100000011,
35};
36
37static const uint32_t s_maximise_button_glyph_data[10] = {
38 0b1111100000,
39 0b1111000000,
40 0b1110000000,
41 0b1100000000,
42 0b1000000000,
43 0b0000000001,
44 0b0000000011,
45 0b0000000111,
46 0b0000001111,
47 0b0000011111
48};
49
50static const uint32_t s_minimise_button_glyph_data[10] = {
51 0b0000000000,
52 0b0000000000,
53 0b0000000000,
54 0b0000000000,
55 0b0000000000,
56 0b0000000000,
57 0b0000000000,
58 0b0000000000,
59 0b1111111111,
60 0b1111111111
61};
62
63WindowFrame::WindowFrame(Window& window)
64 : m_window(window)
65 , m_window_control_buttons()
66 , m_control_panel_buttons()
67{
68 set_style(StatusBarStyle::StandardLight);
69
70 auto* close = new Button();
71 close->set_icon(LG::GlyphBitmap(s_close_button_glyph_data, 10, 10));
72 auto* maximize = new Button();
73 maximize->set_icon(LG::GlyphBitmap(s_maximise_button_glyph_data, 10, 10));
74 auto* minimize = new Button();
75 minimize->set_icon(LG::GlyphBitmap(s_minimise_button_glyph_data, 10, 10));
76
77 close->set_title_color(LG::Color(196, 128, 128));
78
79 m_window_control_buttons.push_back(close);
80 m_window_control_buttons.push_back(maximize);
81 m_window_control_buttons.push_back(minimize);
82}
83
84WindowFrame::WindowFrame(Window& window, std::vector<Button*>&& control_panel_buttons, std::vector<Button*>&& window_control_buttons)
85 : m_window(window)
86 , m_window_control_buttons(std::move(window_control_buttons))
87 , m_control_panel_buttons(std::move(control_panel_buttons))
88{
89}
90
91void WindowFrame::on_set_app_title()
92{
93 m_app_title_width = Helpers::text_width(m_window.app_title(), LG::Font::system_font());
94 if (style().show_text()) {
95 WinServer::Compositor::the().invalidate(bounds());
96 }
97}
98
99void WindowFrame::add_control(const std::string& title)
100{
101 auto* new_control = new Button();
102 new_control->set_title(title);
103 m_control_panel_buttons.push_back(new_control);
104 WinServer::Compositor::the().invalidate(bounds());
105}
106
107void WindowFrame::draw(LG::Context& ctx)
108{
109 if (!visible()) {
110 return;
111 }
112 int x = m_window.bounds().min_x();
113 int y = m_window.bounds().min_y();
114 size_t width = m_window.bounds().width();
115 size_t height = m_window.bounds().height();
116
117 int left_x = x + left_border_size();
118 int right_x = x + width - right_border_size();
119 int bottom_y = y + height - bottom_border_size();
120
121 // Drawing frame and shadings
122 int shadow_spread = LG::Shading::SystemSpread;
123 ctx.set_fill_color(style().color());
124 ctx.fill_rounded(LG::Rect(x + left_border_size(), y + std_top_border_frame_size(), width - 2 * left_border_size(), top_border_size() - std_top_border_frame_size()), LG::CornerMask(4, true, false));
125 ctx.set_fill_color(Color::Shadow);
126 const auto shading_rect = LG::Rect(x + left_border_size(), y + std_top_border_frame_size(), width - 2 * left_border_size(), height - std_top_border_frame_size() - std_bottom_border_size());
127 ctx.draw_box_shading(shading_rect, LG::Shading(LG::Shading::Type::Box, 0, shadow_spread), LG::CornerMask(LG::CornerMask::SystemRadius));
128
129 // Drawing labels, icons.
130 // Drawing positions are calculated using a start of the frame.
131 ctx.set_fill_color(m_text_colors[(int)active()]);
132 if (style().show_text()) {
133 Helpers::draw_text(ctx, { left_x + spacing(), y + text_y_offset() }, m_window.app_title(), LG::Font::system_font());
134 }
135
136 int start_controls_offset = m_app_title_width + 2 * spacing();
137 int start_controls = left_x + start_controls_offset;
138 for (int i = 0; i < m_control_panel_buttons.size(); i++) {
139 m_control_panel_buttons[i]->display(ctx, { start_controls, y + text_y_offset() });
140 start_controls += spacing() + m_control_panel_buttons[i]->bounds().width();
141 }
142
143 int start_buttons = right_x - spacing() - m_window_control_buttons[0]->bounds().width();
144 for (int i = 0; i < m_window_control_buttons.size(); i++) {
145 if (active() && i == 0) {
146 ctx.set_fill_color(m_window_control_buttons[i]->title_color());
147 } else {
148 ctx.set_fill_color(m_text_colors[(int)active()]);
149 }
150
151 m_window_control_buttons[i]->display(ctx, { start_buttons, y + button_y_offset() });
152 start_buttons += -spacing() - m_window_control_buttons[i]->bounds().width();
153 }
154}
155
156void WindowFrame::invalidate(WinServer::Compositor& compositor) const
157{
158 if (!visible()) {
159 return;
160 }
161 int x = m_window.bounds().min_x();
162 int y = m_window.bounds().min_y();
163 size_t width = m_window.bounds().width();
164 size_t height = m_window.bounds().height();
165 int right_x = x + width - right_border_size();
166 int bottom_y = y + height - bottom_border_size();
167 compositor.invalidate(LG::Rect(x, y, width, top_border_size()));
168 compositor.invalidate(LG::Rect(x, y, left_border_size(), height));
169 compositor.invalidate(LG::Rect(right_x, y, right_border_size(), height));
170 compositor.invalidate(LG::Rect(x, bottom_y, width, bottom_border_size()));
171}
172
173void WindowFrame::receive_tap_event(const LG::Point<int>& tap)
174{
175 // Calculating buttons' positions
176 size_t width = m_window.bounds().width();
177 int right_x = width - right_border_size();
178 int start_buttons = right_x - spacing() - m_window_control_buttons[0]->bounds().width();
179 for (int button_id = 0; button_id < m_window_control_buttons.size(); button_id++) {
180 auto button_frame = m_window_control_buttons[button_id]->bounds();
181 button_frame.offset_by(start_buttons, button_y_offset());
182 if (button_frame.contains(tap)) {
183 handle_control_panel_tap(button_id);
184 return;
185 }
186 start_buttons += -spacing() - button_frame.width();
187 }
188}
189
190const LG::Rect WindowFrame::bounds() const
191{
192 const auto& bounds = m_window.bounds();
193 return LG::Rect(bounds.min_x(), bounds.min_y(), bounds.width(), top_border_size());
194}
195
196void WindowFrame::handle_control_panel_tap(int button_id)
197{
198 auto& wm = WindowManager::the();
199 switch (button_id) {
200 case CONTROL_PANEL_CLOSE:
201 wm.close_window(m_window);
202 break;
203 case CONTROL_PANEL_MAXIMIZE:
204 wm.maximize_window(m_window);
205 break;
206 case CONTROL_PANEL_MINIMIZE:
207 wm.minimize_window(m_window);
208 break;
209 default:
210 break;
211 }
212}
213
214void WindowFrame::set_style(StatusBarStyle ts)
215{
216 m_style = ts;
217
218 if (m_style.dark_text()) {
219 m_text_colors[0] = Color::InactiveText;
220 m_text_colors[1] = LG::Color::DarkSystemText;
221 } else {
222 m_text_colors[0] = Color::InactiveText;
223 m_text_colors[1] = LG::Color::LightSystemText;
224 }
225}
226
227void WindowFrame::on_set_icon()
228{
229}
230
231} // namespace WinServer