feat: customize border radius of Views (#42320)

* feat: add View#setBorderRadius

test: initial setBorderRadius tests

fix: robustly set border radius

chore: add PAUSE_CAPTURE_TESTS for easier screencap dev

feat: add view border radius support

test: view border radius

refactor: cleanup view code

* maybe delay capture to fix tests?

* refactor: retry screen captures in an attempt to fix flakiness

* refactor: ScreenCapture constructor no longer async

* increase screen capture timeout, feels a little short

* refactor: move rounded rect util into chromium_src

* skip some capture tests on mas
This commit is contained in:
Sam Maddock 2024-07-16 20:16:25 -04:00 committed by GitHub
parent cbd11bb605
commit 778d3098a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 261 additions and 90 deletions

View file

@ -10,6 +10,7 @@
#include <string>
#include <utility>
#include "ash/style/rounded_rect_cutout_path_builder.h"
#include "gin/data_object_builder.h"
#include "gin/wrappable.h"
#include "shell/browser/javascript_environment.h"
@ -338,6 +339,38 @@ void View::SetBackgroundColor(std::optional<WrappedSkColor> color) {
view_->SetBackground(color ? views::CreateSolidBackground(*color) : nullptr);
}
void View::SetBorderRadius(int radius) {
border_radius_ = radius;
ApplyBorderRadius();
}
void View::ApplyBorderRadius() {
if (!border_radius_.has_value() || !view_)
return;
auto size = view_->bounds().size();
// Restrict border radius to the constraints set in the path builder class.
// If the constraints are exceeded, the builder will crash.
int radius;
{
float r = border_radius_.value() * 1.f;
r = std::min(r, size.width() / 2.f);
r = std::min(r, size.height() / 2.f);
r = std::max(r, 0.f);
radius = std::floor(r);
}
// RoundedRectCutoutPathBuilder has a minimum size of 32 x 32.
if (radius > 0 && size.width() >= 32 && size.height() >= 32) {
auto builder = ash::RoundedRectCutoutPathBuilder(gfx::SizeF(size));
builder.CornerRadius(radius);
view_->SetClipPath(builder.Build());
} else {
view_->SetClipPath(SkPath());
}
}
void View::SetVisible(bool visible) {
if (!view_)
return;
@ -345,6 +378,7 @@ void View::SetVisible(bool visible) {
}
void View::OnViewBoundsChanged(views::View* observed_view) {
ApplyBorderRadius();
Emit("bounds-changed");
}
@ -393,6 +427,7 @@ void View::BuildPrototype(v8::Isolate* isolate,
.SetMethod("setBounds", &View::SetBounds)
.SetMethod("getBounds", &View::GetBounds)
.SetMethod("setBackgroundColor", &View::SetBackgroundColor)
.SetMethod("setBorderRadius", &View::SetBorderRadius)
.SetMethod("setLayout", &View::SetLayout)
.SetMethod("setVisible", &View::SetVisible);
}

View file

@ -37,6 +37,7 @@ class View : public gin_helper::EventEmitter<View>,
void SetLayout(v8::Isolate* isolate, v8::Local<v8::Object> value);
std::vector<v8::Local<v8::Value>> GetChildren();
void SetBackgroundColor(std::optional<WrappedSkColor> color);
void SetBorderRadius(int radius);
void SetVisible(bool visible);
// views::ViewObserver
@ -44,6 +45,7 @@ class View : public gin_helper::EventEmitter<View>,
void OnViewIsDeleting(views::View* observed_view) override;
views::View* view() const { return view_; }
std::optional<int> border_radius() const { return border_radius_; }
// disable copy
View(const View&) = delete;
@ -58,9 +60,11 @@ class View : public gin_helper::EventEmitter<View>,
void set_delete_view(bool should) { delete_view_ = should; }
private:
void ApplyBorderRadius();
void ReorderChildView(gin::Handle<View> child, size_t index);
std::vector<v8::Global<v8::Object>> child_views_;
std::optional<int> border_radius_;
bool delete_view_ = true;
raw_ptr<views::View> view_ = nullptr;

View file

@ -20,6 +20,8 @@
#include "shell/common/options_switches.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/base/hit_test.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/widget/widget.h"
@ -65,6 +67,25 @@ void WebContentsView::SetBackgroundColor(std::optional<WrappedSkColor> color) {
}
}
void WebContentsView::SetBorderRadius(int radius) {
View::SetBorderRadius(radius);
ApplyBorderRadius();
}
void WebContentsView::ApplyBorderRadius() {
if (border_radius().has_value() && api_web_contents_ && view()->GetWidget()) {
auto* web_view = api_web_contents_->inspectable_web_contents()
->GetView()
->contents_web_view();
// WebView won't exist for offscreen rendering.
if (web_view) {
web_view->holder()->SetCornerRadii(
gfx::RoundedCornersF(border_radius().value()));
}
}
}
int WebContentsView::NonClientHitTest(const gfx::Point& point) {
if (api_web_contents_) {
gfx::Point local_point(point);
@ -93,6 +114,7 @@ void WebContentsView::OnViewAddedToWidget(views::View* observed_view) {
// because that's handled in the WebContents dtor called prior.
api_web_contents_->SetOwnerWindow(native_window);
native_window->AddDraggableRegionProvider(this);
ApplyBorderRadius();
}
void WebContentsView::OnViewRemovedFromWidget(views::View* observed_view) {
@ -198,6 +220,7 @@ void WebContentsView::BuildPrototype(
prototype->SetClassName(gin::StringToV8(isolate, "WebContentsView"));
gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetMethod("setBackgroundColor", &WebContentsView::SetBackgroundColor)
.SetMethod("setBorderRadius", &WebContentsView::SetBorderRadius)
.SetProperty("webContents", &WebContentsView::GetWebContents);
}

View file

@ -39,6 +39,7 @@ class WebContentsView : public View,
// Public APIs.
gin::Handle<WebContents> GetWebContents(v8::Isolate* isolate);
void SetBackgroundColor(std::optional<WrappedSkColor> color);
void SetBorderRadius(int radius);
int NonClientHitTest(const gfx::Point& point) override;
@ -57,6 +58,8 @@ class WebContentsView : public View,
private:
static gin_helper::WrappableBase* New(gin_helper::Arguments* args);
void ApplyBorderRadius();
// Keep a reference to v8 wrapper.
v8::Global<v8::Value> web_contents_;
raw_ptr<api::WebContents> api_web_contents_;