1 // Copyright 2018 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 
5 #include "ash/wm/pip/pip_positioner.h"
6 
7 #include <memory>
8 #include <string>
9 #include <tuple>
10 #include <vector>
11 
12 #include "ash/keyboard/ui/keyboard_ui_controller.h"
13 #include "ash/keyboard/ui/test/keyboard_test_util.h"
14 #include "ash/public/cpp/keyboard/keyboard_switches.h"
15 #include "ash/root_window_controller.h"
16 #include "ash/shelf/shelf.h"
17 #include "ash/shell.h"
18 #include "ash/system/unified/unified_system_tray.h"
19 #include "ash/test/ash_test_base.h"
20 #include "ash/wm/pip/pip_test_utils.h"
21 #include "ash/wm/window_state.h"
22 #include "ash/wm/wm_event.h"
23 #include "base/callback_helpers.h"
24 #include "base/command_line.h"
25 #include "ui/aura/window.h"
26 #include "ui/display/scoped_display_for_new_windows.h"
27 #include "ui/gfx/geometry/insets.h"
28 #include "ui/wm/core/coordinate_conversion.h"
29 
30 namespace ash {
31 
32 namespace {
33 
GetDisplayForWindow(aura::Window * window)34 display::Display GetDisplayForWindow(aura::Window* window) {
35   return display::Screen::GetScreen()->GetDisplayNearestWindow(window);
36 }
37 
ConvertToScreenForWindow(aura::Window * window,const gfx::Rect & bounds)38 gfx::Rect ConvertToScreenForWindow(aura::Window* window,
39                                    const gfx::Rect& bounds) {
40   gfx::Rect new_bounds = bounds;
41   ::wm::ConvertRectToScreen(window->GetRootWindow(), &new_bounds);
42   return new_bounds;
43 }
44 
45 }  // namespace
46 
47 class PipPositionerDisplayTest : public AshTestBase,
48                                  public ::testing::WithParamInterface<
49                                      std::tuple<std::string, std::size_t>> {
50  public:
SetUp()51   void SetUp() override {
52     base::CommandLine::ForCurrentProcess()->AppendSwitch(
53         keyboard::switches::kEnableVirtualKeyboard);
54     AshTestBase::SetUp();
55 
56     const std::string& display_string = std::get<0>(GetParam());
57     const std::size_t root_window_index = std::get<1>(GetParam());
58     UpdateWorkArea(display_string);
59     ASSERT_LT(root_window_index, Shell::GetAllRootWindows().size());
60     root_window_ = Shell::GetAllRootWindows()[root_window_index];
61     scoped_display_ =
62         std::make_unique<display::ScopedDisplayForNewWindows>(root_window_);
63     ForceHideShelvesForTest();
64   }
65 
TearDown()66   void TearDown() override {
67     scoped_display_.reset();
68     AshTestBase::TearDown();
69   }
70 
71  protected:
GetDisplay()72   display::Display GetDisplay() { return GetDisplayForWindow(root_window_); }
73 
root_window()74   aura::Window* root_window() { return root_window_; }
75 
ConvertToScreen(const gfx::Rect & bounds)76   gfx::Rect ConvertToScreen(const gfx::Rect& bounds) {
77     return ConvertToScreenForWindow(root_window_, bounds);
78   }
79 
UpdateWorkArea(const std::string & bounds)80   void UpdateWorkArea(const std::string& bounds) {
81     UpdateDisplay(bounds);
82     for (aura::Window* root : Shell::GetAllRootWindows())
83       Shell::Get()->SetDisplayWorkAreaInsets(root, gfx::Insets());
84   }
85 
86  private:
87   std::unique_ptr<display::ScopedDisplayForNewWindows> scoped_display_;
88   aura::Window* root_window_;
89 };
90 
TEST_P(PipPositionerDisplayTest,PipAdjustPositionForDragClampsToMovementArea)91 TEST_P(PipPositionerDisplayTest, PipAdjustPositionForDragClampsToMovementArea) {
92   auto display = GetDisplay();
93 
94   // Adjust near top edge outside movement area.
95   EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 8, 100, 100)),
96             PipPositioner::GetBoundsForDrag(
97                 display, ConvertToScreen(gfx::Rect(100, -50, 100, 100))));
98 
99   // Adjust near bottom edge outside movement area.
100   EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 292, 100, 100)),
101             PipPositioner::GetBoundsForDrag(
102                 display, ConvertToScreen(gfx::Rect(100, 450, 100, 100))));
103 
104   // Adjust near left edge outside movement area.
105   EXPECT_EQ(ConvertToScreen(gfx::Rect(8, 100, 100, 100)),
106             PipPositioner::GetBoundsForDrag(
107                 display, ConvertToScreen(gfx::Rect(-50, 100, 100, 100))));
108 
109   // Adjust near right edge outside movement area.
110   EXPECT_EQ(ConvertToScreen(gfx::Rect(292, 100, 100, 100)),
111             PipPositioner::GetBoundsForDrag(
112                 display, ConvertToScreen(gfx::Rect(450, 100, 100, 100))));
113 }
114 
TEST_P(PipPositionerDisplayTest,PipDismissedPositionDoesNotMoveAnExcessiveDistance)115 TEST_P(PipPositionerDisplayTest,
116        PipDismissedPositionDoesNotMoveAnExcessiveDistance) {
117   auto display = GetDisplay();
118 
119   EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 100, 100, 100)),
120             PipPositioner::GetDismissedPosition(
121                 display, ConvertToScreen(gfx::Rect(100, 100, 100, 100))));
122 }
123 
TEST_P(PipPositionerDisplayTest,PipDismissedPositionChosesClosestEdge)124 TEST_P(PipPositionerDisplayTest, PipDismissedPositionChosesClosestEdge) {
125   auto display = GetDisplay();
126 
127   // Dismiss near top edge outside movement area towards top.
128   EXPECT_EQ(ConvertToScreen(gfx::Rect(100, -100, 100, 100)),
129             PipPositioner::GetDismissedPosition(
130                 display, ConvertToScreen(gfx::Rect(100, 50, 100, 100))));
131 
132   // Dismiss near bottom edge outside movement area towards bottom.
133   EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 400, 100, 100)),
134             PipPositioner::GetDismissedPosition(
135                 display, ConvertToScreen(gfx::Rect(100, 250, 100, 100))));
136 
137   // Dismiss near left edge outside movement area towards left.
138   EXPECT_EQ(ConvertToScreen(gfx::Rect(-100, 100, 100, 100)),
139             PipPositioner::GetDismissedPosition(
140                 display, ConvertToScreen(gfx::Rect(50, 100, 100, 100))));
141 
142   // Dismiss near right edge outside movement area towards right.
143   EXPECT_EQ(ConvertToScreen(gfx::Rect(400, 100, 100, 100)),
144             PipPositioner::GetDismissedPosition(
145                 display, ConvertToScreen(gfx::Rect(250, 100, 100, 100))));
146 }
147 
148 // Verify that if two edges are equally close, the PIP window prefers dismissing
149 // out horizontally.
TEST_P(PipPositionerDisplayTest,PipDismissedPositionPrefersHorizontal)150 TEST_P(PipPositionerDisplayTest, PipDismissedPositionPrefersHorizontal) {
151   auto display = GetDisplay();
152 
153   // Top left corner.
154   EXPECT_EQ(ConvertToScreen(gfx::Rect(-150, 0, 100, 100)),
155             PipPositioner::GetDismissedPosition(
156                 display, ConvertToScreen(gfx::Rect(0, 0, 100, 100))));
157 
158   // Top right corner.
159   EXPECT_EQ(ConvertToScreen(gfx::Rect(450, 0, 100, 100)),
160             PipPositioner::GetDismissedPosition(
161                 display, ConvertToScreen(gfx::Rect(300, 0, 100, 100))));
162 
163   // Bottom left corner.
164   EXPECT_EQ(ConvertToScreen(gfx::Rect(-150, 300, 100, 100)),
165             PipPositioner::GetDismissedPosition(
166                 display, ConvertToScreen(gfx::Rect(0, 300, 100, 100))));
167 
168   // Bottom right corner.
169   EXPECT_EQ(ConvertToScreen(gfx::Rect(450, 300, 100, 100)),
170             PipPositioner::GetDismissedPosition(
171                 display, ConvertToScreen(gfx::Rect(300, 300, 100, 100))));
172 }
173 
174 
175 }  // namespace ash
176