From 411058b2a3c5a1058ac5c2e54ec53dba12e198e4 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 23 Dec 2019 20:24:26 +0100 Subject: [PATCH] WindowServer+LibGUI: Implement basic color theming Color themes are loaded from .ini files in /res/themes/ The theme can be switched from the "Themes" section in the system menu. The basic mechanism is that WindowServer broadcasts a SharedBuffer with all of the color values of the current theme. Clients receive this with the response to their initial WindowServer::Greet handshake. When the theme is changed, WindowServer tells everyone by sending out an UpdateSystemTheme message with a new SharedBuffer to use. This does feel somewhat bloated somehow, but I'm sure we can iterate on it over time and improve things. To get one of the theme colors, use the Color(SystemColor) constructor: painter.fill_rect(rect, SystemColor::HoverHighlight); Some things don't work 100% right without a reboot. Specifically, when constructing a GWidget, it will set its own background and foreground colors based on the current SystemColor::Window and SystemColor::Text. The widget is then stuck with these values, and they don't update on system theme change, only on app restart. All in all though, this is pretty cool. Merry Christmas! :^) --- Applications/IRCClient/IRCAppWindow.cpp | 2 +- Applications/PaintBrush/PaletteWidget.cpp | 2 +- Applications/PaintBrush/ToolboxWidget.cpp | 2 +- .../SystemMonitor/NetworkStatisticsWidget.cpp | 2 +- Applications/SystemMonitor/main.cpp | 4 +- Applications/Terminal/main.cpp | 2 +- Base/home/anon/WindowManager.ini | 23 +--- Base/res/themes/Default.ini | 32 ++++++ Base/res/themes/Xmas.ini | 33 ++++++ DevTools/HackStudio/FormWidget.cpp | 2 +- DevTools/VisualBuilder/VBForm.cpp | 2 +- Games/Minesweeper/Field.cpp | 2 +- Libraries/LibC/SharedBuffer.cpp | 11 ++ Libraries/LibC/SharedBuffer.h | 1 + Libraries/LibCore/CConfigFile.cpp | 30 ------ Libraries/LibCore/CConfigFile.h | 1 - Libraries/LibDraw/Color.cpp | 77 ++++++++++++++ Libraries/LibDraw/Color.h | 29 ++++- Libraries/LibDraw/Makefile | 1 + Libraries/LibDraw/StylePainter.cpp | 52 ++++----- Libraries/LibDraw/StylePainter.h | 2 +- Libraries/LibDraw/SystemTheme.cpp | 73 +++++++++++++ Libraries/LibDraw/SystemTheme.h | 44 ++++++++ Libraries/LibGUI/GAbstractColumnView.cpp | 8 +- Libraries/LibGUI/GCheckBox.cpp | 4 +- Libraries/LibGUI/GFilePicker.cpp | 2 +- Libraries/LibGUI/GGroupBox.cpp | 2 +- Libraries/LibGUI/GItemView.cpp | 6 +- Libraries/LibGUI/GListView.cpp | 4 +- Libraries/LibGUI/GSplitter.cpp | 4 +- Libraries/LibGUI/GTabWidget.cpp | 2 +- Libraries/LibGUI/GTableView.cpp | 6 +- Libraries/LibGUI/GToolBar.cpp | 2 +- Libraries/LibGUI/GTreeView.cpp | 4 +- Libraries/LibGUI/GWidget.cpp | 4 +- Libraries/LibGUI/GWindow.cpp | 7 ++ Libraries/LibGUI/GWindow.h | 8 +- Libraries/LibGUI/GWindowServerConnection.cpp | 15 +++ Libraries/LibGUI/GWindowServerConnection.h | 1 + Servers/WindowServer/WSClientConnection.cpp | 3 +- Servers/WindowServer/WSCompositor.cpp | 2 +- Servers/WindowServer/WSMenu.cpp | 24 +++-- Servers/WindowServer/WSMenu.h | 3 + Servers/WindowServer/WSMenuManager.cpp | 15 +-- Servers/WindowServer/WSWindowFrame.cpp | 24 ++--- Servers/WindowServer/WSWindowManager.cpp | 100 ++++++++++++------ Servers/WindowServer/WSWindowManager.h | 11 ++ Servers/WindowServer/WindowClient.ipc | 2 + Servers/WindowServer/WindowServer.ipc | 2 +- Servers/WindowServer/main.cpp | 9 +- 50 files changed, 525 insertions(+), 178 deletions(-) create mode 100644 Base/res/themes/Default.ini create mode 100644 Base/res/themes/Xmas.ini create mode 100644 Libraries/LibDraw/SystemTheme.cpp create mode 100644 Libraries/LibDraw/SystemTheme.h diff --git a/Applications/IRCClient/IRCAppWindow.cpp b/Applications/IRCClient/IRCAppWindow.cpp index a2671b50b3..3cb8c861ee 100644 --- a/Applications/IRCClient/IRCAppWindow.cpp +++ b/Applications/IRCClient/IRCAppWindow.cpp @@ -156,7 +156,7 @@ void IRCAppWindow::setup_widgets() auto widget = GWidget::construct(); set_main_widget(widget); widget->set_fill_with_background_color(true); - widget->set_background_color(Color::WarmGray); + widget->set_background_color(SystemColor::Window); widget->set_layout(make(Orientation::Vertical)); widget->layout()->set_spacing(0); diff --git a/Applications/PaintBrush/PaletteWidget.cpp b/Applications/PaintBrush/PaletteWidget.cpp index 665a394d00..bd8775cda8 100644 --- a/Applications/PaintBrush/PaletteWidget.cpp +++ b/Applications/PaintBrush/PaletteWidget.cpp @@ -51,7 +51,7 @@ PaletteWidget::PaletteWidget(PaintableWidget& paintable_widget, GWidget* parent) set_frame_shadow(FrameShadow::Raised); set_frame_thickness(0); set_fill_with_background_color(true); - set_background_color(Color::WarmGray); + set_background_color(SystemColor::Window); set_size_policy(SizePolicy::Fill, SizePolicy::Fixed); set_preferred_size(0, 34); diff --git a/Applications/PaintBrush/ToolboxWidget.cpp b/Applications/PaintBrush/ToolboxWidget.cpp index 51c9fd0b0a..80e83d6174 100644 --- a/Applications/PaintBrush/ToolboxWidget.cpp +++ b/Applications/PaintBrush/ToolboxWidget.cpp @@ -35,7 +35,7 @@ private: ToolboxWidget::ToolboxWidget(GWidget* parent) : GFrame(parent) { - set_background_color(Color::WarmGray); + set_background_color(SystemColor::Window); set_fill_with_background_color(true); set_frame_thickness(1); diff --git a/Applications/SystemMonitor/NetworkStatisticsWidget.cpp b/Applications/SystemMonitor/NetworkStatisticsWidget.cpp index e9001cd6ef..c7eb3c3932 100644 --- a/Applications/SystemMonitor/NetworkStatisticsWidget.cpp +++ b/Applications/SystemMonitor/NetworkStatisticsWidget.cpp @@ -11,7 +11,7 @@ NetworkStatisticsWidget::NetworkStatisticsWidget(GWidget* parent) set_layout(make(Orientation::Vertical)); layout()->set_margins({ 4, 4, 4, 4 }); set_fill_with_background_color(true); - set_background_color(Color::WarmGray); + set_background_color(SystemColor::Window); auto adapters_group_box = GGroupBox::construct("Adapters", this); adapters_group_box->set_layout(make(Orientation::Vertical)); diff --git a/Applications/SystemMonitor/main.cpp b/Applications/SystemMonitor/main.cpp index edc690251c..f67bde0f16 100644 --- a/Applications/SystemMonitor/main.cpp +++ b/Applications/SystemMonitor/main.cpp @@ -53,7 +53,7 @@ int main(int argc, char** argv) auto keeper = GWidget::construct(); keeper->set_layout(make(Orientation::Vertical)); keeper->set_fill_with_background_color(true); - keeper->set_background_color(Color::WarmGray); + keeper->set_background_color(SystemColor::Window); keeper->layout()->set_margins({ 4, 4, 4, 4 }); auto tabwidget = GTabWidget::construct(keeper); @@ -355,7 +355,7 @@ NonnullRefPtr build_graphs_tab() graphs_container->on_first_show = [](auto& self) { self.set_fill_with_background_color(true); - self.set_background_color(Color::WarmGray); + self.set_background_color(SystemColor::Window); self.set_layout(make(Orientation::Vertical)); self.layout()->set_margins({ 4, 4, 4, 4 }); diff --git a/Applications/Terminal/main.cpp b/Applications/Terminal/main.cpp index 17bd9dd2d0..528bc65ddf 100644 --- a/Applications/Terminal/main.cpp +++ b/Applications/Terminal/main.cpp @@ -124,7 +124,7 @@ RefPtr create_settings_window(TerminalWidget& terminal, RefPtrset_preferred_size(100, 50); auto slider = GSlider::construct(Orientation::Horizontal, slider_container); slider->set_fill_with_background_color(true); - slider->set_background_color(Color::WarmGray); + slider->set_background_color(SystemColor::Window); slider->on_value_changed = [&terminal, &config](int value) { terminal.set_opacity(value); diff --git a/Base/home/anon/WindowManager.ini b/Base/home/anon/WindowManager.ini index 4ad27fcfe9..385d063a1b 100644 --- a/Base/home/anon/WindowManager.ini +++ b/Base/home/anon/WindowManager.ini @@ -2,6 +2,9 @@ Width=1024 Height=768 +[Theme] +Name=Default + [Cursor] Arrow=/res/cursors/arrow.png ResizeH=/res/cursors/resize-horizontal.png @@ -14,26 +17,6 @@ Move=/res/cursors/move.png Hand=/res/cursors/hand.png Drag=/res/cursors/drag.png -[Colors] -Background=50,50,50 -ActiveWindowBorder=110,34,9 -ActiveWindowBorder2=244,202,158 -ActiveWindowTitle=255,255,255 - -InactiveWindowBorder=128,128,128 -InactiveWindowBorder2=192,192,192 -InactiveWindowTitle=213,208,199 - -MovingWindowBorder=161,50,13 -MovingWindowBorder2=250,220,187 -MovingWindowTitle=255,255,255 - -HighlightWindowBorder=161,13,13 -HighlightWindowBorder2=250,187,187 -HighlightWindowTitle=255,255,255 - -MenuSelectionColor=132,53,26 - [Input] DoubleClickSpeed=250 diff --git a/Base/res/themes/Default.ini b/Base/res/themes/Default.ini new file mode 100644 index 0000000000..526ec3adba --- /dev/null +++ b/Base/res/themes/Default.ini @@ -0,0 +1,32 @@ +[Colors] +DesktopBackground=#404040 + +ActiveWindowBorder1=#6e2209 +ActiveWindowBorder2=#f4ca9e +ActiveWindowTitle=white + +InactiveWindowBorder1=#808080 +InactiveWindowBorder2=#c0c0c0 +InactiveWindowTitle=#d5d0c7 + +MovingWindowBorder1=#a1320d +MovingWindowBorder2=#fadcbb +MovingWindowTitle=white + +HighlightWindowBorder1=#a10d0d +HighlightWindowBorder2=#fabbbb +HighlightWindowTitle=white + +MenuBase=white +MenuStripe=#bbb7b0 +MenuSelection=#ad714f + +Window=#d4d0c8 +Text=black +Base=white + +ThreedHighlight=white +ThreedShadow1=#808080 +ThreedShadow2=#404040 + +HoverHighlight=#e3dfdb diff --git a/Base/res/themes/Xmas.ini b/Base/res/themes/Xmas.ini new file mode 100644 index 0000000000..c11a1be78f --- /dev/null +++ b/Base/res/themes/Xmas.ini @@ -0,0 +1,33 @@ +[Colors] +DesktopBackground=#313819 + +ActiveWindowBorder1=#731013 +ActiveWindowBorder2=#ee3532 +ActiveWindowTitle=white + +InactiveWindowBorder1=#734546 +InactiveWindowBorder2=#ee908f +InactiveWindowTitle=white + +MovingWindowBorder1=#a76f24 +MovingWindowBorder2=#eec666 +MovingWindowTitle=white + +HighlightWindowBorder1=#a76f24 +HighlightWindowBorder2=#eec666 +HighlightWindowTitle=white + +MenuBase=#57691f +MenuStripe=#2b3018 +MenuSelection=#ff8742 + +Window=#d46c64 +Text=black +Base=#d3d7c4 + +ThreedHighlight=#e69e99 +ThreedShadow1=#a24841 +ThreedShadow2=#882d26 + +HoverHighlight=#e6e5e2 + diff --git a/DevTools/HackStudio/FormWidget.cpp b/DevTools/HackStudio/FormWidget.cpp index 7ae3c74096..08506beade 100644 --- a/DevTools/HackStudio/FormWidget.cpp +++ b/DevTools/HackStudio/FormWidget.cpp @@ -7,7 +7,7 @@ FormWidget::FormWidget(FormEditorWidget& parent) : GWidget(&parent) { set_fill_with_background_color(true); - set_background_color(Color::WarmGray); + set_background_color(SystemColor::Window); set_relative_rect(5, 5, 400, 300); set_greedy_for_hits(true); diff --git a/DevTools/VisualBuilder/VBForm.cpp b/DevTools/VisualBuilder/VBForm.cpp index 11a716fe05..5181414c38 100644 --- a/DevTools/VisualBuilder/VBForm.cpp +++ b/DevTools/VisualBuilder/VBForm.cpp @@ -25,7 +25,7 @@ VBForm::VBForm(const String& name, GWidget* parent) { s_current = this; set_fill_with_background_color(true); - set_background_color(Color::WarmGray); + set_background_color(SystemColor::Window); set_greedy_for_hits(true); m_context_menu = GMenu::construct(); diff --git a/Games/Minesweeper/Field.cpp b/Games/Minesweeper/Field.cpp index 10cdb9b115..70e4f2a0c2 100644 --- a/Games/Minesweeper/Field.cpp +++ b/Games/Minesweeper/Field.cpp @@ -121,7 +121,7 @@ Field::Field(GLabel& flag_label, GLabel& time_label, GButton& face_button, GWidg m_number_bitmap[i] = GraphicsBitmap::load_from_file(String::format("/res/icons/minesweeper/%u.png", i + 1)); set_fill_with_background_color(true); - set_background_color(Color::WarmGray); + set_background_color(SystemColor::Window); reset(); m_face_button.on_click = [this](auto&) { reset(); }; diff --git a/Libraries/LibC/SharedBuffer.cpp b/Libraries/LibC/SharedBuffer.cpp index 30fff21a46..86c50edd05 100644 --- a/Libraries/LibC/SharedBuffer.cpp +++ b/Libraries/LibC/SharedBuffer.cpp @@ -25,6 +25,17 @@ bool SharedBuffer::share_with(pid_t peer) return true; } +bool SharedBuffer::share_globally() +{ + int ret = share_buffer_globally(shared_buffer_id()); + if (ret < 0) { + perror("share_buffer_globally"); + return false; + } + return true; +} + + RefPtr SharedBuffer::create_from_shared_buffer_id(int shared_buffer_id) { void* data = get_shared_buffer(shared_buffer_id); diff --git a/Libraries/LibC/SharedBuffer.h b/Libraries/LibC/SharedBuffer.h index e79abad00b..75b3a2a158 100644 --- a/Libraries/LibC/SharedBuffer.h +++ b/Libraries/LibC/SharedBuffer.h @@ -9,6 +9,7 @@ public: static RefPtr create_from_shared_buffer_id(int); ~SharedBuffer(); + bool share_globally(); bool share_with(pid_t); int shared_buffer_id() const { return m_shared_buffer_id; } void seal(); diff --git a/Libraries/LibCore/CConfigFile.cpp b/Libraries/LibCore/CConfigFile.cpp index cef1d1cf4d..dc7a727885 100644 --- a/Libraries/LibCore/CConfigFile.cpp +++ b/Libraries/LibCore/CConfigFile.cpp @@ -110,36 +110,6 @@ int CConfigFile::read_num_entry(const String& group, const String& key, int defa return value; } -Color CConfigFile::read_color_entry(const String& group, const String& key, Color default_value) const -{ - if (!has_key(group, key)) { - const_cast(*this).write_color_entry(group, key, default_value); - return default_value; - } - - auto shades = read_entry(group, key).split(','); - if (shades.size() < 3) - return default_value; - bool ok1 = true, - ok2 = true, - ok3 = true, - ok4 = true; - Color value; - if (shades.size() == 3) { - value = Color(shades[0].to_uint(ok1), - shades[1].to_uint(ok2), - shades[2].to_uint(ok3)); - } else { - value = Color(shades[0].to_uint(ok1), - shades[1].to_uint(ok2), - shades[2].to_uint(ok3), - shades[3].to_uint(ok4)); - } - if (!(ok1 && ok2 && ok3 && ok4)) - return default_value; - return value; -} - bool CConfigFile::read_bool_entry(const String& group, const String& key, bool default_value) const { return read_entry(group, key, default_value ? "1" : "0") == "1"; diff --git a/Libraries/LibCore/CConfigFile.h b/Libraries/LibCore/CConfigFile.h index 6b84947dc5..f21d35f849 100644 --- a/Libraries/LibCore/CConfigFile.h +++ b/Libraries/LibCore/CConfigFile.h @@ -23,7 +23,6 @@ public: String read_entry(const String& group, const String& key, const String& default_vaule = String()) const; int read_num_entry(const String& group, const String& key, int default_value = 0) const; bool read_bool_entry(const String& group, const String& key, bool default_value = false) const; - Color read_color_entry(const String& group, const String& key, Color default_value) const; void write_entry(const String& group, const String& key, const String& value); void write_num_entry(const String& group, const String& key, int value); diff --git a/Libraries/LibDraw/Color.cpp b/Libraries/LibDraw/Color.cpp index fa32743e5e..4324ac440c 100644 --- a/Libraries/LibDraw/Color.cpp +++ b/Libraries/LibDraw/Color.cpp @@ -1,8 +1,85 @@ #include #include +#include #include #include +Color::Color(SystemColor system_color) +{ + auto& theme = current_system_theme(); + switch (system_color) { + case SystemColor::Window: + m_value = theme.window.value(); + break; + case SystemColor::Text: + m_value = theme.text.value(); + break; + case SystemColor::Base: + m_value = theme.base.value(); + break; + case SystemColor::ThreedShadow1: + m_value = theme.threed_shadow1.value(); + break; + case SystemColor::ThreedShadow2: + m_value = theme.threed_shadow2.value(); + break; + case SystemColor::ThreedHighlight: + m_value = theme.threed_highlight.value(); + break; + case SystemColor::HoverHighlight: + m_value = theme.hover_highlight.value(); + break; + case SystemColor::DesktopBackground: + m_value = theme.desktop_background.value(); + break; + case SystemColor::ActiveWindowBorder1: + m_value = theme.active_window_border1.value(); + break; + case SystemColor::ActiveWindowBorder2: + m_value = theme.active_window_border2.value(); + break; + case SystemColor::ActiveWindowTitle: + m_value = theme.active_window_title.value(); + break; + case SystemColor::InactiveWindowBorder1: + m_value = theme.inactive_window_border1.value(); + break; + case SystemColor::InactiveWindowBorder2: + m_value = theme.inactive_window_border2.value(); + break; + case SystemColor::InactiveWindowTitle: + m_value = theme.inactive_window_title.value(); + break; + case SystemColor::MovingWindowBorder1: + m_value = theme.moving_window_border1.value(); + break; + case SystemColor::MovingWindowBorder2: + m_value = theme.moving_window_border2.value(); + break; + case SystemColor::MovingWindowTitle: + m_value = theme.moving_window_title.value(); + break; + case SystemColor::HighlightWindowBorder1: + m_value = theme.highlight_window_border1.value(); + break; + case SystemColor::HighlightWindowBorder2: + m_value = theme.highlight_window_border2.value(); + break; + case SystemColor::HighlightWindowTitle: + m_value = theme.highlight_window_title.value(); + break; + case SystemColor::MenuStripe: + m_value = theme.menu_stripe.value(); + break; + case SystemColor::MenuBase: + m_value = theme.menu_base.value(); + break; + case SystemColor::MenuSelection: + m_value = theme.menu_selection.value(); + break; + } +} + Color::Color(NamedColor named) { struct { diff --git a/Libraries/LibDraw/Color.h b/Libraries/LibDraw/Color.h index f412a29ba9..5b991d05d3 100644 --- a/Libraries/LibDraw/Color.h +++ b/Libraries/LibDraw/Color.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include typedef u32 RGBA32; @@ -11,6 +11,32 @@ inline constexpr u32 make_rgb(u8 r, u8 g, u8 b) return ((r << 16) | (g << 8) | b); } +enum class SystemColor { + DesktopBackground, + ActiveWindowBorder1, + ActiveWindowBorder2, + ActiveWindowTitle, + InactiveWindowBorder1, + InactiveWindowBorder2, + InactiveWindowTitle, + MovingWindowBorder1, + MovingWindowBorder2, + MovingWindowTitle, + HighlightWindowBorder1, + HighlightWindowBorder2, + HighlightWindowTitle, + MenuStripe, + MenuBase, + MenuSelection, + Window, + Text, + Base, + ThreedHighlight, + ThreedShadow1, + ThreedShadow2, + HoverHighlight, +}; + class Color { public: enum NamedColor { @@ -39,6 +65,7 @@ public: Color() {} Color(NamedColor); + Color(SystemColor); Color(u8 r, u8 g, u8 b) : m_value(0xff000000 | (r << 16) | (g << 8) | b) { diff --git a/Libraries/LibDraw/Makefile b/Libraries/LibDraw/Makefile index 8923d22f9c..d6bc6f0b22 100644 --- a/Libraries/LibDraw/Makefile +++ b/Libraries/LibDraw/Makefile @@ -10,6 +10,7 @@ OBJS = \ ImageDecoder.o \ Rect.o \ StylePainter.o \ + SystemTheme.o \ Emoji.o LIBRARY = libdraw.a diff --git a/Libraries/LibDraw/StylePainter.cpp b/Libraries/LibDraw/StylePainter.cpp index 8ef6bbabc3..62e4796c10 100644 --- a/Libraries/LibDraw/StylePainter.cpp +++ b/Libraries/LibDraw/StylePainter.cpp @@ -3,10 +3,10 @@ void StylePainter::paint_tab_button(Painter& painter, const Rect& rect, bool active, bool hovered, bool enabled) { - Color base_color = Color::WarmGray; - Color highlight_color2 = Color::from_rgb(0xe8e7e4); - Color shadow_color1 = Color::from_rgb(0x808080); - Color shadow_color2 = Color::from_rgb(0x404040); + Color base_color = SystemColor::Window; + Color highlight_color2 = SystemColor::ThreedHighlight; + Color shadow_color1 = SystemColor::ThreedShadow1; + Color shadow_color2 = SystemColor::ThreedShadow2; if (hovered && enabled && !active) base_color = StylePainter::hover_highlight_color(); @@ -44,16 +44,16 @@ void StylePainter::paint_tab_button(Painter& painter, const Rect& rect, bool act static void paint_button_new(Painter& painter, const Rect& rect, bool pressed, bool checked, bool hovered, bool enabled) { - Color button_color = Color::WarmGray; - Color highlight_color2 = Color::from_rgb(0xe8e7e4); - Color shadow_color1 = Color::from_rgb(0x808080); - Color shadow_color2 = Color::from_rgb(0x404040); + Color button_color = SystemColor::Window; + Color highlight_color2 = SystemColor::ThreedHighlight; + Color shadow_color1 = SystemColor::ThreedShadow1; + Color shadow_color2 = SystemColor::ThreedShadow2; if (checked && enabled) { if (hovered) - button_color = Color::from_rgb(0xe3dfdb); + button_color = SystemColor::HoverHighlight; else - button_color = Color::from_rgb(0xd6d2ce); + button_color = SystemColor::Window; } else if (hovered && enabled) button_color = StylePainter::hover_highlight_color(); @@ -92,7 +92,7 @@ void StylePainter::paint_button(Painter& painter, const Rect& rect, ButtonStyle if (button_style == ButtonStyle::Normal) return paint_button_new(painter, rect, pressed, checked, hovered, enabled); - Color button_color = checked ? Color::from_rgb(0xd6d2ce) : Color::WarmGray; + Color button_color = SystemColor::Window; Color highlight_color = Color::White; Color shadow_color = Color(96, 96, 96); @@ -129,12 +129,12 @@ void StylePainter::paint_button(Painter& painter, const Rect& rect, ButtonStyle void StylePainter::paint_surface(Painter& painter, const Rect& rect, bool paint_vertical_lines, bool paint_top_line) { - painter.fill_rect({ rect.x(), rect.y() + 1, rect.width(), rect.height() - 2 }, Color::WarmGray); - painter.draw_line(rect.top_left(), rect.top_right(), paint_top_line ? Color::White : Color::WarmGray); - painter.draw_line(rect.bottom_left(), rect.bottom_right(), Color::MidGray); + painter.fill_rect({ rect.x(), rect.y() + 1, rect.width(), rect.height() - 2 }, SystemColor::Window); + painter.draw_line(rect.top_left(), rect.top_right(), paint_top_line ? SystemColor::ThreedHighlight : SystemColor::Window); + painter.draw_line(rect.bottom_left(), rect.bottom_right(), SystemColor::ThreedShadow1); if (paint_vertical_lines) { - painter.draw_line(rect.top_left().translated(0, 1), rect.bottom_left().translated(0, -1), Color::White); - painter.draw_line(rect.top_right(), rect.bottom_right().translated(0, -1), Color::MidGray); + painter.draw_line(rect.top_left().translated(0, 1), rect.bottom_left().translated(0, -1), SystemColor::ThreedHighlight); + painter.draw_line(rect.top_right(), rect.bottom_right().translated(0, -1), SystemColor::ThreedShadow1); } } @@ -142,12 +142,12 @@ void StylePainter::paint_frame(Painter& painter, const Rect& rect, FrameShape sh { Color top_left_color; Color bottom_right_color; - Color dark_shade = Color::from_rgb(0x808080); - Color light_shade = Color::from_rgb(0xffffff); + Color dark_shade = SystemColor::ThreedShadow1; + Color light_shade = SystemColor::ThreedHighlight; if (shape == FrameShape::Container && thickness >= 2) { if (shadow == FrameShadow::Raised) { - dark_shade = Color::from_rgb(0x404040); + dark_shade = SystemColor::ThreedShadow2; } } @@ -175,10 +175,10 @@ void StylePainter::paint_frame(Painter& painter, const Rect& rect, FrameShape sh if (shape == FrameShape::Container && thickness >= 2) { Color top_left_color; Color bottom_right_color; - Color dark_shade = Color::from_rgb(0x404040); - Color light_shade = Color::WarmGray; + Color dark_shade = SystemColor::ThreedShadow2; + Color light_shade = SystemColor::Window; if (shadow == FrameShadow::Raised) { - dark_shade = Color::from_rgb(0x808080); + dark_shade = SystemColor::ThreedShadow1; top_left_color = light_shade; bottom_right_color = dark_shade; } else if (shadow == FrameShadow::Sunken) { @@ -207,10 +207,10 @@ void StylePainter::paint_frame(Painter& painter, const Rect& rect, FrameShape sh void StylePainter::paint_window_frame(Painter& painter, const Rect& rect) { - Color base_color = Color::WarmGray; - Color dark_shade = Color::from_rgb(0x404040); - Color mid_shade = Color::from_rgb(0x808080); - Color light_shade = Color::from_rgb(0xffffff); + Color base_color = SystemColor::Window; + Color dark_shade = SystemColor::ThreedShadow2; + Color mid_shade = SystemColor::ThreedShadow1; + Color light_shade = SystemColor::ThreedHighlight; 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); diff --git a/Libraries/LibDraw/StylePainter.h b/Libraries/LibDraw/StylePainter.h index 82bf8b0b24..d40ad6d336 100644 --- a/Libraries/LibDraw/StylePainter.h +++ b/Libraries/LibDraw/StylePainter.h @@ -32,5 +32,5 @@ public: static void paint_window_frame(Painter&, const Rect&); static void paint_progress_bar(Painter&, const Rect&, int min, int max, int value, const StringView& text = {}); - static Color hover_highlight_color() { return Color::from_rgb(0xe6e5e2); } + static Color hover_highlight_color() { return SystemColor::HoverHighlight; } }; diff --git a/Libraries/LibDraw/SystemTheme.cpp b/Libraries/LibDraw/SystemTheme.cpp new file mode 100644 index 0000000000..c5e05e1ebd --- /dev/null +++ b/Libraries/LibDraw/SystemTheme.cpp @@ -0,0 +1,73 @@ +#include +#include + +static SystemTheme dummy_theme; +static const SystemTheme* theme_page = &dummy_theme; +static RefPtr theme_buffer; + +const SystemTheme& current_system_theme() +{ + ASSERT(theme_page); + return *theme_page; +} + +int current_system_theme_buffer_id() +{ + ASSERT(theme_buffer); + return theme_buffer->shared_buffer_id(); +} + +void set_system_theme(SharedBuffer& buffer) +{ + theme_buffer = buffer; + theme_page = (SystemTheme*)theme_buffer->data(); +} + +RefPtr load_system_theme(const String& path) +{ + auto file = CConfigFile::open(path); + auto buffer = SharedBuffer::create_with_size(sizeof(SystemTheme)); + + dbg() << "Created shared buffer with id " << buffer->shared_buffer_id(); + + auto* data = (SystemTheme*)buffer->data(); + + auto get = [&](auto& name) { + auto color_string = file->read_entry("Colors", name); + auto color = Color::from_string(color_string); + if (!color.has_value()) + return Color(Color::Black); + dbg() << "Parsed system color '" << name << "' = " << color.value(); + return color.value(); + }; + + data->desktop_background = get("DesktopBackground"); + data->threed_highlight = get("ThreedHighlight"); + data->threed_shadow1 = get("ThreedShadow1"); + data->threed_shadow2 = get("ThreedShadow2"); + data->hover_highlight = get("HoverHighlight"); + data->window = get("Window"); + data->text = get("Text"); + data->base = get("Base"); + data->desktop_background = get("DesktopBackground"); + data->active_window_border1 = get("ActiveWindowBorder1"); + data->active_window_border2 = get("ActiveWindowBorder2"); + data->active_window_title = get("ActiveWindowTitle"); + data->inactive_window_border1 = get("InactiveWindowBorder1"); + data->inactive_window_border2 = get("InactiveWindowBorder2"); + data->inactive_window_title = get("InactiveWindowTitle"); + data->moving_window_border1 = get("MovingWindowBorder1"); + data->moving_window_border2 = get("MovingWindowBorder2"); + data->moving_window_title = get("MovingWindowTitle"); + data->highlight_window_border1 = get("HighlightWindowBorder1"); + data->highlight_window_border2 = get("HighlightWindowBorder2"); + data->highlight_window_title = get("HighlightWindowTitle"); + data->menu_stripe = get("MenuStripe"); + data->menu_base = get("MenuBase"); + data->menu_selection = get("MenuSelection"); + + buffer->seal(); + buffer->share_globally(); + + return buffer; +} diff --git a/Libraries/LibDraw/SystemTheme.h b/Libraries/LibDraw/SystemTheme.h new file mode 100644 index 0000000000..2fa7a635b0 --- /dev/null +++ b/Libraries/LibDraw/SystemTheme.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +struct SystemTheme { + Color desktop_background; + + Color active_window_border1; + Color active_window_border2; + Color active_window_title; + + Color inactive_window_border1; + Color inactive_window_border2; + Color inactive_window_title; + + Color moving_window_border1; + Color moving_window_border2; + Color moving_window_title; + + Color highlight_window_border1; + Color highlight_window_border2; + Color highlight_window_title; + + Color menu_stripe; + Color menu_base; + Color menu_selection; + + Color window; + Color text; + Color base; + + Color threed_highlight; + Color threed_shadow1; + Color threed_shadow2; + + Color hover_highlight; +}; + +const SystemTheme& current_system_theme(); +int current_system_theme_buffer_id(); +void set_system_theme(SharedBuffer&); +RefPtr load_system_theme(const String& path); diff --git a/Libraries/LibGUI/GAbstractColumnView.cpp b/Libraries/LibGUI/GAbstractColumnView.cpp index 28b52692da..98ffcfff5a 100644 --- a/Libraries/LibGUI/GAbstractColumnView.cpp +++ b/Libraries/LibGUI/GAbstractColumnView.cpp @@ -99,9 +99,9 @@ void GAbstractColumnView::paint_headers(GPainter& painter) if (!headers_visible()) return; int exposed_width = max(content_size().width(), width()); - painter.fill_rect({ 0, 0, exposed_width, header_height() }, Color::WarmGray); - painter.draw_line({ 0, 0 }, { exposed_width - 1, 0 }, Color::White); - painter.draw_line({ 0, header_height() - 1 }, { exposed_width - 1, header_height() - 1 }, Color::MidGray); + painter.fill_rect({ 0, 0, exposed_width, header_height() }, SystemColor::Window); + painter.draw_line({ 0, 0 }, { exposed_width - 1, 0 }, SystemColor::ThreedHighlight); + painter.draw_line({ 0, header_height() - 1 }, { exposed_width - 1, header_height() - 1 }, SystemColor::ThreedShadow1); int x_offset = 0; int column_count = model()->column_count(); for (int column_index = 0; column_index < column_count; ++column_index) { @@ -129,7 +129,7 @@ void GAbstractColumnView::paint_headers(GPainter& painter) auto text_rect = cell_rect.translated(horizontal_padding(), 0); if (pressed) text_rect.move_by(1, 1); - painter.draw_text(text_rect, text, header_font(), TextAlignment::CenterLeft, Color::Black); + painter.draw_text(text_rect, text, header_font(), TextAlignment::CenterLeft, SystemColor::Text); x_offset += column_width + horizontal_padding() * 2; } } diff --git a/Libraries/LibGUI/GCheckBox.cpp b/Libraries/LibGUI/GCheckBox.cpp index bca4cd8a97..46fd2a2b62 100644 --- a/Libraries/LibGUI/GCheckBox.cpp +++ b/Libraries/LibGUI/GCheckBox.cpp @@ -54,7 +54,7 @@ void GCheckBox::paint_event(GPaintEvent& event) 0, height() / 2 - s_box_height / 2 - 1, s_box_width, s_box_height }; - painter.fill_rect(box_rect, Color::White); + painter.fill_rect(box_rect, SystemColor::Base); StylePainter::paint_frame(painter, box_rect, FrameShape::Container, FrameShadow::Sunken, 2); if (is_being_pressed()) @@ -63,7 +63,7 @@ void GCheckBox::paint_event(GPaintEvent& event) if (is_checked()) { if (!s_checked_bitmap) s_checked_bitmap = &CharacterBitmap::create_from_ascii(s_checked_bitmap_data, s_checked_bitmap_width, s_checked_bitmap_height).leak_ref(); - painter.draw_bitmap(box_rect.shrunken(4, 4).location(), *s_checked_bitmap, foreground_color()); + painter.draw_bitmap(box_rect.shrunken(4, 4).location(), *s_checked_bitmap, SystemColor::Text); } paint_text(painter, text_rect, font(), TextAlignment::TopLeft); diff --git a/Libraries/LibGUI/GFilePicker.cpp b/Libraries/LibGUI/GFilePicker.cpp index 6480f8c603..28d19d197d 100644 --- a/Libraries/LibGUI/GFilePicker.cpp +++ b/Libraries/LibGUI/GFilePicker.cpp @@ -58,7 +58,7 @@ GFilePicker::GFilePicker(Mode mode, const StringView& file_name, const StringVie horizontal_container->set_layout(make(Orientation::Horizontal)); horizontal_container->layout()->set_margins({ 4, 4, 4, 4 }); horizontal_container->set_fill_with_background_color(true); - horizontal_container->set_background_color(Color::WarmGray); + horizontal_container->set_background_color(SystemColor::Window); auto vertical_container = GWidget::construct(horizontal_container.ptr()); vertical_container->set_layout(make(Orientation::Vertical)); diff --git a/Libraries/LibGUI/GGroupBox.cpp b/Libraries/LibGUI/GGroupBox.cpp index 3ce063f248..3d97790262 100644 --- a/Libraries/LibGUI/GGroupBox.cpp +++ b/Libraries/LibGUI/GGroupBox.cpp @@ -12,7 +12,7 @@ GGroupBox::GGroupBox(const StringView& title, GWidget* parent) , m_title(title) { set_fill_with_background_color(true); - set_background_color(Color::WarmGray); + set_background_color(SystemColor::Window); } GGroupBox::~GGroupBox() diff --git a/Libraries/LibGUI/GItemView.cpp b/Libraries/LibGUI/GItemView.cpp index 3c880d220e..183afb4e4b 100644 --- a/Libraries/LibGUI/GItemView.cpp +++ b/Libraries/LibGUI/GItemView.cpp @@ -191,7 +191,7 @@ void GItemView::paint_event(GPaintEvent& event) GPainter painter(*this); painter.add_clip_rect(widget_inner_rect()); painter.add_clip_rect(event.rect()); - painter.fill_rect(event.rect(), Color::White); + painter.fill_rect(event.rect(), SystemColor::Base); painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value()); auto column_metadata = model()->column_metadata(m_model_column); @@ -203,7 +203,7 @@ void GItemView::paint_event(GPaintEvent& event) if (is_selected_item) { background_color = is_focused() ? Color::from_rgb(0x84351a) : Color::from_rgb(0x606060); } else { - background_color = Color::White; + background_color = SystemColor::Base; } Rect item_rect = this->item_rect(item_index); @@ -230,7 +230,7 @@ void GItemView::paint_event(GPaintEvent& event) if (is_selected_item) text_color = Color::White; else - text_color = model()->data(model_index, GModel::Role::ForegroundColor).to_color(Color::Black); + text_color = model()->data(model_index, GModel::Role::ForegroundColor).to_color(SystemColor::Text); painter.fill_rect(text_rect, background_color); painter.draw_text(text_rect, item_text.to_string(), font, TextAlignment::Center, text_color, TextElision::Right); }; diff --git a/Libraries/LibGUI/GListView.cpp b/Libraries/LibGUI/GListView.cpp index b0aea7f0b0..29bf44aa64 100644 --- a/Libraries/LibGUI/GListView.cpp +++ b/Libraries/LibGUI/GListView.cpp @@ -109,7 +109,7 @@ void GListView::paint_event(GPaintEvent& event) if (alternating_row_colors() && (painted_item_index % 2)) background_color = Color(210, 210, 210); else - background_color = Color::White; + background_color = SystemColor::Base; } auto column_metadata = model()->column_metadata(m_model_column); @@ -140,7 +140,7 @@ void GListView::paint_event(GPaintEvent& event) }; Rect unpainted_rect(0, painted_item_index * item_height(), exposed_width, height()); - painter.fill_rect(unpainted_rect, Color::White); + painter.fill_rect(unpainted_rect, SystemColor::Base); } int GListView::item_count() const diff --git a/Libraries/LibGUI/GSplitter.cpp b/Libraries/LibGUI/GSplitter.cpp index 2f82f5219e..b1dc224462 100644 --- a/Libraries/LibGUI/GSplitter.cpp +++ b/Libraries/LibGUI/GSplitter.cpp @@ -8,7 +8,7 @@ GSplitter::GSplitter(Orientation orientation, GWidget* parent) { set_layout(make(orientation)); set_fill_with_background_color(true); - set_background_color(Color::WarmGray); + set_background_color(SystemColor::Window); layout()->set_spacing(3); } @@ -25,7 +25,7 @@ void GSplitter::enter_event(CEvent&) void GSplitter::leave_event(CEvent&) { - set_background_color(Color::WarmGray); + set_background_color(SystemColor::Window); if (!m_resizing) window()->set_override_cursor(GStandardCursor::None); update(); diff --git a/Libraries/LibGUI/GTabWidget.cpp b/Libraries/LibGUI/GTabWidget.cpp index a5dc20864f..7166d3b454 100644 --- a/Libraries/LibGUI/GTabWidget.cpp +++ b/Libraries/LibGUI/GTabWidget.cpp @@ -7,7 +7,7 @@ GTabWidget::GTabWidget(GWidget* parent) : GWidget(parent) { set_fill_with_background_color(true); - set_background_color(Color::WarmGray); + set_background_color(SystemColor::Window); } GTabWidget::~GTabWidget() diff --git a/Libraries/LibGUI/GTableView.cpp b/Libraries/LibGUI/GTableView.cpp index 3bb8588daf..820e5ed3fd 100644 --- a/Libraries/LibGUI/GTableView.cpp +++ b/Libraries/LibGUI/GTableView.cpp @@ -25,7 +25,7 @@ void GTableView::paint_event(GPaintEvent& event) GPainter painter(*this); painter.add_clip_rect(frame_inner_rect()); painter.add_clip_rect(event.rect()); - painter.fill_rect(event.rect(), Color::White); + painter.fill_rect(event.rect(), SystemColor::Base); painter.translate(frame_thickness(), frame_thickness()); painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value()); @@ -60,7 +60,7 @@ void GTableView::paint_event(GPaintEvent& event) background_color = Color(220, 220, 220); key_column_background_color = Color(200, 200, 200); } else { - background_color = Color::White; + background_color = SystemColor::Base; key_column_background_color = Color(220, 220, 220); } } @@ -105,7 +105,7 @@ void GTableView::paint_event(GPaintEvent& event) }; Rect unpainted_rect(0, header_height() + painted_item_index * item_height(), exposed_width, height()); - painter.fill_rect(unpainted_rect, Color::White); + painter.fill_rect(unpainted_rect, SystemColor::Base); // Untranslate the painter vertically and do the column headers. painter.translate(0, vertical_scrollbar().value()); diff --git a/Libraries/LibGUI/GToolBar.cpp b/Libraries/LibGUI/GToolBar.cpp index 59b32c2725..85b468929b 100644 --- a/Libraries/LibGUI/GToolBar.cpp +++ b/Libraries/LibGUI/GToolBar.cpp @@ -93,5 +93,5 @@ void GToolBar::paint_event(GPaintEvent& event) if (m_has_frame) StylePainter::paint_surface(painter, rect(), x() != 0, y() != 0); else - painter.fill_rect(event.rect(), Color::WarmGray); + painter.fill_rect(event.rect(), background_color()); } diff --git a/Libraries/LibGUI/GTreeView.cpp b/Libraries/LibGUI/GTreeView.cpp index e0c3283245..2269f45a64 100644 --- a/Libraries/LibGUI/GTreeView.cpp +++ b/Libraries/LibGUI/GTreeView.cpp @@ -146,7 +146,7 @@ void GTreeView::paint_event(GPaintEvent& event) GPainter painter(*this); painter.add_clip_rect(frame_inner_rect()); painter.add_clip_rect(event.rect()); - painter.fill_rect(event.rect(), Color::White); + painter.fill_rect(event.rect(), SystemColor::Base); if (!model()) return; @@ -192,7 +192,7 @@ void GTreeView::paint_event(GPaintEvent& event) background_color = Color(220, 220, 220); key_column_background_color = Color(200, 200, 200); } else { - background_color = Color::White; + background_color = SystemColor::Base; key_column_background_color = Color(220, 220, 220); } } diff --git a/Libraries/LibGUI/GWidget.cpp b/Libraries/LibGUI/GWidget.cpp index 9dcaf7cdce..66e926acb1 100644 --- a/Libraries/LibGUI/GWidget.cpp +++ b/Libraries/LibGUI/GWidget.cpp @@ -68,8 +68,8 @@ GWidget::GWidget(GWidget* parent) : CObject(parent, true) , m_font(Font::default_font()) { - m_background_color = Color::WarmGray; - m_foreground_color = Color::Black; + m_background_color = SystemColor::Window; + m_foreground_color = SystemColor::Text; } GWidget::~GWidget() diff --git a/Libraries/LibGUI/GWindow.cpp b/Libraries/LibGUI/GWindow.cpp index 5f5c69240f..c9d1511392 100644 --- a/Libraries/LibGUI/GWindow.cpp +++ b/Libraries/LibGUI/GWindow.cpp @@ -686,3 +686,10 @@ void GWindow::schedule_relayout() m_layout_pending = false; }); } + +void GWindow::update_all_windows(Badge) +{ + for (auto* window : all_windows) { + window->update(); + } +} diff --git a/Libraries/LibGUI/GWindow.h b/Libraries/LibGUI/GWindow.h index 82575d4a14..b345bda92e 100644 --- a/Libraries/LibGUI/GWindow.h +++ b/Libraries/LibGUI/GWindow.h @@ -1,15 +1,17 @@ #pragma once -#include +#include #include +#include #include #include #include #include #include -class GWidget; class GWMEvent; +class GWidget; +class GWindowServerConnection; enum class GStandardCursor { None = 0, @@ -133,6 +135,8 @@ public: void schedule_relayout(); + static void update_all_windows(Badge); + protected: GWindow(CObject* parent = nullptr); virtual void wm_event(GWMEvent&); diff --git a/Libraries/LibGUI/GWindowServerConnection.cpp b/Libraries/LibGUI/GWindowServerConnection.cpp index 8e2daca6e9..d7f98e8ce8 100644 --- a/Libraries/LibGUI/GWindowServerConnection.cpp +++ b/Libraries/LibGUI/GWindowServerConnection.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -19,13 +20,27 @@ GWindowServerConnection& GWindowServerConnection::the() return *s_connection; } +static void set_system_theme_from_shared_buffer_id(int id) +{ + auto system_theme = SharedBuffer::create_from_shared_buffer_id(id); + ASSERT(system_theme); + set_system_theme(*system_theme); +} + void GWindowServerConnection::handshake() { auto response = send_sync(); set_my_client_id(response->client_id()); + set_system_theme_from_shared_buffer_id(response->system_theme_buffer_id()); GDesktop::the().did_receive_screen_rect({}, response->screen_rect()); } +void GWindowServerConnection::handle(const WindowClient::UpdateSystemTheme& message) +{ + set_system_theme_from_shared_buffer_id(message.system_theme_buffer_id()); + GWindow::update_all_windows({}); +} + void GWindowServerConnection::handle(const WindowClient::Paint& message) { #ifdef GEVENTLOOP_DEBUG diff --git a/Libraries/LibGUI/GWindowServerConnection.h b/Libraries/LibGUI/GWindowServerConnection.h index 56fc8b0c21..1647d5fc85 100644 --- a/Libraries/LibGUI/GWindowServerConnection.h +++ b/Libraries/LibGUI/GWindowServerConnection.h @@ -44,4 +44,5 @@ private: virtual void handle(const WindowClient::DragDropped&) override; virtual void handle(const WindowClient::DragAccepted&) override; virtual void handle(const WindowClient::DragCancelled&) override; + virtual void handle(const WindowClient::UpdateSystemTheme&) override; }; diff --git a/Servers/WindowServer/WSClientConnection.cpp b/Servers/WindowServer/WSClientConnection.cpp index fc507b4613..d91186513e 100644 --- a/Servers/WindowServer/WSClientConnection.cpp +++ b/Servers/WindowServer/WSClientConnection.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -605,7 +606,7 @@ void WSClientConnection::handle(const WindowServer::WM_SetWindowMinimized& messa OwnPtr WSClientConnection::handle(const WindowServer::Greet&) { - return make(client_id(), WSScreen::the().rect()); + return make(client_id(), WSScreen::the().rect(), current_system_theme_buffer_id()); } bool WSClientConnection::is_showing_modal_window() const diff --git a/Servers/WindowServer/WSCompositor.cpp b/Servers/WindowServer/WSCompositor.cpp index 459cc301ed..ee166e4df3 100644 --- a/Servers/WindowServer/WSCompositor.cpp +++ b/Servers/WindowServer/WSCompositor.cpp @@ -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, wm.m_background_color); + m_back_painter->fill_rect(dirty_rect, SystemColor::DesktopBackground); if (m_wallpaper) { if (m_wallpaper_mode == WallpaperMode::Simple) { m_back_painter->blit(dirty_rect.location(), *m_wallpaper, dirty_rect); diff --git a/Servers/WindowServer/WSMenu.cpp b/Servers/WindowServer/WSMenu.cpp index 71186e4fa9..46e1a3e35c 100644 --- a/Servers/WindowServer/WSMenu.cpp +++ b/Servers/WindowServer/WSMenu.cpp @@ -126,12 +126,14 @@ WSWindow& WSMenu::ensure_menu_window() void WSMenu::draw() { + m_theme_index_at_last_paint = WSWindowManager::the().theme_index(); + ASSERT(menu_window()); ASSERT(menu_window()->backing_store()); Painter painter(*menu_window()->backing_store()); Rect rect { {}, menu_window()->size() }; - painter.fill_rect(rect.shrunken(6, 6), Color::White); + painter.fill_rect(rect.shrunken(6, 6), SystemColor::MenuBase); StylePainter::paint_window_frame(painter, rect); int width = this->width(); @@ -146,15 +148,15 @@ void WSMenu::draw() } Rect stripe_rect { frame_thickness(), frame_thickness(), s_stripe_width, height() - frame_thickness() * 2 }; - painter.fill_rect(stripe_rect, Color::WarmGray); - painter.draw_line(stripe_rect.top_right(), stripe_rect.bottom_right(), Color::from_rgb(0xbbb7b0)); + painter.fill_rect(stripe_rect, SystemColor::MenuStripe); + painter.draw_line(stripe_rect.top_right(), stripe_rect.bottom_right(), Color(SystemColor::MenuStripe).darkened()); for (auto& item : m_items) { if (item.type() == WSMenuItem::Text) { Color text_color = Color::Black; if (&item == m_hovered_item && item.is_enabled()) { - painter.fill_rect(item.rect(), Color::from_rgb(0xad714f)); - painter.draw_rect(item.rect(), Color::from_rgb(0x793016)); + painter.fill_rect(item.rect(), SystemColor::MenuSelection); + painter.draw_rect(item.rect(), Color(SystemColor::MenuSelection).darkened()); text_color = Color::White; } else if (!item.is_enabled()) { text_color = Color::MidGray; @@ -164,10 +166,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, Color::White); + painter.fill_rect(checkbox_rect, SystemColor::Base); StylePainter::paint_frame(painter, checkbox_rect, FrameShape::Container, FrameShadow::Sunken, 2); if (item.is_checked()) { - painter.draw_bitmap(checkmark_rect.location(), *s_checked_bitmap, Color::Black); + painter.draw_bitmap(checkmark_rect.location(), *s_checked_bitmap, SystemColor::Text); } } else if (item.icon()) { Rect icon_rect { item.rect().x() + 3, 0, s_item_icon_width, s_item_icon_width }; @@ -274,11 +276,19 @@ void WSMenu::close() WSWindowManager::the().menu_manager().close_menu_and_descendants(*this); } +void WSMenu::redraw_if_theme_changed() +{ + if (m_theme_index_at_last_paint != WSWindowManager::the().theme_index()) + redraw(); +} + void WSMenu::popup(const Point& position, bool is_submenu) { ASSERT(!is_empty()); auto& window = ensure_menu_window(); + redraw_if_theme_changed(); + const int margin = 30; Point adjusted_pos = position; if (adjusted_pos.x() + window.width() >= WSScreen::the().width() - margin) { diff --git a/Servers/WindowServer/WSMenu.h b/Servers/WindowServer/WSMenu.h index fc72a71b81..7f9afb946a 100644 --- a/Servers/WindowServer/WSMenu.h +++ b/Servers/WindowServer/WSMenu.h @@ -78,6 +78,8 @@ public: bool is_menu_ancestor_of(const WSMenu&) const; + void redraw_if_theme_changed(); + private: virtual void event(CEvent&) override; @@ -92,4 +94,5 @@ private: WSMenuItem* m_hovered_item { nullptr }; NonnullOwnPtrVector m_items; RefPtr m_menu_window; + int m_theme_index_at_last_paint { -1 }; }; diff --git a/Servers/WindowServer/WSMenuManager.cpp b/Servers/WindowServer/WSMenuManager.cpp index cccb76782c..51a9f6e99b 100644 --- a/Servers/WindowServer/WSMenuManager.cpp +++ b/Servers/WindowServer/WSMenuManager.cpp @@ -83,14 +83,14 @@ void WSMenuManager::draw() Painter painter(*window().backing_store()); - painter.fill_rect(menubar_rect, Color::WarmGray); - painter.draw_line({ 0, menubar_rect.bottom() }, { menubar_rect.right(), menubar_rect.bottom() }, Color::MidGray); + painter.fill_rect(menubar_rect, SystemColor::Window); + painter.draw_line({ 0, menubar_rect.bottom() }, { menubar_rect.right(), menubar_rect.bottom() }, SystemColor::ThreedShadow1); int index = 0; wm.for_each_active_menubar_menu([&](WSMenu& menu) { - Color text_color = Color::Black; + Color text_color = SystemColor::Text; if (is_open(menu)) { - painter.fill_rect(menu.rect_in_menubar(), Color::from_rgb(0xad714f)); - painter.draw_rect(menu.rect_in_menubar(), Color::from_rgb(0x793016)); + painter.fill_rect(menu.rect_in_menubar(), SystemColor::MenuSelection); + painter.draw_rect(menu.rect_in_menubar(), Color(SystemColor::MenuSelection).darkened()); text_color = Color::White; } painter.draw_text( @@ -103,7 +103,7 @@ void WSMenuManager::draw() return IterationDecision::Continue; }); - painter.draw_text(m_username_rect, m_username, Font::default_bold_font(), TextAlignment::CenterRight, Color::Black); + painter.draw_text(m_username_rect, m_username, Font::default_bold_font(), TextAlignment::CenterRight, SystemColor::Text); time_t now = time(nullptr); auto* tm = localtime(&now); @@ -115,7 +115,7 @@ void WSMenuManager::draw() tm->tm_min, tm->tm_sec); - painter.draw_text(m_time_rect, time_text, wm.font(), TextAlignment::CenterRight, Color::Black); + painter.draw_text(m_time_rect, time_text, wm.font(), TextAlignment::CenterRight, SystemColor::Text); for (auto& applet : m_applets) { if (!applet) @@ -182,6 +182,7 @@ void WSMenuManager::handle_menu_mouse_event(WSMenu& menu, const WSMouseEvent& ev return; close_everyone(); if (!menu.is_empty()) { + menu.redraw_if_theme_changed(); auto& menu_window = menu.ensure_menu_window(); menu_window.move_to({ menu.rect_in_menubar().x(), menu.rect_in_menubar().bottom() + 2 }); menu_window.set_visible(true); diff --git a/Servers/WindowServer/WSWindowFrame.cpp b/Servers/WindowServer/WSWindowFrame.cpp index 3b4801a26c..44853d3ec1 100644 --- a/Servers/WindowServer/WSWindowFrame.cpp +++ b/Servers/WindowServer/WSWindowFrame.cpp @@ -170,21 +170,21 @@ void WSWindowFrame::paint(Painter& painter) auto& wm = WSWindowManager::the(); if (&window == wm.m_highlight_window) { - border_color = wm.m_highlight_window_border_color; - border_color2 = wm.m_highlight_window_border_color2; - title_color = wm.m_highlight_window_title_color; + border_color = SystemColor::HighlightWindowBorder1; + border_color2 = SystemColor::HighlightWindowBorder2; + title_color = SystemColor::HighlightWindowTitle; } else if (&window == wm.m_move_window) { - border_color = wm.m_moving_window_border_color; - border_color2 = wm.m_moving_window_border_color2; - title_color = wm.m_moving_window_title_color; + border_color = SystemColor::MovingWindowBorder1; + border_color2 = SystemColor::MovingWindowBorder2; + title_color = SystemColor::MovingWindowTitle; } else if (&window == wm.m_active_window) { - border_color = wm.m_active_window_border_color; - border_color2 = wm.m_active_window_border_color2; - title_color = wm.m_active_window_title_color; + border_color = SystemColor::ActiveWindowBorder1; + border_color2 = SystemColor::ActiveWindowBorder2; + title_color = SystemColor::ActiveWindowTitle; } else { - border_color = wm.m_inactive_window_border_color; - border_color2 = wm.m_inactive_window_border_color2; - title_color = wm.m_inactive_window_title_color; + border_color = SystemColor::InactiveWindowBorder1; + border_color2 = SystemColor::InactiveWindowBorder2; + title_color = SystemColor::InactiveWindowTitle; } StylePainter::paint_window_frame(painter, outer_rect); diff --git a/Servers/WindowServer/WSWindowManager.cpp b/Servers/WindowServer/WSWindowManager.cpp index d4b56e953a..be1f2fb71f 100644 --- a/Servers/WindowServer/WSWindowManager.cpp +++ b/Servers/WindowServer/WSWindowManager.cpp @@ -6,6 +6,7 @@ #include "WSMenuItem.h" #include "WSScreen.h" #include "WSWindow.h" +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -47,19 +49,21 @@ WSWindowManager::WSWindowManager() reload_config(false); HashTable seen_app_categories; - CDirIterator dt("/res/apps", CDirIterator::SkipDots); - while (dt.has_next()) { - auto af_name = dt.next_path(); - auto af_path = String::format("/res/apps/%s", af_name.characters()); - auto af = CConfigFile::open(af_path); - if (!af->has_key("App", "Name") || !af->has_key("App", "Executable")) - continue; - auto app_name = af->read_entry("App", "Name"); - auto app_executable = af->read_entry("App", "Executable"); - auto app_category = af->read_entry("App", "Category"); - auto app_icon_path = af->read_entry("Icons", "16x16"); - m_apps.append({ app_executable, app_name, app_icon_path, app_category }); - seen_app_categories.set(app_category); + { + CDirIterator dt("/res/apps", CDirIterator::SkipDots); + while (dt.has_next()) { + auto af_name = dt.next_path(); + auto af_path = String::format("/res/apps/%s", af_name.characters()); + auto af = CConfigFile::open(af_path); + if (!af->has_key("App", "Name") || !af->has_key("App", "Executable")) + continue; + auto app_name = af->read_entry("App", "Name"); + auto app_executable = af->read_entry("App", "Executable"); + auto app_category = af->read_entry("App", "Category"); + auto app_icon_path = af->read_entry("Icons", "16x16"); + m_apps.append({ app_executable, app_name, app_icon_path, app_category }); + seen_app_categories.set(app_category); + } } Vector sorted_app_categories; @@ -98,8 +102,56 @@ WSWindowManager::WSWindowManager() parent_menu->add_item(make(*m_system_menu, app_identifier++, app.name, String(), true, false, false, load_png(app.icon_path))); } + m_system_menu->add_item(make(*m_system_menu, WSMenuItem::Separator)); + + m_themes_menu = WSMenu::construct(nullptr, 9000, "Themes"); + + auto themes_menu_item = make(*m_system_menu, 100, "Themes"); + themes_menu_item->set_submenu_id(m_themes_menu->menu_id()); + m_system_menu->add_item(move(themes_menu_item)); + + { + CDirIterator dt("/res/themes", CDirIterator::SkipDots); + while (dt.has_next()) { + auto theme_name = dt.next_path(); + auto theme_path = String::format("/res/themes/%s", theme_name.characters()); + m_themes.append({ FileSystemPath(theme_name).title(), theme_path }); + } + quick_sort(m_themes.begin(), m_themes.end(), [](auto& a, auto& b) { return a.name < b.name; }); + } + + { + int theme_identifier = 9000; + for (auto& theme : m_themes) { + m_themes_menu->add_item(make(*m_themes_menu, theme_identifier++, theme.name)); + } + } + + m_themes_menu->on_item_activation = [this](WSMenuItem& item) { + auto& theme = m_themes[(int)item.identifier() - 9000]; + auto new_theme = load_system_theme(theme.path); + ASSERT(new_theme); + set_system_theme(*new_theme); + HashTable notified_clients; + for_each_window([&](WSWindow& window) { + if (window.client()) { + if (!notified_clients.contains(window.client())) { + window.client()->post_message(WindowClient::UpdateSystemTheme(current_system_theme_buffer_id())); + notified_clients.set(window.client()); + } + } + return IterationDecision::Continue; + }); + ++m_theme_index; + auto wm_config = CConfigFile::get_for_app("WindowManager"); + wm_config->write_entry("Theme", "Name", theme.name); + wm_config->sync(); + invalidate(); + }; + m_system_menu->add_item(make(*m_system_menu, WSMenuItem::Separator)); m_system_menu->add_item(make(*m_system_menu, 100, "Reload WM Config File")); + m_system_menu->add_item(make(*m_system_menu, WSMenuItem::Separator)); m_system_menu->add_item(make(*m_system_menu, 200, "About...", String(), true, false, false, load_png("/res/icons/16x16/ladybug.png"))); m_system_menu->add_item(make(*m_system_menu, WSMenuItem::Separator)); @@ -186,26 +238,6 @@ void WSWindowManager::reload_config(bool set_screen) m_disallowed_cursor = get_cursor("Disallowed"); m_move_cursor = get_cursor("Move"); m_drag_cursor = get_cursor("Drag"); - - m_background_color = m_wm_config->read_color_entry("Colors", "Background", Color::Red); - - m_active_window_border_color = m_wm_config->read_color_entry("Colors", "ActiveWindowBorder", Color::Red); - m_active_window_border_color2 = m_wm_config->read_color_entry("Colors", "ActiveWindowBorder2", Color::Red); - m_active_window_title_color = m_wm_config->read_color_entry("Colors", "ActiveWindowTitle", Color::Red); - - m_inactive_window_border_color = m_wm_config->read_color_entry("Colors", "InactiveWindowBorder", Color::Red); - m_inactive_window_border_color2 = m_wm_config->read_color_entry("Colors", "InactiveWindowBorder2", Color::Red); - m_inactive_window_title_color = m_wm_config->read_color_entry("Colors", "InactiveWindowTitle", Color::Red); - - m_moving_window_border_color = m_wm_config->read_color_entry("Colors", "MovingWindowBorder", Color::Red); - m_moving_window_border_color2 = m_wm_config->read_color_entry("Colors", "MovingWindowBorder2", Color::Red); - m_moving_window_title_color = m_wm_config->read_color_entry("Colors", "MovingWindowTitle", Color::Red); - - m_highlight_window_border_color = m_wm_config->read_color_entry("Colors", "HighlightWindowBorder", Color::Red); - m_highlight_window_border_color2 = m_wm_config->read_color_entry("Colors", "HighlightWindowBorder2", Color::Red); - m_highlight_window_title_color = m_wm_config->read_color_entry("Colors", "HighlightWindowTitle", Color::Red); - - m_menu_selection_color = m_wm_config->read_color_entry("Colors", "MenuSelectionColor", Color::Red); } const Font& WSWindowManager::font() const @@ -1183,6 +1215,8 @@ Rect WSWindowManager::maximized_window_rect(const WSWindow& window) const WSMenu* WSWindowManager::find_internal_menu_by_id(int menu_id) { + if (m_themes_menu->menu_id() == menu_id) + return m_themes_menu.ptr(); for (auto& it : m_app_category_menus) { if (menu_id == it.value->menu_id()) return it.value; diff --git a/Servers/WindowServer/WSWindowManager.h b/Servers/WindowServer/WSWindowManager.h index 6faaa6699e..2f19aa9b7d 100644 --- a/Servers/WindowServer/WSWindowManager.h +++ b/Servers/WindowServer/WSWindowManager.h @@ -159,6 +159,8 @@ public: WSMenu* find_internal_menu_by_id(int); + int theme_index() const { return m_theme_index; } + private: NonnullRefPtr get_cursor(const String& name); NonnullRefPtr get_cursor(const String& name, const Point& hotspot); @@ -266,6 +268,8 @@ private: Color m_menu_selection_color; WeakPtr m_current_menubar; + int m_theme_index { 0 }; + WSWindowSwitcher m_switcher; WSMenuManager m_menu_manager; @@ -283,6 +287,13 @@ private: Vector m_apps; HashMap> m_app_category_menus; + struct ThemeMetadata { + String name; + String path; + }; + Vector m_themes; + RefPtr m_themes_menu; + WeakPtr m_dnd_client; String m_dnd_text; String m_dnd_data_type; diff --git a/Servers/WindowServer/WindowClient.ipc b/Servers/WindowServer/WindowClient.ipc index 8006d3d814..9e345ef52d 100644 --- a/Servers/WindowServer/WindowClient.ipc +++ b/Servers/WindowServer/WindowClient.ipc @@ -32,4 +32,6 @@ endpoint WindowClient = 4 DragCancelled() =| DragDropped(i32 window_id, Point mouse_position, String text, String data_type, String data) =| + + UpdateSystemTheme(i32 system_theme_buffer_id) =| } diff --git a/Servers/WindowServer/WindowServer.ipc b/Servers/WindowServer/WindowServer.ipc index 64988712f8..0a831081d3 100644 --- a/Servers/WindowServer/WindowServer.ipc +++ b/Servers/WindowServer/WindowServer.ipc @@ -1,6 +1,6 @@ endpoint WindowServer = 2 { - Greet() => (i32 client_id, Rect screen_rect) + Greet() => (i32 client_id, Rect screen_rect, i32 system_theme_buffer_id) CreateMenubar() => (i32 menubar_id) DestroyMenubar(i32 menubar_id) => () diff --git a/Servers/WindowServer/main.cpp b/Servers/WindowServer/main.cpp index 542998a3d0..c629f9464d 100644 --- a/Servers/WindowServer/main.cpp +++ b/Servers/WindowServer/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -18,9 +19,15 @@ int main(int, char**) return 1; } + auto wm_config = CConfigFile::get_for_app("WindowManager"); + auto theme_name = wm_config->read_entry("Theme", "Name", "Default"); + + auto theme = load_system_theme(String::format("/res/themes/%s.ini", theme_name.characters())); + ASSERT(theme); + set_system_theme(*theme); + WSEventLoop loop; - auto wm_config = CConfigFile::get_for_app("WindowManager"); WSScreen screen(wm_config->read_num_entry("Screen", "Width", 1024), wm_config->read_num_entry("Screen", "Height", 768)); WSCompositor::the();