From b39f36496d3e23281dbdf3e06ed31cd8182f6ab0 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 09:54:31 +0100 Subject: [PATCH] fix: improve caption button appearance on Windows 11 (#41586) https://chromium-review.googlesource.com/c/chromium/src/+/4428171 Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- shell/browser/ui/views/win_caption_button.cc | 5 +- shell/browser/ui/views/win_icon_painter.cc | 105 ++++++++++--------- shell/browser/ui/views/win_icon_painter.h | 13 +-- 3 files changed, 64 insertions(+), 59 deletions(-) diff --git a/shell/browser/ui/views/win_caption_button.cc b/shell/browser/ui/views/win_caption_button.cc index 04f8a158323a..711c301bfb42 100644 --- a/shell/browser/ui/views/win_caption_button.cc +++ b/shell/browser/ui/views/win_caption_button.cc @@ -163,7 +163,7 @@ void WinCaptionButton::PaintSymbol(gfx::Canvas* canvas) { gfx::ScopedCanvas scoped_canvas(canvas); const float scale = canvas->UndoDeviceScaleFactor(); - const int symbol_size_pixels = std::round(10 * scale); + const int symbol_size_pixels = base::ClampRound(10 * scale); gfx::RectF bounds_rect(GetContentsBounds()); bounds_rect.Scale(scale); gfx::Rect symbol_rect(gfx::ToEnclosingRect(bounds_rect)); @@ -174,8 +174,7 @@ void WinCaptionButton::PaintSymbol(gfx::Canvas* canvas) { flags.setAntiAlias(false); flags.setColor(symbol_color); flags.setStyle(cc::PaintFlags::kStroke_Style); - // Stroke width jumps up a pixel every time we reach a new integral scale. - const int stroke_width = std::floor(scale); + const int stroke_width = base::ClampRound(scale); flags.setStrokeWidth(stroke_width); switch (button_type_) { diff --git a/shell/browser/ui/views/win_icon_painter.cc b/shell/browser/ui/views/win_icon_painter.cc index d89d63d43905..e55a80615b36 100644 --- a/shell/browser/ui/views/win_icon_painter.cc +++ b/shell/browser/ui/views/win_icon_painter.cc @@ -7,48 +7,28 @@ #include "base/numerics/safe_conversions.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/geometry/rrect_f.h" #include "ui/gfx/geometry/skia_conversions.h" #include "ui/gfx/scoped_canvas.h" namespace { -// Canvas::DrawRect's stroke can bleed out of |rect|'s bounds, so this draws a -// rectangle inset such that the result is constrained to |rect|'s size. +// The rounded rect corner radius for MaximizeIcon and RestoreIcon in +// Windows 11. +constexpr float kWin11RoundedCornerRadius = 1.5f; + void DrawRect(gfx::Canvas* canvas, const gfx::Rect& rect, const cc::PaintFlags& flags) { gfx::RectF rect_f(rect); + + // The rect is used as a bounding box, and the stroke is kept within. float stroke_half_width = flags.getStrokeWidth() / 2; rect_f.Inset(stroke_half_width); + canvas->DrawRect(rect_f, flags); } -void DrawRoundRect(gfx::Canvas* canvas, - const gfx::Rect& rect, - float radius, - const cc::PaintFlags& flags) { - gfx::RectF rect_f(rect); - float stroke_half_width = flags.getStrokeWidth() / 2; - rect_f.Inset(stroke_half_width); - canvas->DrawRoundRect(rect_f, radius, flags); -} - -// Draws a path containing the top and right edges of a rounded rectangle. -void DrawRoundRectEdges(gfx::Canvas* canvas, - const gfx::Rect& rect, - float radius, - const cc::PaintFlags& flags) { - gfx::RectF symbol_rect_f(rect); - float stroke_half_width = flags.getStrokeWidth() / 2; - symbol_rect_f.Inset(stroke_half_width); - SkPath path; - path.moveTo(symbol_rect_f.x(), symbol_rect_f.y()); - path.arcTo(symbol_rect_f.right(), symbol_rect_f.y(), symbol_rect_f.right(), - symbol_rect_f.y() + radius, radius); - path.lineTo(symbol_rect_f.right(), symbol_rect_f.bottom()); - canvas->DrawPath(path, flags); -} - } // namespace namespace electron { @@ -58,7 +38,7 @@ WinIconPainter::~WinIconPainter() = default; void WinIconPainter::PaintMinimizeIcon(gfx::Canvas* canvas, const gfx::Rect& symbol_rect, - const cc::PaintFlags& flags) { + cc::PaintFlags& flags) { const int y = symbol_rect.CenterPoint().y(); const gfx::Point p1 = gfx::Point(symbol_rect.x(), y); const gfx::Point p2 = gfx::Point(symbol_rect.right(), y); @@ -67,13 +47,13 @@ void WinIconPainter::PaintMinimizeIcon(gfx::Canvas* canvas, void WinIconPainter::PaintMaximizeIcon(gfx::Canvas* canvas, const gfx::Rect& symbol_rect, - const cc::PaintFlags& flags) { + cc::PaintFlags& flags) { DrawRect(canvas, symbol_rect, flags); } void WinIconPainter::PaintRestoreIcon(gfx::Canvas* canvas, const gfx::Rect& symbol_rect, - const cc::PaintFlags& flags) { + cc::PaintFlags& flags) { const int separation = base::ClampFloor(2 * canvas->image_scale()); gfx::Rect icon_rect = symbol_rect; icon_rect.Inset(gfx::Insets::TLBR(separation, 0, 0, separation)); @@ -89,7 +69,7 @@ void WinIconPainter::PaintRestoreIcon(gfx::Canvas* canvas, void WinIconPainter::PaintCloseIcon(gfx::Canvas* canvas, const gfx::Rect& symbol_rect, - const cc::PaintFlags& flags) { + cc::PaintFlags& flags) { // TODO(bsep): This sometimes draws misaligned at fractional device scales // because the button's origin isn't necessarily aligned to pixels. cc::PaintFlags paint_flags = flags; @@ -109,32 +89,57 @@ Win11IconPainter::~Win11IconPainter() = default; void Win11IconPainter::PaintMaximizeIcon(gfx::Canvas* canvas, const gfx::Rect& symbol_rect, - const cc::PaintFlags& flags) { - cc::PaintFlags paint_flags = flags; - paint_flags.setAntiAlias(true); + cc::PaintFlags& flags) { + gfx::RectF rect_f(symbol_rect); + flags.setAntiAlias(true); + const float corner_radius = + base::ClampFloor(kWin11RoundedCornerRadius * canvas->image_scale()); - const float corner_radius = canvas->image_scale(); - DrawRoundRect(canvas, symbol_rect, corner_radius, flags); + // The symbol rect is used as a bounding box, and the stroke is kept within. + float stroke_half_width = flags.getStrokeWidth() / 2; + rect_f.Inset(stroke_half_width); + + canvas->DrawRoundRect(rect_f, corner_radius, flags); } void Win11IconPainter::PaintRestoreIcon(gfx::Canvas* canvas, const gfx::Rect& symbol_rect, - const cc::PaintFlags& flags) { - const int separation = base::ClampFloor(2 * canvas->image_scale()); - gfx::Rect icon_rect = symbol_rect; - icon_rect.Inset(gfx::Insets::TLBR(separation, 0, 0, separation)); + cc::PaintFlags& flags) { + gfx::RectF rect_f(symbol_rect); + const float separation = 2.0f * canvas->image_scale(); + const int round_rect_radius = + base::ClampFloor(kWin11RoundedCornerRadius * canvas->image_scale()); + const int top_rect_upper_right_radius = 2 * round_rect_radius; + flags.setAntiAlias(true); - cc::PaintFlags paint_flags = flags; - paint_flags.setAntiAlias(true); + // The symbol rect is used as a bounding box, and the stroke is kept within. + const float stroke_half_width = flags.getStrokeWidth() / 2; + rect_f.Inset(stroke_half_width); - // Bottom left ("in front") rounded square. - const float bottom_rect_radius = canvas->image_scale(); - DrawRoundRect(canvas, icon_rect, bottom_rect_radius, flags); + // Shrink the rect to make space for the top rect. + rect_f.Inset(gfx::InsetsF::TLBR(separation, 0, 0, separation)); - // Top right ("behind") top+right edges of rounded square (2.5x). - icon_rect.Offset(separation, -separation); + gfx::RRectF rrect(rect_f, round_rect_radius); - const float top_rect_radius = 2.5f * canvas->image_scale(); - DrawRoundRectEdges(canvas, icon_rect, top_rect_radius, flags); + // Bottom ("in front") rounded square. + canvas->sk_canvas()->drawRRect(SkRRect(rrect), flags); + + // The top rounded square is clipped to only draw the top and right edges, + // and give corners a flat base. The clip rect sits right below the bottom + // half of the stroke. + gfx::RRectF clip_rrect(rrect); + const float clip_rect_growth = separation - stroke_half_width; + // The upper-right corner radius doesn't need to be updated because |Outset| + // does that for us. + clip_rrect.Outset(clip_rect_growth); + canvas->sk_canvas()->clipRRect(SkRRect(clip_rrect), SkClipOp::kDifference, + true); + + // Top ("behind") rounded square. + rrect.Offset(separation, -separation); + rrect.SetCornerRadii(gfx::RRectF::Corner::kUpperRight, + top_rect_upper_right_radius, + top_rect_upper_right_radius); + canvas->sk_canvas()->drawRRect(SkRRect(rrect), flags); } } // namespace electron diff --git a/shell/browser/ui/views/win_icon_painter.h b/shell/browser/ui/views/win_icon_painter.h index d3c85be296a0..57bf16376f72 100644 --- a/shell/browser/ui/views/win_icon_painter.h +++ b/shell/browser/ui/views/win_icon_painter.h @@ -10,6 +10,7 @@ namespace electron { +// Copied from chrome/browser/ui/views/frame/windows_icon_painter.h class WinIconPainter { public: WinIconPainter(); @@ -22,22 +23,22 @@ class WinIconPainter { // Paints the minimize icon for the button virtual void PaintMinimizeIcon(gfx::Canvas* canvas, const gfx::Rect& symbol_rect, - const cc::PaintFlags& flags); + cc::PaintFlags& flags); // Paints the maximize icon for the button virtual void PaintMaximizeIcon(gfx::Canvas* canvas, const gfx::Rect& symbol_rect, - const cc::PaintFlags& flags); + cc::PaintFlags& flags); // Paints the restore icon for the button virtual void PaintRestoreIcon(gfx::Canvas* canvas, const gfx::Rect& symbol_rect, - const cc::PaintFlags& flags); + cc::PaintFlags& flags); // Paints the close icon for the button virtual void PaintCloseIcon(gfx::Canvas* canvas, const gfx::Rect& symbol_rect, - const cc::PaintFlags& flags); + cc::PaintFlags& flags); }; class Win11IconPainter : public WinIconPainter { @@ -52,12 +53,12 @@ class Win11IconPainter : public WinIconPainter { // Paints the maximize icon for the button void PaintMaximizeIcon(gfx::Canvas* canvas, const gfx::Rect& symbol_rect, - const cc::PaintFlags& flags) override; + cc::PaintFlags& flags) override; // Paints the restore icon for the button void PaintRestoreIcon(gfx::Canvas* canvas, const gfx::Rect& symbol_rect, - const cc::PaintFlags& flags) override; + cc::PaintFlags& flags) override; }; } // namespace electron