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();