Commit a9697328 authored by Mitsuru Oshima's avatar Mitsuru Oshima Committed by Commit Bot

Improve xdg_popup support

* use TYPE_POPUP for xdg_popup. The differences from TYPE_WINDOW are:
  - no frame
  - ash will not control its bounds
* make popups transient children: this fixes following scenarios
  - z order is correctly updated when parent's z order changes
  - they're grouped in overview.
* implement grab
  - capture even when the grab is requested on the surface
  - transfer capture to child if the child popup requested
     grab.
  - close the surface when capture(grab) is lost.
  - but transfer the grab to the parent if the parent had grab.

BUG=788782
TEST=covered by unit tests. manually tested with gtk3-demo.

Change-Id: I73f5bc7e555f2c28f78dc25a55f77f0a70fd9b78
Reviewed-on: https://chromium-review.googlesource.com/1102354
Commit-Queue: Mitsuru Oshima <oshima@chromium.org>
Reviewed-by: default avatarDavid Reveman <reveman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568364}
parent 099d49c3
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "ash/public/cpp/shell_window_ids.h" #include "ash/public/cpp/shell_window_ids.h"
#include "components/exo/pointer_delegate.h" #include "components/exo/pointer_delegate.h"
#include "components/exo/pointer_gesture_pinch_delegate.h" #include "components/exo/pointer_gesture_pinch_delegate.h"
#include "components/exo/shell_surface_base.h"
#include "components/exo/surface.h" #include "components/exo/surface.h"
#include "components/exo/wm_helper.h" #include "components/exo/wm_helper.h"
#include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_request.h"
...@@ -44,13 +45,8 @@ const float kLargeCursorScale = 2.8f; ...@@ -44,13 +45,8 @@ const float kLargeCursorScale = 2.8f;
const double kLocatedEventEpsilonSquared = 1.0 / (2000.0 * 2000.0); const double kLocatedEventEpsilonSquared = 1.0 / (2000.0 * 2000.0);
// Synthesized events typically lack floating point precision so to avoid bool SameLocation(const gfx::PointF& location_in_target,
// generating mouse event jitter we consider the location of these events const gfx::PointF& location) {
// to be the same as |location| if floored values match.
bool SameLocation(const ui::LocatedEvent* event, const gfx::PointF& location) {
if (event->flags() & ui::EF_IS_SYNTHESIZED)
return event->location() == gfx::ToFlooredPoint(location);
// In general, it is good practice to compare floats using an epsilon. // In general, it is good practice to compare floats using an epsilon.
// In particular, the mouse location_f() could differ between the // In particular, the mouse location_f() could differ between the
// MOUSE_PRESSED and MOUSE_RELEASED events. At MOUSE_RELEASED, it will have a // MOUSE_PRESSED and MOUSE_RELEASED events. At MOUSE_RELEASED, it will have a
...@@ -58,7 +54,7 @@ bool SameLocation(const ui::LocatedEvent* event, const gfx::PointF& location) { ...@@ -58,7 +54,7 @@ bool SameLocation(const ui::LocatedEvent* event, const gfx::PointF& location) {
// calculate it passing through all the hierarchy of windows, and that could // calculate it passing through all the hierarchy of windows, and that could
// generate rounding error. std::numeric_limits<float>::epsilon() is not big // generate rounding error. std::numeric_limits<float>::epsilon() is not big
// enough to catch this rounding error. // enough to catch this rounding error.
gfx::Vector2dF offset = event->location_f() - location; gfx::Vector2dF offset = location_in_target - location;
return offset.LengthSquared() < (2 * kLocatedEventEpsilonSquared); return offset.LengthSquared() < (2 * kLocatedEventEpsilonSquared);
} }
...@@ -203,10 +199,16 @@ void Pointer::OnSurfaceDestroying(Surface* surface) { ...@@ -203,10 +199,16 @@ void Pointer::OnSurfaceDestroying(Surface* surface) {
void Pointer::OnMouseEvent(ui::MouseEvent* event) { void Pointer::OnMouseEvent(ui::MouseEvent* event) {
Surface* target = GetEffectiveTargetForEvent(event); Surface* target = GetEffectiveTargetForEvent(event);
gfx::PointF location_in_target = event->location_f();
if (target) {
aura::Window::ConvertPointToTarget(
static_cast<aura::Window*>(event->target()), target->window(),
&location_in_target);
}
// Update focus if target is different than the current pointer focus. // Update focus if target is different than the current pointer focus.
if (target != focus_surface_) if (target != focus_surface_)
SetFocus(target, event->location_f(), event->button_flags()); SetFocus(target, location_in_target, event->button_flags());
if (!focus_surface_) if (!focus_surface_)
return; return;
...@@ -218,8 +220,15 @@ void Pointer::OnMouseEvent(ui::MouseEvent* event) { ...@@ -218,8 +220,15 @@ void Pointer::OnMouseEvent(ui::MouseEvent* event) {
// here as mouse movement can generate both "moved" and "entered" events // here as mouse movement can generate both "moved" and "entered" events
// but OnPointerMotion should only be called if location changed since // but OnPointerMotion should only be called if location changed since
// OnPointerEnter was called. // OnPointerEnter was called.
if (!SameLocation(event, location_)) { // For synthesized events, they typically lack floating point precision
location_ = event->location_f(); // so to avoid generating mouse event jitter we consider the location of
// these events to be the same as |location| if floored values match.
bool same_location = !event->IsSynthesized()
? SameLocation(location_in_target, location_)
: gfx::ToFlooredPoint(location_in_target) ==
gfx::ToFlooredPoint(location_);
if (!same_location) {
location_ = location_in_target;
delegate_->OnPointerMotion(event->time_stamp(), location_); delegate_->OnPointerMotion(event->time_stamp(), location_);
delegate_->OnPointerFrame(); delegate_->OnPointerFrame();
} }
...@@ -360,9 +369,9 @@ void Pointer::OnDisplayConfigurationChanged() { ...@@ -360,9 +369,9 @@ void Pointer::OnDisplayConfigurationChanged() {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Pointer, private: // Pointer, private:
Surface* Pointer::GetEffectiveTargetForEvent(ui::Event* event) const { Surface* Pointer::GetEffectiveTargetForEvent(ui::LocatedEvent* event) const {
Surface* target = Surface* target = ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(event);
Surface::AsSurface(static_cast<aura::Window*>(event->target()));
if (!target) if (!target)
return nullptr; return nullptr;
......
...@@ -27,7 +27,7 @@ class CopyOutputResult; ...@@ -27,7 +27,7 @@ class CopyOutputResult;
} }
namespace ui { namespace ui {
class Event; class LocatedEvent;
class MouseEvent; class MouseEvent;
} }
...@@ -84,7 +84,7 @@ class Pointer : public SurfaceTreeHost, ...@@ -84,7 +84,7 @@ class Pointer : public SurfaceTreeHost,
private: private:
// Returns the effective target for |event|. // Returns the effective target for |event|.
Surface* GetEffectiveTargetForEvent(ui::Event* event) const; Surface* GetEffectiveTargetForEvent(ui::LocatedEvent* event) const;
// Change pointer focus to |surface|. // Change pointer focus to |surface|.
void SetFocus(Surface* surface, void SetFocus(Surface* surface,
......
...@@ -140,6 +140,17 @@ void ShellSurface::SetFullscreen(bool fullscreen) { ...@@ -140,6 +140,17 @@ void ShellSurface::SetFullscreen(bool fullscreen) {
widget_->SetFullscreen(fullscreen); widget_->SetFullscreen(fullscreen);
} }
void ShellSurface::SetPopup() {
DCHECK(!widget_);
is_popup_ = true;
}
void ShellSurface::Grab() {
DCHECK(is_popup_);
DCHECK(!widget_);
has_grab_ = true;
}
void ShellSurface::Resize(int component) { void ShellSurface::Resize(int component) {
TRACE_EVENT1("exo", "ShellSurface::Resize", "component", component); TRACE_EVENT1("exo", "ShellSurface::Resize", "component", component);
......
...@@ -43,6 +43,12 @@ class ShellSurface : public ShellSurfaceBase, ...@@ -43,6 +43,12 @@ class ShellSurface : public ShellSurfaceBase,
// Set fullscreen state for shell surface. // Set fullscreen state for shell surface.
void SetFullscreen(bool fullscreen); void SetFullscreen(bool fullscreen);
// Make the shell surface popup type.
void SetPopup();
// Set event grab on the surface.
void Grab();
// Start an interactive resize of surface. |component| is one of the windows // Start an interactive resize of surface. |component| is one of the windows
// HT constants (see ui/base/hit_test.h) and describes in what direction the // HT constants (see ui/base/hit_test.h) and describes in what direction the
// surface should be resized. // surface should be resized.
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include "ui/gfx/geometry/vector2d_conversions.h" #include "ui/gfx/geometry/vector2d_conversions.h"
#include "ui/gfx/path.h" #include "ui/gfx/path.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
#include "ui/wm/core/capture_controller.h"
#include "ui/wm/core/coordinate_conversion.h" #include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/shadow_controller.h" #include "ui/wm/core/shadow_controller.h"
#include "ui/wm/core/shadow_types.h" #include "ui/wm/core/shadow_types.h"
...@@ -236,7 +237,10 @@ class CustomWindowTargeter : public aura::WindowTargeter { ...@@ -236,7 +237,10 @@ class CustomWindowTargeter : public aura::WindowTargeter {
if (!surface) if (!surface)
return false; return false;
int component = widget_->non_client_view()->NonClientHitTest(local_point); int component =
widget_->non_client_view()
? widget_->non_client_view()->NonClientHitTest(local_point)
: HTNOWHERE;
if (component != HTNOWHERE && component != HTCLIENT && if (component != HTNOWHERE && component != HTCLIENT &&
component != HTBORDER) { component != HTBORDER) {
return true; return true;
...@@ -412,6 +416,8 @@ ShellSurfaceBase::~ShellSurfaceBase() { ...@@ -412,6 +416,8 @@ ShellSurfaceBase::~ShellSurfaceBase() {
parent_->RemoveObserver(this); parent_->RemoveObserver(this);
if (root_surface()) if (root_surface())
root_surface()->RemoveSurfaceObserver(this); root_surface()->RemoveSurfaceObserver(this);
if (has_grab_)
wm::CaptureController::Get()->RemoveObserver(this);
} }
void ShellSurfaceBase::AcknowledgeConfigure(uint32_t serial) { void ShellSurfaceBase::AcknowledgeConfigure(uint32_t serial) {
...@@ -650,6 +656,41 @@ Surface* ShellSurfaceBase::GetMainSurface(const aura::Window* window) { ...@@ -650,6 +656,41 @@ Surface* ShellSurfaceBase::GetMainSurface(const aura::Window* window) {
return window->GetProperty(kMainSurfaceKey); return window->GetProperty(kMainSurfaceKey);
} }
// static
Surface* ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(
ui::LocatedEvent* event) {
aura::Window* window = wm::CaptureController::Get()->GetCaptureWindow();
gfx::PointF location_in_target = event->location_f();
if (!window)
return Surface::AsSurface(static_cast<aura::Window*>(event->target()));
Surface* main_surface = ShellSurfaceBase::GetMainSurface(window);
// Skip if the event is captured by non exo windwows.
if (!main_surface)
return nullptr;
while (true) {
aura::Window* focused = window->GetEventHandlerForPoint(
gfx::ToFlooredPoint(location_in_target));
if (focused) {
aura::Window::ConvertPointToTarget(window, focused, &location_in_target);
return Surface::AsSurface(focused);
}
aura::Window* parent_window = wm::GetTransientParent(window);
if (!parent_window) {
location_in_target = event->location_f();
return main_surface;
}
aura::Window::ConvertPointToTarget(window, parent_window,
&location_in_target);
window = parent_window;
}
}
std::unique_ptr<base::trace_event::TracedValue> std::unique_ptr<base::trace_event::TracedValue>
ShellSurfaceBase::AsTracedValue() const { ShellSurfaceBase::AsTracedValue() const {
std::unique_ptr<base::trace_event::TracedValue> value( std::unique_ptr<base::trace_event::TracedValue> value(
...@@ -746,6 +787,9 @@ void ShellSurfaceBase::OnSurfaceCommit() { ...@@ -746,6 +787,9 @@ void ShellSurfaceBase::OnSurfaceCommit() {
DCHECK(!widget_->IsVisible()); DCHECK(!widget_->IsVisible());
pending_show_widget_ = false; pending_show_widget_ = false;
widget_->Show(); widget_->Show();
if (has_grab_)
StartCapture();
if (container_ == ash::kShellWindowId_SystemModalContainer) if (container_ == ash::kShellWindowId_SystemModalContainer)
UpdateSystemModal(); UpdateSystemModal();
} }
...@@ -970,6 +1014,29 @@ void ShellSurfaceBase::GetWidgetHitTestMask(gfx::Path* mask) const { ...@@ -970,6 +1014,29 @@ void ShellSurfaceBase::GetWidgetHitTestMask(gfx::Path* mask) const {
mask->transform(matrix); mask->transform(matrix);
} }
void ShellSurfaceBase::OnCaptureChanged(aura::Window* lost_capture,
aura::Window* gained_capture) {
if (lost_capture == widget_->GetNativeWindow() && is_popup_) {
wm::CaptureController::Get()->RemoveObserver(this);
if (gained_capture &&
lost_capture == wm::GetTransientParent(gained_capture)) {
// Don't close if the capture has been transferred to the child popup.
return;
}
aura::Window* parent = wm::GetTransientParent(lost_capture);
if (parent) {
// The capture needs to be transferred to the parent if it had grab.
views::Widget* parent_widget =
views::Widget::GetWidgetForNativeWindow(parent);
ShellSurfaceBase* parent_shell_surface = static_cast<ShellSurfaceBase*>(
parent_widget->widget_delegate()->GetContentsView());
if (parent_shell_surface->has_grab_)
parent_shell_surface->StartCapture();
}
widget_->Close();
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// views::Views overrides: // views::Views overrides:
...@@ -1178,7 +1245,8 @@ void ShellSurfaceBase::CreateShellSurfaceWidget( ...@@ -1178,7 +1245,8 @@ void ShellSurfaceBase::CreateShellSurfaceWidget(
DCHECK(!widget_); DCHECK(!widget_);
views::Widget::InitParams params; views::Widget::InitParams params;
params.type = views::Widget::InitParams::TYPE_WINDOW; params.type = is_popup_ ? views::Widget::InitParams::TYPE_POPUP
: views::Widget::InitParams::TYPE_WINDOW;
params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET; params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
params.delegate = this; params.delegate = this;
params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE; params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE;
...@@ -1268,9 +1336,8 @@ void ShellSurfaceBase::Configure() { ...@@ -1268,9 +1336,8 @@ void ShellSurfaceBase::Configure() {
uint32_t serial = 0; uint32_t serial = 0;
if (!configure_callback_.is_null()) { if (!configure_callback_.is_null()) {
if (widget_) { if (widget_) {
const views::NonClientView* non_client_view = widget_->non_client_view();
serial = configure_callback_.Run( serial = configure_callback_.Run(
non_client_view->frame_view()->GetBoundsForClientView().size(), GetClientViewBounds().size(),
ash::wm::GetWindowState(widget_->GetNativeWindow())->GetStateType(), ash::wm::GetWindowState(widget_->GetNativeWindow())->GetStateType(),
IsResizing(), widget_->IsActive(), origin_offset); IsResizing(), widget_->IsActive(), origin_offset);
} else { } else {
...@@ -1349,10 +1416,7 @@ void ShellSurfaceBase::SetWidgetBounds(const gfx::Rect& bounds) { ...@@ -1349,10 +1416,7 @@ void ShellSurfaceBase::SetWidgetBounds(const gfx::Rect& bounds) {
} }
void ShellSurfaceBase::UpdateSurfaceBounds() { void ShellSurfaceBase::UpdateSurfaceBounds() {
gfx::Point origin = widget_->non_client_view() gfx::Point origin = GetClientViewBounds().origin();
->frame_view()
->GetBoundsForClientView()
.origin();
origin += GetSurfaceOrigin().OffsetFromOrigin(); origin += GetSurfaceOrigin().OffsetFromOrigin();
origin -= ToFlooredVector2d(ScaleVector2d( origin -= ToFlooredVector2d(ScaleVector2d(
...@@ -1413,6 +1477,14 @@ gfx::Point ShellSurfaceBase::GetMouseLocation() const { ...@@ -1413,6 +1477,14 @@ gfx::Point ShellSurfaceBase::GetMouseLocation() const {
return location; return location;
} }
gfx::Rect ShellSurfaceBase::GetClientViewBounds() const {
return widget_->non_client_view()
? widget_->non_client_view()
->frame_view()
->GetBoundsForClientView()
: gfx::Rect(widget_->GetWindowBoundsInScreen().size());
}
gfx::Rect ShellSurfaceBase::GetShadowBounds() const { gfx::Rect ShellSurfaceBase::GetShadowBounds() const {
return shadow_bounds_->IsEmpty() return shadow_bounds_->IsEmpty()
? gfx::Rect(widget_->GetNativeWindow()->bounds().size()) ? gfx::Rect(widget_->GetNativeWindow()->bounds().size())
...@@ -1551,8 +1623,11 @@ void ShellSurfaceBase::EndDrag(bool revert) { ...@@ -1551,8 +1623,11 @@ void ShellSurfaceBase::EndDrag(bool revert) {
gfx::Rect ShellSurfaceBase::GetWidgetBounds() const { gfx::Rect ShellSurfaceBase::GetWidgetBounds() const {
gfx::Rect visible_bounds = GetVisibleBounds(); gfx::Rect visible_bounds = GetVisibleBounds();
gfx::Rect new_widget_bounds = gfx::Rect new_widget_bounds =
widget_->non_client_view()->GetWindowBoundsForClientBounds( widget_->non_client_view()
visible_bounds); ? widget_->non_client_view()->GetWindowBoundsForClientBounds(
visible_bounds)
: visible_bounds;
if (movement_disabled_) { if (movement_disabled_) {
new_widget_bounds.set_origin(origin_); new_widget_bounds.set_origin(origin_);
} else if (resize_component_ == HTCAPTION) { } else if (resize_component_ == HTCAPTION) {
...@@ -1574,8 +1649,8 @@ gfx::Point ShellSurfaceBase::GetSurfaceOrigin() const { ...@@ -1574,8 +1649,8 @@ gfx::Point ShellSurfaceBase::GetSurfaceOrigin() const {
DCHECK(!movement_disabled_ || resize_component_ == HTCAPTION); DCHECK(!movement_disabled_ || resize_component_ == HTCAPTION);
gfx::Rect visible_bounds = GetVisibleBounds(); gfx::Rect visible_bounds = GetVisibleBounds();
gfx::Rect client_bounds = gfx::Rect client_bounds = GetClientViewBounds();
widget_->non_client_view()->frame_view()->GetBoundsForClientView();
switch (resize_component_) { switch (resize_component_) {
case HTCAPTION: case HTCAPTION:
return gfx::Point() + origin_offset_ - visible_bounds.OffsetFromOrigin(); return gfx::Point() + origin_offset_ - visible_bounds.OffsetFromOrigin();
...@@ -1619,4 +1694,11 @@ void ShellSurfaceBase::SetParentWindow(aura::Window* parent) { ...@@ -1619,4 +1694,11 @@ void ShellSurfaceBase::SetParentWindow(aura::Window* parent) {
widget_->OnSizeConstraintsChanged(); widget_->OnSizeConstraintsChanged();
} }
void ShellSurfaceBase::StartCapture() {
widget_->set_auto_release_capture(false);
wm::CaptureController::Get()->AddObserver(this);
// Just capture on the window.
widget_->SetCapture(nullptr /* view */);
}
} // namespace exo } // namespace exo
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "components/exo/surface_observer.h" #include "components/exo/surface_observer.h"
#include "components/exo/surface_tree_host.h" #include "components/exo/surface_tree_host.h"
#include "ui/aura/client/capture_client_observer.h"
#include "ui/aura/window_observer.h" #include "ui/aura/window_observer.h"
#include "ui/base/hit_test.h" #include "ui/base/hit_test.h"
#include "ui/compositor/compositor_lock.h" #include "ui/compositor/compositor_lock.h"
...@@ -53,6 +54,7 @@ class Surface; ...@@ -53,6 +54,7 @@ class Surface;
class ShellSurfaceBase : public SurfaceTreeHost, class ShellSurfaceBase : public SurfaceTreeHost,
public SurfaceObserver, public SurfaceObserver,
public aura::WindowObserver, public aura::WindowObserver,
public aura::client::CaptureClientObserver,
public views::WidgetDelegate, public views::WidgetDelegate,
public views::View, public views::View,
public wm::ActivationChangeObserver { public wm::ActivationChangeObserver {
...@@ -166,6 +168,12 @@ class ShellSurfaceBase : public SurfaceTreeHost, ...@@ -166,6 +168,12 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// |window| must not be nullptr. // |window| must not be nullptr.
static Surface* GetMainSurface(const aura::Window* window); static Surface* GetMainSurface(const aura::Window* window);
// Returns the target surface for the located event |event|. If an
// event handling is grabbed by an window, it'll first examine that
// window, then traverse to its transeitn parent if the parent also
// requested grab.
static Surface* GetTargetSurfaceForLocatedEvent(ui::LocatedEvent* event);
// Returns a trace value representing the state of the surface. // Returns a trace value representing the state of the surface.
std::unique_ptr<base::trace_event::TracedValue> AsTracedValue() const; std::unique_ptr<base::trace_event::TracedValue> AsTracedValue() const;
...@@ -181,6 +189,10 @@ class ShellSurfaceBase : public SurfaceTreeHost, ...@@ -181,6 +189,10 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// Overridden from SurfaceObserver: // Overridden from SurfaceObserver:
void OnSurfaceDestroying(Surface* surface) override; void OnSurfaceDestroying(Surface* surface) override;
// Overridden from CaptureClientObserver:
void OnCaptureChanged(aura::Window* lost_capture,
aura::Window* gained_capture) override;
// Overridden from views::WidgetDelegate: // Overridden from views::WidgetDelegate:
bool CanResize() const override; bool CanResize() const override;
bool CanMaximize() const override; bool CanMaximize() const override;
...@@ -278,6 +290,9 @@ class ShellSurfaceBase : public SurfaceTreeHost, ...@@ -278,6 +290,9 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// In the coordinate system of the parent root window. // In the coordinate system of the parent root window.
gfx::Point GetMouseLocation() const; gfx::Point GetMouseLocation() const;
// Returns the bounds of the client area.nnn
gfx::Rect GetClientViewBounds() const;
// In the local coordinate system of the window. // In the local coordinate system of the window.
virtual gfx::Rect GetShadowBounds() const; virtual gfx::Rect GetShadowBounds() const;
...@@ -289,6 +304,9 @@ class ShellSurfaceBase : public SurfaceTreeHost, ...@@ -289,6 +304,9 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// Set the parent window of this surface. // Set the parent window of this surface.
void SetParentWindow(aura::Window* parent); void SetParentWindow(aura::Window* parent);
// Start the event capture on this surface.
void StartCapture();
const gfx::Rect& geometry() const { return geometry_; } const gfx::Rect& geometry() const { return geometry_; }
views::Widget* widget_ = nullptr; views::Widget* widget_ = nullptr;
...@@ -316,6 +334,8 @@ class ShellSurfaceBase : public SurfaceTreeHost, ...@@ -316,6 +334,8 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// complete. https://crbug.com/801666. // complete. https://crbug.com/801666.
bool client_controlled_move_resize_ = true; bool client_controlled_move_resize_ = true;
SurfaceFrameType frame_type_ = SurfaceFrameType::NONE; SurfaceFrameType frame_type_ = SurfaceFrameType::NONE;
bool is_popup_ = false;
bool has_grab_ = false;
bool frame_enabled() const { bool frame_enabled() const {
return frame_type_ != SurfaceFrameType::NONE && return frame_type_ != SurfaceFrameType::NONE &&
......
...@@ -33,7 +33,10 @@ ...@@ -33,7 +33,10 @@
#include "ui/display/display.h" #include "ui/display/display.h"
#include "ui/display/manager/display_manager.h" #include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h" #include "ui/display/screen.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
#include "ui/wm/core/capture_controller.h"
#include "ui/wm/core/window_util.h" #include "ui/wm/core/window_util.h"
namespace exo { namespace exo {
...@@ -57,6 +60,18 @@ uint32_t ConfigureFullscreen(uint32_t serial, ...@@ -57,6 +60,18 @@ uint32_t ConfigureFullscreen(uint32_t serial,
return serial; return serial;
} }
std::unique_ptr<ShellSurface> CreatePopupShellSurface(
Surface* popup_surface,
ShellSurface* parent,
const gfx::Point& origin) {
auto popup_shell_surface = std::make_unique<ShellSurface>(popup_surface);
popup_shell_surface->DisableMovement();
popup_shell_surface->SetPopup();
popup_shell_surface->SetParent(parent);
popup_shell_surface->SetOrigin(origin);
return popup_shell_surface;
}
TEST_F(ShellSurfaceTest, AcknowledgeConfigure) { TEST_F(ShellSurfaceTest, AcknowledgeConfigure) {
gfx::Size buffer_size(32, 32); gfx::Size buffer_size(32, 32);
std::unique_ptr<Buffer> buffer( std::unique_ptr<Buffer> buffer(
...@@ -583,5 +598,86 @@ TEST_F(ShellSurfaceTest, CycleSnap) { ...@@ -583,5 +598,86 @@ TEST_F(ShellSurfaceTest, CycleSnap) {
shell_surface->GetWidget()->GetWindowBoundsInScreen().width()); shell_surface->GetWidget()->GetWindowBoundsInScreen().width());
} }
TEST_F(ShellSurfaceTest, Popup) {
gfx::Size buffer_size(256, 256);
std::unique_ptr<Buffer> buffer(
new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
surface->Attach(buffer.get());
surface->Commit();
shell_surface->GetWidget()->SetBounds(gfx::Rect(0, 0, 256, 256));
std::unique_ptr<Buffer> popup_buffer(
new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
std::unique_ptr<Surface> popup_surface(new Surface);
popup_surface->Attach(popup_buffer.get());
std::unique_ptr<ShellSurface> popup_shell_surface(CreatePopupShellSurface(
popup_surface.get(), shell_surface.get(), gfx::Point(50, 50)));
popup_shell_surface->Grab();
popup_surface->Commit();
ASSERT_EQ(gfx::Rect(50, 50, 256, 256),
popup_shell_surface->GetWidget()->GetWindowBoundsInScreen());
// Verify that created shell surface is popup and has capture.
EXPECT_EQ(aura::client::WINDOW_TYPE_POPUP,
popup_shell_surface->GetWidget()->GetNativeWindow()->type());
EXPECT_EQ(wm::CaptureController::Get()->GetCaptureWindow(),
popup_shell_surface->GetWidget()->GetNativeWindow());
// ShellSurface can capture the event even after it is craeted.
std::unique_ptr<Buffer> sub_popup_buffer(
new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
std::unique_ptr<Surface> sub_popup_surface(new Surface);
sub_popup_surface->Attach(popup_buffer.get());
std::unique_ptr<ShellSurface> sub_popup_shell_surface(CreatePopupShellSurface(
sub_popup_surface.get(), popup_shell_surface.get(), gfx::Point(100, 50)));
sub_popup_shell_surface->Grab();
sub_popup_surface->Commit();
ASSERT_EQ(gfx::Rect(100, 50, 256, 256),
sub_popup_shell_surface->GetWidget()->GetWindowBoundsInScreen());
// The capture should be on sub_popup_shell_surface.
EXPECT_EQ(wm::CaptureController::Get()->GetCaptureWindow(),
sub_popup_shell_surface->GetWidget()->GetNativeWindow());
EXPECT_EQ(aura::client::WINDOW_TYPE_POPUP,
sub_popup_shell_surface->GetWidget()->GetNativeWindow()->type());
{
// Mouse is on the top most popup.
ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0),
gfx::Point(100, 50), ui::EventTimeForNow(), 0, 0);
EXPECT_EQ(sub_popup_surface.get(),
ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(&event));
}
{
// Move the mouse to the parent popup.
ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(-25, 0),
gfx::Point(75, 50), ui::EventTimeForNow(), 0, 0);
EXPECT_EQ(popup_surface.get(),
ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(&event));
}
{
// Move the mouse to the main window.
ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(-25, -25),
gfx::Point(75, 25), ui::EventTimeForNow(), 0, 0);
EXPECT_EQ(surface.get(),
ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(&event));
}
// Removing top most popup moves the grab to parent popup.
sub_popup_shell_surface.reset();
EXPECT_EQ(wm::CaptureController::Get()->GetCaptureWindow(),
popup_shell_surface->GetWidget()->GetNativeWindow());
{
// Targetting should still work.
ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0),
gfx::Point(50, 50), ui::EventTimeForNow(), 0, 0);
EXPECT_EQ(popup_surface.get(),
ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(&event));
}
}
} // namespace } // namespace
} // namespace exo } // namespace exo
...@@ -4,12 +4,15 @@ ...@@ -4,12 +4,15 @@
#include "components/exo/touch.h" #include "components/exo/touch.h"
#include "components/exo/shell_surface_base.h"
#include "components/exo/surface.h" #include "components/exo/surface.h"
#include "components/exo/touch_delegate.h" #include "components/exo/touch_delegate.h"
#include "components/exo/touch_stylus_delegate.h" #include "components/exo/touch_stylus_delegate.h"
#include "components/exo/wm_helper.h" #include "components/exo/wm_helper.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
#include "ui/events/event.h" #include "ui/events/event.h"
#include "ui/wm/core/capture_controller.h"
#include "ui/wm/core/window_util.h"
namespace exo { namespace exo {
namespace { namespace {
...@@ -190,9 +193,9 @@ void Touch::OnSurfaceDestroying(Surface* surface) { ...@@ -190,9 +193,9 @@ void Touch::OnSurfaceDestroying(Surface* surface) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Touch, private: // Touch, private:
Surface* Touch::GetEffectiveTargetForEvent(ui::Event* event) const { Surface* Touch::GetEffectiveTargetForEvent(ui::LocatedEvent* event) const {
Surface* target = Surface* target = ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(event);
Surface::AsSurface(static_cast<aura::Window*>(event->target()));
if (!target) if (!target)
return nullptr; return nullptr;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/point_f.h"
namespace ui { namespace ui {
class LocatedEvent;
class TouchEvent; class TouchEvent;
} }
...@@ -41,7 +42,7 @@ class Touch : public ui::EventHandler, public SurfaceObserver { ...@@ -41,7 +42,7 @@ class Touch : public ui::EventHandler, public SurfaceObserver {
private: private:
// Returns the effective target for |event|. // Returns the effective target for |event|.
Surface* GetEffectiveTargetForEvent(ui::Event* event) const; Surface* GetEffectiveTargetForEvent(ui::LocatedEvent* event) const;
// The delegate instance that all events are dispatched to. // The delegate instance that all events are dispatched to.
TouchDelegate* const delegate_; TouchDelegate* const delegate_;
......
...@@ -1792,18 +1792,43 @@ const struct zxdg_toplevel_v6_interface xdg_toplevel_v6_implementation = { ...@@ -1792,18 +1792,43 @@ const struct zxdg_toplevel_v6_interface xdg_toplevel_v6_implementation = {
// Wrapper around shell surface that allows us to handle the case where the // Wrapper around shell surface that allows us to handle the case where the
// xdg surface resource is destroyed before the popup resource. // xdg surface resource is destroyed before the popup resource.
class WaylandPopup { class WaylandPopup : aura::WindowObserver {
public: public:
WaylandPopup(wl_resource* resource, wl_resource* surface_resource) WaylandPopup(wl_resource* resource, wl_resource* surface_resource)
: resource_(resource), weak_ptr_factory_(this) { : resource_(resource),
ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(surface_resource); shell_surface_(GetUserDataAs<ShellSurface>(surface_resource)),
shell_surface->set_close_callback( weak_ptr_factory_(this) {
shell_surface_->host_window()->AddObserver(this);
shell_surface_->set_close_callback(
base::Bind(&WaylandPopup::OnClose, weak_ptr_factory_.GetWeakPtr())); base::Bind(&WaylandPopup::OnClose, weak_ptr_factory_.GetWeakPtr()));
shell_surface->set_configure_callback( shell_surface_->set_configure_callback(
base::Bind(&HandleXdgSurfaceV6ConfigureCallback, surface_resource, base::Bind(&HandleXdgSurfaceV6ConfigureCallback, surface_resource,
base::Bind(&WaylandPopup::OnConfigure, base::Bind(&WaylandPopup::OnConfigure,
weak_ptr_factory_.GetWeakPtr()))); weak_ptr_factory_.GetWeakPtr())));
} }
~WaylandPopup() override {
if (shell_surface_)
shell_surface_->host_window()->RemoveObserver(this);
}
void Grab() {
if (!shell_surface_) {
wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
"the surface has already been destroyed");
return;
}
if (shell_surface_->GetWidget()) {
wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
"grab must be called before construction");
return;
}
shell_surface_->Grab();
}
// Overridden from aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override {
shell_surface_ = nullptr;
}
private: private:
void OnClose() { void OnClose() {
...@@ -1819,6 +1844,7 @@ class WaylandPopup { ...@@ -1819,6 +1844,7 @@ class WaylandPopup {
} }
wl_resource* const resource_; wl_resource* const resource_;
ShellSurface* shell_surface_;
base::WeakPtrFactory<WaylandPopup> weak_ptr_factory_; base::WeakPtrFactory<WaylandPopup> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(WaylandPopup); DISALLOW_COPY_AND_ASSIGN(WaylandPopup);
...@@ -1832,7 +1858,7 @@ void xdg_popup_v6_grab(wl_client* client, ...@@ -1832,7 +1858,7 @@ void xdg_popup_v6_grab(wl_client* client,
wl_resource* resource, wl_resource* resource,
wl_resource* seat, wl_resource* seat,
uint32_t serial) { uint32_t serial) {
NOTIMPLEMENTED(); GetUserDataAs<WaylandPopup>(resource)->Grab();
} }
const struct zxdg_popup_v6_interface xdg_popup_v6_implementation = { const struct zxdg_popup_v6_interface xdg_popup_v6_implementation = {
...@@ -1885,6 +1911,12 @@ void xdg_surface_v6_get_popup(wl_client* client, ...@@ -1885,6 +1911,12 @@ void xdg_surface_v6_get_popup(wl_client* client,
return; return;
} }
if (shell_surface->GetWidget()) {
wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
"get_popup is called after constructed");
return;
}
gfx::Point position = GetUserDataAs<WaylandPositioner>(positioner_resource) gfx::Point position = GetUserDataAs<WaylandPositioner>(positioner_resource)
->CalculatePosition(); ->CalculatePosition();
// |position| is relative to the parent's contents view origin, and |origin| // |position| is relative to the parent's contents view origin, and |origin|
...@@ -1897,6 +1929,8 @@ void xdg_surface_v6_get_popup(wl_client* client, ...@@ -1897,6 +1929,8 @@ void xdg_surface_v6_get_popup(wl_client* client,
shell_surface->DisableMovement(); shell_surface->DisableMovement();
shell_surface->SetActivatable(false); shell_surface->SetActivatable(false);
shell_surface->SetCanMinimize(false); shell_surface->SetCanMinimize(false);
shell_surface->SetParent(parent);
shell_surface->SetPopup();
shell_surface->SetEnabled(true); shell_surface->SetEnabled(true);
wl_resource* xdg_popup_resource = wl_resource* xdg_popup_resource =
......
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