mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:12:43 +00:00 
			
		
		
		
	WindowServer: Implement simple window shadows
This implements simple window shadows around most windows, including tooltips. Because this method uses a bitmap for the shadow bits, it is limited to rectangular window frames. For non-rectangular window frames we'll need to implement a more sophisticated algorithm.
This commit is contained in:
		
							parent
							
								
									72fdab7bfb
								
							
						
					
					
						commit
						0ce4b9d7db
					
				
					 14 changed files with 234 additions and 70 deletions
				
			
		
							
								
								
									
										
											BIN
										
									
								
								Base/res/icons/themes/Default/window-shadow.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Base/res/icons/themes/Default/window-shadow.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 6 KiB | 
|  | @ -71,3 +71,7 @@ TooltipText=black | ||||||
| TitleHeight=19 | TitleHeight=19 | ||||||
| TitleButtonWidth=15 | TitleButtonWidth=15 | ||||||
| TitleButtonHeight=15 | TitleButtonHeight=15 | ||||||
|  | 
 | ||||||
|  | [Paths] | ||||||
|  | WindowShadow=/res/icons/themes/Default/window-shadow.png | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -47,6 +47,7 @@ public: | ||||||
|     virtual IntRect frame_rect_for_window(WindowType, const IntRect& window_rect, const Palette&) const override; |     virtual IntRect frame_rect_for_window(WindowType, const IntRect& window_rect, const Palette&) const override; | ||||||
| 
 | 
 | ||||||
|     virtual Vector<IntRect> layout_buttons(WindowType, const IntRect& window_rect, const Palette&, size_t buttons) const override; |     virtual Vector<IntRect> layout_buttons(WindowType, const IntRect& window_rect, const Palette&, size_t buttons) const override; | ||||||
|  |     virtual bool is_simple_rect_frame() const override { return true; } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     struct FrameColors { |     struct FrameColors { | ||||||
|  |  | ||||||
|  | @ -735,13 +735,13 @@ void Painter::blit_with_alpha(const IntPoint& position, const Gfx::Bitmap& sourc | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Painter::blit(const IntPoint& position, const Gfx::Bitmap& source, const IntRect& a_src_rect, float opacity) | void Painter::blit(const IntPoint& position, const Gfx::Bitmap& source, const IntRect& a_src_rect, float opacity, bool apply_alpha) | ||||||
| { | { | ||||||
|     ASSERT(scale() >= source.scale() && "painter doesn't support downsampling scale factors"); |     ASSERT(scale() >= source.scale() && "painter doesn't support downsampling scale factors"); | ||||||
| 
 | 
 | ||||||
|     if (opacity < 1.0f) |     if (opacity < 1.0f) | ||||||
|         return blit_with_opacity(position, source, a_src_rect, opacity); |         return blit_with_opacity(position, source, a_src_rect, opacity); | ||||||
|     if (source.has_alpha_channel()) |     if (source.has_alpha_channel() && apply_alpha) | ||||||
|         return blit_with_alpha(position, source, a_src_rect); |         return blit_with_alpha(position, source, a_src_rect); | ||||||
| 
 | 
 | ||||||
|     auto safe_src_rect = a_src_rect.intersected(source.rect()); |     auto safe_src_rect = a_src_rect.intersected(source.rect()); | ||||||
|  |  | ||||||
|  | @ -70,7 +70,7 @@ public: | ||||||
|     void draw_line(const IntPoint&, const IntPoint&, Color, int thickness = 1, LineStyle style = LineStyle::Solid); |     void draw_line(const IntPoint&, const IntPoint&, Color, int thickness = 1, LineStyle style = LineStyle::Solid); | ||||||
|     void draw_quadratic_bezier_curve(const IntPoint& control_point, const IntPoint&, const IntPoint&, Color, int thickness = 1, LineStyle style = LineStyle::Solid); |     void draw_quadratic_bezier_curve(const IntPoint& control_point, const IntPoint&, const IntPoint&, Color, int thickness = 1, LineStyle style = LineStyle::Solid); | ||||||
|     void draw_elliptical_arc(const IntPoint& p1, const IntPoint& p2, const IntPoint& center, const FloatPoint& radii, float x_axis_rotation, float theta_1, float theta_delta, Color, int thickness = 1, LineStyle style = LineStyle::Solid); |     void draw_elliptical_arc(const IntPoint& p1, const IntPoint& p2, const IntPoint& center, const FloatPoint& radii, float x_axis_rotation, float theta_1, float theta_delta, Color, int thickness = 1, LineStyle style = LineStyle::Solid); | ||||||
|     void blit(const IntPoint&, const Gfx::Bitmap&, const IntRect& src_rect, float opacity = 1.0f); |     void blit(const IntPoint&, const Gfx::Bitmap&, const IntRect& src_rect, float opacity = 1.0f, bool apply_alpha = true); | ||||||
|     void blit_dimmed(const IntPoint&, const Gfx::Bitmap&, const IntRect& src_rect); |     void blit_dimmed(const IntPoint&, const Gfx::Bitmap&, const IntRect& src_rect); | ||||||
|     void blit_brightened(const IntPoint&, const Gfx::Bitmap&, const IntRect& src_rect); |     void blit_brightened(const IntPoint&, const Gfx::Bitmap&, const IntRect& src_rect); | ||||||
|     void blit_filtered(const IntPoint&, const Gfx::Bitmap&, const IntRect& src_rect, Function<Color(Color)>); |     void blit_filtered(const IntPoint&, const Gfx::Bitmap&, const IntRect& src_rect, Function<Color(Color)>); | ||||||
|  |  | ||||||
|  | @ -140,6 +140,7 @@ public: | ||||||
|     int window_title_button_height() const { return metric(MetricRole::TitleButtonHeight); } |     int window_title_button_height() const { return metric(MetricRole::TitleButtonHeight); } | ||||||
| 
 | 
 | ||||||
|     String title_button_icons_path() const { return path(PathRole::TitleButtonIcons); } |     String title_button_icons_path() const { return path(PathRole::TitleButtonIcons); } | ||||||
|  |     String window_shadow_path() const { return path(PathRole::WindowShadow); } | ||||||
| 
 | 
 | ||||||
|     Color color(ColorRole role) const { return m_impl->color(role); } |     Color color(ColorRole role) const { return m_impl->color(role); } | ||||||
|     int metric(MetricRole role) const { return m_impl->metric(role); } |     int metric(MetricRole role) const { return m_impl->metric(role); } | ||||||
|  |  | ||||||
|  | @ -119,6 +119,7 @@ Core::AnonymousBuffer load_system_theme(const String& path) | ||||||
|     } while (0) |     } while (0) | ||||||
| 
 | 
 | ||||||
|     DO_PATH(TitleButtonIcons); |     DO_PATH(TitleButtonIcons); | ||||||
|  |     DO_PATH(WindowShadow); | ||||||
| 
 | 
 | ||||||
|     return buffer; |     return buffer; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -144,6 +144,7 @@ enum class MetricRole { | ||||||
| enum class PathRole { | enum class PathRole { | ||||||
|     NoRole, |     NoRole, | ||||||
|     TitleButtonIcons, |     TitleButtonIcons, | ||||||
|  |     WindowShadow, | ||||||
|     __Count, |     __Count, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -61,6 +61,7 @@ public: | ||||||
|     virtual IntRect frame_rect_for_window(WindowType, const IntRect& window_rect, const Palette&) const = 0; |     virtual IntRect frame_rect_for_window(WindowType, const IntRect& window_rect, const Palette&) const = 0; | ||||||
| 
 | 
 | ||||||
|     virtual Vector<IntRect> layout_buttons(WindowType, const IntRect& window_rect, const Palette&, size_t buttons) const = 0; |     virtual Vector<IntRect> layout_buttons(WindowType, const IntRect& window_rect, const Palette&, size_t buttons) const = 0; | ||||||
|  |     virtual bool is_simple_rect_frame() const = 0; | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     WindowTheme() { } |     WindowTheme() { } | ||||||
|  |  | ||||||
|  | @ -151,7 +151,7 @@ void Compositor::compose() | ||||||
| 
 | 
 | ||||||
|     // Mark window regions as dirty that need to be re-rendered
 |     // Mark window regions as dirty that need to be re-rendered
 | ||||||
|     wm.for_each_visible_window_from_back_to_front([&](Window& window) { |     wm.for_each_visible_window_from_back_to_front([&](Window& window) { | ||||||
|         auto frame_rect = window.frame().rect(); |         auto frame_rect = window.frame().render_rect(); | ||||||
|         for (auto& dirty_rect : dirty_screen_rects.rects()) { |         for (auto& dirty_rect : dirty_screen_rects.rects()) { | ||||||
|             auto invalidate_rect = dirty_rect.intersected(frame_rect); |             auto invalidate_rect = dirty_rect.intersected(frame_rect); | ||||||
|             if (!invalidate_rect.is_empty()) { |             if (!invalidate_rect.is_empty()) { | ||||||
|  | @ -173,12 +173,12 @@ void Compositor::compose() | ||||||
|         if (transparency_rects.is_empty()) |         if (transparency_rects.is_empty()) | ||||||
|             return IterationDecision::Continue; |             return IterationDecision::Continue; | ||||||
| 
 | 
 | ||||||
|         auto frame_rect = window.frame().rect(); |         auto frame_rect = window.frame().render_rect(); | ||||||
|         auto& dirty_rects = window.dirty_rects(); |         auto& dirty_rects = window.dirty_rects(); | ||||||
|         wm.for_each_visible_window_from_back_to_front([&](Window& w) { |         wm.for_each_visible_window_from_back_to_front([&](Window& w) { | ||||||
|             if (&w == &window) |             if (&w == &window) | ||||||
|                 return IterationDecision::Continue; |                 return IterationDecision::Continue; | ||||||
|             auto frame_rect2 = w.frame().rect(); |             auto frame_rect2 = w.frame().render_rect(); | ||||||
|             if (!frame_rect2.intersects(frame_rect)) |             if (!frame_rect2.intersects(frame_rect)) | ||||||
|                 return IterationDecision::Continue; |                 return IterationDecision::Continue; | ||||||
|             transparency_rects.for_each_intersected(w.dirty_rects(), [&](const Gfx::IntRect& intersected_dirty) { |             transparency_rects.for_each_intersected(w.dirty_rects(), [&](const Gfx::IntRect& intersected_dirty) { | ||||||
|  | @ -206,6 +206,9 @@ void Compositor::compose() | ||||||
|     auto cursor_rect = current_cursor_rect(); |     auto cursor_rect = current_cursor_rect(); | ||||||
|     bool need_to_draw_cursor = false; |     bool need_to_draw_cursor = false; | ||||||
| 
 | 
 | ||||||
|  |     auto back_painter = *m_back_painter; | ||||||
|  |     auto temp_painter = *m_temp_painter; | ||||||
|  | 
 | ||||||
|     auto check_restore_cursor_back = [&](const Gfx::IntRect& rect) { |     auto check_restore_cursor_back = [&](const Gfx::IntRect& rect) { | ||||||
|         if (!need_to_draw_cursor && rect.intersects(cursor_rect)) { |         if (!need_to_draw_cursor && rect.intersects(cursor_rect)) { | ||||||
|             // Restore what's behind the cursor if anything touches the area of the cursor
 |             // Restore what's behind the cursor if anything touches the area of the cursor
 | ||||||
|  | @ -225,26 +228,18 @@ void Compositor::compose() | ||||||
|     auto prepare_transparency_rect = [&](const Gfx::IntRect& rect) { |     auto prepare_transparency_rect = [&](const Gfx::IntRect& rect) { | ||||||
|         dbgln_if(COMPOSE_DEBUG, "   -> flush transparent: {}", rect); |         dbgln_if(COMPOSE_DEBUG, "   -> flush transparent: {}", rect); | ||||||
|         ASSERT(!flush_rects.intersects(rect)); |         ASSERT(!flush_rects.intersects(rect)); | ||||||
|         bool have_rect = false; |  | ||||||
|         for (auto& r : flush_transparent_rects.rects()) { |         for (auto& r : flush_transparent_rects.rects()) { | ||||||
|             if (r == rect) { |             if (r == rect) | ||||||
|                 have_rect = true; |                 return; | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!have_rect) { |         flush_transparent_rects.add(rect); | ||||||
|             flush_transparent_rects.add(rect); |         check_restore_cursor_back(rect); | ||||||
|             check_restore_cursor_back(rect); |  | ||||||
|         } |  | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     if (!m_cursor_back_bitmap || m_invalidated_cursor) |     if (!m_cursor_back_bitmap || m_invalidated_cursor) | ||||||
|         check_restore_cursor_back(cursor_rect); |         check_restore_cursor_back(cursor_rect); | ||||||
| 
 | 
 | ||||||
|     auto back_painter = *m_back_painter; |  | ||||||
|     auto temp_painter = *m_temp_painter; |  | ||||||
| 
 |  | ||||||
|     auto paint_wallpaper = [&](Gfx::Painter& painter, const Gfx::IntRect& rect) { |     auto paint_wallpaper = [&](Gfx::Painter& painter, const Gfx::IntRect& rect) { | ||||||
|         // FIXME: If the wallpaper is opaque and covers the whole rect, no need to fill with color!
 |         // FIXME: If the wallpaper is opaque and covers the whole rect, no need to fill with color!
 | ||||||
|         painter.fill_rect(rect, background_color); |         painter.fill_rect(rect, background_color); | ||||||
|  | @ -277,7 +272,7 @@ void Compositor::compose() | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     auto compose_window = [&](Window& window) -> IterationDecision { |     auto compose_window = [&](Window& window) -> IterationDecision { | ||||||
|         auto frame_rect = window.frame().rect(); |         auto frame_rect = window.frame().render_rect(); | ||||||
|         if (!frame_rect.intersects(ws.rect())) |         if (!frame_rect.intersects(ws.rect())) | ||||||
|             return IterationDecision::Continue; |             return IterationDecision::Continue; | ||||||
|         auto frame_rects = frame_rect.shatter(window.rect()); |         auto frame_rects = frame_rect.shatter(window.rect()); | ||||||
|  | @ -849,7 +844,7 @@ bool Compositor::any_opaque_window_above_this_one_contains_rect(const Window& a_ | ||||||
|             return IterationDecision::Continue; |             return IterationDecision::Continue; | ||||||
|         if (!window.is_opaque()) |         if (!window.is_opaque()) | ||||||
|             return IterationDecision::Continue; |             return IterationDecision::Continue; | ||||||
|         if (window.frame().rect().contains(rect)) { |         if (window.frame().render_rect().contains(rect)) { | ||||||
|             found_containing_window = true; |             found_containing_window = true; | ||||||
|             return IterationDecision::Break; |             return IterationDecision::Break; | ||||||
|         } |         } | ||||||
|  | @ -907,7 +902,7 @@ void Compositor::recompute_occlusions() | ||||||
|         Gfx::DisjointRectSet visible_rects(screen_rect); |         Gfx::DisjointRectSet visible_rects(screen_rect); | ||||||
|         bool have_transparent = false; |         bool have_transparent = false; | ||||||
|         WindowManager::the().for_each_visible_window_from_front_to_back([&](Window& w) { |         WindowManager::the().for_each_visible_window_from_front_to_back([&](Window& w) { | ||||||
|             auto window_frame_rect = w.frame().rect().intersected(screen_rect); |             auto window_frame_rect = w.frame().render_rect().intersected(screen_rect); | ||||||
|             w.transparency_wallpaper_rects().clear(); |             w.transparency_wallpaper_rects().clear(); | ||||||
|             auto& visible_opaque = w.opaque_rects(); |             auto& visible_opaque = w.opaque_rects(); | ||||||
|             auto& transparency_rects = w.transparency_rects(); |             auto& transparency_rects = w.transparency_rects(); | ||||||
|  | @ -948,7 +943,7 @@ void Compositor::recompute_occlusions() | ||||||
| 
 | 
 | ||||||
|                 if (w2.is_minimized()) |                 if (w2.is_minimized()) | ||||||
|                     return IterationDecision::Continue; |                     return IterationDecision::Continue; | ||||||
|                 auto window_frame_rect2 = w2.frame().rect().intersected(screen_rect); |                 auto window_frame_rect2 = w2.frame().render_rect().intersected(screen_rect); | ||||||
|                 auto covering_rect = window_frame_rect2.intersected(window_frame_rect); |                 auto covering_rect = window_frame_rect2.intersected(window_frame_rect); | ||||||
|                 if (covering_rect.is_empty()) |                 if (covering_rect.is_empty()) | ||||||
|                     return IterationDecision::Continue; |                     return IterationDecision::Continue; | ||||||
|  | @ -1026,7 +1021,7 @@ void Compositor::recompute_occlusions() | ||||||
|             // Determine what transparent window areas need to render the wallpaper first
 |             // Determine what transparent window areas need to render the wallpaper first
 | ||||||
|             WindowManager::the().for_each_visible_window_from_back_to_front([&](Window& w) { |             WindowManager::the().for_each_visible_window_from_back_to_front([&](Window& w) { | ||||||
|                 auto& transparency_wallpaper_rects = w.transparency_wallpaper_rects(); |                 auto& transparency_wallpaper_rects = w.transparency_wallpaper_rects(); | ||||||
|                 if ((w.is_opaque() && w.frame().is_opaque()) || w.is_minimized()) { |                 if (w.is_minimized()) { | ||||||
|                     transparency_wallpaper_rects.clear(); |                     transparency_wallpaper_rects.clear(); | ||||||
|                     return IterationDecision::Continue; |                     return IterationDecision::Continue; | ||||||
|                 } |                 } | ||||||
|  | @ -1053,7 +1048,7 @@ void Compositor::recompute_occlusions() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     wm.for_each_visible_window_from_back_to_front([&](Window& w) { |     wm.for_each_visible_window_from_back_to_front([&](Window& w) { | ||||||
|         auto window_frame_rect = w.frame().rect().intersected(screen_rect); |         auto window_frame_rect = w.frame().render_rect().intersected(screen_rect); | ||||||
|         if (w.is_minimized() || window_frame_rect.is_empty()) |         if (w.is_minimized() || window_frame_rect.is_empty()) | ||||||
|             return IterationDecision::Continue; |             return IterationDecision::Continue; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -268,7 +268,7 @@ void Window::set_minimized(bool minimized) | ||||||
|     m_minimized = minimized; |     m_minimized = minimized; | ||||||
|     update_menu_item_text(PopupMenuItem::Minimize); |     update_menu_item_text(PopupMenuItem::Minimize); | ||||||
|     Compositor::the().invalidate_occlusions(); |     Compositor::the().invalidate_occlusions(); | ||||||
|     Compositor::the().invalidate_screen(frame().rect()); |     Compositor::the().invalidate_screen(frame().render_rect()); | ||||||
|     if (!blocking_modal_window()) |     if (!blocking_modal_window()) | ||||||
|         start_minimize_animation(); |         start_minimize_animation(); | ||||||
|     if (!minimized) |     if (!minimized) | ||||||
|  | @ -323,7 +323,7 @@ void Window::set_opacity(float opacity) | ||||||
|     m_opacity = opacity; |     m_opacity = opacity; | ||||||
|     if (was_opaque != is_opaque()) |     if (was_opaque != is_opaque()) | ||||||
|         Compositor::the().invalidate_occlusions(); |         Compositor::the().invalidate_occlusions(); | ||||||
|     Compositor::the().invalidate_screen(frame().rect()); |     Compositor::the().invalidate_screen(frame().render_rect()); | ||||||
|     WindowManager::the().notify_opacity_changed(*this); |     WindowManager::the().notify_opacity_changed(*this); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -449,14 +449,17 @@ void Window::set_visible(bool b) | ||||||
|     if (m_visible) |     if (m_visible) | ||||||
|         invalidate(true); |         invalidate(true); | ||||||
|     else |     else | ||||||
|         Compositor::the().invalidate_screen(frame().rect()); |         Compositor::the().invalidate_screen(frame().render_rect()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Window::invalidate(bool invalidate_frame) | void Window::invalidate(bool invalidate_frame) | ||||||
| { | { | ||||||
|     m_invalidated = true; |     m_invalidated = true; | ||||||
|     m_invalidated_all = true; |     m_invalidated_all = true; | ||||||
|     m_invalidated_frame |= invalidate_frame; |     if (invalidate_frame && !m_invalidated_frame) { | ||||||
|  |         m_invalidated_frame = true; | ||||||
|  |         frame().set_dirty(); | ||||||
|  |     } | ||||||
|     m_dirty_rects.clear(); |     m_dirty_rects.clear(); | ||||||
|     Compositor::the().invalidate_window(); |     Compositor::the().invalidate_window(); | ||||||
| } | } | ||||||
|  | @ -477,11 +480,14 @@ bool Window::invalidate_no_notify(const Gfx::IntRect& rect, bool with_frame) | ||||||
|     if (rect.is_empty()) |     if (rect.is_empty()) | ||||||
|         return false; |         return false; | ||||||
|     if (m_invalidated_all) { |     if (m_invalidated_all) { | ||||||
|         m_invalidated_frame |= with_frame; |         if (with_frame && !m_invalidated_frame) { | ||||||
|  |             m_invalidated_frame = true; | ||||||
|  |             frame().set_dirty(); | ||||||
|  |         } | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     auto outer_rect = frame().rect(); |     auto outer_rect = frame().render_rect(); | ||||||
|     auto inner_rect = rect; |     auto inner_rect = rect; | ||||||
|     inner_rect.move_by(position()); |     inner_rect.move_by(position()); | ||||||
|     // FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect.
 |     // FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect.
 | ||||||
|  | @ -490,7 +496,10 @@ bool Window::invalidate_no_notify(const Gfx::IntRect& rect, bool with_frame) | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     m_invalidated = true; |     m_invalidated = true; | ||||||
|     m_invalidated_frame |= with_frame; |     if (with_frame && !m_invalidated_frame) { | ||||||
|  |         m_invalidated_frame = true; | ||||||
|  |         frame().set_dirty(); | ||||||
|  |     } | ||||||
|     m_dirty_rects.add(inner_rect.translated(-outer_rect.location())); |     m_dirty_rects.add(inner_rect.translated(-outer_rect.location())); | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  | @ -499,11 +508,11 @@ void Window::prepare_dirty_rects() | ||||||
| { | { | ||||||
|     if (m_invalidated_all) { |     if (m_invalidated_all) { | ||||||
|         if (m_invalidated_frame) |         if (m_invalidated_frame) | ||||||
|             m_dirty_rects = frame().rect(); |             m_dirty_rects = frame().render_rect(); | ||||||
|         else |         else | ||||||
|             m_dirty_rects = rect(); |             m_dirty_rects = rect(); | ||||||
|     } else { |     } else { | ||||||
|         m_dirty_rects.move_by(frame().rect().location()); |         m_dirty_rects.move_by(frame().render_rect().location()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -56,10 +56,20 @@ static Gfx::Bitmap* s_minimize_icon; | ||||||
| static Gfx::Bitmap* s_maximize_icon; | static Gfx::Bitmap* s_maximize_icon; | ||||||
| static Gfx::Bitmap* s_restore_icon; | static Gfx::Bitmap* s_restore_icon; | ||||||
| static Gfx::Bitmap* s_close_icon; | static Gfx::Bitmap* s_close_icon; | ||||||
|  | static Gfx::Bitmap* s_window_shadow; | ||||||
| 
 | 
 | ||||||
| static String s_last_title_button_icons_path; | static String s_last_title_button_icons_path; | ||||||
| static int s_last_title_button_icons_scale; | static int s_last_title_button_icons_scale; | ||||||
| 
 | 
 | ||||||
|  | static String s_last_window_shadow_path; | ||||||
|  | 
 | ||||||
|  | static Gfx::IntRect frame_rect_for_window(Window& window, const Gfx::IntRect& rect) | ||||||
|  | { | ||||||
|  |     if (window.is_frameless()) | ||||||
|  |         return rect; | ||||||
|  |     return Gfx::WindowTheme::current().frame_rect_for_window(to_theme_window_type(window.type()), rect, WindowManager::the().palette()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| WindowFrame::WindowFrame(Window& window) | WindowFrame::WindowFrame(Window& window) | ||||||
|     : m_window(window) |     : m_window(window) | ||||||
| { | { | ||||||
|  | @ -94,6 +104,7 @@ WindowFrame::~WindowFrame() | ||||||
| 
 | 
 | ||||||
| void WindowFrame::set_button_icons() | void WindowFrame::set_button_icons() | ||||||
| { | { | ||||||
|  |     m_dirty = true; | ||||||
|     if (m_window.is_frameless()) |     if (m_window.is_frameless()) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|  | @ -104,6 +115,8 @@ void WindowFrame::set_button_icons() | ||||||
|     if (!s_minimize_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) { |     if (!s_minimize_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) { | ||||||
|         full_path.append(icons_path); |         full_path.append(icons_path); | ||||||
|         full_path.append("window-minimize.png"); |         full_path.append("window-minimize.png"); | ||||||
|  |         if (s_minimize_icon) | ||||||
|  |             s_minimize_icon->unref(); | ||||||
|         if (!(s_minimize_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale).leak_ref())) |         if (!(s_minimize_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale).leak_ref())) | ||||||
|             s_minimize_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/downward-triangle.png", icons_scale).leak_ref(); |             s_minimize_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/downward-triangle.png", icons_scale).leak_ref(); | ||||||
|         full_path.clear(); |         full_path.clear(); | ||||||
|  | @ -111,6 +124,8 @@ void WindowFrame::set_button_icons() | ||||||
|     if (!s_maximize_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) { |     if (!s_maximize_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) { | ||||||
|         full_path.append(icons_path); |         full_path.append(icons_path); | ||||||
|         full_path.append("window-maximize.png"); |         full_path.append("window-maximize.png"); | ||||||
|  |         if (s_maximize_icon) | ||||||
|  |             s_maximize_icon->unref(); | ||||||
|         if (!(s_maximize_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale).leak_ref())) |         if (!(s_maximize_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale).leak_ref())) | ||||||
|             s_maximize_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/upward-triangle.png", icons_scale).leak_ref(); |             s_maximize_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/upward-triangle.png", icons_scale).leak_ref(); | ||||||
|         full_path.clear(); |         full_path.clear(); | ||||||
|  | @ -118,6 +133,8 @@ void WindowFrame::set_button_icons() | ||||||
|     if (!s_restore_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) { |     if (!s_restore_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) { | ||||||
|         full_path.append(icons_path); |         full_path.append(icons_path); | ||||||
|         full_path.append("window-restore.png"); |         full_path.append("window-restore.png"); | ||||||
|  |         if (s_restore_icon) | ||||||
|  |             s_restore_icon->unref(); | ||||||
|         if (!(s_restore_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale).leak_ref())) |         if (!(s_restore_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale).leak_ref())) | ||||||
|             s_restore_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-restore.png", icons_scale).leak_ref(); |             s_restore_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-restore.png", icons_scale).leak_ref(); | ||||||
|         full_path.clear(); |         full_path.clear(); | ||||||
|  | @ -125,6 +142,8 @@ void WindowFrame::set_button_icons() | ||||||
|     if (!s_close_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) { |     if (!s_close_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) { | ||||||
|         full_path.append(icons_path); |         full_path.append(icons_path); | ||||||
|         full_path.append("window-close.png"); |         full_path.append("window-close.png"); | ||||||
|  |         if (s_close_icon) | ||||||
|  |             s_close_icon->unref(); | ||||||
|         if (!(s_close_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale).leak_ref())) |         if (!(s_close_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale).leak_ref())) | ||||||
|             s_close_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-close.png", icons_scale).leak_ref(); |             s_close_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-close.png", icons_scale).leak_ref(); | ||||||
|         full_path.clear(); |         full_path.clear(); | ||||||
|  | @ -138,6 +157,26 @@ void WindowFrame::set_button_icons() | ||||||
| 
 | 
 | ||||||
|     s_last_title_button_icons_path = icons_path; |     s_last_title_button_icons_path = icons_path; | ||||||
|     s_last_title_button_icons_scale = icons_scale; |     s_last_title_button_icons_scale = icons_scale; | ||||||
|  | 
 | ||||||
|  |     String window_shadow_path = WindowManager::the().palette().window_shadow_path(); | ||||||
|  |     if (!s_window_shadow || s_window_shadow->scale() != icons_scale || s_last_window_shadow_path != window_shadow_path) { | ||||||
|  |         s_window_shadow = Gfx::Bitmap::load_from_file(window_shadow_path, icons_scale).leak_ref(); | ||||||
|  |     } | ||||||
|  |     s_last_window_shadow_path = window_shadow_path; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Gfx::Bitmap* WindowFrame::window_shadow() const | ||||||
|  | { | ||||||
|  |     if (m_window.type() == WindowType::Desktop) | ||||||
|  |         return nullptr; | ||||||
|  |     return s_window_shadow; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool WindowFrame::frame_has_alpha() const | ||||||
|  | { | ||||||
|  |     if (auto* shadow_bitmap = window_shadow(); shadow_bitmap && shadow_bitmap->format() == Gfx::BitmapFormat::RGBA32) | ||||||
|  |         return true; | ||||||
|  |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void WindowFrame::did_set_maximized(Badge<Window>, bool maximized) | void WindowFrame::did_set_maximized(Badge<Window>, bool maximized) | ||||||
|  | @ -205,7 +244,7 @@ void WindowFrame::paint(Gfx::Painter& painter, const Gfx::IntRect& rect) | ||||||
| { | { | ||||||
|     render_to_cache(); |     render_to_cache(); | ||||||
| 
 | 
 | ||||||
|     auto frame_rect = this->rect(); |     auto frame_rect = render_rect(); | ||||||
|     auto window_rect = m_window.rect(); |     auto window_rect = m_window.rect(); | ||||||
| 
 | 
 | ||||||
|     if (m_top_bottom) { |     if (m_top_bottom) { | ||||||
|  | @ -265,48 +304,62 @@ void WindowFrame::render_to_cache() | ||||||
| { | { | ||||||
|     if (!m_dirty) |     if (!m_dirty) | ||||||
|         return; |         return; | ||||||
|     m_dirty = true; |     m_dirty = false; | ||||||
| 
 | 
 | ||||||
|     static RefPtr<Gfx::Bitmap> s_tmp_bitmap; |     static Gfx::Bitmap* s_tmp_bitmap; | ||||||
|     auto frame_rect = rect(); |     auto frame_rect = rect(); | ||||||
|  |     auto total_frame_rect = frame_rect; | ||||||
|  |     auto* shadow_bitmap = inflate_for_shadow(total_frame_rect, m_shadow_offset); | ||||||
|     auto window_rect = m_window.rect(); |     auto window_rect = m_window.rect(); | ||||||
|     auto scale = Screen::the().scale_factor(); |     auto scale = Screen::the().scale_factor(); | ||||||
|     if (!s_tmp_bitmap || !s_tmp_bitmap->size().contains(frame_rect.size()) || s_tmp_bitmap->scale() != scale) |     if (!s_tmp_bitmap || !s_tmp_bitmap->size().contains(total_frame_rect.size()) || s_tmp_bitmap->scale() != scale) { | ||||||
|         s_tmp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, frame_rect.size(), scale); |         if (s_tmp_bitmap) | ||||||
|  |             s_tmp_bitmap->unref(); | ||||||
|  |         s_tmp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, total_frame_rect.size(), scale).leak_ref(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     Gfx::Painter painter(*s_tmp_bitmap); |     Gfx::Painter painter(*s_tmp_bitmap); | ||||||
|     painter.fill_rect({ { 0, 0 }, frame_rect.size() }, Color::White); |     for (auto& rect : total_frame_rect.shatter(window_rect)) | ||||||
|     render(painter); |         painter.clear_rect({ rect.location() - total_frame_rect.location(), rect.size() }, { 255, 255, 255, 0 }); | ||||||
| 
 | 
 | ||||||
|     auto top_bottom_height = frame_rect.height() - window_rect.height(); |     if (shadow_bitmap) | ||||||
|  |         paint_simple_rect_shadow(painter, { { 0, 0 }, total_frame_rect.size() }, *shadow_bitmap); | ||||||
|  | 
 | ||||||
|  |     { | ||||||
|  |         Gfx::PainterStateSaver save(painter); | ||||||
|  |         painter.translate(m_shadow_offset); | ||||||
|  |         render(painter); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto top_bottom_height = total_frame_rect.height() - window_rect.height(); | ||||||
|     if (top_bottom_height > 0) { |     if (top_bottom_height > 0) { | ||||||
|         if (!m_top_bottom || m_top_bottom->width() != frame_rect.width() || m_top_bottom->height() != top_bottom_height || m_top_bottom->scale() != scale) |         if (!m_top_bottom || m_top_bottom->width() != total_frame_rect.width() || m_top_bottom->height() != top_bottom_height || m_top_bottom->scale() != scale) | ||||||
|             m_top_bottom = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { frame_rect.width(), top_bottom_height }, scale); |             m_top_bottom = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { total_frame_rect.width(), top_bottom_height }, scale); | ||||||
|         m_bottom_y = window_rect.y() - frame_rect.y(); |         m_bottom_y = window_rect.y() - total_frame_rect.y(); | ||||||
|         ASSERT(m_bottom_y >= 0); |         ASSERT(m_bottom_y >= 0); | ||||||
| 
 | 
 | ||||||
|         Gfx::Painter top_bottom_painter(*m_top_bottom); |         Gfx::Painter top_bottom_painter(*m_top_bottom); | ||||||
|         if (m_bottom_y > 0) |         if (m_bottom_y > 0) | ||||||
|             top_bottom_painter.blit({ 0, 0 }, *s_tmp_bitmap, { 0, 0, frame_rect.width(), m_bottom_y }); |             top_bottom_painter.blit({ 0, 0 }, *s_tmp_bitmap, { 0, 0, total_frame_rect.width(), m_bottom_y }, 1.0, false); | ||||||
|         if (m_bottom_y < top_bottom_height) |         if (m_bottom_y < top_bottom_height) | ||||||
|             top_bottom_painter.blit({ 0, m_bottom_y }, *s_tmp_bitmap, { 0, frame_rect.height() - (frame_rect.bottom() - window_rect.bottom()), frame_rect.width(), top_bottom_height - m_bottom_y }); |             top_bottom_painter.blit({ 0, m_bottom_y }, *s_tmp_bitmap, { 0, total_frame_rect.height() - (total_frame_rect.bottom() - window_rect.bottom()), total_frame_rect.width(), top_bottom_height - m_bottom_y }, 1.0, false); | ||||||
|     } else { |     } else { | ||||||
|         m_top_bottom = nullptr; |         m_top_bottom = nullptr; | ||||||
|         m_bottom_y = 0; |         m_bottom_y = 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     auto left_right_width = frame_rect.width() - window_rect.width(); |     auto left_right_width = total_frame_rect.width() - window_rect.width(); | ||||||
|     if (left_right_width > 0) { |     if (left_right_width > 0) { | ||||||
|         if (!m_left_right || m_left_right->height() != frame_rect.height() || m_left_right->width() != left_right_width || m_left_right->scale() != scale) |         if (!m_left_right || m_left_right->height() != total_frame_rect.height() || m_left_right->width() != left_right_width || m_left_right->scale() != scale) | ||||||
|             m_left_right = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { left_right_width, frame_rect.height() }, scale); |             m_left_right = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { left_right_width, total_frame_rect.height() }, scale); | ||||||
|         m_right_x = window_rect.x() - frame_rect.x(); |         m_right_x = window_rect.x() - total_frame_rect.x(); | ||||||
|         ASSERT(m_right_x >= 0); |         ASSERT(m_right_x >= 0); | ||||||
| 
 | 
 | ||||||
|         Gfx::Painter left_right_painter(*m_left_right); |         Gfx::Painter left_right_painter(*m_left_right); | ||||||
|         if (m_right_x > 0) |         if (m_right_x > 0) | ||||||
|             left_right_painter.blit({ 0, 0 }, *s_tmp_bitmap, { 0, m_bottom_y, m_right_x, window_rect.height() }); |             left_right_painter.blit({ 0, 0 }, *s_tmp_bitmap, { 0, m_bottom_y, m_right_x, window_rect.height() }, 1.0, false); | ||||||
|         if (m_right_x < left_right_width) |         if (m_right_x < left_right_width) | ||||||
|             left_right_painter.blit({ m_right_x, 0 }, *s_tmp_bitmap, { (window_rect.right() - frame_rect.x()) + 1, m_bottom_y, frame_rect.width() - (frame_rect.right() - window_rect.right()), window_rect.height() }); |             left_right_painter.blit({ m_right_x, 0 }, *s_tmp_bitmap, { (window_rect.right() - total_frame_rect.x()) + 1, m_bottom_y, total_frame_rect.width() - (total_frame_rect.right() - window_rect.right()), window_rect.height() }, 1.0, false); | ||||||
|     } else { |     } else { | ||||||
|         m_left_right = nullptr; |         m_left_right = nullptr; | ||||||
|         m_right_x = 0; |         m_right_x = 0; | ||||||
|  | @ -321,30 +374,45 @@ void WindowFrame::set_opacity(float opacity) | ||||||
|     m_opacity = opacity; |     m_opacity = opacity; | ||||||
|     if (was_opaque != is_opaque()) |     if (was_opaque != is_opaque()) | ||||||
|         Compositor::the().invalidate_occlusions(); |         Compositor::the().invalidate_occlusions(); | ||||||
|     Compositor::the().invalidate_screen(rect()); |     Compositor::the().invalidate_screen(render_rect()); | ||||||
|     WindowManager::the().notify_opacity_changed(m_window); |     WindowManager::the().notify_opacity_changed(m_window); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static Gfx::IntRect frame_rect_for_window(Window& window, const Gfx::IntRect& rect) | Gfx::IntRect WindowFrame::inflated_for_shadow(const Gfx::IntRect& frame_rect) const | ||||||
| { | { | ||||||
|     if (window.is_frameless()) |     if (auto* shadow = window_shadow()) { | ||||||
|         return rect; |         auto total_shadow_size = shadow->height(); | ||||||
|     return Gfx::WindowTheme::current().frame_rect_for_window(to_theme_window_type(window.type()), rect, WindowManager::the().palette()); |         return frame_rect.inflated(total_shadow_size, total_shadow_size); | ||||||
|  |     } | ||||||
|  |     return frame_rect; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static Gfx::IntRect frame_rect_for_window(Window& window) | Gfx::Bitmap* WindowFrame::inflate_for_shadow(Gfx::IntRect& frame_rect, Gfx::IntPoint& shadow_offset) const | ||||||
| { | { | ||||||
|     return frame_rect_for_window(window, window.rect()); |     auto* shadow = window_shadow(); | ||||||
|  |     if (shadow) { | ||||||
|  |         auto total_shadow_size = shadow->height(); | ||||||
|  |         frame_rect.inflate(total_shadow_size, total_shadow_size); | ||||||
|  |         auto offset = total_shadow_size / 2; | ||||||
|  |         shadow_offset = { offset, offset }; | ||||||
|  |     } else { | ||||||
|  |         shadow_offset = {}; | ||||||
|  |     } | ||||||
|  |     return shadow; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Gfx::IntRect WindowFrame::rect() const | Gfx::IntRect WindowFrame::rect() const | ||||||
| { | { | ||||||
|     return frame_rect_for_window(m_window); |     return frame_rect_for_window(m_window, m_window.rect()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Gfx::IntRect WindowFrame::render_rect() const | ||||||
|  | { | ||||||
|  |     return inflated_for_shadow(rect()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void WindowFrame::invalidate_title_bar() | void WindowFrame::invalidate_title_bar() | ||||||
| { | { | ||||||
|     m_dirty = true; |  | ||||||
|     invalidate(title_bar_rect()); |     invalidate(title_bar_rect()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -353,6 +421,7 @@ void WindowFrame::invalidate(Gfx::IntRect relative_rect) | ||||||
|     auto frame_rect = rect(); |     auto frame_rect = rect(); | ||||||
|     auto window_rect = m_window.rect(); |     auto window_rect = m_window.rect(); | ||||||
|     relative_rect.move_by(frame_rect.x() - window_rect.x(), frame_rect.y() - window_rect.y()); |     relative_rect.move_by(frame_rect.x() - window_rect.x(), frame_rect.y() - window_rect.y()); | ||||||
|  |     m_dirty = true; | ||||||
|     m_window.invalidate(relative_rect, true); |     m_window.invalidate(relative_rect, true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -360,15 +429,15 @@ void WindowFrame::notify_window_rect_changed(const Gfx::IntRect& old_rect, const | ||||||
| { | { | ||||||
|     layout_buttons(); |     layout_buttons(); | ||||||
| 
 | 
 | ||||||
|     auto old_frame_rect = frame_rect_for_window(m_window, old_rect); |     auto old_frame_rect = inflated_for_shadow(frame_rect_for_window(m_window, old_rect)); | ||||||
|     auto new_frame_rect = frame_rect_for_window(m_window, new_rect); |     auto new_frame_rect = inflated_for_shadow(frame_rect_for_window(m_window, new_rect)); | ||||||
|     if (old_frame_rect.width() != new_frame_rect.width()) |     if (old_frame_rect.size() != new_frame_rect.size()) | ||||||
|         m_dirty = true; |         m_dirty = true; | ||||||
|     auto& compositor = Compositor::the(); |     auto& compositor = Compositor::the(); | ||||||
|     for (auto& dirty : old_frame_rect.shatter(rect())) |     for (auto& dirty : old_frame_rect.shatter(new_frame_rect)) | ||||||
|         compositor.invalidate_screen(dirty); |         compositor.invalidate_screen(dirty); | ||||||
|     if (!m_window.is_opaque()) |     if (!m_window.is_opaque()) | ||||||
|         compositor.invalidate_screen(rect()); |         compositor.invalidate_screen(new_frame_rect); | ||||||
| 
 | 
 | ||||||
|     compositor.invalidate_occlusions(); |     compositor.invalidate_occlusions(); | ||||||
| 
 | 
 | ||||||
|  | @ -491,4 +560,79 @@ void WindowFrame::start_flash_animation() | ||||||
|     m_flash_timer->start(); |     m_flash_timer->start(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void WindowFrame::paint_simple_rect_shadow(Gfx::Painter& painter, const Gfx::IntRect& containing_rect, const Gfx::Bitmap& shadow_bitmap) const | ||||||
|  | { | ||||||
|  |     // The layout of the shadow_bitmap is defined like this:
 | ||||||
|  |     // +---------+----+---------+----+----+----+
 | ||||||
|  |     // |   TL    | T  |   TR    | LT | L  | LB |
 | ||||||
|  |     // +---------+----+---------+----+----+----+
 | ||||||
|  |     // |   BL    | B  |   BR    | RT | R  | RB |
 | ||||||
|  |     // +---------+----+---------+----+----+----+
 | ||||||
|  |     // Located strictly on the top or bottom of the rectangle, above or below of the content:
 | ||||||
|  |     //   TL = top-left     T = top     TR = top-right
 | ||||||
|  |     //   BL = bottom-left  B = bottom  BR = bottom-right
 | ||||||
|  |     // Located on the left or right of the rectangle, but not above or below of the content:
 | ||||||
|  |     //   LT = left-top     L = left    LB = left-bottom
 | ||||||
|  |     //   RT = right-top    R = right   RB = right-bottom
 | ||||||
|  |     // So, the bitmap has two rows and 6 column, two of which are twice as wide.
 | ||||||
|  |     // The height divided by two defines a cell size, and width of each
 | ||||||
|  |     // column must be the same as the height of the cell, except for the
 | ||||||
|  |     // first an third column, which are twice as wide.
 | ||||||
|  |     if (shadow_bitmap.height() % 2 != 0) { | ||||||
|  |         dbgln("Can't paint simple rect shadow, shadow bitmap height {} is not even", shadow_bitmap.height()); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     auto base_size = shadow_bitmap.height() / 2; | ||||||
|  |     if (shadow_bitmap.width() != base_size * (6 + 2)) { | ||||||
|  |         if (shadow_bitmap.width() % base_size != 0) | ||||||
|  |             dbgln("Can't paint simple rect shadow, shadow bitmap width {} is not a multiple of {}", shadow_bitmap.width(), base_size); | ||||||
|  |         else | ||||||
|  |             dbgln("Can't paint simple rect shadow, shadow bitmap width {} but expected {}", shadow_bitmap.width(), base_size * (6 + 2)); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // The containing_rect should have been inflated appropriately
 | ||||||
|  |     ASSERT(containing_rect.size().contains(Gfx::IntSize { base_size, base_size })); | ||||||
|  | 
 | ||||||
|  |     auto half_width = containing_rect.width() / 2; | ||||||
|  |     auto paint_horizontal = [&](int y, int src_row) { | ||||||
|  |         Gfx::PainterStateSaver save(painter); | ||||||
|  |         painter.add_clip_rect({ containing_rect.left(), y, containing_rect.width(), base_size }); | ||||||
|  |         int corner_piece_width = base_size * 2; | ||||||
|  |         int left_corners_right = min(half_width, corner_piece_width); | ||||||
|  |         int right_corners_left = max(half_width, containing_rect.width() - corner_piece_width); | ||||||
|  |         painter.blit({ containing_rect.left() + left_corners_right - corner_piece_width, y }, shadow_bitmap, { 0, src_row * base_size, corner_piece_width, base_size }); | ||||||
|  |         painter.blit({ containing_rect.left() + right_corners_left, y }, shadow_bitmap, { corner_piece_width + base_size, src_row * base_size, corner_piece_width, base_size }); | ||||||
|  |         for (int x = left_corners_right; x < right_corners_left; x += base_size) { | ||||||
|  |             auto width = min(right_corners_left - x, base_size); | ||||||
|  |             painter.blit({ containing_rect.left() + x, y }, shadow_bitmap, { corner_piece_width, src_row * base_size, width, base_size }); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     paint_horizontal(containing_rect.top(), 0); | ||||||
|  |     paint_horizontal(containing_rect.bottom() - base_size + 1, 1); | ||||||
|  | 
 | ||||||
|  |     auto sides_height = containing_rect.height() - 2 * base_size; | ||||||
|  |     auto half_height = sides_height / 2; | ||||||
|  |     auto paint_vertical = [&](int x, int src_row) { | ||||||
|  |         Gfx::PainterStateSaver save(painter); | ||||||
|  |         painter.add_clip_rect({ x, containing_rect.y() + base_size, base_size, containing_rect.height() - 2 * base_size }); | ||||||
|  |         int top_corners_bottom = base_size + min(half_height, base_size); | ||||||
|  |         int top_corner_height = top_corners_bottom - base_size; | ||||||
|  |         int bottom_corners_top = base_size + max(half_height, sides_height - base_size); | ||||||
|  |         int bottom_corner_height = sides_height + base_size - bottom_corners_top; | ||||||
|  |         painter.blit({ x, containing_rect.top() + top_corners_bottom - top_corner_height }, shadow_bitmap, { base_size * 5, src_row * base_size, base_size, top_corner_height }); | ||||||
|  |         painter.blit({ x, containing_rect.top() + bottom_corners_top }, shadow_bitmap, { base_size * 7, src_row * base_size + base_size - bottom_corner_height, base_size, bottom_corner_height }); | ||||||
|  |         if (sides_height > 2 * base_size) { | ||||||
|  |             for (int y = top_corners_bottom; y < bottom_corners_top; y += base_size) { | ||||||
|  |                 auto height = min(bottom_corners_top - y, base_size); | ||||||
|  |                 painter.blit({ x, containing_rect.top() + y }, shadow_bitmap, { base_size * 6, src_row * base_size, base_size, height }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     paint_vertical(containing_rect.left(), 0); | ||||||
|  |     paint_vertical(containing_rect.right() - base_size + 1, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -45,6 +45,7 @@ public: | ||||||
|     ~WindowFrame(); |     ~WindowFrame(); | ||||||
| 
 | 
 | ||||||
|     Gfx::IntRect rect() const; |     Gfx::IntRect rect() const; | ||||||
|  |     Gfx::IntRect render_rect() const; | ||||||
|     void paint(Gfx::Painter&, const Gfx::IntRect&); |     void paint(Gfx::Painter&, const Gfx::IntRect&); | ||||||
|     void render(Gfx::Painter&); |     void render(Gfx::Painter&); | ||||||
|     void render_to_cache(); |     void render_to_cache(); | ||||||
|  | @ -64,7 +65,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     void start_flash_animation(); |     void start_flash_animation(); | ||||||
| 
 | 
 | ||||||
|     bool has_alpha_channel() const { return m_has_alpha_channel; } |     bool has_alpha_channel() const { return m_has_alpha_channel || frame_has_alpha(); } | ||||||
|     void set_has_alpha_channel(bool value) { m_has_alpha_channel = value; } |     void set_has_alpha_channel(bool value) { m_has_alpha_channel = value; } | ||||||
| 
 | 
 | ||||||
|     void set_opacity(float); |     void set_opacity(float); | ||||||
|  | @ -79,14 +80,19 @@ public: | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void scale_changed() |     void set_dirty() | ||||||
|     { |     { | ||||||
|         m_dirty = true; |         m_dirty = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  |     void paint_simple_rect_shadow(Gfx::Painter&, const Gfx::IntRect&, const Gfx::Bitmap&) const; | ||||||
|     void paint_notification_frame(Gfx::Painter&); |     void paint_notification_frame(Gfx::Painter&); | ||||||
|     void paint_normal_frame(Gfx::Painter&); |     void paint_normal_frame(Gfx::Painter&); | ||||||
|  |     Gfx::Bitmap* window_shadow() const; | ||||||
|  |     bool frame_has_alpha() const; | ||||||
|  |     Gfx::IntRect inflated_for_shadow(const Gfx::IntRect&) const; | ||||||
|  |     Gfx::Bitmap* inflate_for_shadow(Gfx::IntRect&, Gfx::IntPoint&) const; | ||||||
| 
 | 
 | ||||||
|     Gfx::WindowTheme::WindowState window_state_for_theme() const; |     Gfx::WindowTheme::WindowState window_state_for_theme() const; | ||||||
| 
 | 
 | ||||||
|  | @ -96,6 +102,8 @@ private: | ||||||
|     Button* m_maximize_button { nullptr }; |     Button* m_maximize_button { nullptr }; | ||||||
|     Button* m_minimize_button { nullptr }; |     Button* m_minimize_button { nullptr }; | ||||||
| 
 | 
 | ||||||
|  |     Gfx::IntPoint m_shadow_offset {}; | ||||||
|  | 
 | ||||||
|     RefPtr<Gfx::Bitmap> m_top_bottom; |     RefPtr<Gfx::Bitmap> m_top_bottom; | ||||||
|     RefPtr<Gfx::Bitmap> m_left_right; |     RefPtr<Gfx::Bitmap> m_left_right; | ||||||
|     int m_bottom_y { 0 }; // y-offset in m_top_bottom for the bottom half
 |     int m_bottom_y { 0 }; // y-offset in m_top_bottom for the bottom half
 | ||||||
|  |  | ||||||
|  | @ -270,7 +270,7 @@ void WindowManager::remove_window(Window& window) | ||||||
|     if (active == &window || active_input == &window || (active && window.is_descendant_of(*active)) || (active_input && active_input != active && window.is_descendant_of(*active_input))) |     if (active == &window || active_input == &window || (active && window.is_descendant_of(*active)) || (active_input && active_input != active && window.is_descendant_of(*active_input))) | ||||||
|         pick_new_active_window(&window); |         pick_new_active_window(&window); | ||||||
| 
 | 
 | ||||||
|     Compositor::the().invalidate_screen(window.frame().rect()); |     Compositor::the().invalidate_screen(window.frame().render_rect()); | ||||||
| 
 | 
 | ||||||
|     if (m_switcher.is_visible() && window.type() != WindowType::WindowSwitcher) |     if (m_switcher.is_visible() && window.type() != WindowType::WindowSwitcher) | ||||||
|         m_switcher.refresh(); |         m_switcher.refresh(); | ||||||
|  | @ -1517,7 +1517,6 @@ void WindowManager::reload_icon_bitmaps_after_scale_change(bool allow_hidpi_icon | ||||||
|     for_each_window([&](Window& window) { |     for_each_window([&](Window& window) { | ||||||
|         auto& window_frame = window.frame(); |         auto& window_frame = window.frame(); | ||||||
|         window_frame.set_button_icons(); |         window_frame.set_button_icons(); | ||||||
|         window_frame.scale_changed(); |  | ||||||
|         return IterationDecision::Continue; |         return IterationDecision::Continue; | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Tom
						Tom