1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/exo/shell_surface.h"
6
7 #include "ash/public/cpp/shell_window_ids.h"
8 #include "ash/public/cpp/window_state_type.h"
9 #include "ash/shell.h"
10 #include "ash/wm/desks/desks_util.h"
11 #include "ash/wm/toplevel_window_event_handler.h"
12 #include "ash/wm/window_resizer.h"
13 #include "ash/wm/window_state.h"
14 #include "base/bind.h"
15 #include "base/logging.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "components/exo/shell_surface_util.h"
18 #include "ui/aura/client/aura_constants.h"
19 #include "ui/aura/client/cursor_client.h"
20 #include "ui/aura/env.h"
21 #include "ui/aura/window.h"
22 #include "ui/aura/window_event_dispatcher.h"
23 #include "ui/aura/window_tree_host.h"
24 #include "ui/base/ui_base_types.h"
25 #include "ui/views/widget/widget.h"
26 #include "ui/wm/core/coordinate_conversion.h"
27 #include "ui/wm/core/transient_window_manager.h"
28 #include "ui/wm/core/window_util.h"
29
30 namespace exo {
31 namespace {
32
33 // Maximum amount of time to wait for contents after a change to maximize,
34 // fullscreen or pinned state.
35 constexpr int kMaximizedOrFullscreenOrPinnedLockTimeoutMs = 100;
36
37 } // namespace
38
39 ////////////////////////////////////////////////////////////////////////////////
40 // ShellSurface, ScopedAnimationsDisabled:
41
42 // Helper class used to temporarily disable animations. Restores the
43 // animations disabled property when instance is destroyed.
44 class ShellSurface::ScopedAnimationsDisabled {
45 public:
46 explicit ScopedAnimationsDisabled(ShellSurface* shell_surface);
47 ~ScopedAnimationsDisabled();
48
49 private:
50 ShellSurface* const shell_surface_;
51 bool saved_animations_disabled_ = false;
52
53 DISALLOW_COPY_AND_ASSIGN(ScopedAnimationsDisabled);
54 };
55
ScopedAnimationsDisabled(ShellSurface * shell_surface)56 ShellSurface::ScopedAnimationsDisabled::ScopedAnimationsDisabled(
57 ShellSurface* shell_surface)
58 : shell_surface_(shell_surface) {
59 if (shell_surface_->widget_) {
60 aura::Window* window = shell_surface_->widget_->GetNativeWindow();
61 saved_animations_disabled_ =
62 window->GetProperty(aura::client::kAnimationsDisabledKey);
63 window->SetProperty(aura::client::kAnimationsDisabledKey, true);
64 }
65 }
66
~ScopedAnimationsDisabled()67 ShellSurface::ScopedAnimationsDisabled::~ScopedAnimationsDisabled() {
68 if (shell_surface_->widget_) {
69 aura::Window* window = shell_surface_->widget_->GetNativeWindow();
70 DCHECK_EQ(window->GetProperty(aura::client::kAnimationsDisabledKey), true);
71 window->SetProperty(aura::client::kAnimationsDisabledKey,
72 saved_animations_disabled_);
73 }
74 }
75
76 ////////////////////////////////////////////////////////////////////////////////
77 // ShellSurface, Config:
78
79 // Surface state associated with each configure request.
80 struct ShellSurface::Config {
81 Config(uint32_t serial,
82 const gfx::Vector2d& origin_offset,
83 int resize_component,
84 std::unique_ptr<ui::CompositorLock> compositor_lock);
85 ~Config() = default;
86
87 uint32_t serial;
88 gfx::Vector2d origin_offset;
89 int resize_component;
90 std::unique_ptr<ui::CompositorLock> compositor_lock;
91 };
92
Config(uint32_t serial,const gfx::Vector2d & origin_offset,int resize_component,std::unique_ptr<ui::CompositorLock> compositor_lock)93 ShellSurface::Config::Config(
94 uint32_t serial,
95 const gfx::Vector2d& origin_offset,
96 int resize_component,
97 std::unique_ptr<ui::CompositorLock> compositor_lock)
98 : serial(serial),
99 origin_offset(origin_offset),
100 resize_component(resize_component),
101 compositor_lock(std::move(compositor_lock)) {}
102
103 ////////////////////////////////////////////////////////////////////////////////
104 // ShellSurface, ScopedConfigure:
105
ScopedConfigure(ShellSurface * shell_surface,bool force_configure)106 ShellSurface::ScopedConfigure::ScopedConfigure(ShellSurface* shell_surface,
107 bool force_configure)
108 : shell_surface_(shell_surface), force_configure_(force_configure) {
109 // ScopedConfigure instances cannot be nested.
110 DCHECK(!shell_surface_->scoped_configure_);
111 shell_surface_->scoped_configure_ = this;
112 }
113
~ScopedConfigure()114 ShellSurface::ScopedConfigure::~ScopedConfigure() {
115 DCHECK_EQ(shell_surface_->scoped_configure_, this);
116 shell_surface_->scoped_configure_ = nullptr;
117 if (needs_configure_ || force_configure_)
118 shell_surface_->Configure();
119 // ScopedConfigure instance might have suppressed a widget bounds update.
120 if (shell_surface_->widget_) {
121 shell_surface_->UpdateWidgetBounds();
122 shell_surface_->UpdateShadow();
123 }
124 }
125
126 ////////////////////////////////////////////////////////////////////////////////
127 // ShellSurface, public:
128
ShellSurface(Surface * surface,const gfx::Point & origin,bool activatable,bool can_minimize,int container)129 ShellSurface::ShellSurface(Surface* surface,
130 const gfx::Point& origin,
131 bool activatable,
132 bool can_minimize,
133 int container)
134 : ShellSurfaceBase(surface, origin, activatable, can_minimize, container) {}
135
ShellSurface(Surface * surface)136 ShellSurface::ShellSurface(Surface* surface)
137 : ShellSurfaceBase(surface,
138 gfx::Point(),
139 true,
140 true,
141 ash::desks_util::GetActiveDeskContainerId()) {}
142
~ShellSurface()143 ShellSurface::~ShellSurface() {
144 DCHECK(!scoped_configure_);
145 if (widget_)
146 ash::WindowState::Get(widget_->GetNativeWindow())->RemoveObserver(this);
147 }
148
AcknowledgeConfigure(uint32_t serial)149 void ShellSurface::AcknowledgeConfigure(uint32_t serial) {
150 TRACE_EVENT1("exo", "ShellSurface::AcknowledgeConfigure", "serial", serial);
151
152 // Apply all configs that are older or equal to |serial|. The result is that
153 // the origin of the main surface will move and the resize direction will
154 // change to reflect the acknowledgement of configure request with |serial|
155 // at the next call to Commit().
156 while (!pending_configs_.empty()) {
157 std::unique_ptr<Config> config = std::move(pending_configs_.front());
158 pending_configs_.pop_front();
159
160 // Add the config offset to the accumulated offset that will be applied when
161 // Commit() is called.
162 pending_origin_offset_ += config->origin_offset;
163
164 // Set the resize direction that will be applied when Commit() is called.
165 pending_resize_component_ = config->resize_component;
166
167 if (config->serial == serial)
168 break;
169 }
170
171 if (widget_) {
172 UpdateWidgetBounds();
173 UpdateShadow();
174 }
175 }
176
SetParent(ShellSurface * parent)177 void ShellSurface::SetParent(ShellSurface* parent) {
178 TRACE_EVENT1("exo", "ShellSurface::SetParent", "parent",
179 parent ? base::UTF16ToASCII(parent->title_) : "null");
180
181 SetParentWindow(parent ? parent->GetWidget()->GetNativeWindow() : nullptr);
182 }
183
Maximize()184 void ShellSurface::Maximize() {
185 TRACE_EVENT0("exo", "ShellSurface::Maximize");
186
187 if (!widget_) {
188 initial_show_state_ = ui::SHOW_STATE_MAXIMIZED;
189 return;
190 }
191
192 // Note: This will ask client to configure its surface even if already
193 // maximized.
194 ScopedConfigure scoped_configure(this, true);
195 widget_->Maximize();
196 }
197
Minimize()198 void ShellSurface::Minimize() {
199 TRACE_EVENT0("exo", "ShellSurface::Minimize");
200
201 if (!widget_) {
202 initial_show_state_ = ui::SHOW_STATE_MINIMIZED;
203 return;
204 }
205
206 // Note: This will ask client to configure its surface even if already
207 // minimized.
208 ScopedConfigure scoped_configure(this, true);
209 widget_->Minimize();
210 }
211
Restore()212 void ShellSurface::Restore() {
213 TRACE_EVENT0("exo", "ShellSurface::Restore");
214
215 if (!widget_) {
216 initial_show_state_ = ui::SHOW_STATE_NORMAL;
217 return;
218 }
219
220 // Note: This will ask client to configure its surface even if not already
221 // maximized or minimized.
222 ScopedConfigure scoped_configure(this, true);
223 widget_->Restore();
224 }
225
SetFullscreen(bool fullscreen)226 void ShellSurface::SetFullscreen(bool fullscreen) {
227 TRACE_EVENT1("exo", "ShellSurface::SetFullscreen", "fullscreen", fullscreen);
228
229 if (!widget_) {
230 initial_show_state_ = ui::SHOW_STATE_FULLSCREEN;
231 return;
232 }
233
234 // Note: This will ask client to configure its surface even if fullscreen
235 // state doesn't change.
236 ScopedConfigure scoped_configure(this, true);
237 widget_->SetFullscreen(fullscreen);
238 }
239
SetPopup()240 void ShellSurface::SetPopup() {
241 DCHECK(!widget_);
242 is_popup_ = true;
243 }
244
Grab()245 void ShellSurface::Grab() {
246 DCHECK(is_popup_);
247 DCHECK(!widget_);
248 has_grab_ = true;
249 }
250
StartMove()251 void ShellSurface::StartMove() {
252 TRACE_EVENT0("exo", "ShellSurface::StartMove");
253
254 if (!widget_)
255 return;
256
257 AttemptToStartDrag(HTCAPTION);
258 }
259
StartResize(int component)260 void ShellSurface::StartResize(int component) {
261 TRACE_EVENT1("exo", "ShellSurface::StartResize", "component", component);
262
263 if (!widget_)
264 return;
265
266 AttemptToStartDrag(component);
267 }
268
ShouldAutoMaximize()269 bool ShellSurface::ShouldAutoMaximize() {
270 // Unless a child class overrides the behaviour, we will never auto-maximize.
271 return false;
272 }
273
274 ////////////////////////////////////////////////////////////////////////////////
275 // SurfaceDelegate overrides:
276
OnSetParent(Surface * parent,const gfx::Point & position)277 void ShellSurface::OnSetParent(Surface* parent, const gfx::Point& position) {
278 views::Widget* parent_widget =
279 parent ? views::Widget::GetTopLevelWidgetForNativeView(parent->window())
280 : nullptr;
281 if (parent_widget) {
282 // Set parent window if using one of the desks container and the container
283 // itself is not the parent.
284 if (ash::desks_util::IsDeskContainerId(container_))
285 SetParentWindow(parent_widget->GetNativeWindow());
286
287 origin_ = position;
288 views::View::ConvertPointToScreen(
289 parent_widget->widget_delegate()->GetContentsView(), &origin_);
290
291 if (!widget_)
292 return;
293
294 ash::WindowState* window_state =
295 ash::WindowState::Get(widget_->GetNativeWindow());
296 if (window_state->is_dragged())
297 return;
298
299 gfx::Rect widget_bounds = widget_->GetWindowBoundsInScreen();
300 gfx::Rect new_widget_bounds(origin_, widget_bounds.size());
301 if (new_widget_bounds != widget_bounds) {
302 base::AutoReset<bool> auto_ignore_window_bounds_changes(
303 &ignore_window_bounds_changes_, true);
304 widget_->SetBounds(new_widget_bounds);
305 UpdateSurfaceBounds();
306 }
307 } else {
308 SetParentWindow(nullptr);
309 }
310 }
311
312 ////////////////////////////////////////////////////////////////////////////////
313 // ShellSurfaceBase overrides:
314
InitializeWindowState(ash::WindowState * window_state)315 void ShellSurface::InitializeWindowState(ash::WindowState* window_state) {
316 window_state->AddObserver(this);
317 window_state->set_allow_set_bounds_direct(movement_disabled_);
318 window_state->set_ignore_keyboard_bounds_change(movement_disabled_);
319 widget_->set_movement_disabled(movement_disabled_);
320
321 // If this window is a child of some window, it should be made transient.
322 MaybeMakeTransient();
323 }
324
GetWidgetBounds() const325 base::Optional<gfx::Rect> ShellSurface::GetWidgetBounds() const {
326 // Defer if configure requests are pending.
327 if (!pending_configs_.empty() || scoped_configure_)
328 return base::nullopt;
329
330 gfx::Rect visible_bounds = GetVisibleBounds();
331 gfx::Rect new_widget_bounds =
332 widget_->non_client_view()
333 ? widget_->non_client_view()->GetWindowBoundsForClientBounds(
334 visible_bounds)
335 : visible_bounds;
336
337 if (movement_disabled_) {
338 new_widget_bounds.set_origin(origin_);
339 } else if (resize_component_ == HTCAPTION) {
340 // Preserve widget position.
341 new_widget_bounds.set_origin(widget_->GetWindowBoundsInScreen().origin());
342 } else {
343 // Compute widget origin using surface origin if the current location of
344 // surface is being anchored to one side of the widget as a result of a
345 // resize operation.
346 gfx::Rect visible_bounds = GetVisibleBounds();
347 gfx::Point origin = GetSurfaceOrigin() + visible_bounds.OffsetFromOrigin();
348 wm::ConvertPointToScreen(widget_->GetNativeWindow(), &origin);
349 new_widget_bounds.set_origin(origin);
350 }
351 return new_widget_bounds;
352 }
353
GetSurfaceOrigin() const354 gfx::Point ShellSurface::GetSurfaceOrigin() const {
355 DCHECK(!movement_disabled_ || resize_component_ == HTCAPTION);
356
357 gfx::Rect visible_bounds = GetVisibleBounds();
358 gfx::Rect client_bounds = GetClientViewBounds();
359
360 switch (resize_component_) {
361 case HTCAPTION:
362 return gfx::Point() + origin_offset_ - visible_bounds.OffsetFromOrigin();
363 case HTBOTTOM:
364 case HTRIGHT:
365 case HTBOTTOMRIGHT:
366 return gfx::Point() - visible_bounds.OffsetFromOrigin();
367 case HTTOP:
368 case HTTOPRIGHT:
369 return gfx::Point(0, client_bounds.height() - visible_bounds.height()) -
370 visible_bounds.OffsetFromOrigin();
371 case HTLEFT:
372 case HTBOTTOMLEFT:
373 return gfx::Point(client_bounds.width() - visible_bounds.width(), 0) -
374 visible_bounds.OffsetFromOrigin();
375 case HTTOPLEFT:
376 return gfx::Point(client_bounds.width() - visible_bounds.width(),
377 client_bounds.height() - visible_bounds.height()) -
378 visible_bounds.OffsetFromOrigin();
379 default:
380 NOTREACHED();
381 return gfx::Point();
382 }
383 }
384
385 ////////////////////////////////////////////////////////////////////////////////
386 // aura::WindowObserver overrides:
387
OnWindowBoundsChanged(aura::Window * window,const gfx::Rect & old_bounds,const gfx::Rect & new_bounds,ui::PropertyChangeReason reason)388 void ShellSurface::OnWindowBoundsChanged(aura::Window* window,
389 const gfx::Rect& old_bounds,
390 const gfx::Rect& new_bounds,
391 ui::PropertyChangeReason reason) {
392 if (!widget_ || !root_surface() || ignore_window_bounds_changes_)
393 return;
394
395 if (window == widget_->GetNativeWindow()) {
396 if (new_bounds.size() == old_bounds.size())
397 return;
398
399 // If size changed then give the client a chance to produce new contents
400 // before origin on screen is changed. Retain the old origin by reverting
401 // the origin delta until the next configure is acknowledged.
402 gfx::Vector2d delta = new_bounds.origin() - old_bounds.origin();
403 origin_offset_ -= delta;
404 pending_origin_offset_accumulator_ += delta;
405
406 UpdateSurfaceBounds();
407
408 // The shadow size may be updated to match the widget. Change it back
409 // to the shadow content size. Note that this relies on wm::ShadowController
410 // being notified of the change before |this|.
411 UpdateShadow();
412
413 Configure();
414 }
415 }
416
417 ////////////////////////////////////////////////////////////////////////////////
418 // ash::WindowStateObserver overrides:
419
OnPreWindowStateTypeChange(ash::WindowState * window_state,ash::WindowStateType old_type)420 void ShellSurface::OnPreWindowStateTypeChange(ash::WindowState* window_state,
421 ash::WindowStateType old_type) {
422 ash::WindowStateType new_type = window_state->GetStateType();
423 if (ash::IsMinimizedWindowStateType(old_type) ||
424 ash::IsMinimizedWindowStateType(new_type)) {
425 return;
426 }
427
428 if (ash::IsMaximizedOrFullscreenOrPinnedWindowStateType(old_type) ||
429 ash::IsMaximizedOrFullscreenOrPinnedWindowStateType(new_type)) {
430 if (!widget_)
431 return;
432 // When transitioning in/out of maximized or fullscreen mode, we need to
433 // make sure we have a configure callback before we allow the default
434 // cross-fade animations. The configure callback provides a mechanism for
435 // the client to inform us that a frame has taken the state change into
436 // account, and without this cross-fade animations are unreliable.
437 if (!configure_callback_.is_null()) {
438 // Give client a chance to produce a frame that takes state change into
439 // account by acquiring a compositor lock.
440 ui::Compositor* compositor =
441 widget_->GetNativeWindow()->layer()->GetCompositor();
442 configure_compositor_lock_ = compositor->GetCompositorLock(
443 nullptr, base::TimeDelta::FromMilliseconds(
444 kMaximizedOrFullscreenOrPinnedLockTimeoutMs));
445 } else {
446 scoped_animations_disabled_ =
447 std::make_unique<ScopedAnimationsDisabled>(this);
448 }
449 }
450 }
451
OnPostWindowStateTypeChange(ash::WindowState * window_state,ash::WindowStateType old_type)452 void ShellSurface::OnPostWindowStateTypeChange(ash::WindowState* window_state,
453 ash::WindowStateType old_type) {
454 ash::WindowStateType new_type = window_state->GetStateType();
455 if (ash::IsMaximizedOrFullscreenOrPinnedWindowStateType(new_type)) {
456 Configure();
457 }
458
459 if (widget_) {
460 UpdateWidgetBounds();
461 UpdateShadow();
462 }
463
464 // Re-enable animations if they were disabled in pre state change handler.
465 scoped_animations_disabled_.reset();
466 }
467
468 ////////////////////////////////////////////////////////////////////////////////
469 // wm::ActivationChangeObserver overrides:
470
OnWindowActivated(ActivationReason reason,aura::Window * gained_active,aura::Window * lost_active)471 void ShellSurface::OnWindowActivated(ActivationReason reason,
472 aura::Window* gained_active,
473 aura::Window* lost_active) {
474 ShellSurfaceBase::OnWindowActivated(reason, gained_active, lost_active);
475
476 if (!widget_)
477 return;
478
479 if (gained_active == widget_->GetNativeWindow() ||
480 lost_active == widget_->GetNativeWindow()) {
481 Configure();
482 }
483 }
484
485 ////////////////////////////////////////////////////////////////////////////////
486 // ShellSurfaceBase overrides:
487
SetWidgetBounds(const gfx::Rect & bounds)488 void ShellSurface::SetWidgetBounds(const gfx::Rect& bounds) {
489 if (bounds == widget_->GetWindowBoundsInScreen())
490 return;
491
492 // Set |ignore_window_bounds_changes_| as this change to window bounds
493 // should not result in a configure request.
494 DCHECK(!ignore_window_bounds_changes_);
495 ignore_window_bounds_changes_ = true;
496
497 widget_->SetBounds(bounds);
498 UpdateSurfaceBounds();
499
500 ignore_window_bounds_changes_ = false;
501 }
502
OnPreWidgetCommit()503 bool ShellSurface::OnPreWidgetCommit() {
504 if (!widget_ && GetEnabled()) {
505 // Defer widget creation and commit until surface has contents.
506 if (host_window()->bounds().IsEmpty() &&
507 root_surface()->surface_hierarchy_content_bounds().IsEmpty()) {
508 Configure();
509 return false;
510 }
511
512 // Allow the window to maximize itself on launch.
513 if (ShouldAutoMaximize())
514 initial_show_state_ = ui::SHOW_STATE_MAXIMIZED;
515
516 CreateShellSurfaceWidget(initial_show_state_);
517 }
518
519 // Apply the accumulated pending origin offset to reflect acknowledged
520 // configure requests.
521 origin_offset_ += pending_origin_offset_;
522 pending_origin_offset_ = gfx::Vector2d();
523
524 // Update resize direction to reflect acknowledged configure requests.
525 resize_component_ = pending_resize_component_;
526
527 return true;
528 }
529
OnPostWidgetCommit()530 void ShellSurface::OnPostWidgetCommit() {}
531
532 ////////////////////////////////////////////////////////////////////////////////
533 // ShellSurface, private:
534
SetParentWindow(aura::Window * parent)535 void ShellSurface::SetParentWindow(aura::Window* parent) {
536 if (parent_) {
537 parent_->RemoveObserver(this);
538 if (widget_) {
539 aura::Window* child_window = widget_->GetNativeWindow();
540 wm::TransientWindowManager::GetOrCreate(child_window)
541 ->set_parent_controls_visibility(false);
542 wm::RemoveTransientChild(parent_, child_window);
543 }
544 }
545 parent_ = parent;
546 if (parent_) {
547 parent_->AddObserver(this);
548 MaybeMakeTransient();
549 }
550
551 // If |parent_| is set effects the ability to maximize the window.
552 if (widget_)
553 widget_->OnSizeConstraintsChanged();
554 }
555
MaybeMakeTransient()556 void ShellSurface::MaybeMakeTransient() {
557 if (!parent_ || !widget_)
558 return;
559 aura::Window* child_window = widget_->GetNativeWindow();
560 wm::AddTransientChild(parent_, child_window);
561 // In the case of activatable non-popups, we also want the parent to control
562 // the child's visibility.
563 if (!widget_->is_top_level() || !widget_->CanActivate())
564 return;
565 wm::TransientWindowManager::GetOrCreate(child_window)
566 ->set_parent_controls_visibility(true);
567 }
568
Configure(bool ends_drag)569 void ShellSurface::Configure(bool ends_drag) {
570 // Delay configure callback if |scoped_configure_| is set.
571 if (scoped_configure_) {
572 scoped_configure_->set_needs_configure();
573 return;
574 }
575
576 gfx::Vector2d origin_offset = pending_origin_offset_accumulator_;
577 pending_origin_offset_accumulator_ = gfx::Vector2d();
578
579 auto* window_state =
580 widget_ ? ash::WindowState::Get(widget_->GetNativeWindow()) : nullptr;
581 int resize_component = HTCAPTION;
582 // If surface is being resized, save the resize direction.
583 if (window_state && window_state->is_dragged() && !ends_drag)
584 resize_component = window_state->drag_details()->window_component;
585
586 uint32_t serial = 0;
587 if (!configure_callback_.is_null()) {
588 if (window_state) {
589 serial = configure_callback_.Run(
590 GetClientViewBounds().size(), window_state->GetStateType(),
591 IsResizing(), widget_->IsActive(), origin_offset);
592 } else {
593 serial =
594 configure_callback_.Run(gfx::Size(), ash::WindowStateType::kNormal,
595 false, false, origin_offset);
596 }
597 }
598
599 if (!serial) {
600 pending_origin_offset_ += origin_offset;
601 pending_resize_component_ = resize_component;
602 return;
603 }
604
605 // Apply origin offset and resize component at the first Commit() after this
606 // configure request has been acknowledged.
607 pending_configs_.push_back(
608 std::make_unique<Config>(serial, origin_offset, resize_component,
609 std::move(configure_compositor_lock_)));
610 LOG_IF(WARNING, pending_configs_.size() > 100)
611 << "Number of pending configure acks for shell surface has reached: "
612 << pending_configs_.size();
613 }
614
AttemptToStartDrag(int component)615 void ShellSurface::AttemptToStartDrag(int component) {
616 ash::WindowState* window_state =
617 ash::WindowState::Get(widget_->GetNativeWindow());
618
619 // Ignore if surface is already being dragged.
620 if (window_state->is_dragged())
621 return;
622
623 aura::Window* target = widget_->GetNativeWindow();
624 ash::ToplevelWindowEventHandler* toplevel_handler =
625 ash::Shell::Get()->toplevel_window_event_handler();
626 aura::Window* mouse_pressed_handler =
627 target->GetHost()->dispatcher()->mouse_pressed_handler();
628 // Start dragging only if:
629 // 1) touch guesture is in progress.
630 // 2) mouse was pressed on the target or its subsurfaces.
631 aura::Window* gesture_target = toplevel_handler->gesture_target();
632 if (!gesture_target && !mouse_pressed_handler &&
633 target->Contains(mouse_pressed_handler)) {
634 return;
635 }
636 auto end_drag = [](ShellSurface* shell_surface,
637 ash::ToplevelWindowEventHandler::DragResult result) {
638 shell_surface->EndDrag();
639 };
640
641 if (gesture_target) {
642 gfx::PointF location = toplevel_handler->event_location_in_gesture_target();
643 aura::Window::ConvertPointToTarget(
644 gesture_target, widget_->GetNativeWindow()->GetRootWindow(), &location);
645 toplevel_handler->AttemptToStartDrag(
646 target, location, component,
647 base::BindOnce(end_drag, base::Unretained(this)));
648 } else {
649 gfx::Point location = aura::Env::GetInstance()->last_mouse_location();
650 ::wm::ConvertPointFromScreen(widget_->GetNativeWindow()->GetRootWindow(),
651 &location);
652 toplevel_handler->AttemptToStartDrag(
653 target, gfx::PointF(location), component,
654 base::BindOnce(end_drag, base::Unretained(this)));
655 }
656 // Notify client that resizing state has changed.
657 if (IsResizing())
658 Configure();
659 }
660
EndDrag()661 void ShellSurface::EndDrag() {
662 if (resize_component_ != HTCAPTION) {
663 Configure(/*ends_drag=*/true);
664 }
665 }
666
667 } // namespace exo
668