/* * Copyright (c) 2020, Andreas Kling * Copyright (c) 2021, Sam Atkins * Copyright (c) 2021, Antonio Di Stefano * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include namespace GUI { AbstractThemePreview::AbstractThemePreview(Gfx::Palette const& preview_palette) : m_preview_palette(preview_palette) { m_active_window_icon = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/window.png").release_value_but_fixme_should_propagate_errors(); m_inactive_window_icon = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/window.png").release_value_but_fixme_should_propagate_errors(); m_default_close_bitmap = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/window-close.png").release_value_but_fixme_should_propagate_errors(); m_default_maximize_bitmap = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/upward-triangle.png").release_value_but_fixme_should_propagate_errors(); m_default_minimize_bitmap = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/downward-triangle.png").release_value_but_fixme_should_propagate_errors(); VERIFY(m_active_window_icon); VERIFY(m_inactive_window_icon); VERIFY(m_default_close_bitmap); VERIFY(m_default_maximize_bitmap); VERIFY(m_default_minimize_bitmap); load_theme_bitmaps(); } void AbstractThemePreview::load_theme_bitmaps() { auto load_bitmap = [](String const& path, String& last_path, RefPtr& bitmap) { if (path.is_empty()) { last_path = String::empty(); bitmap = nullptr; } else if (last_path != path) { auto bitmap_or_error = Gfx::Bitmap::try_load_from_file(path); if (bitmap_or_error.is_error()) { last_path = String::empty(); bitmap = nullptr; } else { last_path = path; bitmap = bitmap_or_error.release_value(); } } }; auto buttons_path = m_preview_palette.title_button_icons_path(); load_bitmap(LexicalPath::absolute_path(buttons_path, "window-close.png"), m_last_close_path, m_close_bitmap); load_bitmap(LexicalPath::absolute_path(buttons_path, "window-maximize.png"), m_last_maximize_path, m_maximize_bitmap); load_bitmap(LexicalPath::absolute_path(buttons_path, "window-minimize.png"), m_last_minimize_path, m_minimize_bitmap); load_bitmap(m_preview_palette.active_window_shadow_path(), m_last_active_window_shadow_path, m_active_window_shadow); load_bitmap(m_preview_palette.inactive_window_shadow_path(), m_last_inactive_window_shadow_path, m_inactive_window_shadow); load_bitmap(m_preview_palette.menu_shadow_path(), m_last_menu_shadow_path, m_menu_shadow); load_bitmap(m_preview_palette.taskbar_shadow_path(), m_last_taskbar_shadow_path, m_taskbar_shadow); load_bitmap(m_preview_palette.tooltip_shadow_path(), m_last_tooltip_shadow_path, m_tooltip_shadow); } void AbstractThemePreview::set_preview_palette(Gfx::Palette const& palette) { m_preview_palette = palette; if (on_palette_change) { on_palette_change(); } load_theme_bitmaps(); update(); } void AbstractThemePreview::set_theme_from_file(Core::File& file) { auto config_file = Core::ConfigFile::open(file.filename(), file.leak_fd()).release_value_but_fixme_should_propagate_errors(); auto theme = Gfx::load_system_theme(config_file); VERIFY(theme.is_valid()); m_preview_palette = Gfx::Palette(Gfx::PaletteImpl::create_with_anonymous_buffer(theme)); set_preview_palette(m_preview_palette); if (on_theme_load_from_file) on_theme_load_from_file(file.filename()); } void AbstractThemePreview::paint_window(StringView title, Gfx::IntRect const& rect, Gfx::WindowTheme::WindowState state, Gfx::Bitmap const& icon, int button_count) { GUI::Painter painter(*this); struct Button { Gfx::IntRect rect; RefPtr bitmap; }; int window_button_width = m_preview_palette.window_title_button_width(); int window_button_height = m_preview_palette.window_title_button_height(); auto titlebar_text_rect = Gfx::WindowTheme::current().titlebar_text_rect(Gfx::WindowTheme::WindowType::Normal, rect, m_preview_palette); int pos = titlebar_text_rect.right() + 1; Array possible_buttons { Button { {}, m_close_bitmap.is_null() ? m_default_close_bitmap : m_close_bitmap }, Button { {}, m_maximize_bitmap.is_null() ? m_default_maximize_bitmap : m_maximize_bitmap }, Button { {}, m_minimize_bitmap.is_null() ? m_default_minimize_bitmap : m_minimize_bitmap } }; auto buttons = possible_buttons.span().trim(button_count); for (auto& button : buttons) { pos -= window_button_width; Gfx::IntRect button_rect { pos, 0, window_button_width, window_button_height }; button_rect.center_vertically_within(titlebar_text_rect); button.rect = button_rect; } auto frame_rect = Gfx::WindowTheme::current().frame_rect_for_window(Gfx::WindowTheme::WindowType::Normal, rect, m_preview_palette, 0); auto paint_shadow = [](Gfx::Painter& painter, Gfx::IntRect& frame_rect, Gfx::Bitmap const& shadow_bitmap) { auto total_shadow_size = shadow_bitmap.height(); auto shadow_rect = frame_rect.inflated(total_shadow_size, total_shadow_size); Gfx::StylePainter::paint_simple_rect_shadow(painter, shadow_rect, shadow_bitmap); }; if ((state == Gfx::WindowTheme::WindowState::Active || state == Gfx::WindowTheme::WindowState::Highlighted) && m_active_window_shadow) { paint_shadow(painter, frame_rect, *m_active_window_shadow); } else if (state == Gfx::WindowTheme::WindowState::Inactive && m_inactive_window_shadow) { paint_shadow(painter, frame_rect, *m_inactive_window_shadow); } Gfx::PainterStateSaver saver(painter); painter.translate(frame_rect.location()); Gfx::WindowTheme::current().paint_normal_frame(painter, state, rect, title, icon, m_preview_palette, buttons.last().rect, 0, false); painter.fill_rect(rect.translated(-frame_rect.location()), m_preview_palette.color(Gfx::ColorRole::Background)); for (auto& button : buttons) { Gfx::StylePainter::paint_button(painter, button.rect, m_preview_palette, Gfx::ButtonStyle::Normal, false); auto bitmap_rect = button.bitmap->rect().centered_within(button.rect); painter.blit(bitmap_rect.location(), *button.bitmap, button.bitmap->rect()); } } void AbstractThemePreview::paint_event(GUI::PaintEvent& event) { GUI::Frame::paint_event(event); GUI::Painter painter(*this); painter.add_clip_rect(event.rect()); painter.add_clip_rect(frame_inner_rect()); painter.fill_rect(frame_inner_rect(), m_preview_palette.desktop_background()); paint_preview(event); } }