// Copyright 2018 The Chromium Authors. All rights reserved. // 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 #include "ash/frame/header_view.h" #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/bind.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/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(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: 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 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(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(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; params.type = views::Widget::InitParams::TYPE_POPUP; params.delegate = this; params.bounds = GetFrameBounds(target); params.name = "WideFrameView"; params.parent = target->GetNativeWindow(); params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; // 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(); 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(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() { if (widget_) widget_->CloseNow(); if (target_) { HeaderView* target_header_view = GetTargetHeaderView(); target_header_view->SetShouldPaintHeader(true); target_header_view->GetFrameHeader()->UpdateFrameHeaderKey(); target_->GetNativeWindow()->RemoveObserver(this); } } void WideFrameView::Layout() { 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(); Layout(); } void WideFrameView::SetVisibleFraction(double visible_fraction) { header_view_->SetVisibleFraction(visible_fraction); } std::vector 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))); } HeaderView* WideFrameView::GetTargetHeaderView() { auto* frame_view = static_cast( target_->non_client_view()->frame_view()); return frame_view->GetHeaderView(); } void WideFrameView::PaintAsActiveChanged() { header_view_->GetFrameHeader()->SetPaintAsActive( target_->ShouldPaintAsActive()); } } // namespace ash