client_controlled_state.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. // Copyright 2017 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. #include "ash/wm/client_controlled_state.h"
  5. #include "ash/public/cpp/shell_window_ids.h"
  6. #include "ash/public/cpp/window_animation_types.h"
  7. #include "ash/root_window_controller.h"
  8. #include "ash/screen_util.h"
  9. #include "ash/shell.h"
  10. #include "ash/wm/pip/pip_positioner.h"
  11. #include "ash/wm/screen_pinning_controller.h"
  12. #include "ash/wm/splitview/split_view_controller.h"
  13. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  14. #include "ash/wm/window_positioning_utils.h"
  15. #include "ash/wm/window_state.h"
  16. #include "ash/wm/window_state_delegate.h"
  17. #include "ash/wm/window_state_util.h"
  18. #include "ash/wm/wm_event.h"
  19. #include "chromeos/ui/base/window_state_type.h"
  20. #include "ui/aura/client/aura_constants.h"
  21. #include "ui/aura/window.h"
  22. #include "ui/aura/window_delegate.h"
  23. #include "ui/display/display.h"
  24. #include "ui/display/screen.h"
  25. #include "ui/wm/core/window_util.h"
  26. namespace ash {
  27. namespace {
  28. using ::chromeos::WindowStateType;
  29. // |kMinimumOnScreenArea + 1| is used to avoid adjusting loop.
  30. constexpr int kClientControlledWindowMinimumOnScreenArea =
  31. kMinimumOnScreenArea + 1;
  32. } // namespace
  33. // static
  34. void ClientControlledState::AdjustBoundsForMinimumWindowVisibility(
  35. const gfx::Rect& display_bounds,
  36. gfx::Rect* bounds) {
  37. AdjustBoundsToEnsureWindowVisibility(
  38. display_bounds, kClientControlledWindowMinimumOnScreenArea,
  39. kClientControlledWindowMinimumOnScreenArea, bounds);
  40. }
  41. ClientControlledState::ClientControlledState(std::unique_ptr<Delegate> delegate)
  42. : BaseState(WindowStateType::kDefault), delegate_(std::move(delegate)) {}
  43. ClientControlledState::~ClientControlledState() = default;
  44. void ClientControlledState::ResetDelegate() {
  45. delegate_.reset();
  46. }
  47. void ClientControlledState::HandleTransitionEvents(WindowState* window_state,
  48. const WMEvent* event) {
  49. if (!delegate_)
  50. return;
  51. const WMEventType event_type = event->type();
  52. bool pin_transition = window_state->IsTrustedPinned() ||
  53. window_state->IsPinned() || event->IsPinEvent();
  54. // Pinned State transition is handled on server side.
  55. if (pin_transition) {
  56. // Only one window can be pinned.
  57. if (event->IsPinEvent() &&
  58. Shell::Get()->screen_pinning_controller()->IsPinned()) {
  59. return;
  60. }
  61. WindowStateType next_state_type =
  62. GetStateForTransitionEvent(window_state, event);
  63. delegate_->HandleWindowStateRequest(window_state, next_state_type);
  64. WindowStateType old_state_type = state_type_;
  65. bool was_pinned = window_state->IsPinned();
  66. bool was_trusted_pinned = window_state->IsTrustedPinned();
  67. set_next_bounds_change_animation_type(
  68. WindowState::BoundsChangeAnimationType::kCrossFade);
  69. EnterNextState(window_state, next_state_type);
  70. VLOG(1) << "Processing Pinned Transition: event=" << event_type
  71. << ", state=" << old_state_type << "=>" << next_state_type
  72. << ", pinned=" << was_pinned << "=>" << window_state->IsPinned()
  73. << ", trusted pinned=" << was_trusted_pinned << "=>"
  74. << window_state->IsTrustedPinned();
  75. return;
  76. }
  77. switch (event_type) {
  78. case WM_EVENT_NORMAL:
  79. case WM_EVENT_MAXIMIZE:
  80. case WM_EVENT_MINIMIZE:
  81. case WM_EVENT_FULLSCREEN:
  82. case WM_EVENT_SNAP_PRIMARY:
  83. case WM_EVENT_SNAP_SECONDARY: {
  84. WindowStateType next_state =
  85. GetResolvedNextWindowStateType(window_state, event);
  86. UpdateWindowForTransitionEvents(window_state, next_state, event_type);
  87. break;
  88. }
  89. case WM_EVENT_FLOAT:
  90. // TODO(crbug.com/1346061): Implement this.
  91. break;
  92. case WM_EVENT_RESTORE:
  93. UpdateWindowForTransitionEvents(
  94. window_state, window_state->GetRestoreWindowState(), event_type);
  95. break;
  96. case WM_EVENT_SHOW_INACTIVE:
  97. NOTREACHED();
  98. break;
  99. default:
  100. NOTREACHED() << "Unknown event :" << event->type();
  101. }
  102. }
  103. void ClientControlledState::AttachState(
  104. WindowState* window_state,
  105. WindowState::State* state_in_previous_mode) {}
  106. void ClientControlledState::DetachState(WindowState* window_state) {}
  107. #if DCHECK_IS_ON()
  108. void ClientControlledState::CheckMaximizableCondition(
  109. const WindowState* window_state) const {
  110. // A client decides when the window should be maximizable.
  111. }
  112. #endif // DCHECK_IS_ON()
  113. void ClientControlledState::HandleWorkspaceEvents(WindowState* window_state,
  114. const WMEvent* event) {
  115. if (!delegate_)
  116. return;
  117. // Client is responsible for adjusting bounds after workspace bounds change.
  118. if (window_state->IsSnapped()) {
  119. gfx::Rect bounds = GetSnappedWindowBoundsInParent(
  120. window_state->window(), window_state->GetStateType());
  121. // Then ask delegate to set the desired bounds for the snap state.
  122. delegate_->HandleBoundsRequest(window_state, window_state->GetStateType(),
  123. bounds, window_state->GetDisplay().id());
  124. } else if (event->type() == WM_EVENT_DISPLAY_BOUNDS_CHANGED) {
  125. // Explicitly handle the primary change because it can change the display id
  126. // with no bounds change.
  127. if (event->AsDisplayMetricsChangedWMEvent()->primary_changed()) {
  128. const gfx::Rect bounds = window_state->window()->bounds();
  129. delegate_->HandleBoundsRequest(window_state, window_state->GetStateType(),
  130. bounds, window_state->GetDisplay().id());
  131. }
  132. } else if (event->type() == WM_EVENT_ADDED_TO_WORKSPACE) {
  133. aura::Window* window = window_state->window();
  134. gfx::Rect bounds = window->bounds();
  135. AdjustBoundsForMinimumWindowVisibility(window->GetRootWindow()->bounds(),
  136. &bounds);
  137. if (window->bounds() != bounds)
  138. window_state->SetBoundsConstrained(bounds);
  139. }
  140. }
  141. void ClientControlledState::HandleCompoundEvents(WindowState* window_state,
  142. const WMEvent* event) {
  143. if (!delegate_)
  144. return;
  145. switch (event->type()) {
  146. case WM_EVENT_TOGGLE_MAXIMIZE_CAPTION:
  147. if (window_state->IsFullscreen()) {
  148. const WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN);
  149. window_state->OnWMEvent(&event);
  150. } else if (window_state->IsMaximized()) {
  151. window_state->Restore();
  152. } else if (window_state->IsNormalOrSnapped()) {
  153. if (window_state->CanMaximize())
  154. window_state->Maximize();
  155. }
  156. break;
  157. case WM_EVENT_TOGGLE_MAXIMIZE:
  158. if (window_state->IsFullscreen()) {
  159. const WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN);
  160. window_state->OnWMEvent(&event);
  161. } else if (window_state->IsMaximized()) {
  162. window_state->Restore();
  163. } else if (window_state->CanMaximize()) {
  164. window_state->Maximize();
  165. }
  166. break;
  167. case WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE:
  168. // TODO(oshima): Implement this.
  169. break;
  170. case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
  171. // TODO(oshima): Implement this.
  172. break;
  173. case WM_EVENT_TOGGLE_FULLSCREEN:
  174. ToggleFullScreen(window_state, window_state->delegate());
  175. break;
  176. case WM_EVENT_CYCLE_SNAP_PRIMARY:
  177. case WM_EVENT_CYCLE_SNAP_SECONDARY:
  178. CycleSnap(window_state, event->type());
  179. break;
  180. default:
  181. NOTREACHED() << "Invalid event :" << event->type();
  182. break;
  183. }
  184. }
  185. void ClientControlledState::HandleBoundsEvents(WindowState* window_state,
  186. const WMEvent* event) {
  187. if (!delegate_)
  188. return;
  189. switch (event->type()) {
  190. case WM_EVENT_SET_BOUNDS: {
  191. const auto* set_bounds_event =
  192. static_cast<const SetBoundsWMEvent*>(event);
  193. const gfx::Rect& bounds = set_bounds_event->requested_bounds();
  194. if (set_bounds_locally_) {
  195. switch (next_bounds_change_animation_type_) {
  196. case WindowState::BoundsChangeAnimationType::kNone:
  197. window_state->SetBoundsDirect(bounds);
  198. break;
  199. case WindowState::BoundsChangeAnimationType::kCrossFade:
  200. window_state->SetBoundsDirectCrossFade(bounds);
  201. break;
  202. case WindowState::BoundsChangeAnimationType::kAnimate:
  203. window_state->SetBoundsDirectAnimated(
  204. bounds, bounds_change_animation_duration_);
  205. break;
  206. case WindowState::BoundsChangeAnimationType::kAnimateZero:
  207. NOTREACHED();
  208. break;
  209. }
  210. next_bounds_change_animation_type_ =
  211. WindowState::BoundsChangeAnimationType::kNone;
  212. } else if (!window_state->IsPinned()) {
  213. // TODO(oshima): Define behavior for pinned app.
  214. bounds_change_animation_duration_ = set_bounds_event->duration();
  215. int64_t display_id = set_bounds_event->display_id();
  216. auto* window = window_state->window();
  217. if (display_id == display::kInvalidDisplayId) {
  218. display_id = display::Screen::GetScreen()
  219. ->GetDisplayNearestWindow(window)
  220. .id();
  221. }
  222. #if DCHECK_IS_ON()
  223. gfx::Rect bounds_in_display(bounds);
  224. // The coordinates of the WindowState's parent must be same as display
  225. // coordinates. The following code is only to verify this condition.
  226. const aura::Window* root = window->GetRootWindow();
  227. aura::Window::ConvertRectToTarget(window->parent(), root,
  228. &bounds_in_display);
  229. DCHECK_EQ(bounds_in_display.x(), bounds.x());
  230. DCHECK_EQ(bounds_in_display.y(), bounds.y());
  231. #endif
  232. delegate_->HandleBoundsRequest(
  233. window_state, window_state->GetStateType(), bounds, display_id);
  234. }
  235. break;
  236. }
  237. case WM_EVENT_CENTER:
  238. CenterWindow(window_state);
  239. break;
  240. default:
  241. NOTREACHED() << "Unknown event:" << event->type();
  242. }
  243. }
  244. void ClientControlledState::OnWindowDestroying(WindowState* window_state) {
  245. ResetDelegate();
  246. }
  247. bool ClientControlledState::EnterNextState(WindowState* window_state,
  248. WindowStateType next_state_type) {
  249. // Do nothing if we're already in the same state, or delegate has already
  250. // been deleted.
  251. if (state_type_ == next_state_type || !delegate_)
  252. return false;
  253. WindowStateType previous_state_type = state_type_;
  254. state_type_ = next_state_type;
  255. window_state->UpdateWindowPropertiesFromStateType();
  256. window_state->NotifyPreStateTypeChange(previous_state_type);
  257. // Don't update the window if the window is detached from parent.
  258. // This can happen during dragging.
  259. // TODO(oshima): This was added for DOCKED windows. Investigate if
  260. // we still need this.
  261. if (window_state->window()->parent())
  262. UpdateMinimizedState(window_state, previous_state_type);
  263. window_state->NotifyPostStateTypeChange(previous_state_type);
  264. if (IsPinnedWindowStateType(next_state_type) ||
  265. IsPinnedWindowStateType(previous_state_type)) {
  266. Shell::Get()->screen_pinning_controller()->SetPinnedWindow(
  267. window_state->window());
  268. }
  269. return true;
  270. }
  271. WindowStateType ClientControlledState::GetResolvedNextWindowStateType(
  272. WindowState* window_state,
  273. const WMEvent* event) {
  274. DCHECK(event->IsTransitionEvent());
  275. const WindowStateType next = GetStateForTransitionEvent(window_state, event);
  276. if (Shell::Get()->tablet_mode_controller()->InTabletMode() &&
  277. next == WindowStateType::kNormal && window_state->CanMaximize())
  278. return WindowStateType::kMaximized;
  279. return next;
  280. }
  281. void ClientControlledState::UpdateWindowForTransitionEvents(
  282. WindowState* window_state,
  283. chromeos::WindowStateType next_state_type,
  284. WMEventType event_type) {
  285. aura::Window* window = window_state->window();
  286. if (next_state_type == WindowStateType::kPrimarySnapped ||
  287. next_state_type == WindowStateType::kSecondarySnapped) {
  288. if (window_state->CanSnap()) {
  289. HandleWindowSnapping(window_state,
  290. next_state_type == WindowStateType::kPrimarySnapped
  291. ? WM_EVENT_SNAP_PRIMARY
  292. : WM_EVENT_SNAP_SECONDARY);
  293. if (event_type == WM_EVENT_RESTORE) {
  294. window_state->set_snap_action_source(
  295. WindowSnapActionSource::kSnapByWindowStateRestore);
  296. }
  297. window_state->RecordAndResetWindowSnapActionSource(
  298. window_state->GetStateType(), next_state_type);
  299. // Get the desired window bounds for the snap state.
  300. gfx::Rect bounds =
  301. GetSnappedWindowBoundsInParent(window, next_state_type);
  302. // We don't want Unminimize() to restore the pre-snapped state during the
  303. // transition. See crbug.com/1031313 for why we need this.
  304. // kRestoreShowStateKey property will be updated properly after the window
  305. // is snapped correctly.
  306. if (window_state->IsMinimized())
  307. window->ClearProperty(aura::client::kRestoreShowStateKey);
  308. window_state->UpdateWindowPropertiesFromStateType();
  309. VLOG(1) << "Processing State Transtion: event=" << event_type
  310. << ", state=" << state_type_
  311. << ", next_state=" << next_state_type;
  312. // Then ask delegate to set the desired bounds for the snap state.
  313. delegate_->HandleBoundsRequest(window_state, next_state_type, bounds,
  314. window_state->GetDisplay().id());
  315. }
  316. } else {
  317. // Clients handle a window state change asynchronously. So in the case
  318. // that the window is in a transitional state (already snapped but not
  319. // applied to its window state yet), we here skip to pass WM_EVENT.
  320. if (SplitViewController::Get(window)->IsWindowInTransitionalState(window))
  321. return;
  322. // Reset window state.
  323. window_state->UpdateWindowPropertiesFromStateType();
  324. VLOG(1) << "Processing State Transtion: event=" << event_type
  325. << ", state=" << state_type_ << ", next_state=" << next_state_type;
  326. // Then ask delegate to handle the window state change.
  327. delegate_->HandleWindowStateRequest(window_state, next_state_type);
  328. }
  329. }
  330. } // namespace ash