1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 03:07:44 +00:00

LibGUI+LibDraw: Add "Palette" concept for scoped color theming

GApplication now has a palette. This palette contains all the system
theme colors by default, and is inherited by a new top-level GWidget.
New child widgets inherit their parents palette.

It is possible to override the GApplication palette, and the palette
of any GWidget.

The Palette object contains a bunch of colors, each corresponding to
a ColorRole. Each role has a convenience getter as well.

Each GWidget now has a background_role() and foreground_role(), which
are then looked up in their current palette when painting. This means
that you no longer alter the background color of a widget by setting
it directly, rather you alter either its background role, or the
widget's palette.
This commit is contained in:
Andreas Kling 2019-12-24 20:57:54 +01:00
parent cb4e51a7a5
commit a79bac428b
62 changed files with 448 additions and 410 deletions

View file

@ -18,14 +18,15 @@ WSButton::~WSButton()
void WSButton::paint(Painter& painter)
{
auto& palette = WSWindowManager::the().palette();
PainterStateSaver saver(painter);
painter.translate(relative_rect().location());
StylePainter::paint_button(painter, rect(), ButtonStyle::Normal, m_pressed, m_hovered);
StylePainter::paint_button(painter, rect(), palette, ButtonStyle::Normal, m_pressed, m_hovered);
auto x_location = rect().center();
x_location.move_by(-(m_bitmap->width() / 2), -(m_bitmap->height() / 2));
if (m_pressed)
x_location.move_by(1, 1);
painter.draw_bitmap(x_location, *m_bitmap, SystemColor::ButtonText);
painter.draw_bitmap(x_location, *m_bitmap, palette.button_text());
}
void WSButton::on_mouse_event(const WSMouseEvent& event)

View file

@ -113,7 +113,7 @@ void WSCompositor::compose()
if (wm.any_opaque_window_contains_rect(dirty_rect))
continue;
// FIXME: If the wallpaper is opaque, no need to fill with color!
m_back_painter->fill_rect(dirty_rect, SystemColor::DesktopBackground);
m_back_painter->fill_rect(dirty_rect, wm.palette().desktop_background());
if (m_wallpaper) {
if (m_wallpaper_mode == WallpaperMode::Simple) {
m_back_painter->blit(dirty_rect.location(), *m_wallpaper, dirty_rect);

View file

@ -126,6 +126,7 @@ WSWindow& WSMenu::ensure_menu_window()
void WSMenu::draw()
{
auto& palette = WSWindowManager::the().palette();
m_theme_index_at_last_paint = WSWindowManager::the().theme_index();
ASSERT(menu_window());
@ -133,8 +134,8 @@ void WSMenu::draw()
Painter painter(*menu_window()->backing_store());
Rect rect { {}, menu_window()->size() };
painter.fill_rect(rect.shrunken(6, 6), SystemColor::MenuBase);
StylePainter::paint_window_frame(painter, rect);
painter.fill_rect(rect.shrunken(6, 6), palette.menu_base());
StylePainter::paint_window_frame(painter, rect, palette);
int width = this->width();
if (!s_checked_bitmap)
@ -148,15 +149,15 @@ void WSMenu::draw()
}
Rect stripe_rect { frame_thickness(), frame_thickness(), s_stripe_width, height() - frame_thickness() * 2 };
painter.fill_rect(stripe_rect, SystemColor::MenuStripe);
painter.draw_line(stripe_rect.top_right(), stripe_rect.bottom_right(), Color(SystemColor::MenuStripe).darkened());
painter.fill_rect(stripe_rect, palette.menu_stripe());
painter.draw_line(stripe_rect.top_right(), stripe_rect.bottom_right(), palette.menu_stripe().darkened());
for (auto& item : m_items) {
if (item.type() == WSMenuItem::Text) {
Color text_color = SystemColor::WindowText;
Color text_color = palette.window_text();
if (&item == m_hovered_item && item.is_enabled()) {
painter.fill_rect(item.rect(), SystemColor::MenuSelection);
painter.draw_rect(item.rect(), Color(SystemColor::MenuSelection).darkened());
painter.fill_rect(item.rect(), palette.menu_selection());
painter.draw_rect(item.rect(), palette.menu_selection().darkened());
text_color = Color::White;
} else if (!item.is_enabled()) {
text_color = Color::MidGray;
@ -166,10 +167,10 @@ void WSMenu::draw()
Rect checkmark_rect { item.rect().x() + 7, 0, s_checked_bitmap_width, s_checked_bitmap_height };
checkmark_rect.center_vertically_within(text_rect);
Rect checkbox_rect = checkmark_rect.inflated(4, 4);
painter.fill_rect(checkbox_rect, SystemColor::Base);
StylePainter::paint_frame(painter, checkbox_rect, FrameShape::Container, FrameShadow::Sunken, 2);
painter.fill_rect(checkbox_rect, palette.base());
StylePainter::paint_frame(painter, checkbox_rect, palette, FrameShape::Container, FrameShadow::Sunken, 2);
if (item.is_checked()) {
painter.draw_bitmap(checkmark_rect.location(), *s_checked_bitmap, SystemColor::ButtonText);
painter.draw_bitmap(checkmark_rect.location(), *s_checked_bitmap, palette.button_text());
}
} else if (item.icon()) {
Rect icon_rect { item.rect().x() + 3, 0, s_item_icon_width, s_item_icon_width };
@ -189,13 +190,13 @@ void WSMenu::draw()
s_submenu_arrow_bitmap_height
};
submenu_arrow_rect.center_vertically_within(item.rect());
painter.draw_bitmap(submenu_arrow_rect.location(), submenu_arrow_bitmap, SystemColor::WindowText);
painter.draw_bitmap(submenu_arrow_rect.location(), submenu_arrow_bitmap, palette.window_text());
}
} else if (item.type() == WSMenuItem::Separator) {
Point p1(item.rect().translated(stripe_rect.width() + 4, 0).x(), item.rect().center().y() - 1);
Point p2(width - 7, item.rect().center().y() - 1);
painter.draw_line(p1, p2, SystemColor::ThreedShadow1);
painter.draw_line(p1.translated(0, 1), p2.translated(0, 1), SystemColor::ThreedHighlight);
painter.draw_line(p1, p2, palette.threed_shadow1());
painter.draw_line(p1.translated(0, 1), p2.translated(0, 1), palette.threed_highlight());
}
}
}

View file

@ -44,6 +44,7 @@ bool WSMenuManager::is_open(const WSMenu& menu) const
void WSMenuManager::draw()
{
auto& wm = WSWindowManager::the();
auto& palette = wm.palette();
auto menubar_rect = this->menubar_rect();
if (m_needs_window_resize) {
@ -83,14 +84,14 @@ void WSMenuManager::draw()
Painter painter(*window().backing_store());
painter.fill_rect(menubar_rect, SystemColor::Window);
painter.draw_line({ 0, menubar_rect.bottom() }, { menubar_rect.right(), menubar_rect.bottom() }, SystemColor::ThreedShadow1);
painter.fill_rect(menubar_rect, palette.window());
painter.draw_line({ 0, menubar_rect.bottom() }, { menubar_rect.right(), menubar_rect.bottom() }, palette.threed_shadow1());
int index = 0;
wm.for_each_active_menubar_menu([&](WSMenu& menu) {
Color text_color = SystemColor::WindowText;
Color text_color = palette.window_text();
if (is_open(menu)) {
painter.fill_rect(menu.rect_in_menubar(), SystemColor::MenuSelection);
painter.draw_rect(menu.rect_in_menubar(), Color(SystemColor::MenuSelection).darkened());
painter.fill_rect(menu.rect_in_menubar(), palette.menu_selection());
painter.draw_rect(menu.rect_in_menubar(), palette.menu_selection().darkened());
text_color = Color::White;
}
painter.draw_text(
@ -103,7 +104,7 @@ void WSMenuManager::draw()
return IterationDecision::Continue;
});
painter.draw_text(m_username_rect, m_username, Font::default_bold_font(), TextAlignment::CenterRight, SystemColor::WindowText);
painter.draw_text(m_username_rect, m_username, Font::default_bold_font(), TextAlignment::CenterRight, palette.window_text());
time_t now = time(nullptr);
auto* tm = localtime(&now);
@ -115,7 +116,7 @@ void WSMenuManager::draw()
tm->tm_min,
tm->tm_sec);
painter.draw_text(m_time_rect, time_text, wm.font(), TextAlignment::CenterRight, SystemColor::WindowText);
painter.draw_text(m_time_rect, time_text, wm.font(), TextAlignment::CenterRight, palette.window_text());
for (auto& applet : m_applets) {
if (!applet)

View file

@ -153,6 +153,7 @@ void WSWindowFrame::paint(Painter& painter)
if (m_window.type() != WSWindowType::Normal)
return;
auto& palette = WSWindowManager::the().palette();
auto& window = m_window;
auto titlebar_rect = title_bar_rect();
@ -170,29 +171,29 @@ void WSWindowFrame::paint(Painter& painter)
auto& wm = WSWindowManager::the();
if (&window == wm.m_highlight_window) {
border_color = SystemColor::HighlightWindowBorder1;
border_color2 = SystemColor::HighlightWindowBorder2;
title_color = SystemColor::HighlightWindowTitle;
border_color = palette.highlight_window_border1();
border_color2 = palette.highlight_window_border2();
title_color = palette.highlight_window_title();
} else if (&window == wm.m_move_window) {
border_color = SystemColor::MovingWindowBorder1;
border_color2 = SystemColor::MovingWindowBorder2;
title_color = SystemColor::MovingWindowTitle;
border_color = palette.moving_window_border1();
border_color2 = palette.moving_window_border2();
title_color = palette.moving_window_title();
} else if (&window == wm.m_active_window) {
border_color = SystemColor::ActiveWindowBorder1;
border_color2 = SystemColor::ActiveWindowBorder2;
title_color = SystemColor::ActiveWindowTitle;
border_color = palette.active_window_border1();
border_color2 = palette.active_window_border2();
title_color = palette.active_window_title();
} else {
border_color = SystemColor::InactiveWindowBorder1;
border_color2 = SystemColor::InactiveWindowBorder2;
title_color = SystemColor::InactiveWindowTitle;
border_color = palette.inactive_window_border1();
border_color2 = palette.inactive_window_border2();
title_color = palette.inactive_window_title();
}
StylePainter::paint_window_frame(painter, outer_rect);
StylePainter::paint_window_frame(painter, outer_rect, palette);
if (!window.show_titlebar())
return;
painter.draw_line(titlebar_rect.bottom_left().translated(0, 1), titlebar_rect.bottom_right().translated(0, 1), SystemColor::Button);
painter.draw_line(titlebar_rect.bottom_left().translated(0, 1), titlebar_rect.bottom_right().translated(0, 1), palette.button());
auto leftmost_button_rect = m_buttons.is_empty() ? Rect() : m_buttons.last().relative_rect();

View file

@ -42,7 +42,8 @@ WSWindowManager& WSWindowManager::the()
return *s_the;
}
WSWindowManager::WSWindowManager()
WSWindowManager::WSWindowManager(const Palette& palette)
: m_palette(palette)
{
s_the = this;
@ -132,6 +133,7 @@ WSWindowManager::WSWindowManager()
auto new_theme = load_system_theme(theme.path);
ASSERT(new_theme);
set_system_theme(*new_theme);
m_palette = Palette::create_with_shared_buffer(*new_theme);
HashTable<WSClientConnection*> notified_clients;
for_each_window([&](WSWindow& window) {
if (window.client()) {

View file

@ -9,6 +9,7 @@
#include <LibDraw/Color.h>
#include <LibDraw/DisjointRectSet.h>
#include <LibDraw/Painter.h>
#include <LibDraw/Palette.h>
#include <LibDraw/Rect.h>
#include <WindowServer/WSCursor.h>
#include <WindowServer/WSEvent.h>
@ -50,10 +51,15 @@ class WSWindowManager : public CObject {
public:
static WSWindowManager& the();
WSWindowManager();
explicit WSWindowManager(const Palette&);
virtual ~WSWindowManager() override;
RefPtr<CConfigFile> wm_config() const { return m_wm_config; }
const Palette& palette() const { return *m_palette; }
RefPtr<CConfigFile> wm_config() const
{
return m_wm_config;
}
void reload_config(bool);
void add_window(WSWindow&);
@ -276,6 +282,8 @@ private:
WeakPtr<WSButton> m_cursor_tracking_button;
WeakPtr<WSButton> m_hovered_button;
NonnullRefPtr<Palette> m_palette;
RefPtr<CConfigFile> m_wm_config;
struct AppMetadata {

View file

@ -70,9 +70,10 @@ void WSWindowSwitcher::on_key_event(const WSKeyEvent& event)
void WSWindowSwitcher::draw()
{
auto& palette = WSWindowManager::the().palette();
Painter painter(*m_switcher_window->backing_store());
painter.fill_rect({ {}, m_rect.size() }, SystemColor::Window);
painter.draw_rect({ {}, m_rect.size() }, SystemColor::ThreedShadow2);
painter.fill_rect({ {}, m_rect.size() }, palette.window());
painter.draw_rect({ {}, m_rect.size() }, palette.threed_shadow2());
for (int index = 0; index < m_windows.size(); ++index) {
auto& window = *m_windows.at(index);
Rect item_rect {
@ -84,21 +85,21 @@ void WSWindowSwitcher::draw()
Color text_color;
Color rect_text_color;
if (index == m_selected_index) {
painter.fill_rect(item_rect, SystemColor::Selection);
text_color = SystemColor::SelectionText;
rect_text_color = SystemColor::ThreedShadow1;
painter.fill_rect(item_rect, palette.selection());
text_color = palette.selection_text();
rect_text_color = palette.threed_shadow1();
} else {
text_color = SystemColor::WindowText;
rect_text_color = SystemColor::ThreedShadow2;
text_color = palette.window_text();
rect_text_color = palette.threed_shadow2();
}
item_rect.shrink(item_padding(), 0);
Rect thumbnail_rect = { item_rect.location().translated(0, 5), { thumbnail_width(), thumbnail_height() } };
if (window.backing_store()) {
painter.draw_scaled_bitmap(thumbnail_rect, *window.backing_store(), window.backing_store()->rect());
StylePainter::paint_frame(painter, thumbnail_rect.inflated(4, 4), FrameShape::Container, FrameShadow::Sunken, 2);
StylePainter::paint_frame(painter, thumbnail_rect.inflated(4, 4), palette, FrameShape::Container, FrameShadow::Sunken, 2);
}
Rect icon_rect = { thumbnail_rect.bottom_right().translated(-window.icon().width(), -window.icon().height()), { window.icon().width(), window.icon().height() } };
painter.fill_rect(icon_rect, SystemColor::Window);
painter.fill_rect(icon_rect, palette.window());
painter.blit(icon_rect.location(), window.icon(), window.icon().rect());
painter.draw_text(item_rect.translated(thumbnail_width() + 12, 0), window.title(), WSWindowManager::the().window_title_font(), TextAlignment::CenterLeft, text_color);
painter.draw_text(item_rect, window.rect().to_string(), TextAlignment::CenterRight, rect_text_color);

View file

@ -1,4 +1,5 @@
#include <LibCore/CConfigFile.h>
#include <LibDraw/Palette.h>
#include <LibDraw/SystemTheme.h>
#include <WindowServer/WSCompositor.h>
#include <WindowServer/WSEventLoop.h>
@ -25,13 +26,14 @@ int main(int, char**)
auto theme = load_system_theme(String::format("/res/themes/%s.ini", theme_name.characters()));
ASSERT(theme);
set_system_theme(*theme);
auto palette = Palette::create_with_shared_buffer(*theme);
WSEventLoop loop;
WSScreen screen(wm_config->read_num_entry("Screen", "Width", 1024),
wm_config->read_num_entry("Screen", "Height", 768));
WSCompositor::the();
auto wm = WSWindowManager::construct();
auto wm = WSWindowManager::construct(*palette);
dbgprintf("Entering WindowServer main loop.\n");
loop.exec();