blob: 568460aa909be8a25b066178a85a60b96dc5c5c6 [file] [log] [blame]
// Copyright 2014 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/wm/core/easy_resize_window_targeter.h"
#include <algorithm>
#include "services/ui/public/interfaces/window_manager.mojom.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/transient_window_client.h"
#include "ui/aura/env.h"
#include "ui/aura/mus/window_port_mus.h"
#include "ui/aura/mus/window_tree_client.h"
#include "ui/aura/window.h"
#include "ui/events/event.h"
#include "ui/gfx/geometry/insets_f.h"
#include "ui/gfx/geometry/rect.h"
namespace wm {
namespace {
gfx::Insets InsetsWithOnlyPositiveValues(const gfx::Insets& insets) {
return gfx::Insets(std::max(0, insets.top()), std::max(0, insets.left()),
std::max(0, insets.bottom()), std::max(0, insets.right()));
}
} // namespace
// HitMaskSetter is responsible for setting the hit-test mask on a Window.
class EasyResizeWindowTargeter::HitMaskSetter : public aura::WindowObserver {
public:
explicit HitMaskSetter(aura::Window* window) : window_(window) {
window_->AddObserver(this);
}
~HitMaskSetter() override {
if (window_) {
aura::WindowPortMus::Get(window_)->SetHitTestMask(base::nullopt);
window_->RemoveObserver(this);
}
}
void SetHitMaskInsets(const gfx::Insets& insets) {
if (insets == insets_)
return;
insets_ = insets;
ApplyHitTestMask();
}
private:
void ApplyHitTestMask() {
base::Optional<gfx::Rect> hit_test_mask(
gfx::Rect(window_->bounds().size()));
hit_test_mask->Inset(insets_);
aura::WindowPortMus::Get(window_)->SetHitTestMask(hit_test_mask);
}
// aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override {
window_->RemoveObserver(this);
window_ = nullptr;
}
void OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) override {
ApplyHitTestMask();
}
private:
aura::Window* window_;
gfx::Insets insets_;
DISALLOW_COPY_AND_ASSIGN(HitMaskSetter);
};
EasyResizeWindowTargeter::EasyResizeWindowTargeter(
aura::Window* container,
const gfx::Insets& mouse_extend,
const gfx::Insets& touch_extend)
: container_(container) {
DCHECK(container_);
SetInsets(mouse_extend, touch_extend);
}
EasyResizeWindowTargeter::~EasyResizeWindowTargeter() {}
void EasyResizeWindowTargeter::OnSetInsets(
const gfx::Insets& last_mouse_extend,
const gfx::Insets& last_touch_extend) {
if (container_->env()->mode() != aura::Env::Mode::MUS)
return;
// Positive values equate to a hit test mask.
const gfx::Insets positive_mouse_insets =
InsetsWithOnlyPositiveValues(mouse_extend());
if (positive_mouse_insets.IsEmpty()) {
hit_mask_setter_.reset();
} else {
if (!hit_mask_setter_)
hit_mask_setter_ = std::make_unique<HitMaskSetter>(container_);
hit_mask_setter_->SetHitMaskInsets(positive_mouse_insets);
}
}
bool EasyResizeWindowTargeter::EventLocationInsideBounds(
aura::Window* target,
const ui::LocatedEvent& event) const {
return WindowTargeter::EventLocationInsideBounds(target, event);
}
bool EasyResizeWindowTargeter::ShouldUseExtendedBounds(
const aura::Window* window) const {
// Use the extended bounds only for immediate child windows of |container_|.
// Use the default targeter otherwise.
if (window->parent() != container_)
return false;
// Only resizable windows benefit from the extended hit-test region.
if ((window->GetProperty(aura::client::kResizeBehaviorKey) &
ui::mojom::kResizeBehaviorCanResize) == 0) {
return false;
}
// For transient children use extended bounds if a transient parent or if
// transient parent's parent is a top level window in |container_|.
aura::client::TransientWindowClient* transient_window_client =
aura::client::GetTransientWindowClient();
const aura::Window* transient_parent =
transient_window_client
? transient_window_client->GetTransientParent(window)
: nullptr;
return !transient_parent || transient_parent == container_ ||
transient_parent->parent() == container_;
}
} // namespace wm