Merge lp:~3v1n0/unity/switcher-spread-animation-multimonitor-fix into lp:unity

Proposed by Marco Trevisan (Treviño)
Status: Merged
Approved by: Brandon Schaefer
Approved revision: no longer in the source branch.
Merged at revision: 3050
Proposed branch: lp:~3v1n0/unity/switcher-spread-animation-multimonitor-fix
Merge into: lp:unity
Diff against target: 497 lines (+256/-61)
6 files modified
launcher/SwitcherModel.h (+1/-3)
launcher/SwitcherView.cpp (+41/-39)
launcher/SwitcherView.h (+14/-16)
tests/CMakeLists.txt (+1/-0)
tests/test_switcher_controller.cpp (+1/-3)
tests/test_switcher_view.cpp (+198/-0)
To merge this branch: bzr merge lp:~3v1n0/unity/switcher-spread-animation-multimonitor-fix
Reviewer Review Type Date Requested Status
Brandon Schaefer (community) Approve
PS Jenkins bot continuous-integration Pending
Review via email: mp+143708@code.launchpad.net

Commit message

SwitcherView: correctly compute the windows positions in spread mode

Description of the change

In multimonitor environment, the switcher spread animation is not working, this code changes the way we compute the position of the detailed windows we have.

Added tests, and some general cleanup.

To post a comment you must log in.
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Awesome, looks good to me!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'launcher/SwitcherModel.h'
2--- launcher/SwitcherModel.h 2012-12-18 00:09:23 +0000
3+++ launcher/SwitcherModel.h 2013-01-17 15:37:28 +0000
4@@ -26,8 +26,6 @@
5 #include "LauncherModel.h"
6
7 #include "unity-shared/Introspectable.h"
8-
9-#include <boost/shared_ptr.hpp>
10 #include <sigc++/sigc++.h>
11
12 namespace unity
13@@ -53,7 +51,7 @@
14 {
15
16 public:
17- typedef boost::shared_ptr<SwitcherModel> Ptr;
18+ typedef std::shared_ptr<SwitcherModel> Ptr;
19
20 typedef std::vector<launcher::AbstractLauncherIcon::Ptr> Applications;
21 typedef Applications::iterator iterator;
22
23=== modified file 'launcher/SwitcherView.cpp'
24--- launcher/SwitcherView.cpp 2012-12-14 12:14:34 +0000
25+++ launcher/SwitcherView.cpp 2013-01-17 15:37:28 +0000
26@@ -137,7 +137,10 @@
27 text_view_->SetVisible(!detail);
28
29 if (!detail)
30+ {
31 text_view_->SetText(model_->Selection()->tooltip_text());
32+ render_targets_.clear();
33+ }
34
35 SaveLast();
36 QueueDraw();
37@@ -205,7 +208,7 @@
38 return result;
39 }
40
41-nux::Geometry SwitcherView::InterpolateBackground (nux::Geometry const& start, nux::Geometry const& end, float progress)
42+nux::Geometry SwitcherView::InterpolateBackground(nux::Geometry const& start, nux::Geometry const& end, float progress)
43 {
44 progress = -pow(progress - 1.0f, 2) + 1;
45
46@@ -219,12 +222,10 @@
47 return result;
48 }
49
50-nux::Geometry SwitcherView::UpdateRenderTargets(nux::Point const& center, timespec const& current)
51+nux::Geometry SwitcherView::UpdateRenderTargets(float progress)
52 {
53 std::vector<Window> const& xids = model_->DetailXids();
54-
55- DeltaTime ms_since_change = TimeUtil::TimeDelta(&current, &save_time_);
56- float progress = std::min<float>(1.0f, ms_since_change / static_cast<float>(animation_length()));
57+ render_targets_.clear();
58
59 for (Window window : xids)
60 {
61@@ -236,33 +237,39 @@
62 render_targets_.push_back(layout_window);
63 }
64
65- nux::Geometry max_bounds;
66- nux::Geometry const& absolute = GetAbsoluteGeometry();
67+ nux::Geometry max_bounds = GetAbsoluteGeometry();
68 nux::Size const& spread_size = SpreadSize();
69- max_bounds.x = absolute.x + center.x - spread_size.width / 2;
70- max_bounds.y = absolute.y + center.y - spread_size.height / 2;
71+ max_bounds.x -= spread_size.width / 2;
72+ max_bounds.y -= spread_size.height / 2;
73 max_bounds.width = spread_size.width;
74 max_bounds.height = spread_size.height;
75
76- nux::Geometry final_bounds;
77- layout_system_.LayoutWindows(render_targets_, max_bounds, final_bounds);
78-
79- if (progress < 1.0f)
80+ nux::Geometry layout_geo;
81+ layout_system_.LayoutWindows(render_targets_, max_bounds, layout_geo);
82+
83+ return layout_geo;
84+}
85+
86+void SwitcherView::ResizeRenderTargets(nux::Geometry const& layout_geo, float progress)
87+{
88+ if (progress >= 1.0f)
89+ return;
90+
91+ // Animate the windows thumbnail sizes to make them grow with the switcher
92+ float to_finish = 1.0f - progress;
93+ nux::Point layout_abs_center((layout_geo.x + layout_geo.width/2.0f) * to_finish,
94+ (layout_geo.y + layout_geo.height/2.0f) * to_finish);
95+
96+ for (LayoutWindow::Ptr const& win : render_targets_)
97 {
98- // Animate the windows thumbnail sizes to make them grow with the switcher
99- for (LayoutWindow::Ptr const& win : render_targets_)
100- {
101- auto old_geo = win->result;
102- win->result = old_geo * progress;
103- win->result.x += (old_geo.width - win->result.width) / 4;
104- win->result.y += (old_geo.height - win->result.height) / 4;
105- }
106+ auto final_geo = win->result;
107+ win->result = final_geo * progress;
108+ win->result.x += layout_abs_center.x;
109+ win->result.y += layout_abs_center.y;
110 }
111-
112- return final_bounds;
113 }
114
115-void SwitcherView::OffsetRenderTargets (int x, int y)
116+void SwitcherView::OffsetRenderTargets(int x, int y)
117 {
118 for (LayoutWindow::Ptr const& target : render_targets_)
119 {
120@@ -356,13 +363,11 @@
121 }
122 }
123
124-std::list<RenderArg> SwitcherView::RenderArgsFlat(nux::Geometry& background_geo, int selection, timespec const& current)
125+std::list<RenderArg> SwitcherView::RenderArgsFlat(nux::Geometry& background_geo, int selection, float progress)
126 {
127 std::list<RenderArg> results;
128 nux::Geometry const& base = GetGeometry();
129
130- render_targets_.clear ();
131-
132 bool detail_selection = model_->detail_selection;
133
134 background_geo.y = base.y + base.height / 2 - (vertical_size / 2);
135@@ -380,7 +385,8 @@
136 int spread_padded_width = 0;
137 if (detail_selection)
138 {
139- nux::Geometry const& spread_bounds = UpdateRenderTargets(nux::Point(), current);
140+ nux::Geometry const& spread_bounds = UpdateRenderTargets(progress);
141+ ResizeRenderTargets(spread_bounds, progress);
142 // remove extra space consumed by spread
143 spread_padded_width = spread_bounds.width + 100;
144 max_width -= spread_padded_width - tile_size;
145@@ -450,10 +456,7 @@
146
147 x += (half_size + flat_spacing) * scalar;
148
149- if (should_flat)
150- arg.render_center = nux::Point3((int) x, y, 0);
151- else
152- arg.render_center = nux::Point3(x, y, 0);
153+ arg.render_center = nux::Point3(should_flat ? std::floor(x) : x, y, 0);
154
155 x += (half_size + flat_spacing) * scalar;
156
157@@ -478,18 +481,15 @@
158 if (i == selection && detail_selection)
159 {
160 arg.skip = true;
161- OffsetRenderTargets (arg.render_center.x, arg.render_center.y);
162+ OffsetRenderTargets(arg.render_center.x, arg.render_center.y);
163 }
164
165 results.push_back(arg);
166 ++i;
167 }
168
169- DeltaTime ms_since_change = TimeUtil::TimeDelta(&current, &save_time_);
170- if (saved_args_.size () == results.size () && ms_since_change < animation_length)
171+ if (saved_args_.size () == results.size () && progress < 1.0f)
172 {
173- float progress = (float) ms_since_change / (float) animation_length();
174-
175 std::list<RenderArg> end = results;
176 results.clear();
177
178@@ -499,7 +499,7 @@
179 results.push_back(InterpolateRenderArgs(*start_it, *end_it, progress));
180 }
181
182- background_geo = InterpolateBackground (saved_background_, background_geo, progress);
183+ background_geo = InterpolateBackground(saved_background_, background_geo, progress);
184 }
185 }
186
187@@ -509,6 +509,8 @@
188 void SwitcherView::PreDraw(nux::GraphicsEngine& GfxContext, bool force_draw)
189 {
190 clock_gettime(CLOCK_MONOTONIC, &current_);
191+ DeltaTime ms_since_change = TimeUtil::TimeDelta(&current_, &save_time_);
192+ float progress = std::min<float>(1.0f, ms_since_change / static_cast<float>(animation_length()));
193
194 if (!target_sizes_set_)
195 {
196@@ -517,7 +519,7 @@
197 }
198
199 nux::Geometry background_geo;
200- last_args_ = RenderArgsFlat(background_geo, model_->SelectionIndex(), current_);
201+ last_args_ = RenderArgsFlat(background_geo, model_->SelectionIndex(), progress);
202 last_background_ = background_geo;
203
204 icon_renderer_->PreprocessIcons(last_args_, GetGeometry());
205
206=== modified file 'launcher/SwitcherView.h'
207--- launcher/SwitcherView.h 2012-12-14 12:14:34 +0000
208+++ launcher/SwitcherView.h 2013-01-17 15:37:28 +0000
209@@ -81,9 +81,9 @@
210 nux::Geometry GetBackgroundGeometry();
211
212 ui::RenderArg InterpolateRenderArgs(ui::RenderArg const& start, ui::RenderArg const& end, float progress);
213- nux::Geometry InterpolateBackground (nux::Geometry const& start, nux::Geometry const& end, float progress);
214+ nux::Geometry InterpolateBackground(nux::Geometry const& start, nux::Geometry const& end, float progress);
215
216- std::list<ui::RenderArg> RenderArgsFlat(nux::Geometry& background_geo, int selection, timespec const& current);
217+ std::list<ui::RenderArg> RenderArgsFlat(nux::Geometry& background_geo, int selection, float progress);
218
219 ui::RenderArg CreateBaseArgForIcon(launcher::AbstractLauncherIcon::Ptr const& icon);
220 private:
221@@ -94,20 +94,16 @@
222 void OnIconSizeChanged (int size);
223 void OnTileSizeChanged (int size);
224
225- nux::Geometry UpdateRenderTargets (nux::Point const& center, timespec const& current);
226- void OffsetRenderTargets (int x, int y);
227-
228- nux::Size SpreadSize ();
229-
230- void GetFlatIconPositions (int n_flat_icons,
231- int size,
232- int selection,
233- int &first_flat,
234- int &last_flat,
235- int &half_fold_left,
236- int &half_fold_right);
237-
238- void SaveLast ();
239+ nux::Geometry UpdateRenderTargets(float progress);
240+ void ResizeRenderTargets(nux::Geometry const& layout_geo, float progress);
241+ void OffsetRenderTargets(int x, int y);
242+
243+ nux::Size SpreadSize();
244+
245+ void GetFlatIconPositions(int n_flat_icons, int size, int selection,
246+ int &first_flat, int &last_flat,
247+ int &half_fold_left, int &half_fold_right);
248+ void SaveLast();
249
250 SwitcherModel::Ptr model_;
251 ui::LayoutSystem layout_system_;
252@@ -130,6 +126,8 @@
253 timespec save_time_;
254
255 glib::Source::UniquePtr redraw_idle_;
256+
257+ friend class TestSwitcherView;
258 };
259
260 }
261
262=== modified file 'tests/CMakeLists.txt'
263--- tests/CMakeLists.txt 2013-01-11 08:59:00 +0000
264+++ tests/CMakeLists.txt 2013-01-17 15:37:28 +0000
265@@ -236,6 +236,7 @@
266 test_static_cairo_text.cpp
267 test_switcher_controller.cpp
268 test_switcher_model.cpp
269+ test_switcher_view.cpp
270 test_texture_cache.cpp
271 test_text_input.cpp
272 test_thumbnail_generator.cpp
273
274=== modified file 'tests/test_switcher_controller.cpp'
275--- tests/test_switcher_controller.cpp 2013-01-09 20:31:16 +0000
276+++ tests/test_switcher_controller.cpp 2013-01-17 15:37:28 +0000
277@@ -60,8 +60,7 @@
278 EXPECT_EQ(controller.GetConstructTimeout(), DEFAULT_LAZY_CONSTRUCT_TIMEOUT);
279 }
280
281-/*
282-TEST(TestSwitcherController, LazyWindowConstruction)
283+TEST(TestSwitcherController, DISABLED_LazyWindowConstruction)
284 {
285 // Setting the timeout to a lower value to speed-up the test
286 StubSwitcherController controller(2);
287@@ -78,7 +77,6 @@
288 Utils::WaitUntil(controller.window_constructed_, controller.GetConstructTimeout() + 1);
289 EXPECT_TRUE(controller.window_constructed_);
290 }
291-*/
292
293 TEST(TestSwitcherController, InitialDetailTimeout)
294 {
295
296=== added file 'tests/test_switcher_view.cpp'
297--- tests/test_switcher_view.cpp 1970-01-01 00:00:00 +0000
298+++ tests/test_switcher_view.cpp 2013-01-17 15:37:28 +0000
299@@ -0,0 +1,198 @@
300+/*
301+ * Copyright 2013 Canonical Ltd.
302+ *
303+ * This program is free software: you can redistribute it and/or modify it
304+ * under the terms of the GNU General Public License version 3, as published
305+ * by the Free Software Foundation.
306+ *
307+ * This program is distributed in the hope that it will be useful, but
308+ * WITHOUT ANY WARRANTY; without even the implied warranties of
309+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
310+ * PURPOSE. See the GNU General Public License for more details.
311+ *
312+ * You should have received a copy of the GNU General Public License
313+ * version 3 along with this program. If not, see
314+ * <http://www.gnu.org/licenses/>
315+ *
316+ * Authored by: Marco Trevisan <marco.trevisan@canonical.com>
317+ *
318+ */
319+
320+#include <gtest/gtest.h>
321+
322+#include "SwitcherModel.h"
323+#include "SwitcherView.h"
324+#include "MockLauncherIcon.h"
325+#include "StandaloneWindowManager.h"
326+#include "unity-shared/IconRenderer.h"
327+#include "unity-shared/UnitySettings.h"
328+
329+namespace unity
330+{
331+using namespace launcher;
332+namespace switcher
333+{
334+namespace
335+{
336+ struct MockMockLauncherIcon : MockLauncherIcon
337+ {
338+ WindowList Windows() { return windows_; }
339+ void AddWindow(Window xid) { windows_.push_back(std::make_shared<MockApplicationWindow>(xid)); }
340+
341+ WindowList windows_;
342+ };
343+
344+ int rand_coord() { return g_random_int_range(1, 1024); }
345+}
346+
347+struct TestSwitcherView : testing::Test
348+{
349+ TestSwitcherView()
350+ : WM(dynamic_cast<StandaloneWindowManager*>(&WindowManager::Default()))
351+ {}
352+
353+ struct MockSwitcherView : SwitcherView
354+ {
355+ using SwitcherView::UpdateRenderTargets;
356+ using SwitcherView::ResizeRenderTargets;
357+ using SwitcherView::SpreadSize;
358+ using SwitcherView::text_view_;
359+ using SwitcherView::icon_renderer_;
360+ using SwitcherView::model_;
361+ };
362+
363+ StandaloneWindow::Ptr AddFakeWindowToWM(Window xid)
364+ {
365+ const unsigned top_deco = 5;
366+ auto fake_window = std::make_shared<StandaloneWindow>(xid);
367+ fake_window->geo = nux::Geometry(rand_coord(), rand_coord(), rand_coord(), rand_coord());
368+ fake_window->deco_sizes[unsigned(WindowManager::Edge::TOP)] = nux::Size(fake_window->geo.width, top_deco);
369+
370+ WM->AddStandaloneWindow(fake_window);
371+
372+ return fake_window;
373+ }
374+
375+ AbstractLauncherIcon::Ptr AddFakeApplicationToSwitcher(unsigned num_of_windows = 5)
376+ {
377+ MockMockLauncherIcon* app = new MockMockLauncherIcon();
378+
379+ for (unsigned i = 0; i < num_of_windows; ++i)
380+ {
381+ Window xid = g_random_int();
382+ AddFakeWindowToWM(xid);
383+ app->AddWindow(xid);
384+ }
385+
386+ SwitcherModel::Applications apps;
387+ apps.push_back(AbstractLauncherIcon::Ptr(app));
388+ switcher.SetModel(std::make_shared<SwitcherModel>(apps));
389+
390+ return apps[0];
391+ }
392+
393+ StandaloneWindowManager* WM;
394+ unity::Settings settings;
395+ MockSwitcherView switcher;
396+};
397+
398+TEST_F(TestSwitcherView, Initiate)
399+{
400+ EXPECT_FALSE(switcher.render_boxes);
401+ EXPECT_EQ(switcher.border_size, 50);
402+ EXPECT_EQ(switcher.flat_spacing, 10);
403+ EXPECT_EQ(switcher.icon_size, 128);
404+ EXPECT_EQ(switcher.minimum_spacing, 10);
405+ EXPECT_EQ(switcher.tile_size, 150);
406+ EXPECT_EQ(switcher.vertical_size, switcher.tile_size + 80);
407+ EXPECT_EQ(switcher.text_size, 15);
408+ EXPECT_EQ(switcher.animation_length, 250);
409+ EXPECT_EQ(switcher.monitor, -1);
410+ EXPECT_EQ(switcher.spread_size, 3.5f);
411+ ASSERT_NE(switcher.text_view_, nullptr);
412+ ASSERT_NE(switcher.icon_renderer_, nullptr);
413+ EXPECT_EQ(switcher.icon_renderer_->pip_style, ui::OVER_TILE);
414+}
415+
416+TEST_F(TestSwitcherView, SetModel)
417+{
418+ SwitcherModel::Applications apps;
419+ apps.push_back(AbstractLauncherIcon::Ptr(new MockLauncherIcon()));
420+ apps.push_back(AbstractLauncherIcon::Ptr(new MockLauncherIcon()));
421+ apps.push_back(AbstractLauncherIcon::Ptr(new MockLauncherIcon()));
422+ auto model = std::make_shared<SwitcherModel>(apps);
423+
424+ switcher.SetModel(model);
425+ ASSERT_EQ(switcher.model_, model);
426+ ASSERT_EQ(switcher.GetModel(), model);
427+ EXPECT_FALSE(switcher.model_->selection_changed.empty());
428+ EXPECT_FALSE(switcher.model_->detail_selection.changed.empty());
429+ EXPECT_FALSE(switcher.model_->detail_selection_index.changed.empty());
430+}
431+
432+TEST_F(TestSwitcherView, UpdateRenderTargets)
433+{
434+ float progress = 0.55;
435+
436+ AddFakeApplicationToSwitcher();
437+ auto const& model = switcher.GetModel();
438+
439+ switcher.UpdateRenderTargets(progress);
440+ auto const& render_targets = switcher.ExternalTargets();
441+
442+ ASSERT_EQ(render_targets.size(), model->DetailXids().size());
443+
444+ for (Window xid : model->DetailXids())
445+ {
446+ auto win_it = std::find_if (render_targets.begin(), render_targets.end(),
447+ [xid] (ui::LayoutWindow::Ptr const& win) { return win->xid == xid; });
448+
449+ ASSERT_NE(win_it, render_targets.end());
450+ auto const& layout_win = *win_it;
451+ bool should_be_selected = (xid == model->DetailSelectionWindow());
452+ ASSERT_EQ(layout_win->selected, should_be_selected);
453+ ASSERT_EQ(layout_win->alpha, (should_be_selected ? 1.0f : 0.9f) * progress);
454+ }
455+}
456+
457+TEST_F(TestSwitcherView, ResizeRenderTargets)
458+{
459+ AddFakeApplicationToSwitcher();
460+
461+ for (float progress = 0.1f; progress <= 1.0f; progress += 0.1f)
462+ {
463+ auto const& layout_geo = switcher.UpdateRenderTargets(progress);
464+ std::map<Window, nux::Geometry> old_thumbs;
465+
466+ for (auto const& win : switcher.ExternalTargets())
467+ old_thumbs[win->xid] = win->result;
468+
469+ float to_finish = 1.0f - progress;
470+ nux::Point layout_abs_center((layout_geo.x + layout_geo.width/2.0f) * to_finish,
471+ (layout_geo.y + layout_geo.height/2.0f) * to_finish);
472+
473+ switcher.ResizeRenderTargets(layout_geo, progress);
474+
475+ for (auto const& win : switcher.ExternalTargets())
476+ {
477+ auto const& thumb_geo = win->result;
478+ auto const& old_thumb_geo = old_thumbs[win->xid];
479+
480+ nux::Geometry expected_geo;
481+ expected_geo.x = old_thumb_geo.x * progress + layout_abs_center.x;
482+ expected_geo.y = old_thumb_geo.y * progress + layout_abs_center.y;
483+ expected_geo.width = old_thumb_geo.width * progress;
484+ expected_geo.height = old_thumb_geo.height * progress;
485+
486+ // Like ASSERT_EQ(thumb_geo, expected_geo), but more informative on failure
487+ ASSERT_EQ(thumb_geo.x, expected_geo.x);
488+ ASSERT_EQ(thumb_geo.y, expected_geo.y);
489+ ASSERT_EQ(thumb_geo.width, expected_geo.width);
490+ ASSERT_EQ(thumb_geo.height, expected_geo.height);
491+ ASSERT_EQ(thumb_geo, expected_geo);
492+ }
493+ }
494+}
495+
496+}
497+}