mirror of
https://github.com/SerenityOS/serenity.git
synced 2026-11-07 04:29:30 -10:00
Previously, the title bar text could be painted under the buttons for small window sizes (or long titles).
172 lines
7.9 KiB
C++
172 lines
7.9 KiB
C++
/*
|
|
* 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);
|
|
}
|
|
|
|
}
|