| // Copyright 2016 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 "ui/views/mus/desktop_window_tree_host_mus.h" |
| |
| #include "base/run_loop.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/client/capture_client.h" |
| #include "ui/aura/client/cursor_client.h" |
| #include "ui/aura/client/drag_drop_client.h" |
| #include "ui/aura/client/focus_client.h" |
| #include "ui/aura/client/transient_window_client.h" |
| #include "ui/aura/env.h" |
| #include "ui/aura/mus/focus_synchronizer.h" |
| #include "ui/aura/mus/window_port_mus.h" |
| #include "ui/aura/mus/window_tree_client.h" |
| #include "ui/aura/mus/window_tree_host_mus.h" |
| #include "ui/aura/mus/window_tree_host_mus_init_params.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_tracker.h" |
| #include "ui/base/hit_test.h" |
| #include "ui/display/screen.h" |
| #include "ui/events/gestures/gesture_recognizer.h" |
| #include "ui/events/gestures/gesture_recognizer_observer.h" |
| #include "ui/gfx/geometry/dip_util.h" |
| #include "ui/gfx/geometry/vector2d_conversions.h" |
| #include "ui/views/accessibility/view_accessibility.h" |
| #include "ui/views/corewm/tooltip_aura.h" |
| #include "ui/views/mus/mus_client.h" |
| #include "ui/views/mus/mus_property_mirror.h" |
| #include "ui/views/mus/window_manager_frame_values.h" |
| #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" |
| #include "ui/views/widget/native_widget_aura.h" |
| #include "ui/views/widget/widget_delegate.h" |
| #include "ui/wm/core/cursor_manager.h" |
| #include "ui/wm/core/native_cursor_manager.h" |
| #include "ui/wm/core/window_util.h" |
| #include "ui/wm/public/activation_client.h" |
| |
| namespace views { |
| |
| namespace { |
| |
| // As the window manager renderers the non-client decorations this class does |
| // very little but honor kTopViewInset. |
| class ClientSideNonClientFrameView : public NonClientFrameView, |
| public aura::WindowObserver { |
| public: |
| explicit ClientSideNonClientFrameView(views::Widget* widget) |
| : widget_(widget) { |
| // Not part of the accessibility node hierarchy because the window frame is |
| // provided by the window manager. |
| if (MusClient::Get()->use_remote_accessibility_host()) |
| GetViewAccessibility().OverrideIsIgnored(true); |
| |
| // Initialize kTopViewInset to a default value. Further updates will come |
| // from Ash. This is necessary so that during app window creation, |
| // GetWindowBoundsForClientBounds() can calculate correctly. |
| const auto& values = views::WindowManagerFrameValues::instance(); |
| widget->GetNativeWindow()->SetProperty(aura::client::kTopViewInset, |
| widget->IsMaximized() |
| ? values.maximized_insets.top() |
| : values.normal_insets.top()); |
| observed_.Add(window()); |
| } |
| ~ClientSideNonClientFrameView() override {} |
| |
| private: |
| gfx::Insets GetClientInsets() const { |
| const int top_inset = window()->GetProperty(aura::client::kTopViewInset); |
| return gfx::Insets(top_inset, 0, 0, 0); |
| } |
| |
| // View: |
| const char* GetClassName() const override { |
| return "ClientSideNonClientFrameView"; |
| } |
| |
| // NonClientFrameView: |
| gfx::Rect GetBoundsForClientView() const override { |
| gfx::Rect result(GetLocalBounds()); |
| if (widget_->IsFullscreen()) |
| return result; |
| result.Inset(GetClientInsets()); |
| return result; |
| } |
| gfx::Rect GetWindowBoundsForClientBounds( |
| const gfx::Rect& client_bounds) const override { |
| if (widget_->IsFullscreen()) |
| return client_bounds; |
| |
| gfx::Rect outset_bounds = client_bounds; |
| outset_bounds.Inset(-GetClientInsets()); |
| return outset_bounds; |
| } |
| int NonClientHitTest(const gfx::Point& point) override { return HTNOWHERE; } |
| void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask) override { |
| // The window manager provides the shape; do nothing. |
| } |
| void ResetWindowControls() override { |
| // TODO(sky): push to wm? |
| } |
| |
| // These have no implementation. The Window Manager handles the actual |
| // rendering of the icon/title. See NonClientFrameViewMash. The values |
| // associated with these methods are pushed to the server by the way of |
| // NativeWidgetMus functions. |
| void UpdateWindowIcon() override {} |
| void UpdateWindowTitle() override {} |
| void SizeConstraintsChanged() override {} |
| |
| gfx::Size CalculatePreferredSize() const override { |
| return widget_->non_client_view() |
| ->GetWindowBoundsForClientBounds( |
| gfx::Rect(widget_->client_view()->GetPreferredSize())) |
| .size(); |
| } |
| gfx::Size GetMinimumSize() const override { |
| return widget_->non_client_view() |
| ->GetWindowBoundsForClientBounds( |
| gfx::Rect(widget_->client_view()->GetMinimumSize())) |
| .size(); |
| } |
| gfx::Size GetMaximumSize() const override { |
| gfx::Size max_size = widget_->client_view()->GetMaximumSize(); |
| gfx::Size converted_size = |
| widget_->non_client_view() |
| ->GetWindowBoundsForClientBounds(gfx::Rect(max_size)) |
| .size(); |
| return gfx::Size(max_size.width() == 0 ? 0 : converted_size.width(), |
| max_size.height() == 0 ? 0 : converted_size.height()); |
| } |
| |
| // aura::WindowObserver: |
| void OnWindowDestroying(aura::Window* window) override { |
| observed_.Remove(window); |
| } |
| |
| void OnWindowPropertyChanged(aura::Window* window, |
| const void* key, |
| intptr_t old) override { |
| if (key == aura::client::kTopViewInset) { |
| InvalidateLayout(); |
| widget_->GetRootView()->Layout(); |
| } |
| } |
| |
| aura::Window* window() const { |
| return widget_->GetNativeWindow()->GetRootWindow(); |
| } |
| |
| views::Widget* widget_; |
| ScopedObserver<aura::Window, aura::WindowObserver> observed_{this}; |
| |
| DISALLOW_COPY_AND_ASSIGN(ClientSideNonClientFrameView); |
| }; |
| |
| class NativeCursorManagerMus : public wm::NativeCursorManager { |
| public: |
| explicit NativeCursorManagerMus(aura::Window* window) : window_(window) {} |
| ~NativeCursorManagerMus() override {} |
| |
| // wm::NativeCursorManager: |
| void SetDisplay(const display::Display& display, |
| wm::NativeCursorManagerDelegate* delegate) override { |
| // We ignore this entirely, as cursor are set on the client. |
| } |
| |
| void SetCursor(gfx::NativeCursor cursor, |
| wm::NativeCursorManagerDelegate* delegate) override { |
| ui::CursorData mojo_cursor; |
| if (cursor.native_type() == ui::CursorType::kCustom) { |
| mojo_cursor = |
| ui::CursorData(cursor.GetHotspot(), {cursor.GetBitmap()}, |
| cursor.device_scale_factor(), base::TimeDelta()); |
| } else { |
| mojo_cursor = ui::CursorData(cursor.native_type()); |
| } |
| |
| aura::WindowPortMus::Get(window_)->SetCursor(mojo_cursor); |
| delegate->CommitCursor(cursor); |
| } |
| |
| void SetVisibility(bool visible, |
| wm::NativeCursorManagerDelegate* delegate) override { |
| delegate->CommitVisibility(visible); |
| |
| if (visible) { |
| SetCursor(delegate->GetCursor(), delegate); |
| } else { |
| aura::WindowPortMus::Get(window_)->SetCursor( |
| ui::CursorData(ui::CursorType::kNone)); |
| } |
| } |
| |
| void SetCursorSize(ui::CursorSize cursor_size, |
| wm::NativeCursorManagerDelegate* delegate) override { |
| // TODO(erg): For now, ignore the difference between SET_NORMAL and |
| // SET_LARGE here. This feels like a thing that mus should decide instead. |
| // |
| // Also, it's NOTIMPLEMENTED() in the desktop version!? Including not |
| // acknowledging the call in the delegate. |
| NOTIMPLEMENTED(); |
| } |
| |
| void SetMouseEventsEnabled( |
| bool enabled, |
| wm::NativeCursorManagerDelegate* delegate) override { |
| // TODO(erg): How do we actually implement this? |
| // |
| // Mouse event dispatch is potentially done in a different process, |
| // definitely in a different mojo service. Each app is fairly locked down. |
| delegate->CommitMouseEventsEnabled(enabled); |
| NOTIMPLEMENTED(); |
| } |
| |
| private: |
| aura::Window* window_; |
| |
| DISALLOW_COPY_AND_ASSIGN(NativeCursorManagerMus); |
| }; |
| |
| void OnMoveLoopEnd(bool* out_success, |
| base::Closure quit_closure, |
| bool in_success) { |
| *out_success = in_success; |
| quit_closure.Run(); |
| } |
| |
| // ScopedTouchTransferController controls the transfer of touch events for |
| // window move loop. It transfers touches before the window move starts, and |
| // then transfers them back to the original window when the window move ends. |
| // However this transferring back to the original shouldn't happen if the client |
| // wants to continue the dragging on another window (like attaching the dragged |
| // tab to another window). |
| class ScopedTouchTransferController : public ui::GestureRecognizerObserver { |
| public: |
| ScopedTouchTransferController(aura::Window* source, aura::Window* dest) |
| : tracker_({source, dest}), |
| gesture_recognizer_(source->env()->gesture_recognizer()) { |
| gesture_recognizer_->TransferEventsTo( |
| source, dest, ui::TransferTouchesBehavior::kDontCancel); |
| gesture_recognizer_->AddObserver(this); |
| } |
| ~ScopedTouchTransferController() override { |
| gesture_recognizer_->RemoveObserver(this); |
| if (tracker_.windows().size() == 2) { |
| aura::Window* source = tracker_.Pop(); |
| aura::Window* dest = tracker_.Pop(); |
| gesture_recognizer_->TransferEventsTo( |
| dest, source, ui::TransferTouchesBehavior::kDontCancel); |
| } |
| } |
| |
| private: |
| // ui::GestureRecognizerObserver: |
| void OnActiveTouchesCanceledExcept( |
| ui::GestureConsumer* not_cancelled) override {} |
| void OnEventsTransferred( |
| ui::GestureConsumer* current_consumer, |
| ui::GestureConsumer* new_consumer, |
| ui::TransferTouchesBehavior transfer_touches_behavior) override { |
| if (tracker_.windows().size() <= 1) |
| return; |
| aura::Window* dest = tracker_.windows()[1]; |
| if (current_consumer == dest) |
| tracker_.Remove(dest); |
| } |
| void OnActiveTouchesCanceled(ui::GestureConsumer* consumer) override {} |
| |
| aura::WindowTracker tracker_; |
| |
| ui::GestureRecognizer* gesture_recognizer_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScopedTouchTransferController); |
| }; |
| |
| } // namespace |
| |
| // WindowObserver installed on DesktopWindowTreeHostMus::window(). Mostly |
| // forwards interesting events to DesktopWindowTreeHostMus. To avoid having |
| // DesktopWindowTreeHostMus be a WindowObserver on two windows (which is mildly |
| // error prone), this helper class is used. |
| class DesktopWindowTreeHostMus::WindowTreeHostWindowObserver |
| : public aura::WindowObserver { |
| public: |
| explicit WindowTreeHostWindowObserver(DesktopWindowTreeHostMus* host) |
| : host_(host) { |
| host->window()->AddObserver(this); |
| } |
| ~WindowTreeHostWindowObserver() override { |
| host_->window()->RemoveObserver(this); |
| } |
| |
| void set_is_waiting_for_restore(bool value) { |
| is_waiting_for_restore_ = value; |
| } |
| bool is_waiting_for_restore() const { return is_waiting_for_restore_; } |
| |
| // aura::WindowObserver: |
| void OnWindowVisibilityChanged(aura::Window* window, bool visible) override { |
| if (window == host_->window()) |
| host_->OnWindowTreeHostWindowVisibilityChanged(visible); |
| } |
| void OnWindowPropertyChanged(aura::Window* window, |
| const void* key, |
| intptr_t old) override { |
| if (key == aura::client::kShowStateKey) |
| is_waiting_for_restore_ = false; |
| } |
| |
| private: |
| DesktopWindowTreeHostMus* host_; |
| |
| // True while waiting for the show state to change. |
| bool is_waiting_for_restore_ = false; |
| |
| DISALLOW_COPY_AND_ASSIGN(WindowTreeHostWindowObserver); |
| }; |
| |
| DesktopWindowTreeHostMus::DesktopWindowTreeHostMus( |
| aura::WindowTreeHostMusInitParams init_params, |
| internal::NativeWidgetDelegate* native_widget_delegate, |
| DesktopNativeWidgetAura* desktop_native_widget_aura) |
| : aura::WindowTreeHostMus(std::move(init_params)), |
| native_widget_delegate_(native_widget_delegate), |
| desktop_native_widget_aura_(desktop_native_widget_aura), |
| close_widget_factory_(this) { |
| MusClient::Get()->AddObserver(this); |
| MusClient::Get()->window_tree_client()->focus_synchronizer()->AddObserver( |
| this); |
| content_window()->AddObserver(this); |
| // DesktopNativeWidgetAura registers the association between |content_window_| |
| // and Widget, but code may also want to go from the root (window()) to the |
| // Widget. This call enables that. |
| NativeWidgetAura::RegisterNativeWidgetForWindow(desktop_native_widget_aura, |
| window()); |
| |
| window_tree_host_window_observer_ = |
| std::make_unique<WindowTreeHostWindowObserver>(this); |
| // TODO: use display id and bounds if available, likely need to pass in |
| // InitParams for that. |
| } |
| |
| DesktopWindowTreeHostMus::~DesktopWindowTreeHostMus() { |
| window_tree_host_window_observer_.reset(); |
| |
| // The cursor-client can be accessed during WindowTreeHostMus tear-down. So |
| // the cursor-client needs to be unset on the root-window before |
| // |cursor_manager_| is destroyed. |
| aura::client::SetCursorClient(window(), nullptr); |
| content_window()->RemoveObserver(this); |
| MusClient::Get()->RemoveObserver(this); |
| MusClient::Get()->window_tree_client()->focus_synchronizer()->RemoveObserver( |
| this); |
| desktop_native_widget_aura_->OnDesktopWindowTreeHostDestroyed(this); |
| } |
| |
| void DesktopWindowTreeHostMus::SendClientAreaToServer() { |
| if (!ShouldSendClientAreaToServer()) |
| return; |
| |
| NonClientView* non_client_view = |
| native_widget_delegate_->AsWidget()->non_client_view(); |
| if (!non_client_view || !non_client_view->client_view()) |
| return; |
| |
| View* client_view = non_client_view->client_view(); |
| if (!observed_client_view_.IsObserving(client_view)) |
| observed_client_view_.Add(client_view); |
| |
| const gfx::Rect client_area_rect(non_client_view->client_view()->bounds()); |
| SetClientArea( |
| gfx::Insets( |
| client_area_rect.y(), client_area_rect.x(), |
| non_client_view->bounds().height() - client_area_rect.bottom(), |
| non_client_view->bounds().width() - client_area_rect.right()), |
| std::vector<gfx::Rect>()); |
| } |
| |
| bool DesktopWindowTreeHostMus::IsFocusClientInstalledOnFocusSynchronizer() |
| const { |
| return MusClient::Get() |
| ->window_tree_client() |
| ->focus_synchronizer() |
| ->active_focus_client() == aura::client::GetFocusClient(window()); |
| } |
| |
| float DesktopWindowTreeHostMus::GetScaleFactor() const { |
| // TODO(sky): GetDisplayNearestWindow() should take a const aura::Window*. |
| return display::Screen::GetScreen() |
| ->GetDisplayNearestWindow(const_cast<aura::Window*>(window())) |
| .device_scale_factor(); |
| } |
| |
| void DesktopWindowTreeHostMus::SetBoundsInDIP(const gfx::Rect& bounds_in_dip) { |
| // Do not use ConvertRectToPixel, enclosing rects cause problems. |
| const gfx::Rect rect( |
| gfx::ScaleToFlooredPoint(bounds_in_dip.origin(), GetScaleFactor()), |
| gfx::ScaleToCeiledSize(bounds_in_dip.size(), GetScaleFactor())); |
| SetBoundsInPixels(rect, viz::LocalSurfaceIdAllocation()); |
| } |
| |
| bool DesktopWindowTreeHostMus::IsWaitingForRestoreToComplete() const { |
| return window_tree_host_window_observer_->is_waiting_for_restore(); |
| } |
| |
| bool DesktopWindowTreeHostMus::ShouldSendClientAreaToServer() const { |
| if (!auto_update_client_area_) |
| return false; |
| |
| using WIP = views::Widget::InitParams; |
| const WIP::Type type = desktop_native_widget_aura_->widget_type(); |
| return type == WIP::TYPE_WINDOW || type == WIP::TYPE_PANEL; |
| } |
| |
| void DesktopWindowTreeHostMus::RestoreToPreminimizedState() { |
| DCHECK(IsMinimized()); |
| window_tree_host_window_observer_->set_is_waiting_for_restore(true); |
| base::AutoReset<bool> setter(&is_updating_window_visibility_, true); |
| window()->Show(); |
| } |
| |
| void DesktopWindowTreeHostMus::OnWindowTreeHostWindowVisibilityChanged( |
| bool visible) { |
| if (is_updating_window_visibility_) |
| return; |
| |
| // Call Show()/Hide() so that the visibility state is mirrored correctly. This |
| // function makes it so that calling Show()/Hide() on window() is the same as |
| // calling Show()/Hide() on the Widget. |
| base::AutoReset<bool> setter(&is_updating_window_visibility_, true); |
| if (visible) |
| Show(ui::SHOW_STATE_INACTIVE, gfx::Rect()); |
| else |
| Hide(); |
| } |
| |
| void DesktopWindowTreeHostMus::Init(const Widget::InitParams& params) { |
| const bool translucent = |
| MusClient::ShouldMakeWidgetWindowsTranslucent(params); |
| content_window()->SetTransparent(translucent); |
| window()->SetTransparent(translucent); |
| |
| // The window manager may provide the initial show state, for example for |
| // Chrome OS lock screen windows. https://crbug.com/899055 |
| if (params.show_state != ui::SHOW_STATE_DEFAULT) |
| window()->SetProperty(aura::client::kShowStateKey, params.show_state); |
| |
| if (!params.bounds.IsEmpty()) { |
| // Init the scale now (before InitHost below), it is used by SetBoundsInDIP. |
| IntializeDeviceScaleFactor(GetDisplay().device_scale_factor()); |
| SetBoundsInDIP(params.bounds); |
| } |
| |
| cursor_manager_ = std::make_unique<wm::CursorManager>( |
| std::make_unique<NativeCursorManagerMus>(window())); |
| aura::client::SetCursorClient(window(), cursor_manager_.get()); |
| InitHost(); |
| |
| NativeWidgetAura::SetShadowElevationFromInitParams(window(), params); |
| |
| // Widget's |InitParams::parent| has different meanings depending on the |
| // NativeWidgetPrivate implementation that the Widget creates (each Widget |
| // creates a NativeWidgetPrivate). When DesktopNativeWidgetAura is used as |
| // the NativeWidgetPrivate implementation, |InitParams::parent| means the |
| // entirety of the contents of the new Widget should be stacked above the |
| // entirety of the contents of the Widget for |InitParams::parent|, and |
| // the new Widget should be deleted when the Widget for |
| // |InitParams::parent| is deleted. Aura and mus provide support for |
| // transient windows, which provides both the stacking and ownership needed to |
| // support |InitParams::parent|. |
| // |
| // DesktopNativeWidgetAura internally creates two aura::Windows (one by |
| // WindowTreeHost, the other in |DesktopNativeWidgetAura::content_window_|). |
| // To have the entirety of the contents of the Widget appear on top of the |
| // entirety of the contents of another Widget, the stacking is done on the |
| // WindowTreeHost's window. For these reasons, the following code uses the |
| // Window associated with the WindowTreeHost of the |params.parent|. |
| // |
| // Views/Aura provide support for child-modal windows. Child-modal windows |
| // are windows that are modal to their transient parent. Because this code |
| // implements |InitParams::parent| in terms of transient parents, it means |
| // it is not possible to support both |InitParams::parent| as well as a |
| // child-modal window. This is *only* an issue if a Widget that uses a |
| // DesktopNativeWidgetAura needs to be child-modal to another window. At |
| // the current time NativeWidgetAura is always used for child-modal windows, |
| // so this isn't an issue. |
| // |
| // If we end up needing to use DesktopNativeWidgetAura for child-modal |
| // Widgets then we need something different. Possibilities include: |
| // . Have mus ignore child-modal windows and instead implement child-modal |
| // entirely in the client (this is what we do on Windows). To get this |
| // right likely means we need the ability to disable windows (see |
| // HWNDMessageHandler::InitModalType() for how Windows OS does this). |
| // . Implement |InitParams::parent| using a different (new) API. |
| if (params.parent && params.parent->GetHost()) { |
| aura::client::GetTransientWindowClient()->AddTransientChild( |
| params.parent->GetHost()->window(), window()); |
| } |
| |
| if (!params.accept_events) |
| window()->SetEventTargetingPolicy(ws::mojom::EventTargetingPolicy::NONE); |
| else |
| aura::WindowPortMus::Get(content_window())->SetCanAcceptDrops(true); |
| |
| // Sets the has-content info for the occlusion tracker that runs on the Window |
| // Service side. |
| content_window()->SetProperty( |
| aura::client::kClientWindowHasContent, |
| params.layer_type != ui::LAYER_NOT_DRAWN && |
| params.opacity == views::Widget::InitParams::OPAQUE_WINDOW); |
| } |
| |
| void DesktopWindowTreeHostMus::OnNativeWidgetCreated( |
| const Widget::InitParams& params) { |
| if (params.parent && params.parent->GetHost()) { |
| parent_ = static_cast<DesktopWindowTreeHostMus*>(params.parent->GetHost()); |
| parent_->children_.insert(this); |
| } |
| native_widget_delegate_->OnNativeWidgetCreated(true); |
| } |
| |
| void DesktopWindowTreeHostMus::OnActiveWindowChanged(bool active) { |
| // This function is called when there is a change in the active window the |
| // FocusClient for window() is associated with. This needs to potentially |
| // propagate to mus (the change may originate locally, not from mus). |
| // Propagating to the server is done by resetting the ActiveFocusClient. |
| if (active && !IsFocusClientInstalledOnFocusSynchronizer()) { |
| MusClient::Get() |
| ->window_tree_client() |
| ->focus_synchronizer() |
| ->SetActiveFocusClient(aura::client::GetFocusClient(window()), |
| window()); |
| } else if (!active && IsFocusClientInstalledOnFocusSynchronizer()) { |
| MusClient::Get() |
| ->window_tree_client() |
| ->focus_synchronizer() |
| ->SetActiveFocusClient(nullptr, nullptr); |
| } |
| } |
| |
| void DesktopWindowTreeHostMus::OnWidgetInitDone() { |
| // Because of construction order it's possible the bounds have changed before |
| // the NonClientView was created, which means we may not have sent the |
| // client-area and hit-test-mask. |
| SendClientAreaToServer(); |
| |
| MusClient::Get()->OnCaptureClientSet( |
| aura::client::GetCaptureClient(window())); |
| |
| // These views are not part of the accessibility node hierarchy because the |
| // window frame is provided by the window manager. |
| Widget* widget = native_widget_delegate_->AsWidget(); |
| if (MusClient::Get()->use_remote_accessibility_host()) { |
| if (widget->non_client_view()) |
| widget->non_client_view()->GetViewAccessibility().OverrideIsIgnored(true); |
| if (widget->client_view()) |
| widget->client_view()->GetViewAccessibility().OverrideIsIgnored(true); |
| } |
| |
| MusClient::Get()->OnWidgetInitDone(widget); |
| } |
| |
| std::unique_ptr<corewm::Tooltip> DesktopWindowTreeHostMus::CreateTooltip() { |
| return std::make_unique<corewm::TooltipAura>(); |
| } |
| |
| std::unique_ptr<aura::client::DragDropClient> |
| DesktopWindowTreeHostMus::CreateDragDropClient( |
| DesktopNativeCursorManager* cursor_manager) { |
| // aura-mus handles installing a DragDropClient. |
| return nullptr; |
| } |
| |
| void DesktopWindowTreeHostMus::Close() { |
| if (close_widget_factory_.HasWeakPtrs()) |
| return; |
| |
| // Even though we don't close immediately, we need to hide immediately |
| // (otherwise events may be processed, which is unexpected). |
| Hide(); |
| |
| // This has to happen *after* Hide() above, otherwise animations won't work. |
| content_window()->Hide(); |
| |
| // Close doesn't delete this immediately, as 'this' may still be on the stack |
| // resulting in possible crashes when the stack unwindes. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(&DesktopWindowTreeHostMus::CloseNow, |
| close_widget_factory_.GetWeakPtr())); |
| } |
| |
| void DesktopWindowTreeHostMus::CloseNow() { |
| MusClient::Get()->OnCaptureClientUnset( |
| aura::client::GetCaptureClient(window())); |
| |
| native_widget_delegate_->OnNativeWidgetDestroying(); |
| |
| // If we have children, close them. Use a copy for iteration because they'll |
| // remove themselves from |children_|. |
| std::set<DesktopWindowTreeHostMus*> children_copy = children_; |
| for (DesktopWindowTreeHostMus* child : children_copy) |
| child->CloseNow(); |
| DCHECK(children_.empty()); |
| |
| if (parent_) { |
| parent_->children_.erase(this); |
| parent_ = nullptr; |
| } |
| |
| DestroyCompositor(); |
| desktop_native_widget_aura_->OnHostClosed(); |
| } |
| |
| aura::WindowTreeHost* DesktopWindowTreeHostMus::AsWindowTreeHost() { |
| return this; |
| } |
| |
| void DesktopWindowTreeHostMus::Show(ui::WindowShowState show_state, |
| const gfx::Rect& restore_bounds) { |
| // Only notify if the visibility is really changing. |
| const bool notify_visibility_change = |
| is_updating_window_visibility_ || !IsVisible(); |
| if (notify_visibility_change) |
| native_widget_delegate_->OnNativeWidgetVisibilityChanging(true); |
| |
| // NOTE: this code is called from Widget::Show() (no args). Widget::Show() |
| // supplies ui::SHOW_STATE_DEFAULT as the |show_state| after the first call. |
| // If SHOW_STATE_DEFAULT is supplied, and the Window is currently minimized, |
| // the window should be restored to its preminimized state. |
| |
| if (show_state == ui::SHOW_STATE_MAXIMIZED && !restore_bounds.IsEmpty()) { |
| window()->SetProperty(aura::client::kRestoreBoundsKey, |
| new gfx::Rect(restore_bounds)); |
| } |
| if (show_state == ui::SHOW_STATE_MAXIMIZED || |
| show_state == ui::SHOW_STATE_FULLSCREEN) { |
| window()->SetProperty(aura::client::kShowStateKey, show_state); |
| window_tree_host_window_observer_->set_is_waiting_for_restore(false); |
| } else if (show_state == ui::SHOW_STATE_DEFAULT && IsMinimized()) { |
| RestoreToPreminimizedState(); |
| } else if (show_state == ui::SHOW_STATE_MINIMIZED && !IsMinimized()) { |
| Minimize(); |
| } |
| // DesktopWindowTreeHostMus is unique in that it calls window()->Show() here. |
| // All other implementations call window()->Show() from the constructor. This |
| // is necessary as window()'s visibility is mirrored in the server, on other |
| // platforms it's the visibility of the AcceleratedWidget that matters and |
| // dictates what is actually drawn on screen. |
| { |
| base::AutoReset<bool> setter(&is_updating_window_visibility_, true); |
| window()->Show(); |
| } |
| if (compositor()) |
| compositor()->SetVisible(true); |
| |
| // |content_window_| is the Window that will be focused by way of Activate(). |
| // Ensure |content_window_| is visible before the call to Activate(), |
| // otherwise focus goes to window(). |
| content_window()->Show(); |
| |
| if (notify_visibility_change) |
| native_widget_delegate_->OnNativeWidgetVisibilityChanged(true); |
| |
| if (native_widget_delegate_->CanActivate()) { |
| if (show_state != ui::SHOW_STATE_INACTIVE && |
| show_state != ui::SHOW_STATE_MINIMIZED) { |
| Activate(); |
| } |
| |
| // SetInitialFocus() should be always be called, even for |
| // SHOW_STATE_INACTIVE. If the window has to stay inactive, the method will |
| // do the right thing. |
| // Activate() might fail if the window is non-activatable. In this case, we |
| // should pass SHOW_STATE_INACTIVE to SetInitialFocus() to stop the initial |
| // focused view from getting focused. See crbug.com/515594 for example. |
| native_widget_delegate_->SetInitialFocus( |
| IsActive() ? show_state : ui::SHOW_STATE_INACTIVE); |
| } |
| } |
| |
| bool DesktopWindowTreeHostMus::IsVisible() const { |
| // Go through the DesktopNativeWidgetAura::IsVisible() for checking |
| // visibility of the parent as it has additional checks beyond checking the |
| // aura::Window. |
| return window()->IsVisible() && |
| (!parent_ || |
| static_cast<const internal::NativeWidgetPrivate*>( |
| parent_->desktop_native_widget_aura_) |
| ->IsVisible()); |
| } |
| |
| void DesktopWindowTreeHostMus::SetSize(const gfx::Size& size) { |
| // Use GetBoundsInPixels(), as the origin of window() is always at (0, 0). |
| gfx::Rect screen_bounds = |
| gfx::ConvertRectToDIP(GetScaleFactor(), GetBoundsInPixels()); |
| screen_bounds.set_size(size); |
| SetBoundsInDIP(screen_bounds); |
| } |
| |
| void DesktopWindowTreeHostMus::StackAbove(aura::Window* relative) { |
| // Windows and X11 check for |relative| being nullptr and fail silently. It |
| // also looks like |relative| is usually multiple children deep in the root |
| // window, which we must pass instead. |
| if (relative && relative->GetRootWindow()) |
| WindowTreeHostMus::StackAbove(relative->GetRootWindow()); |
| } |
| |
| void DesktopWindowTreeHostMus::StackAtTop() { |
| // Request to the server to stack our current mus window at the top. Our |
| // window() is a root, and we can't reach up past it so we can't just request |
| // a Reorder(), which is what we'd do to reorder our own subwindows. |
| WindowTreeHostMus::StackAtTop(); |
| } |
| |
| void DesktopWindowTreeHostMus::CenterWindow(const gfx::Size& size) { |
| gfx::Rect bounds_to_center_in = GetWorkAreaBoundsInScreen(); |
| |
| // If there is a transient parent and it fits |size|, then center over it. |
| if (wm::GetTransientParent(content_window())) { |
| gfx::Rect transient_parent_bounds = |
| wm::GetTransientParent(content_window())->GetBoundsInScreen(); |
| if (transient_parent_bounds.height() >= size.height() && |
| transient_parent_bounds.width() >= size.width()) { |
| bounds_to_center_in = transient_parent_bounds; |
| } |
| } |
| |
| gfx::Rect resulting_bounds(bounds_to_center_in); |
| resulting_bounds.ClampToCenteredSize(size); |
| SetBoundsInDIP(resulting_bounds); |
| } |
| |
| void DesktopWindowTreeHostMus::GetWindowPlacement( |
| gfx::Rect* bounds, |
| ui::WindowShowState* show_state) const { |
| // Implementation matches that of NativeWidgetAura. |
| *bounds = GetRestoredBounds(); |
| if (IsWaitingForRestoreToComplete()) { |
| // The real state is not known, use ui::SHOW_STATE_NORMAL to avoid saving |
| // the minimized state. |
| *show_state = ui::SHOW_STATE_NORMAL; |
| } else { |
| *show_state = window()->GetProperty(aura::client::kShowStateKey); |
| } |
| } |
| |
| gfx::Rect DesktopWindowTreeHostMus::GetWindowBoundsInScreen() const { |
| return gfx::ConvertRectToDIP(GetScaleFactor(), GetBoundsInPixels()); |
| } |
| |
| gfx::Rect DesktopWindowTreeHostMus::GetClientAreaBoundsInScreen() const { |
| // View-to-screen coordinate system transformations depend on this returning |
| // the full window bounds, for example View::ConvertPointToScreen(). |
| return GetWindowBoundsInScreen(); |
| } |
| |
| gfx::Rect DesktopWindowTreeHostMus::GetRestoredBounds() const { |
| // Restored bounds should only be relevant if the window is minimized, |
| // maximized, or fullscreen. However, in some places the code expects |
| // GetRestoredBounds() to return the current window bounds if the window is |
| // not in either state. |
| if (IsMinimized() || IsMaximized() || IsFullscreen()) { |
| // Restore bounds are in screen coordinates, no need to convert. |
| gfx::Rect* restore_bounds = |
| window()->GetProperty(aura::client::kRestoreBoundsKey); |
| if (restore_bounds) |
| return *restore_bounds; |
| } |
| return GetWindowBoundsInScreen(); |
| } |
| |
| std::string DesktopWindowTreeHostMus::GetWorkspace() const { |
| // Only used on x11. |
| return std::string(); |
| } |
| |
| gfx::Rect DesktopWindowTreeHostMus::GetWorkAreaBoundsInScreen() const { |
| return GetDisplay().work_area(); |
| } |
| |
| void DesktopWindowTreeHostMus::SetShape( |
| std::unique_ptr<Widget::ShapeRects> native_shape) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void DesktopWindowTreeHostMus::Activate() { |
| if (!IsVisible() && !IsMinimized()) |
| return; |
| |
| // Activate() is expected to restore a minimized window. |
| if (IsMinimized()) |
| RestoreToPreminimizedState(); |
| |
| // This should result in OnActiveFocusClientChanged() being called, which |
| // triggers a call to DesktopNativeWidgetAura::HandleActivationChanged(), |
| // which focuses the right window. |
| MusClient::Get() |
| ->window_tree_client() |
| ->focus_synchronizer() |
| ->SetActiveFocusClient(aura::client::GetFocusClient(window()), window()); |
| if (is_active_) |
| window()->SetProperty(aura::client::kDrawAttentionKey, false); |
| } |
| |
| void DesktopWindowTreeHostMus::Deactivate() { |
| if (!is_active_) |
| return; |
| |
| // Reset the active focus client, which will trigger resetting active status. |
| // This is done so that we deactivate immediately. |
| DCHECK(IsFocusClientInstalledOnFocusSynchronizer()); |
| MusClient::Get() |
| ->window_tree_client() |
| ->focus_synchronizer() |
| ->SetActiveFocusClient(nullptr, nullptr); |
| DCHECK(!is_active_); |
| |
| // Then ask the window manager to deactivate, which effectively means pick |
| // another window to activate. |
| DeactivateWindow(); |
| } |
| |
| bool DesktopWindowTreeHostMus::IsActive() const { |
| return is_active_; |
| } |
| |
| void DesktopWindowTreeHostMus::Maximize() { |
| window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); |
| } |
| |
| void DesktopWindowTreeHostMus::Minimize() { |
| window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); |
| |
| // When minimized, this should no longer be active. |
| if (IsFocusClientInstalledOnFocusSynchronizer()) { |
| MusClient::Get() |
| ->window_tree_client() |
| ->focus_synchronizer() |
| ->SetActiveFocusClient(nullptr, nullptr); |
| } |
| } |
| |
| void DesktopWindowTreeHostMus::Restore() { |
| window()->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); |
| } |
| |
| bool DesktopWindowTreeHostMus::IsMaximized() const { |
| return window()->GetProperty(aura::client::kShowStateKey) == |
| ui::SHOW_STATE_MAXIMIZED; |
| } |
| |
| bool DesktopWindowTreeHostMus::IsMinimized() const { |
| return window()->GetProperty(aura::client::kShowStateKey) == |
| ui::SHOW_STATE_MINIMIZED && |
| !IsWaitingForRestoreToComplete(); |
| } |
| |
| bool DesktopWindowTreeHostMus::HasCapture() const { |
| // Capture state is held by DesktopNativeWidgetAura::content_window_. |
| // DesktopNativeWidgetAura::HasCapture() calls content_window_->HasCapture(), |
| // and this. That means this function can always return true. |
| return true; |
| } |
| |
| void DesktopWindowTreeHostMus::SetAlwaysOnTop(bool always_on_top) { |
| window()->SetProperty(aura::client::kAlwaysOnTopKey, always_on_top); |
| } |
| |
| bool DesktopWindowTreeHostMus::IsAlwaysOnTop() const { |
| return window()->GetProperty(aura::client::kAlwaysOnTopKey); |
| } |
| |
| void DesktopWindowTreeHostMus::SetVisibleOnAllWorkspaces(bool always_visible) { |
| // Not applicable to chromeos. |
| } |
| |
| bool DesktopWindowTreeHostMus::IsVisibleOnAllWorkspaces() const { |
| return false; |
| } |
| |
| bool DesktopWindowTreeHostMus::SetWindowTitle(const base::string16& title) { |
| WidgetDelegate* widget_delegate = |
| native_widget_delegate_->AsWidget()->widget_delegate(); |
| const bool show = widget_delegate && widget_delegate->ShouldShowWindowTitle(); |
| if (window()->GetTitle() == title && |
| window()->GetProperty(aura::client::kTitleShownKey) == show) { |
| return false; |
| } |
| window()->SetProperty(aura::client::kTitleShownKey, show); |
| window()->SetTitle(title); |
| return true; |
| } |
| |
| void DesktopWindowTreeHostMus::ClearNativeFocus() { |
| aura::client::FocusClient* client = aura::client::GetFocusClient(window()); |
| if (client && window()->Contains(client->GetFocusedWindow())) |
| client->ResetFocusWithinActiveWindow(window()); |
| } |
| |
| Widget::MoveLoopResult DesktopWindowTreeHostMus::RunMoveLoop( |
| const gfx::Vector2d& drag_offset, |
| Widget::MoveLoopSource source, |
| Widget::MoveLoopEscapeBehavior escape_behavior) { |
| // When using WindowService, the touch events for the window move will |
| // happen on the root window, so the events need to be transferred from |
| // widget to its root before starting move loop. |
| ScopedTouchTransferController scoped_controller(content_window(), window()); |
| |
| static_cast<internal::NativeWidgetPrivate*>( |
| desktop_native_widget_aura_)->ReleaseCapture(); |
| |
| base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); |
| |
| ws::mojom::MoveLoopSource mus_source = |
| source == Widget::MOVE_LOOP_SOURCE_MOUSE |
| ? ws::mojom::MoveLoopSource::MOUSE |
| : ws::mojom::MoveLoopSource::TOUCH; |
| |
| bool success = false; |
| // Don't use display::Screen::GetCursorScreenPoint() -- that's incorrect for |
| // touch events. Rather the cursor location can be computed from window's |
| // location with drag_offset. |
| gfx::Point cursor_location = window()->GetBoundsInScreen().origin() + |
| gfx::ToFlooredVector2d(drag_offset); |
| WindowTreeHostMus::PerformWindowMove( |
| mus_source, cursor_location, |
| base::Bind(OnMoveLoopEnd, &success, run_loop.QuitClosure())); |
| |
| run_loop.Run(); |
| |
| return success ? Widget::MOVE_LOOP_SUCCESSFUL : Widget::MOVE_LOOP_CANCELED; |
| } |
| |
| void DesktopWindowTreeHostMus::EndMoveLoop() { |
| WindowTreeHostMus::CancelWindowMove(); |
| } |
| |
| void DesktopWindowTreeHostMus::SetVisibilityChangedAnimationsEnabled( |
| bool value) { |
| window()->SetProperty(aura::client::kAnimationsDisabledKey, !value); |
| } |
| |
| NonClientFrameView* DesktopWindowTreeHostMus::CreateNonClientFrameView() { |
| if (!ShouldSendClientAreaToServer()) |
| return nullptr; |
| |
| return new ClientSideNonClientFrameView(native_widget_delegate_->AsWidget()); |
| } |
| |
| bool DesktopWindowTreeHostMus::ShouldUseNativeFrame() const { |
| return false; |
| } |
| |
| bool DesktopWindowTreeHostMus::ShouldWindowContentsBeTransparent() const { |
| return false; |
| } |
| |
| void DesktopWindowTreeHostMus::FrameTypeChanged() { |
| native_widget_delegate_->AsWidget()->ThemeChanged(); |
| } |
| |
| void DesktopWindowTreeHostMus::SetFullscreen(bool fullscreen) { |
| if (IsFullscreen() == fullscreen) |
| return; // Nothing to do. |
| |
| wm::SetWindowFullscreen(window(), fullscreen); |
| } |
| |
| bool DesktopWindowTreeHostMus::IsFullscreen() const { |
| return window()->GetProperty(aura::client::kShowStateKey) == |
| ui::SHOW_STATE_FULLSCREEN; |
| } |
| |
| void DesktopWindowTreeHostMus::SetOpacity(float opacity) { |
| WindowTreeHostMus::SetOpacity(opacity); |
| } |
| |
| void DesktopWindowTreeHostMus::SetWindowIcons(const gfx::ImageSkia& window_icon, |
| const gfx::ImageSkia& app_icon) { |
| NativeWidgetAura::AssignIconToAuraWindow(window(), window_icon, app_icon); |
| } |
| |
| void DesktopWindowTreeHostMus::InitModalType(ui::ModalType modal_type) { |
| // See comment in Init() related to |InitParams::parent| as to why this DCHECK |
| // is here. |
| DCHECK_NE(modal_type, ui::MODAL_TYPE_CHILD); |
| window()->SetProperty(aura::client::kModalKey, modal_type); |
| } |
| |
| void DesktopWindowTreeHostMus::FlashFrame(bool flash_frame) { |
| window()->SetProperty(aura::client::kDrawAttentionKey, flash_frame); |
| } |
| |
| bool DesktopWindowTreeHostMus::IsAnimatingClosed() const { |
| return false; |
| } |
| |
| bool DesktopWindowTreeHostMus::IsTranslucentWindowOpacitySupported() const { |
| return true; |
| } |
| |
| void DesktopWindowTreeHostMus::SizeConstraintsChanged() { |
| int32_t behavior = ws::mojom::kResizeBehaviorNone; |
| Widget* widget = native_widget_delegate_->AsWidget(); |
| if (widget->widget_delegate()) |
| behavior = widget->widget_delegate()->GetResizeBehavior(); |
| window()->SetProperty(aura::client::kResizeBehaviorKey, behavior); |
| } |
| |
| bool DesktopWindowTreeHostMus::ShouldUpdateWindowTransparency() const { |
| // Needed so the window manager can render the client decorations. |
| return false; |
| } |
| |
| bool DesktopWindowTreeHostMus::ShouldUseDesktopNativeCursorManager() const { |
| // We manage the cursor ourself. |
| return false; |
| } |
| |
| bool DesktopWindowTreeHostMus::ShouldCreateVisibilityController() const { |
| // Window manager takes care of all top-level window animations. |
| return false; |
| } |
| |
| void DesktopWindowTreeHostMus::OnWindowManagerFrameValuesChanged() { |
| NonClientView* non_client_view = |
| native_widget_delegate_->AsWidget()->non_client_view(); |
| if (non_client_view) { |
| non_client_view->Layout(); |
| non_client_view->SchedulePaint(); |
| } |
| |
| SendClientAreaToServer(); |
| } |
| |
| void DesktopWindowTreeHostMus::OnActiveFocusClientChanged( |
| aura::client::FocusClient* focus_client, |
| aura::Window* focus_client_root) { |
| if (focus_client_root == this->window()) { |
| is_active_ = true; |
| desktop_native_widget_aura_->HandleActivationChanged(true); |
| } else if (is_active_) { |
| is_active_ = false; |
| desktop_native_widget_aura_->HandleActivationChanged(false); |
| } |
| } |
| |
| void DesktopWindowTreeHostMus::OnWindowPropertyChanged(aura::Window* window, |
| const void* key, |
| intptr_t old) { |
| DCHECK_EQ(window, content_window()); |
| DCHECK(!window->GetRootWindow() || this->window() == window->GetRootWindow()); |
| if (!this->window()) |
| return; |
| |
| // Allow mus clients to mirror widget window properties to their root windows. |
| MusPropertyMirror* property_mirror = MusClient::Get()->mus_property_mirror(); |
| if (property_mirror) { |
| property_mirror->MirrorPropertyFromWidgetWindowToRootWindow( |
| window, this->window(), key); |
| } |
| } |
| |
| void DesktopWindowTreeHostMus::ShowImpl() { |
| // This code path is hit when the server initiated the change. In such a case |
| // the window should not be made active. If the server wants the window to be |
| // active, it will make the window active. |
| Show(ui::SHOW_STATE_INACTIVE, gfx::Rect()); |
| } |
| |
| void DesktopWindowTreeHostMus::HideImpl() { |
| // If |is_updating_window_visibility_| is true, this is being called in |
| // response to window()'s visibility changing, in which case we need to |
| // continue on to complete processing of the hide. |
| if (!IsVisible() && !is_updating_window_visibility_) |
| return; |
| |
| native_widget_delegate_->OnNativeWidgetVisibilityChanging(false); |
| { |
| base::AutoReset<bool> setter(&is_updating_window_visibility_, true); |
| WindowTreeHostMus::HideImpl(); |
| } |
| native_widget_delegate_->OnNativeWidgetVisibilityChanged(false); |
| |
| // When hiding we can't possibly be active any more. Reset the FocusClient, |
| // which effectively triggers giving up focus (and activation). Mus will |
| // eventually generate a focus event, but that's async. This should be done |
| // after the window gets hidden actually, since some code (like |
| // WindowActivityWatcher) assumes closing window is already invisible when the |
| // focus is lost. See https://crbug.com/896080. |
| if (IsFocusClientInstalledOnFocusSynchronizer()) { |
| MusClient::Get() |
| ->window_tree_client() |
| ->focus_synchronizer() |
| ->SetActiveFocusClient(nullptr, nullptr); |
| } |
| } |
| |
| void DesktopWindowTreeHostMus::SetBoundsInPixels( |
| const gfx::Rect& bounds_in_pixels, |
| const viz::LocalSurfaceIdAllocation& local_surface_id_allocation) { |
| gfx::Rect final_bounds_in_pixels = bounds_in_pixels; |
| if (GetBoundsInPixels().size() != bounds_in_pixels.size()) { |
| gfx::Size size = bounds_in_pixels.size(); |
| size.SetToMax(gfx::ConvertSizeToPixel( |
| GetScaleFactor(), native_widget_delegate_->GetMinimumSize())); |
| const gfx::Size max_size_in_pixels = gfx::ConvertSizeToPixel( |
| GetScaleFactor(), native_widget_delegate_->GetMaximumSize()); |
| if (!max_size_in_pixels.IsEmpty()) |
| size.SetToMin(max_size_in_pixels); |
| final_bounds_in_pixels.set_size(size); |
| } |
| WindowTreeHostMus::SetBoundsInPixels(final_bounds_in_pixels, |
| local_surface_id_allocation); |
| } |
| |
| void DesktopWindowTreeHostMus::OnViewBoundsChanged(views::View* observed_view) { |
| DCHECK_EQ( |
| observed_view, |
| native_widget_delegate_->AsWidget()->non_client_view()->client_view()); |
| |
| SendClientAreaToServer(); |
| } |
| |
| void DesktopWindowTreeHostMus::OnViewIsDeleting(View* observed_view) { |
| observed_client_view_.Remove(observed_view); |
| } |
| |
| aura::Window* DesktopWindowTreeHostMus::content_window() { |
| return desktop_native_widget_aura_->content_window(); |
| } |
| |
| } // namespace views |