[go: nahoru, domu]

blob: 491835036e763f0cb442c6e0fbe03003d6d7abbc [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/frame/wide_frame_view.h"
#include <memory>
#include "ash/frame/non_client_frame_view_ash.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/screen_util.h"
#include "ash/shell.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/window_state.h"
#include "ash/wm/wm_event.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/user_metrics.h"
#include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
#include "chromeos/ui/frame/default_frame_header.h"
#include "chromeos/ui/frame/header_view.h"
#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
#include "ui/aura/window.h"
#include "ui/aura/window_targeter.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/caption_button_layout_constants.h"
namespace ash {
namespace {
using ::chromeos::ImmersiveFullscreenController;
class WideFrameTargeter : public aura::WindowTargeter {
public:
explicit WideFrameTargeter(chromeos::HeaderView* header_view)
: header_view_(header_view) {}
WideFrameTargeter(const WideFrameTargeter&) = delete;
WideFrameTargeter& operator=(const WideFrameTargeter&) = delete;
~WideFrameTargeter() override = default;
// aura::WindowTargeter:
bool GetHitTestRects(aura::Window* target,
gfx::Rect* hit_test_rect_mouse,
gfx::Rect* hit_test_rect_touch) const override {
if (header_view_->in_immersive_mode() && !header_view_->is_revealed()) {
aura::Window* source = header_view_->GetWidget()->GetNativeWindow();
*hit_test_rect_mouse = source->bounds();
aura::Window::ConvertRectToTarget(source, target->parent(),
hit_test_rect_mouse);
hit_test_rect_mouse->set_y(target->bounds().y());
hit_test_rect_mouse->set_height(1);
hit_test_rect_touch->SetRect(0, 0, 0, 0);
return true;
}
return aura::WindowTargeter::GetHitTestRects(target, hit_test_rect_mouse,
hit_test_rect_touch);
}
private:
raw_ptr<chromeos::HeaderView> header_view_;
};
} // namespace
// static
gfx::Rect WideFrameView::GetFrameBounds(views::Widget* target) {
static const int kFrameHeight =
views::GetCaptionButtonLayoutSize(
views::CaptionButtonLayoutSize::kNonBrowserCaption)
.height();
aura::Window* target_window = target->GetNativeWindow();
gfx::Rect bounds =
screen_util::GetIdealBoundsForMaximizedOrFullscreenOrPinnedState(
target_window);
bounds.set_height(kFrameHeight);
return bounds;
}
void WideFrameView::Init(ImmersiveFullscreenController* controller) {
DCHECK(target_);
controller->Init(this, target_, header_view_);
}
void WideFrameView::SetCaptionButtonModel(
std::unique_ptr<chromeos::CaptionButtonModel> model) {
header_view_->caption_button_container()->SetModel(std::move(model));
header_view_->UpdateCaptionButtons();
}
WideFrameView::WideFrameView(views::Widget* target)
: target_(target),
frame_context_menu_controller_(
std::make_unique<FrameContextMenuController>(target_, this)) {
// WideFrameView is owned by its client, not by Views.
SetOwnedByWidget(false);
aura::Window* target_window = target->GetNativeWindow();
target_window->AddObserver(this);
// Use the HeaderView itself as a frame view because WideFrameView is
// is the frame only.
header_view_ = AddChildView(
std::make_unique<chromeos::HeaderView>(target, /*frame view=*/nullptr));
header_view_->Init();
GetTargetHeaderView()->SetShouldPaintHeader(false);
header_view_->set_context_menu_controller(
frame_context_menu_controller_.get());
views::Widget::InitParams params(
views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
views::Widget::InitParams::TYPE_POPUP);
params.delegate = this;
params.bounds = GetFrameBounds(target);
params.name = "WideFrameView";
params.parent = target->GetNativeWindow();
// Setup Opacity Control.
// WideFrame should be used only when the rounded corner is not necessary.
params.opacity = views::Widget::InitParams::WindowOpacity::kOpaque;
auto widget = std::make_unique<views::Widget>();
widget->Init(std::move(params));
widget_ = std::move(widget);
aura::Window* window = widget_->GetNativeWindow();
// Overview normally clips the caption container which exists on the same
// window. But this WideFrameView exists as a separate window, which we hide
// in overview using the `kHideInOverviewKey` property. However, we still want
// to show it in the desks mini_views.
window->SetProperty(kHideInOverviewKey, true);
window->SetProperty(kForceVisibleInMiniViewKey, true);
window->SetEventTargeter(std::make_unique<WideFrameTargeter>(header_view()));
set_owned_by_client();
WindowState::Get(window)->set_allow_set_bounds_direct(true);
paint_as_active_subscription_ =
target_->RegisterPaintAsActiveChangedCallback(base::BindRepeating(
&WideFrameView::PaintAsActiveChanged, base::Unretained(this)));
PaintAsActiveChanged();
}
WideFrameView::~WideFrameView() {
header_view_->set_context_menu_controller(nullptr);
if (widget_)
widget_->CloseNow();
if (target_) {
chromeos::HeaderView* target_header_view = GetTargetHeaderView();
target_header_view->SetShouldPaintHeader(true);
target_header_view->GetFrameHeader()->UpdateFrameHeaderKey();
target_->GetNativeWindow()->RemoveObserver(this);
}
}
void WideFrameView::Layout(PassKey) {
int onscreen_height = header_view_->GetPreferredOnScreenHeight();
if (onscreen_height == 0 || !GetVisible()) {
header_view_->SetVisible(false);
} else {
const int height = header_view_->GetPreferredHeight();
header_view_->SetBounds(0, onscreen_height - height, width(), height);
header_view_->SetVisible(true);
}
}
void WideFrameView::OnMouseEvent(ui::MouseEvent* event) {
if (event->IsOnlyLeftMouseButton()) {
if ((event->flags() & ui::EF_IS_DOUBLE_CLICK)) {
base::RecordAction(
base::UserMetricsAction("Caption_ClickTogglesMaximize"));
const WMEvent wm_event(WM_EVENT_TOGGLE_MAXIMIZE_CAPTION);
WindowState::Get(target_->GetNativeWindow())->OnWMEvent(&wm_event);
}
event->SetHandled();
}
}
void WideFrameView::OnWindowDestroying(aura::Window* window) {
window->RemoveObserver(this);
target_ = nullptr;
}
void WideFrameView::OnDisplayMetricsChanged(const display::Display& display,
uint32_t changed_metrics) {
aura::Window* target_window = target_->GetNativeWindow();
// The target window is detached from the tree when the window is being moved
// to another desks, or another display, and it may trigger the display
// metrics change if the target window is in fullscreen. Do not try to layout
// in this case. The it'll be layout when the window is added back.
// (crbug.com/1267128)
if (!target_window->GetRootWindow())
return;
display::Screen* screen = display::Screen::GetScreen();
if (screen->GetDisplayNearestWindow(target_->GetNativeWindow()).id() !=
display.id() ||
!widget_) {
return;
}
DCHECK(target_);
// Ignore if this is called when the targe window state is nor proper
// state. This can happen when switching the state to other states, in which
// case, the wide frame will be removed.
if (!WindowState::Get(target_window)->IsMaximizedOrFullscreenOrPinned()) {
return;
}
GetWidget()->SetBounds(GetFrameBounds(target_));
}
void WideFrameView::OnImmersiveRevealStarted() {
header_view_->OnImmersiveRevealStarted();
}
void WideFrameView::OnImmersiveRevealEnded() {
header_view_->OnImmersiveRevealEnded();
}
void WideFrameView::OnImmersiveFullscreenEntered() {
header_view_->OnImmersiveFullscreenEntered();
widget_->GetNativeWindow()->SetTransparent(true);
if (target_)
GetTargetHeaderView()->OnImmersiveFullscreenEntered();
}
void WideFrameView::OnImmersiveFullscreenExited() {
header_view_->OnImmersiveFullscreenExited();
widget_->GetNativeWindow()->SetTransparent(false);
if (target_)
GetTargetHeaderView()->OnImmersiveFullscreenExited();
DeprecatedLayoutImmediately();
}
void WideFrameView::SetVisibleFraction(double visible_fraction) {
header_view_->SetVisibleFraction(visible_fraction);
}
std::vector<gfx::Rect> WideFrameView::GetVisibleBoundsInScreen() const {
return header_view_->GetVisibleBoundsInScreen();
}
bool WideFrameView::ShouldShowContextMenu(
View* source,
const gfx::Point& screen_coords_point) {
gfx::Point point_in_header_coords(screen_coords_point);
views::View::ConvertPointToTarget(this, header_view_,
&point_in_header_coords);
return header_view_->HitTestRect(
gfx::Rect(point_in_header_coords, gfx::Size(1, 1)));
}
chromeos::HeaderView* WideFrameView::GetTargetHeaderView() {
auto* frame_view = static_cast<NonClientFrameViewAsh*>(
target_->non_client_view()->frame_view());
return frame_view->GetHeaderView();
}
void WideFrameView::PaintAsActiveChanged() {
header_view_->GetFrameHeader()->SetPaintAsActive(
target_->ShouldPaintAsActive());
}
} // namespace ash