Serenity Operating System
at master 355 lines 12 kB view raw
1/* 2 * Copyright (c) 2021, the SerenityOS developers. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "Overlays.h" 8#include "Compositor.h" 9#include "WindowManager.h" 10#include <LibGfx/StylePainter.h> 11 12namespace WindowServer { 13 14Overlay::~Overlay() 15{ 16 Compositor::the().remove_overlay(*this); 17} 18 19bool Overlay::invalidate() 20{ 21 if (m_invalidated) 22 return false; 23 m_invalidated = true; 24 // m_current_rect should only get updated by recompute_overlay_rects() 25 if (!m_current_rect.is_empty()) 26 Compositor::the().invalidate_screen(m_current_rect); 27 return true; 28} 29 30void Overlay::set_enabled(bool enable) 31{ 32 if (is_enabled() == enable) 33 return; 34 35 if (enable) 36 Compositor::the().add_overlay(*this); 37 else 38 Compositor::the().remove_overlay(*this); 39} 40 41void Overlay::set_rect(Gfx::IntRect const& rect) 42{ 43 if (m_rect == rect) 44 return; 45 auto previous_rect = m_rect; 46 m_rect = rect; 47 invalidate(); 48 if (is_enabled()) 49 Compositor::the().overlay_rects_changed(); 50 rect_changed(previous_rect); 51} 52 53BitmapOverlay::BitmapOverlay() 54{ 55 clear_bitmaps(); 56} 57 58void BitmapOverlay::rect_changed(Gfx::IntRect const& previous_rect) 59{ 60 if (rect().size() != previous_rect.size()) 61 clear_bitmaps(); 62 Overlay::rect_changed(previous_rect); 63} 64 65void BitmapOverlay::clear_bitmaps() 66{ 67 m_bitmaps = MultiScaleBitmaps::create_empty(); 68} 69 70void BitmapOverlay::render(Gfx::Painter& painter, Screen const& screen) 71{ 72 auto scale_factor = screen.scale_factor(); 73 auto* bitmap = m_bitmaps->find_bitmap(scale_factor); 74 if (!bitmap) { 75 auto new_bitmap = create_bitmap(scale_factor); 76 if (!new_bitmap) 77 return; 78 bitmap = new_bitmap.ptr(); 79 m_bitmaps->add_bitmap(scale_factor, new_bitmap.release_nonnull()); 80 } 81 82 painter.blit({}, *bitmap, bitmap->rect()); 83} 84 85RectangularOverlay::RectangularOverlay() 86{ 87 clear_bitmaps(); 88} 89 90void RectangularOverlay::rect_changed(Gfx::IntRect const& previous_rect) 91{ 92 if (rect().size() != previous_rect.size()) 93 clear_bitmaps(); 94} 95 96void RectangularOverlay::clear_bitmaps() 97{ 98 m_rendered_bitmaps = MultiScaleBitmaps::create_empty(); 99} 100 101void RectangularOverlay::render(Gfx::Painter& painter, Screen const& screen) 102{ 103 if (m_content_invalidated) { 104 clear_bitmaps(); 105 m_content_invalidated = false; 106 } 107 auto scale_factor = screen.scale_factor(); 108 auto* bitmap = m_rendered_bitmaps->find_bitmap(scale_factor); 109 if (!bitmap) { 110 auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, rect().size(), scale_factor); 111 if (bitmap_or_error.is_error()) 112 return; 113 auto new_bitmap = bitmap_or_error.release_value_but_fixme_should_propagate_errors(); 114 bitmap = new_bitmap.ptr(); 115 116 Gfx::Painter bitmap_painter(*new_bitmap); 117 if (auto* shadow_bitmap = WindowManager::the().overlay_rect_shadow()) { 118 Gfx::StylePainter::paint_simple_rect_shadow(bitmap_painter, new_bitmap->rect(), shadow_bitmap->bitmap(scale_factor), true, true); 119 } else { 120 bitmap_painter.fill_rect(new_bitmap->rect(), Color(Color::Black).with_alpha(0xcc)); 121 } 122 render_overlay_bitmap(bitmap_painter); 123 m_rendered_bitmaps->add_bitmap(scale_factor, move(new_bitmap)); 124 } 125 126 painter.blit({}, *bitmap, bitmap->rect()); 127} 128 129Gfx::IntRect RectangularOverlay::calculate_frame_rect(Gfx::IntRect const& rect) 130{ 131 if (auto* shadow_bitmap = WindowManager::the().overlay_rect_shadow()) { 132 Gfx::IntSize size; 133 int base_size = shadow_bitmap->default_bitmap().height() / 2; 134 size = { base_size, base_size }; 135 return rect.inflated(2 * base_size, 2 * base_size); 136 } 137 return rect.inflated(2 * default_frame_thickness, 2 * default_frame_thickness); 138} 139 140void RectangularOverlay::set_content_rect(Gfx::IntRect const& rect) 141{ 142 set_rect(calculate_frame_rect(rect)); 143} 144 145void RectangularOverlay::invalidate_content() 146{ 147 m_content_invalidated = true; 148 invalidate(); 149} 150 151Gfx::Font const* ScreenNumberOverlay::s_font { nullptr }; 152 153ScreenNumberOverlay::ScreenNumberOverlay(Screen& screen) 154 : m_screen(screen) 155{ 156 if (!s_font) 157 pick_font(); 158 159 Gfx::IntRect rect { 160 default_offset, 161 default_offset, 162 default_size, 163 default_size 164 }; 165 rect.translate_by(screen.rect().location()); 166 set_rect(rect); 167} 168 169void ScreenNumberOverlay::pick_font() 170{ 171 auto screen_number_content_rect_size = calculate_content_rect_for_screen(Screen::main()).size(); 172 auto& font_database = Gfx::FontDatabase::the(); 173 auto& default_font = WindowManager::the().font(); 174 DeprecatedString best_font_name; 175 int best_font_size = -1; 176 font_database.for_each_font([&](Gfx::Font const& font) { 177 // TODO: instead of picking *any* font we should probably compare font.name() 178 // with default_font.name(). But the default font currently does not provide larger sizes 179 auto size = font.pixel_size_rounded_up(); 180 if (size * 2 <= screen_number_content_rect_size.height() && size > best_font_size) { 181 for (unsigned ch = '0'; ch <= '9'; ch++) { 182 if (!font.contains_glyph(ch)) { 183 // Skip this font, it doesn't have glyphs for digits 184 return; 185 } 186 } 187 best_font_name = font.qualified_name(); 188 best_font_size = size; 189 } 190 }); 191 192 if (auto best_font = font_database.get_by_name(best_font_name)) { 193 s_font = best_font.ptr(); 194 } else { 195 s_font = &default_font; 196 } 197 198 Compositor::the().for_each_overlay([&](auto& overlay) { 199 if (overlay.zorder() == ZOrder::ScreenNumber) 200 overlay.invalidate(); 201 return IterationDecision::Continue; 202 }); 203} 204 205Gfx::Font const& ScreenNumberOverlay::font() 206{ 207 if (!s_font) { 208 pick_font(); 209 VERIFY(s_font); 210 } 211 return *s_font; 212} 213 214void ScreenNumberOverlay::render_overlay_bitmap(Gfx::Painter& painter) 215{ 216 painter.draw_text(Gfx::IntRect { {}, rect().size() }, DeprecatedString::formatted("{}", m_screen.index() + 1), font(), Gfx::TextAlignment::Center, Color::White); 217} 218 219Gfx::IntRect ScreenNumberOverlay::calculate_content_rect_for_screen(Screen& screen) 220{ 221 Gfx::IntRect content_rect { 222 screen.rect().location().translated(default_offset, default_offset), 223 { default_size, default_size } 224 }; 225 226 return calculate_frame_rect(content_rect); 227} 228 229WindowGeometryOverlay::WindowGeometryOverlay(Window& window) 230 : m_window(window) 231{ 232 update_rect(); 233} 234 235void WindowGeometryOverlay::update_rect() 236{ 237 if (auto* window = m_window.ptr()) { 238 auto& wm = WindowManager::the(); 239 if (!window->size_increment().is_empty()) { 240 int width_steps = (window->width() - window->base_size().width()) / window->size_increment().width(); 241 int height_steps = (window->height() - window->base_size().height()) / window->size_increment().height(); 242 m_label = DeprecatedString::formatted("{} ({}x{})", window->rect(), width_steps, height_steps); 243 } else { 244 m_label = window->rect().to_deprecated_string(); 245 } 246 m_label_rect = Gfx::IntRect { 0, 0, static_cast<int>(ceilf(wm.font().width(m_label))) + 16, wm.font().pixel_size_rounded_up() + 10 }; 247 248 auto rect = calculate_frame_rect(m_label_rect).centered_within(window->frame().rect()); 249 auto desktop_rect = wm.desktop_rect(ScreenInput::the().cursor_location_screen()); 250 if (rect.left() < desktop_rect.left()) 251 rect.set_left(desktop_rect.left()); 252 if (rect.top() < desktop_rect.top()) 253 rect.set_top(desktop_rect.top()); 254 if (rect.right() > desktop_rect.right()) 255 rect.set_right_without_resize(desktop_rect.right()); 256 if (rect.bottom() > desktop_rect.bottom()) 257 rect.set_bottom_without_resize(desktop_rect.bottom()); 258 259 set_rect(rect); 260 } else { 261 set_enabled(false); 262 } 263} 264 265void WindowGeometryOverlay::render_overlay_bitmap(Gfx::Painter& painter) 266{ 267 painter.draw_text(Gfx::IntRect { {}, rect().size() }, m_label, WindowManager::the().font(), Gfx::TextAlignment::Center, Color::White); 268} 269 270void WindowGeometryOverlay::window_rect_changed() 271{ 272 update_rect(); 273 invalidate_content(); 274} 275 276DndOverlay::DndOverlay(DeprecatedString const& text, Gfx::Bitmap const* bitmap) 277 : m_bitmap(bitmap) 278 , m_text(text) 279{ 280 update_rect(); 281} 282 283Gfx::Font const& DndOverlay::font() 284{ 285 return WindowManager::the().font(); 286} 287 288void DndOverlay::update_rect() 289{ 290 int bitmap_width = m_bitmap ? m_bitmap->width() : 0; 291 int bitmap_height = m_bitmap ? m_bitmap->height() : 0; 292 auto& font = this->font(); 293 int width = font.width(m_text) + bitmap_width; 294 int height = max(font.pixel_size_rounded_up(), bitmap_height); 295 auto location = ScreenInput::the().cursor_location().translated(8, 8); 296 set_rect(Gfx::IntRect(location, { width, height }).inflated(16, 8)); 297} 298 299RefPtr<Gfx::Bitmap> DndOverlay::create_bitmap(int scale_factor) 300{ 301 auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, rect().size(), scale_factor); 302 if (bitmap_or_error.is_error()) 303 return {}; 304 auto new_bitmap = bitmap_or_error.release_value_but_fixme_should_propagate_errors(); 305 306 auto& wm = WindowManager::the(); 307 Gfx::Painter bitmap_painter(*new_bitmap); 308 auto bitmap_rect = new_bitmap->rect(); 309 bitmap_painter.fill_rect(bitmap_rect, wm.palette().selection().with_alpha(200)); 310 bitmap_painter.draw_rect(bitmap_rect, wm.palette().selection()); 311 if (!m_text.is_empty()) { 312 auto text_rect = bitmap_rect; 313 if (m_bitmap) 314 text_rect.translate_by(m_bitmap->width() + 8, 0); 315 bitmap_painter.draw_text(text_rect, m_text, Gfx::TextAlignment::CenterLeft, wm.palette().selection_text()); 316 } 317 if (m_bitmap) 318 bitmap_painter.blit(bitmap_rect.top_left().translated(4, 4), *m_bitmap, m_bitmap->rect()); 319 return new_bitmap; 320} 321 322void WindowStackSwitchOverlay::render_overlay_bitmap(Gfx::Painter& painter) 323{ 324 // We should come up with a more elegant way to get the content rectangle 325 auto content_rect = Gfx::IntRect({}, m_content_size).centered_within({ {}, rect().size() }); 326 auto active_color = WindowManager::the().palette().selection(); 327 auto inactive_color = WindowManager::the().palette().window().darkened(0.9f); 328 for (int y = 0; y < m_rows; y++) { 329 for (int x = 0; x < m_columns; x++) { 330 Gfx::IntRect rect { 331 content_rect.left() + x * (default_screen_rect_width + default_screen_rect_padding), 332 content_rect.top() + y * (default_screen_rect_height + default_screen_rect_padding), 333 default_screen_rect_width, 334 default_screen_rect_height 335 }; 336 bool is_target = y == m_target_row && x == m_target_column; 337 painter.fill_rect(rect, is_target ? active_color : inactive_color); 338 } 339 } 340} 341 342WindowStackSwitchOverlay::WindowStackSwitchOverlay(Screen& screen, WindowStack& target_window_stack) 343 : m_rows((int)WindowManager::the().window_stack_rows()) 344 , m_columns((int)WindowManager::the().window_stack_columns()) 345 , m_target_row((int)target_window_stack.row()) 346 , m_target_column((int)target_window_stack.column()) 347{ 348 m_content_size = { 349 m_columns * (default_screen_rect_width + default_screen_rect_padding) - default_screen_rect_padding, 350 m_rows * (default_screen_rect_height + default_screen_rect_padding) - default_screen_rect_padding, 351 }; 352 set_rect(calculate_frame_rect(Gfx::IntRect({}, m_content_size).inflated(2 * default_screen_rect_margin, 2 * default_screen_rect_margin)).centered_within(screen.rect())); 353} 354 355}