Commit 869ae46b authored by Mike Wasserman's avatar Mike Wasserman Committed by Commit Bot

ws: Implement WindowTree::SetHitTestMask for KSV touch editing handles.

Add a simple WindowTree::SetHitTestMask impl and unit test.
Wire up the TouchHandleWindowTargeter, used in the KSV app, etc.
Clarify that the targeter only insets bounds, it does not extend them.

Bug: 872891
Test: KSV app text touches work as expected (modulo Issue 873743).
Change-Id: I4a1b79a8826a7e176ce3dafb29e1223ddd003966
Reviewed-on: https://chromium-review.googlesource.com/1171218Reviewed-by: default avatarScott Violet <sky@chromium.org>
Commit-Queue: Michael Wasserman <msw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583034}
parent 1c63da45
...@@ -459,6 +459,13 @@ void ServerWindow::SetClientArea( ...@@ -459,6 +459,13 @@ void ServerWindow::SetClientArea(
client_area_ = insets; client_area_ = insets;
} }
void ServerWindow::SetHitTestMask(const base::Optional<gfx::Rect>& mask) {
gfx::Insets insets;
if (mask)
insets = gfx::Rect(window_->bounds().size()).InsetsFrom(mask.value());
window_targeter_->SetInsets(insets);
}
void ServerWindow::SetCaptureOwner(WindowTree* owner) { void ServerWindow::SetCaptureOwner(WindowTree* owner) {
capture_owner_ = owner; capture_owner_ = owner;
if (!IsTopLevel()) if (!IsTopLevel())
...@@ -522,7 +529,9 @@ ServerWindow::ServerWindow(aura::Window* window, ...@@ -522,7 +529,9 @@ ServerWindow::ServerWindow(aura::Window* window,
event_handler_ = std::make_unique<TopLevelEventHandler>(this); event_handler_ = std::make_unique<TopLevelEventHandler>(this);
else else
event_handler_ = std::make_unique<ServerWindowEventHandler>(this); event_handler_ = std::make_unique<ServerWindowEventHandler>(this);
window_->SetEventTargeter(std::make_unique<ServerWindowTargeter>(this)); auto server_window_targeter = std::make_unique<ServerWindowTargeter>(this);
window_targeter_ = server_window_targeter.get();
window_->SetEventTargeter(std::move(server_window_targeter));
// In order for a window to receive events it must have a target_handler() // In order for a window to receive events it must have a target_handler()
// (see Window::CanAcceptEvent()). Normally the delegate is the TargetHandler, // (see Window::CanAcceptEvent()). Normally the delegate is the TargetHandler,
// but if the delegate is null, then so is the target_handler(). Set // but if the delegate is null, then so is the target_handler(). Set
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
namespace aura { namespace aura {
class Window; class Window;
class WindowTargeter;
} }
namespace ui { namespace ui {
...@@ -75,6 +76,8 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) ServerWindow { ...@@ -75,6 +76,8 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) ServerWindow {
void SetClientArea(const gfx::Insets& insets, void SetClientArea(const gfx::Insets& insets,
const std::vector<gfx::Rect>& additional_client_areas); const std::vector<gfx::Rect>& additional_client_areas);
void SetHitTestMask(const base::Optional<gfx::Rect>& mask);
void SetCaptureOwner(WindowTree* owner); void SetCaptureOwner(WindowTree* owner);
WindowTree* capture_owner() const { return capture_owner_; } WindowTree* capture_owner() const { return capture_owner_; }
...@@ -162,6 +165,8 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) ServerWindow { ...@@ -162,6 +165,8 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) ServerWindow {
gfx::Insets client_area_; gfx::Insets client_area_;
std::vector<gfx::Rect> additional_client_areas_; std::vector<gfx::Rect> additional_client_areas_;
aura::WindowTargeter* window_targeter_ = nullptr;
std::unique_ptr<ui::EventHandler> event_handler_; std::unique_ptr<ui::EventHandler> event_handler_;
// When a window has capture there are two possible clients that can get the // When a window has capture there are two possible clients that can get the
......
...@@ -1363,14 +1363,34 @@ void WindowTree::SetClientArea( ...@@ -1363,14 +1363,34 @@ void WindowTree::SetClientArea(
} }
ServerWindow* server_window = ServerWindow::GetMayBeNull(window); ServerWindow* server_window = ServerWindow::GetMayBeNull(window);
DCHECK(server_window); // Must exist because of preceeding conditionals. DCHECK(server_window); // Must exist because of preceding conditionals.
server_window->SetClientArea( server_window->SetClientArea(
insets, additional_client_areas.value_or(std::vector<gfx::Rect>())); insets, additional_client_areas.value_or(std::vector<gfx::Rect>()));
} }
void WindowTree::SetHitTestMask(Id window_id, void WindowTree::SetHitTestMask(Id transport_window_id,
const base::Optional<gfx::Rect>& mask) { const base::Optional<gfx::Rect>& mask) {
NOTIMPLEMENTED_LOG_ONCE(); const ClientWindowId window_id = MakeClientWindowId(transport_window_id);
aura::Window* window = GetWindowByClientId(window_id);
DVLOG(3) << "SetHitTestMask client window_id=" << window_id.ToString()
<< " mask=" << (mask ? mask.value().ToString() : "null");
if (!window) {
DVLOG(1) << "SetHitTestMask failed (invalid window id)";
return;
}
if (!IsClientCreatedWindow(window)) {
DVLOG(1) << "SetHitTestMask failed (access denied)";
return;
}
const gfx::Rect window_local_bounds(window->bounds().size());
if (mask && !window_local_bounds.Contains(mask.value())) {
DVLOG(1) << "SetHitTestMask failed (mask extends beyond window bounds)";
return;
}
ServerWindow* server_window = ServerWindow::GetMayBeNull(window);
DCHECK(server_window); // Must exist because of preceding conditionals.
server_window->SetHitTestMask(mask);
} }
void WindowTree::SetCanAcceptDrops(Id window_id, bool accepts_drops) { void WindowTree::SetCanAcceptDrops(Id window_id, bool accepts_drops) {
...@@ -1385,7 +1405,7 @@ void WindowTree::SetCanAcceptDrops(Id window_id, bool accepts_drops) { ...@@ -1385,7 +1405,7 @@ void WindowTree::SetCanAcceptDrops(Id window_id, bool accepts_drops) {
} }
ServerWindow* server_window = ServerWindow::GetMayBeNull(window); ServerWindow* server_window = ServerWindow::GetMayBeNull(window);
DCHECK(server_window); // Must exist because of preceeding conditionals. DCHECK(server_window); // Must exist because of preceding conditionals.
if (accepts_drops && !server_window->HasDragDropDelegate()) { if (accepts_drops && !server_window->HasDragDropDelegate()) {
auto drag_drop_delegate = std::make_unique<DragDropDelegate>( auto drag_drop_delegate = std::make_unique<DragDropDelegate>(
window_tree_client_, window, window_id); window_tree_client_, window, window_id);
......
...@@ -343,7 +343,7 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowTree ...@@ -343,7 +343,7 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowTree
const gfx::Insets& insets, const gfx::Insets& insets,
const base::Optional<std::vector<gfx::Rect>>& const base::Optional<std::vector<gfx::Rect>>&
additional_client_areas) override; additional_client_areas) override;
void SetHitTestMask(Id window_id, void SetHitTestMask(Id transport_window_id,
const base::Optional<gfx::Rect>& mask) override; const base::Optional<gfx::Rect>& mask) override;
void SetCanAcceptDrops(Id window_id, bool accepts_drops) override; void SetCanAcceptDrops(Id window_id, bool accepts_drops) override;
void SetWindowVisibility(uint32_t change_id, void SetWindowVisibility(uint32_t change_id,
......
...@@ -96,6 +96,11 @@ void WindowTreeTestHelper::SetClientArea( ...@@ -96,6 +96,11 @@ void WindowTreeTestHelper::SetClientArea(
additional_client_areas); additional_client_areas);
} }
void WindowTreeTestHelper::SetHitTestMask(aura::Window* window,
base::Optional<gfx::Rect> mask) {
window_tree_->SetHitTestMask(TransportIdForWindow(window), mask);
}
void WindowTreeTestHelper::SetWindowProperty(aura::Window* window, void WindowTreeTestHelper::SetWindowProperty(aura::Window* window,
const std::string& name, const std::string& name,
const std::vector<uint8_t>& value, const std::vector<uint8_t>& value,
......
...@@ -78,6 +78,7 @@ class WindowTreeTestHelper { ...@@ -78,6 +78,7 @@ class WindowTreeTestHelper {
const gfx::Insets& insets, const gfx::Insets& insets,
base::Optional<std::vector<gfx::Rect>> additional_client_areas = base::Optional<std::vector<gfx::Rect>> additional_client_areas =
base::Optional<std::vector<gfx::Rect>>()); base::Optional<std::vector<gfx::Rect>>());
void SetHitTestMask(aura::Window* window, base::Optional<gfx::Rect> mask);
void SetWindowProperty(aura::Window* window, void SetWindowProperty(aura::Window* window,
const std::string& name, const std::string& name,
const std::vector<uint8_t>& value, const std::vector<uint8_t>& value,
......
...@@ -613,6 +613,41 @@ TEST(WindowTreeTest, MouseDownInNonClientDragToClientWithChildWindow) { ...@@ -613,6 +613,41 @@ TEST(WindowTreeTest, MouseDownInNonClientDragToClientWithChildWindow) {
EXPECT_TRUE(window_tree_client->input_events().empty()); EXPECT_TRUE(window_tree_client->input_events().empty());
} }
TEST(WindowTreeTest, SetHitTestMask) {
EventRecordingWindowDelegate window_delegate;
WindowServiceTestSetup setup;
setup.delegate()->set_delegate_for_next_top_level(&window_delegate);
aura::Window* top_level =
setup.window_tree_test_helper()->NewTopLevelWindow();
ASSERT_TRUE(top_level);
top_level->Show();
top_level->SetBounds(gfx::Rect(10, 10, 100, 100));
TestWindowTreeClient* window_tree_client = setup.window_tree_client();
window_tree_client->ClearInputEvents();
window_delegate.ClearEvents();
// Set a hit test mask in the window's bounds that excludes the top half.
setup.window_tree_test_helper()->SetHitTestMask(top_level,
gfx::Rect(0, 50, 100, 50));
// Events outside the hit test mask are not seen by the delegate or client.
test::EventGenerator event_generator(setup.root());
event_generator.MoveMouseTo(50, 30);
EXPECT_TRUE(window_tree_client->input_events().empty());
EXPECT_TRUE(window_delegate.events().empty());
// Events in the hit test mask are seen by the delegate and client.
event_generator.MoveMouseTo(50, 80);
EXPECT_EQ("POINTER_MOVED 40,70",
LocatedEventToEventTypeAndLocation(
window_tree_client->PopInputEvent().event.get()));
EXPECT_EQ("MOUSE_ENTERED 40,70", LocatedEventToEventTypeAndLocation(
window_delegate.PopEvent().get()));
EXPECT_EQ("MOUSE_MOVED 40,70", LocatedEventToEventTypeAndLocation(
window_delegate.PopEvent().get()));
}
TEST(WindowTreeTest, PointerWatcher) { TEST(WindowTreeTest, PointerWatcher) {
WindowServiceTestSetup setup; WindowServiceTestSetup setup;
TestWindowTreeClient* window_tree_client = setup.window_tree_client(); TestWindowTreeClient* window_tree_client = setup.window_tree_client();
......
...@@ -85,8 +85,8 @@ void WindowPortMus::SetCanAcceptDrops(bool can_accept_drops) { ...@@ -85,8 +85,8 @@ void WindowPortMus::SetCanAcceptDrops(bool can_accept_drops) {
window_tree_client_->SetCanAcceptDrops(this, can_accept_drops); window_tree_client_->SetCanAcceptDrops(this, can_accept_drops);
} }
void WindowPortMus::SetHitTestMask(const base::Optional<gfx::Rect>& rect) { void WindowPortMus::SetHitTestMask(const base::Optional<gfx::Rect>& mask) {
window_tree_client_->SetHitTestMask(this, rect); window_tree_client_->SetHitTestMask(this, mask);
} }
void WindowPortMus::Embed(ui::mojom::WindowTreeClientPtr client, void WindowPortMus::Embed(ui::mojom::WindowTreeClientPtr client,
......
...@@ -87,7 +87,7 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { ...@@ -87,7 +87,7 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus {
void SetCanAcceptDrops(bool can_accept_drops); void SetCanAcceptDrops(bool can_accept_drops);
// See description in mojom for details on this. // See description in mojom for details on this.
void SetHitTestMask(const base::Optional<gfx::Rect>& rect); void SetHitTestMask(const base::Optional<gfx::Rect>& mask);
// Embeds a new client in this Window. See WindowTreeClient::Embed() for // Embeds a new client in this Window. See WindowTreeClient::Embed() for
// details on arguments. // details on arguments.
......
...@@ -274,11 +274,8 @@ void WindowTreeClient::SetImeVisibility(WindowMus* window, ...@@ -274,11 +274,8 @@ void WindowTreeClient::SetImeVisibility(WindowMus* window,
void WindowTreeClient::SetHitTestMask( void WindowTreeClient::SetHitTestMask(
WindowMus* window, WindowMus* window,
const base::Optional<gfx::Rect>& mask_rect) { const base::Optional<gfx::Rect>& mask_rect) {
base::Optional<gfx::Rect> out_rect = base::nullopt; DCHECK(tree_);
if (mask_rect) tree_->SetHitTestMask(window->server_id(), mask_rect);
out_rect = mask_rect.value();
tree_->SetHitTestMask(window->server_id(), out_rect);
} }
void WindowTreeClient::Embed(Window* window, void WindowTreeClient::Embed(Window* window,
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "ui/aura/client/cursor_client.h" #include "ui/aura/client/cursor_client.h"
#include "ui/aura/env.h" #include "ui/aura/env.h"
#include "ui/aura/mus/window_port_mus.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
#include "ui/aura/window_targeter.h" #include "ui/aura/window_targeter.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
...@@ -129,10 +130,11 @@ gfx::Image* GetHandleImage(gfx::SelectionBound::Type bound_type) { ...@@ -129,10 +130,11 @@ gfx::Image* GetHandleImage(gfx::SelectionBound::Type bound_type) {
} }
// Calculates the bounds of the widget containing the selection handle based // Calculates the bounds of the widget containing the selection handle based
// on the SelectionBound's type and location // on the SelectionBound's type and location.
gfx::Rect GetSelectionWidgetBounds(const gfx::SelectionBound& bound) { gfx::Rect GetSelectionWidgetBounds(const gfx::SelectionBound& bound) {
gfx::Size image_size = GetHandleImage(bound.type())->Size(); gfx::Size image_size = GetHandleImage(bound.type())->Size();
int widget_width = image_size.width() + 2 * kSelectionHandleHorizPadding; int widget_width = image_size.width() + 2 * kSelectionHandleHorizPadding;
// Extend the widget height to handle touch events below the painted image.
int widget_height = bound.GetHeight() + image_size.height() + int widget_height = bound.GetHeight() + image_size.height() +
kSelectionHandleVerticalVisualOffset + kSelectionHandleVerticalVisualOffset +
kSelectionHandleVertPadding; kSelectionHandleVertPadding;
...@@ -200,21 +202,40 @@ gfx::Rect BoundToRect(const gfx::SelectionBound& bound) { ...@@ -200,21 +202,40 @@ gfx::Rect BoundToRect(const gfx::SelectionBound& bound) {
bound.edge_bottom_rounded()); bound.edge_bottom_rounded());
} }
} // namespace // A WindowTargeter that insets the top of the touch handle's hit-test region.
// This ensures that the client receives touch events above the painted image.
namespace views { // The widget extends its height to handle touch events below the painted image.
typedef TouchSelectionControllerImpl::EditingHandleView EditingHandleView;
// A WindowTargeter that shifts the hit-test target down - away from the text
// cursor and expanding the hit-test area just below the visible drag handle.
class TouchHandleWindowTargeter : public aura::WindowTargeter { class TouchHandleWindowTargeter : public aura::WindowTargeter {
public: public:
void SetHitTestOffset(int offset) { explicit TouchHandleWindowTargeter(aura::Window* window) : window_(window) {}
SetInsets(gfx::Insets(offset, 0, -offset, 0)); ~TouchHandleWindowTargeter() override = default;
void SetTopInset(int inset) { SetInsets(gfx::Insets(inset, 0, 0, 0)); }
// aura::WindowTargeter:
void OnSetInsets(const gfx::Insets& last_mouse_extend,
const gfx::Insets& last_touch_extend) override {
// Send the targeter insets to the window service if this is a mus client.
// This helps the window service send events directly to the text window.
// OnSetInsets is generally only called when the insets actually change.
if (window_->env()->mode() == aura::Env::Mode::MUS) {
gfx::Rect mask(window_->bounds().size());
mask.Inset(touch_extend());
aura::WindowPortMus::Get(window_->GetRootWindow())->SetHitTestMask(mask);
}
} }
private:
aura::Window* window_;
DISALLOW_COPY_AND_ASSIGN(TouchHandleWindowTargeter);
}; };
} // namespace
namespace views {
using EditingHandleView = TouchSelectionControllerImpl::EditingHandleView;
// A View that displays the text selection handle. // A View that displays the text selection handle.
class TouchSelectionControllerImpl::EditingHandleView class TouchSelectionControllerImpl::EditingHandleView
: public views::WidgetDelegateView { : public views::WidgetDelegateView {
...@@ -230,7 +251,7 @@ class TouchSelectionControllerImpl::EditingHandleView ...@@ -230,7 +251,7 @@ class TouchSelectionControllerImpl::EditingHandleView
widget_.reset(CreateTouchSelectionPopupWidget(context, this)); widget_.reset(CreateTouchSelectionPopupWidget(context, this));
aura::Window* window = widget_->GetNativeWindow(); aura::Window* window = widget_->GetNativeWindow();
targeter_ = new TouchHandleWindowTargeter(); targeter_ = new TouchHandleWindowTargeter(window);
window->SetEventTargeter(std::unique_ptr<ui::EventTargeter>(targeter_)); window->SetEventTargeter(std::unique_ptr<ui::EventTargeter>(targeter_));
// We are owned by the TouchSelectionControllerImpl. // We are owned by the TouchSelectionControllerImpl.
...@@ -357,8 +378,9 @@ class TouchSelectionControllerImpl::EditingHandleView ...@@ -357,8 +378,9 @@ class TouchSelectionControllerImpl::EditingHandleView
wm::ConvertPointFromScreen(window, &edge_bottom); wm::ConvertPointFromScreen(window, &edge_bottom);
selection_bound_.SetEdge(gfx::PointF(edge_top), gfx::PointF(edge_bottom)); selection_bound_.SetEdge(gfx::PointF(edge_top), gfx::PointF(edge_bottom));
} }
targeter_->SetHitTestOffset(selection_bound_.GetHeight() +
kSelectionHandleVerticalVisualOffset); targeter_->SetTopInset(selection_bound_.GetHeight() +
kSelectionHandleVerticalVisualOffset);
} }
void SetDrawInvisible(bool draw_invisible) { void SetDrawInvisible(bool draw_invisible) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment