Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(196)

Unified Diff: components/exo/shell_surface.cc

Issue 2396883003: exo: Fix dragging edge cases (Closed)
Patch Set: Fix unit tests Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: components/exo/shell_surface.cc
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc
index 2027afdc95cefa71592fedd06a1681c7bc6043ee..e3a5ea0448223ad537651b8ff0f2b6b991f1810c 100644
--- a/components/exo/shell_surface.cc
+++ b/components/exo/shell_surface.cc
@@ -15,6 +15,7 @@
#include "ash/common/wm/window_state.h"
#include "ash/common/wm/window_state_delegate.h"
#include "ash/common/wm_shell.h"
+#include "ash/wm/drag_window_resizer.h"
#include "ash/wm/window_state_aura.h"
#include "ash/wm/window_util.h"
#include "base/logging.h"
@@ -23,6 +24,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_argument.h"
+#include "components/exo/display.h"
#include "components/exo/surface.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/cursor_client.h"
@@ -188,6 +190,23 @@ class CustomWindowStateDelegate : public ash::wm::WindowStateDelegate,
DISALLOW_COPY_AND_ASSIGN(CustomWindowStateDelegate);
};
+class CustomWindowResizer : public ash::WindowResizer {
+ public:
+ explicit CustomWindowResizer(ash::wm::WindowState* window_state)
+ : WindowResizer(window_state), shell_(GetTarget()->GetShell()) {
+ shell_->LockCursor();
+ }
+
+ ~CustomWindowResizer() override { shell_->UnlockCursor(); }
+
oshima 2016/10/12 01:56:54 // ash::WindowResizer:
Dominik Laskowski 2016/10/13 03:21:17 Done.
+ void Drag(const gfx::Point& location, int event_flags) override {}
+ void CompleteDrag() override {}
+ void RevertDrag() override {}
+
+ private:
+ ash::WmShell* const shell_;
oshima 2016/10/12 01:56:54 DISALLOW_COPY_AND_ASSIGN
Dominik Laskowski 2016/10/13 03:21:17 Done.
+};
+
class ShellSurfaceWidget : public views::Widget {
public:
explicit ShellSurfaceWidget(ShellSurface* shell_surface)
@@ -335,18 +354,21 @@ ShellSurface::ScopedAnimationsDisabled::~ScopedAnimationsDisabled() {
DEFINE_LOCAL_WINDOW_PROPERTY_KEY(std::string*, kApplicationIdKey, nullptr)
DEFINE_LOCAL_WINDOW_PROPERTY_KEY(Surface*, kMainSurfaceKey, nullptr)
-ShellSurface::ShellSurface(Surface* surface,
+ShellSurface::ShellSurface(const Display& display,
+ Surface* surface,
ShellSurface* parent,
const gfx::Rect& initial_bounds,
bool activatable,
int container)
- : widget_(nullptr),
+ : display_(display),
+ widget_(nullptr),
surface_(surface),
parent_(parent ? parent->GetWidget()->GetNativeWindow() : nullptr),
initial_bounds_(initial_bounds),
activatable_(activatable),
container_(container) {
WMHelper::GetInstance()->AddActivationObserver(this);
+ WMHelper::GetInstance()->AddShellObserver(this);
surface_->SetSurfaceDelegate(this);
surface_->AddSurfaceObserver(this);
surface_->window()->Show();
@@ -355,8 +377,9 @@ ShellSurface::ShellSurface(Surface* surface,
parent_->AddObserver(this);
}
-ShellSurface::ShellSurface(Surface* surface)
- : ShellSurface(surface,
+ShellSurface::ShellSurface(const Display& display, Surface* surface)
+ : ShellSurface(display,
+ surface,
nullptr,
gfx::Rect(),
true,
@@ -364,8 +387,7 @@ ShellSurface::ShellSurface(Surface* surface)
ShellSurface::~ShellSurface() {
DCHECK(!scoped_configure_);
- if (resizer_)
- EndDrag(false /* revert */);
+ EndDragOrMove(false /* revert */);
if (widget_) {
ash::wm::GetWindowState(widget_->GetNativeWindow())->RemoveObserver(this);
widget_->GetNativeWindow()->RemoveObserver(this);
@@ -374,6 +396,7 @@ ShellSurface::~ShellSurface() {
widget_->CloseNow();
}
WMHelper::GetInstance()->RemoveActivationObserver(this);
+ WMHelper::GetInstance()->RemoveShellObserver(this);
if (parent_)
parent_->RemoveObserver(this);
if (surface_) {
@@ -474,6 +497,41 @@ void ShellSurface::Restore() {
widget_->Restore();
}
+void ShellSurface::SetMoving() {
+ TRACE_EVENT0("exo", "ShellSurface::SetMoving");
+
+ if (move_resizer_)
+ return;
+
+ ash::wm::WindowState* const window_state =
+ ash::wm::GetWindowState(widget_->GetNativeWindow());
+
+ DCHECK(!window_state->drag_details());
+ window_state->CreateDragDetails(GetMouseLocation(), HTCAPTION,
+ aura::client::WINDOW_MOVE_SOURCE_MOUSE);
+
+ // The resizer renders phantom windows, but does not control window movement.
+ move_resizer_.reset(ash::DragWindowResizer::Create(
+ new CustomWindowResizer(window_state), window_state));
+
+ surface_->window()->SetCapture();
+}
+
+void ShellSurface::UnsetMoving(bool revert) {
+ TRACE_EVENT1("exo", "ShellSurface::UnsetMoving", "revert", revert);
+
+ if (!move_resizer_)
+ return;
+
+ if (revert)
+ move_resizer_->RevertDrag();
+ else
+ move_resizer_->CompleteDrag();
+
+ move_resizer_.reset();
+ surface_->window()->ReleaseCapture();
+}
+
void ShellSurface::SetFullscreen(bool fullscreen) {
TRACE_EVENT1("exo", "ShellSurface::SetFullscreen", "fullscreen", fullscreen);
@@ -688,9 +746,7 @@ void ShellSurface::OnSurfaceCommit() {
}
}
- // Update surface bounds.
- surface_->window()->SetBounds(
- gfx::Rect(surface_origin, surface_->window()->layer()->size()));
+ UpdateSurfaceBounds();
// Update surface scale.
if (pending_scale_ != scale_) {
@@ -720,8 +776,7 @@ bool ShellSurface::IsSurfaceSynchronized() const {
// SurfaceObserver overrides:
void ShellSurface::OnSurfaceDestroying(Surface* surface) {
- if (resizer_)
- EndDrag(false /* revert */);
+ EndDragOrMove(false /* revert */);
if (widget_)
SetMainSurface(widget_->GetNativeWindow(), nullptr);
surface->RemoveSurfaceObserver(this);
@@ -761,8 +816,7 @@ base::string16 ShellSurface::GetWindowTitle() const {
}
void ShellSurface::WindowClosing() {
- if (resizer_)
- EndDrag(true /* revert */);
+ EndDragOrMove(true /* revert */);
SetEnabled(false);
widget_ = nullptr;
shadow_overlay_ = nullptr;
@@ -824,6 +878,8 @@ void ShellSurface::OnPreWindowStateTypeChange(
ash::wm::WindowStateType new_type = window_state->GetStateType();
if (ash::wm::IsMaximizedOrFullscreenOrPinnedWindowStateType(old_type) ||
ash::wm::IsMaximizedOrFullscreenOrPinnedWindowStateType(new_type)) {
+ EndDragOrMove(false /* revert */);
+
// When transitioning in/out of maximized or fullscreen mode we need to
// make sure we have a configure callback before we allow the default
// cross-fade animations. The configure callback provides a mechanism for
@@ -875,8 +931,7 @@ void ShellSurface::OnWindowBoundsChanged(aura::Window* window,
pending_origin_config_offset_ += origin_offset;
origin_ -= origin_offset;
- surface_->window()->SetBounds(
- gfx::Rect(GetSurfaceOrigin(), surface_->window()->layer()->size()));
+ UpdateSurfaceBounds();
// The shadow size may be updated to match the widget. Change it back
// to the shadow content size.
@@ -907,6 +962,9 @@ void ShellSurface::OnWindowActivated(
if (!widget_)
return;
+ if (lost_active == widget_->GetNativeWindow())
+ UnsetMoving(false /* revert */);
+
if (gained_active == widget_->GetNativeWindow() ||
lost_active == widget_->GetNativeWindow()) {
DCHECK(activatable_);
@@ -916,6 +974,18 @@ void ShellSurface::OnWindowActivated(
}
////////////////////////////////////////////////////////////////////////////////
+// WMHelper::ShellObserver overrides:
+
+void ShellSurface::OnOverviewModeStarted() {
+ EndDragOrMove(false /* revert */);
+ ignore_widget_bounds_changes_ = true;
+}
+
+void ShellSurface::OnOverviewModeEnded() {
+ ignore_widget_bounds_changes_ = false;
+}
oshima 2016/10/12 01:56:54 Can you test what happens when display configurati
Dominik Laskowski 2016/10/13 03:21:17 In general, this edge case is broken even for Chro
oshima 2016/10/17 17:22:58 That's a separate issue. When display configuraito
Dominik Laskowski 2016/10/18 21:22:56 Sorry, I misread that as changing the display conf
+
+////////////////////////////////////////////////////////////////////////////////
// ui::EventHandler overrides:
void ShellSurface::OnKeyEvent(ui::KeyEvent* event) {
@@ -1012,6 +1082,7 @@ void ShellSurface::CreateShellSurfaceWidget(ui::WindowShowState show_state) {
params.parent =
parent_ ? parent_ : WMHelper::GetInstance()->GetContainer(container_);
params.bounds = initial_bounds_;
+ display_.ConvertFromVirtualToScreen(&params.bounds);
bool activatable = activatable_;
// ShellSurfaces in system modal container are only activatable if input
// region is non-empty. See OnCommitSurface() for more details.
@@ -1125,16 +1196,10 @@ void ShellSurface::AttemptToStartDrag(int component) {
if (widget_->GetNativeWindow()->HasCapture())
return;
- aura::Window* root_window = widget_->GetNativeWindow()->GetRootWindow();
- gfx::Point drag_location =
- root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot();
- aura::Window::ConvertPointToTarget(
- root_window, widget_->GetNativeWindow()->parent(), &drag_location);
-
// Set the cursor before calling CreateWindowResizer(), as that will
// eventually call LockCursor() and prevent the cursor from changing.
- aura::client::CursorClient* cursor_client =
- aura::client::GetCursorClient(root_window);
+ aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(
+ widget_->GetNativeWindow()->GetRootWindow());
DCHECK(cursor_client);
switch (component) {
@@ -1171,7 +1236,7 @@ void ShellSurface::AttemptToStartDrag(int component) {
}
resizer_ = ash::CreateWindowResizer(
- ash::WmWindowAura::Get(widget_->GetNativeWindow()), drag_location,
+ ash::WmWindowAura::Get(widget_->GetNativeWindow()), GetMouseLocation(),
component, aura::client::WINDOW_MOVE_SOURCE_MOUSE);
if (!resizer_)
return;
@@ -1213,6 +1278,13 @@ void ShellSurface::EndDrag(bool revert) {
UpdateWidgetBounds();
}
+void ShellSurface::EndDragOrMove(bool revert) {
+ if (resizer_)
+ EndDrag(revert);
+ else
+ UnsetMoving(revert);
+}
+
bool ShellSurface::IsResizing() const {
if (!resizer_)
return false;
@@ -1232,8 +1304,12 @@ gfx::Point ShellSurface::GetSurfaceOrigin() const {
// If initial bounds were specified then surface origin is always relative
// to those bounds.
- if (!initial_bounds_.IsEmpty())
- return initial_bounds_.origin() - window_bounds.OffsetFromOrigin();
+ if (!initial_bounds_.IsEmpty()) {
+ gfx::Point origin = window_bounds.origin();
+ wm::ConvertPointToScreen(widget_->GetNativeWindow()->parent(), &origin);
+ display_.ConvertFromScreenToVirtual(&origin);
+ return initial_bounds_.origin() - origin.OffsetFromOrigin();
+ }
gfx::Rect visible_bounds = GetVisibleBounds();
switch (resize_component_) {
@@ -1275,15 +1351,15 @@ void ShellSurface::UpdateWidgetBounds() {
return;
}
- // 2) When a window is being dragged.
- if (IsResizing())
+ // 2) When a window is being dragged, or is being moved by the shell.
+ if (IsResizing() || ignore_widget_bounds_changes_)
return;
// Return early if there is pending configure requests.
if (!pending_configs_.empty() || scoped_configure_)
return;
- gfx::Rect visible_bounds = GetVisibleBounds();
+ gfx::Rect visible_bounds = GetVisibleBounds(); // In virtual coordinates.
gfx::Rect new_widget_bounds = visible_bounds;
// Avoid changing widget origin unless initial bounds were specified and
@@ -1291,6 +1367,12 @@ void ShellSurface::UpdateWidgetBounds() {
if (initial_bounds_.IsEmpty())
new_widget_bounds.set_origin(widget_->GetWindowBoundsInScreen().origin());
+ else {
+ new_widget_bounds.set_origin(initial_bounds_.origin() +
+ visible_bounds.OffsetFromOrigin());
+ display_.ConvertFromVirtualToScreen(&new_widget_bounds);
+ }
+
// Update widget origin using the surface origin if the current location of
// surface is being anchored to one side of the widget as a result of a
// resize operation.
@@ -1305,11 +1387,29 @@ void ShellSurface::UpdateWidgetBounds() {
// should not result in a configure request.
DCHECK(!ignore_window_bounds_changes_);
ignore_window_bounds_changes_ = true;
- if (widget_->GetWindowBoundsInScreen() != new_widget_bounds)
- widget_->SetBounds(new_widget_bounds);
+ if (widget_->GetWindowBoundsInScreen() != new_widget_bounds) {
+ if (!move_resizer_) {
+ widget_->SetBounds(new_widget_bounds);
+ UpdateSurfaceBounds();
+ } else {
+ // Convert from screen to display coordinates.
+ gfx::Point origin = new_widget_bounds.origin();
+ wm::ConvertPointFromScreen(widget_->GetNativeWindow()->parent(), &origin);
+ new_widget_bounds.set_origin(origin);
+
+ // Move the window relative to the current display.
+ widget_->GetNativeWindow()->SetBounds(new_widget_bounds);
+ UpdateSurfaceBounds();
+
+ // Render phantom windows when beyond the current display.
+ move_resizer_->Drag(GetMouseLocation(), 0);
+ }
+ }
+
ignore_window_bounds_changes_ = false;
+}
- // A change to the widget size requires surface bounds to be re-adjusted.
+void ShellSurface::UpdateSurfaceBounds() {
surface_->window()->SetBounds(
gfx::Rect(GetSurfaceOrigin(), surface_->window()->layer()->size()));
}
@@ -1325,10 +1425,11 @@ void ShellSurface::UpdateShadow() {
} else {
wm::SetShadowType(window, wm::SHADOW_TYPE_RECTANGULAR);
- // TODO(oshima): Adjust the coordinates from client screen to
- // chromeos screen when multi displays are supported.
gfx::Point origin = window->bounds().origin();
gfx::Point shadow_origin = shadow_content_bounds_.origin();
+
+ display_.ConvertFromVirtualToScreen(&shadow_origin);
+ wm::ConvertPointFromScreen(window->parent(), &shadow_origin);
shadow_origin -= origin.OffsetFromOrigin();
gfx::Rect shadow_bounds(shadow_origin, shadow_content_bounds_.size());
@@ -1403,4 +1504,13 @@ void ShellSurface::UpdateShadow() {
}
}
+gfx::Point ShellSurface::GetMouseLocation() const {
+ aura::Window* const root_window = widget_->GetNativeWindow()->GetRootWindow();
+ gfx::Point location =
+ root_window->GetHost()->dispatcher()->GetLastMouseLocationInRoot();
+ aura::Window::ConvertPointToTarget(
+ root_window, widget_->GetNativeWindow()->parent(), &location);
+ return location;
+}
+
} // namespace exo

Powered by Google App Engine
This is Rietveld 408576698