// Copyright 2016 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/shelf/shelf_window_targeter.h"
#include "ash/public/cpp/session/session_types.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ui/aura/window.h"
namespace ash {
namespace {
gfx::Insets GetInsetsForAlignment(int distance, ShelfAlignment alignment) {
if (alignment == ShelfAlignment::kLeft) {
return gfx::Insets::TLBR(0, 0, 0, distance);
}
if (alignment == ShelfAlignment::kRight) {
return gfx::Insets::TLBR(0, distance, 0, 0);
}
return gfx::Insets::TLBR(distance, 0, 0, 0);
}
} // namespace
ShelfWindowTargeter::ShelfWindowTargeter(aura::Window* container, Shelf* shelf)
: ::wm::EasyResizeWindowTargeter(gfx::Insets(), gfx::Insets()),
shelf_(shelf) {
UpdateInsetsForVisibilityState(shelf_->GetVisibilityState());
container->AddObserver(this);
shelf_->AddObserver(this);
Shell::Get()->AddShellObserver(this);
}
ShelfWindowTargeter::~ShelfWindowTargeter() {
// Ensure that the observers were removed and the shelf pointer was cleared.
DCHECK(!shelf_);
Shell::Get()->RemoveShellObserver(this);
}
bool ShelfWindowTargeter::ShouldUseExtendedBounds(const aura::Window* w) const {
// Use extended bounds only for direct child of the container.
return window() == w->parent();
}
bool ShelfWindowTargeter::GetHitTestRects(
aura::Window* target,
gfx::Rect* hit_test_rect_mouse,
gfx::Rect* hit_test_rect_touch) const {
bool target_is_shelf_widget =
target == shelf_->shelf_widget()->GetNativeWindow();
*hit_test_rect_mouse = *hit_test_rect_touch = target->bounds();
if (ShouldUseExtendedBounds(target)) {
hit_test_rect_mouse->Inset(mouse_extend());
// Whether the touch hit area should be extended beyond the window top when
// the shelf is in auto-hide state (to make targeting hidden shelf easier).
// This should be applied for shelf widget only, to prevent other widgets
// positioned below display bounds (e.g. hidden hotseat widget) from
// handling touch events instead of the shelf.
if (target_is_shelf_widget)
hit_test_rect_touch->Inset(touch_extend());
}
return true;
}
void ShelfWindowTargeter::OnWindowDestroying(aura::Window* window) {
window->RemoveObserver(this);
shelf_->RemoveObserver(this);
shelf_ = nullptr;
}
void ShelfWindowTargeter::OnShelfAlignmentChanged(
aura::Window* root_window,
ShelfAlignment old_alignment) {
if (!shelf_) {
return;
}
if (shelf_->shelf_widget()->GetNativeWindow()->GetRootWindow() !=
root_window) {
return;
}
UpdateInsets();
}
void ShelfWindowTargeter::OnShelfVisibilityStateChanged(
ShelfVisibilityState new_state) {
UpdateInsetsForVisibilityState(new_state);
}
void ShelfWindowTargeter::UpdateInsetsForVisibilityState(
ShelfVisibilityState state) {
mouse_inset_size_for_shelf_visibility_ =
state == SHELF_VISIBLE
? ShelfConfig::Get()->workspace_area_visible_inset()
: 0;
touch_inset_size_for_shelf_visibility_ =
state == SHELF_AUTO_HIDE
? -ShelfConfig::Get()->workspace_area_auto_hide_inset()
: 0;
UpdateInsets();
}
void ShelfWindowTargeter::UpdateInsets() {
const gfx::Insets mouse_insets = GetInsetsForAlignment(
mouse_inset_size_for_shelf_visibility_, shelf_->alignment());
const gfx::Insets touch_insets = GetInsetsForAlignment(
touch_inset_size_for_shelf_visibility_, shelf_->alignment());
// Remember the insets. See GetHitTestsRects when they're actually used.
SetInsets(mouse_insets, touch_insets);
}
} // namespace ash