Skip to content

Commit

Permalink
Start Lacros port of non-browser-window client-side decoration
Browse files Browse the repository at this point in the history
Extract minimum viable client-side decoration functionality from Ash to
a common base class and compile that into Lacros. This allows Lacros to
start doing client-side decoration for non-browser windows like Ash.

This commit does not attempt to port all of the Ash functionality (e.g.
hiding the titlebar when in tablet mode) to Lacros. Remaining unported
functionality can be ported as desired, hopefully more easily now that
the inheritance scaffolding is in place.

Bug: 1338969
Change-Id: I8ab5f2c0eaea22205fd1fcef0fb3634c62e1823a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3975540
Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
Reviewed-by: Peter Kasting <pkasting@chromium.org>
Commit-Queue: Alex Yang <aycyang@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1070715}
  • Loading branch information
Alex Yang authored and Chromium LUCI CQ committed Nov 12, 2022
1 parent 29d6909 commit 7e4abc5
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 227 deletions.
192 changes: 2 additions & 190 deletions ash/frame/non_client_frame_view_ash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "chromeos/ui/frame/frame_utils.h"
#include "chromeos/ui/frame/header_view.h"
#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
#include "chromeos/ui/frame/non_client_frame_view_base.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
Expand Down Expand Up @@ -150,89 +151,15 @@ class NonClientFrameViewAshImmersiveHelper : public WindowStateObserver,
immersive_fullscreen_controller_;
};

// View which takes up the entire widget and contains the HeaderView. HeaderView
// is a child of OverlayView to avoid creating a larger texture than necessary
// when painting the HeaderView to its own layer.
class NonClientFrameViewAsh::OverlayView : public views::View,
public views::ViewTargeterDelegate {
public:
METADATA_HEADER(OverlayView);
explicit OverlayView(chromeos::HeaderView* header_view);
OverlayView(const OverlayView&) = delete;
OverlayView& operator=(const OverlayView&) = delete;
~OverlayView() override;

// views::View:
void Layout() override;

private:
// views::ViewTargeterDelegate:
bool DoesIntersectRect(const views::View* target,
const gfx::Rect& rect) const override;

chromeos::HeaderView* header_view_;
};

NonClientFrameViewAsh::OverlayView::OverlayView(
chromeos::HeaderView* header_view)
: header_view_(header_view) {
AddChildView(header_view);
SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
}

NonClientFrameViewAsh::OverlayView::~OverlayView() = default;

void NonClientFrameViewAsh::OverlayView::Layout() {
// Layout |header_view_| because layout affects the result of
// GetPreferredOnScreenHeight().
header_view_->Layout();

int onscreen_height = header_view_->GetPreferredOnScreenHeight();
int height = header_view_->GetPreferredHeight();
if (onscreen_height == 0 || !GetVisible()) {
header_view_->SetVisible(false);
// Make sure the correct width is set even when immersive is enabled, but
// never revealed yet.
header_view_->SetBounds(0, 0, width(), height);
} else {
header_view_->SetBounds(0, onscreen_height - height, width(), height);
header_view_->SetVisible(true);
}
}

bool NonClientFrameViewAsh::OverlayView::DoesIntersectRect(
const views::View* target,
const gfx::Rect& rect) const {
CHECK_EQ(target, this);
// Grab events in the header view. Return false for other events so that they
// can be handled by the client view.
return header_view_->HitTestRect(rect);
}

BEGIN_METADATA(NonClientFrameViewAsh, OverlayView, views::View)
END_METADATA

NonClientFrameViewAsh::NonClientFrameViewAsh(views::Widget* frame)
: frame_(frame),
header_view_(new chromeos::HeaderView(frame, this)),
overlay_view_(new OverlayView(header_view_)),
: chromeos::NonClientFrameViewBase(frame),
frame_context_menu_controller_(
std::make_unique<FrameContextMenuController>(frame, this)) {
DCHECK(frame_);

header_view_->Init();
header_view_->set_immersive_mode_changed_callback(base::BindRepeating(
&NonClientFrameViewAsh::InvalidateLayout, weak_factory_.GetWeakPtr()));

aura::Window* frame_window = frame->GetNativeWindow();
window_util::InstallResizeHandleWindowTargeterForWindow(frame_window);
// |header_view_| is set as the non client view's overlay view so that it can
// overlay the web contents in immersive fullscreen.
// TODO(pkasting): Consider having something like NonClientViewAsh, which
// would avoid the need to expose an "overlay view" concept on the
// cross-platform class, and might allow for simpler creation/ownership/
// plumbing.
frame->non_client_view()->SetOverlayView(overlay_view_);

// A delegate may be set which takes over the responsibilities of the
// NonClientFrameViewAshImmersiveHelper. This is the case for container apps
Expand All @@ -250,8 +177,6 @@ NonClientFrameViewAsh::NonClientFrameViewAsh(views::Widget* frame)

header_view_->set_context_menu_controller(
frame_context_menu_controller_.get());

UpdateDefaultFrameColors();
}

NonClientFrameViewAsh::~NonClientFrameViewAsh() = default;
Expand Down Expand Up @@ -291,91 +216,6 @@ gfx::Rect NonClientFrameViewAsh::GetClientBoundsForWindowBounds(
return client_bounds;
}

gfx::Rect NonClientFrameViewAsh::GetBoundsForClientView() const {
gfx::Rect client_bounds = bounds();
client_bounds.Inset(gfx::Insets::TLBR(NonClientTopBorderHeight(), 0, 0, 0));
return client_bounds;
}

gfx::Rect NonClientFrameViewAsh::GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const {
gfx::Rect window_bounds = client_bounds;
window_bounds.Inset(gfx::Insets::TLBR(-NonClientTopBorderHeight(), 0, 0, 0));
return window_bounds;
}

int NonClientFrameViewAsh::NonClientHitTest(const gfx::Point& point) {
return chromeos::FrameBorderNonClientHitTest(this, point);
}

void NonClientFrameViewAsh::GetWindowMask(const gfx::Size& size,
SkPath* window_mask) {
// No window masks in Aura.
}

void NonClientFrameViewAsh::ResetWindowControls() {
header_view_->ResetWindowControls();
}

void NonClientFrameViewAsh::UpdateWindowIcon() {}

void NonClientFrameViewAsh::UpdateWindowTitle() {
header_view_->SchedulePaintForTitle();
}

void NonClientFrameViewAsh::SizeConstraintsChanged() {
header_view_->UpdateCaptionButtons();
}

views::View::Views NonClientFrameViewAsh::GetChildrenInZOrder() {
return header_view_->GetFrameHeader()->GetAdjustedChildrenInZOrder(this);
}

gfx::Size NonClientFrameViewAsh::CalculatePreferredSize() const {
gfx::Size pref = frame_->client_view()->GetPreferredSize();
gfx::Rect bounds(0, 0, pref.width(), pref.height());
return frame_->non_client_view()
->GetWindowBoundsForClientBounds(bounds)
.size();
}

void NonClientFrameViewAsh::Layout() {
views::NonClientFrameView::Layout();
if (!GetFrameEnabled())
return;
aura::Window* frame_window = frame_->GetNativeWindow();
frame_window->SetProperty(aura::client::kTopViewInset,
NonClientTopBorderHeight());
}

gfx::Size NonClientFrameViewAsh::GetMinimumSize() const {
if (!GetFrameEnabled())
return gfx::Size();

gfx::Size min_client_view_size(frame_->client_view()->GetMinimumSize());
return gfx::Size(
std::max(header_view_->GetMinimumWidth(), min_client_view_size.width()),
NonClientTopBorderHeight() + min_client_view_size.height());
}

gfx::Size NonClientFrameViewAsh::GetMaximumSize() const {
gfx::Size max_client_size(frame_->client_view()->GetMaximumSize());
int width = 0;
int height = 0;

if (max_client_size.width() > 0)
width = std::max(header_view_->GetMinimumWidth(), max_client_size.width());
if (max_client_size.height() > 0)
height = NonClientTopBorderHeight() + max_client_size.height();

return gfx::Size(width, height);
}

void NonClientFrameViewAsh::OnThemeChanged() {
NonClientFrameView::OnThemeChanged();
UpdateDefaultFrameColors();
}

bool NonClientFrameViewAsh::ShouldShowContextMenu(
views::View* source,
const gfx::Point& screen_coords_point) {
Expand All @@ -400,16 +240,6 @@ void NonClientFrameViewAsh::SetShouldPaintHeader(bool paint) {
header_view_->SetShouldPaintHeader(paint);
}

int NonClientFrameViewAsh::NonClientTopBorderHeight() const {
// The frame should not occupy the window area when it's in fullscreen,
// not visible or disabled.
if (frame_->IsFullscreen() || !GetFrameEnabled() ||
header_view_->in_immersive_mode()) {
return 0;
}
return header_view_->GetPreferredHeight();
}

int NonClientFrameViewAsh::NonClientTopBorderPreferredHeight() const {
return header_view_->GetPreferredHeight();
}
Expand Down Expand Up @@ -474,24 +304,6 @@ void NonClientFrameViewAsh::AddedToWidget() {
std::make_unique<HighlightBorderOverlay>(GetWidget());
}

// views::NonClientFrameView:
bool NonClientFrameViewAsh::DoesIntersectRect(const views::View* target,
const gfx::Rect& rect) const {
CHECK_EQ(target, this);

// Give the OverlayView the first chance to handle events.
if (frame_enabled_ && overlay_view_->HitTestRect(rect))
return false;

// Handle the event if it's within the bounds of the ClientView.
gfx::RectF rect_in_client_view_coords_f(rect);
View::ConvertRectToTarget(this, frame_->client_view(),
&rect_in_client_view_coords_f);
gfx::Rect rect_in_client_view_coords =
gfx::ToEnclosingRect(rect_in_client_view_coords_f);
return frame_->client_view()->HitTestRect(rect_in_client_view_coords);
}

chromeos::FrameCaptionButtonContainerView*
NonClientFrameViewAsh::GetFrameCaptionButtonContainerViewForTest() {
return header_view_->caption_button_container();
Expand Down
40 changes: 3 additions & 37 deletions ash/frame/non_client_frame_view_ash.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "base/memory/weak_ptr.h"
#include "chromeos/ui/frame/header_view.h"
#include "chromeos/ui/frame/highlight_border_overlay.h"
#include "chromeos/ui/frame/non_client_frame_view_base.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/widget/widget.h"
Expand All @@ -39,7 +40,7 @@ class NonClientFrameViewAshImmersiveHelper;
// the top of the screen. See also views::CustomFrameView and
// BrowserNonClientFrameViewAsh.
class ASH_EXPORT NonClientFrameViewAsh
: public views::NonClientFrameView,
: public chromeos::NonClientFrameViewBase,
public FrameContextMenuController::Delegate {
public:
METADATA_HEADER(NonClientFrameViewAsh);
Expand Down Expand Up @@ -77,23 +78,6 @@ class ASH_EXPORT NonClientFrameViewAsh
gfx::Rect GetClientBoundsForWindowBounds(
const gfx::Rect& window_bounds) const;

// views::NonClientFrameView:
gfx::Rect GetBoundsForClientView() const override;
gfx::Rect GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const override;
int NonClientHitTest(const gfx::Point& point) override;
void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override;
void ResetWindowControls() override;
void UpdateWindowIcon() override;
void UpdateWindowTitle() override;
void SizeConstraintsChanged() override;
views::View::Views GetChildrenInZOrder() override;
gfx::Size CalculatePreferredSize() const override;
void Layout() override;
gfx::Size GetMinimumSize() const override;
gfx::Size GetMaximumSize() const override;
void OnThemeChanged() override;

// FrameContextMenuController::Delegate:
bool ShouldShowContextMenu(views::View* source,
const gfx::Point& screen_coords_point) override;
Expand All @@ -103,9 +87,6 @@ class ASH_EXPORT NonClientFrameViewAsh
// header of v2 and ARC apps.
virtual void SetShouldPaintHeader(bool paint);

// Height from top of window to top of client area.
int NonClientTopBorderHeight() const;

// Expected height from top of window to top of client area when non client
// view is visible.
int NonClientTopBorderPreferredHeight() const;
Expand Down Expand Up @@ -133,15 +114,10 @@ class ASH_EXPORT NonClientFrameViewAsh
void AddedToWidget() override;

private:
class OverlayView;
friend class NonClientFrameViewAshTestWidgetDelegate;
friend class TestWidgetConstraintsDelegate;
friend class WindowServiceDelegateImplTest;

// views::NonClientFrameView:
bool DoesIntersectRect(const views::View* target,
const gfx::Rect& rect) const override;

// Returns the container for the minimize/maximize/close buttons that is
// held by the HeaderView. Used in testing.
chromeos::FrameCaptionButtonContainerView*
Expand All @@ -151,21 +127,11 @@ class ASH_EXPORT NonClientFrameViewAsh
void PaintAsActiveChanged();

// Updates the windows default frame colors if necessary.
void UpdateDefaultFrameColors();
void UpdateDefaultFrameColors() override;

// Generates a nine patch layer painted with a highlight border.
std::unique_ptr<HighlightBorderOverlay> highlight_border_overlay_;

// Not owned.
views::Widget* const frame_;

// View which contains the title and window controls.
chromeos::HeaderView* header_view_ = nullptr;

OverlayView* overlay_view_ = nullptr;

bool frame_enabled_ = true;

std::unique_ptr<NonClientFrameViewAshImmersiveHelper> immersive_helper_;

std::unique_ptr<FrameContextMenuController> frame_context_menu_controller_;
Expand Down
1 change: 1 addition & 0 deletions chrome/browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -4090,6 +4090,7 @@ static_library("ui") {
}
if (is_chromeos_lacros) {
sources += [
"views/chrome_views_delegate_lacros.cc",
"views/frame/browser_desktop_window_tree_host_lacros.cc",
"views/frame/browser_desktop_window_tree_host_lacros.h",
]
Expand Down
3 changes: 3 additions & 0 deletions chrome/browser/ui/views/chrome_views_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class ChromeViewsDelegate : public views::ViewsDelegate {
ProcessMenuAcceleratorResult ProcessAcceleratorWhileMenuShowing(
const ui::Accelerator& accelerator) override;
bool ShouldCloseMenuIfMouseCaptureLost() const override;
#endif

#if BUILDFLAG(IS_CHROMEOS)
std::unique_ptr<views::NonClientFrameView> CreateDefaultNonClientFrameView(
views::Widget* widget) override;
#endif
Expand Down
14 changes: 14 additions & 0 deletions chrome/browser/ui/views/chrome_views_delegate_lacros.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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/chrome_views_delegate.h"

#include <memory>

#include "chromeos/ui/frame/non_client_frame_view_base.h"

std::unique_ptr<views::NonClientFrameView>
ChromeViewsDelegate::CreateDefaultNonClientFrameView(views::Widget* widget) {
return std::make_unique<chromeos::NonClientFrameViewBase>(widget);
}
3 changes: 3 additions & 0 deletions chromeos/ui/frame/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ source_set("frame") {
"multitask_menu/multitask_menu_view.h",
"multitask_menu/split_button_view.cc",
"multitask_menu/split_button_view.h",
"non_client_frame_view_base.cc",
"non_client_frame_view_base.h",
]

deps = [
Expand All @@ -71,6 +73,7 @@ source_set("frame") {
"//skia",
"//ui/aura",
"//ui/base",
"//ui/color",
"//ui/strings:ui_strings_grit",
"//ui/views",
"//ui/views/window/vector_icons",
Expand Down
2 changes: 2 additions & 0 deletions chromeos/ui/frame/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ include_rules = [
"+ui/aura",
"+chromeos/strings/grit/chromeos_strings.h",
"+ui/base",
"+ui/chromeos",
"+ui/color",
"+ui/compositor",
"+ui/display",
"+ui/events",
Expand Down

0 comments on commit 7e4abc5

Please sign in to comment.