overview_grid.cc 100 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545
  1. // Copyright 2014 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/overview/overview_grid.h"
  5. #include <algorithm>
  6. #include <functional>
  7. #include <memory>
  8. #include <utility>
  9. #include "ash/constants/ash_features.h"
  10. #include "ash/constants/notifier_catalogs.h"
  11. #include "ash/metrics/histogram_macros.h"
  12. #include "ash/public/cpp/desks_templates_delegate.h"
  13. #include "ash/public/cpp/metrics_util.h"
  14. #include "ash/public/cpp/shelf_config.h"
  15. #include "ash/public/cpp/shelf_types.h"
  16. #include "ash/public/cpp/window_properties.h"
  17. #include "ash/root_window_controller.h"
  18. #include "ash/root_window_settings.h"
  19. #include "ash/rotator/screen_rotation_animator.h"
  20. #include "ash/screen_util.h"
  21. #include "ash/shell.h"
  22. #include "ash/strings/grit/ash_strings.h"
  23. #include "ash/system/toast/toast_manager_impl.h"
  24. #include "ash/wallpaper/wallpaper_controller_impl.h"
  25. #include "ash/wm/desks/desk_mini_view.h"
  26. #include "ash/wm/desks/desk_name_view.h"
  27. #include "ash/wm/desks/desk_preview_view.h"
  28. #include "ash/wm/desks/desks_bar_view.h"
  29. #include "ash/wm/desks/desks_controller.h"
  30. #include "ash/wm/desks/desks_util.h"
  31. #include "ash/wm/desks/expanded_desks_bar_button.h"
  32. #include "ash/wm/desks/templates/save_desk_template_button.h"
  33. #include "ash/wm/desks/templates/save_desk_template_button_container.h"
  34. #include "ash/wm/desks/templates/saved_desk_animations.h"
  35. #include "ash/wm/desks/templates/saved_desk_grid_view.h"
  36. #include "ash/wm/desks/templates/saved_desk_library_view.h"
  37. #include "ash/wm/desks/templates/saved_desk_name_view.h"
  38. #include "ash/wm/desks/templates/saved_desk_presenter.h"
  39. #include "ash/wm/desks/templates/saved_desk_util.h"
  40. #include "ash/wm/desks/zero_state_button.h"
  41. #include "ash/wm/mru_window_tracker.h"
  42. #include "ash/wm/overview/cleanup_animation_observer.h"
  43. #include "ash/wm/overview/drop_target_view.h"
  44. #include "ash/wm/overview/overview_constants.h"
  45. #include "ash/wm/overview/overview_controller.h"
  46. #include "ash/wm/overview/overview_delegate.h"
  47. #include "ash/wm/overview/overview_grid_event_handler.h"
  48. #include "ash/wm/overview/overview_highlight_controller.h"
  49. #include "ash/wm/overview/overview_item.h"
  50. #include "ash/wm/overview/overview_item_view.h"
  51. #include "ash/wm/overview/overview_session.h"
  52. #include "ash/wm/overview/overview_utils.h"
  53. #include "ash/wm/overview/overview_window_drag_controller.h"
  54. #include "ash/wm/overview/scoped_overview_animation_settings.h"
  55. #include "ash/wm/splitview/split_view_constants.h"
  56. #include "ash/wm/splitview/split_view_divider.h"
  57. #include "ash/wm/splitview/split_view_utils.h"
  58. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  59. #include "ash/wm/tablet_mode/tablet_mode_window_state.h"
  60. #include "ash/wm/window_util.h"
  61. #include "ash/wm/workspace/backdrop_controller.h"
  62. #include "ash/wm/workspace/workspace_layout_manager.h"
  63. #include "ash/wm/workspace_controller.h"
  64. #include "base/bind.h"
  65. #include "base/containers/unique_ptr_adapters.h"
  66. #include "base/cxx17_backports.h"
  67. #include "base/numerics/safe_conversions.h"
  68. #include "base/strings/utf_string_conversions.h"
  69. #include "chromeos/ui/base/window_properties.h"
  70. #include "components/app_restore/full_restore_utils.h"
  71. #include "ui/aura/client/aura_constants.h"
  72. #include "ui/base/l10n/l10n_util.h"
  73. #include "ui/compositor/compositor_observer.h"
  74. #include "ui/compositor/layer.h"
  75. #include "ui/compositor/layer_animation_observer.h"
  76. #include "ui/compositor/presentation_time_recorder.h"
  77. #include "ui/compositor/throughput_tracker.h"
  78. #include "ui/gfx/geometry/transform_util.h"
  79. #include "ui/gfx/geometry/vector2d_f.h"
  80. #include "ui/gfx/paint_vector_icon.h"
  81. #include "ui/views/controls/button/label_button.h"
  82. #include "ui/views/view.h"
  83. #include "ui/views/widget/widget.h"
  84. #include "ui/wm/core/coordinate_conversion.h"
  85. #include "ui/wm/core/window_animations.h"
  86. namespace ash {
  87. namespace {
  88. // Values for the no items indicator which appears when opening overview mode
  89. // with no opened windows.
  90. constexpr int kNoItemsIndicatorHeightDp = 32;
  91. constexpr int kNoItemsIndicatorHorizontalPaddingDp = 16;
  92. constexpr int kNoItemsIndicatorRoundingDp = 16;
  93. constexpr int kNoItemsIndicatorVerticalPaddingDp = 8;
  94. // Distance from the bottom of the SaveDeskAsTemplate button to the top of the
  95. // first overview item.
  96. constexpr int kSaveDeskAsTemplateOverviewItemSpacingDp = 40;
  97. // Windows are not allowed to get taller than this.
  98. constexpr int kMaxHeight = 512;
  99. // Margins reserved in the overview mode.
  100. constexpr float kOverviewInsetRatio = 0.05f;
  101. // Additional vertical inset reserved for windows in overview mode.
  102. constexpr float kOverviewVerticalInset = 0.1f;
  103. // Number of rows for windows in tablet overview mode.
  104. constexpr int kTabletLayoutRow = 2;
  105. constexpr int kMinimumItemsForNewLayout = 6;
  106. constexpr int kTabletModeOverviewItemTopPaddingDp = 16;
  107. // The threshold for expanding desks bar while dragging the window. When the
  108. // length between the center point of the window being dragged and the center
  109. // point of the `zero_state_new_desk_button_` is smaller than
  110. // `kExpandDesksBarThreshold`, desks bar will be transformed from zero state to
  111. // expanded state to help user dropping the dragged window on the new desk.
  112. constexpr int kExpandDesksBarThreshold = 130;
  113. // Wait a while before unpausing the occlusion tracker after a scroll has
  114. // completed as the user may start another scroll.
  115. constexpr base::TimeDelta kOcclusionUnpauseDurationForScroll =
  116. base::Milliseconds(500);
  117. constexpr base::TimeDelta kOcclusionUnpauseDurationForRotation =
  118. base::Milliseconds(300);
  119. // Toast id for the toast that is displayed when a user tries to move a window
  120. // that is visible on all desks to another desk.
  121. constexpr char kMoveVisibleOnAllDesksWindowToastId[] =
  122. "ash.wm.overview.move_visible_on_all_desks_window_toast";
  123. // Histogram names for overview enter/exit smoothness in clamshell,
  124. // tablet mode and splitview.
  125. constexpr char kOverviewEnterClamshellHistogram[] =
  126. "Ash.Overview.AnimationSmoothness.Enter.ClamshellMode";
  127. constexpr char kOverviewEnterSingleClamshellHistogram[] =
  128. "Ash.Overview.AnimationSmoothness.Enter.SingleClamshellMode";
  129. constexpr char kOverviewEnterTabletHistogram[] =
  130. "Ash.Overview.AnimationSmoothness.Enter.TabletMode";
  131. constexpr char kOverviewEnterMinimizedTabletHistogram[] =
  132. "Ash.Overview.AnimationSmoothness.Enter.MinimizedTabletMode";
  133. constexpr char kOverviewEnterSplitViewHistogram[] =
  134. "Ash.Overview.AnimationSmoothness.Enter.SplitView";
  135. constexpr char kOverviewExitClamshellHistogram[] =
  136. "Ash.Overview.AnimationSmoothness.Exit.ClamshellMode";
  137. constexpr char kOverviewExitSingleClamshellHistogram[] =
  138. "Ash.Overview.AnimationSmoothness.Exit.SingleClamshellMode";
  139. constexpr char kOverviewExitTabletHistogram[] =
  140. "Ash.Overview.AnimationSmoothness.Exit.TabletMode";
  141. constexpr char kOverviewExitMinimizedTabletHistogram[] =
  142. "Ash.Overview.AnimationSmoothness.Exit.MinimizedTabletMode";
  143. constexpr char kOverviewExitSplitViewHistogram[] =
  144. "Ash.Overview.AnimationSmoothness.Exit.SplitView";
  145. // The UMA histogram that records presentation time for grid scrolling in the
  146. // new overview layout.
  147. constexpr char kOverviewScrollHistogram[] =
  148. "Ash.Overview.Scroll.PresentationTime.TabletMode";
  149. constexpr char kOverviewScrollMaxLatencyHistogram[] =
  150. "Ash.Overview.Scroll.PresentationTime.MaxLatency.TabletMode";
  151. template <const char* clamshell_single_name,
  152. const char* clamshell_multi_name,
  153. const char* tablet_name,
  154. const char* splitview_name,
  155. const char* tablet_minimized_name>
  156. class OverviewMetricsTracker : public OverviewGrid::MetricsTracker {
  157. public:
  158. OverviewMetricsTracker(ui::Compositor* compositor,
  159. bool in_split_view,
  160. bool single_animation_in_clamshell,
  161. bool minimized_in_tablet)
  162. : tracker_(compositor->RequestNewThroughputTracker()) {
  163. tracker_.Start(metrics_util::ForSmoothness(base::BindRepeating(
  164. &OverviewMetricsTracker::ReportOverviewSmoothness, in_split_view,
  165. single_animation_in_clamshell, minimized_in_tablet)));
  166. }
  167. OverviewMetricsTracker(const OverviewMetricsTracker&) = delete;
  168. OverviewMetricsTracker& operator=(const OverviewMetricsTracker&) = delete;
  169. ~OverviewMetricsTracker() override { tracker_.Stop(); }
  170. static void ReportOverviewSmoothness(bool in_split_view,
  171. bool single_animation_in_clamshell,
  172. bool minimized_in_tablet,
  173. int smoothness) {
  174. if (single_animation_in_clamshell)
  175. UMA_HISTOGRAM_PERCENTAGE_IN_CLAMSHELL(clamshell_single_name, smoothness);
  176. else
  177. UMA_HISTOGRAM_PERCENTAGE_IN_CLAMSHELL(clamshell_multi_name, smoothness);
  178. if (minimized_in_tablet) {
  179. UMA_HISTOGRAM_PERCENTAGE_IN_TABLET_NON_SPLITVIEW(
  180. in_split_view, tablet_minimized_name, smoothness);
  181. } else {
  182. UMA_HISTOGRAM_PERCENTAGE_IN_TABLET_NON_SPLITVIEW(in_split_view,
  183. tablet_name, smoothness);
  184. }
  185. UMA_HISTOGRAM_PERCENTAGE_IN_SPLITVIEW(in_split_view, splitview_name,
  186. smoothness);
  187. }
  188. private:
  189. ui::ThroughputTracker tracker_;
  190. };
  191. using OverviewEnterMetricsTracker =
  192. OverviewMetricsTracker<kOverviewEnterSingleClamshellHistogram,
  193. kOverviewEnterClamshellHistogram,
  194. kOverviewEnterTabletHistogram,
  195. kOverviewEnterSplitViewHistogram,
  196. kOverviewEnterMinimizedTabletHistogram>;
  197. using OverviewExitMetricsTracker =
  198. OverviewMetricsTracker<kOverviewExitSingleClamshellHistogram,
  199. kOverviewExitClamshellHistogram,
  200. kOverviewExitTabletHistogram,
  201. kOverviewExitSplitViewHistogram,
  202. kOverviewExitMinimizedTabletHistogram>;
  203. class ShutdownAnimationMetricsTrackerObserver : public OverviewObserver,
  204. public ui::CompositorObserver {
  205. public:
  206. ShutdownAnimationMetricsTrackerObserver(ui::Compositor* compositor,
  207. bool in_split_view,
  208. bool single_animation,
  209. bool minimized_in_tablet)
  210. : compositor_(compositor),
  211. metrics_tracker_(compositor,
  212. in_split_view,
  213. single_animation,
  214. minimized_in_tablet) {
  215. compositor->AddObserver(this);
  216. Shell::Get()->overview_controller()->AddObserver(this);
  217. }
  218. ShutdownAnimationMetricsTrackerObserver(
  219. const ShutdownAnimationMetricsTrackerObserver&) = delete;
  220. ShutdownAnimationMetricsTrackerObserver& operator=(
  221. const ShutdownAnimationMetricsTrackerObserver&) = delete;
  222. ~ShutdownAnimationMetricsTrackerObserver() override {
  223. compositor_->RemoveObserver(this);
  224. if (Shell::Get()->overview_controller())
  225. Shell::Get()->overview_controller()->RemoveObserver(this);
  226. }
  227. // OverviewObserver:
  228. void OnOverviewModeEndingAnimationComplete(bool canceled) override {
  229. delete this;
  230. }
  231. void OnCompositingShuttingDown(ui::Compositor* compositor) override {
  232. DCHECK_EQ(compositor_, compositor);
  233. delete this;
  234. }
  235. private:
  236. ui::Compositor* compositor_;
  237. OverviewExitMetricsTracker metrics_tracker_;
  238. };
  239. // Creates |drop_target_widget_|. It's created when a window or overview item is
  240. // dragged around, and destroyed when the drag ends.
  241. std::unique_ptr<views::Widget> CreateDropTargetWidget(
  242. aura::Window* root_window,
  243. aura::Window* dragged_window) {
  244. views::Widget::InitParams params;
  245. params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
  246. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  247. params.activatable = views::Widget::InitParams::Activatable::kNo;
  248. params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
  249. params.name = "OverviewDropTarget";
  250. params.accept_events = false;
  251. params.parent = desks_util::GetActiveDeskContainerForRoot(root_window);
  252. params.init_properties_container.SetProperty(kHideInDeskMiniViewKey, true);
  253. auto widget = std::make_unique<views::Widget>();
  254. widget->set_focus_on_creation(false);
  255. widget->Init(std::move(params));
  256. widget->SetVisibilityAnimationTransition(views::Widget::ANIMATE_NONE);
  257. // Show plus icon if drag a tab from a multi-tab window.
  258. widget->SetContentsView(std::make_unique<DropTargetView>(
  259. dragged_window->GetProperty(kTabDraggingSourceWindowKey)));
  260. aura::Window* drop_target_window = widget->GetNativeWindow();
  261. drop_target_window->parent()->StackChildAtBottom(drop_target_window);
  262. widget->Show();
  263. return widget;
  264. }
  265. // Creates `save_desk_button_container_widget_`. It contains save desk as
  266. // template button and save for later button.
  267. std::unique_ptr<views::Widget> CreateSaveDeskButtonContainerWidget(
  268. aura::Window* root_window) {
  269. views::Widget::InitParams params;
  270. params.type = views::Widget::InitParams::TYPE_POPUP;
  271. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  272. params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
  273. params.name = "SaveDeskButtonContainerWidget";
  274. params.accept_events = true;
  275. // This widget is hidden during window dragging, but will become visible on
  276. // mouse/touch release. Place it in the active desk container so it remains
  277. // beneath the dragged window when it is animating back to the overview grid.
  278. params.parent = desks_util::GetActiveDeskContainerForRoot(root_window);
  279. params.init_properties_container.SetProperty(kHideInDeskMiniViewKey, true);
  280. auto widget = std::make_unique<views::Widget>();
  281. widget->set_focus_on_creation(false);
  282. widget->Init(std::move(params));
  283. aura::Window* window = widget->GetNativeWindow();
  284. window->parent()->StackChildAtBottom(window);
  285. window->SetId(kShellWindowId_SaveDeskButtonContainer);
  286. return widget;
  287. }
  288. float GetWantedDropTargetOpacity(
  289. SplitViewDragIndicators::WindowDraggingState window_dragging_state) {
  290. switch (window_dragging_state) {
  291. case SplitViewDragIndicators::WindowDraggingState::kNoDrag:
  292. case SplitViewDragIndicators::WindowDraggingState::kOtherDisplay:
  293. case SplitViewDragIndicators::WindowDraggingState::kToSnapLeft:
  294. case SplitViewDragIndicators::WindowDraggingState::kToSnapRight:
  295. return 0.f;
  296. case SplitViewDragIndicators::WindowDraggingState::kFromOverview:
  297. case SplitViewDragIndicators::WindowDraggingState::kFromTop:
  298. case SplitViewDragIndicators::WindowDraggingState::kFromShelf:
  299. return 1.f;
  300. case SplitViewDragIndicators::WindowDraggingState::kFromFloat:
  301. NOTREACHED();
  302. return 0.f;
  303. }
  304. }
  305. gfx::Insets GetGridInsetsImpl(const gfx::Rect& grid_bounds) {
  306. const int horizontal_inset =
  307. base::ClampFloor(kOverviewInsetRatio *
  308. std::min(grid_bounds.width(), grid_bounds.height()));
  309. const int vertical_inset =
  310. horizontal_inset +
  311. kOverviewVerticalInset * (grid_bounds.height() - 2 * horizontal_inset);
  312. return gfx::Insets::VH(std::max(0, vertical_inset - kWindowMargin),
  313. std::max(0, horizontal_inset - kWindowMargin));
  314. }
  315. bool ShouldExcludeItemFromGridLayout(
  316. OverviewItem* item,
  317. const base::flat_set<OverviewItem*>& ignored_items) {
  318. return item->animating_to_close() || ignored_items.contains(item);
  319. }
  320. } // namespace
  321. // The class to observe the overview window that the dragged tabs will merge
  322. // into. After the dragged tabs merge into the overview window, and if the
  323. // overview window represents a minimized window, we need to update the
  324. // overview minimized widget's content view so that it reflects the merge.
  325. class OverviewGrid::TargetWindowObserver : public aura::WindowObserver {
  326. public:
  327. TargetWindowObserver() = default;
  328. TargetWindowObserver(const TargetWindowObserver&) = delete;
  329. TargetWindowObserver& operator=(const TargetWindowObserver&) = delete;
  330. ~TargetWindowObserver() override { StopObserving(); }
  331. void StartObserving(aura::Window* window) {
  332. if (target_window_)
  333. StopObserving();
  334. target_window_ = window;
  335. target_window_->AddObserver(this);
  336. }
  337. // aura::WindowObserver:
  338. void OnWindowPropertyChanged(aura::Window* window,
  339. const void* key,
  340. intptr_t old) override {
  341. DCHECK_EQ(window, target_window_);
  342. // When the property is cleared, the dragged window should have been merged
  343. // into |target_window_|, update the corresponding window item in overview.
  344. if (key == chromeos::kIsDeferredTabDraggingTargetWindowKey &&
  345. !window->GetProperty(chromeos::kIsDeferredTabDraggingTargetWindowKey)) {
  346. UpdateWindowItemInOverviewContaining(window);
  347. StopObserving();
  348. }
  349. }
  350. void OnWindowDestroying(aura::Window* window) override {
  351. DCHECK_EQ(window, target_window_);
  352. StopObserving();
  353. }
  354. private:
  355. void UpdateWindowItemInOverviewContaining(aura::Window* window) {
  356. OverviewController* overview_controller =
  357. Shell::Get()->overview_controller();
  358. if (!overview_controller->InOverviewSession())
  359. return;
  360. OverviewGrid* grid =
  361. overview_controller->overview_session()->GetGridWithRootWindow(
  362. window->GetRootWindow());
  363. if (!grid)
  364. return;
  365. OverviewItem* item = grid->GetOverviewItemContaining(window);
  366. if (!item)
  367. return;
  368. item->UpdateItemContentViewForMinimizedWindow();
  369. }
  370. void StopObserving() {
  371. if (target_window_)
  372. target_window_->RemoveObserver(this);
  373. target_window_ = nullptr;
  374. }
  375. aura::Window* target_window_ = nullptr;
  376. };
  377. OverviewGrid::OverviewGrid(aura::Window* root_window,
  378. const std::vector<aura::Window*>& windows,
  379. OverviewSession* overview_session)
  380. : root_window_(root_window),
  381. overview_session_(overview_session),
  382. split_view_drag_indicators_(
  383. ShouldAllowSplitView()
  384. ? std::make_unique<SplitViewDragIndicators>(root_window)
  385. : nullptr),
  386. bounds_(GetGridBoundsInScreen(root_window)) {
  387. for (auto* window : windows) {
  388. if (window->GetRootWindow() != root_window)
  389. continue;
  390. // Stop ongoing animations before entering overview mode. Because we are
  391. // deferring SetTransform of the windows beneath the window covering the
  392. // available workspace, we need to set the correct transforms of these
  393. // windows before entering overview mode again in the
  394. // OnImplicitAnimationsCompleted() of the observer of the
  395. // available-workspace-covering window's animation.
  396. auto* animator = window->layer()->GetAnimator();
  397. if (animator->is_animating())
  398. window->layer()->GetAnimator()->StopAnimating();
  399. window_list_.push_back(
  400. std::make_unique<OverviewItem>(window, overview_session_, this));
  401. UpdateNumIncognitoUnsupportedWindows(window, /*increment*/true);
  402. }
  403. }
  404. OverviewGrid::~OverviewGrid() = default;
  405. void OverviewGrid::Shutdown(OverviewEnterExitType exit_type) {
  406. EndNudge();
  407. SplitViewController::Get(root_window_)->RemoveObserver(this);
  408. ScreenRotationAnimator::GetForRootWindow(root_window_)->RemoveObserver(this);
  409. Shell::Get()->wallpaper_controller()->RemoveObserver(this);
  410. grid_event_handler_.reset();
  411. if (IsShowingDesksTemplatesGrid())
  412. HideDesksTemplatesGrid(/*exit_overview=*/true);
  413. bool has_non_cover_animating = false;
  414. int animate_count = 0;
  415. for (const auto& window : window_list_) {
  416. if (window->should_animate_when_exiting() && !has_non_cover_animating) {
  417. has_non_cover_animating |=
  418. !CanCoverAvailableWorkspace(window->GetWindow());
  419. animate_count++;
  420. }
  421. window->Shutdown();
  422. }
  423. bool single_animation_in_clamshell =
  424. (animate_count == 1 && !has_non_cover_animating) &&
  425. !Shell::Get()->tablet_mode_controller()->InTabletMode();
  426. const bool in_split_view =
  427. SplitViewController::Get(root_window_)->InSplitViewMode();
  428. // OverviewGrid in splitscreen does not include the window to be activated.
  429. if (!window_list_.empty() || in_split_view) {
  430. bool minimized_in_tablet = overview_session_->enter_exit_overview_type() ==
  431. OverviewEnterExitType::kFadeOutExit;
  432. // The following instance self-destructs when shutdown animation ends.
  433. new ShutdownAnimationMetricsTrackerObserver(
  434. root_window_->layer()->GetCompositor(), in_split_view,
  435. single_animation_in_clamshell, minimized_in_tablet);
  436. }
  437. window_list_.clear();
  438. overview_session_ = nullptr;
  439. if (no_windows_widget_) {
  440. if (exit_type == OverviewEnterExitType::kImmediateExit) {
  441. ImmediatelyCloseWidgetOnExit(std::move(no_windows_widget_));
  442. return;
  443. }
  444. // Fade out the no windows widget. This animation continues past the
  445. // lifetime of |this|.
  446. FadeOutWidgetFromOverview(std::move(no_windows_widget_),
  447. OVERVIEW_ANIMATION_RESTORE_WINDOW);
  448. }
  449. }
  450. void OverviewGrid::PrepareForOverview() {
  451. if (!ShouldAnimateWallpaper(root_window_))
  452. MaybeInitDesksWidget();
  453. for (const auto& window : window_list_)
  454. window->PrepareForOverview();
  455. SplitViewController::Get(root_window_)->AddObserver(this);
  456. if (Shell::Get()->tablet_mode_controller()->InTabletMode())
  457. ScreenRotationAnimator::GetForRootWindow(root_window_)->AddObserver(this);
  458. grid_event_handler_ = std::make_unique<OverviewGridEventHandler>(this);
  459. Shell::Get()->wallpaper_controller()->AddObserver(this);
  460. }
  461. void OverviewGrid::PositionWindows(
  462. bool animate,
  463. const base::flat_set<OverviewItem*>& ignored_items,
  464. OverviewTransition transition) {
  465. if (!overview_session_ || suspend_reposition_ || window_list_.empty())
  466. return;
  467. DCHECK_NE(transition, OverviewTransition::kExit);
  468. std::vector<gfx::RectF> rects =
  469. ShouldUseTabletModeGridLayout() &&
  470. (window_list_.size() - ignored_items.size() >=
  471. kMinimumItemsForNewLayout)
  472. ? GetWindowRectsForTabletModeLayout(ignored_items)
  473. : GetWindowRects(ignored_items);
  474. if (transition == OverviewTransition::kEnter) {
  475. CalculateWindowListAnimationStates(/*selected_item=*/nullptr, transition,
  476. rects);
  477. }
  478. // Position the windows centering the left-aligned rows vertically. Do not
  479. // position items in |ignored_items|.
  480. OverviewAnimationType animation_type = OVERVIEW_ANIMATION_NONE;
  481. switch (transition) {
  482. case OverviewTransition::kEnter: {
  483. const bool entering_from_home =
  484. overview_session_->enter_exit_overview_type() ==
  485. OverviewEnterExitType::kFadeInEnter;
  486. animation_type = entering_from_home
  487. ? OVERVIEW_ANIMATION_ENTER_FROM_HOME_LAUNCHER
  488. : OVERVIEW_ANIMATION_LAYOUT_OVERVIEW_ITEMS_ON_ENTER;
  489. break;
  490. }
  491. case OverviewTransition::kInOverview:
  492. animation_type = OVERVIEW_ANIMATION_LAYOUT_OVERVIEW_ITEMS_IN_OVERVIEW;
  493. break;
  494. case OverviewTransition::kExit:
  495. NOTREACHED();
  496. }
  497. int animate_count = 0;
  498. bool has_non_cover_animating = false;
  499. std::vector<OverviewAnimationType> animation_types(rects.size());
  500. const bool can_do_spawn_animation =
  501. animate && transition == OverviewTransition::kInOverview;
  502. for (size_t i = 0; i < window_list_.size(); ++i) {
  503. OverviewItem* window_item = window_list_[i].get();
  504. if (ShouldExcludeItemFromGridLayout(window_item, ignored_items)) {
  505. rects[i].SetRect(0, 0, 0, 0);
  506. continue;
  507. }
  508. // Calculate if each window item needs animation.
  509. bool should_animate_item = animate;
  510. // If we're in entering overview process, not all window items in the grid
  511. // might need animation even if the grid needs animation.
  512. if (animate && transition == OverviewTransition::kEnter)
  513. should_animate_item = window_item->should_animate_when_entering();
  514. if (animate && transition == OverviewTransition::kEnter) {
  515. if (window_item->should_animate_when_entering() &&
  516. !has_non_cover_animating) {
  517. has_non_cover_animating |=
  518. !CanCoverAvailableWorkspace(window_item->GetWindow());
  519. ++animate_count;
  520. }
  521. }
  522. if (can_do_spawn_animation && window_item->should_use_spawn_animation())
  523. animation_type = OVERVIEW_ANIMATION_SPAWN_ITEM_IN_OVERVIEW;
  524. animation_types[i] =
  525. should_animate_item ? animation_type : OVERVIEW_ANIMATION_NONE;
  526. }
  527. if (animate && transition == OverviewTransition::kEnter &&
  528. !window_list_.empty()) {
  529. bool single_animation_in_clamshell =
  530. animate_count == 1 && !has_non_cover_animating &&
  531. !Shell::Get()->tablet_mode_controller()->InTabletMode();
  532. bool minimized_in_tablet = overview_session_->enter_exit_overview_type() ==
  533. OverviewEnterExitType::kFadeInEnter;
  534. metrics_tracker_ = std::make_unique<OverviewEnterMetricsTracker>(
  535. window_list_[0]->GetWindow()->layer()->GetCompositor(),
  536. SplitViewController::Get(root_window_)->InSplitViewMode(),
  537. single_animation_in_clamshell, minimized_in_tablet);
  538. }
  539. // Apply the animation after creating metrics_tracker_ so that unit test
  540. // can correctly count the measure requests.
  541. for (size_t i = 0; i < window_list_.size(); ++i) {
  542. if (rects[i].IsEmpty())
  543. continue;
  544. OverviewItem* window_item = window_list_[i].get();
  545. window_item->SetBounds(rects[i], animation_types[i]);
  546. }
  547. UpdateSaveDeskButtons();
  548. }
  549. OverviewItem* OverviewGrid::GetOverviewItemContaining(
  550. const aura::Window* window) const {
  551. for (const auto& window_item : window_list_) {
  552. if (window_item && window_item->Contains(window))
  553. return window_item.get();
  554. }
  555. return nullptr;
  556. }
  557. void OverviewGrid::AddItem(aura::Window* window,
  558. bool reposition,
  559. bool animate,
  560. const base::flat_set<OverviewItem*>& ignored_items,
  561. size_t index,
  562. bool use_spawn_animation,
  563. bool restack) {
  564. DCHECK(!GetOverviewItemContaining(window));
  565. DCHECK_LE(index, window_list_.size());
  566. UpdateNumIncognitoUnsupportedWindows(window, /*increment*/true);
  567. window_list_.insert(
  568. window_list_.begin() + index,
  569. std::make_unique<OverviewItem>(window, overview_session_, this));
  570. if (overview_session_)
  571. overview_session_->UpdateFrameThrottling();
  572. auto* item = window_list_[index].get();
  573. item->PrepareForOverview();
  574. // No animations if the templates grid is showing, even if `animate` is true.
  575. const bool should_animate = animate && !IsShowingDesksTemplatesGrid();
  576. if (should_animate && use_spawn_animation && reposition) {
  577. item->set_should_use_spawn_animation(true);
  578. } else {
  579. // The item is added after overview enter animation is complete, so
  580. // just call OnStartingAnimationComplete() only if we won't animate it with
  581. // with the spawn animation. Otherwise, OnStartingAnimationComplete() will
  582. // be called when the spawn-item-animation completes (See
  583. // OverviewItem::OnItemSpawnedAnimationCompleted()).
  584. item->OnStartingAnimationComplete();
  585. }
  586. if (restack) {
  587. if (reposition && should_animate)
  588. item->set_should_restack_on_animation_end(true);
  589. else
  590. item->Restack();
  591. }
  592. if (reposition)
  593. PositionWindows(should_animate, ignored_items);
  594. if (IsShowingDesksTemplatesGrid())
  595. item->HideForDesksTemplatesGrid(/*animate=*/false);
  596. }
  597. void OverviewGrid::AppendItem(aura::Window* window,
  598. bool reposition,
  599. bool animate,
  600. bool use_spawn_animation) {
  601. AddItem(window, reposition, animate, /*ignored_items=*/{},
  602. window_list_.size(), use_spawn_animation, /*restack=*/false);
  603. }
  604. void OverviewGrid::AddItemInMruOrder(aura::Window* window,
  605. bool reposition,
  606. bool animate,
  607. bool restack,
  608. bool use_spawn_animation) {
  609. AddItem(window, reposition, animate, /*ignored_items=*/{},
  610. FindInsertionIndex(window), use_spawn_animation, restack);
  611. }
  612. void OverviewGrid::RemoveItem(OverviewItem* overview_item,
  613. bool item_destroying,
  614. bool reposition) {
  615. EndNudge();
  616. // Use reverse iterator to be efficient when removing all.
  617. auto iter = std::find_if(window_list_.rbegin(), window_list_.rend(),
  618. base::MatchesUniquePtr(overview_item));
  619. DCHECK(iter != window_list_.rend());
  620. UpdateNumIncognitoUnsupportedWindows(overview_item->GetWindow(),
  621. /*increment*/false);
  622. // This can also be called when shutting down |this|, at which the item will
  623. // be cleaning up and its associated view may be nullptr. |overview_item|
  624. // needs to still be in |window_list_| so we can compute what the deleted
  625. // index is.
  626. if (overview_session_ && (*iter)->overview_item_view()) {
  627. overview_session_->highlight_controller()->OnViewDestroyingOrDisabling(
  628. (*iter)->overview_item_view());
  629. }
  630. // Erase from the list first because deleting OverviewItem can lead to
  631. // iterating through the |window_list_|.
  632. std::unique_ptr<OverviewItem> tmp = std::move(*iter);
  633. window_list_.erase(std::next(iter).base());
  634. tmp.reset();
  635. if (overview_session_)
  636. overview_session_->UpdateFrameThrottling();
  637. if (!item_destroying)
  638. return;
  639. if (!overview_session_)
  640. return;
  641. if (empty()) {
  642. overview_session_->OnGridEmpty();
  643. return;
  644. }
  645. if (reposition) {
  646. // Update the grid bounds if needed and reposition the windows minus the
  647. // currently overview dragged window, if there is one. Note: this does not
  648. // update the grid bounds if the window being dragged from the top or shelf,
  649. // the former being handled in TabletModeWindowDragDelegate's destructor.
  650. base::flat_set<OverviewItem*> ignored_items;
  651. OverviewItem* dragged_item =
  652. overview_session_->GetCurrentDraggedOverviewItem();
  653. if (dragged_item)
  654. ignored_items.insert(dragged_item);
  655. const gfx::Rect grid_bounds = GetGridBoundsInScreen(
  656. root_window_,
  657. split_view_drag_indicators_
  658. ? absl::make_optional(
  659. split_view_drag_indicators_->current_window_dragging_state())
  660. : absl::nullopt,
  661. /*divider_changed=*/false,
  662. /*account_for_hotseat=*/true);
  663. SetBoundsAndUpdatePositions(grid_bounds, ignored_items, /*animate=*/true);
  664. }
  665. }
  666. void OverviewGrid::RemoveAllItemsForDesksTemplatesLaunch() {
  667. for (auto& item : window_list_) {
  668. item->RevertHideForDesksTemplatesGrid(/*animate=*/false);
  669. item->RestoreWindow(/*reset_tranform=*/true,
  670. /*was_desks_templates_grid_showing=*/true);
  671. }
  672. window_list_.clear();
  673. }
  674. void OverviewGrid::AddDropTargetForDraggingFromThisGrid(
  675. OverviewItem* dragged_item) {
  676. DCHECK(!drop_target_widget_);
  677. drop_target_widget_ =
  678. CreateDropTargetWidget(root_window_, dragged_item->GetWindow());
  679. const size_t position = GetOverviewItemIndex(dragged_item) + 1u;
  680. overview_session_->AddItem(drop_target_widget_->GetNativeWindow(),
  681. /*reposition=*/true, /*animate=*/false,
  682. /*ignored_items=*/{dragged_item}, position);
  683. }
  684. void OverviewGrid::AddDropTargetNotForDraggingFromThisGrid(
  685. aura::Window* dragged_window,
  686. bool animate) {
  687. DCHECK(!drop_target_widget_);
  688. drop_target_widget_ = CreateDropTargetWidget(root_window_, dragged_window);
  689. aura::Window* drop_target_window = drop_target_widget_->GetNativeWindow();
  690. if (animate) {
  691. drop_target_widget_->SetOpacity(0.f);
  692. ScopedOverviewAnimationSettings settings(
  693. OVERVIEW_ANIMATION_DROP_TARGET_FADE, drop_target_window);
  694. drop_target_widget_->SetOpacity(1.f);
  695. }
  696. const size_t position = FindInsertionIndex(dragged_window);
  697. overview_session_->AddItem(drop_target_window, /*reposition=*/true, animate,
  698. /*ignored_items=*/{}, position);
  699. }
  700. void OverviewGrid::RemoveDropTarget() {
  701. DCHECK(drop_target_widget_);
  702. OverviewItem* drop_target = GetDropTarget();
  703. overview_session_->RemoveItem(drop_target);
  704. drop_target_widget_.reset();
  705. }
  706. void OverviewGrid::SetBoundsAndUpdatePositions(
  707. const gfx::Rect& bounds_in_screen,
  708. const base::flat_set<OverviewItem*>& ignored_items,
  709. bool animate) {
  710. const bool bounds_updated = bounds_in_screen != bounds_;
  711. bounds_ = bounds_in_screen;
  712. MaybeUpdateDesksWidgetBounds();
  713. PositionWindows(animate, ignored_items);
  714. if (bounds_updated && saved_desk_library_widget_)
  715. saved_desk_library_widget_->SetBounds(GetGridEffectiveBounds());
  716. }
  717. void OverviewGrid::RearrangeDuringDrag(
  718. OverviewItem* dragged_item,
  719. SplitViewDragIndicators::WindowDraggingState window_dragging_state) {
  720. OverviewItem* drop_target = GetDropTarget();
  721. // Update the drop target visibility according to |window_dragging_state|.
  722. if (drop_target) {
  723. ScopedOverviewAnimationSettings settings(
  724. OVERVIEW_ANIMATION_DROP_TARGET_FADE,
  725. drop_target_widget_->GetNativeWindow());
  726. drop_target->SetOpacity(GetWantedDropTargetOpacity(window_dragging_state));
  727. }
  728. // Update the grid's bounds.
  729. const gfx::Rect wanted_grid_bounds = GetGridBoundsInScreen(
  730. root_window_, absl::make_optional(window_dragging_state),
  731. /*divider_changed=*/false, /*account_for_hotseat=*/true);
  732. if (bounds_ != wanted_grid_bounds) {
  733. base::flat_set<OverviewItem*> ignored_items;
  734. if (dragged_item)
  735. ignored_items.insert(dragged_item);
  736. SetBoundsAndUpdatePositions(wanted_grid_bounds, ignored_items,
  737. /*animate=*/true);
  738. }
  739. }
  740. void OverviewGrid::SetSplitViewDragIndicatorsDraggedWindow(
  741. aura::Window* dragged_window) {
  742. DCHECK(split_view_drag_indicators_);
  743. split_view_drag_indicators_->SetDraggedWindow(dragged_window);
  744. }
  745. void OverviewGrid::SetSplitViewDragIndicatorsWindowDraggingState(
  746. SplitViewDragIndicators::WindowDraggingState window_dragging_state) {
  747. DCHECK(split_view_drag_indicators_);
  748. split_view_drag_indicators_->SetWindowDraggingState(window_dragging_state);
  749. }
  750. bool OverviewGrid::MaybeUpdateDesksWidgetBounds() {
  751. if (!desks_widget_)
  752. return false;
  753. const gfx::Rect desks_widget_bounds = GetDesksWidgetBounds();
  754. if (desks_widget_bounds != desks_widget_->GetWindowBoundsInScreen()) {
  755. // Note that the desks widget window is placed on the active desk container,
  756. // which has the kUsesScreenCoordinatesKey property set to true, and hence
  757. // we use the screen coordinates when positioning the desks widget.
  758. //
  759. // On certain display zooms, the requested |desks_widget_bounds| may differ
  760. // than the current screen bounds of the desks widget by 1dp, but internally
  761. // it will end up being the same and therefore a layout may not be
  762. // triggered. This can cause mini views not to show up at all. We must
  763. // guarantee that a layout will always occur by invalidating the layout.
  764. // See crbug.com/1056371 for more details.
  765. desks_bar_view_->InvalidateLayout();
  766. desks_widget_->SetBounds(desks_widget_bounds);
  767. return true;
  768. }
  769. return false;
  770. }
  771. void OverviewGrid::UpdateDropTargetBackgroundVisibility(
  772. OverviewItem* dragged_item,
  773. const gfx::PointF& location_in_screen) {
  774. DCHECK(drop_target_widget_);
  775. aura::Window* target_window =
  776. GetTargetWindowOnLocation(location_in_screen, dragged_item);
  777. DropTargetView* drop_target_view =
  778. static_cast<DropTargetView*>(drop_target_widget_->GetContentsView());
  779. DCHECK(drop_target_view);
  780. drop_target_view->UpdateBackgroundVisibility(
  781. target_window && IsDropTargetWindow(target_window));
  782. }
  783. void OverviewGrid::OnSelectorItemDragStarted(OverviewItem* item) {
  784. CommitNameChanges();
  785. for (auto& overview_mode_item : window_list_)
  786. overview_mode_item->OnSelectorItemDragStarted(item);
  787. }
  788. void OverviewGrid::OnSelectorItemDragEnded(bool snap) {
  789. for (auto& overview_mode_item : window_list_)
  790. overview_mode_item->OnSelectorItemDragEnded(snap);
  791. }
  792. void OverviewGrid::OnWindowDragStarted(aura::Window* dragged_window,
  793. bool animate) {
  794. dragged_window_ = dragged_window;
  795. AddDropTargetNotForDraggingFromThisGrid(dragged_window, animate);
  796. // Stack the |dragged_window| at top during drag.
  797. dragged_window->parent()->StackChildAtTop(dragged_window);
  798. // Called to set caption and title visibility during dragging.
  799. OnSelectorItemDragStarted(/*item=*/nullptr);
  800. }
  801. void OverviewGrid::OnWindowDragContinued(
  802. aura::Window* dragged_window,
  803. const gfx::PointF& location_in_screen,
  804. SplitViewDragIndicators::WindowDraggingState window_dragging_state) {
  805. DCHECK_EQ(dragged_window_, dragged_window);
  806. DCHECK_EQ(dragged_window->GetRootWindow(), root_window_);
  807. RearrangeDuringDrag(nullptr, window_dragging_state);
  808. UpdateDropTargetBackgroundVisibility(nullptr, location_in_screen);
  809. aura::Window* target_window =
  810. GetTargetWindowOnLocation(location_in_screen, /*ignored_item=*/nullptr);
  811. if (SplitViewDragIndicators::GetSnapPosition(window_dragging_state) !=
  812. SplitViewController::NONE) {
  813. // If the dragged window is currently dragged into preview window area,
  814. // hide the highlight.
  815. overview_session_->highlight_controller()->HideTabDragHighlight();
  816. // Also clear chromeos::kIsDeferredTabDraggingTargetWindowKey key on the
  817. // target overview item so that it can't merge into this overview item if
  818. // the dragged window is currently in preview window area.
  819. if (target_window && !IsDropTargetWindow(target_window))
  820. target_window->ClearProperty(
  821. chromeos::kIsDeferredTabDraggingTargetWindowKey);
  822. return;
  823. }
  824. // Show the tab drag highlight if |location_in_screen| is contained by the
  825. // browser windows' overview item in overview.
  826. if (target_window && target_window->GetProperty(
  827. chromeos::kIsDeferredTabDraggingTargetWindowKey)) {
  828. auto* item = GetOverviewItemContaining(target_window);
  829. if (!item)
  830. return;
  831. overview_session_->highlight_controller()->ShowTabDragHighlight(
  832. item->overview_item_view());
  833. return;
  834. }
  835. overview_session_->highlight_controller()->HideTabDragHighlight();
  836. }
  837. void OverviewGrid::OnWindowDragEnded(aura::Window* dragged_window,
  838. const gfx::PointF& location_in_screen,
  839. bool should_drop_window_into_overview,
  840. bool snap) {
  841. DCHECK_EQ(dragged_window_, dragged_window);
  842. DCHECK_EQ(dragged_window->GetRootWindow(), root_window_);
  843. DCHECK(drop_target_widget_.get());
  844. dragged_window_ = nullptr;
  845. // Add the dragged window into drop target in overview if
  846. // |should_drop_window_into_overview| is true. Only consider add the dragged
  847. // window into drop target if SelectedWindow is false since drop target will
  848. // not be selected and tab dragging might drag a tab window to merge it into a
  849. // browser window in overview.
  850. if (overview_session_->highlight_controller()->IsTabDragHighlightVisible())
  851. overview_session_->highlight_controller()->HideTabDragHighlight();
  852. else if (should_drop_window_into_overview)
  853. AddDraggedWindowIntoOverviewOnDragEnd(dragged_window);
  854. RemoveDropTarget();
  855. // Called to reset caption and title visibility after dragging.
  856. OnSelectorItemDragEnded(snap);
  857. // After drag ends, if the dragged window needs to merge into another window
  858. // |target_window|, and we may need to update |minimized_widget_| that holds
  859. // the contents of |target_window| if |target_window| is a minimized window
  860. // in overview.
  861. aura::Window* target_window =
  862. GetTargetWindowOnLocation(location_in_screen, /*ignored_item=*/nullptr);
  863. if (target_window && target_window->GetProperty(
  864. chromeos::kIsDeferredTabDraggingTargetWindowKey)) {
  865. // Create an window observer and update the minimized window widget after
  866. // the dragged window merges into |target_window|.
  867. if (!target_window_observer_)
  868. target_window_observer_ = std::make_unique<TargetWindowObserver>();
  869. target_window_observer_->StartObserving(target_window);
  870. }
  871. // Update the grid bounds and reposition windows. Since the grid bounds might
  872. // be updated based on the preview area during drag, but the window finally
  873. // didn't be snapped to the preview area.
  874. SetBoundsAndUpdatePositions(GetGridBoundsInScreen(root_window_),
  875. /*ignored_items=*/{},
  876. /*animate=*/true);
  877. }
  878. void OverviewGrid::MergeWindowIntoOverviewForWebUITabStrip(
  879. aura::Window* dragged_window) {
  880. AddDraggedWindowIntoOverviewOnDragEnd(dragged_window);
  881. SetBoundsAndUpdatePositions(GetGridBoundsInScreen(root_window_),
  882. /*ignored_items=*/{},
  883. /*animate=*/true);
  884. }
  885. void OverviewGrid::SetVisibleDuringWindowDragging(bool visible, bool animate) {
  886. for (const auto& window_item : window_list_)
  887. window_item->SetVisibleDuringWindowDragging(visible, animate);
  888. // Update |desks_widget_|.
  889. if (desks_widget_) {
  890. ui::Layer* layer = desks_widget_->GetNativeWindow()->layer();
  891. float new_opacity = visible ? 1.f : 0.f;
  892. if (layer->GetTargetOpacity() == new_opacity)
  893. return;
  894. if (animate) {
  895. ScopedOverviewAnimationSettings settings(
  896. OVERVIEW_ANIMATION_OPACITY_ON_WINDOW_DRAG,
  897. desks_widget_->GetNativeWindow());
  898. layer->SetOpacity(new_opacity);
  899. } else {
  900. layer->SetOpacity(new_opacity);
  901. }
  902. }
  903. }
  904. bool OverviewGrid::IsDropTargetWindow(aura::Window* window) const {
  905. return drop_target_widget_ &&
  906. drop_target_widget_->GetNativeWindow() == window;
  907. }
  908. OverviewItem* OverviewGrid::GetDropTarget() {
  909. return drop_target_widget_
  910. ? GetOverviewItemContaining(drop_target_widget_->GetNativeWindow())
  911. : nullptr;
  912. }
  913. void OverviewGrid::OnDisplayMetricsChanged() {
  914. if (split_view_drag_indicators_)
  915. split_view_drag_indicators_->OnDisplayBoundsChanged();
  916. UpdateCannotSnapWarningVisibility(/*animate=*/true);
  917. // In case of split view mode, the grid bounds and item positions will be
  918. // updated in |OnSplitViewDividerPositionChanged|.
  919. if (SplitViewController::Get(root_window_)->InSplitViewMode())
  920. return;
  921. SetBoundsAndUpdatePositions(GetGridBoundsInScreen(root_window_),
  922. /*ignored_items=*/{}, /*animate=*/false);
  923. }
  924. void OverviewGrid::OnUserWorkAreaInsetsChanged(aura::Window* root_window) {
  925. DCHECK_EQ(root_window, root_window_);
  926. if (!desks_widget_)
  927. return;
  928. SetBoundsAndUpdatePositions(GetGridBoundsInScreen(root_window_),
  929. /*ignored_items=*/{}, /*animate=*/false);
  930. }
  931. void OverviewGrid::OnStartingAnimationComplete(bool canceled) {
  932. metrics_tracker_.reset();
  933. if (canceled)
  934. return;
  935. MaybeInitDesksWidget();
  936. for (auto& window : window_list())
  937. window->OnStartingAnimationComplete();
  938. }
  939. void OverviewGrid::CalculateWindowListAnimationStates(
  940. OverviewItem* selected_item,
  941. OverviewTransition transition,
  942. const std::vector<gfx::RectF>& target_bounds) {
  943. using OverviewTransition = OverviewTransition;
  944. // Sanity checks to enforce assumptions used in later codes.
  945. switch (transition) {
  946. case OverviewTransition::kEnter:
  947. DCHECK_EQ(target_bounds.size(), window_list_.size());
  948. break;
  949. case OverviewTransition::kExit:
  950. DCHECK(target_bounds.empty());
  951. break;
  952. default:
  953. NOTREACHED();
  954. }
  955. // On top items are items that are higher up on the z-order, or in the always
  956. // on top or float containers.
  957. auto is_on_top_item = [](OverviewItem* item) -> bool {
  958. DCHECK(item);
  959. if (item->GetWindow()->GetProperty(aura::client::kZOrderingKey) !=
  960. ui::ZOrderLevel::kNormal) {
  961. return true;
  962. }
  963. aura::Window* parent = item->GetWindow()->parent();
  964. aura::Window* root = parent->GetRootWindow();
  965. return parent == root->GetChildById(kShellWindowId_AlwaysOnTopContainer) ||
  966. parent == root->GetChildById(kShellWindowId_FloatContainer);
  967. };
  968. // Create a copy of `window_list_` which has the selected item and on top
  969. // windows in the front.
  970. std::vector<OverviewItem*> on_top_items;
  971. std::vector<OverviewItem*> regular_items;
  972. for (const std::unique_ptr<OverviewItem>& item : window_list_) {
  973. OverviewItem* item_ptr = item.get();
  974. DCHECK(item_ptr);
  975. // Skip the selected item, it will be inserted into the front.
  976. if (item_ptr == selected_item)
  977. continue;
  978. if (is_on_top_item(item_ptr))
  979. on_top_items.push_back(item_ptr);
  980. else
  981. regular_items.push_back(item_ptr);
  982. }
  983. // Construct `items` so they are ordered like so.
  984. // 1) Selected window which is on top.
  985. // 2) On top windows.
  986. // 3) Selected window which is not on top.
  987. // 4) Regular window.
  988. // Windows in the same group maintain their ordering from `window_list`.
  989. std::vector<OverviewItem*> items;
  990. if (selected_item && is_on_top_item(selected_item))
  991. items.insert(items.begin(), selected_item);
  992. items.insert(items.end(), on_top_items.begin(), on_top_items.end());
  993. if (selected_item && !is_on_top_item(selected_item))
  994. items.insert(items.end(), selected_item);
  995. items.insert(items.end(), regular_items.begin(), regular_items.end());
  996. SkRegion occluded_region;
  997. auto* split_view_controller = SplitViewController::Get(root_window_);
  998. if (split_view_controller->InSplitViewMode()) {
  999. // Snapped windows and the split view divider are not included in
  1000. // |target_bounds| or |window_list_|, but can occlude other windows, so add
  1001. // them manually to |region| here.
  1002. SkIRect snapped_window_bounds = gfx::RectToSkIRect(
  1003. split_view_controller->GetDefaultSnappedWindow()->GetBoundsInScreen());
  1004. occluded_region.op(snapped_window_bounds, SkRegion::kUnion_Op);
  1005. auto* divider = split_view_controller->split_view_divider();
  1006. if (divider) {
  1007. aura::Window* divider_window =
  1008. divider->divider_widget()->GetNativeWindow();
  1009. SkIRect divider_bounds =
  1010. gfx::RectToSkIRect(divider_window->GetBoundsInScreen());
  1011. occluded_region.op(divider_bounds, SkRegion::kUnion_Op);
  1012. }
  1013. }
  1014. // TODO(sammiequon): Investigate the bounds used here.
  1015. gfx::Rect grid_bounds = GetGridEffectiveBounds();
  1016. for (size_t i = 0; i < items.size(); ++i) {
  1017. const bool minimized =
  1018. WindowState::Get(items[i]->GetWindow())->IsMinimized();
  1019. bool src_occluded = minimized;
  1020. bool dst_occluded = false;
  1021. gfx::Rect src_bounds_temp =
  1022. minimized ? gfx::Rect()
  1023. : items[i]->GetWindow()->GetBoundsInRootWindow();
  1024. if (!src_bounds_temp.IsEmpty()) {
  1025. if (transition == OverviewTransition::kEnter &&
  1026. Shell::Get()->tablet_mode_controller()->InTabletMode()) {
  1027. BackdropController* backdrop_controller =
  1028. GetActiveWorkspaceController(root_window_)
  1029. ->layout_manager()
  1030. ->backdrop_controller();
  1031. if (backdrop_controller->GetTopmostWindowWithBackdrop() ==
  1032. items[i]->GetWindow()) {
  1033. src_bounds_temp = screen_util::GetDisplayWorkAreaBoundsInParent(
  1034. items[i]->GetWindow());
  1035. }
  1036. } else if (transition == OverviewTransition::kExit) {
  1037. // On exiting overview, |GetBoundsInRootWindow()| will have the overview
  1038. // translation applied to it, so use |bounds()| and
  1039. // |ConvertRectToScreen()| to get the true target bounds.
  1040. src_bounds_temp = items[i]->GetWindow()->bounds();
  1041. ::wm::ConvertRectToScreen(items[i]->root_window(), &src_bounds_temp);
  1042. }
  1043. src_bounds_temp.Intersect(grid_bounds);
  1044. }
  1045. // The bounds of of the destination may be partially or fully offscreen.
  1046. // Partially offscreen rects should be clipped so the onscreen portion is
  1047. // treated normally. Fully offscreen rects (intersection with the screen
  1048. // bounds is empty) should never be animated.
  1049. gfx::Rect dst_bounds_temp = gfx::ToEnclosedRect(
  1050. transition == OverviewTransition::kEnter ? target_bounds[i]
  1051. : items[i]->target_bounds());
  1052. dst_bounds_temp.Intersect(grid_bounds);
  1053. if (dst_bounds_temp.IsEmpty()) {
  1054. items[i]->set_should_animate_when_entering(false);
  1055. items[i]->set_should_animate_when_exiting(false);
  1056. continue;
  1057. }
  1058. SkIRect src_bounds = gfx::RectToSkIRect(src_bounds_temp);
  1059. SkIRect dst_bounds = gfx::RectToSkIRect(dst_bounds_temp);
  1060. if (!occluded_region.isEmpty()) {
  1061. src_occluded |=
  1062. (!src_bounds.isEmpty() && occluded_region.contains(src_bounds));
  1063. dst_occluded |= occluded_region.contains(dst_bounds);
  1064. }
  1065. // Add |src_bounds| to our region if it is not empty (minimized window).
  1066. if (!src_bounds.isEmpty())
  1067. occluded_region.op(src_bounds, SkRegion::kUnion_Op);
  1068. const bool should_animate = !(src_occluded && dst_occluded);
  1069. if (transition == OverviewTransition::kEnter)
  1070. items[i]->set_should_animate_when_entering(should_animate);
  1071. else if (transition == OverviewTransition::kExit)
  1072. items[i]->set_should_animate_when_exiting(should_animate);
  1073. }
  1074. }
  1075. void OverviewGrid::SetWindowListNotAnimatedWhenExiting() {
  1076. should_animate_when_exiting_ = false;
  1077. for (const auto& item : window_list_)
  1078. item->set_should_animate_when_exiting(false);
  1079. }
  1080. void OverviewGrid::StartNudge(OverviewItem* item) {
  1081. // When there is one window left, there is no need to nudge.
  1082. if (window_list_.size() <= 1) {
  1083. nudge_data_.clear();
  1084. return;
  1085. }
  1086. // If any of the items are being animated to close, do not nudge any windows
  1087. // otherwise we have to deal with potential items getting removed from
  1088. // |window_list_| midway through a nudge.
  1089. for (const auto& window_item : window_list_) {
  1090. if (window_item->animating_to_close()) {
  1091. nudge_data_.clear();
  1092. return;
  1093. }
  1094. }
  1095. DCHECK(item);
  1096. // Get the bounds of the windows currently, and the bounds if |item| were to
  1097. // be removed.
  1098. std::vector<gfx::RectF> src_rects;
  1099. for (const auto& window_item : window_list_)
  1100. src_rects.push_back(window_item->target_bounds());
  1101. std::vector<gfx::RectF> dst_rects = GetWindowRects({item});
  1102. const size_t index = GetOverviewItemIndex(item);
  1103. // Returns a vector of integers indicating which row the item is in. |index|
  1104. // is the index of the element which is going to be deleted and should not
  1105. // factor into calculations. The call site should mark |index| as -1 if it
  1106. // should not be used. The item at |index| is marked with a 0. The heights of
  1107. // items are all set to the same value so a new row is determined if the y
  1108. // value has changed from the previous item.
  1109. auto get_rows = [](const std::vector<gfx::RectF>& bounds_list, size_t index) {
  1110. std::vector<int> row_numbers;
  1111. int current_row = 1;
  1112. float last_y = 0;
  1113. for (size_t i = 0; i < bounds_list.size(); ++i) {
  1114. if (i == index) {
  1115. row_numbers.push_back(0);
  1116. continue;
  1117. }
  1118. // Update |current_row| if the y position has changed (heights are all
  1119. // equal in overview, so a new y position indicates a new row).
  1120. if (last_y != 0 && last_y != bounds_list[i].y())
  1121. ++current_row;
  1122. row_numbers.push_back(current_row);
  1123. last_y = bounds_list[i].y();
  1124. }
  1125. return row_numbers;
  1126. };
  1127. std::vector<int> src_rows = get_rows(src_rects, -1);
  1128. std::vector<int> dst_rows = get_rows(dst_rects, index);
  1129. // Do nothing if the number of rows change.
  1130. if (dst_rows.back() != 0 && src_rows.back() != dst_rows.back())
  1131. return;
  1132. size_t second_last_index = src_rows.size() - 2;
  1133. if (dst_rows.back() == 0 &&
  1134. src_rows[second_last_index] != dst_rows[second_last_index]) {
  1135. return;
  1136. }
  1137. // Do nothing if the last item from the previous row will drop onto the
  1138. // current row, this will cause the items in the current row to shift to the
  1139. // right while the previous item stays in the previous row, which looks weird.
  1140. if (src_rows[index] > 1) {
  1141. // Find the last item from the previous row.
  1142. size_t previous_row_last_index = index;
  1143. while (src_rows[previous_row_last_index] == src_rows[index]) {
  1144. --previous_row_last_index;
  1145. }
  1146. // Early return if the last item in the previous row changes rows.
  1147. if (src_rows[previous_row_last_index] != dst_rows[previous_row_last_index])
  1148. return;
  1149. }
  1150. // Helper to check whether the item at |item_index| will be nudged.
  1151. auto should_nudge = [&src_rows, &dst_rows, &index](size_t item_index) {
  1152. // Out of bounds.
  1153. if (item_index >= src_rows.size())
  1154. return false;
  1155. // Nudging happens when the item stays on the same row and is also on the
  1156. // same row as the item to be deleted was.
  1157. if (dst_rows[item_index] == src_rows[index] &&
  1158. dst_rows[item_index] == src_rows[item_index]) {
  1159. return true;
  1160. }
  1161. return false;
  1162. };
  1163. // Starting from |index| go up and down while the nudge condition returns
  1164. // true.
  1165. std::vector<int> affected_indexes;
  1166. size_t loop_index;
  1167. if (index > 0) {
  1168. loop_index = index - 1;
  1169. while (should_nudge(loop_index)) {
  1170. affected_indexes.push_back(loop_index);
  1171. --loop_index;
  1172. }
  1173. }
  1174. loop_index = index + 1;
  1175. while (should_nudge(loop_index)) {
  1176. affected_indexes.push_back(loop_index);
  1177. ++loop_index;
  1178. }
  1179. // Populate |nudge_data_| with the indexes in |affected_indexes| and their
  1180. // respective source and destination bounds.
  1181. nudge_data_.resize(affected_indexes.size());
  1182. for (size_t i = 0; i < affected_indexes.size(); ++i) {
  1183. NudgeData data;
  1184. data.index = affected_indexes[i];
  1185. data.src = src_rects[data.index];
  1186. data.dst = dst_rects[data.index];
  1187. nudge_data_[i] = data;
  1188. }
  1189. }
  1190. void OverviewGrid::UpdateNudge(OverviewItem* item, double value) {
  1191. for (const auto& data : nudge_data_) {
  1192. DCHECK_LT(data.index, window_list_.size());
  1193. OverviewItem* nudged_item = window_list_[data.index].get();
  1194. double nudge_param = value * value / 30.0;
  1195. nudge_param = base::clamp(nudge_param, 0.0, 1.0);
  1196. gfx::RectF bounds =
  1197. gfx::Tween::RectFValueBetween(nudge_param, data.src, data.dst);
  1198. nudged_item->SetBounds(bounds, OVERVIEW_ANIMATION_NONE);
  1199. }
  1200. }
  1201. void OverviewGrid::EndNudge() {
  1202. nudge_data_.clear();
  1203. }
  1204. aura::Window* OverviewGrid::GetTargetWindowOnLocation(
  1205. const gfx::PointF& location_in_screen,
  1206. OverviewItem* ignored_item) {
  1207. for (std::unique_ptr<OverviewItem>& item : window_list_) {
  1208. if (item.get() == ignored_item)
  1209. continue;
  1210. if (item->target_bounds().Contains(location_in_screen))
  1211. return item->GetWindow();
  1212. }
  1213. return nullptr;
  1214. }
  1215. bool OverviewGrid::IsDesksBarViewActive() const {
  1216. DCHECK(desks_util::ShouldDesksBarBeCreated());
  1217. // The desk bar view is not active if there is only a single desk when
  1218. // overview is started. Or when the desks bar view has been created and in
  1219. // zero state.
  1220. return DesksController::Get()->desks().size() > 1 ||
  1221. (desks_bar_view_ && !desks_bar_view_->IsZeroState());
  1222. }
  1223. gfx::Rect OverviewGrid::GetGridEffectiveBounds() const {
  1224. // There's an edge case where is in tablet mode, there're more than one desk,
  1225. // after entering overview mode, deleting desks to just keep one, even though
  1226. // there's only one desk now in tablet mode, the desks bar will stay. That's
  1227. // why we need to check the existence of `desks_bar_view_` here.
  1228. if (!desks_bar_view_ && !desks_util::ShouldDesksBarBeCreated())
  1229. return bounds_;
  1230. gfx::Rect effective_bounds = bounds_;
  1231. effective_bounds.Inset(gfx::Insets::TLBR(GetDesksBarHeight(), 0, 0, 0));
  1232. return effective_bounds;
  1233. }
  1234. gfx::Insets OverviewGrid::GetGridInsets() const {
  1235. return GetGridInsetsImpl(GetGridEffectiveBounds());
  1236. }
  1237. bool OverviewGrid::IntersectsWithDesksBar(const gfx::Point& screen_location,
  1238. bool update_desks_bar_drag_details,
  1239. bool for_drop) {
  1240. DCHECK(desks_util::ShouldDesksBarBeCreated());
  1241. const bool dragged_item_over_bar =
  1242. desks_widget_->GetWindowBoundsInScreen().Contains(screen_location);
  1243. if (update_desks_bar_drag_details) {
  1244. desks_bar_view_->SetDragDetails(screen_location,
  1245. !for_drop && dragged_item_over_bar);
  1246. }
  1247. return dragged_item_over_bar;
  1248. }
  1249. bool OverviewGrid::MaybeDropItemOnDeskMiniViewOrNewDeskButton(
  1250. const gfx::Point& screen_location,
  1251. OverviewItem* drag_item) {
  1252. DCHECK(desks_util::ShouldDesksBarBeCreated());
  1253. aura::Window* const dragged_window = drag_item->GetWindow();
  1254. const bool dragged_window_is_visible_on_all_desks =
  1255. dragged_window &&
  1256. desks_util::IsWindowVisibleOnAllWorkspaces(dragged_window);
  1257. // End the drag for the DesksBarView.
  1258. if (!IntersectsWithDesksBar(screen_location,
  1259. /*update_desks_bar_drag_details=*/
  1260. !dragged_window_is_visible_on_all_desks,
  1261. /*for_drop=*/true)) {
  1262. return false;
  1263. }
  1264. if (dragged_window_is_visible_on_all_desks) {
  1265. // Show toast since items that are visible on all desks should not be able
  1266. // to be unassigned during overview.
  1267. Shell::Get()->toast_manager()->Show(
  1268. ToastData(kMoveVisibleOnAllDesksWindowToastId,
  1269. ToastCatalogName::kMoveVisibleOnAllDesksWindow,
  1270. l10n_util::GetStringUTF16(
  1271. IDS_ASH_OVERVIEW_VISIBLE_ON_ALL_DESKS_TOAST)));
  1272. return false;
  1273. }
  1274. auto* desks_controller = DesksController::Get();
  1275. for (auto* mini_view : desks_bar_view_->mini_views()) {
  1276. if (!mini_view->IsPointOnMiniView(screen_location))
  1277. continue;
  1278. Desk* const target_desk = mini_view->desk();
  1279. if (target_desk == desks_controller->active_desk())
  1280. return false;
  1281. return desks_controller->MoveWindowFromActiveDeskTo(
  1282. dragged_window, target_desk, root_window_,
  1283. DesksMoveWindowFromActiveDeskSource::kDragAndDrop);
  1284. }
  1285. if (!features::IsDragWindowToNewDeskEnabled())
  1286. return false;
  1287. if (!desks_controller->CanCreateDesks())
  1288. return false;
  1289. if (!desks_bar_view_->expanded_state_new_desk_button()->IsPointOnButton(
  1290. screen_location)) {
  1291. return false;
  1292. }
  1293. desks_bar_view_->OnNewDeskButtonPressed(
  1294. DesksCreationRemovalSource::kDragToNewDeskButton);
  1295. return desks_controller->MoveWindowFromActiveDeskTo(
  1296. dragged_window, desks_controller->desks().back().get(), root_window_,
  1297. DesksMoveWindowFromActiveDeskSource::kDragAndDrop);
  1298. }
  1299. void OverviewGrid::MaybeExpandDesksBarView(const gfx::PointF& screen_location) {
  1300. if (desks_bar_view_ && desks_bar_view_->IsZeroState()) {
  1301. if ((gfx::ToRoundedPoint(screen_location) -
  1302. desks_bar_view_->zero_state_new_desk_button()
  1303. ->GetBoundsInScreen()
  1304. .CenterPoint())
  1305. .LengthSquared() <=
  1306. kExpandDesksBarThreshold * kExpandDesksBarThreshold) {
  1307. desks_bar_view_->UpdateNewMiniViews(/*initializing_bar_view=*/false,
  1308. /*expanding_bar_view=*/true);
  1309. }
  1310. }
  1311. }
  1312. void OverviewGrid::MaybeShrinkDesksBarView() {
  1313. if (desks_bar_view_ && !desks_bar_view_->IsZeroState() &&
  1314. !IsShowingDesksTemplatesGrid() &&
  1315. desks_bar_view_->mini_views().size() == 1) {
  1316. desks_bar_view_->SwitchToZeroState();
  1317. }
  1318. }
  1319. void OverviewGrid::StartScroll() {
  1320. Shell::Get()->overview_controller()->PauseOcclusionTracker();
  1321. // Users are not allowed to scroll past the leftmost or rightmost bounds of
  1322. // the items on screen in the grid. |scroll_offset_min_| is the amount needed
  1323. // to fit the rightmost window into |total_bounds|. The max is zero which is
  1324. // default because windows are aligned to the left from the beginning.
  1325. gfx::Rect total_bounds = GetGridEffectiveBounds();
  1326. total_bounds.Inset(GetGridInsetsImpl(total_bounds));
  1327. float rightmost_window_right = 0;
  1328. for (const auto& item : window_list_) {
  1329. const gfx::RectF bounds = item->target_bounds();
  1330. if (rightmost_window_right < bounds.right())
  1331. rightmost_window_right = bounds.right();
  1332. item->set_scrolling_bounds(bounds);
  1333. }
  1334. // |rightmost_window_right| may have been modified by an earlier scroll.
  1335. // |scroll_offset_| is added to adjust for that.
  1336. rightmost_window_right -= scroll_offset_;
  1337. scroll_offset_min_ = total_bounds.right() - rightmost_window_right;
  1338. if (scroll_offset_min_ > 0.f)
  1339. scroll_offset_min_ = 0.f;
  1340. presentation_time_recorder_ = CreatePresentationTimeHistogramRecorder(
  1341. const_cast<ui::Compositor*>(root_window()->layer()->GetCompositor()),
  1342. kOverviewScrollHistogram, kOverviewScrollMaxLatencyHistogram);
  1343. }
  1344. bool OverviewGrid::UpdateScrollOffset(float delta) {
  1345. float new_scroll_offset = scroll_offset_;
  1346. new_scroll_offset += delta;
  1347. new_scroll_offset = base::clamp(new_scroll_offset, scroll_offset_min_, 0.f);
  1348. // For flings, we want to return false if we hit one of the edges, which is
  1349. // when |new_scroll_offset| is exactly 0.f or |scroll_offset_min_|.
  1350. const bool in_range =
  1351. new_scroll_offset < 0.f && new_scroll_offset > scroll_offset_min_;
  1352. if (new_scroll_offset == scroll_offset_)
  1353. return in_range;
  1354. // Update the bounds of the items which are currently visible on screen.
  1355. for (const auto& item : window_list_) {
  1356. absl::optional<gfx::RectF> scrolling_bounds_optional =
  1357. item->scrolling_bounds();
  1358. DCHECK(scrolling_bounds_optional);
  1359. const gfx::RectF previous_bounds = scrolling_bounds_optional.value();
  1360. gfx::RectF new_bounds = previous_bounds;
  1361. new_bounds.Offset(new_scroll_offset - scroll_offset_, 0.f);
  1362. item->set_scrolling_bounds(new_bounds);
  1363. if (gfx::RectF(GetGridEffectiveBounds()).Intersects(new_bounds) ||
  1364. gfx::RectF(GetGridEffectiveBounds()).Intersects(previous_bounds)) {
  1365. item->SetBounds(new_bounds, OVERVIEW_ANIMATION_NONE);
  1366. }
  1367. }
  1368. scroll_offset_ = new_scroll_offset;
  1369. DCHECK(presentation_time_recorder_);
  1370. presentation_time_recorder_->RequestNext();
  1371. return in_range;
  1372. }
  1373. void OverviewGrid::EndScroll() {
  1374. Shell::Get()->overview_controller()->UnpauseOcclusionTracker(
  1375. kOcclusionUnpauseDurationForScroll);
  1376. for (const auto& item : window_list_)
  1377. item->set_scrolling_bounds(absl::nullopt);
  1378. presentation_time_recorder_.reset();
  1379. if (!overview_session_->is_shutting_down())
  1380. PositionWindows(/*animate=*/false);
  1381. }
  1382. int OverviewGrid::CalculateWidthAndMaybeSetUnclippedBounds(OverviewItem* item,
  1383. int height) {
  1384. const gfx::Size item_size(0, height);
  1385. gfx::SizeF target_size = item->GetTargetBoundsInScreen().size();
  1386. float scale = item->GetItemScale(item_size);
  1387. OverviewGridWindowFillMode grid_fill_mode = item->GetWindowDimensionsType();
  1388. // The drop target, unlike the other windows has its bounds set directly, so
  1389. // |GetTargetBoundsInScreen()| won't return the value we want. Instead, get
  1390. // the scale from the window it was meant to be a placeholder for.
  1391. if (IsDropTargetWindow(item->GetWindow())) {
  1392. aura::Window* dragged_window = nullptr;
  1393. OverviewItem* grid_dragged_item =
  1394. overview_session_->window_drag_controller()
  1395. ? overview_session_->window_drag_controller()->item()
  1396. : nullptr;
  1397. if (grid_dragged_item)
  1398. dragged_window = grid_dragged_item->GetWindow();
  1399. else if (dragged_window_)
  1400. dragged_window = dragged_window_;
  1401. if (dragged_window && dragged_window->parent()) {
  1402. const gfx::Size work_area_size =
  1403. screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
  1404. root_window_)
  1405. .size();
  1406. if (WindowState::Get(dragged_window)->IsMaximized()) {
  1407. grid_fill_mode = ScopedOverviewTransformWindow::GetWindowDimensionsType(
  1408. work_area_size);
  1409. target_size = gfx::SizeF(work_area_size);
  1410. } else {
  1411. gfx::Size dragged_window_size = dragged_window->bounds().size();
  1412. // If the drag started from a different root window, |dragged_window|
  1413. // may not fit into the work area of |root_window_|. Then if
  1414. // |dragged_window| is dropped into this grid, |dragged_window| will
  1415. // shrink to fit into this work area. The drop target shall reflect
  1416. // that.
  1417. dragged_window_size.SetToMin(work_area_size);
  1418. grid_fill_mode = ScopedOverviewTransformWindow::GetWindowDimensionsType(
  1419. dragged_window_size);
  1420. target_size = ::ash::GetTargetBoundsInScreen(dragged_window).size();
  1421. target_size.SetToMin(gfx::SizeF(work_area_size));
  1422. }
  1423. const gfx::SizeF inset_size(0, height);
  1424. scale = ScopedOverviewTransformWindow::GetItemScale(
  1425. target_size, inset_size,
  1426. dragged_window->GetProperty(aura::client::kTopViewInset),
  1427. kHeaderHeightDp);
  1428. }
  1429. }
  1430. int width = std::max(1, base::ClampFloor(target_size.width() * scale));
  1431. switch (grid_fill_mode) {
  1432. case OverviewGridWindowFillMode::kLetterBoxed:
  1433. width = kExtremeWindowRatioThreshold * height;
  1434. break;
  1435. case OverviewGridWindowFillMode::kPillarBoxed:
  1436. width = height / kExtremeWindowRatioThreshold;
  1437. break;
  1438. default:
  1439. break;
  1440. }
  1441. // Get the bounds of the item if there is a snapped window or a window
  1442. // about to be snapped. If the height is less than that of the header, there
  1443. // is nothing from the original window to be shown and nothing to be clipped.
  1444. absl::optional<gfx::RectF> split_view_bounds =
  1445. GetSplitviewBoundsMaintainingAspectRatio();
  1446. if (!split_view_bounds || split_view_bounds->height() < kHeaderHeightDp) {
  1447. item->set_unclipped_size(absl::nullopt);
  1448. return width;
  1449. }
  1450. // Perform horizontal clipping if the window's aspect ratio is wider than the
  1451. // split view bounds aspect ratio, and vertical clipping otherwise.
  1452. const float aspect_ratio =
  1453. target_size.width() /
  1454. (target_size.height() -
  1455. item->GetWindow()->GetProperty(aura::client::kTopViewInset));
  1456. const float target_aspect_ratio =
  1457. split_view_bounds->width() / split_view_bounds->height();
  1458. const bool clip_horizontally = aspect_ratio > target_aspect_ratio;
  1459. const int window_height = height - kHeaderHeightDp;
  1460. gfx::Size unclipped_size;
  1461. if (clip_horizontally) {
  1462. unclipped_size.set_width(width);
  1463. unclipped_size.set_height(height);
  1464. // For horizontal clipping, shrink |width| so that the aspect ratio matches
  1465. // that of |split_view_bounds|.
  1466. width = std::max(1, base::ClampFloor(target_aspect_ratio * window_height));
  1467. } else {
  1468. // For vertical clipping, we want |height| to stay the same, so calculate
  1469. // what the unclipped height would be based on |split_view_bounds|.
  1470. // Find the width so that it matches height and matches the aspect ratio of
  1471. // |split_view_bounds|.
  1472. width = split_view_bounds->width() * window_height /
  1473. split_view_bounds->height();
  1474. // The unclipped height is the height which matches |width| but keeps the
  1475. // aspect ratio of |target_bounds|. Clipping takes the overview header into
  1476. // account, so add that back in.
  1477. const int unclipped_height =
  1478. width * target_size.height() / target_size.width();
  1479. unclipped_size.set_width(width);
  1480. unclipped_size.set_height(unclipped_height + kHeaderHeightDp);
  1481. }
  1482. DCHECK(!unclipped_size.IsEmpty());
  1483. item->set_unclipped_size(absl::make_optional(unclipped_size));
  1484. return width;
  1485. }
  1486. bool OverviewGrid::IsDeskNameBeingModified() const {
  1487. return desks_bar_view_ && desks_bar_view_->IsDeskNameBeingModified();
  1488. }
  1489. void OverviewGrid::CommitNameChanges() {
  1490. // The desks bar widget may not be ready, since it is created asynchronously
  1491. // later when the entering overview animations finish.
  1492. if (desks_widget_)
  1493. DeskNameView::CommitChanges(desks_widget_.get());
  1494. // The templates grid may not be shown.
  1495. if (saved_desk_library_widget_)
  1496. SavedDeskNameView::CommitChanges(saved_desk_library_widget_.get());
  1497. }
  1498. void OverviewGrid::ShowDesksTemplatesGrid(bool was_zero_state) {
  1499. if (!saved_desk_library_widget_) {
  1500. saved_desk_library_widget_ =
  1501. SavedDeskLibraryView::CreateSavedDeskLibraryWidget(root_window_);
  1502. }
  1503. for (auto& overview_mode_item : window_list_)
  1504. overview_mode_item->HideForDesksTemplatesGrid(/*animate=*/true);
  1505. // There may be an existing animation in progress triggered by
  1506. // `HideDeskTemplatesGrid()` below, which animates a widget to 0.f before
  1507. // calling `OnDesksTemplatesGridFadedOut()` to hide the widget on animation
  1508. // end. Stop animating so that the callbacks associated get fired, otherwise
  1509. // we may end up trying to show a widget that's already shown.
  1510. // `StopAnimating()` is a no-op if there is no animation in progress.
  1511. saved_desk_library_widget_->GetLayer()->GetAnimator()->StopAnimating();
  1512. saved_desk_library_widget_->Show();
  1513. // Fade in the widget from its current opacity.
  1514. PerformFadeInLayer(saved_desk_library_widget_->GetLayer(),
  1515. /*animate=*/true);
  1516. UpdateSaveDeskButtons();
  1517. if (was_zero_state) {
  1518. desks_bar_view_->UpdateNewMiniViews(/*initializing_bar_view=*/false,
  1519. /*expanded_desks_bar_button=*/true);
  1520. }
  1521. desks_bar_view_->UpdateButtonsForDesksTemplatesGrid();
  1522. }
  1523. void OverviewGrid::HideDesksTemplatesGrid(bool exit_overview) {
  1524. if (!saved_desk_library_widget_)
  1525. return;
  1526. auto* grid_layer = saved_desk_library_widget_->GetLayer();
  1527. const bool already_hiding_grid = grid_layer->GetAnimator()->is_animating() &&
  1528. grid_layer->GetTargetOpacity() == 0.f;
  1529. if (already_hiding_grid)
  1530. return;
  1531. if (exit_overview && overview_session_->enter_exit_overview_type() ==
  1532. OverviewEnterExitType::kImmediateExit) {
  1533. // Since we're immediately exiting, we don't need to animate anything.
  1534. // Reshow the overview items and let the `saved_desk_library_widget_`
  1535. // handle its own destruction.
  1536. for (auto& overview_mode_item : window_list_)
  1537. overview_mode_item->RevertHideForDesksTemplatesGrid(/*animate=*/false);
  1538. return;
  1539. }
  1540. if (exit_overview) {
  1541. // Un-hide the overview mode items.
  1542. for (auto& overview_mode_item : window_list_)
  1543. overview_mode_item->RevertHideForDesksTemplatesGrid(/*animate=*/true);
  1544. // Disable the `saved_desk_library_widget_`'s event targeting so it can't
  1545. // get any events during the animation.
  1546. saved_desk_library_widget_->GetNativeWindow()->SetEventTargetingPolicy(
  1547. aura::EventTargetingPolicy::kNone);
  1548. FadeOutWidgetFromOverview(
  1549. std::move(saved_desk_library_widget_),
  1550. OVERVIEW_ANIMATION_EXIT_OVERVIEW_MODE_DESKS_TEMPLATES_GRID_FADE_OUT);
  1551. return;
  1552. }
  1553. // Fade out the `saved_desk_library_widget_` and then when its animation is
  1554. // done fade in the supporting widgets and revert the overview item hides.
  1555. PerformFadeOutLayer(
  1556. saved_desk_library_widget_->GetLayer(),
  1557. /*animate=*/true,
  1558. base::BindOnce(&OverviewGrid::OnDesksTemplatesGridFadedOut,
  1559. weak_ptr_factory_.GetWeakPtr()));
  1560. }
  1561. bool OverviewGrid::IsShowingDesksTemplatesGrid() const {
  1562. return saved_desk_library_widget_ && saved_desk_library_widget_->IsVisible();
  1563. }
  1564. bool OverviewGrid::IsTemplateNameBeingModified() const {
  1565. if (auto* library_view = GetSavedDeskLibraryView()) {
  1566. for (auto* grid_view : library_view->grid_views()) {
  1567. if (grid_view->IsTemplateNameBeingModified())
  1568. return true;
  1569. }
  1570. }
  1571. return false;
  1572. }
  1573. void OverviewGrid::UpdateNoWindowsWidget(bool no_items) {
  1574. // Hide the widget if there is an item in overview or the desk templates grid
  1575. // is visible.
  1576. if (!no_items || IsShowingDesksTemplatesGrid()) {
  1577. no_windows_widget_.reset();
  1578. return;
  1579. }
  1580. if (!no_windows_widget_) {
  1581. // Create and fade in the widget.
  1582. RoundedLabelWidget::InitParams params;
  1583. params.name = "OverviewNoWindowsLabel";
  1584. params.horizontal_padding = kNoItemsIndicatorHorizontalPaddingDp;
  1585. params.vertical_padding = kNoItemsIndicatorVerticalPaddingDp;
  1586. params.rounding_dp = kNoItemsIndicatorRoundingDp;
  1587. params.preferred_height = kNoItemsIndicatorHeightDp;
  1588. params.message_id = IDS_ASH_OVERVIEW_NO_RECENT_ITEMS;
  1589. params.parent =
  1590. root_window_->GetChildById(desks_util::GetActiveDeskContainerId());
  1591. params.hide_in_mini_view = true;
  1592. no_windows_widget_ = std::make_unique<RoundedLabelWidget>();
  1593. no_windows_widget_->Init(std::move(params));
  1594. aura::Window* widget_window = no_windows_widget_->GetNativeWindow();
  1595. widget_window->parent()->StackChildAtBottom(widget_window);
  1596. widget_window->SetId(kShellWindowId_OverviewNoWindowsLabelWindow);
  1597. ScopedOverviewAnimationSettings settings(OVERVIEW_ANIMATION_NO_RECENTS_FADE,
  1598. widget_window);
  1599. no_windows_widget_->SetOpacity(1.f);
  1600. }
  1601. RefreshNoWindowsWidgetBounds(/*animate=*/false);
  1602. }
  1603. void OverviewGrid::RefreshNoWindowsWidgetBounds(bool animate) {
  1604. if (!no_windows_widget_)
  1605. return;
  1606. no_windows_widget_->SetBoundsCenteredIn(GetGridEffectiveBounds(), animate);
  1607. }
  1608. void OverviewGrid::UpdateSaveDeskButtons() {
  1609. // TODO(crbug.com/1275282): The button should be updated whenever the
  1610. // overview grid changes, i.e. switches between active desks and/or the
  1611. // templates grid. This will be needed when we make it so that switching desks
  1612. // keeps us in overview mode.
  1613. if (!saved_desk_util::IsSavedDesksEnabled())
  1614. return;
  1615. // If there is only one item and it is animating to close, hide the widget as
  1616. // the closing window cannot be saved as part of a template.
  1617. const bool no_items =
  1618. window_list_.empty() ||
  1619. (window_list_.size() == 1u && window_list_.front()->animating_to_close());
  1620. // Do not create or show the save desk buttons if there are no
  1621. // windows in this grid, during a window drag or in tablet mode, the desks
  1622. // templates grid is visible, or if the desks bar hasn't been created yet.
  1623. const bool target_visible =
  1624. !no_items && !overview_session_->GetCurrentDraggedOverviewItem() &&
  1625. !Shell::Get()->tablet_mode_controller()->InTabletMode() &&
  1626. !IsShowingDesksTemplatesGrid() && desks_widget_;
  1627. const bool visibility_changed =
  1628. target_visible != IsSaveDeskButtonContainerVisible();
  1629. // Adds or removes the widget from the accessibility focus order when exiting
  1630. // the scope. Skip the update if the widget's visibility hasn't changed.
  1631. base::ScopedClosureRunner update_accessibility_focus(base::BindOnce(
  1632. [](OverviewSession* session, bool widget_visibility_changed) {
  1633. if (widget_visibility_changed)
  1634. session->UpdateAccessibilityFocus();
  1635. },
  1636. overview_session_, visibility_changed));
  1637. if (!target_visible) {
  1638. if (visibility_changed && save_desk_button_container_widget_) {
  1639. PerformFadeOutLayer(
  1640. save_desk_button_container_widget_->GetLayer(),
  1641. /*animate=*/true,
  1642. base::BindOnce(&OverviewGrid::OnSaveDeskButtonContainerFadedOut,
  1643. weak_ptr_factory_.GetWeakPtr()));
  1644. }
  1645. return;
  1646. }
  1647. // Create `save_desk_button_container_widget_`.
  1648. if (!save_desk_button_container_widget_) {
  1649. save_desk_button_container_widget_ =
  1650. CreateSaveDeskButtonContainerWidget(root_window_);
  1651. save_desk_button_container_widget_->SetContentsView(
  1652. std::make_unique<SaveDeskTemplateButtonContainer>(
  1653. base::BindRepeating(
  1654. &OverviewGrid::OnSaveDeskAsTemplateButtonPressed,
  1655. weak_ptr_factory_.GetWeakPtr()),
  1656. base::BindRepeating(&OverviewGrid::OnSaveDeskForLaterButtonPressed,
  1657. weak_ptr_factory_.GetWeakPtr())));
  1658. }
  1659. // There may be an existing animation in progress triggered by
  1660. // `PerformFadeOutLayer()` above, which animates a widget to 0.f before
  1661. // calling `OnSaveDeskButtonContainerFadedOut()` to hide the widget on
  1662. // animation end. Stop animating so that the callbacks associated get fired,
  1663. // otherwise we may end up trying to show a widget that's already shown.
  1664. // `StopAnimating()` is a no-op if there is no animation in progress.
  1665. if (visibility_changed) {
  1666. save_desk_button_container_widget_->GetLayer()
  1667. ->GetAnimator()
  1668. ->StopAnimating();
  1669. save_desk_button_container_widget_->Show();
  1670. PerformFadeInLayer(save_desk_button_container_widget_->GetLayer(),
  1671. /*animate=*/true);
  1672. }
  1673. // Enable/disable button and update tooltip.
  1674. const SavedDeskPresenter* saved_desk_presenter =
  1675. overview_session_->saved_desk_presenter();
  1676. SaveDeskTemplateButtonContainer* container =
  1677. static_cast<SaveDeskTemplateButtonContainer*>(
  1678. save_desk_button_container_widget_->GetContentsView());
  1679. container->UpdateButtonEnableStateAndTooltip(
  1680. SaveDeskTemplateButton::Type::kSaveAsTemplate,
  1681. saved_desk_presenter->GetEntryCount(DeskTemplateType::kTemplate),
  1682. saved_desk_presenter->GetMaxEntryCount(DeskTemplateType::kTemplate),
  1683. num_incognito_windows_, num_unsupported_windows_, size());
  1684. container->UpdateButtonEnableStateAndTooltip(
  1685. SaveDeskTemplateButton::Type::kSaveForLater,
  1686. saved_desk_presenter->GetEntryCount(DeskTemplateType::kSaveAndRecall),
  1687. saved_desk_presenter->GetMaxEntryCount(DeskTemplateType::kSaveAndRecall),
  1688. num_incognito_windows_, num_unsupported_windows_, size());
  1689. // Set the widget position above the overview item window and default width
  1690. // and height.
  1691. gfx::RectF first_overview_item_bounds;
  1692. if (window_list_.front()->animating_to_close()) {
  1693. DCHECK_GT(window_list_.size(), 1u);
  1694. first_overview_item_bounds = window_list_[1]->target_bounds();
  1695. } else {
  1696. first_overview_item_bounds = window_list_.front()->target_bounds();
  1697. }
  1698. // Animate the widget so it moves with the items. The widget's size isn't
  1699. // changing, so its ok to use a bounds animation as opposed to a transform
  1700. // animation. If the visibility has changed, skip the bounds animation and use
  1701. // the fade animation from above. Align the widget so it is visually aligned
  1702. // with the first overview item, which has an invisible border of
  1703. // `kWindowMargin` thickness.
  1704. ScopedOverviewAnimationSettings settings(
  1705. visibility_changed ? OVERVIEW_ANIMATION_NONE
  1706. : OVERVIEW_ANIMATION_LAYOUT_OVERVIEW_ITEMS_IN_OVERVIEW,
  1707. save_desk_button_container_widget_->GetNativeWindow());
  1708. gfx::Point available_origin =
  1709. gfx::ToRoundedPoint(first_overview_item_bounds.origin()) +
  1710. gfx::Vector2d(kWindowMargin, -kSaveDeskAsTemplateOverviewItemSpacingDp);
  1711. save_desk_button_container_widget_->SetBounds(gfx::Rect(
  1712. available_origin, save_desk_button_container_widget_->GetContentsView()
  1713. ->GetPreferredSize()));
  1714. }
  1715. bool OverviewGrid::IsSaveDeskButtonContainerVisible() const {
  1716. // The widget may be visible but in the process of fading away. We treat that
  1717. // as not visible.
  1718. return save_desk_button_container_widget_ &&
  1719. save_desk_button_container_widget_->IsVisible() &&
  1720. save_desk_button_container_widget_->GetLayer()->GetTargetOpacity() ==
  1721. 1.f;
  1722. }
  1723. bool OverviewGrid::IsSaveDeskAsTemplateButtonVisible() const {
  1724. if (!IsSaveDeskButtonContainerVisible())
  1725. return false;
  1726. SaveDeskTemplateButtonContainer* container =
  1727. static_cast<SaveDeskTemplateButtonContainer*>(
  1728. save_desk_button_container_widget_->GetContentsView());
  1729. return container->save_desk_as_template_button() &&
  1730. container->save_desk_as_template_button()->GetVisible();
  1731. }
  1732. bool OverviewGrid::IsSaveDeskForLaterButtonVisible() const {
  1733. if (!IsSaveDeskButtonContainerVisible())
  1734. return false;
  1735. SaveDeskTemplateButtonContainer* container =
  1736. static_cast<SaveDeskTemplateButtonContainer*>(
  1737. save_desk_button_container_widget_->GetContentsView());
  1738. return container->save_desk_for_later_button() &&
  1739. container->save_desk_for_later_button()->GetVisible();
  1740. }
  1741. SaveDeskTemplateButton* OverviewGrid::GetSaveDeskAsTemplateButton() const {
  1742. return save_desk_button_container_widget_
  1743. ? static_cast<SaveDeskTemplateButtonContainer*>(
  1744. save_desk_button_container_widget_->GetContentsView())
  1745. ->save_desk_as_template_button()
  1746. : nullptr;
  1747. }
  1748. SaveDeskTemplateButton* OverviewGrid::GetSaveDeskForLaterButton() const {
  1749. return save_desk_button_container_widget_
  1750. ? static_cast<SaveDeskTemplateButtonContainer*>(
  1751. save_desk_button_container_widget_->GetContentsView())
  1752. ->save_desk_for_later_button()
  1753. : nullptr;
  1754. }
  1755. SaveDeskTemplateButtonContainer*
  1756. OverviewGrid::GetSaveDeskTemplateButtonContainer() const {
  1757. return save_desk_button_container_widget_
  1758. ? static_cast<SaveDeskTemplateButtonContainer*>(
  1759. save_desk_button_container_widget_->GetContentsView())
  1760. : nullptr;
  1761. }
  1762. void OverviewGrid::OnSplitViewStateChanged(
  1763. SplitViewController::State previous_state,
  1764. SplitViewController::State state) {
  1765. // Do nothing if overview is being shutdown.
  1766. OverviewController* overview_controller = Shell::Get()->overview_controller();
  1767. if (!overview_controller->InOverviewSession())
  1768. return;
  1769. SplitViewController* split_view_controller =
  1770. SplitViewController::Get(root_window_);
  1771. const bool unsnappable_window_activated =
  1772. state == SplitViewController::State::kNoSnap &&
  1773. split_view_controller->end_reason() ==
  1774. SplitViewController::EndReason::kUnsnappableWindowActivated;
  1775. // If two windows were snapped to both sides of the screen or an unsnappable
  1776. // window was just activated, or we're in single split mode in clamshell mode
  1777. // and there is no window in overview, end overview mode and bail out.
  1778. if (state == SplitViewController::State::kBothSnapped ||
  1779. unsnappable_window_activated ||
  1780. (split_view_controller->InClamshellSplitViewMode() &&
  1781. overview_session_->IsEmpty())) {
  1782. overview_session_->RestoreWindowActivation(false);
  1783. overview_controller->EndOverview(OverviewEndAction::kSplitView);
  1784. return;
  1785. }
  1786. // Update the cannot snap warnings and adjust the grid bounds.
  1787. UpdateCannotSnapWarningVisibility(/*animate=*/true);
  1788. SetBoundsAndUpdatePositions(GetGridBoundsInScreen(root_window_),
  1789. /*ignored_items=*/{}, /*animate=*/false);
  1790. // If split view mode was ended, then activate the overview focus window, to
  1791. // match the behavior of entering overview mode in the beginning.
  1792. if (state == SplitViewController::State::kNoSnap)
  1793. wm::ActivateWindow(overview_session_->GetOverviewFocusWindow());
  1794. }
  1795. void OverviewGrid::OnSplitViewDividerPositionChanged() {
  1796. SetBoundsAndUpdatePositions(
  1797. GetGridBoundsInScreen(root_window_,
  1798. /*window_dragging_state=*/absl::nullopt,
  1799. /*divider_changed=*/true,
  1800. /*account_for_hotseat=*/true),
  1801. /*ignored_items=*/{}, /*animate=*/false);
  1802. }
  1803. void OverviewGrid::OnScreenCopiedBeforeRotation() {
  1804. Shell::Get()->overview_controller()->PauseOcclusionTracker();
  1805. for (auto& window : window_list()) {
  1806. window->set_disable_mask(true);
  1807. window->UpdateRoundedCornersAndShadow();
  1808. window->StopWidgetAnimation();
  1809. }
  1810. }
  1811. void OverviewGrid::OnScreenRotationAnimationFinished(
  1812. ScreenRotationAnimator* animator,
  1813. bool canceled) {
  1814. for (auto& window : window_list())
  1815. window->set_disable_mask(false);
  1816. Shell::Get()->overview_controller()->DelayedUpdateRoundedCornersAndShadow();
  1817. Shell::Get()->overview_controller()->UnpauseOcclusionTracker(
  1818. kOcclusionUnpauseDurationForRotation);
  1819. }
  1820. void OverviewGrid::OnWallpaperChanging() {
  1821. grid_event_handler_.reset();
  1822. }
  1823. void OverviewGrid::OnWallpaperChanged() {
  1824. grid_event_handler_ = std::make_unique<OverviewGridEventHandler>(this);
  1825. }
  1826. SavedDeskLibraryView* OverviewGrid::GetSavedDeskLibraryView() const {
  1827. return saved_desk_library_widget_
  1828. ? static_cast<SavedDeskLibraryView*>(
  1829. saved_desk_library_widget_->GetContentsView())
  1830. : nullptr;
  1831. }
  1832. void OverviewGrid::MaybeInitDesksWidget() {
  1833. if (!desks_util::ShouldDesksBarBeCreated() || desks_widget_)
  1834. return;
  1835. desks_widget_ =
  1836. DesksBarView::CreateDesksWidget(root_window_, GetDesksWidgetBounds());
  1837. // The following order of function calls is significant: SetContentsView()
  1838. // must be called before DesksBarView:: Init(). This is needed because the
  1839. // desks mini views need to access the widget to get the root window in order
  1840. // to know how to layout themselves.
  1841. desks_bar_view_ =
  1842. desks_widget_->SetContentsView(std::make_unique<DesksBarView>(this));
  1843. desks_bar_view_->Init();
  1844. desks_widget_->Show();
  1845. // TODO(afakhry): Check if we need to keep this as the bottom-most window in
  1846. // the container.
  1847. auto* window = desks_widget_->GetNativeWindow();
  1848. window->parent()->StackChildAtBottom(window);
  1849. UpdateSaveDeskButtons();
  1850. }
  1851. std::vector<gfx::RectF> OverviewGrid::GetWindowRects(
  1852. const base::flat_set<OverviewItem*>& ignored_items) {
  1853. gfx::Rect total_bounds = GetGridEffectiveBounds();
  1854. // Windows occupy vertically centered area with additional vertical insets.
  1855. total_bounds.Inset(GetGridInsetsImpl(total_bounds));
  1856. std::vector<gfx::RectF> rects;
  1857. // Keep track of the lowest coordinate.
  1858. int max_bottom = total_bounds.y();
  1859. // Right bound of the narrowest row.
  1860. int min_right = total_bounds.right();
  1861. // Right bound of the widest row.
  1862. int max_right = total_bounds.x();
  1863. // Keep track of the difference between the narrowest and the widest row.
  1864. // Initially this is set to the worst it can ever be assuming the windows fit.
  1865. int width_diff = total_bounds.width();
  1866. // Initially allow the windows to occupy all available width. Shrink this
  1867. // available space horizontally to find the breakdown into rows that achieves
  1868. // the minimal |width_diff|.
  1869. int right_bound = total_bounds.right();
  1870. // Determine the optimal height bisecting between |low_height| and
  1871. // |high_height|. Once this optimal height is known, |height_fixed| is set to
  1872. // true and the rows are balanced by repeatedly squeezing the widest row to
  1873. // cause windows to overflow to the subsequent rows.
  1874. int low_height = 2 * kWindowMargin;
  1875. int high_height = std::max(low_height, total_bounds.height() + 1);
  1876. int height = 0.5 * (low_height + high_height);
  1877. bool height_fixed = false;
  1878. // Repeatedly try to fit the windows |rects| within |right_bound|.
  1879. // If a maximum |height| is found such that all window |rects| fit, this
  1880. // fitting continues while shrinking the |right_bound| in order to balance the
  1881. // rows. If the windows fit the |right_bound| would have been decremented at
  1882. // least once so it needs to be incremented once before getting out of this
  1883. // loop and one additional pass made to actually fit the |rects|.
  1884. // If the |rects| cannot fit (e.g. there are too many windows) the bisection
  1885. // will still finish and we might increment the |right_bound| once pixel extra
  1886. // which is acceptable since there is an unused margin on the right.
  1887. bool make_last_adjustment = false;
  1888. while (true) {
  1889. gfx::Rect overview_mode_bounds(total_bounds);
  1890. overview_mode_bounds.set_width(right_bound - total_bounds.x());
  1891. bool windows_fit = FitWindowRectsInBounds(
  1892. overview_mode_bounds, std::min(kMaxHeight, height), ignored_items,
  1893. &rects, &max_bottom, &min_right, &max_right);
  1894. if (height_fixed) {
  1895. if (!windows_fit) {
  1896. // Revert the previous change to |right_bound| and do one last pass.
  1897. right_bound++;
  1898. make_last_adjustment = true;
  1899. break;
  1900. }
  1901. // Break if all the windows are zero-width at the current scale.
  1902. if (max_right <= total_bounds.x())
  1903. break;
  1904. } else {
  1905. // Find the optimal row height bisecting between |low_height| and
  1906. // |high_height|.
  1907. if (windows_fit)
  1908. low_height = height;
  1909. else
  1910. high_height = height;
  1911. height = 0.5 * (low_height + high_height);
  1912. // When height can no longer be improved, start balancing the rows.
  1913. if (height == low_height)
  1914. height_fixed = true;
  1915. }
  1916. if (windows_fit && height_fixed) {
  1917. if (max_right - min_right <= width_diff) {
  1918. // Row alignment is getting better. Try to shrink the |right_bound| in
  1919. // order to squeeze the widest row.
  1920. right_bound = max_right - 1;
  1921. width_diff = max_right - min_right;
  1922. } else {
  1923. // Row alignment is getting worse.
  1924. // Revert the previous change to |right_bound| and do one last pass.
  1925. right_bound++;
  1926. make_last_adjustment = true;
  1927. break;
  1928. }
  1929. }
  1930. }
  1931. // Once the windows in |window_list_| no longer fit, the change to
  1932. // |right_bound| was reverted. Perform one last pass to position the |rects|.
  1933. if (make_last_adjustment) {
  1934. gfx::Rect overview_mode_bounds(total_bounds);
  1935. overview_mode_bounds.set_width(right_bound - total_bounds.x());
  1936. FitWindowRectsInBounds(overview_mode_bounds, std::min(kMaxHeight, height),
  1937. ignored_items, &rects, &max_bottom, &min_right,
  1938. &max_right);
  1939. }
  1940. gfx::Vector2dF offset(0, (total_bounds.bottom() - max_bottom) / 2.f);
  1941. for (size_t i = 0; i < rects.size(); ++i)
  1942. rects[i] += offset;
  1943. return rects;
  1944. }
  1945. std::vector<gfx::RectF> OverviewGrid::GetWindowRectsForTabletModeLayout(
  1946. const base::flat_set<OverviewItem*>& ignored_items) {
  1947. gfx::Rect total_bounds = GetGridEffectiveBounds();
  1948. // Windows occupy vertically centered area with additional vertical insets.
  1949. total_bounds.Inset(GetGridInsetsImpl(total_bounds));
  1950. total_bounds.Inset(
  1951. gfx::Insets::TLBR(kTabletModeOverviewItemTopPaddingDp, 0, 0, 0));
  1952. // |scroll_offset_min_| may be changed on positioning (either by closing
  1953. // windows or display changes). Recalculate it and clamp |scroll_offset_|, so
  1954. // that the items are always aligned left or right.
  1955. float rightmost_window_right = 0;
  1956. for (const auto& item : window_list_) {
  1957. if (ShouldExcludeItemFromGridLayout(item.get(), ignored_items))
  1958. continue;
  1959. rightmost_window_right =
  1960. std::max(rightmost_window_right, item->target_bounds().right());
  1961. }
  1962. // `rightmost_window_right` may have been modified by an earlier scroll.
  1963. // `scroll_offset_` is added to adjust for that. If `rightmost_window_right`
  1964. // is less than `total_bounds.right()`, the grid cannot be scrolled. Set
  1965. // `scroll_offset_min_` to 0 so that `base::clamp()` is happy.
  1966. rightmost_window_right -= scroll_offset_;
  1967. scroll_offset_min_ = total_bounds.right() - rightmost_window_right;
  1968. if (scroll_offset_min_ > 0.f)
  1969. scroll_offset_min_ = 0.f;
  1970. scroll_offset_ = base::clamp(scroll_offset_, scroll_offset_min_, 0.f);
  1971. // Map which contains up to |kTabletLayoutRow| entries with information on the
  1972. // last items right bound per row. Used so we can place the next item directly
  1973. // next to the last item. The key is the y-value of the row, and the value is
  1974. // the rightmost x-value.
  1975. base::flat_map<float, float> right_edge_map;
  1976. // Since the number of rows is limited, windows are laid out column-wise so
  1977. // that the most recently used windows are displayed first. When the dragged
  1978. // item becomes an |ignored_item|, move the other windows accordingly.
  1979. // |window_position| matches the positions of the windows' indexes from
  1980. // |window_list_|. However, if a window turns out to be an ignored item,
  1981. // |window_position| remains where the item was as to then reposition the
  1982. // other window's bounds in place of that item.
  1983. const int height = total_bounds.height() / kTabletLayoutRow;
  1984. int window_position = 0;
  1985. std::vector<gfx::RectF> rects;
  1986. for (size_t i = 0; i < window_list_.size(); ++i) {
  1987. OverviewItem* item = window_list_[i].get();
  1988. if (ShouldExcludeItemFromGridLayout(item, ignored_items)) {
  1989. rects.push_back(gfx::RectF());
  1990. continue;
  1991. }
  1992. // Calculate the width and y position of the item.
  1993. const int width =
  1994. CalculateWidthAndMaybeSetUnclippedBounds(window_list_[i].get(), height);
  1995. const int y =
  1996. height * (window_position % kTabletLayoutRow) + total_bounds.y();
  1997. // Use the right bounds of the item next to in the row as the x position, if
  1998. // that item exists.
  1999. const int x = right_edge_map.contains(y)
  2000. ? right_edge_map[y]
  2001. : total_bounds.x() + scroll_offset_;
  2002. right_edge_map[y] = x + width;
  2003. DCHECK_LE(static_cast<int>(right_edge_map.size()), kTabletLayoutRow);
  2004. const gfx::RectF bounds(x, y, width, height);
  2005. rects.push_back(bounds);
  2006. ++window_position;
  2007. }
  2008. return rects;
  2009. }
  2010. bool OverviewGrid::FitWindowRectsInBounds(
  2011. const gfx::Rect& bounds,
  2012. int height,
  2013. const base::flat_set<OverviewItem*>& ignored_items,
  2014. std::vector<gfx::RectF>* out_rects,
  2015. int* out_max_bottom,
  2016. int* out_min_right,
  2017. int* out_max_right) {
  2018. const size_t window_count = window_list_.size();
  2019. out_rects->resize(window_count);
  2020. // Start in the top-left corner of |bounds|.
  2021. int left = bounds.x();
  2022. int top = bounds.y();
  2023. // Keep track of the lowest coordinate.
  2024. *out_max_bottom = bounds.y();
  2025. // Right bound of the narrowest row.
  2026. *out_min_right = bounds.right();
  2027. // Right bound of the widest row.
  2028. *out_max_right = bounds.x();
  2029. // All elements are of same height and only the height is necessary to
  2030. // determine each item's scale.
  2031. for (size_t i = 0u; i < window_count; ++i) {
  2032. if (ShouldExcludeItemFromGridLayout(window_list_[i].get(), ignored_items))
  2033. continue;
  2034. int width = CalculateWidthAndMaybeSetUnclippedBounds(window_list_[i].get(),
  2035. height) +
  2036. 2 * kWindowMargin;
  2037. int height_with_margin = height + 2 * kWindowMargin;
  2038. if (left + width > bounds.right()) {
  2039. // Move to the next row if possible.
  2040. if (*out_min_right > left)
  2041. *out_min_right = left;
  2042. if (*out_max_right < left)
  2043. *out_max_right = left;
  2044. top += height_with_margin;
  2045. // Check if the new row reaches the bottom or if the first item in the new
  2046. // row does not fit within the available width.
  2047. if (top + height_with_margin > bounds.bottom() ||
  2048. bounds.x() + width > bounds.right()) {
  2049. return false;
  2050. }
  2051. left = bounds.x();
  2052. }
  2053. // Position the current rect.
  2054. (*out_rects)[i] = gfx::RectF(left, top, width, height_with_margin);
  2055. // Increment horizontal position using sanitized positive |width|.
  2056. left += width;
  2057. *out_max_bottom = top + height_with_margin;
  2058. }
  2059. // Update the narrowest and widest row width for the last row.
  2060. if (*out_min_right > left)
  2061. *out_min_right = left;
  2062. if (*out_max_right < left)
  2063. *out_max_right = left;
  2064. return true;
  2065. }
  2066. size_t OverviewGrid::GetOverviewItemIndex(OverviewItem* item) const {
  2067. auto iter = std::find_if(window_list_.begin(), window_list_.end(),
  2068. base::MatchesUniquePtr(item));
  2069. DCHECK(iter != window_list_.end());
  2070. return iter - window_list_.begin();
  2071. }
  2072. size_t OverviewGrid::FindInsertionIndex(const aura::Window* window) {
  2073. const auto mru_windows =
  2074. Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
  2075. // As we iterate over the whole MRU window list, the windows in this grid
  2076. // will be encountered in the same order, but possibly with other windows in
  2077. // between. Ignore those other windows, and only increment `grid_item_index`
  2078. // when we reach the next window in this grid.
  2079. size_t grid_item_index = 0, mru_window_index = 0;
  2080. while (grid_item_index < size() && mru_window_index < mru_windows.size()) {
  2081. aura::Window* grid_item_window = window_list_[grid_item_index]->GetWindow();
  2082. aura::Window* mru_window = mru_windows[mru_window_index];
  2083. if (IsDropTargetWindow(grid_item_window) || mru_window == window)
  2084. return grid_item_index;
  2085. if (mru_window == grid_item_window)
  2086. grid_item_index++;
  2087. mru_window_index++;
  2088. }
  2089. // If there is no drop target window and `window` is not in the MRU window
  2090. // list, insert at the end.
  2091. return size();
  2092. }
  2093. void OverviewGrid::AddDraggedWindowIntoOverviewOnDragEnd(
  2094. aura::Window* dragged_window) {
  2095. DCHECK(overview_session_);
  2096. if (overview_session_->IsWindowInOverview(dragged_window))
  2097. return;
  2098. // Update the dragged window's bounds before adding it to overview. The
  2099. // dragged window might have resized to a smaller size if the drag
  2100. // happens on tab(s).
  2101. if (window_util::IsDraggingTabs(dragged_window)) {
  2102. const gfx::Rect old_bounds = dragged_window->bounds();
  2103. // We need to temporarily disable the dragged window's ability to merge
  2104. // into another window when changing the dragged window's bounds, so
  2105. // that the dragged window doesn't merge into another window because of
  2106. // its changed bounds.
  2107. dragged_window->SetProperty(chromeos::kCanAttachToAnotherWindowKey, false);
  2108. TabletModeWindowState::UpdateWindowPosition(
  2109. WindowState::Get(dragged_window),
  2110. WindowState::BoundsChangeAnimationType::kNone);
  2111. const gfx::Rect new_bounds = dragged_window->bounds();
  2112. if (old_bounds != new_bounds) {
  2113. // It's for smoother animation.
  2114. const gfx::Transform transform = gfx::TransformBetweenRects(
  2115. gfx::RectF(new_bounds), gfx::RectF(old_bounds));
  2116. dragged_window->SetTransform(transform);
  2117. }
  2118. dragged_window->ClearProperty(chromeos::kCanAttachToAnotherWindowKey);
  2119. }
  2120. overview_session_->AddItemInMruOrder(dragged_window, /*reposition=*/false,
  2121. /*animate=*/false, /*restack=*/true,
  2122. /*use_spawn_animation=*/false);
  2123. }
  2124. gfx::Rect OverviewGrid::GetDesksWidgetBounds() const {
  2125. gfx::Rect desks_widget_screen_bounds = bounds_;
  2126. desks_widget_screen_bounds.set_height(GetDesksBarHeight());
  2127. // Shift the widget down to make room for the splitview indicator guidance
  2128. // when it's shown at the top of the screen and no other windows are snapped.
  2129. if (split_view_drag_indicators_ &&
  2130. split_view_drag_indicators_->current_window_dragging_state() ==
  2131. SplitViewDragIndicators::WindowDraggingState::kFromOverview &&
  2132. !SplitViewController::IsLayoutHorizontal(root_window_) &&
  2133. !SplitViewController::Get(root_window_)->InSplitViewMode()) {
  2134. desks_widget_screen_bounds.Offset(
  2135. 0, split_view_drag_indicators_->GetLeftHighlightViewBounds().height() +
  2136. 2 * kHighlightScreenEdgePaddingDp);
  2137. }
  2138. return screen_util::SnapBoundsToDisplayEdge(desks_widget_screen_bounds,
  2139. root_window_);
  2140. }
  2141. void OverviewGrid::UpdateCannotSnapWarningVisibility(bool animate) {
  2142. for (auto& overview_mode_item : window_list_)
  2143. overview_mode_item->UpdateCannotSnapWarningVisibility(animate);
  2144. }
  2145. void OverviewGrid::OnSaveDeskAsTemplateButtonPressed() {
  2146. overview_session_->saved_desk_presenter()->MaybeSaveActiveDeskAsTemplate(
  2147. DeskTemplateType::kTemplate,
  2148. save_desk_button_container_widget_->GetNativeWindow()->GetRootWindow());
  2149. }
  2150. void OverviewGrid::OnSaveDeskForLaterButtonPressed() {
  2151. overview_session_->saved_desk_presenter()->MaybeSaveActiveDeskAsTemplate(
  2152. DeskTemplateType::kSaveAndRecall,
  2153. save_desk_button_container_widget_->GetNativeWindow()->GetRootWindow());
  2154. }
  2155. void OverviewGrid::OnDesksTemplatesGridFadedOut() {
  2156. for (auto& overview_mode_item : window_list_)
  2157. overview_mode_item->RevertHideForDesksTemplatesGrid(/*animate=*/true);
  2158. saved_desk_library_widget_->Hide();
  2159. desks_bar_view_->UpdateButtonsForDesksTemplatesGrid();
  2160. desks_bar_view_->OnDesksTemplatesGridHidden();
  2161. UpdateSaveDeskButtons();
  2162. UpdateNoWindowsWidget(/*no_items=*/empty());
  2163. }
  2164. void OverviewGrid::OnSaveDeskButtonContainerFadedOut() {
  2165. save_desk_button_container_widget_->Hide();
  2166. }
  2167. void OverviewGrid::UpdateNumIncognitoUnsupportedWindows(aura::Window* window,
  2168. bool increment) {
  2169. if (!saved_desk_util::IsSavedDesksEnabled())
  2170. return;
  2171. // Count apps without full restore in `num_unsupported_windows_`. This is to
  2172. // ensure Save Template behavior, which will disable the button if
  2173. // num_unsupported_windows_ == window_list.size().
  2174. // TODO(crbug.com/1297710): Separate apps without Full Restore app id from
  2175. // unsupported apps so that they are not labeled as "Linux" apps in text.
  2176. const bool has_restore_id = !wm::GetTransientParent(window) &&
  2177. (Shell::Get()
  2178. ->overview_controller()
  2179. ->disable_app_id_check_for_saved_desks() ||
  2180. !full_restore::GetAppId(window).empty());
  2181. int addend = increment ? 1 : -1;
  2182. if (!DeskTemplate::IsAppTypeSupported(window) || !has_restore_id)
  2183. num_unsupported_windows_ += addend;
  2184. else if (Shell::Get()->desks_templates_delegate()->IsIncognitoWindow(window))
  2185. num_incognito_windows_ += addend;
  2186. DCHECK_GE(num_unsupported_windows_, 0);
  2187. DCHECK_GE(num_incognito_windows_, 0);
  2188. }
  2189. int OverviewGrid::GetDesksBarHeight() const {
  2190. const bool should_show_zero_state_desks_bar =
  2191. desks_bar_view_ ? desks_bar_view_->IsZeroState()
  2192. : !IsShowingDesksTemplatesGrid() &&
  2193. DesksController::Get()->GetNumberOfDesks() == 1;
  2194. return should_show_zero_state_desks_bar
  2195. ? DesksBarView::kZeroStateBarHeight
  2196. : DesksBarView::GetExpandedBarHeight(root_window_);
  2197. }
  2198. } // namespace ash