Draw PiP 2.0 window frame borders for classic theme on Linux
We draw the borders in a way similar to BrowserFrameViewLinux when
classic theme is enabled on Linux, and still need to handle other
platforms in upcoming CLs. Since there are many codes in common, I'm
adding a utils file to reuse codes.
Bug: 1366897
Change-Id: Id497ee53082fca41ac2c3c7e5eb63ed78d6c8480
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4018006
Commit-Queue: Yiren Wang <yrw@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1072039}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index b25ca2e..c2d21e0a 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -5323,6 +5323,8 @@
"views/frame/browser_frame_view_linux.h",
"views/frame/browser_frame_view_linux_native.cc",
"views/frame/browser_frame_view_linux_native.h",
+ "views/frame/browser_frame_view_paint_utils_linux.cc",
+ "views/frame/browser_frame_view_paint_utils_linux.h",
]
}
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_layout_linux.cc b/chrome/browser/ui/views/frame/browser_frame_view_layout_linux.cc
index 54508219..7dc44fcc5 100644
--- a/chrome/browser/ui/views/frame/browser_frame_view_layout_linux.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_view_layout_linux.cc
@@ -7,8 +7,7 @@
#include "base/i18n/rtl.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/views/frame/browser_frame_view_linux.h"
-#include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
-#include "ui/gfx/geometry/rect.h"
+#include "chrome/browser/ui/views/frame/browser_frame_view_paint_utils_linux.h"
namespace {
@@ -45,46 +44,10 @@
}
gfx::Insets BrowserFrameViewLayoutLinux::RestoredFrameBorderInsets() const {
- if (!delegate_->ShouldDrawRestoredFrameShadow()) {
- gfx::Insets insets =
- OpaqueBrowserFrameViewLayout::RestoredFrameBorderInsets();
- insets.set_top(0);
- return insets;
- }
-
- // The border must be at least as large as the shadow.
- gfx::Rect frame_extents;
- const auto tiled_edges = delegate_->GetTiledEdges();
- for (const auto& shadow_value : view_->GetShadowValues()) {
- const auto shadow_radius = shadow_value.blur() / 4;
- const gfx::InsetsF shadow_insets =
- gfx::InsetsF::TLBR(tiled_edges.top ? 0 : shadow_radius,
- tiled_edges.left ? 0 : shadow_radius,
- tiled_edges.bottom ? 0 : shadow_radius,
- tiled_edges.right ? 0 : shadow_radius);
- gfx::RectF shadow_extents;
- shadow_extents.Inset(-shadow_insets);
- if (!tiled_edges.top) {
- shadow_extents.set_y(shadow_extents.y() + shadow_value.y());
- // If the bottom edge is tiled, fix the height to compensate the addition
- // to the top inset made above.
- if (tiled_edges.bottom)
- shadow_extents.set_height(-shadow_extents.y());
- }
- frame_extents.Union(gfx::ToEnclosingRect(shadow_extents));
- }
-
- // The border must be at least as large as the input region.
- const auto insets = gfx::Insets::TLBR(tiled_edges.top ? 0 : kResizeBorder,
- tiled_edges.left ? 0 : kResizeBorder,
- tiled_edges.bottom ? 0 : kResizeBorder,
- tiled_edges.right ? 0 : kResizeBorder);
- gfx::Rect input_extents;
- input_extents.Inset(-insets);
- frame_extents.Union(input_extents);
-
- return gfx::Insets::TLBR(-frame_extents.y(), -frame_extents.x(),
- frame_extents.bottom(), frame_extents.right());
+ return GetRestoredFrameBorderInsetsLinux(
+ delegate_->ShouldDrawRestoredFrameShadow(),
+ OpaqueBrowserFrameViewLayout::RestoredFrameBorderInsets(),
+ delegate_->GetTiledEdges(), view_->GetShadowValues(), kResizeBorder);
}
gfx::Insets BrowserFrameViewLayoutLinux::RestoredFrameEdgeInsets() const {
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_linux.cc b/chrome/browser/ui/views/frame/browser_frame_view_linux.cc
index a06c5f5..257ced2 100644
--- a/chrome/browser/ui/views/frame/browser_frame_view_linux.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_view_linux.cc
@@ -5,16 +5,12 @@
#include "chrome/browser/ui/views/frame/browser_frame_view_linux.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/frame/browser_frame_view_paint_utils_linux.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/desktop_browser_frame_aura_linux.h"
-#include "ui/color/color_id.h"
-#include "ui/color/color_provider.h"
#include "ui/gfx/geometry/skia_conversions.h"
-#include "ui/gfx/scoped_canvas.h"
-#include "ui/gfx/skia_paint_util.h"
#include "ui/linux/linux_ui.h"
#include "ui/views/layout/layout_provider.h"
-#include "ui/views/window/frame_background.h"
#include "ui/views/window/window_button_order_provider.h"
BrowserFrameViewLinux::BrowserFrameViewLinux(
@@ -79,42 +75,10 @@
void BrowserFrameViewLinux::PaintRestoredFrameBorder(
gfx::Canvas* canvas) const {
- auto clip = GetRestoredClipRegion();
- bool showing_shadow = ShouldDrawRestoredFrameShadow();
-
- if (auto* frame_bg = frame_background()) {
- gfx::ScopedCanvas scoped_canvas(canvas);
- canvas->sk_canvas()->clipRRect(clip, SkClipOp::kIntersect, true);
- auto border = layout_->MirroredFrameBorderInsets();
- auto shadow_inset = showing_shadow ? border : gfx::Insets();
- frame_bg->PaintMaximized(canvas, GetNativeTheme(), GetColorProvider(),
- shadow_inset.left(), shadow_inset.top(),
- width() - shadow_inset.width());
- if (!showing_shadow)
- frame_bg->FillFrameBorders(canvas, this, border.left(), border.right(),
- border.bottom());
- }
-
- // If rendering shadows, draw a 1px exterior border, otherwise
- // draw a 1px interior border.
- const SkScalar one_pixel = SkFloatToScalar(1 / canvas->image_scale());
- auto rect = clip;
- if (showing_shadow)
- rect.outset(one_pixel, one_pixel);
- else
- clip.inset(one_pixel, one_pixel);
-
- cc::PaintFlags flags;
- flags.setColor(GetColorProvider()->GetColor(
- showing_shadow ? ui::kColorBubbleBorderWhenShadowPresent
- : ui::kColorBubbleBorder));
- flags.setAntiAlias(true);
- if (showing_shadow)
- flags.setLooper(gfx::CreateShadowDrawLooper(GetShadowValues()));
-
- gfx::ScopedCanvas scoped_canvas(canvas);
- canvas->sk_canvas()->clipRRect(clip, SkClipOp::kDifference, true);
- canvas->sk_canvas()->drawRRect(rect, flags);
+ PaintRestoredFrameBorderLinux(
+ *canvas, *this, frame_background(), GetRestoredClipRegion(),
+ ShouldDrawRestoredFrameShadow(), layout_->MirroredFrameBorderInsets(),
+ GetShadowValues());
}
void BrowserFrameViewLinux::GetWindowMask(const gfx::Size& size,
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_paint_utils_linux.cc b/chrome/browser/ui/views/frame/browser_frame_view_paint_utils_linux.cc
new file mode 100644
index 0000000..0c006d6
--- /dev/null
+++ b/chrome/browser/ui/views/frame/browser_frame_view_paint_utils_linux.cc
@@ -0,0 +1,101 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/frame/browser_frame_view_paint_utils_linux.h"
+
+#include "ui/color/color_provider.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/scoped_canvas.h"
+#include "ui/gfx/skia_paint_util.h"
+#include "ui/views/view.h"
+#include "ui/views/window/frame_background.h"
+
+void PaintRestoredFrameBorderLinux(gfx::Canvas& canvas,
+ const views::View& view,
+ views::FrameBackground* frame_background,
+ const SkRRect& clip,
+ bool showing_shadow,
+ const gfx::Insets& border,
+ const gfx::ShadowValues& shadow_values) {
+ const auto* color_provider = view.GetColorProvider();
+ if (frame_background) {
+ gfx::ScopedCanvas scoped_canvas(&canvas);
+ canvas.sk_canvas()->clipRRect(clip, SkClipOp::kIntersect, true);
+ auto shadow_inset = showing_shadow ? border : gfx::Insets();
+ frame_background->PaintMaximized(
+ &canvas, view.GetNativeTheme(), color_provider, shadow_inset.left(),
+ shadow_inset.top(), view.width() - shadow_inset.width());
+ if (!showing_shadow)
+ frame_background->FillFrameBorders(&canvas, &view, border.left(),
+ border.right(), border.bottom());
+ }
+
+ // If rendering shadows, draw a 1px exterior border, otherwise draw a 1px
+ // interior border.
+ const SkScalar one_pixel = SkFloatToScalar(1 / canvas.image_scale());
+ SkRRect outset_rect = clip;
+ SkRRect inset_rect = clip;
+ if (showing_shadow)
+ outset_rect.outset(one_pixel, one_pixel);
+ else
+ inset_rect.inset(one_pixel, one_pixel);
+
+ cc::PaintFlags flags;
+ flags.setColor(color_provider->GetColor(
+ showing_shadow ? ui::kColorBubbleBorderWhenShadowPresent
+ : ui::kColorBubbleBorder));
+ flags.setAntiAlias(true);
+ if (showing_shadow)
+ flags.setLooper(gfx::CreateShadowDrawLooper(shadow_values));
+
+ gfx::ScopedCanvas scoped_canvas(&canvas);
+ canvas.sk_canvas()->clipRRect(inset_rect, SkClipOp::kDifference, true);
+ canvas.sk_canvas()->drawRRect(outset_rect, flags);
+}
+
+gfx::Insets GetRestoredFrameBorderInsetsLinux(
+ bool showing_shadow,
+ const gfx::Insets& default_border,
+ const ui::WindowTiledEdges& tiled_edges,
+ const gfx::ShadowValues& shadow_values,
+ int resize_border) {
+ if (!showing_shadow) {
+ auto no_shadow_border = default_border;
+ no_shadow_border.set_top(0);
+ return no_shadow_border;
+ }
+
+ // The border must be at least as large as the shadow.
+ gfx::Rect frame_extents;
+ for (const auto& shadow_value : shadow_values) {
+ const auto shadow_radius = shadow_value.blur() / 4;
+ const gfx::InsetsF shadow_insets =
+ gfx::InsetsF::TLBR(tiled_edges.top ? 0 : shadow_radius,
+ tiled_edges.left ? 0 : shadow_radius,
+ tiled_edges.bottom ? 0 : shadow_radius,
+ tiled_edges.right ? 0 : shadow_radius);
+ gfx::RectF shadow_extents;
+ shadow_extents.Inset(-shadow_insets);
+ if (!tiled_edges.top) {
+ shadow_extents.set_y(shadow_extents.y() + shadow_value.y());
+ // If the bottom edge is tiled, fix the height to compensate the addition
+ // to the top inset made above.
+ if (tiled_edges.bottom)
+ shadow_extents.set_height(-shadow_extents.y());
+ }
+ frame_extents.Union(gfx::ToEnclosingRect(shadow_extents));
+ }
+
+ // The border must be at least as large as the input region.
+ const auto insets = gfx::Insets::TLBR(tiled_edges.top ? 0 : resize_border,
+ tiled_edges.left ? 0 : resize_border,
+ tiled_edges.bottom ? 0 : resize_border,
+ tiled_edges.right ? 0 : resize_border);
+ gfx::Rect input_extents;
+ input_extents.Inset(-insets);
+ frame_extents.Union(input_extents);
+
+ return gfx::Insets::TLBR(-frame_extents.y(), -frame_extents.x(),
+ frame_extents.bottom(), frame_extents.right());
+}
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_paint_utils_linux.h b/chrome/browser/ui/views/frame/browser_frame_view_paint_utils_linux.h
new file mode 100644
index 0000000..06e4a47
--- /dev/null
+++ b/chrome/browser/ui/views/frame/browser_frame_view_paint_utils_linux.h
@@ -0,0 +1,43 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_VIEW_PAINT_UTILS_LINUX_H_
+#define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_VIEW_PAINT_UTILS_LINUX_H_
+
+#include "ui/base/ui_base_types.h"
+#include "ui/gfx/shadow_value.h"
+
+class SkRRect;
+
+namespace gfx {
+class Canvas;
+class Insets;
+} // namespace gfx
+
+namespace views {
+class FrameBackground;
+class View;
+} // namespace views
+
+// Paint the window borders, shadows and the background of the top bar area for
+// BrowserFrameViewLinux and PictureInPictureBrowserFrameView on Linux.
+void PaintRestoredFrameBorderLinux(gfx::Canvas& canvas,
+ const views::View& view,
+ views::FrameBackground* frame_background,
+ const SkRRect& clip,
+ bool showing_shadow,
+ const gfx::Insets& border,
+ const gfx::ShadowValues& shadow_values);
+
+// Get the insets from the native window edge to the client view when the window
+// is restored for BrowserFrameViewLayoutLinux and
+// PictureInPictureBrowserFrameView on Linux.
+gfx::Insets GetRestoredFrameBorderInsetsLinux(
+ bool showing_shadow,
+ const gfx::Insets& default_border,
+ const ui::WindowTiledEdges& tiled_edges,
+ const gfx::ShadowValues& shadow_values,
+ int resize_border);
+
+#endif // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_VIEW_PAINT_UTILS_LINUX_H_
diff --git a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
index ea3a3bab..28493ea 100644
--- a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
@@ -8,6 +8,7 @@
#include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/overlay/overlay_window_image_button.h"
@@ -25,6 +26,7 @@
#include "ui/compositor/layer.h"
#include "ui/display/screen.h"
#include "ui/views/widget/widget_delegate.h"
+#include "ui/views/window/frame_background.h"
#include "ui/views/window/window_shape.h"
#if !BUILDFLAG(IS_MAC)
@@ -33,6 +35,7 @@
#endif
#if BUILDFLAG(IS_LINUX)
+#include "chrome/browser/ui/views/frame/browser_frame_view_paint_utils_linux.h"
#include "chrome/browser/ui/views/frame/desktop_browser_frame_aura_linux.h"
#endif
@@ -47,7 +50,13 @@
// The height of the controls bar at the top of the window.
constexpr int kTopControlsHeight = 30;
-constexpr int kWindowBorderThickness = 10;
+// Frame border when window shadow is not drawn.
+constexpr int kFrameBorderThickness = 4;
+
+#if BUILDFLAG(IS_LINUX)
+constexpr int kResizeBorder = 10;
+#endif
+
constexpr int kResizeAreaCornerSize = 16;
// The window has a smaller minimum size than normal Chrome windows.
@@ -150,6 +159,10 @@
->ExitPictureInPicture();
},
base::Unretained(this))));
+
+#if BUILDFLAG(IS_LINUX)
+ frame_background_ = std::make_unique<views::FrameBackground>();
+#endif
}
PictureInPictureBrowserFrameView::~PictureInPictureBrowserFrameView() = default;
@@ -256,17 +269,13 @@
for (ContentSettingImageView* view : content_setting_views_)
view->SetIconColor(color_provider->GetColor(kColorOmniboxResultsIcon));
-#if BUILDFLAG(IS_LINUX)
- // If the top bar background is already drawn by window_frame_provider_, skip
- // drawing it again below.
- if (window_frame_provider_) {
- BrowserNonClientFrameView::OnThemeChanged();
- return;
- }
-#endif
+#if !BUILDFLAG(IS_LINUX)
+ // On Linux the top bar background will be drawn in OnPaint().
controls_container_view_->SetBackground(views::CreateSolidBackground(
SkColorSetA(color_provider->GetColor(kColorPipWindowControlsBackground),
SK_AlphaOPAQUE)));
+#endif
+
BrowserNonClientFrameView::OnThemeChanged();
}
@@ -302,7 +311,7 @@
}
gfx::Insets PictureInPictureBrowserFrameView::GetInputInsets() const {
- return gfx::Insets(ShouldDrawFrameShadow() ? -kWindowBorderThickness : 0);
+ return gfx::Insets(ShouldDrawFrameShadow() ? -kResizeBorder : 0);
}
SkRRect PictureInPictureBrowserFrameView::GetRestoredClipRegion() const {
@@ -315,6 +324,9 @@
float radius_dip = 0;
if (window_frame_provider_) {
radius_dip = window_frame_provider_->GetTopCornerRadiusDip();
+ } else {
+ radius_dip = ChromeLayoutProvider::Get()->GetCornerRadiusMetric(
+ views::Emphasis::kHigh);
}
SkVector radii[4]{{radius_dip, radius_dip}, {radius_dip, radius_dip}, {}, {}};
SkRRect clip;
@@ -497,12 +509,27 @@
// views::View implementations:
void PictureInPictureBrowserFrameView::OnPaint(gfx::Canvas* canvas) {
#if BUILDFLAG(IS_LINUX)
+ // Draw the PiP window frame borders and shadows, including the top bar
+ // background.
if (window_frame_provider_) {
- // Draw the PiP window frame borders and shadows, including the top bar
- // background.
window_frame_provider_->PaintWindowFrame(
canvas, GetLocalBounds(), GetTopAreaHeight(), ShouldPaintAsActive(),
frame()->tiled_edges());
+ } else {
+ DCHECK(frame_background_);
+ frame_background_->set_frame_color(
+ GetFrameColor(BrowserFrameActiveState::kUseCurrent));
+ frame_background_->set_use_custom_frame(frame()->UseCustomFrame());
+ frame_background_->set_is_active(ShouldPaintAsActive());
+ frame_background_->set_theme_image(GetFrameImage());
+ frame_background_->set_theme_image_y_inset(
+ ThemeProperties::kFrameHeightAboveTabs - GetTopAreaHeight());
+ frame_background_->set_theme_overlay_image(GetFrameOverlayImage());
+ frame_background_->set_top_area_height(GetTopAreaHeight());
+ PaintRestoredFrameBorderLinux(
+ *canvas, *this, frame_background_.get(), GetRestoredClipRegion(),
+ ShouldDrawFrameShadow(), MirroredFrameBorderInsets(),
+ GetShadowValues());
}
#endif
BrowserNonClientFrameView::OnPaint(canvas);
@@ -576,8 +603,12 @@
tiled_edges.bottom ? 0 : insets.bottom(),
tiled_edges.right ? 0 : insets.right());
}
+ return GetRestoredFrameBorderInsetsLinux(
+ ShouldDrawFrameShadow(), gfx::Insets(kFrameBorderThickness),
+ frame()->tiled_edges(), GetShadowValues(), kResizeBorder);
+#else
+ return gfx::Insets(kFrameBorderThickness);
#endif
- return gfx::Insets(kWindowBorderThickness);
}
int PictureInPictureBrowserFrameView::GetTopAreaHeight() const {
@@ -587,7 +618,11 @@
#if BUILDFLAG(IS_LINUX)
void PictureInPictureBrowserFrameView::SetWindowFrameProvider(
ui::WindowFrameProvider* window_frame_provider) {
+ DCHECK(window_frame_provider);
window_frame_provider_ = window_frame_provider;
+
+ // Only one of window_frame_provider_ and frame_background_ will be used.
+ frame_background_.reset();
}
bool PictureInPictureBrowserFrameView::ShouldDrawFrameShadow() const {
@@ -595,6 +630,13 @@
frame()->native_browser_frame())
->ShouldDrawRestoredFrameShadow();
}
+
+// static
+gfx::ShadowValues PictureInPictureBrowserFrameView::GetShadowValues() {
+ int elevation = ChromeLayoutProvider::Get()->GetShadowElevationMetric(
+ views::Emphasis::kMaximum);
+ return gfx::ShadowValue::MakeMdShadowValues(elevation);
+}
#endif
BEGIN_METADATA(PictureInPictureBrowserFrameView, BrowserNonClientFrameView)
diff --git a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.h b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.h
index 92e3eae..9dd02c5 100644
--- a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.h
@@ -25,6 +25,7 @@
#endif
namespace views {
+class FrameBackground;
class Label;
}
@@ -155,6 +156,10 @@
// Returns whether a client-side shadow should be drawn for the window.
bool ShouldDrawFrameShadow() const;
+
+ // Gets the shadow metrics (radius, offset, and number of shadows) even if
+ // shadows are not drawn.
+ static gfx::ShadowValues GetShadowValues();
#endif
private:
@@ -185,6 +190,10 @@
// Used to draw window frame borders and shadow on Linux when GTK theme is
// enabled.
raw_ptr<ui::WindowFrameProvider> window_frame_provider_ = nullptr;
+
+ // Used to draw window frame borders and shadow on Linux when classic theme is
+ // enabled.
+ std::unique_ptr<views::FrameBackground> frame_background_;
#endif
};