Browse Source

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 <[email protected]>
trop[bot] 1 year ago
parent
commit
b39f36496d

+ 2 - 3
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_) {

+ 57 - 52
shell/browser/ui/views/win_icon_painter.cc

@@ -7,46 +7,26 @@
 #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);
-  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);
+  // 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->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);
+  canvas->DrawRect(rect_f, flags);
 }
 
 }  // namespace
@@ -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 paint_flags = flags;
-  paint_flags.setAntiAlias(true);
-
-  // Bottom left ("in front") rounded square.
-  const float bottom_rect_radius = canvas->image_scale();
-  DrawRoundRect(canvas, icon_rect, bottom_rect_radius, flags);
-
-  // Top right ("behind") top+right edges of rounded square (2.5x).
-  icon_rect.Offset(separation, -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);
+
+  // 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);
 
-  const float top_rect_radius = 2.5f * canvas->image_scale();
-  DrawRoundRectEdges(canvas, icon_rect, top_rect_radius, flags);
+  // Shrink the rect to make space for the top rect.
+  rect_f.Inset(gfx::InsetsF::TLBR(separation, 0, 0, separation));
+
+  gfx::RRectF rrect(rect_f, round_rect_radius);
+
+  // 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

+ 7 - 6
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