diff --git a/Base/res/themes/Basalt.ini b/Base/res/themes/Basalt.ini new file mode 100644 index 0000000000..237713902f --- /dev/null +++ b/Base/res/themes/Basalt.ini @@ -0,0 +1,62 @@ +[Colors] +DesktopBackground=#171717 +ActiveWindowBorder1=black +ActiveWindowBorder2=#1f1f1f +ActiveWindowTitle=white +InactiveWindowBorder1=#171717 +InactiveWindowBorder2=#1f1f1f +InactiveWindowTitle=#aaaaaa +MovingWindowBorder1=black +MovingWindowBorder2=#2f1f1f +MovingWindowTitle=white +HighlightWindowBorder1=black +HighlightWindowBorder2=#3f2f0f +HighlightWindowTitle=white +WindowTitleShadow=#00000000 +WindowTitleStripes=#00000000 +MenuBase=#1f1f1f +MenuBaseText=white +MenuStripe=#171717 +MenuSelection=#ff7f00 +MenuSelectionText=black +Window=#1f1f1f +WindowText=white +Button=#1f1f1f +ButtonText=white +Base=#1f1f1f +BaseText=white +ThreedHighlight=#2f2f2f +ThreedShadow1=#171717 +ThreedShadow2=#0f0f0f +HoverHighlight=#272727 +Selection=#ff7f00 +SelectionText=black +InactiveSelection=#7f0000 +InactiveSelectionText=black +RubberBandFill=#ff7f002f +RubberBandBorder=#ff7f00 +Link=#88c +ActiveLink=#c88 +VisitedLink=#c8c +Ruler=#0f0f0f +RulerBorder=#2f2f2f +RulerActiveText=white +RulerInactiveText=#2f2f2f +TextCursor=#ff4f4f +FocusOutline=#4f4f4f +SyntaxComment=#4fbf4f +SyntaxNumber=white +SyntaxString=#ff8f4f +SyntaxType=#6f8fff +SyntaxPunctuation=white +SyntaxOperator=white +SyntaxKeyword=#cf7fff +SyntaxControlKeyword=orchid +SyntaxIdentifier=white +SyntaxPreprocessorStatement=#ffafff +SyntaxPreprocessorValue=orange + +[Metrics] +TitleHeight=24 +TitleButtonWidth=32 +TitleButtonHeight=18 diff --git a/Base/res/themes/Default.ini b/Base/res/themes/Default.ini index 8f50b5ac13..4281581667 100644 --- a/Base/res/themes/Default.ini +++ b/Base/res/themes/Default.ini @@ -12,6 +12,8 @@ MovingWindowTitle=white HighlightWindowBorder1=#a10d0d HighlightWindowBorder2=#fabbbb HighlightWindowTitle=white +WindowTitleShadow=#421405 +WindowTitleStripes=#6e2209 MenuBase=white MenuBaseText=black MenuStripe=#bbb7b0 @@ -53,3 +55,8 @@ SyntaxControlKeyword=black SyntaxIdentifier=#092e64 SyntaxPreprocessorStatement=#008080 SyntaxPreprocessorValue=#800000 + +[Metrics] +TitleHeight=19 +TitleButtonWidth=15 +TitleButtonHeight=15 diff --git a/Libraries/LibGfx/Palette.cpp b/Libraries/LibGfx/Palette.cpp index b288f345b4..de86256771 100644 --- a/Libraries/LibGfx/Palette.cpp +++ b/Libraries/LibGfx/Palette.cpp @@ -61,6 +61,12 @@ Color PaletteImpl::color(ColorRole role) const return theme().color[(int)role]; } +int PaletteImpl::metric(MetricRole role) const +{ + ASSERT((int)role < (int)MetricRole::__Count); + return theme().metric[(int)role]; +} + NonnullRefPtr PaletteImpl::clone() const { auto new_theme_buffer = SharedBuffer::create_with_size(m_theme_buffer->size()); @@ -76,6 +82,14 @@ void Palette::set_color(ColorRole role, Color color) theme.color[(int)role] = color; } +void Palette::set_metric(MetricRole role, int value) +{ + if (m_impl->ref_count() != 1) + m_impl = m_impl->clone(); + auto& theme = const_cast(impl().theme()); + theme.metric[(int)role] = value; +} + PaletteImpl::~PaletteImpl() { } diff --git a/Libraries/LibGfx/Palette.h b/Libraries/LibGfx/Palette.h index aa13eca4cf..e23d12dde2 100644 --- a/Libraries/LibGfx/Palette.h +++ b/Libraries/LibGfx/Palette.h @@ -44,6 +44,7 @@ public: NonnullRefPtr clone() const; Color color(ColorRole) const; + int metric(MetricRole) const; const SystemTheme& theme() const; void replace_internal_buffer(Badge, SharedBuffer& buffer); @@ -79,6 +80,8 @@ public: Color highlight_window_border1() const { return color(ColorRole::HighlightWindowBorder1); } Color highlight_window_border2() const { return color(ColorRole::HighlightWindowBorder2); } Color highlight_window_title() const { return color(ColorRole::HighlightWindowTitle); } + Color window_title_stripes() const { return color(ColorRole::WindowTitleStripes); } + Color window_title_shadow() const { return color(ColorRole::WindowTitleShadow); } Color menu_stripe() const { return color(ColorRole::MenuStripe); } Color menu_base() const { return color(ColorRole::MenuBase); } Color menu_base_text() const { return color(ColorRole::MenuBaseText); } @@ -117,9 +120,15 @@ public: Color syntax_preprocessor_statement() const { return color(ColorRole::SyntaxPreprocessorStatement); } Color syntax_preprocessor_value() const { return color(ColorRole::SyntaxPreprocessorValue); } + int window_title_height() const { return metric(MetricRole::TitleHeight); } + int window_title_button_width() const { return metric(MetricRole::TitleButtonWidth); } + int window_title_button_height() const { return metric(MetricRole::TitleButtonHeight); } + Color color(ColorRole role) const { return m_impl->color(role); } + int metric(MetricRole role) const { return m_impl->metric(role); } void set_color(ColorRole, Color); + void set_metric(MetricRole, int); const SystemTheme& theme() const { return m_impl->theme(); } diff --git a/Libraries/LibGfx/SystemTheme.cpp b/Libraries/LibGfx/SystemTheme.cpp index 05aab14692..f52010bef8 100644 --- a/Libraries/LibGfx/SystemTheme.cpp +++ b/Libraries/LibGfx/SystemTheme.cpp @@ -67,6 +67,24 @@ RefPtr load_system_theme(const String& path) return color.value(); }; + auto get_metric = [&](auto& name, auto role) { + int metric = file->read_num_entry("Metrics", name, -1); + if (metric == -1) { + switch (role) { + case (int)MetricRole::TitleHeight: + return 19; + case (int)MetricRole::TitleButtonHeight: + return 15; + case (int)MetricRole::TitleButtonWidth: + return 15; + default: + dbg() << "Metric " << name << " has no fallback value!"; + return 16; + } + } + return metric; + }; + #define DO_COLOR(x) \ data->color[(int)ColorRole::x] = get_color(#x) @@ -98,6 +116,8 @@ RefPtr load_system_theme(const String& path) DO_COLOR(HighlightWindowBorder1); DO_COLOR(HighlightWindowBorder2); DO_COLOR(HighlightWindowTitle); + DO_COLOR(WindowTitleShadow); + DO_COLOR(WindowTitleStripes); DO_COLOR(MenuStripe); DO_COLOR(MenuBase); DO_COLOR(MenuBaseText); @@ -126,6 +146,13 @@ RefPtr load_system_theme(const String& path) DO_COLOR(SyntaxPreprocessorStatement); DO_COLOR(SyntaxPreprocessorValue); +#define DO_METRIC(x) \ + data->metric[(int)MetricRole::x] = get_metric(#x, (int)MetricRole::x) + + DO_METRIC(TitleHeight); + DO_METRIC(TitleButtonWidth); + DO_METRIC(TitleButtonHeight); + buffer->seal(); buffer->share_globally(); diff --git a/Libraries/LibGfx/SystemTheme.h b/Libraries/LibGfx/SystemTheme.h index 28e5dfb255..a2fa7ea4ea 100644 --- a/Libraries/LibGfx/SystemTheme.h +++ b/Libraries/LibGfx/SystemTheme.h @@ -47,6 +47,8 @@ enum class ColorRole { HighlightWindowBorder1, HighlightWindowBorder2, HighlightWindowTitle, + WindowTitleShadow, + WindowTitleStripes, MenuStripe, MenuBase, MenuBaseText, @@ -95,8 +97,17 @@ enum class ColorRole { DisabledText = ThreedShadow1, }; +enum class MetricRole { + NoRole, + TitleHeight, + TitleButtonWidth, + TitleButtonHeight, + __Count, +}; + struct SystemTheme { Color color[(int)ColorRole::__Count]; + int metric[(int)MetricRole::__Count]; }; const SystemTheme& current_system_theme(); diff --git a/Services/WindowServer/WindowFrame.cpp b/Services/WindowServer/WindowFrame.cpp index ce3e4d1b66..746556a759 100644 --- a/Services/WindowServer/WindowFrame.cpp +++ b/Services/WindowServer/WindowFrame.cpp @@ -39,8 +39,6 @@ namespace WindowServer { -static const int window_titlebar_height = 19; - static const char* s_close_button_bitmap_data = { "## ##" "### ###" @@ -153,6 +151,7 @@ void WindowFrame::did_set_maximized(Badge, bool maximized) Gfx::IntRect WindowFrame::title_bar_rect() const { + auto window_titlebar_height = WindowManager::the().palette().window_title_height(); if (m_window.type() == WindowType::Notification) return { m_window.width() + 3, 3, window_titlebar_height, m_window.height() }; return { 4, 4, m_window.width(), window_titlebar_height }; @@ -207,8 +206,8 @@ void WindowFrame::paint_notification_frame(Gfx::Painter& painter) int stripe_top = m_buttons.last().relative_rect().bottom() + 4; int stripe_bottom = m_window.height() - 3; if (stripe_top && stripe_bottom && stripe_top < stripe_bottom) { - for (int i = 2; i <= window_titlebar_height - 2; i += 2) { - painter.draw_line({ titlebar_rect.x() + i, stripe_top }, { titlebar_rect.x() + i, stripe_bottom }, palette.active_window_border1()); + for (int i = 2; i <= palette.window_title_height() - 2; i += 2) { + painter.draw_line({ titlebar_rect.x() + i, stripe_top }, { titlebar_rect.x() + i, stripe_bottom }, palette.window_title_stripes()); } } } @@ -241,7 +240,7 @@ void WindowFrame::paint_normal_frame(Gfx::Painter& painter) int stripe_right = leftmost_button_rect.left() - 3; if (stripe_left && stripe_right && stripe_left < stripe_right) { for (int i = 2; i <= titlebar_inner_rect.height() - 2; i += 2) { - painter.draw_line({ stripe_left, titlebar_inner_rect.y() + i }, { stripe_right, titlebar_inner_rect.y() + i }, border_color); + painter.draw_line({ stripe_left, titlebar_inner_rect.y() + i }, { stripe_right, titlebar_inner_rect.y() + i }, palette.window_title_stripes()); } } @@ -259,7 +258,7 @@ void WindowFrame::paint_normal_frame(Gfx::Painter& painter) auto clipped_title_rect = titlebar_title_rect; clipped_title_rect.set_width(stripe_right - clipped_title_rect.x()); if (!clipped_title_rect.is_empty()) { - painter.draw_text(clipped_title_rect.translated(1, 2), title_text, wm.window_title_font(), Gfx::TextAlignment::CenterLeft, border_color.darkened(0.4), Gfx::TextElision::Right); + painter.draw_text(clipped_title_rect.translated(1, 2), title_text, wm.window_title_font(), Gfx::TextAlignment::CenterLeft, palette.window_title_shadow(), Gfx::TextElision::Right); // FIXME: The translated(0, 1) wouldn't be necessary if we could center text based on its baseline. painter.draw_text(clipped_title_rect.translated(0, 1), title_text, wm.window_title_font(), Gfx::TextAlignment::CenterLeft, title_color, Gfx::TextElision::Right); } @@ -293,6 +292,7 @@ static Gfx::IntRect frame_rect_for_window(Window& window, const Gfx::IntRect& re return rect; auto type = window.type(); + auto window_titlebar_height = WindowManager::the().palette().window_title_height(); switch (type) { case WindowType::Normal: @@ -331,8 +331,9 @@ void WindowFrame::invalidate_title_bar() void WindowFrame::notify_window_rect_changed(const Gfx::IntRect& old_rect, const Gfx::IntRect& new_rect) { - int window_button_width = 15; - int window_button_height = 15; + auto palette = WindowManager::the().palette(); + int window_button_width = palette.window_title_button_width(); + int window_button_height = palette.window_title_button_height(); int pos; if (m_window.type() == WindowType::Notification) pos = title_bar_rect().top() + 2; @@ -341,10 +342,11 @@ void WindowFrame::notify_window_rect_changed(const Gfx::IntRect& old_rect, const for (auto& button : m_buttons) { if (m_window.type() == WindowType::Notification) { - Gfx::IntRect rect { 0, pos, window_button_width, window_button_height }; + // The button height & width have to be equal or it leaks out of its area + Gfx::IntRect rect { 0, pos, window_button_height, window_button_height }; rect.center_horizontally_within(title_bar_rect()); button.set_relative_rect(rect); - pos += window_button_width; + pos += window_button_height; } else { pos -= window_button_width; Gfx::IntRect rect { pos, 0, window_button_width, window_button_height };