LibGfx: Add window border/title theming options
This commit adds support the following properties to theming: Flags: - IsTitleCenter: true if the title should be centered. Metrics: - BorderThickness: The border width. - BorderRadius: The border corner radius.
This commit is contained in:
parent
14b2656107
commit
8a1d77f65c
Notes:
sideshowbarker
2024-07-17 21:51:02 +09:00
Author: https://github.com/filiphsps Commit: https://github.com/SerenityOS/serenity/commit/8a1d77f65cd
18 changed files with 74 additions and 13 deletions
|
@ -74,6 +74,7 @@ TrayText=white
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=true
|
IsDark=true
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
||||||
[Metrics]
|
[Metrics]
|
||||||
TitleHeight=24
|
TitleHeight=24
|
||||||
|
|
|
@ -70,6 +70,7 @@ TrayText=white
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=false
|
IsDark=false
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
||||||
[Paths]
|
[Paths]
|
||||||
TitleButtonIcons=/res/icons/themes/Coffee/16x16/
|
TitleButtonIcons=/res/icons/themes/Coffee/16x16/
|
||||||
|
|
|
@ -66,3 +66,4 @@ TrayText=white
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=true
|
IsDark=true
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
|
@ -74,8 +74,11 @@ TrayText=#ffffff
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=false
|
IsDark=false
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
||||||
[Metrics]
|
[Metrics]
|
||||||
|
BorderThickness=4
|
||||||
|
BorderRadius=0
|
||||||
TitleHeight=19
|
TitleHeight=19
|
||||||
TitleButtonWidth=15
|
TitleButtonWidth=15
|
||||||
TitleButtonHeight=15
|
TitleButtonHeight=15
|
||||||
|
|
|
@ -74,6 +74,7 @@ TrayText=white
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=false
|
IsDark=false
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
||||||
[Metrics]
|
[Metrics]
|
||||||
TitleHeight=19
|
TitleHeight=19
|
||||||
|
|
|
@ -66,3 +66,4 @@ TrayText=white
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=false
|
IsDark=false
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
|
@ -74,6 +74,7 @@ TrayText=white
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=false
|
IsDark=false
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
||||||
[Paths]
|
[Paths]
|
||||||
MenuShadow=/res/icons/themes/Redmond/menu-shadow.png
|
MenuShadow=/res/icons/themes/Redmond/menu-shadow.png
|
||||||
|
|
|
@ -66,3 +66,4 @@ TrayText=white
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=true
|
IsDark=true
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
|
@ -74,6 +74,7 @@ TrayText=#ffffff
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=false
|
IsDark=false
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
||||||
[Metrics]
|
[Metrics]
|
||||||
TitleHeight=19
|
TitleHeight=19
|
||||||
|
|
|
@ -70,6 +70,7 @@ TrayText=white
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=false
|
IsDark=false
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
||||||
[Metrics]
|
[Metrics]
|
||||||
TitleButtonWidth=17
|
TitleButtonWidth=17
|
||||||
|
|
|
@ -70,6 +70,7 @@ TrayText=white
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=false
|
IsDark=false
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
||||||
[Metrics]
|
[Metrics]
|
||||||
TitleButtonWidth=17
|
TitleButtonWidth=17
|
||||||
|
|
|
@ -66,6 +66,7 @@ TrayText=white
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=false
|
IsDark=false
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
||||||
[Paths]
|
[Paths]
|
||||||
TitleButtonIcons=/res/icons/themes/Silver/16x16/
|
TitleButtonIcons=/res/icons/themes/Silver/16x16/
|
||||||
|
|
|
@ -70,6 +70,7 @@ TrayText=white
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
IsDark=false
|
IsDark=false
|
||||||
|
IsTitleCenter=false
|
||||||
|
|
||||||
[Paths]
|
[Paths]
|
||||||
TitleButtonIcons=/res/icons/themes/Sunshine/16x16/
|
TitleButtonIcons=/res/icons/themes/Sunshine/16x16/
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2020, Sarah Taube <metalflakecobaltpaint@gmail.com>
|
* Copyright (c) 2020, Sarah Taube <metalflakecobaltpaint@gmail.com>
|
||||||
|
* Copyright (c) 2021, Filiph Sandström <filiph.sandstrom@filfatstudios.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -286,8 +287,27 @@ void ClassicStylePainter::paint_window_frame(Painter& painter, IntRect const& re
|
||||||
Color dark_shade = palette.threed_shadow2();
|
Color dark_shade = palette.threed_shadow2();
|
||||||
Color mid_shade = palette.threed_shadow1();
|
Color mid_shade = palette.threed_shadow1();
|
||||||
Color light_shade = palette.threed_highlight();
|
Color light_shade = palette.threed_highlight();
|
||||||
|
auto border_thickness = palette.window_border_thickness();
|
||||||
|
auto border_radius = palette.window_border_radius();
|
||||||
|
|
||||||
|
if (border_radius > 0) {
|
||||||
|
// FIXME: This will draw "useless" pixels that'll get drawn over by the window contents.
|
||||||
|
// preferrably we should just remove the corner pixels from the completely drawn window
|
||||||
|
// but I don't know how to do that yet. :^)
|
||||||
|
painter.fill_rect_with_rounded_corners({ rect.x() - border_radius / 2,
|
||||||
|
rect.y() - border_radius / 2,
|
||||||
|
rect.width() + border_radius,
|
||||||
|
rect.height() + border_radius },
|
||||||
|
base_color, border_radius);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
painter.draw_rect_with_thickness({ rect.x() + border_thickness / 2,
|
||||||
|
rect.y() + border_thickness / 2,
|
||||||
|
rect.width() - border_thickness,
|
||||||
|
rect.height() - border_thickness },
|
||||||
|
base_color, border_thickness);
|
||||||
|
|
||||||
painter.draw_line(rect.top_left(), rect.top_right(), base_color);
|
|
||||||
painter.draw_line(rect.top_left().translated(0, 1), rect.bottom_left(), base_color);
|
painter.draw_line(rect.top_left().translated(0, 1), rect.bottom_left(), base_color);
|
||||||
painter.draw_line(rect.top_left().translated(1, 1), rect.top_right().translated(-1, 1), light_shade);
|
painter.draw_line(rect.top_left().translated(1, 1), rect.top_right().translated(-1, 1), light_shade);
|
||||||
painter.draw_line(rect.top_left().translated(1, 1), rect.bottom_left().translated(1, -1), light_shade);
|
painter.draw_line(rect.top_left().translated(1, 1), rect.bottom_left().translated(1, -1), light_shade);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||||
|
* Copyright (c) 2021, Filiph Sandström <filiph.sandstrom@filfatstudios.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -75,10 +76,20 @@ void ClassicWindowTheme::paint_normal_frame(Painter& painter, WindowState window
|
||||||
|
|
||||||
int stripe_right = leftmost_button_rect.left() - 3;
|
int stripe_right = leftmost_button_rect.left() - 3;
|
||||||
if (stripes_color.alpha() > 0) {
|
if (stripes_color.alpha() > 0) {
|
||||||
int stripe_left = titlebar_title_rect.right() + 5;
|
if (palette.is_title_center()) {
|
||||||
if (stripe_left && stripe_right && stripe_left < stripe_right) {
|
auto stripe_width = (leftmost_button_rect.left() / 2 - titlebar_title_rect.width() / 2) - titlebar_icon_rect.width() - 3;
|
||||||
|
|
||||||
for (int i = 2; i <= titlebar_inner_rect.height() - 2; i += 2) {
|
for (int i = 2; i <= titlebar_inner_rect.height() - 2; i += 2) {
|
||||||
painter.draw_line({ stripe_left, titlebar_inner_rect.y() + i }, { stripe_right, titlebar_inner_rect.y() + i }, stripes_color);
|
painter.draw_line({ titlebar_inner_rect.left(), titlebar_inner_rect.y() + i }, { titlebar_inner_rect.left() + stripe_width, titlebar_inner_rect.y() + i }, stripes_color);
|
||||||
|
painter.draw_line({ stripe_right - stripe_width, titlebar_inner_rect.y() + i }, { stripe_right, titlebar_inner_rect.y() + i }, stripes_color);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int stripe_left = titlebar_title_rect.right() + 5;
|
||||||
|
|
||||||
|
if (stripe_left && stripe_right && stripe_left < stripe_right) {
|
||||||
|
for (int i = 2; i <= titlebar_inner_rect.height() - 2; i += 2) {
|
||||||
|
painter.draw_line({ stripe_left, titlebar_inner_rect.y() + i }, { stripe_right, titlebar_inner_rect.y() + i }, stripes_color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,9 +97,13 @@ void ClassicWindowTheme::paint_normal_frame(Painter& painter, WindowState window
|
||||||
auto clipped_title_rect = titlebar_title_rect;
|
auto clipped_title_rect = titlebar_title_rect;
|
||||||
clipped_title_rect.set_width(stripe_right - clipped_title_rect.x());
|
clipped_title_rect.set_width(stripe_right - clipped_title_rect.x());
|
||||||
if (!clipped_title_rect.is_empty()) {
|
if (!clipped_title_rect.is_empty()) {
|
||||||
painter.draw_text(clipped_title_rect.translated(1, 2), window_title, title_font, Gfx::TextAlignment::CenterLeft, shadow_color, Gfx::TextElision::Right);
|
auto align = Gfx::TextAlignment::CenterLeft;
|
||||||
|
if (palette.is_title_center())
|
||||||
|
align = Gfx::TextAlignment::Center;
|
||||||
|
|
||||||
|
painter.draw_text(clipped_title_rect.translated(1, 2), window_title, title_font, align, shadow_color, Gfx::TextElision::Right);
|
||||||
// FIXME: The translated(0, 1) wouldn't be necessary if we could center text based on its baseline.
|
// 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, Gfx::TextAlignment::CenterLeft, title_color, Gfx::TextElision::Right);
|
painter.draw_text(clipped_title_rect.translated(0, 1), window_title, title_font, align, title_color, Gfx::TextElision::Right);
|
||||||
}
|
}
|
||||||
|
|
||||||
painter.draw_scaled_bitmap(titlebar_icon_rect, icon, icon.rect());
|
painter.draw_scaled_bitmap(titlebar_icon_rect, icon, icon.rect());
|
||||||
|
@ -129,7 +144,7 @@ IntRect ClassicWindowTheme::menubar_rect(WindowType window_type, const IntRect&
|
||||||
{
|
{
|
||||||
if (window_type != WindowType::Normal)
|
if (window_type != WindowType::Normal)
|
||||||
return {};
|
return {};
|
||||||
return { 4, 3 + titlebar_height(window_type, palette) + 2, window_rect.width(), menubar_height * menu_row_count };
|
return { palette.window_border_thickness(), palette.window_border_thickness() - 1 + titlebar_height(window_type, palette) + 2, window_rect.width(), menubar_height * menu_row_count };
|
||||||
}
|
}
|
||||||
|
|
||||||
IntRect ClassicWindowTheme::titlebar_rect(WindowType window_type, const IntRect& window_rect, const Palette& palette) const
|
IntRect ClassicWindowTheme::titlebar_rect(WindowType window_type, const IntRect& window_rect, const Palette& palette) const
|
||||||
|
@ -141,7 +156,7 @@ IntRect ClassicWindowTheme::titlebar_rect(WindowType window_type, const IntRect&
|
||||||
|
|
||||||
if (window_type == WindowType::Notification)
|
if (window_type == WindowType::Notification)
|
||||||
return { window_rect.width() + 3, total_vertical_padding / 2 - 1, window_titlebar_height, window_rect.height() };
|
return { window_rect.width() + 3, total_vertical_padding / 2 - 1, window_titlebar_height, window_rect.height() };
|
||||||
return { 4, 4, window_rect.width(), window_titlebar_height };
|
return { palette.window_border_thickness(), palette.window_border_thickness(), window_rect.width(), window_titlebar_height };
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassicWindowTheme::FrameColors ClassicWindowTheme::compute_frame_colors(WindowState state, const Palette& palette) const
|
ClassicWindowTheme::FrameColors ClassicWindowTheme::compute_frame_colors(WindowState state, const Palette& palette) const
|
||||||
|
@ -183,15 +198,16 @@ void ClassicWindowTheme::paint_notification_frame(Painter& painter, const IntRec
|
||||||
IntRect ClassicWindowTheme::frame_rect_for_window(WindowType window_type, const IntRect& window_rect, const Gfx::Palette& palette, int menu_row_count) const
|
IntRect ClassicWindowTheme::frame_rect_for_window(WindowType window_type, const IntRect& window_rect, const Gfx::Palette& palette, int menu_row_count) const
|
||||||
{
|
{
|
||||||
auto window_titlebar_height = titlebar_height(window_type, palette);
|
auto window_titlebar_height = titlebar_height(window_type, palette);
|
||||||
|
auto border_thickness = palette.window_border_thickness();
|
||||||
|
|
||||||
switch (window_type) {
|
switch (window_type) {
|
||||||
case WindowType::Normal:
|
case WindowType::Normal:
|
||||||
case WindowType::ToolWindow:
|
case WindowType::ToolWindow:
|
||||||
return {
|
return {
|
||||||
window_rect.x() - 4,
|
window_rect.x() - border_thickness,
|
||||||
window_rect.y() - window_titlebar_height - 5 - menu_row_count * menubar_height,
|
window_rect.y() - window_titlebar_height - border_thickness - 1 - menu_row_count * menubar_height,
|
||||||
window_rect.width() + 8,
|
window_rect.width() + (border_thickness * 2),
|
||||||
window_rect.height() + 9 + window_titlebar_height + menu_row_count * menubar_height
|
window_rect.height() + (border_thickness * 2) + 1 + window_titlebar_height + menu_row_count * menubar_height
|
||||||
};
|
};
|
||||||
case WindowType::Notification:
|
case WindowType::Notification:
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -126,7 +126,10 @@ public:
|
||||||
Color syntax_preprocessor_value() const { return color(ColorRole::SyntaxPreprocessorValue); }
|
Color syntax_preprocessor_value() const { return color(ColorRole::SyntaxPreprocessorValue); }
|
||||||
|
|
||||||
bool is_dark() const { return flag(FlagRole::IsDark); }
|
bool is_dark() const { return flag(FlagRole::IsDark); }
|
||||||
|
bool is_title_center() const { return flag(FlagRole::IsTitleCenter); }
|
||||||
|
|
||||||
|
int window_border_thickness() const { return metric(MetricRole::BorderThickness); }
|
||||||
|
int window_border_radius() const { return metric(MetricRole::BorderRadius); }
|
||||||
int window_title_height() const { return metric(MetricRole::TitleHeight); }
|
int window_title_height() const { return metric(MetricRole::TitleHeight); }
|
||||||
int window_title_button_width() const { return metric(MetricRole::TitleButtonWidth); }
|
int window_title_button_width() const { return metric(MetricRole::TitleButtonWidth); }
|
||||||
int window_title_button_height() const { return metric(MetricRole::TitleButtonHeight); }
|
int window_title_button_height() const { return metric(MetricRole::TitleButtonHeight); }
|
||||||
|
|
|
@ -49,6 +49,10 @@ Core::AnonymousBuffer load_system_theme(Core::ConfigFile const& file)
|
||||||
int metric = file.read_num_entry("Metrics", name, -1);
|
int metric = file.read_num_entry("Metrics", name, -1);
|
||||||
if (metric == -1) {
|
if (metric == -1) {
|
||||||
switch (role) {
|
switch (role) {
|
||||||
|
case (int)MetricRole::BorderThickness:
|
||||||
|
return 4;
|
||||||
|
case (int)MetricRole::BorderRadius:
|
||||||
|
return 0;
|
||||||
case (int)MetricRole::TitleHeight:
|
case (int)MetricRole::TitleHeight:
|
||||||
return 19;
|
return 19;
|
||||||
case (int)MetricRole::TitleButtonHeight:
|
case (int)MetricRole::TitleButtonHeight:
|
||||||
|
|
|
@ -91,9 +91,12 @@ namespace Gfx {
|
||||||
C(WindowText)
|
C(WindowText)
|
||||||
|
|
||||||
#define ENUMERATE_FLAG_ROLES(C) \
|
#define ENUMERATE_FLAG_ROLES(C) \
|
||||||
C(IsDark)
|
C(IsDark) \
|
||||||
|
C(IsTitleCenter)
|
||||||
|
|
||||||
#define ENUMERATE_METRIC_ROLES(C) \
|
#define ENUMERATE_METRIC_ROLES(C) \
|
||||||
|
C(BorderThickness) \
|
||||||
|
C(BorderRadius) \
|
||||||
C(TitleHeight) \
|
C(TitleHeight) \
|
||||||
C(TitleButtonWidth) \
|
C(TitleButtonWidth) \
|
||||||
C(TitleButtonHeight)
|
C(TitleButtonHeight)
|
||||||
|
|
Loading…
Add table
Reference in a new issue