-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
/
Copy pathGlassWindowTheme.cpp
171 lines (150 loc) Β· 7.92 KB
/
GlassWindowTheme.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/*
* Copyright (c) 2023, MacDue <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Array.h>
#include <LibGfx/CharacterBitmap.h>
#include <LibGfx/GlassWindowTheme.h>
#include <LibGfx/Gradients.h>
#include <LibGfx/Painter.h>
#include <LibGfx/Palette.h>
#include <LibGfx/StylePainter.h>
namespace Gfx {
// TODO: Somehow allow colors to be configured in the theme .ini file.
static Array const s_title_gradient {
ColorStop { Color(25, 40, 55, 191), 0.35f },
ColorStop { Color(65, 85, 100, 191), 0.40f },
ColorStop { Color(65, 85, 100, 191), 0.42f },
ColorStop { Color(25, 40, 55, 191), 0.50f },
ColorStop { Color(25, 40, 55, 191), 0.55f },
ColorStop { Color(70, 85, 100, 191), 0.60f },
ColorStop { Color(70, 85, 100, 191), 0.75f },
ColorStop { Color(25, 40, 55, 191), 0.90f }
};
static constexpr struct {
Color base { 235, 235, 236 };
Color border { 2, 3, 4, 219 };
} s_frame_colors;
static constexpr Gfx::CharacterBitmap s_window_border_radius_mask {
"#####"
"### "
"## "
"# "
"# "sv,
5, 5
};
static constexpr Gfx::CharacterBitmap s_window_border_radius_accent {
" "
" ##"
" # "
" # "
" # "sv,
5, 5
};
static constexpr Gfx::CharacterBitmap s_window_border_radius_accent2 {
" "
" "
" ##"
" # "
" # "sv,
5, 5
};
IntRect GlassWindowTheme::titlebar_rect(WindowType window_type, WindowMode window_mode, IntRect const& window_rect, Palette const& palette) const
{
auto window_titlebar_height = titlebar_height(window_type, window_mode, palette);
// FIXME: Theme notifications.
if (window_type == WindowType::Notification)
return ClassicWindowTheme::titlebar_rect(window_type, window_mode, window_rect, palette);
return { 0, 0, window_rect.width() + palette.window_border_thickness() * 2, window_titlebar_height };
}
void GlassWindowTheme::paint_normal_frame(Painter& painter, WindowState window_state, WindowMode window_mode, IntRect const& window_rect, StringView window_title, Bitmap const& icon, Palette const& palette, IntRect const& leftmost_button_rect, int menu_row_count, bool window_modified) const
{
// FIXME: Handle these cases.
(void)window_state;
(void)icon;
(void)window_modified;
auto frame_rect = frame_rect_for_window(WindowType::Normal, window_mode, window_rect, palette, menu_row_count);
auto relative_window_location = window_rect.location() - frame_rect.location();
frame_rect.set_location({ 0, 0 });
frame_rect.shrink(0, 1, 1, 1);
auto frame_rects = frame_rect.shatter({ relative_window_location, window_rect.size() });
for (auto const& clip_rect : frame_rects) {
// Paint Aero-style gradient.
PainterStateSaver saver { painter };
painter.add_clip_rect(clip_rect);
painter.fill_rect(frame_rect, s_frame_colors.base.with_alpha(150));
painter.fill_rect_with_linear_gradient(frame_rect, s_title_gradient, 45, 0.9f);
}
// Draw frame title.
auto titlebar_rect = this->titlebar_rect(WindowType::Normal, window_mode, window_rect, palette);
titlebar_rect.set_height(titlebar_rect.height() + palette.window_border_thickness() + 1);
auto& title_font = FontDatabase::window_title_font();
auto clipped_title_rect = titlebar_rect.translated(7, 0);
clipped_title_rect.set_width(leftmost_button_rect.left() - clipped_title_rect.x());
if (!clipped_title_rect.is_empty()) {
auto title_alignment = palette.title_alignment();
painter.draw_text(clipped_title_rect.translated(1, 2), window_title, title_font, title_alignment, Color(15, 16, 137), Gfx::TextElision::Right);
// FIXME: The translated(0, 1) wouldn't be necessary if we could center text based on its baseline.
painter.draw_text(clipped_title_rect.translated(0, 1), window_title, title_font, title_alignment, Color::White, Gfx::TextElision::Right);
}
// Draw frame border.
auto inner = frame_rect.shrunken(palette.window_title_height() + palette.window_border_thickness(), palette.window_border_thickness(), palette.window_border_thickness(), palette.window_border_thickness());
painter.draw_rect_with_thickness(frame_rect, s_frame_colors.border, 1);
painter.draw_rect_with_thickness(frame_rect.shrunken(1, 1, 1, 1), s_frame_colors.base.with_alpha(170), 1);
painter.draw_rect_with_thickness(inner.inflated(1, 1, 1, 1), s_frame_colors.base.with_alpha(110), 1);
painter.draw_rect_with_thickness(inner, s_frame_colors.border.with_alpha(110), 1);
// Paint/clip border radii.
Gfx::IntRect point(0, 0, 1, 1);
auto border_radius = s_window_border_radius_mask.width();
auto left_border_radius_pos = frame_rect.location();
auto right_border_radius_pos = frame_rect.location().translated(frame_rect.width() - border_radius, 0);
for (unsigned y = 0; y < s_window_border_radius_mask.height(); y++) {
for (unsigned x = 0; x < s_window_border_radius_mask.width(); x++) {
if (s_window_border_radius_mask.bit_at(x, y)) {
painter.clear_rect(point.translated(left_border_radius_pos).translated(x, y), Color::Transparent);
painter.clear_rect(point.translated(right_border_radius_pos).translated(border_radius - x, y), Color::Transparent);
}
if (s_window_border_radius_accent.bit_at(x, y)) {
painter.fill_rect(point.translated(left_border_radius_pos).translated(x, y), s_frame_colors.border);
painter.fill_rect(point.translated(right_border_radius_pos).translated(border_radius - x, y), s_frame_colors.border);
}
if (s_window_border_radius_accent2.bit_at(x, y)) {
painter.fill_rect(point.translated(left_border_radius_pos).translated(x, y), s_frame_colors.base.with_alpha(170));
painter.fill_rect(point.translated(right_border_radius_pos).translated(border_radius - x, y), s_frame_colors.base.with_alpha(170));
}
}
}
}
void GlassWindowTheme::paint_notification_frame(Painter& painter, WindowMode window_mode, IntRect const& window_rect, Palette const& palette, IntRect const& close_button_rect) const
{
(void)close_button_rect;
auto frame_rect = frame_rect_for_window(WindowType::Notification, window_mode, window_rect, palette, 0);
frame_rect.set_location({ 0, 0 });
frame_rect.shrink(0, 1, 1, 0);
painter.fill_rect(frame_rect, s_frame_colors.base.with_alpha(150));
painter.fill_rect_with_linear_gradient(frame_rect, s_title_gradient, 45, 0.9f);
painter.draw_rect_with_thickness(frame_rect, s_frame_colors.border, 1);
painter.draw_rect_with_thickness(frame_rect.shrunken(1, 1, 1, 1), s_frame_colors.base.with_alpha(170), 1);
}
Vector<IntRect> GlassWindowTheme::layout_buttons(WindowType window_type, WindowMode window_mode, IntRect const& window_rect, Palette const& palette, size_t buttons, bool is_maximized) const
{
auto button_rects = ClassicWindowTheme::layout_buttons(window_type, window_mode, window_rect, palette, buttons, is_maximized);
auto button_offset = [&](IntRect button_rect) -> IntPoint {
if (window_type == WindowType::Notification)
return { 1, -1 };
return { -max(palette.window_border_thickness(), s_window_border_radius_mask.width()) - 1,
-button_rect.y() + (is_maximized ? palette.window_border_thickness() : 1) + 3 };
};
for (auto& button_rect : button_rects)
button_rect.translate_by(button_offset(button_rect));
return button_rects;
}
void GlassWindowTheme::paint_taskbar(Painter& painter, IntRect const& taskbar_rect, Palette const&) const
{
painter.clear_rect(taskbar_rect, Color::Transparent);
painter.fill_rect_with_linear_gradient(taskbar_rect, s_title_gradient, 45, 0.9f);
painter.draw_line(taskbar_rect.top_left(), taskbar_rect.top_right(), s_frame_colors.border, 1);
painter.draw_line(taskbar_rect.top_left().translated(0, 1), taskbar_rect.top_right().translated(0, 1), s_frame_colors.base.with_alpha(170), 1);
}
}