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 <shelley.vohr@gmail.com>
This commit is contained in:
parent
d16f1d52e8
commit
b39f36496d
3 changed files with 64 additions and 59 deletions
|
@ -163,7 +163,7 @@ void WinCaptionButton::PaintSymbol(gfx::Canvas* canvas) {
|
||||||
gfx::ScopedCanvas scoped_canvas(canvas);
|
gfx::ScopedCanvas scoped_canvas(canvas);
|
||||||
const float scale = canvas->UndoDeviceScaleFactor();
|
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());
|
gfx::RectF bounds_rect(GetContentsBounds());
|
||||||
bounds_rect.Scale(scale);
|
bounds_rect.Scale(scale);
|
||||||
gfx::Rect symbol_rect(gfx::ToEnclosingRect(bounds_rect));
|
gfx::Rect symbol_rect(gfx::ToEnclosingRect(bounds_rect));
|
||||||
|
@ -174,8 +174,7 @@ void WinCaptionButton::PaintSymbol(gfx::Canvas* canvas) {
|
||||||
flags.setAntiAlias(false);
|
flags.setAntiAlias(false);
|
||||||
flags.setColor(symbol_color);
|
flags.setColor(symbol_color);
|
||||||
flags.setStyle(cc::PaintFlags::kStroke_Style);
|
flags.setStyle(cc::PaintFlags::kStroke_Style);
|
||||||
// Stroke width jumps up a pixel every time we reach a new integral scale.
|
const int stroke_width = base::ClampRound(scale);
|
||||||
const int stroke_width = std::floor(scale);
|
|
||||||
flags.setStrokeWidth(stroke_width);
|
flags.setStrokeWidth(stroke_width);
|
||||||
|
|
||||||
switch (button_type_) {
|
switch (button_type_) {
|
||||||
|
|
|
@ -7,48 +7,28 @@
|
||||||
#include "base/numerics/safe_conversions.h"
|
#include "base/numerics/safe_conversions.h"
|
||||||
#include "ui/gfx/color_utils.h"
|
#include "ui/gfx/color_utils.h"
|
||||||
#include "ui/gfx/geometry/rect_conversions.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/geometry/skia_conversions.h"
|
||||||
#include "ui/gfx/scoped_canvas.h"
|
#include "ui/gfx/scoped_canvas.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Canvas::DrawRect's stroke can bleed out of |rect|'s bounds, so this draws a
|
// The rounded rect corner radius for MaximizeIcon and RestoreIcon in
|
||||||
// rectangle inset such that the result is constrained to |rect|'s size.
|
// Windows 11.
|
||||||
|
constexpr float kWin11RoundedCornerRadius = 1.5f;
|
||||||
|
|
||||||
void DrawRect(gfx::Canvas* canvas,
|
void DrawRect(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& rect,
|
const gfx::Rect& rect,
|
||||||
const cc::PaintFlags& flags) {
|
const cc::PaintFlags& flags) {
|
||||||
gfx::RectF rect_f(rect);
|
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;
|
float stroke_half_width = flags.getStrokeWidth() / 2;
|
||||||
rect_f.Inset(stroke_half_width);
|
rect_f.Inset(stroke_half_width);
|
||||||
|
|
||||||
canvas->DrawRect(rect_f, flags);
|
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
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
@ -58,7 +38,7 @@ WinIconPainter::~WinIconPainter() = default;
|
||||||
|
|
||||||
void WinIconPainter::PaintMinimizeIcon(gfx::Canvas* canvas,
|
void WinIconPainter::PaintMinimizeIcon(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& symbol_rect,
|
const gfx::Rect& symbol_rect,
|
||||||
const cc::PaintFlags& flags) {
|
cc::PaintFlags& flags) {
|
||||||
const int y = symbol_rect.CenterPoint().y();
|
const int y = symbol_rect.CenterPoint().y();
|
||||||
const gfx::Point p1 = gfx::Point(symbol_rect.x(), y);
|
const gfx::Point p1 = gfx::Point(symbol_rect.x(), y);
|
||||||
const gfx::Point p2 = gfx::Point(symbol_rect.right(), 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,
|
void WinIconPainter::PaintMaximizeIcon(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& symbol_rect,
|
const gfx::Rect& symbol_rect,
|
||||||
const cc::PaintFlags& flags) {
|
cc::PaintFlags& flags) {
|
||||||
DrawRect(canvas, symbol_rect, flags);
|
DrawRect(canvas, symbol_rect, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WinIconPainter::PaintRestoreIcon(gfx::Canvas* canvas,
|
void WinIconPainter::PaintRestoreIcon(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& symbol_rect,
|
const gfx::Rect& symbol_rect,
|
||||||
const cc::PaintFlags& flags) {
|
cc::PaintFlags& flags) {
|
||||||
const int separation = base::ClampFloor(2 * canvas->image_scale());
|
const int separation = base::ClampFloor(2 * canvas->image_scale());
|
||||||
gfx::Rect icon_rect = symbol_rect;
|
gfx::Rect icon_rect = symbol_rect;
|
||||||
icon_rect.Inset(gfx::Insets::TLBR(separation, 0, 0, separation));
|
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,
|
void WinIconPainter::PaintCloseIcon(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& symbol_rect,
|
const gfx::Rect& symbol_rect,
|
||||||
const cc::PaintFlags& flags) {
|
cc::PaintFlags& flags) {
|
||||||
// TODO(bsep): This sometimes draws misaligned at fractional device scales
|
// TODO(bsep): This sometimes draws misaligned at fractional device scales
|
||||||
// because the button's origin isn't necessarily aligned to pixels.
|
// because the button's origin isn't necessarily aligned to pixels.
|
||||||
cc::PaintFlags paint_flags = flags;
|
cc::PaintFlags paint_flags = flags;
|
||||||
|
@ -109,32 +89,57 @@ Win11IconPainter::~Win11IconPainter() = default;
|
||||||
|
|
||||||
void Win11IconPainter::PaintMaximizeIcon(gfx::Canvas* canvas,
|
void Win11IconPainter::PaintMaximizeIcon(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& symbol_rect,
|
const gfx::Rect& symbol_rect,
|
||||||
const cc::PaintFlags& flags) {
|
cc::PaintFlags& flags) {
|
||||||
cc::PaintFlags paint_flags = flags;
|
gfx::RectF rect_f(symbol_rect);
|
||||||
paint_flags.setAntiAlias(true);
|
flags.setAntiAlias(true);
|
||||||
|
const float corner_radius =
|
||||||
|
base::ClampFloor(kWin11RoundedCornerRadius * canvas->image_scale());
|
||||||
|
|
||||||
const float corner_radius = canvas->image_scale();
|
// The symbol rect is used as a bounding box, and the stroke is kept within.
|
||||||
DrawRoundRect(canvas, symbol_rect, corner_radius, flags);
|
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,
|
void Win11IconPainter::PaintRestoreIcon(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& symbol_rect,
|
const gfx::Rect& symbol_rect,
|
||||||
const cc::PaintFlags& flags) {
|
cc::PaintFlags& flags) {
|
||||||
const int separation = base::ClampFloor(2 * canvas->image_scale());
|
gfx::RectF rect_f(symbol_rect);
|
||||||
gfx::Rect icon_rect = symbol_rect;
|
const float separation = 2.0f * canvas->image_scale();
|
||||||
icon_rect.Inset(gfx::Insets::TLBR(separation, 0, 0, separation));
|
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;
|
// The symbol rect is used as a bounding box, and the stroke is kept within.
|
||||||
paint_flags.setAntiAlias(true);
|
const float stroke_half_width = flags.getStrokeWidth() / 2;
|
||||||
|
rect_f.Inset(stroke_half_width);
|
||||||
|
|
||||||
// Bottom left ("in front") rounded square.
|
// Shrink the rect to make space for the top rect.
|
||||||
const float bottom_rect_radius = canvas->image_scale();
|
rect_f.Inset(gfx::InsetsF::TLBR(separation, 0, 0, separation));
|
||||||
DrawRoundRect(canvas, icon_rect, bottom_rect_radius, flags);
|
|
||||||
|
|
||||||
// Top right ("behind") top+right edges of rounded square (2.5x).
|
gfx::RRectF rrect(rect_f, round_rect_radius);
|
||||||
icon_rect.Offset(separation, -separation);
|
|
||||||
|
|
||||||
const float top_rect_radius = 2.5f * canvas->image_scale();
|
// Bottom ("in front") rounded square.
|
||||||
DrawRoundRectEdges(canvas, icon_rect, top_rect_radius, flags);
|
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
|
} // namespace electron
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
|
// Copied from chrome/browser/ui/views/frame/windows_icon_painter.h
|
||||||
class WinIconPainter {
|
class WinIconPainter {
|
||||||
public:
|
public:
|
||||||
WinIconPainter();
|
WinIconPainter();
|
||||||
|
@ -22,22 +23,22 @@ class WinIconPainter {
|
||||||
// Paints the minimize icon for the button
|
// Paints the minimize icon for the button
|
||||||
virtual void PaintMinimizeIcon(gfx::Canvas* canvas,
|
virtual void PaintMinimizeIcon(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& symbol_rect,
|
const gfx::Rect& symbol_rect,
|
||||||
const cc::PaintFlags& flags);
|
cc::PaintFlags& flags);
|
||||||
|
|
||||||
// Paints the maximize icon for the button
|
// Paints the maximize icon for the button
|
||||||
virtual void PaintMaximizeIcon(gfx::Canvas* canvas,
|
virtual void PaintMaximizeIcon(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& symbol_rect,
|
const gfx::Rect& symbol_rect,
|
||||||
const cc::PaintFlags& flags);
|
cc::PaintFlags& flags);
|
||||||
|
|
||||||
// Paints the restore icon for the button
|
// Paints the restore icon for the button
|
||||||
virtual void PaintRestoreIcon(gfx::Canvas* canvas,
|
virtual void PaintRestoreIcon(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& symbol_rect,
|
const gfx::Rect& symbol_rect,
|
||||||
const cc::PaintFlags& flags);
|
cc::PaintFlags& flags);
|
||||||
|
|
||||||
// Paints the close icon for the button
|
// Paints the close icon for the button
|
||||||
virtual void PaintCloseIcon(gfx::Canvas* canvas,
|
virtual void PaintCloseIcon(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& symbol_rect,
|
const gfx::Rect& symbol_rect,
|
||||||
const cc::PaintFlags& flags);
|
cc::PaintFlags& flags);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Win11IconPainter : public WinIconPainter {
|
class Win11IconPainter : public WinIconPainter {
|
||||||
|
@ -52,12 +53,12 @@ class Win11IconPainter : public WinIconPainter {
|
||||||
// Paints the maximize icon for the button
|
// Paints the maximize icon for the button
|
||||||
void PaintMaximizeIcon(gfx::Canvas* canvas,
|
void PaintMaximizeIcon(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& symbol_rect,
|
const gfx::Rect& symbol_rect,
|
||||||
const cc::PaintFlags& flags) override;
|
cc::PaintFlags& flags) override;
|
||||||
|
|
||||||
// Paints the restore icon for the button
|
// Paints the restore icon for the button
|
||||||
void PaintRestoreIcon(gfx::Canvas* canvas,
|
void PaintRestoreIcon(gfx::Canvas* canvas,
|
||||||
const gfx::Rect& symbol_rect,
|
const gfx::Rect& symbol_rect,
|
||||||
const cc::PaintFlags& flags) override;
|
cc::PaintFlags& flags) override;
|
||||||
};
|
};
|
||||||
} // namespace electron
|
} // namespace electron
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue