phantom_window_controller.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. // Copyright (c) 2012 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/workspace/phantom_window_controller.h"
  5. #include <math.h>
  6. #include "ash/public/cpp/shell_window_ids.h"
  7. #include "ash/public/cpp/style/scoped_light_mode_as_default.h"
  8. #include "ash/root_window_controller.h"
  9. #include "ash/strings/grit/ash_strings.h"
  10. #include "ash/style/ash_color_provider.h"
  11. #include "ash/wm/window_util.h"
  12. #include "ui/aura/window.h"
  13. #include "ui/base/l10n/l10n_util.h"
  14. #include "ui/compositor/layer.h"
  15. #include "ui/compositor/scoped_layer_animation_settings.h"
  16. #include "ui/display/screen.h"
  17. #include "ui/gfx/canvas.h"
  18. #include "ui/gfx/geometry/rect.h"
  19. #include "ui/views/animation/animation_builder.h"
  20. #include "ui/views/background.h"
  21. #include "ui/views/border.h"
  22. #include "ui/views/controls/label.h"
  23. #include "ui/views/highlight_border.h"
  24. #include "ui/views/layout/box_layout.h"
  25. #include "ui/views/view.h"
  26. #include "ui/views/widget/widget.h"
  27. #include "ui/wm/core/coordinate_conversion.h"
  28. #include "ui/wm/core/shadow_controller.h"
  29. #include "ui/wm/core/shadow_types.h"
  30. namespace ash {
  31. namespace {
  32. // The size of the phantom window at the beginning of the show animation in
  33. // relation to the size of the phantom window at the end of the animation.
  34. constexpr float kScrimStartBoundsRatio = 0.75f;
  35. // The duration of the phantom scrim entrance animation for size transform.
  36. constexpr base::TimeDelta kScrimEntranceSizeAnimationDurationMs =
  37. base::Milliseconds(150);
  38. // The duration of the phantom scrim entrance animation for opacity.
  39. constexpr base::TimeDelta kScrimEntranceOpacityAnimationDurationMs =
  40. base::Milliseconds(50);
  41. // The elevation of the shadow for the phantom window should match that of an
  42. // active window.
  43. // The shadow ninebox requires a minimum size to work well. See
  44. // ui/compositor_extra/shadow.cc
  45. constexpr int kMinWidthWithShadow = 2 * ::wm::kShadowElevationActiveWindow;
  46. constexpr int kMinHeightWithShadow = 4 * ::wm::kShadowElevationActiveWindow;
  47. // The top margin of the cue from the top of work area.
  48. constexpr int kMaximizeCueTopMargin = 32;
  49. constexpr int kMaximizeCueHeight = 36;
  50. constexpr int kMaximizeCueHorizontalInsets = 16;
  51. constexpr int kMaximizeCueVerticalInsets = 8;
  52. constexpr int kPhantomWindowCornerRadius = 4;
  53. constexpr gfx::Insets kPhantomWindowInsets(8);
  54. // The move up factor of starting y-position from the target position for
  55. // entrance animation of maximize cue.
  56. constexpr float kMaximizeCueEntraceAnimationYPositionMoveUpFactor = 0.5f;
  57. // The duration of the maximize cue entrance animation.
  58. constexpr base::TimeDelta kMaximizeCueEntranceAnimationDurationMs =
  59. base::Milliseconds(150);
  60. // The duration of the maximize cue exit animation.
  61. constexpr base::TimeDelta kMaximizeCueExitAnimationDurationMs =
  62. base::Milliseconds(100);
  63. // The delay of the maximize cue entrance and exit animation.
  64. constexpr base::TimeDelta kMaximizeCueAnimationDelayMs =
  65. base::Milliseconds(100);
  66. } // namespace
  67. // PhantomWindowController ----------------------------------------------------
  68. PhantomWindowController::PhantomWindowController(aura::Window* window)
  69. : window_(window) {}
  70. PhantomWindowController::~PhantomWindowController() = default;
  71. void PhantomWindowController::Show(const gfx::Rect& window_bounds_in_screen) {
  72. gfx::Rect target_bounds_in_screen = window_bounds_in_screen;
  73. target_bounds_in_screen.Inset(kPhantomWindowInsets);
  74. if (target_bounds_in_screen == target_bounds_in_screen_)
  75. return;
  76. target_bounds_in_screen_ = target_bounds_in_screen;
  77. // Computes starting size with a shrinking ratio |kScrimStartBoundsRatio|
  78. // from the actual size |target_bounds_in_screen| used for entrace animation
  79. // in `ShowPhantomWidget()`.
  80. gfx::Rect start_bounds_in_screen = target_bounds_in_screen_;
  81. int start_width = std::max(kMinWidthWithShadow,
  82. static_cast<int>(start_bounds_in_screen.width() *
  83. kScrimStartBoundsRatio));
  84. int start_height = std::max(kMinHeightWithShadow,
  85. static_cast<int>(start_bounds_in_screen.height() *
  86. kScrimStartBoundsRatio));
  87. start_bounds_in_screen.Inset(gfx::Insets::VH(
  88. floor((start_bounds_in_screen.height() - start_height) / 2.0f),
  89. floor((start_bounds_in_screen.width() - start_width) / 2.0f)));
  90. // Create a phantom widget with starting size so `ShowPhantomWidget()` can
  91. // animate from that current size to |target_bounds_in_screen|.
  92. phantom_widget_ = CreatePhantomWidget(
  93. window_util::GetRootWindowMatching(target_bounds_in_screen_),
  94. start_bounds_in_screen);
  95. ShowPhantomWidget();
  96. }
  97. void PhantomWindowController::HideMaximizeCue() {
  98. DCHECK(maximize_cue_widget_);
  99. ui::Layer* widget_layer = maximize_cue_widget_->GetLayer();
  100. views::AnimationBuilder()
  101. .SetPreemptionStrategy(
  102. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
  103. .Once()
  104. .SetDuration(kMaximizeCueAnimationDelayMs)
  105. .Then()
  106. .SetDuration(kMaximizeCueExitAnimationDurationMs)
  107. .SetOpacity(widget_layer, 0, gfx::Tween::LINEAR);
  108. }
  109. void PhantomWindowController::ShowMaximizeCue() {
  110. if (!maximize_cue_widget_) {
  111. maximize_cue_widget_ = CreateMaximizeCue(
  112. window_util::GetRootWindowMatching(target_bounds_in_screen_));
  113. maximize_cue_widget_->Show();
  114. }
  115. maximize_cue_widget_->SetOpacity(0);
  116. // Starts entrance animation with fade in and moving the cue from 50%
  117. // higher y position to the actual y position.
  118. const gfx::Rect target_bounds =
  119. maximize_cue_widget_->GetNativeView()->bounds();
  120. const gfx::Rect starting_bounds = gfx::Rect(
  121. target_bounds.x(),
  122. target_bounds.y() * kMaximizeCueEntraceAnimationYPositionMoveUpFactor,
  123. target_bounds.width(), target_bounds.height());
  124. maximize_cue_widget_->SetBounds(starting_bounds);
  125. ui::Layer* widget_layer = maximize_cue_widget_->GetLayer();
  126. views::AnimationBuilder()
  127. .SetPreemptionStrategy(
  128. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
  129. .Once()
  130. .SetDuration(kMaximizeCueAnimationDelayMs)
  131. .Then()
  132. .SetDuration(kMaximizeCueEntranceAnimationDurationMs)
  133. .SetBounds(widget_layer, target_bounds, gfx::Tween::ACCEL_LIN_DECEL_100)
  134. .SetOpacity(widget_layer, 1, gfx::Tween::LINEAR);
  135. }
  136. void PhantomWindowController::TransformPhantomWidgetFromSnapTopToMaximize(
  137. const gfx::Rect& maximize_window_bounds_in_screen) {
  138. gfx::Rect target_bounds_in_screen = maximize_window_bounds_in_screen;
  139. target_bounds_in_screen.Inset(kPhantomWindowInsets);
  140. target_bounds_in_screen_ = target_bounds_in_screen;
  141. ui::Layer* widget_layer = phantom_widget_->GetLayer();
  142. views::AnimationBuilder()
  143. .SetPreemptionStrategy(
  144. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
  145. .Once()
  146. .SetDuration(kScrimEntranceSizeAnimationDurationMs)
  147. .SetBounds(widget_layer, target_bounds_in_screen,
  148. gfx::Tween::ACCEL_20_DECEL_100);
  149. }
  150. gfx::Rect PhantomWindowController::GetTargetWindowBounds() const {
  151. gfx::Rect target_window_bounds = target_bounds_in_screen_;
  152. target_window_bounds.Inset(-kPhantomWindowInsets);
  153. return target_window_bounds;
  154. }
  155. views::Widget* PhantomWindowController::GetMaximizeCueForTesting() const {
  156. return maximize_cue_widget_.get();
  157. }
  158. std::unique_ptr<views::Widget> PhantomWindowController::CreatePhantomWidget(
  159. aura::Window* root_window,
  160. const gfx::Rect& bounds_in_screen) {
  161. std::unique_ptr<views::Widget> phantom_widget(new views::Widget);
  162. views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
  163. params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
  164. // PhantomWindowController is used by FrameMaximizeButton to highlight the
  165. // launcher button. Put the phantom in the same window as the launcher so that
  166. // the phantom is visible.
  167. params.z_order = ui::ZOrderLevel::kFloatingUIElement;
  168. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  169. params.name = "PhantomWindow";
  170. params.layer_type = ui::LAYER_SOLID_COLOR;
  171. params.shadow_type = views::Widget::InitParams::ShadowType::kNone;
  172. params.parent = root_window->GetChildById(kShellWindowId_ShelfContainer);
  173. phantom_widget->set_focus_on_creation(false);
  174. phantom_widget->Init(std::move(params));
  175. phantom_widget->SetVisibilityChangedAnimationsEnabled(false);
  176. aura::Window* phantom_widget_window = phantom_widget->GetNativeWindow();
  177. phantom_widget_window->SetId(kShellWindowId_PhantomWindow);
  178. phantom_widget->SetBounds(bounds_in_screen);
  179. // TODO(sky): I suspect this is never true, verify that.
  180. if (phantom_widget_window->parent() == window_->parent()) {
  181. phantom_widget_window->parent()->StackChildAbove(phantom_widget_window,
  182. window_);
  183. } else {
  184. // Ensure the phantom and its shadow do not cover the shelf.
  185. phantom_widget_window->parent()->StackChildAtBottom(phantom_widget_window);
  186. }
  187. views::View* phantom_view =
  188. phantom_widget->SetContentsView(std::make_unique<views::View>());
  189. phantom_view->SetPaintToLayer();
  190. phantom_view->layer()->SetFillsBoundsOpaquely(false);
  191. phantom_view->SetBackground(views::CreateRoundedRectBackground(
  192. AshColorProvider::Get()->GetShieldLayerColor(
  193. AshColorProvider::ShieldLayerType::kShield20),
  194. kPhantomWindowCornerRadius));
  195. phantom_view->SetBorder(std::make_unique<views::HighlightBorder>(
  196. kPhantomWindowCornerRadius,
  197. views::HighlightBorder::Type::kHighlightBorder1,
  198. /*use_light_colors=*/false));
  199. return phantom_widget;
  200. }
  201. std::unique_ptr<views::Widget> PhantomWindowController::CreateMaximizeCue(
  202. aura::Window* root_window) {
  203. DCHECK(phantom_widget_);
  204. std::unique_ptr<views::Widget> maximize_cue_widget(new views::Widget);
  205. views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
  206. params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
  207. // Put the maximize cue in the same window as the floating UI.
  208. params.z_order = ui::ZOrderLevel::kFloatingUIElement;
  209. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  210. params.name = "MaximizeCueWidget";
  211. params.shadow_type = views::Widget::InitParams::ShadowType::kNone;
  212. params.parent = root_window->GetChildById(kShellWindowId_OverlayContainer);
  213. maximize_cue_widget->set_focus_on_creation(false);
  214. maximize_cue_widget->Init(std::move(params));
  215. aura::Window* maximize_cue_widget_window =
  216. maximize_cue_widget->GetNativeWindow();
  217. maximize_cue_widget_window->parent()->StackChildAtTop(
  218. maximize_cue_widget_window);
  219. views::View* maximize_cue =
  220. maximize_cue_widget->SetContentsView(std::make_unique<views::View>());
  221. maximize_cue->SetPaintToLayer();
  222. maximize_cue->layer()->SetFillsBoundsOpaquely(false);
  223. auto* color_provider = AshColorProvider::Get();
  224. maximize_cue->SetBackground(views::CreateRoundedRectBackground(
  225. color_provider->GetBaseLayerColor(
  226. AshColorProvider::BaseLayerType::kTransparent80),
  227. kMaximizeCueHeight / 2));
  228. maximize_cue->layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
  229. const gfx::RoundedCornersF radii(kMaximizeCueHeight / 2);
  230. maximize_cue->layer()->SetRoundedCornerRadius(radii);
  231. maximize_cue->SetBorder(std::make_unique<views::HighlightBorder>(
  232. kMaximizeCueHeight / 2, views::HighlightBorder::Type::kHighlightBorder1,
  233. /*use_light_colors=*/false));
  234. // Set layout of cue view and add a label to the view.
  235. maximize_cue->SetLayoutManager(std::make_unique<views::BoxLayout>(
  236. views::BoxLayout::Orientation::kHorizontal,
  237. gfx::Insets::VH(kMaximizeCueVerticalInsets,
  238. kMaximizeCueHorizontalInsets)));
  239. views::Label* maximize_cue_label =
  240. maximize_cue->AddChildView(std::make_unique<views::Label>(
  241. l10n_util::GetStringUTF16(IDS_ASH_SPLIT_VIEW_HOLD_TO_MAXIMIZE)));
  242. maximize_cue_label->SetPaintToLayer();
  243. maximize_cue_label->layer()->SetFillsBoundsOpaquely(false);
  244. maximize_cue_label->SetEnabledColor(color_provider->GetContentLayerColor(
  245. AshColorProvider::ContentLayerType::kTextColorPrimary));
  246. maximize_cue_label->SetVisible(true);
  247. maximize_cue_label->SetAutoColorReadabilityEnabled(false);
  248. maximize_cue_label->SetFontList(views::Label::GetDefaultFontList().Derive(
  249. 2, gfx::Font::FontStyle::NORMAL, gfx::Font::Weight::NORMAL));
  250. const display::Display& display =
  251. display::Screen::GetScreen()->GetDisplayNearestWindow(root_window);
  252. const gfx::Rect work_area = display.work_area();
  253. const int maximize_cue_width = maximize_cue->GetPreferredSize().width();
  254. const int maximize_cue_y = work_area.y() + kMaximizeCueTopMargin;
  255. const gfx::Rect phantom_bounds = phantom_widget_->GetNativeView()->bounds();
  256. const gfx::Rect maximize_cue_bounds =
  257. gfx::Rect(phantom_bounds.CenterPoint().x() - maximize_cue_width / 2,
  258. maximize_cue_y, maximize_cue_width, kMaximizeCueHeight);
  259. maximize_cue_widget->SetBounds(maximize_cue_bounds);
  260. return maximize_cue_widget;
  261. }
  262. void PhantomWindowController::ShowPhantomWidget() {
  263. phantom_widget_->Show();
  264. phantom_widget_->SetOpacity(0);
  265. ui::Layer* widget_layer = phantom_widget_->GetLayer();
  266. // Layer uses bounds in parent so convert from screen bounds.
  267. gfx::Rect bounds = target_bounds_in_screen_;
  268. wm::ConvertRectFromScreen(phantom_widget_->GetNativeWindow()->parent(),
  269. &bounds);
  270. views::AnimationBuilder()
  271. .SetPreemptionStrategy(
  272. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
  273. .Once()
  274. .SetDuration(kScrimEntranceSizeAnimationDurationMs)
  275. .SetBounds(widget_layer, bounds, gfx::Tween::ACCEL_20_DECEL_100)
  276. .At(base::Seconds(0))
  277. .SetDuration(kScrimEntranceOpacityAnimationDurationMs)
  278. .SetOpacity(widget_layer, 1, gfx::Tween::LINEAR);
  279. }
  280. } // namespace ash