| // Copyright (c) 2012 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/controls/native/native_view_host_aura.h" |
| |
| #include "base/logging.h" |
| #include "build/build_config.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/client/focus_client.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_delegate.h" |
| #include "ui/aura/window_occlusion_tracker.h" |
| #include "ui/base/cursor/cursor.h" |
| #include "ui/base/hit_test.h" |
| #include "ui/compositor/paint_recorder.h" |
| #include "ui/views/controls/native/native_view_host.h" |
| #include "ui/views/painter.h" |
| #include "ui/views/view_constants_aura.h" |
| #include "ui/views/view_properties.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/wm/core/window_util.h" |
| |
| namespace views { |
| |
| class NativeViewHostAura::ClippingWindowDelegate : public aura::WindowDelegate { |
| public: |
| ClippingWindowDelegate() : native_view_(NULL) {} |
| ~ClippingWindowDelegate() override {} |
| |
| void set_native_view(aura::Window* native_view) { |
| native_view_ = native_view; |
| } |
| |
| gfx::Size GetMinimumSize() const override { return gfx::Size(); } |
| gfx::Size GetMaximumSize() const override { return gfx::Size(); } |
| void OnBoundsChanged(const gfx::Rect& old_bounds, |
| const gfx::Rect& new_bounds) override {} |
| gfx::NativeCursor GetCursor(const gfx::Point& point) override { |
| return gfx::kNullCursor; |
| } |
| int GetNonClientComponent(const gfx::Point& point) const override { |
| return HTCLIENT; |
| } |
| bool ShouldDescendIntoChildForEventHandling( |
| aura::Window* child, |
| const gfx::Point& location) override { |
| return true; |
| } |
| bool CanFocus() override { |
| // Ask the hosted native view's delegate because directly calling |
| // aura::Window::CanFocus() will call back into this when checking whether |
| // parents can focus. |
| return native_view_ && native_view_->delegate() |
| ? native_view_->delegate()->CanFocus() |
| : true; |
| } |
| void OnCaptureLost() override {} |
| void OnPaint(const ui::PaintContext& context) override {} |
| void OnDeviceScaleFactorChanged(float old_device_scale_factor, |
| float new_device_scale_factor) override {} |
| void OnWindowDestroying(aura::Window* window) override {} |
| void OnWindowDestroyed(aura::Window* window) override {} |
| void OnWindowTargetVisibilityChanged(bool visible) override {} |
| bool HasHitTestMask() const override { return false; } |
| void GetHitTestMask(gfx::Path* mask) const override {} |
| |
| private: |
| aura::Window* native_view_; |
| }; |
| |
| NativeViewHostAura::NativeViewHostAura(NativeViewHost* host) : host_(host) {} |
| |
| NativeViewHostAura::~NativeViewHostAura() { |
| if (host_->native_view()) { |
| host_->native_view()->RemoveObserver(this); |
| host_->native_view()->ClearProperty(views::kHostViewKey); |
| host_->native_view()->ClearProperty(aura::client::kHostWindowKey); |
| clipping_window_->ClearProperty(views::kHostViewKey); |
| if (host_->native_view()->parent() == clipping_window_.get()) |
| clipping_window_->RemoveChild(host_->native_view()); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // NativeViewHostAura, NativeViewHostWrapper implementation: |
| void NativeViewHostAura::AttachNativeView() { |
| if (!clipping_window_) |
| CreateClippingWindow(); |
| clipping_window_delegate_->set_native_view(host_->native_view()); |
| host_->native_view()->AddObserver(this); |
| host_->native_view()->SetProperty(views::kHostViewKey, |
| static_cast<View*>(host_)); |
| original_transform_ = host_->native_view()->transform(); |
| original_transform_changed_ = false; |
| AddClippingWindow(); |
| InstallMask(); |
| } |
| |
| void NativeViewHostAura::NativeViewDetaching(bool destroyed) { |
| // This method causes a succession of window tree changes. |
| // ScopedPauseOcclusionTracking ensures that occlusion is recomputed at the |
| // end of the method instead of after each change. |
| aura::WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion; |
| |
| clipping_window_delegate_->set_native_view(NULL); |
| RemoveClippingWindow(); |
| if (!destroyed) { |
| host_->native_view()->RemoveObserver(this); |
| host_->native_view()->ClearProperty(views::kHostViewKey); |
| host_->native_view()->ClearProperty(aura::client::kHostWindowKey); |
| if (original_transform_changed_) |
| host_->native_view()->SetTransform(original_transform_); |
| host_->native_view()->Hide(); |
| if (host_->native_view()->parent()) |
| Widget::ReparentNativeView(host_->native_view(), NULL); |
| } |
| } |
| |
| void NativeViewHostAura::AddedToWidget() { |
| if (!host_->native_view()) |
| return; |
| |
| AddClippingWindow(); |
| if (host_->IsDrawn()) |
| host_->native_view()->Show(); |
| else |
| host_->native_view()->Hide(); |
| host_->Layout(); |
| } |
| |
| void NativeViewHostAura::RemovedFromWidget() { |
| if (host_->native_view()) { |
| host_->native_view()->Hide(); |
| host_->native_view()->ClearProperty(aura::client::kHostWindowKey); |
| if (host_->native_view()->parent()) |
| host_->native_view()->parent()->RemoveChild(host_->native_view()); |
| RemoveClippingWindow(); |
| } |
| } |
| |
| bool NativeViewHostAura::SetCustomMask(std::unique_ptr<ui::LayerOwner> mask) { |
| #if defined(OS_WIN) |
| // TODO(crbug/843250): On Aura, layer masks don't play with HiDPI. Fix this |
| // and enable this on Windows. |
| return false; |
| #else |
| UninstallMask(); |
| mask_ = std::move(mask); |
| if (mask_) |
| mask_->layer()->SetFillsBoundsOpaquely(false); |
| InstallMask(); |
| return true; |
| #endif |
| } |
| |
| void NativeViewHostAura::InstallClip(int x, int y, int w, int h) { |
| clip_rect_.reset( |
| new gfx::Rect(host_->ConvertRectToWidget(gfx::Rect(x, y, w, h)))); |
| } |
| |
| bool NativeViewHostAura::HasInstalledClip() { |
| return !!clip_rect_; |
| } |
| |
| void NativeViewHostAura::UninstallClip() { |
| clip_rect_.reset(); |
| } |
| |
| void NativeViewHostAura::ShowWidget(int x, |
| int y, |
| int w, |
| int h, |
| int native_w, |
| int native_h) { |
| if (host_->fast_resize()) { |
| gfx::Point origin(x, y); |
| views::View::ConvertPointFromWidget(host_, &origin); |
| InstallClip(origin.x(), origin.y(), w, h); |
| native_w = host_->native_view()->bounds().width(); |
| native_h = host_->native_view()->bounds().height(); |
| } else { |
| gfx::Transform transform = original_transform_; |
| if (w > 0 && h > 0 && native_w > 0 && native_h > 0) { |
| transform.Scale(static_cast<SkMScalar>(w) / native_w, |
| static_cast<SkMScalar>(h) / native_h); |
| } |
| // Only set the transform when it is actually different. |
| if (transform != host_->native_view()->transform()) { |
| host_->native_view()->SetTransform(transform); |
| original_transform_changed_ = true; |
| } |
| } |
| |
| clipping_window_->SetBounds(clip_rect_ ? *clip_rect_ : gfx::Rect(x, y, w, h)); |
| gfx::Point clip_offset = clipping_window_->bounds().origin(); |
| host_->native_view()->SetBounds( |
| gfx::Rect(x - clip_offset.x(), y - clip_offset.y(), native_w, native_h)); |
| host_->native_view()->Show(); |
| clipping_window_->Show(); |
| } |
| |
| void NativeViewHostAura::HideWidget() { |
| host_->native_view()->Hide(); |
| clipping_window_->Hide(); |
| } |
| |
| void NativeViewHostAura::SetFocus() { |
| aura::Window* window = host_->native_view(); |
| aura::client::FocusClient* client = aura::client::GetFocusClient(window); |
| if (client) |
| client->FocusWindow(window); |
| } |
| |
| gfx::NativeViewAccessible NativeViewHostAura::GetNativeViewAccessible() { |
| return NULL; |
| } |
| |
| gfx::NativeCursor NativeViewHostAura::GetCursor(int x, int y) { |
| if (host_->native_view()) |
| return host_->native_view()->GetCursor(gfx::Point(x, y)); |
| return gfx::kNullCursor; |
| } |
| |
| void NativeViewHostAura::OnWindowBoundsChanged( |
| aura::Window* window, |
| const gfx::Rect& old_bounds, |
| const gfx::Rect& new_bounds, |
| ui::PropertyChangeReason reason) { |
| if (mask_) { |
| // Having a mask means this layer has a render surface of its own. This |
| // means we want this layer snapped as the render surface uses this layer |
| // (its primary layer) to snap to the physical pixel grid. |
| // See https://crbug.com/843250 for more details. |
| wm::SnapWindowToPixelBoundary(window); |
| |
| mask_->layer()->SetBounds(gfx::Rect(host_->native_view()->bounds().size())); |
| } |
| } |
| |
| void NativeViewHostAura::OnWindowDestroying(aura::Window* window) { |
| DCHECK(window == host_->native_view()); |
| clipping_window_delegate_->set_native_view(NULL); |
| } |
| |
| void NativeViewHostAura::OnWindowDestroyed(aura::Window* window) { |
| DCHECK(window == host_->native_view()); |
| host_->NativeViewDestroyed(); |
| } |
| |
| // static |
| NativeViewHostWrapper* NativeViewHostWrapper::CreateWrapper( |
| NativeViewHost* host) { |
| return new NativeViewHostAura(host); |
| } |
| |
| void NativeViewHostAura::CreateClippingWindow() { |
| clipping_window_delegate_ = std::make_unique<ClippingWindowDelegate>(); |
| // Use WINDOW_TYPE_CONTROLLER type so descendant views (including popups) get |
| // positioned appropriately. |
| clipping_window_ = std::make_unique<aura::Window>( |
| clipping_window_delegate_.get(), aura::client::WINDOW_TYPE_CONTROL, |
| host_->native_view()->env()); |
| clipping_window_->Init(ui::LAYER_NOT_DRAWN); |
| clipping_window_->set_owned_by_parent(false); |
| clipping_window_->SetName("NativeViewHostAuraClip"); |
| clipping_window_->layer()->SetMasksToBounds(true); |
| clipping_window_->SetProperty(views::kHostViewKey, static_cast<View*>(host_)); |
| } |
| |
| void NativeViewHostAura::AddClippingWindow() { |
| RemoveClippingWindow(); |
| |
| host_->native_view()->SetProperty(aura::client::kHostWindowKey, |
| host_->GetWidget()->GetNativeView()); |
| Widget::ReparentNativeView(host_->native_view(), clipping_window_.get()); |
| if (host_->GetWidget()->GetNativeView()) { |
| Widget::ReparentNativeView(clipping_window_.get(), |
| host_->GetWidget()->GetNativeView()); |
| } |
| } |
| |
| void NativeViewHostAura::RemoveClippingWindow() { |
| clipping_window_->Hide(); |
| if (host_->native_view()) |
| host_->native_view()->ClearProperty(aura::client::kHostWindowKey); |
| |
| if (host_->native_view()->parent() == clipping_window_.get()) { |
| if (host_->GetWidget() && host_->GetWidget()->GetNativeView()) { |
| Widget::ReparentNativeView(host_->native_view(), |
| host_->GetWidget()->GetNativeView()); |
| } else { |
| clipping_window_->RemoveChild(host_->native_view()); |
| } |
| host_->native_view()->SetBounds(clipping_window_->bounds()); |
| } |
| if (clipping_window_->parent()) |
| clipping_window_->parent()->RemoveChild(clipping_window_.get()); |
| } |
| |
| void NativeViewHostAura::InstallMask() { |
| if (!mask_) |
| return; |
| if (host_->native_view()) { |
| // Setting a mask triggers this layer to have a render surface of its own. |
| // This means we cannot skip computing its subpixel offset positioning as |
| // the render surface uses this layer (its primary layer) to snap to the |
| // physical pixel grid. |
| // See https://crbug.com/843250 for more details. |
| wm::SnapWindowToPixelBoundary(host_->native_view()); |
| |
| mask_->layer()->SetBounds(gfx::Rect(host_->native_view()->bounds().size())); |
| host_->native_view()->layer()->SetMaskLayer(mask_->layer()); |
| } |
| } |
| |
| void NativeViewHostAura::UninstallMask() { |
| if (!host_->native_view() || !mask_) |
| return; |
| |
| host_->native_view()->layer()->SetMaskLayer(nullptr); |
| mask_.reset(); |
| } |
| |
| } // namespace views |