chinsenj | 12a1104d | 2021-01-08 00:30:28 | [diff] [blame] | 1 | // Copyright 2018 The Chromium Authors. All rights reserved. |
Ahmed Fakhry | ec348379 | 2019-03-04 18:09:27 | [diff] [blame] | 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 "ash/wm/desks/desk.h" |
| 6 | |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 7 | #include <algorithm> |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 8 | #include <utility> |
| 9 | |
Ahmed Fakhry | 38e118a | 2019-08-28 19:55:40 | [diff] [blame] | 10 | #include "ash/public/cpp/app_types.h" |
Cattalyya Nuengsigkapian | 8c45e30 | 2021-01-06 21:48:08 | [diff] [blame] | 11 | #include "ash/public/cpp/ash_features.h" |
Ahmed Fakhry | baac2b31 | 2020-02-19 21:21:55 | [diff] [blame] | 12 | #include "ash/public/cpp/shell_window_ids.h" |
Ahmed Fakhry | 9df7b43 | 2019-05-15 17:02:24 | [diff] [blame] | 13 | #include "ash/public/cpp/window_properties.h" |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 14 | #include "ash/shell.h" |
Cattalyya Nuengsigkapian | 8c45e30 | 2021-01-06 21:48:08 | [diff] [blame] | 15 | #include "ash/wm/desks/desks_controller.h" |
Ahmed Fakhry | fc2ca39 | 2021-01-26 17:59:25 | [diff] [blame] | 16 | #include "ash/wm/desks/desks_util.h" |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 17 | #include "ash/wm/mru_window_tracker.h" |
Ahmed | 3892c17 | 2020-03-24 17:54:36 | [diff] [blame] | 18 | #include "ash/wm/overview/overview_controller.h" |
Ahmed Fakhry | 6d599092 | 2019-07-10 01:16:50 | [diff] [blame] | 19 | #include "ash/wm/window_positioner.h" |
Ahmed Fakhry | 00f77847 | 2019-05-08 00:50:09 | [diff] [blame] | 20 | #include "ash/wm/window_state.h" |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 21 | #include "ash/wm/window_transient_descendant_iterator.h" |
| 22 | #include "ash/wm/window_util.h" |
Ahmed Fakhry | 756d77a | 2019-05-31 23:21:23 | [diff] [blame] | 23 | #include "ash/wm/workspace/backdrop_controller.h" |
| 24 | #include "ash/wm/workspace/workspace_layout_manager.h" |
| 25 | #include "ash/wm/workspace_controller.h" |
Ahmed Fakhry | 3d28cb4 | 2020-02-27 21:37:04 | [diff] [blame] | 26 | #include "base/containers/adapters.h" |
Jan Wilken Dörrie | b5a41c3 | 2020-12-09 18:55:47 | [diff] [blame] | 27 | #include "base/containers/contains.h" |
chinsenj | 932efe34 | 2021-03-06 06:17:14 | [diff] [blame] | 28 | #include "base/macros.h" |
chinsenj | 7936a65 | 2021-03-04 18:26:26 | [diff] [blame] | 29 | #include "base/metrics/histogram_functions.h" |
chinsenj | 932efe34 | 2021-03-06 06:17:14 | [diff] [blame] | 30 | #include "base/no_destructor.h" |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 31 | #include "base/stl_util.h" |
Cattalyya Nuengsigkapian | 8c45e30 | 2021-01-06 21:48:08 | [diff] [blame] | 32 | #include "chromeos/ui/base/window_properties.h" |
Ahmed Fakhry | 38e118a | 2019-08-28 19:55:40 | [diff] [blame] | 33 | #include "ui/aura/client/aura_constants.h" |
Ahmed Fakhry | 3d28cb4 | 2020-02-27 21:37:04 | [diff] [blame] | 34 | #include "ui/aura/window_tracker.h" |
Ahmed Fakhry | d984db51 | 2020-03-13 22:02:24 | [diff] [blame] | 35 | #include "ui/display/screen.h" |
Ahmed Fakhry | c336898 | 2019-05-30 23:00:44 | [diff] [blame] | 36 | #include "ui/wm/core/window_util.h" |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 37 | |
Ahmed Fakhry | ec348379 | 2019-03-04 18:09:27 | [diff] [blame] | 38 | namespace ash { |
| 39 | |
Ahmed Fakhry | 756d77a | 2019-05-31 23:21:23 | [diff] [blame] | 40 | namespace { |
| 41 | |
chinsenj | 932efe34 | 2021-03-06 06:17:14 | [diff] [blame] | 42 | // The name of the histogram for consecutive daily visits. |
| 43 | constexpr char kConsecutiveDailyVisitsHistogramName[] = |
| 44 | "Ash.Desks.ConsecutiveDailyVisits"; |
| 45 | |
chinsenj | 7936a65 | 2021-03-04 18:26:26 | [diff] [blame] | 46 | // Prefix for the desks lifetime histogram. |
| 47 | constexpr char kDeskLifetimeHistogramNamePrefix[] = "Ash.Desks.DeskLifetime_"; |
| 48 | |
Ahmed Fakhry | 756d77a | 2019-05-31 23:21:23 | [diff] [blame] | 49 | void UpdateBackdropController(aura::Window* desk_container) { |
| 50 | auto* workspace_controller = GetWorkspaceController(desk_container); |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 51 | // Work might have already been cleared when the display is removed. See |
| 52 | // |RootWindowController::MoveWindowsTo()|. |
| 53 | if (!workspace_controller) |
| 54 | return; |
| 55 | |
Ahmed Fakhry | 756d77a | 2019-05-31 23:21:23 | [diff] [blame] | 56 | WorkspaceLayoutManager* layout_manager = |
| 57 | workspace_controller->layout_manager(); |
| 58 | BackdropController* backdrop_controller = |
| 59 | layout_manager->backdrop_controller(); |
| 60 | backdrop_controller->OnDeskContentChanged(); |
| 61 | } |
| 62 | |
| 63 | // Returns true if |window| can be managed by the desk, and therefore can be |
| 64 | // moved out of the desk when the desk is removed. |
| 65 | bool CanMoveWindowOutOfDeskContainer(aura::Window* window) { |
Ahmed Fakhry | baac2b31 | 2020-02-19 21:21:55 | [diff] [blame] | 66 | // The desks bar widget is an activatable window placed in the active desk's |
| 67 | // container, therefore it should be allowed to move outside of its desk when |
| 68 | // its desk is removed. |
| 69 | if (window->id() == kShellWindowId_DesksBarWindow) |
| 70 | return true; |
| 71 | |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 72 | // We never move transient descendants directly, this is taken care of by |
| 73 | // `wm::TransientWindowManager::OnWindowHierarchyChanged()`. |
| 74 | auto* transient_root = ::wm::GetTransientRoot(window); |
| 75 | if (transient_root != window) |
| 76 | return false; |
| 77 | |
Ahmed Fakhry | 38e118a | 2019-08-28 19:55:40 | [diff] [blame] | 78 | // Only allow app windows to move to other desks. |
| 79 | return window->GetProperty(aura::client::kAppType) != |
| 80 | static_cast<int>(AppType::NON_APP); |
Ahmed Fakhry | 756d77a | 2019-05-31 23:21:23 | [diff] [blame] | 81 | } |
| 82 | |
Ahmed Fakhry | fc2ca39 | 2021-01-26 17:59:25 | [diff] [blame] | 83 | // Adjusts the z-order stacking of |window_to_fix| in its parent to match its |
| 84 | // order in the MRU window list. This is done after the window is moved from one |
| 85 | // desk container to another by means of calling AddChild() which adds it as the |
| 86 | // top-most window, which doesn't necessarily match the MRU order. |
| 87 | // |window_to_fix| must be a child of a desk container, and the root of a |
| 88 | // transient hierarchy (if it belongs to one). |
| 89 | // This function must be called AddChild() was called to add the |window_to_fix| |
| 90 | // (i.e. |window_to_fix| is the top-most window or the top-most window is a |
| 91 | // transient child of |window_to_fix|). |
| 92 | void FixWindowStackingAccordingToGlobalMru(aura::Window* window_to_fix) { |
| 93 | aura::Window* container = window_to_fix->parent(); |
| 94 | DCHECK(desks_util::IsDeskContainer(container)); |
| 95 | DCHECK_EQ(window_to_fix, wm::GetTransientRoot(window_to_fix)); |
| 96 | DCHECK(window_to_fix == container->children().back() || |
| 97 | window_to_fix == wm::GetTransientRoot(container->children().back())); |
| 98 | |
| 99 | const auto mru_windows = |
| 100 | Shell::Get()->mru_window_tracker()->BuildWindowListIgnoreModal( |
| 101 | DesksMruType::kAllDesks); |
| 102 | // Find the closest sibling that is not a transient descendant, which |
| 103 | // |window_to_fix| should be stacked below. |
| 104 | aura::Window* closest_sibling_above_window = nullptr; |
| 105 | for (auto* window : mru_windows) { |
| 106 | if (window == window_to_fix) { |
| 107 | if (closest_sibling_above_window) |
| 108 | container->StackChildBelow(window_to_fix, closest_sibling_above_window); |
| 109 | return; |
| 110 | } |
| 111 | |
| 112 | if (window->parent() == container && |
| 113 | !wm::HasTransientAncestor(window, window_to_fix)) { |
| 114 | closest_sibling_above_window = window; |
| 115 | } |
| 116 | } |
| 117 | } |
| 118 | |
chinsenj | 932efe34 | 2021-03-06 06:17:14 | [diff] [blame] | 119 | // Returns Jan 1, 2010 00:00:00 as a base::Time object in the local timezone. |
| 120 | base::Time GetLocalEpoch() { |
| 121 | static base::NoDestructor<base::Time> local_epoch; |
| 122 | if (local_epoch->is_null()) { |
| 123 | ignore_result(base::Time::FromLocalExploded({2010, 1, 5, 1, 0, 0, 0, 0}, |
| 124 | local_epoch.get())); |
| 125 | } |
| 126 | return *local_epoch; |
| 127 | } |
| 128 | |
Ahmed Fakhry | 6d599092 | 2019-07-10 01:16:50 | [diff] [blame] | 129 | // Used to temporarily turn off the automatic window positioning while windows |
| 130 | // are being moved between desks. |
| 131 | class ScopedWindowPositionerDisabler { |
| 132 | public: |
| 133 | ScopedWindowPositionerDisabler() { |
| 134 | WindowPositioner::DisableAutoPositioning(true); |
| 135 | } |
| 136 | |
| 137 | ~ScopedWindowPositionerDisabler() { |
| 138 | WindowPositioner::DisableAutoPositioning(false); |
| 139 | } |
| 140 | |
| 141 | private: |
| 142 | DISALLOW_COPY_AND_ASSIGN(ScopedWindowPositionerDisabler); |
| 143 | }; |
| 144 | |
Ahmed Fakhry | 756d77a | 2019-05-31 23:21:23 | [diff] [blame] | 145 | } // namespace |
| 146 | |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 147 | class DeskContainerObserver : public aura::WindowObserver { |
| 148 | public: |
| 149 | DeskContainerObserver(Desk* owner, aura::Window* container) |
| 150 | : owner_(owner), container_(container) { |
| 151 | DCHECK_EQ(container_->id(), owner_->container_id()); |
| 152 | container->AddObserver(this); |
| 153 | } |
| 154 | |
| 155 | ~DeskContainerObserver() override { container_->RemoveObserver(this); } |
| 156 | |
| 157 | // aura::WindowObserver: |
| 158 | void OnWindowAdded(aura::Window* new_window) override { |
| 159 | // TODO(afakhry): Overview mode creates a new widget for each window under |
Sammie Quon | 64596be3 | 2019-10-28 21:53:00 | [diff] [blame] | 160 | // the same parent for the OverviewItemView. We will be notified with |
Ahmed Fakhry | ce8bf1d82 | 2019-04-30 22:06:24 | [diff] [blame] | 161 | // this window addition here. Consider ignoring these windows if they cause |
| 162 | // problems. |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 163 | owner_->AddWindowToDesk(new_window); |
chinsenj | 560df07 | 2021-02-16 21:16:36 | [diff] [blame] | 164 | MaybeNotifyAllDesksOfContentChange(new_window); |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 165 | } |
| 166 | |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 167 | void OnWindowRemoved(aura::Window* removed_window) override { |
| 168 | // We listen to `OnWindowRemoved()` as opposed to `OnWillRemoveWindow()` |
| 169 | // since we want to refresh the mini_views only after the window has been |
| 170 | // removed from the window tree hierarchy. |
| 171 | owner_->RemoveWindowFromDesk(removed_window); |
chinsenj | 560df07 | 2021-02-16 21:16:36 | [diff] [blame] | 172 | MaybeNotifyAllDesksOfContentChange(removed_window); |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 173 | } |
| 174 | |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 175 | void OnWindowDestroyed(aura::Window* window) override { |
| 176 | // We should never get here. We should be notified in |
| 177 | // `OnRootWindowClosing()` before the child containers of the root window |
| 178 | // are destroyed, and this object should have already been destroyed. |
| 179 | NOTREACHED(); |
| 180 | } |
| 181 | |
| 182 | private: |
chinsenj | 560df07 | 2021-02-16 21:16:36 | [diff] [blame] | 183 | void MaybeNotifyAllDesksOfContentChange(aura::Window* window) { |
| 184 | // If a visible on all desks window is added/removed from a desk, only the |
| 185 | // desks directly involved will know about their contents changing since it |
| 186 | // only resides on the active desk. Since visible on all desks windows |
| 187 | // appear in each desks' preview view, we need to notify each desk. |
| 188 | auto* desks_controller = DesksController::Get(); |
| 189 | if (desks_controller->visible_on_all_desks_windows().contains(window)) |
| 190 | desks_controller->NotifyAllDesksForContentChanged(); |
| 191 | } |
| 192 | |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 193 | Desk* const owner_; |
| 194 | aura::Window* const container_; |
| 195 | |
| 196 | DISALLOW_COPY_AND_ASSIGN(DeskContainerObserver); |
| 197 | }; |
| 198 | |
| 199 | // ----------------------------------------------------------------------------- |
| 200 | // Desk: |
| 201 | |
| 202 | Desk::Desk(int associated_container_id) |
chinsenj | 7936a65 | 2021-03-04 18:26:26 | [diff] [blame] | 203 | : container_id_(associated_container_id), |
| 204 | creation_time_(base::Time::Now()) { |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 205 | // For the very first default desk added during initialization, there won't be |
| 206 | // any root windows yet. That's OK, OnRootWindowAdded() will be called |
| 207 | // explicitly by the RootWindowController when they're initialized. |
| 208 | for (aura::Window* root : Shell::GetAllRootWindows()) |
| 209 | OnRootWindowAdded(root); |
| 210 | } |
| 211 | |
| 212 | Desk::~Desk() { |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 213 | #if DCHECK_IS_ON() |
Ahmed Fakhry | 756d77a | 2019-05-31 23:21:23 | [diff] [blame] | 214 | for (auto* window : windows_) { |
| 215 | DCHECK(!CanMoveWindowOutOfDeskContainer(window)) |
| 216 | << "DesksController should remove this desk's application windows " |
| 217 | "first."; |
Ahmed Fakhry | 756d77a | 2019-05-31 23:21:23 | [diff] [blame] | 218 | } |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 219 | #endif |
Ahmed Fakhry | 9df7b43 | 2019-05-15 17:02:24 | [diff] [blame] | 220 | |
| 221 | for (auto& observer : observers_) { |
| 222 | observers_.RemoveObserver(&observer); |
| 223 | observer.OnDeskDestroyed(this); |
| 224 | } |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 225 | } |
| 226 | |
Ahmed Fakhry | 927eabc | 2019-05-11 00:14:15 | [diff] [blame] | 227 | void Desk::AddObserver(Observer* observer) { |
| 228 | observers_.AddObserver(observer); |
| 229 | } |
| 230 | |
| 231 | void Desk::RemoveObserver(Observer* observer) { |
| 232 | observers_.RemoveObserver(observer); |
| 233 | } |
| 234 | |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 235 | void Desk::OnRootWindowAdded(aura::Window* root) { |
| 236 | DCHECK(!roots_to_containers_observers_.count(root)); |
| 237 | |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 238 | // No windows should be added to the desk container on |root| prior to |
| 239 | // tracking it by the desk. |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 240 | aura::Window* desk_container = root->GetChildById(container_id_); |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 241 | DCHECK(desk_container->children().empty()); |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 242 | auto container_observer = |
| 243 | std::make_unique<DeskContainerObserver>(this, desk_container); |
| 244 | roots_to_containers_observers_.emplace(root, std::move(container_observer)); |
| 245 | } |
| 246 | |
| 247 | void Desk::OnRootWindowClosing(aura::Window* root) { |
| 248 | const size_t count = roots_to_containers_observers_.erase(root); |
| 249 | DCHECK(count); |
Ahmed Fakhry | a0f9755b | 2019-07-24 23:06:54 | [diff] [blame] | 250 | |
| 251 | // The windows on this root are about to be destroyed. We already stopped |
| 252 | // observing the container above, so we won't get a call to |
| 253 | // DeskContainerObserver::OnWindowRemoved(). Therefore, we must remove those |
Ahmed Fakhry | 57835ef | 2019-07-25 18:01:16 | [diff] [blame] | 254 | // windows manually. If this is part of shutdown (i.e. when the |
| 255 | // RootWindowController is being destroyed), then we're done with those |
| 256 | // windows. If this is due to a display being removed, then the |
| 257 | // WindowTreeHostManager will move those windows to another host/root, and |
| 258 | // they will be added again to the desk container on the new root. |
| 259 | const auto windows = windows_; |
| 260 | for (auto* window : windows) { |
| 261 | if (window->GetRootWindow() == root) |
| 262 | base::Erase(windows_, window); |
| 263 | } |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 264 | } |
| 265 | |
| 266 | void Desk::AddWindowToDesk(aura::Window* window) { |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 267 | DCHECK(!base::Contains(windows_, window)); |
| 268 | windows_.push_back(window); |
| 269 | // No need to refresh the mini_views if the destroyed window doesn't show up |
chinsenj | 560df07 | 2021-02-16 21:16:36 | [diff] [blame] | 270 | // there in the first place. Also don't refresh for visible on all desks |
| 271 | // windows since they're already refreshed in OnWindowAdded(). |
| 272 | if (!window->GetProperty(kHideInDeskMiniViewKey) && |
| 273 | !window->GetProperty(aura::client::kVisibleOnAllWorkspacesKey)) { |
Ahmed | 3892c17 | 2020-03-24 17:54:36 | [diff] [blame] | 274 | NotifyContentChanged(); |
chinsenj | 560df07 | 2021-02-16 21:16:36 | [diff] [blame] | 275 | } |
Cattalyya Nuengsigkapian | 8c45e30 | 2021-01-06 21:48:08 | [diff] [blame] | 276 | |
| 277 | // Update the window's workspace to this parent desk. |
Sammie Quon | b0a7ed55 | 2021-02-11 02:40:58 | [diff] [blame] | 278 | if ((features::IsBentoEnabled() || features::IsFullRestoreEnabled()) && |
| 279 | !is_desk_being_removed_) { |
Cattalyya Nuengsigkapian | 8c45e30 | 2021-01-06 21:48:08 | [diff] [blame] | 280 | auto* desks_controller = DesksController::Get(); |
| 281 | window->SetProperty(aura::client::kWindowWorkspaceKey, |
| 282 | desks_controller->GetDeskIndex(this)); |
| 283 | } |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 284 | } |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 285 | |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 286 | void Desk::RemoveWindowFromDesk(aura::Window* window) { |
| 287 | DCHECK(base::Contains(windows_, window)); |
| 288 | base::Erase(windows_, window); |
| 289 | // No need to refresh the mini_views if the destroyed window doesn't show up |
chinsenj | 560df07 | 2021-02-16 21:16:36 | [diff] [blame] | 290 | // there in the first place. Also don't refresh for visible on all desks |
| 291 | // windows since they're already refreshed in OnWindowRemoved(). |
| 292 | if (!window->GetProperty(kHideInDeskMiniViewKey) && |
| 293 | !window->GetProperty(aura::client::kVisibleOnAllWorkspacesKey)) { |
Ahmed | 3892c17 | 2020-03-24 17:54:36 | [diff] [blame] | 294 | NotifyContentChanged(); |
chinsenj | 560df07 | 2021-02-16 21:16:36 | [diff] [blame] | 295 | } |
Ahmed Fakhry | 9df7b43 | 2019-05-15 17:02:24 | [diff] [blame] | 296 | } |
| 297 | |
Ahmed Fakhry | 3571b4f5 | 2019-05-15 20:55:49 | [diff] [blame] | 298 | base::AutoReset<bool> Desk::GetScopedNotifyContentChangedDisabler() { |
| 299 | return base::AutoReset<bool>(&should_notify_content_changed_, false); |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 300 | } |
| 301 | |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame^] | 302 | void Desk::SetName(std::u16string new_name, bool set_by_user) { |
Ahmed Fakhry | 5d041ef | 2020-02-20 21:39:05 | [diff] [blame] | 303 | // Even if the user focuses the DeskNameView for the first time and hits enter |
| 304 | // without changing the desk's name (i.e. |new_name| is the same, |
| 305 | // |is_name_set_by_user_| is false, and |set_by_user| is true), we don't |
| 306 | // change |is_name_set_by_user_| and keep considering the name as a default |
| 307 | // name. |
| 308 | if (name_ == new_name) |
| 309 | return; |
| 310 | |
Ahmed Fakhry | a1c9db7 | 2020-01-29 03:14:53 | [diff] [blame] | 311 | name_ = std::move(new_name); |
Ahmed Fakhry | 5d041ef | 2020-02-20 21:39:05 | [diff] [blame] | 312 | is_name_set_by_user_ = set_by_user; |
Ahmed Fakhry | a1c9db7 | 2020-01-29 03:14:53 | [diff] [blame] | 313 | |
| 314 | for (auto& observer : observers_) |
| 315 | observer.OnDeskNameChanged(name_); |
| 316 | } |
| 317 | |
Ahmed Fakhry | 2bc1268 | 2020-02-08 00:20:11 | [diff] [blame] | 318 | void Desk::PrepareForActivationAnimation() { |
| 319 | DCHECK(!is_active_); |
| 320 | |
| 321 | for (aura::Window* root : Shell::GetAllRootWindows()) { |
| 322 | auto* container = root->GetChildById(container_id_); |
| 323 | container->layer()->SetOpacity(0); |
| 324 | container->Show(); |
| 325 | } |
| 326 | started_activation_animation_ = true; |
| 327 | } |
| 328 | |
Ahmed Fakhry | ce8bf1d82 | 2019-04-30 22:06:24 | [diff] [blame] | 329 | void Desk::Activate(bool update_window_activation) { |
Ahmed Fakhry | 2bc1268 | 2020-02-08 00:20:11 | [diff] [blame] | 330 | DCHECK(!is_active_); |
| 331 | |
| 332 | if (!MaybeResetContainersOpacities()) { |
| 333 | for (aura::Window* root : Shell::GetAllRootWindows()) |
| 334 | root->GetChildById(container_id_)->Show(); |
| 335 | } |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 336 | |
Ahmed Fakhry | ce8bf1d82 | 2019-04-30 22:06:24 | [diff] [blame] | 337 | is_active_ = true; |
| 338 | |
chinsenj | 932efe34 | 2021-03-06 06:17:14 | [diff] [blame] | 339 | if (!IsConsecutiveDailyVisit()) |
| 340 | RecordAndResetConsecutiveDailyVisits(/*being_removed=*/false); |
| 341 | |
| 342 | int current_date = GetDaysFromLocalEpoch(); |
| 343 | if (current_date < last_day_visited_ || first_day_visited_ == -1) { |
| 344 | // If |current_date| < |last_day_visited_| then the user has moved timezones |
| 345 | // or the stored data has been corrupted so reset |first_day_visited_|. |
| 346 | first_day_visited_ = current_date; |
| 347 | } |
| 348 | last_day_visited_ = current_date; |
| 349 | |
Ahmed Fakhry | ce8bf1d82 | 2019-04-30 22:06:24 | [diff] [blame] | 350 | if (!update_window_activation || windows_.empty()) |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 351 | return; |
| 352 | |
| 353 | // Activate the window on this desk that was most recently used right before |
| 354 | // the user switched to another desk, so as not to break the user's workflow. |
| 355 | for (auto* window : |
Ahmed Fakhry | c336898 | 2019-05-30 23:00:44 | [diff] [blame] | 356 | Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk)) { |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 357 | if (!base::Contains(windows_, window)) |
Ahmed Fakhry | 00f77847 | 2019-05-08 00:50:09 | [diff] [blame] | 358 | continue; |
| 359 | |
| 360 | // Do not activate minimized windows, otherwise they will unminimize. |
James Cook | 00e65e9 | 2019-07-25 03:19:08 | [diff] [blame] | 361 | if (WindowState::Get(window)->IsMinimized()) |
Ahmed Fakhry | 00f77847 | 2019-05-08 00:50:09 | [diff] [blame] | 362 | continue; |
| 363 | |
| 364 | wm::ActivateWindow(window); |
| 365 | return; |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 366 | } |
| 367 | } |
| 368 | |
Ahmed Fakhry | ce8bf1d82 | 2019-04-30 22:06:24 | [diff] [blame] | 369 | void Desk::Deactivate(bool update_window_activation) { |
Ahmed Fakhry | 2bc1268 | 2020-02-08 00:20:11 | [diff] [blame] | 370 | DCHECK(is_active_); |
| 371 | |
James Cook | 00e65e9 | 2019-07-25 03:19:08 | [diff] [blame] | 372 | auto* active_window = window_util::GetActiveWindow(); |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 373 | |
| 374 | // Hide the associated containers on all roots. |
| 375 | for (aura::Window* root : Shell::GetAllRootWindows()) |
| 376 | root->GetChildById(container_id_)->Hide(); |
| 377 | |
Ahmed Fakhry | ce8bf1d82 | 2019-04-30 22:06:24 | [diff] [blame] | 378 | is_active_ = false; |
chinsenj | 932efe34 | 2021-03-06 06:17:14 | [diff] [blame] | 379 | last_day_visited_ = GetDaysFromLocalEpoch(); |
Ahmed Fakhry | ce8bf1d82 | 2019-04-30 22:06:24 | [diff] [blame] | 380 | |
| 381 | if (!update_window_activation) |
| 382 | return; |
| 383 | |
| 384 | // Deactivate the active window (if it belongs to this desk; active window may |
| 385 | // be on a different container, or one of the widgets created by overview mode |
| 386 | // which are not considered desk windows) after this desk's associated |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 387 | // containers have been hidden. This is to prevent the focus controller from |
| 388 | // activating another window on the same desk when the active window loses |
| 389 | // focus. |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 390 | if (active_window && base::Contains(windows_, active_window)) |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 391 | wm::DeactivateWindow(active_window); |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 392 | } |
| 393 | |
| 394 | void Desk::MoveWindowsToDesk(Desk* target_desk) { |
| 395 | DCHECK(target_desk); |
| 396 | |
Ahmed Fakhry | 927eabc | 2019-05-11 00:14:15 | [diff] [blame] | 397 | { |
Ahmed Fakhry | 6d599092 | 2019-07-10 01:16:50 | [diff] [blame] | 398 | ScopedWindowPositionerDisabler window_positioner_disabler; |
| 399 | |
Ahmed Fakhry | 927eabc | 2019-05-11 00:14:15 | [diff] [blame] | 400 | // Throttle notifying the observers, while we move those windows and notify |
| 401 | // them only once when done. |
Ahmed Fakhry | bab46dd | 2019-05-23 00:27:34 | [diff] [blame] | 402 | auto this_desk_throttled = GetScopedNotifyContentChangedDisabler(); |
| 403 | auto target_desk_throttled = |
| 404 | target_desk->GetScopedNotifyContentChangedDisabler(); |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 405 | |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 406 | // Moving windows will change the hierarchy and hence |windows_|, and has to |
| 407 | // be done without changing the relative z-order. So we make a copy of all |
Ahmed Fakhry | 3d28cb4 | 2020-02-27 21:37:04 | [diff] [blame] | 408 | // the top-level windows on all the containers of this desk, such that |
| 409 | // windows in each container are copied from top-most (z-order) to |
| 410 | // bottom-most. |
| 411 | // Note that moving windows out of the container and restacking them |
| 412 | // differently may trigger events that lead to destroying a window on the |
| 413 | // list. For example moving the top-most window which has a backdrop will |
| 414 | // cause the backdrop to be destroyed. Therefore observe such events using |
| 415 | // an |aura::WindowTracker|. |
| 416 | aura::WindowTracker windows_to_move; |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 417 | for (aura::Window* root : Shell::GetAllRootWindows()) { |
| 418 | const aura::Window* container = GetDeskContainerForRoot(root); |
Ahmed Fakhry | 3d28cb4 | 2020-02-27 21:37:04 | [diff] [blame] | 419 | for (auto* window : base::Reversed(container->children())) |
| 420 | windows_to_move.Add(window); |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 421 | } |
Ahmed Fakhry | 927eabc | 2019-05-11 00:14:15 | [diff] [blame] | 422 | |
Ahmed Fakhry | 3d28cb4 | 2020-02-27 21:37:04 | [diff] [blame] | 423 | auto* mru_tracker = Shell::Get()->mru_window_tracker(); |
| 424 | while (!windows_to_move.windows().empty()) { |
| 425 | auto* window = windows_to_move.Pop(); |
| 426 | if (!CanMoveWindowOutOfDeskContainer(window)) |
| 427 | continue; |
| 428 | |
| 429 | // Note that windows that belong to the same container in |
| 430 | // |windows_to_move| are sorted from top-most to bottom-most, hence |
| 431 | // calling |StackChildAtBottom()| on each in this order will maintain that |
| 432 | // same order in the |target_desk|'s container. |
Ahmed Fakhry | d984db51 | 2020-03-13 22:02:24 | [diff] [blame] | 433 | MoveWindowToDeskInternal(window, target_desk, window->GetRootWindow()); |
Ahmed Fakhry | 3d28cb4 | 2020-02-27 21:37:04 | [diff] [blame] | 434 | window->parent()->StackChildAtBottom(window); |
| 435 | mru_tracker->OnWindowMovedOutFromRemovingDesk(window); |
Ahmed Fakhry | 756d77a | 2019-05-31 23:21:23 | [diff] [blame] | 436 | } |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 437 | } |
| 438 | |
Ahmed | 3892c17 | 2020-03-24 17:54:36 | [diff] [blame] | 439 | NotifyContentChanged(); |
| 440 | target_desk->NotifyContentChanged(); |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 441 | } |
| 442 | |
Ahmed Fakhry | d984db51 | 2020-03-13 22:02:24 | [diff] [blame] | 443 | void Desk::MoveWindowToDesk(aura::Window* window, |
| 444 | Desk* target_desk, |
| 445 | aura::Window* target_root) { |
Ahmed Fakhry | bab46dd | 2019-05-23 00:27:34 | [diff] [blame] | 446 | DCHECK(window); |
Ahmed Fakhry | d984db51 | 2020-03-13 22:02:24 | [diff] [blame] | 447 | DCHECK(target_desk); |
| 448 | DCHECK(target_root); |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 449 | DCHECK(base::Contains(windows_, window)); |
Ahmed Fakhry | bab46dd | 2019-05-23 00:27:34 | [diff] [blame] | 450 | DCHECK(this != target_desk); |
Ahmed Fakhry | baac2b31 | 2020-02-19 21:21:55 | [diff] [blame] | 451 | // The desks bar should not be allowed to move individually to another desk. |
| 452 | // Only as part of `MoveWindowsToDesk()` when the desk is removed. |
| 453 | DCHECK_NE(window->id(), kShellWindowId_DesksBarWindow); |
Ahmed Fakhry | bab46dd | 2019-05-23 00:27:34 | [diff] [blame] | 454 | |
| 455 | { |
Ahmed Fakhry | 6d599092 | 2019-07-10 01:16:50 | [diff] [blame] | 456 | ScopedWindowPositionerDisabler window_positioner_disabler; |
| 457 | |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 458 | // Throttling here is necessary even though we're attempting to move a |
| 459 | // single window. This is because that window might exist in a transient |
| 460 | // window tree, which will result in actually moving multiple windows if the |
| 461 | // transient children used to be on the same container. |
| 462 | // See `wm::TransientWindowManager::OnWindowHierarchyChanged()`. |
Ahmed Fakhry | bab46dd | 2019-05-23 00:27:34 | [diff] [blame] | 463 | auto this_desk_throttled = GetScopedNotifyContentChangedDisabler(); |
| 464 | auto target_desk_throttled = |
| 465 | target_desk->GetScopedNotifyContentChangedDisabler(); |
| 466 | |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 467 | // Always move the root of the transient window tree. We should never move a |
| 468 | // transient child and leave its parent behind. Moving the transient |
| 469 | // descendants that exist on the same desk container will be taken care of |
| 470 | // by `wm::TransientWindowManager::OnWindowHierarchyChanged()`. |
| 471 | aura::Window* transient_root = ::wm::GetTransientRoot(window); |
Ahmed Fakhry | d984db51 | 2020-03-13 22:02:24 | [diff] [blame] | 472 | MoveWindowToDeskInternal(transient_root, target_desk, target_root); |
Ahmed Fakhry | fc2ca39 | 2021-01-26 17:59:25 | [diff] [blame] | 473 | FixWindowStackingAccordingToGlobalMru(transient_root); |
Ahmed Fakhry | e61d2af | 2019-06-06 01:29:15 | [diff] [blame] | 474 | |
| 475 | // Unminimize the window so that it shows up in the mini_view after it had |
chinsenj | 12a1104d | 2021-01-08 00:30:28 | [diff] [blame] | 476 | // been dragged and moved to another desk. Don't unminimize if the window is |
| 477 | // visible on all desks since it's being moved during desk activation. |
James Cook | 00e65e9 | 2019-07-25 03:19:08 | [diff] [blame] | 478 | auto* window_state = WindowState::Get(transient_root); |
chinsenj | 12a1104d | 2021-01-08 00:30:28 | [diff] [blame] | 479 | if (window_state->IsMinimized() && |
| 480 | !window->GetProperty(aura::client::kVisibleOnAllWorkspacesKey)) { |
Ahmed Fakhry | e61d2af | 2019-06-06 01:29:15 | [diff] [blame] | 481 | window_state->Unminimize(); |
chinsenj | 12a1104d | 2021-01-08 00:30:28 | [diff] [blame] | 482 | } |
Ahmed Fakhry | bab46dd | 2019-05-23 00:27:34 | [diff] [blame] | 483 | } |
| 484 | |
Ahmed | 3892c17 | 2020-03-24 17:54:36 | [diff] [blame] | 485 | NotifyContentChanged(); |
| 486 | target_desk->NotifyContentChanged(); |
Ahmed Fakhry | bab46dd | 2019-05-23 00:27:34 | [diff] [blame] | 487 | } |
| 488 | |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 489 | aura::Window* Desk::GetDeskContainerForRoot(aura::Window* root) const { |
| 490 | DCHECK(root); |
| 491 | |
| 492 | return root->GetChildById(container_id_); |
| 493 | } |
| 494 | |
Ahmed | 3892c17 | 2020-03-24 17:54:36 | [diff] [blame] | 495 | void Desk::NotifyContentChanged() { |
Ahmed Fakhry | 9df7b43 | 2019-05-15 17:02:24 | [diff] [blame] | 496 | if (!should_notify_content_changed_) |
Ahmed Fakhry | 927eabc | 2019-05-11 00:14:15 | [diff] [blame] | 497 | return; |
| 498 | |
Ahmed | 3892c17 | 2020-03-24 17:54:36 | [diff] [blame] | 499 | // Updating the backdrops below may lead to the removal or creation of |
| 500 | // backdrop windows in this desk, which can cause us to recurse back here. |
| 501 | // Disable this. |
| 502 | auto disable_recursion = GetScopedNotifyContentChangedDisabler(); |
| 503 | |
| 504 | // The availability and visibility of backdrops of all containers associated |
| 505 | // with this desk will be updated *before* notifying observer, so that the |
| 506 | // mini_views update *after* the backdrops do. |
| 507 | // This is *only* needed if the WorkspaceLayoutManager won't take care of this |
| 508 | // for us while overview is active. |
| 509 | if (Shell::Get()->overview_controller()->InOverviewSession()) |
Ahmed Fakhry | ed257cf | 2020-01-10 03:25:03 | [diff] [blame] | 510 | UpdateDeskBackdrops(); |
Ahmed Fakhry | 756d77a | 2019-05-31 23:21:23 | [diff] [blame] | 511 | |
Ahmed Fakhry | 927eabc | 2019-05-11 00:14:15 | [diff] [blame] | 512 | for (auto& observer : observers_) |
Ahmed Fakhry | 9df7b43 | 2019-05-15 17:02:24 | [diff] [blame] | 513 | observer.OnContentChanged(); |
Ahmed Fakhry | eb7375b2 | 2019-04-24 22:47:36 | [diff] [blame] | 514 | } |
Ahmed Fakhry | ec348379 | 2019-03-04 18:09:27 | [diff] [blame] | 515 | |
Ahmed Fakhry | 756d77a | 2019-05-31 23:21:23 | [diff] [blame] | 516 | void Desk::UpdateDeskBackdrops() { |
| 517 | for (auto* root : Shell::GetAllRootWindows()) |
| 518 | UpdateBackdropController(GetDeskContainerForRoot(root)); |
| 519 | } |
| 520 | |
Cattalyya Nuengsigkapian | 8c45e30 | 2021-01-06 21:48:08 | [diff] [blame] | 521 | void Desk::SetDeskBeingRemoved() { |
| 522 | is_desk_being_removed_ = true; |
| 523 | } |
| 524 | |
chinsenj | 7936a65 | 2021-03-04 18:26:26 | [diff] [blame] | 525 | void Desk::RecordLifetimeHistogram() { |
| 526 | // Desk index is 1-indexed in histograms. |
| 527 | const int desk_index = |
| 528 | Shell::Get()->desks_controller()->GetDeskIndex(this) + 1; |
| 529 | base::UmaHistogramCounts1000( |
| 530 | base::StringPrintf("%s%i", kDeskLifetimeHistogramNamePrefix, desk_index), |
| 531 | (base::Time::Now() - creation_time_).InHours()); |
| 532 | } |
| 533 | |
chinsenj | 932efe34 | 2021-03-06 06:17:14 | [diff] [blame] | 534 | bool Desk::IsConsecutiveDailyVisit() const { |
| 535 | if (last_day_visited_ == -1) |
| 536 | return true; |
| 537 | |
| 538 | const int days_since_last_visit = GetDaysFromLocalEpoch() - last_day_visited_; |
| 539 | return days_since_last_visit <= 1; |
| 540 | } |
| 541 | |
| 542 | void Desk::RecordAndResetConsecutiveDailyVisits(bool being_removed) { |
| 543 | if (being_removed && is_active_) { |
| 544 | // When the user removes the active desk, update |last_day_visited_| to the |
| 545 | // current day to account for the time they spent on this desk. |
| 546 | last_day_visited_ = GetDaysFromLocalEpoch(); |
| 547 | } |
| 548 | |
| 549 | const int consecutive_daily_visits = |
| 550 | last_day_visited_ - first_day_visited_ + 1; |
| 551 | DCHECK_GE(consecutive_daily_visits, 1); |
| 552 | base::UmaHistogramCounts1000(kConsecutiveDailyVisitsHistogramName, |
| 553 | consecutive_daily_visits); |
| 554 | |
| 555 | last_day_visited_ = -1; |
| 556 | first_day_visited_ = -1; |
| 557 | } |
| 558 | |
| 559 | int Desk::GetDaysFromLocalEpoch() const { |
| 560 | base::Time now = override_clock_ ? override_clock_->Now() : base::Time::Now(); |
| 561 | return (now - GetLocalEpoch()).InDays(); |
| 562 | } |
| 563 | |
| 564 | void Desk::OverrideClockForTesting(base::Clock* test_clock) { |
| 565 | DCHECK(!override_clock_); |
| 566 | override_clock_ = test_clock; |
| 567 | } |
| 568 | |
| 569 | void Desk::ResetVisitedMetricsForTesting() { |
| 570 | const int current_date = GetDaysFromLocalEpoch(); |
| 571 | first_day_visited_ = current_date; |
| 572 | last_day_visited_ = current_date; |
| 573 | } |
| 574 | |
Ahmed Fakhry | d984db51 | 2020-03-13 22:02:24 | [diff] [blame] | 575 | void Desk::MoveWindowToDeskInternal(aura::Window* window, |
| 576 | Desk* target_desk, |
| 577 | aura::Window* target_root) { |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 578 | DCHECK(base::Contains(windows_, window)); |
Ahmed Fakhry | 756d77a | 2019-05-31 23:21:23 | [diff] [blame] | 579 | DCHECK(CanMoveWindowOutOfDeskContainer(window)) |
| 580 | << "Non-desk windows are not allowed to move out of the container."; |
| 581 | |
Ahmed Fakhry | d984db51 | 2020-03-13 22:02:24 | [diff] [blame] | 582 | // When |target_root| is different than the current window's |root|, this can |
| 583 | // only happen when dragging and dropping a window on mini desk view on |
| 584 | // another display. Therefore |target_desk| is an inactive desk (i.e. |
| 585 | // invisible). The order doesn't really matter, but we move the window to the |
| 586 | // target desk's container first (so that it becomes hidden), then move it to |
| 587 | // the target display (while it's hidden). |
Ahmed Fakhry | bab46dd | 2019-05-23 00:27:34 | [diff] [blame] | 588 | aura::Window* root = window->GetRootWindow(); |
| 589 | aura::Window* source_container = GetDeskContainerForRoot(root); |
| 590 | aura::Window* target_container = target_desk->GetDeskContainerForRoot(root); |
Ahmed Fakhry | 7664e91 | 2019-06-19 16:56:26 | [diff] [blame] | 591 | DCHECK(window->parent() == source_container); |
| 592 | target_container->AddChild(window); |
Ahmed Fakhry | d984db51 | 2020-03-13 22:02:24 | [diff] [blame] | 593 | |
| 594 | if (root != target_root) { |
| 595 | // Move the window to the container with the same ID on the target display's |
| 596 | // root (i.e. container that belongs to the same desk), and adjust its |
| 597 | // bounds to fit in the new display's work area. |
| 598 | window_util::MoveWindowToDisplay(window, |
| 599 | display::Screen::GetScreen() |
| 600 | ->GetDisplayNearestWindow(target_root) |
| 601 | .id()); |
| 602 | DCHECK_EQ(target_desk->container_id_, window->parent()->id()); |
| 603 | } |
Ahmed Fakhry | bab46dd | 2019-05-23 00:27:34 | [diff] [blame] | 604 | } |
| 605 | |
Ahmed Fakhry | 2bc1268 | 2020-02-08 00:20:11 | [diff] [blame] | 606 | bool Desk::MaybeResetContainersOpacities() { |
| 607 | if (!started_activation_animation_) |
| 608 | return false; |
| 609 | |
| 610 | for (aura::Window* root : Shell::GetAllRootWindows()) { |
| 611 | auto* container = root->GetChildById(container_id_); |
| 612 | container->layer()->SetOpacity(1); |
| 613 | } |
| 614 | started_activation_animation_ = false; |
| 615 | return true; |
| 616 | } |
| 617 | |
Ahmed Fakhry | ec348379 | 2019-03-04 18:09:27 | [diff] [blame] | 618 | } // namespace ash |