[go: nahoru, domu]

blob: 7655c7eaa2ad8c82722c49422af39bd8da2bbd7c [file] [log] [blame]
Sammie Quon3e582d642017-09-08 02:01:541// Copyright 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Sammie Quonbe8f7b02018-03-14 02:08:175#include "ash/wm/splitview/split_view_drag_indicators.h"
Sammie Quon3e582d642017-09-08 02:01:546
Ahmed Fakhry4acdf652019-09-05 18:53:067#include <utility>
8
Avery Musbach08062d852019-11-09 01:41:399#include "ash/display/screen_orientation_controller.h"
Sammie Quon3e582d642017-09-08 02:01:5410#include "ash/public/cpp/shell_window_ids.h"
Avery Musbachca258382019-03-26 22:27:3311#include "ash/public/cpp/window_animation_types.h"
Sammie Quondc782782017-11-15 23:33:1512#include "ash/screen_util.h"
Min Chen10d413c2018-12-07 01:27:3613#include "ash/shelf/shelf.h"
Sammie Quon3e582d642017-09-08 02:01:5414#include "ash/shell.h"
15#include "ash/strings/grit/ash_strings.h"
Cattalyya Nuengsigkapian69d6cc52020-10-15 19:26:0316#include "ash/style/ash_color_provider.h"
17#include "ash/style/default_color_constants.h"
18#include "ash/style/default_colors.h"
Sammie Quon6c5cf1a2017-12-12 01:11:3719#include "ash/wm/splitview/split_view_constants.h"
Sammie Quona75d28c2018-05-08 16:39:5720#include "ash/wm/splitview/split_view_highlight_view.h"
Sammie Quon6c5cf1a2017-12-12 01:11:3721#include "ash/wm/splitview/split_view_utils.h"
Avery Musbachca258382019-03-26 22:27:3322#include "ash/wm/window_animations.h"
James Cook00e65e92019-07-25 03:19:0823#include "ash/wm/window_util.h"
Sammie Quon7e6f2692017-10-27 16:42:5224#include "base/i18n/rtl.h"
Sammie Quon3e582d642017-09-08 02:01:5425#include "base/strings/utf_string_conversions.h"
26#include "ui/aura/window.h"
Avery Musbachc32f919e2019-10-30 20:36:5327#include "ui/aura/window_observer.h"
Sammie Quon3e582d642017-09-08 02:01:5428#include "ui/base/l10n/l10n_util.h"
Sammie Quon8250507f2018-02-23 00:08:2829#include "ui/compositor/scoped_layer_animation_settings.h"
Sammie Quonbc2e913b2017-10-25 22:28:2630#include "ui/display/display_observer.h"
minch29e3879d2020-11-11 21:25:1431#include "ui/views/background.h"
Sammie Quon3e582d642017-09-08 02:01:5432#include "ui/views/controls/label.h"
Sammie Quon4486fd62017-10-10 20:22:4033#include "ui/views/layout/box_layout.h"
Sammie Quon3e582d642017-09-08 02:01:5434#include "ui/views/view.h"
35#include "ui/views/widget/widget.h"
Sammie Quondc782782017-11-15 23:33:1536#include "ui/wm/core/coordinate_conversion.h"
Avery Musbachca258382019-03-26 22:27:3337#include "ui/wm/core/window_animations.h"
Sammie Quon3e582d642017-09-08 02:01:5438
39namespace ash {
40
41namespace {
42
Avery Musbach189df0e2019-04-01 23:21:0643// When a preview is shown, the opposite highlight shall contract to this ratio
44// of the screen length.
45constexpr float kOtherHighlightScreenPrimaryAxisRatio = 0.03f;
Sammie Quona75d28c2018-05-08 16:39:5746
Sammie Quon3e582d642017-09-08 02:01:5447// Creates the widget responsible for displaying the indicators.
Avery Musbach8f349292019-11-12 17:29:5348std::unique_ptr<views::Widget> CreateWidget(aura::Window* root_window) {
Sammie Quon16869be2018-02-16 02:59:3349 auto widget = std::make_unique<views::Widget>();
Sammie Quon3e582d642017-09-08 02:01:5450 views::Widget::InitParams params;
51 params.type = views::Widget::InitParams::TYPE_POPUP;
Sammie Quon3e582d642017-09-08 02:01:5452 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
Hwanseung Lee787d0aa2019-11-22 00:31:0853 params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
Sammie Quon3e582d642017-09-08 02:01:5454 params.accept_events = false;
Sammie Quon45a13962020-02-03 17:50:5855 params.layer_type = ui::LAYER_NOT_DRAWN;
Avery Musbach8f349292019-11-12 17:29:5356 params.parent =
57 Shell::GetContainer(root_window, kShellWindowId_OverlayContainer);
Sammie Quon3e582d642017-09-08 02:01:5458 widget->set_focus_on_creation(false);
Ahmed Fakhry32f3c452019-08-01 16:36:3459 widget->Init(std::move(params));
Sammie Quon3e582d642017-09-08 02:01:5460 return widget;
61}
62
Sammie Quonbc2e913b2017-10-25 22:28:2663// Computes the transform which rotates the labels |angle| degrees. The point
64// of rotation is the relative center point of |bounds|.
Sammie Quon3e582d642017-09-08 02:01:5465gfx::Transform ComputeRotateAroundCenterTransform(const gfx::Rect& bounds,
Sammie Quonbc2e913b2017-10-25 22:28:2666 double angle) {
Sammie Quon3e582d642017-09-08 02:01:5467 gfx::Transform transform;
68 const gfx::Vector2dF center_point_vector =
69 bounds.CenterPoint() - bounds.origin();
70 transform.Translate(center_point_vector);
Sammie Quonbc2e913b2017-10-25 22:28:2671 transform.Rotate(angle);
Sammie Quon3e582d642017-09-08 02:01:5472 transform.Translate(-center_point_vector);
73 return transform;
74}
75
Min Chen10d413c2018-12-07 01:27:3676// Returns the work area bounds that has no overlap with shelf.
77gfx::Rect GetWorkAreaBoundsNoOverlapWithShelf(aura::Window* root_window) {
78 aura::Window* window =
79 root_window->GetChildById(kShellWindowId_OverlayContainer);
80 gfx::Rect bounds = screen_util::GetDisplayWorkAreaBoundsInParent(window);
81 ::wm::ConvertRectToScreen(root_window, &bounds);
82
83 bounds.Subtract(Shelf::ForWindow(root_window)->GetIdealBounds());
84 return bounds;
85}
86
Sammie Quon3e582d642017-09-08 02:01:5487} // namespace
88
Min Chen632ec8d2018-08-15 21:32:0889// static
Avery Musbachabe3a2d2019-11-04 21:51:2790SplitViewController::SnapPosition SplitViewDragIndicators::GetSnapPosition(
91 WindowDraggingState window_dragging_state) {
92 switch (window_dragging_state) {
93 case WindowDraggingState::kToSnapLeft:
94 return SplitViewController::LEFT;
95 case WindowDraggingState::kToSnapRight:
96 return SplitViewController::RIGHT;
97 default:
98 return SplitViewController::NONE;
99 }
Min Chen632ec8d2018-08-15 21:32:08100}
101
102// static
Avery Musbachabe3a2d2019-11-04 21:51:27103SplitViewDragIndicators::WindowDraggingState
104SplitViewDragIndicators::ComputeWindowDraggingState(
105 bool is_dragging,
106 WindowDraggingState non_snap_state,
107 SplitViewController::SnapPosition snap_position) {
108 if (!is_dragging || !ShouldAllowSplitView())
109 return WindowDraggingState::kNoDrag;
110 switch (snap_position) {
111 case SplitViewController::NONE:
112 return non_snap_state;
113 case SplitViewController::LEFT:
114 return WindowDraggingState::kToSnapLeft;
115 case SplitViewController::RIGHT:
116 return WindowDraggingState::kToSnapRight;
117 }
Min Chen632ec8d2018-08-15 21:32:08118}
119
Sammie Quon6c5cf1a2017-12-12 01:11:37120// View which contains a label and can be rotated. Used by and rotated by
Sammie Quonbe8f7b02018-03-14 02:08:17121// SplitViewDragIndicatorsView.
Sammie Quone61bdc12018-03-17 02:05:54122class SplitViewDragIndicators::RotatedImageLabelView : public views::View {
Sammie Quon4486fd62017-10-10 20:22:40123 public:
Min Chen632ec8d2018-08-15 21:32:08124 explicit RotatedImageLabelView(bool is_right_or_bottom)
125 : is_right_or_bottom_(is_right_or_bottom) {
Sammie Quon45a13962020-02-03 17:50:58126 // TODO(sammiequon): Remove this extra intermediate layer.
127 SetPaintToLayer(ui::LAYER_NOT_DRAWN);
128 layer()->SetFillsBoundsOpaquely(false);
Sammie Quon4486fd62017-10-10 20:22:40129
Sammie Quone61bdc12018-03-17 02:05:54130 // Use |label_parent_| to add padding and rounded edges to the text. Create
131 // this extra view so that we can rotate the label, while having a slide
132 // animation at times on the whole thing.
minch29e3879d2020-11-11 21:25:14133 label_parent_ = AddChildView(std::make_unique<views::View>());
Sammie Quone61bdc12018-03-17 02:05:54134 label_parent_->SetPaintToLayer();
135 label_parent_->layer()->SetFillsBoundsOpaquely(false);
minch29e3879d2020-11-11 21:25:14136 label_parent_->SetBackground(views::CreateRoundedRectBackground(
137 DeprecatedGetBaseLayerColor(
138 AshColorProvider::BaseLayerType::kTransparent80,
139 kSplitviewLabelBackgroundColor),
140 kSplitviewLabelRoundRectRadiusDp));
Sammie Quone61bdc12018-03-17 02:05:54141 label_parent_->SetLayoutManager(std::make_unique<views::BoxLayout>(
Hwanseung Lee5b9e43d2019-06-25 01:55:32142 views::BoxLayout::Orientation::kVertical,
Brett Wilsonbec75cd2017-12-15 20:47:34143 gfx::Insets(kSplitviewLabelVerticalInsetDp,
144 kSplitviewLabelHorizontalInsetDp)));
Sammie Quone61bdc12018-03-17 02:05:54145
Sammie Quon45a13962020-02-03 17:50:58146 label_ = label_parent_->AddChildView(std::make_unique<views::Label>(
Jan Wilken Dörrie85285b02021-03-11 23:38:47147 std::u16string(), views::style::CONTEXT_LABEL));
Cattalyya Nuengsigkapian69d6cc52020-10-15 19:26:03148 label_->SetEnabledColor(DeprecatedGetContentLayerColor(
149 AshColorProvider::ContentLayerType::kTextColorPrimary,
150 kSplitviewLabelEnabledColor));
151 label_->SetBackgroundColor(DeprecatedGetBaseLayerColor(
152 AshColorProvider::BaseLayerType::kTransparent80,
153 kSplitviewLabelBackgroundColor));
Sammie Quon4486fd62017-10-10 20:22:40154 }
155
156 ~RotatedImageLabelView() override = default;
157
Jan Wilken Dörrie85285b02021-03-11 23:38:47158 void SetLabelText(const std::u16string& text) { label_->SetText(text); }
Sammie Quon4486fd62017-10-10 20:22:40159
Sammie Quonbc2e913b2017-10-25 22:28:26160 // Called when the view's bounds are altered. Rotates the view by |angle|
161 // degrees.
162 void OnBoundsUpdated(const gfx::Rect& bounds, double angle) {
Sammie Quon1d70db62017-10-11 17:59:38163 SetBoundsRect(bounds);
Sammie Quone61bdc12018-03-17 02:05:54164 label_parent_->SetBoundsRect(gfx::Rect(bounds.size()));
165 label_parent_->SetTransform(
166 ComputeRotateAroundCenterTransform(bounds, angle));
167 }
168
Min Chen632ec8d2018-08-15 21:32:08169 // Called to update the opacity of the labels view on |indicator_state|.
Avery Musbachabe3a2d2019-11-04 21:51:27170 void OnWindowDraggingStateChanged(
171 WindowDraggingState window_dragging_state,
172 WindowDraggingState previous_window_dragging_state,
173 bool can_dragged_window_be_snapped) {
Avery Musbach8a91bee2019-11-06 19:57:42174 // No top label for dragging from the top in portrait orientation.
175 if (window_dragging_state == WindowDraggingState::kFromTop &&
176 !IsCurrentScreenOrientationLandscape() && !is_right_or_bottom_) {
177 return;
178 }
179
Avery Musbach81520172019-12-20 18:47:51180 // If there is no drag currently in this display, any label that is showing
181 // shall fade out with the corresponding indicator.
182 if (window_dragging_state == WindowDraggingState::kNoDrag ||
183 window_dragging_state == WindowDraggingState::kOtherDisplay) {
Avery Musbacha1fe8012019-03-22 16:56:26184 DoSplitviewOpacityAnimation(
185 layer(), SPLITVIEW_ANIMATION_TEXT_FADE_OUT_WITH_HIGHLIGHT);
186 return;
187 }
188
Avery Musbachabe3a2d2019-11-04 21:51:27189 // When a snap preview is shown, any label that is showing shall fade out.
190 if (GetSnapPosition(window_dragging_state) != SplitViewController::NONE) {
Min Chen632ec8d2018-08-15 21:32:08191 DoSplitviewOpacityAnimation(layer(), SPLITVIEW_ANIMATION_TEXT_FADE_OUT);
192 return;
193 }
194
Avery Musbachfcf2cf6b2019-11-16 03:00:59195 // Set the text according to |can_dragged_window_be_snapped|.
196 SetLabelText(l10n_util::GetStringUTF16(
197 can_dragged_window_be_snapped ? IDS_ASH_SPLIT_VIEW_GUIDANCE
198 : IDS_ASH_SPLIT_VIEW_CANNOT_SNAP));
199
Avery Musbach81520172019-12-20 18:47:51200 // When dragging begins in this display or comes in from another display, if
201 // there is now no snap preview, fade in with an indicator.
202 if (previous_window_dragging_state == WindowDraggingState::kNoDrag ||
203 previous_window_dragging_state == WindowDraggingState::kOtherDisplay) {
Avery Musbacha1fe8012019-03-22 16:56:26204 DoSplitviewOpacityAnimation(
205 layer(), SPLITVIEW_ANIMATION_TEXT_FADE_IN_WITH_HIGHLIGHT);
206 return;
Min Chen632ec8d2018-08-15 21:32:08207 }
Avery Musbacha1fe8012019-03-22 16:56:26208
Avery Musbachabe3a2d2019-11-04 21:51:27209 // If a snap preview was shown, the labels shall now fade in.
210 if (GetSnapPosition(previous_window_dragging_state) !=
211 SplitViewController::NONE) {
Avery Musbacha1fe8012019-03-22 16:56:26212 DoSplitviewOpacityAnimation(layer(), SPLITVIEW_ANIMATION_TEXT_FADE_IN);
213 return;
214 }
Min Chen632ec8d2018-08-15 21:32:08215 }
216
Sammie Quone61bdc12018-03-17 02:05:54217 protected:
218 gfx::Size CalculatePreferredSize() const override {
219 return label_parent_->GetPreferredSize();
Sammie Quon1d70db62017-10-11 17:59:38220 }
221
Sammie Quon4486fd62017-10-10 20:22:40222 private:
Min Chen632ec8d2018-08-15 21:32:08223 // True if the label view is the right/bottom side one, false if it is the
224 // left/top one.
225 const bool is_right_or_bottom_;
226
minch29e3879d2020-11-11 21:25:14227 views::View* label_parent_ = nullptr;
Sammie Quon4486fd62017-10-10 20:22:40228 views::Label* label_ = nullptr;
229
230 DISALLOW_COPY_AND_ASSIGN(RotatedImageLabelView);
231};
232
Sammie Quon3e582d642017-09-08 02:01:54233// View which contains two highlights on each side indicator where a user should
234// drag a selected window in order to initiate splitview. Each highlight has a
Sammie Quonbc2e913b2017-10-25 22:28:26235// label with instructions to further guide users. The highlights are on the
236// left and right of the display in landscape mode, and on the top and bottom of
Sammie Quon8250507f2018-02-23 00:08:28237// the display in landscape mode. The highlights can expand and shrink if a
238// window has entered a snap region to display the bounds of the window, if it
239// were to get snapped.
Sammie Quonbe8f7b02018-03-14 02:08:17240class SplitViewDragIndicators::SplitViewDragIndicatorsView
Avery Musbachc32f919e2019-10-30 20:36:53241 : public views::View,
242 public aura::WindowObserver {
Sammie Quon3e582d642017-09-08 02:01:54243 public:
Sammie Quonbe8f7b02018-03-14 02:08:17244 SplitViewDragIndicatorsView() {
Sammie Quon45a13962020-02-03 17:50:58245 left_highlight_view_ = AddChildView(
246 std::make_unique<SplitViewHighlightView>(/*is_right_or_bottom=*/false));
247 right_highlight_view_ = AddChildView(
248 std::make_unique<SplitViewHighlightView>(/*is_right_or_bottom=*/true));
Sammie Quon4486fd62017-10-10 20:22:40249
Sammie Quon45a13962020-02-03 17:50:58250 left_rotated_view_ = AddChildView(
251 std::make_unique<RotatedImageLabelView>(/*is_right_or_bottom=*/false));
252 right_rotated_view_ = AddChildView(
253 std::make_unique<RotatedImageLabelView>(/*is_right_or_bottom=*/true));
Sammie Quon16869be2018-02-16 02:59:33254
255 // Nothing is shown initially.
Sammie Quon36ab97f2018-03-30 18:09:20256 left_highlight_view_->layer()->SetOpacity(0.f);
257 right_highlight_view_->layer()->SetOpacity(0.f);
Sammie Quon16869be2018-02-16 02:59:33258 left_rotated_view_->layer()->SetOpacity(0.f);
259 right_rotated_view_->layer()->SetOpacity(0.f);
Sammie Quon3e582d642017-09-08 02:01:54260 }
261
Avery Musbachc32f919e2019-10-30 20:36:53262 ~SplitViewDragIndicatorsView() override {
263 if (dragged_window_)
264 dragged_window_->RemoveObserver(this);
265 }
Sammie Quon3e582d642017-09-08 02:01:54266
Ahmed Fakhry4acdf652019-09-05 18:53:06267 SplitViewHighlightView* left_highlight_view() { return left_highlight_view_; }
268
Avery Musbach847b36a2019-08-26 22:54:08269 // Called by parent widget when the state machine changes. Handles setting the
270 // opacity and bounds of the highlights and labels.
Avery Musbachabe3a2d2019-11-04 21:51:27271 void OnWindowDraggingStateChanged(WindowDraggingState window_dragging_state) {
272 DCHECK_NE(window_dragging_state_, window_dragging_state);
273 previous_window_dragging_state_ = window_dragging_state_;
274 window_dragging_state_ = window_dragging_state;
Sammie Quon16869be2018-02-16 02:59:33275
Friedrich [CET]7c791002019-12-03 15:35:35276 SplitViewController* split_view_controller =
277 SplitViewController::Get(GetWidget()->GetNativeWindow());
Avery Musbach745a9ac2019-11-07 01:33:02278 const bool previews_only =
279 window_dragging_state == WindowDraggingState::kFromShelf ||
Xiaoqian Dai7d1288892019-11-20 20:17:47280 window_dragging_state == WindowDraggingState::kFromTop ||
Friedrich [CET]7c791002019-12-03 15:35:35281 split_view_controller->InSplitViewMode();
Avery Musbach329724e2019-10-31 21:10:37282 const bool can_dragged_window_be_snapped =
Friedrich [CET]7c791002019-12-03 15:35:35283 dragged_window_ &&
284 split_view_controller->CanSnapWindow(dragged_window_);
Avery Musbach745a9ac2019-11-07 01:33:02285 if (!previews_only) {
286 left_rotated_view_->OnWindowDraggingStateChanged(
287 window_dragging_state, previous_window_dragging_state_,
288 can_dragged_window_be_snapped);
289 right_rotated_view_->OnWindowDraggingStateChanged(
290 window_dragging_state, previous_window_dragging_state_,
291 can_dragged_window_be_snapped);
292 }
Avery Musbachabe3a2d2019-11-04 21:51:27293 left_highlight_view_->OnWindowDraggingStateChanged(
Avery Musbach745a9ac2019-11-07 01:33:02294 window_dragging_state, previous_window_dragging_state_, previews_only,
Avery Musbachabe3a2d2019-11-04 21:51:27295 can_dragged_window_be_snapped);
296 right_highlight_view_->OnWindowDraggingStateChanged(
Avery Musbach745a9ac2019-11-07 01:33:02297 window_dragging_state, previous_window_dragging_state_, previews_only,
Avery Musbach329724e2019-10-31 21:10:37298 can_dragged_window_be_snapped);
Sammie Quon16869be2018-02-16 02:59:33299
Avery Musbachabe3a2d2019-11-04 21:51:27300 if (window_dragging_state != WindowDraggingState::kNoDrag ||
301 GetSnapPosition(previous_window_dragging_state_) !=
302 SplitViewController::NONE) {
303 Layout(previous_window_dragging_state_ != WindowDraggingState::kNoDrag);
304 }
Sammie Quon3e582d642017-09-08 02:01:54305 }
306
Sammie Quon16869be2018-02-16 02:59:33307 views::View* GetViewForIndicatorType(IndicatorType type) {
308 switch (type) {
309 case IndicatorType::kLeftHighlight:
Sammie Quon36ab97f2018-03-30 18:09:20310 return left_highlight_view_;
Sammie Quon16869be2018-02-16 02:59:33311 case IndicatorType::kLeftText:
312 return left_rotated_view_;
313 case IndicatorType::kRightHighlight:
Sammie Quon36ab97f2018-03-30 18:09:20314 return right_highlight_view_;
Sammie Quon16869be2018-02-16 02:59:33315 case IndicatorType::kRightText:
316 return right_rotated_view_;
317 }
318
319 NOTREACHED();
320 return nullptr;
321 }
322
Avery Musbachc32f919e2019-10-30 20:36:53323 void SetDraggedWindow(aura::Window* dragged_window) {
324 if (dragged_window_)
325 dragged_window_->RemoveObserver(this);
326 dragged_window_ = dragged_window;
327 if (dragged_window)
328 dragged_window->AddObserver(this);
329 }
330
Sammie Quonbc2e913b2017-10-25 22:28:26331 // views::View:
Sammie Quona75d28c2018-05-08 16:39:57332 void Layout() override { Layout(/*animate=*/false); }
333
Avery Musbachc32f919e2019-10-30 20:36:53334 // aura::WindowObserver:
335 void OnWindowDestroyed(aura::Window* window) override {
336 DCHECK_EQ(dragged_window_, window);
337 dragged_window_ = nullptr;
338 }
339
Sammie Quona75d28c2018-05-08 16:39:57340 private:
341 // Layout the bounds of the highlight views and helper labels. One should
342 // animate when changing states, but not when bounds or orientation is
343 // changed.
344 void Layout(bool animate) {
Ahmed Fakhry4acdf652019-09-05 18:53:06345 // TODO(xdai|afakhry): Attempt to simplify this logic.
Avery Musbach5e294642019-12-06 21:39:19346 const bool horizontal = SplitViewController::IsLayoutHorizontal();
347 const int display_width = horizontal ? width() : height();
348 const int display_height = horizontal ? height() : width();
Min Chen632ec8d2018-08-15 21:32:08349
Sammie Quonbc2e913b2017-10-25 22:28:26350 // Calculate the bounds of the two highlight regions.
Sammie Quon6c5cf1a2017-12-12 01:11:37351 const int highlight_width =
Min Chen632ec8d2018-08-15 21:32:08352 display_width * kHighlightScreenPrimaryAxisRatio;
Sammie Quon6c5cf1a2017-12-12 01:11:37353 const int highlight_height =
Min Chen632ec8d2018-08-15 21:32:08354 display_height - 2 * kHighlightScreenEdgePaddingDp;
355 gfx::Size highlight_size(highlight_width, highlight_height);
Sammie Quon16869be2018-02-16 02:59:33356
Avery Musbach189df0e2019-04-01 23:21:06357 // When one highlight expands to become a preview area, the other highlight
358 // contracts to this width.
359 const int other_highlight_width =
360 display_width * kOtherHighlightScreenPrimaryAxisRatio;
361
Avery Musbach5e294642019-12-06 21:39:19362 // The origin of the right highlight view in horizontal split view layout,
363 // or the bottom highlight view in vertical split view layout.
Min Chen632ec8d2018-08-15 21:32:08364 gfx::Point right_bottom_origin(
365 display_width - highlight_width - kHighlightScreenEdgePaddingDp,
366 kHighlightScreenEdgePaddingDp);
Sammie Quon16869be2018-02-16 02:59:33367
Min Chen10d413c2018-12-07 01:27:36368 const gfx::Point highlight_padding_point(kHighlightScreenEdgePaddingDp,
369 kHighlightScreenEdgePaddingDp);
370 gfx::Rect left_highlight_bounds(highlight_padding_point, highlight_size);
Min Chen632ec8d2018-08-15 21:32:08371 gfx::Rect right_highlight_bounds(right_bottom_origin, highlight_size);
Avery Musbach5e294642019-12-06 21:39:19372 if (!horizontal) {
Min Chenb99878d2018-09-04 23:52:52373 left_highlight_bounds.Transpose();
374 right_highlight_bounds.Transpose();
Min Chen632ec8d2018-08-15 21:32:08375 }
Avery Musbachabe3a2d2019-11-04 21:51:27376
377 // True when the drag ends in a snap area, meaning that the dragged window
378 // actually becomes snapped.
379 const bool drag_ending_in_snap =
380 window_dragging_state_ == WindowDraggingState::kNoDrag &&
381 GetSnapPosition(previous_window_dragging_state_) !=
382 SplitViewController::NONE;
383
384 SplitViewController::SnapPosition snap_position =
385 GetSnapPosition(window_dragging_state_);
386 if (snap_position == SplitViewController::NONE)
387 snap_position = GetSnapPosition(previous_window_dragging_state_);
388
Avery Musbachdb2c6222019-04-02 21:13:20389 gfx::Rect preview_area_bounds;
Avery Musbach7a69bab2019-04-02 18:41:27390 base::Optional<SplitviewAnimationType> left_highlight_animation_type;
391 base::Optional<SplitviewAnimationType> right_highlight_animation_type;
Avery Musbachabe3a2d2019-11-04 21:51:27392 if (GetSnapPosition(window_dragging_state_) != SplitViewController::NONE ||
393 drag_ending_in_snap) {
Sammie Quonbe8f7b02018-03-14 02:08:17394 // Get the preview area bounds from the split view controller.
Avery Musbachdb2c6222019-04-02 21:13:20395 preview_area_bounds =
Avery Musbachdfc1f322019-10-15 23:04:19396 SplitViewController::Get(GetWidget()->GetNativeWindow())
Avery Musbachabe3a2d2019-11-04 21:51:27397 ->GetSnappedWindowBoundsInScreen(snap_position, dragged_window_);
Min Chen10d413c2018-12-07 01:27:36398
399 aura::Window* root_window =
400 GetWidget()->GetNativeWindow()->GetRootWindow();
Ahmed Fakhry4acdf652019-09-05 18:53:06401 wm::ConvertRectFromScreen(root_window, &preview_area_bounds);
402
Min Chen10d413c2018-12-07 01:27:36403 // Preview area should have no overlap with the shelf.
404 preview_area_bounds.Subtract(
405 Shelf::ForWindow(root_window)->GetIdealBounds());
406
Ahmed Fakhry4acdf652019-09-05 18:53:06407 gfx::Rect work_area_bounds =
Min Chen10d413c2018-12-07 01:27:36408 GetWorkAreaBoundsNoOverlapWithShelf(root_window);
Ahmed Fakhry4acdf652019-09-05 18:53:06409 wm::ConvertRectFromScreen(root_window, &work_area_bounds);
Min Chenca9a3f32018-10-24 16:34:48410 preview_area_bounds.set_y(preview_area_bounds.y() - work_area_bounds.y());
Avery Musbachabe3a2d2019-11-04 21:51:27411 if (!drag_ending_in_snap) {
Avery Musbach931f79f2019-03-08 23:49:36412 preview_area_bounds.Inset(kHighlightScreenEdgePaddingDp,
413 kHighlightScreenEdgePaddingDp);
414 }
Sammie Quon16869be2018-02-16 02:59:33415
Sammie Quona75d28c2018-05-08 16:39:57416 // Calculate the bounds of the other highlight, which is the one that
417 // shrinks and fades away, while the other one, the preview area, expands
418 // and takes up half the screen.
Min Chen632ec8d2018-08-15 21:32:08419 gfx::Rect other_bounds(
Avery Musbach189df0e2019-04-01 23:21:06420 display_width - other_highlight_width - kHighlightScreenEdgePaddingDp,
421 kHighlightScreenEdgePaddingDp, other_highlight_width,
Min Chen632ec8d2018-08-15 21:32:08422 display_height - 2 * kHighlightScreenEdgePaddingDp);
Avery Musbach5e294642019-12-06 21:39:19423 if (!horizontal)
Min Chenb99878d2018-09-04 23:52:52424 other_bounds.Transpose();
Sammie Quon59159ca2018-03-01 06:05:14425
Avery Musbach5e294642019-12-06 21:39:19426 if (SplitViewController::IsPhysicalLeftOrTop(snap_position)) {
Sammie Quona75d28c2018-05-08 16:39:57427 left_highlight_bounds = preview_area_bounds;
428 right_highlight_bounds = other_bounds;
Avery Musbach7a69bab2019-04-02 18:41:27429 if (animate) {
Avery Musbachabe3a2d2019-11-04 21:51:27430 if (drag_ending_in_snap) {
Avery Musbach7a69bab2019-04-02 18:41:27431 left_highlight_animation_type =
432 SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET;
433 } else {
434 left_highlight_animation_type =
435 SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN;
436 right_highlight_animation_type =
437 SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_OUT;
438 }
439 }
Sammie Quona75d28c2018-05-08 16:39:57440 } else {
Min Chen10d413c2018-12-07 01:27:36441 other_bounds.set_origin(highlight_padding_point);
Sammie Quona75d28c2018-05-08 16:39:57442 left_highlight_bounds = other_bounds;
443 right_highlight_bounds = preview_area_bounds;
Avery Musbach7a69bab2019-04-02 18:41:27444 if (animate) {
Avery Musbachabe3a2d2019-11-04 21:51:27445 if (drag_ending_in_snap) {
Avery Musbach7a69bab2019-04-02 18:41:27446 right_highlight_animation_type =
447 SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET;
448 } else {
449 left_highlight_animation_type =
450 SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_OUT;
451 right_highlight_animation_type =
452 SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN;
453 }
454 }
455 }
Avery Musbachabe3a2d2019-11-04 21:51:27456 } else if (GetSnapPosition(previous_window_dragging_state_) !=
457 SplitViewController::NONE &&
458 animate) {
Avery Musbach5e294642019-12-06 21:39:19459 if (SplitViewController::IsPhysicalLeftOrTop(snap_position)) {
Avery Musbach7a69bab2019-04-02 18:41:27460 left_highlight_animation_type =
461 SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_OUT;
462 right_highlight_animation_type =
463 SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_IN;
464 } else {
465 left_highlight_animation_type =
466 SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_IN;
467 right_highlight_animation_type =
468 SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_OUT;
Sammie Quon59159ca2018-03-01 06:05:14469 }
Sammie Quon16869be2018-02-16 02:59:33470 }
471
Sammie Quona75d28c2018-05-08 16:39:57472 left_highlight_view_->SetBounds(GetMirroredRect(left_highlight_bounds),
Sammie Quonded34612020-02-04 19:10:41473 left_highlight_animation_type);
Sammie Quona75d28c2018-05-08 16:39:57474 right_highlight_view_->SetBounds(GetMirroredRect(right_highlight_bounds),
Avery Musbach5e294642019-12-06 21:39:19475 right_highlight_animation_type);
Sammie Quonbc2e913b2017-10-25 22:28:26476
477 // Calculate the bounds of the views which contain the guidance text and
Avery Musbach5e294642019-12-06 21:39:19478 // icon. Rotate the two views in horizontal split view layout.
Avery Musbachfeb73212019-11-01 22:53:38479 const gfx::Size size(right_rotated_view_->GetPreferredSize().width(),
480 kSplitviewLabelPreferredHeightDp);
Avery Musbach5e294642019-12-06 21:39:19481 if (!horizontal)
Min Chen632ec8d2018-08-15 21:32:08482 highlight_size.SetSize(highlight_size.height(), highlight_size.width());
483 gfx::Rect left_rotated_bounds(
484 highlight_size.width() / 2 - size.width() / 2,
485 highlight_size.height() / 2 - size.height() / 2, size.width(),
486 size.height());
Sammie Quon8250507f2018-02-23 00:08:28487 gfx::Rect right_rotated_bounds = left_rotated_bounds;
Min Chen10d413c2018-12-07 01:27:36488 left_rotated_bounds.Offset(highlight_padding_point.x(),
489 highlight_padding_point.y());
Avery Musbach5e294642019-12-06 21:39:19490 if (!horizontal) {
Min Chen632ec8d2018-08-15 21:32:08491 right_bottom_origin.SetPoint(right_bottom_origin.y(),
492 right_bottom_origin.x());
493 }
Sammie Quon8250507f2018-02-23 00:08:28494 right_rotated_bounds.Offset(right_bottom_origin.x(),
495 right_bottom_origin.y());
Sammie Quon7e6f2692017-10-27 16:42:52496
497 // In portrait mode, there is no need to rotate the text and warning icon.
Avery Musbach5e294642019-12-06 21:39:19498 // In horizontal split view layout, rotate the left text 90 degrees
499 // clockwise in rtl and 90 degress anti clockwise in ltr. The right text is
500 // rotated 90 degrees in the opposite direction of the left text.
Sammie Quon7e6f2692017-10-27 16:42:52501 double left_rotation_angle = 0.0;
Avery Musbach5e294642019-12-06 21:39:19502 if (horizontal)
Sammie Quon7e6f2692017-10-27 16:42:52503 left_rotation_angle = 90.0 * (base::i18n::IsRTL() ? 1 : -1);
Sammie Quon8250507f2018-02-23 00:08:28504
505 left_rotated_view_->OnBoundsUpdated(left_rotated_bounds,
Sammie Quone61bdc12018-03-17 02:05:54506 /*angle=*/left_rotation_angle);
Sammie Quon8250507f2018-02-23 00:08:28507 right_rotated_view_->OnBoundsUpdated(right_rotated_bounds,
Sammie Quone61bdc12018-03-17 02:05:54508 /*angle=*/-left_rotation_angle);
509
Avery Musbachabe3a2d2019-11-04 21:51:27510 if (drag_ending_in_snap) {
511 // Reset the label transforms, in preparation for the next drag (if any).
Avery Musbachc0891852019-03-28 22:14:15512 left_rotated_view_->layer()->SetTransform(gfx::Transform());
513 right_rotated_view_->layer()->SetTransform(gfx::Transform());
514 return;
515 }
516
Avery Musbachdb2c6222019-04-02 21:13:20517 ui::Layer* preview_label_layer;
518 ui::Layer* other_highlight_label_layer;
Avery Musbachabe3a2d2019-11-04 21:51:27519 if (snap_position == SplitViewController::NONE) {
520 preview_label_layer = nullptr;
521 other_highlight_label_layer = nullptr;
Avery Musbach5e294642019-12-06 21:39:19522 } else if (SplitViewController::IsPhysicalLeftOrTop(snap_position)) {
Avery Musbachdb2c6222019-04-02 21:13:20523 preview_label_layer = left_rotated_view_->layer();
524 other_highlight_label_layer = right_rotated_view_->layer();
525 } else {
526 preview_label_layer = right_rotated_view_->layer();
527 other_highlight_label_layer = left_rotated_view_->layer();
Sammie Quone61bdc12018-03-17 02:05:54528 }
529
Avery Musbachabe3a2d2019-11-04 21:51:27530 // Slide out the labels when a snap preview appears. This code also adjusts
531 // the label transforms for things like display rotation while there is a
532 // snap preview.
533 if (GetSnapPosition(window_dragging_state_) != SplitViewController::NONE) {
Avery Musbachdb2c6222019-04-02 21:13:20534 // How far each label shall slide to stay centered in the corresponding
535 // highlight as it expands/contracts. Include distance traveled with zero
536 // opacity (whence a label still slides, not only for simplicity in
537 // calculating the values below, but also to facilitate that the label
538 // transform and the highlight transform have matching easing).
539 const float preview_label_distance =
540 0.5f * (preview_area_bounds.width() - highlight_width);
541 const float other_highlight_label_distance =
542 0.5f * (highlight_width - other_highlight_width);
543
544 // Positive for right or down; negative for left or up.
545 float preview_label_delta, other_highlight_label_delta;
Avery Musbach5e294642019-12-06 21:39:19546 if (SplitViewController::IsPhysicalLeftOrTop(snap_position)) {
Avery Musbachdb2c6222019-04-02 21:13:20547 preview_label_delta = preview_label_distance;
548 other_highlight_label_delta = other_highlight_label_distance;
549 } else {
550 preview_label_delta = -preview_label_distance;
551 other_highlight_label_delta = -other_highlight_label_distance;
552 }
553
Avery Musbach5e294642019-12-06 21:39:19554 // x-axis if |horizontal|; else y-axis.
Avery Musbachdb2c6222019-04-02 21:13:20555 gfx::Transform preview_label_transform, other_highlight_label_transform;
Avery Musbach5e294642019-12-06 21:39:19556 if (horizontal) {
Avery Musbachdb2c6222019-04-02 21:13:20557 preview_label_transform.Translate(preview_label_delta, 0.f);
558 other_highlight_label_transform.Translate(other_highlight_label_delta,
559 0.f);
560 } else {
561 preview_label_transform.Translate(0.f, preview_label_delta);
562 other_highlight_label_transform.Translate(0.f,
563 other_highlight_label_delta);
564 }
565
566 if (animate) {
567 // Animate the labels sliding out.
568 DoSplitviewTransformAnimation(
569 preview_label_layer,
570 SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_OUT,
Xiaoqian Dai17c45e512019-12-19 00:14:22571 preview_label_transform, /*animation_observer=*/nullptr);
Avery Musbachdb2c6222019-04-02 21:13:20572 DoSplitviewTransformAnimation(
573 other_highlight_label_layer,
574 SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_OUT,
Xiaoqian Dai17c45e512019-12-19 00:14:22575 other_highlight_label_transform, /*animation_observer=*/nullptr);
Avery Musbachdb2c6222019-04-02 21:13:20576 } else {
577 // Put the labels where they belong.
578 preview_label_layer->SetTransform(preview_label_transform);
579 other_highlight_label_layer->SetTransform(
580 other_highlight_label_transform);
581 }
582 return;
583 }
584
Avery Musbachabe3a2d2019-11-04 21:51:27585 // Slide in the labels when a snap preview disappears because you drag
586 // inward. (Having reached this code, we know that the window is not
587 // becoming snapped, because that case is handled earlier and we bail out.)
588 if (GetSnapPosition(previous_window_dragging_state_) !=
589 SplitViewController::NONE) {
Avery Musbachdb2c6222019-04-02 21:13:20590 if (animate) {
591 // Animate the labels sliding in.
592 DoSplitviewTransformAnimation(
593 preview_label_layer, SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_IN,
Xiaoqian Dai17c45e512019-12-19 00:14:22594 gfx::Transform(), /*animation_observer=*/nullptr);
Avery Musbachdb2c6222019-04-02 21:13:20595 DoSplitviewTransformAnimation(
596 other_highlight_label_layer,
Xiaoqian Dai17c45e512019-12-19 00:14:22597 SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_IN, gfx::Transform(),
598 /*animation_observer=*/nullptr);
Avery Musbachdb2c6222019-04-02 21:13:20599 } else {
600 // Put the labels where they belong.
601 preview_label_layer->SetTransform(gfx::Transform());
602 other_highlight_label_layer->SetTransform(gfx::Transform());
603 }
604 return;
605 }
Sammie Quonbc2e913b2017-10-25 22:28:26606 }
607
Sammie Quona75d28c2018-05-08 16:39:57608 SplitViewHighlightView* left_highlight_view_ = nullptr;
609 SplitViewHighlightView* right_highlight_view_ = nullptr;
Sammie Quon4486fd62017-10-10 20:22:40610 RotatedImageLabelView* left_rotated_view_ = nullptr;
611 RotatedImageLabelView* right_rotated_view_ = nullptr;
612
Avery Musbachabe3a2d2019-11-04 21:51:27613 WindowDraggingState window_dragging_state_ = WindowDraggingState::kNoDrag;
614 WindowDraggingState previous_window_dragging_state_ =
615 WindowDraggingState::kNoDrag;
Sammie Quon3e582d642017-09-08 02:01:54616
Avery Musbachc32f919e2019-10-30 20:36:53617 aura::Window* dragged_window_ = nullptr;
618
Sammie Quonbe8f7b02018-03-14 02:08:17619 DISALLOW_COPY_AND_ASSIGN(SplitViewDragIndicatorsView);
Sammie Quon3e582d642017-09-08 02:01:54620};
621
Avery Musbach8f349292019-11-12 17:29:53622SplitViewDragIndicators::SplitViewDragIndicators(aura::Window* root_window) {
Avery Musbach8f349292019-11-12 17:29:53623 widget_ = CreateWidget(root_window);
624 widget_->SetBounds(GetWorkAreaBoundsNoOverlapWithShelf(root_window));
Wei Lieb7513bb2020-08-31 23:56:18625 indicators_view_ =
626 widget_->SetContentsView(std::make_unique<SplitViewDragIndicatorsView>());
Sammie Quon6c5cf1a2017-12-12 01:11:37627 widget_->Show();
Sammie Quon3e582d642017-09-08 02:01:54628}
629
Avery Musbachca258382019-03-26 22:27:33630SplitViewDragIndicators::~SplitViewDragIndicators() {
631 // Allow some extra time for animations to finish.
632 aura::Window* window = widget_->GetNativeWindow();
633 if (window == nullptr)
634 return;
James Cook00e65e92019-07-25 03:19:08635 wm::SetWindowVisibilityAnimationType(
636 window, WINDOW_VISIBILITY_ANIMATION_TYPE_STEP_END);
Avery Musbachca258382019-03-26 22:27:33637 AnimateOnChildWindowVisibilityChanged(window, /*visible=*/false);
638}
Sammie Quon3e582d642017-09-08 02:01:54639
Avery Musbachc32f919e2019-10-30 20:36:53640void SplitViewDragIndicators::SetDraggedWindow(aura::Window* dragged_window) {
Avery Musbachabe3a2d2019-11-04 21:51:27641 DCHECK_EQ(WindowDraggingState::kNoDrag, current_window_dragging_state_);
Avery Musbachc32f919e2019-10-30 20:36:53642 indicators_view_->SetDraggedWindow(dragged_window);
643}
644
Avery Musbachabe3a2d2019-11-04 21:51:27645void SplitViewDragIndicators::SetWindowDraggingState(
Avery Musbach8f349292019-11-12 17:29:53646 WindowDraggingState window_dragging_state) {
Avery Musbachabe3a2d2019-11-04 21:51:27647 if (window_dragging_state == current_window_dragging_state_)
Sammie Quon4486fd62017-10-10 20:22:40648 return;
Avery Musbachabe3a2d2019-11-04 21:51:27649 current_window_dragging_state_ = window_dragging_state;
650 indicators_view_->OnWindowDraggingStateChanged(window_dragging_state);
Sammie Quon3e582d642017-09-08 02:01:54651}
652
Sammie Quonbe8f7b02018-03-14 02:08:17653void SplitViewDragIndicators::OnDisplayBoundsChanged() {
Sammie Quon2a2d0462018-03-29 01:26:30654 aura::Window* root_window = widget_->GetNativeView()->GetRootWindow();
Min Chen10d413c2018-12-07 01:27:36655 widget_->SetBounds(GetWorkAreaBoundsNoOverlapWithShelf(root_window));
Sammie Quonbc2e913b2017-10-25 22:28:26656}
657
Sammie Quonbe8f7b02018-03-14 02:08:17658bool SplitViewDragIndicators::GetIndicatorTypeVisibilityForTesting(
Sammie Quon16869be2018-02-16 02:59:33659 IndicatorType type) const {
Sammie Quonbe8f7b02018-03-14 02:08:17660 return indicators_view_->GetViewForIndicatorType(type)->layer()->opacity() >
661 0.f;
Sammie Quon16869be2018-02-16 02:59:33662}
663
Sammie Quon3e9f42b2020-01-04 02:42:54664gfx::Rect SplitViewDragIndicators::GetLeftHighlightViewBounds() const {
Ahmed Fakhry4acdf652019-09-05 18:53:06665 return indicators_view_->left_highlight_view()->bounds();
666}
667
Sammie Quon3e582d642017-09-08 02:01:54668} // namespace ash