Merge lp:~brandontschaefer/unity/alt+tab-mouse into lp:unity

Proposed by Brandon Schaefer
Status: Merged
Approved by: Brandon Schaefer
Approved revision: no longer in the source branch.
Merged at revision: 3462
Proposed branch: lp:~brandontschaefer/unity/alt+tab-mouse
Merge into: lp:unity
Diff against target: 1214 lines (+599/-73)
16 files modified
launcher/StandaloneSwitcher.cpp (+0/-1)
launcher/SwitcherController.cpp (+59/-23)
launcher/SwitcherController.h (+1/-2)
launcher/SwitcherControllerImpl.h (+2/-1)
launcher/SwitcherModel.cpp (+6/-0)
launcher/SwitcherModel.h (+1/-0)
launcher/SwitcherView.cpp (+241/-6)
launcher/SwitcherView.h (+35/-3)
plugins/unityshell/src/unityshell.cpp (+73/-35)
plugins/unityshell/src/unityshell.h (+3/-0)
tests/autopilot/unity/emulators/switcher.py (+14/-0)
tests/autopilot/unity/tests/test_switcher.py (+99/-0)
tests/test_switcher_controller.cpp (+28/-0)
unity-shared/AbstractIconRenderer.h (+15/-1)
unity-shared/LayoutSystem.cpp (+14/-0)
unity-shared/LayoutSystem.h (+8/-1)
To merge this branch: bzr merge lp:~brandontschaefer/unity/alt+tab-mouse
Reviewer Review Type Date Requested Status
Marco Trevisan (Treviño) Approve
PS Jenkins bot (community) continuous-integration Approve
Christopher Townsend Approve
Review via email: mp+174898@code.launchpad.net

Commit message

Adds the ability to use the mouse in the switcher.

Description of the change

We use compiz to grab the screen, and from there send all the grabbed events down to nux, which then nux emits them back up to the SwitcherView.

From there we have to figure out where the mouse is located x/y, if its over an icon change selection, same with detail mode. As the SwitcherView takes up most of the screen.

Right clicking toggles detail mode.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

I like a lot the change and the cleanup in event handling for the switcher we had. Even more that we can reuse some code we wrote to get things work easily.
Some comments.

It would be nice if you could abastract a little these signal names:

right_clicked_icon (detail request?)
mouse_moving_over_icon (mouse_move?)

131 + introspection_results_.clear();

Do we really need to keep them around? Why not just a local var?

Anyway:
135 + for (auto target : render_targets_)

322 + int half_size = icon_size.Get() / 2 + 10;

10? Magic number? :)

use auto const& please.

Also there are other behaviors to fix:
* The logic of the scroll wheel, as now here it's inverted to the one we had before.
* The click activation, as it should make sure that you activate an icon only if the mouse-up happened where mouse down happened before.
* The detail selection doesn't work on secondary monitors.
* It would be nice to remove the spread offset (or at least to define it in the controller, just once)

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Christopher Townsend (townsend) wrote :

Jenkins failures are due to an undefined member:

/tmp/buildd/unity-7.0.2+13.10.20130705.1bzr3437pkg0saucy156/launcher/StandaloneSwitcher.cpp: In function 'void ThreadWidgetInit(nux::NThread*, void*)':
/tmp/buildd/unity-7.0.2+13.10.20130705.1bzr3437pkg0saucy156/launcher/StandaloneSwitcher.cpp:134:15: error: 'class unity::switcher::Controller' has no member named 'SetWorkspace'
   controller->SetWorkspace(nux::Geometry(0, 0, 900, 600), 0);
               ^

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

Good... Some comments:

124 + monitor_geo.x += XY_OFFSET;
125 + monitor_geo.y += XY_OFFSET;
126 + monitor_geo.width -= WH_OFFSET;
127 + monitor_geo.height -= WH_OFFSET;

What about monitor_geo.OffsetSize() and monitor_geo.OffsetPosition() ?

139 + view_->hide_request.connect ([this] (bool activate) { Hide(activate); });

Since both have a bool as parameter, you can just use sigc::mem_fun or std::bind there...

994
  LayoutWindow::Ptr layout_window(0);

No need to set it to 0, however you can also just return inside the loop and return nullptr otherwise, g++ will be smart enough to convert.

UnityScreen::get (screen)

Get rid of the spaces

825 + LayoutWindow::Ptr const &layout_window = UnityScreen::get(screen)->
826 + GetSwitcherDetailLayoutWindow(window->id());

One line please (in case use auto const&).

Some code in lines 659-673 and 681-696 (middle click mouse handling in UnityWindow) is duplicated, what about adding a function to check if the position hits a window thumbnail (that basically checks the case of the switcher and of the scale).

I've also incurred into a regression: if I select the top-left window in detail mode (or the only one existent in case we have just one), then the switcher selects the window but it doesn't hide.

Finally, this not happen always, but when I do click -> move the mouse outside the clicked area -> release, the switcher closes

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Christopher Townsend (townsend) wrote :

I'm running all of the unity.tests.test_switcher.* AP tests and getting a Compiz crash in (I think) the test_switcher_scroll_next (show_desktop_icon_true) test.

The stack trace shows the crash manifests itself in unity/launcher/SwitcherModel.cpp on line 154 which is new to this MP.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Christopher Townsend (townsend) wrote :

The unity.tests.test_switcher.SwitcherWindowsManagementTests.test_switcher_rises_other_application AP test fails with this MP. It does not fail with trunk.

review: Needs Fixing
Revision history for this message
Christopher Townsend (townsend) wrote :

Well, I think the failing AP test is caused by something on my machine. It failed twice in a row, then after that, has passed at least 5 times with no issue.

Since Marco has weighed in and all of his concerns are addressed and now all AP tests pass, I'm going to approve this thing.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

Cool, I see all the changes I requested are in the merged version, nice! :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'launcher/StandaloneSwitcher.cpp'
2--- launcher/StandaloneSwitcher.cpp 2013-02-06 17:27:24 +0000
3+++ launcher/StandaloneSwitcher.cpp 2013-08-09 17:51:48 +0000
4@@ -131,7 +131,6 @@
5 nux::VLayout* layout = new nux::VLayout(TEXT(""), NUX_TRACKER_LOCATION);
6
7 controller = std::make_shared<Controller>();
8- controller->SetWorkspace(nux::Geometry(0, 0, 900, 600), 0);
9
10 layout->SetContentDistribution(nux::MAJOR_POSITION_CENTER);
11 layout->SetHorizontalExternalMargin (10);
12
13=== modified file 'launcher/SwitcherController.cpp'
14--- launcher/SwitcherController.cpp 2013-06-18 19:04:55 +0000
15+++ launcher/SwitcherController.cpp 2013-08-09 17:51:48 +0000
16@@ -23,6 +23,7 @@
17
18 #include "unity-shared/UBusMessages.h"
19 #include "unity-shared/WindowManager.h"
20+#include "unity-shared/UScreen.h"
21
22 #include "SwitcherController.h"
23 #include "SwitcherControllerImpl.h"
24@@ -41,6 +42,8 @@
25 const std::string DETAIL_TIMEOUT = "detail-timeout";
26 const std::string VIEW_CONSTRUCT_IDLE = "view-construct-idle";
27 const unsigned FADE_DURATION = 80;
28+const int XY_OFFSET = 100;
29+const int WH_OFFSET = -200;
30
31 /**
32 * Helper comparison functor for sorting application icons.
33@@ -78,7 +81,6 @@
34 , impl_(new Controller::Impl(this, 20, create_window))
35 {}
36
37-
38 Controller::~Controller()
39 {}
40
41@@ -93,6 +95,9 @@
42 SortMode sort,
43 std::vector<AbstractLauncherIcon::Ptr> results)
44 {
45+ auto uscreen = UScreen::GetDefault();
46+ monitor_ = uscreen->GetMonitorWithMouse();
47+
48 impl_->Show(show, sort, results);
49 }
50
51@@ -102,15 +107,6 @@
52 impl_->model_->Select(index);
53 }
54
55-void Controller::SetWorkspace(nux::Geometry geo, int monitor)
56-{
57- monitor_ = monitor;
58- impl_->workarea_ = geo;
59-
60- if (impl_->view_)
61- impl_->view_->monitor = monitor_;
62-}
63-
64 void Controller::Hide(bool accept_state)
65 {
66 if (Visible())
67@@ -124,6 +120,14 @@
68 return visible_;
69 }
70
71+nux::Geometry Controller::GetInputWindowGeometry() const
72+{
73+ if (impl_->view_window_)
74+ return impl_->view_window_->GetGeometry();
75+
76+ return {0, 0, 0, 0};
77+}
78+
79 bool Controller::StartDetailMode()
80 {
81 if (visible_)
82@@ -333,6 +337,7 @@
83 obj_->AddChild(model_.get());
84 model_->selection_changed.connect(sigc::mem_fun(this, &Controller::Impl::OnModelSelectionChanged));
85 model_->detail_selection.changed.connect([this] (bool) { sources_.Remove(DETAIL_TIMEOUT); });
86+ model_->request_detail_hide.connect(sigc::mem_fun(this, &Controller::Impl::DetailHide));
87 model_->only_detail_on_viewport = (show == ShowMode::CURRENT_VIEWPORT);
88
89 SelectFirstItem();
90@@ -350,17 +355,21 @@
91 ShowView();
92 }
93
94- if (obj_->detail_on_timeout)
95- {
96- auto cb_func = sigc::mem_fun(this, &Controller::Impl::OnDetailTimer);
97- sources_.AddTimeout(obj_->initial_detail_timeout_length, cb_func, DETAIL_TIMEOUT);
98- }
99+ ResetDetailTimer(obj_->initial_detail_timeout_length);
100
101 ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST);
102 ubus_manager_.SendMessage(UBUS_SWITCHER_SHOWN,
103 g_variant_new("(bi)", true, obj_->monitor_));
104 }
105
106+void Controller::Impl::ResetDetailTimer(int timeout_length)
107+{
108+ if (obj_->detail_on_timeout)
109+ {
110+ auto cb_func = sigc::mem_fun(this, &Controller::Impl::OnDetailTimer);
111+ sources_.AddTimeout(timeout_length, cb_func, DETAIL_TIMEOUT);
112+ }
113+}
114
115 bool Controller::Impl::OnDetailTimer()
116 {
117@@ -375,11 +384,7 @@
118
119 void Controller::Impl::OnModelSelectionChanged(AbstractLauncherIcon::Ptr const& icon)
120 {
121- if (obj_->detail_on_timeout)
122- {
123- auto cb_func = sigc::mem_fun(this, &Controller::Impl::OnDetailTimer);
124- sources_.AddTimeout(obj_->detail_timeout_length, cb_func, DETAIL_TIMEOUT);
125- }
126+ ResetDetailTimer(obj_->detail_timeout_length);
127
128 if (icon)
129 {
130@@ -434,10 +439,21 @@
131 view_window_->SetOpacity(0.0f);
132 view_window_->SetLayout(main_layout_);
133 view_window_->SetBackgroundColor(nux::color::Transparent);
134- view_window_->SetGeometry(workarea_);
135 }
136 }
137
138+nux::Geometry GetSwitcherViewsGeometry()
139+{
140+ auto uscreen = UScreen::GetDefault();
141+ int monitor = uscreen->GetMonitorWithMouse();
142+ auto monitor_geo = uscreen->GetMonitorGeometry(monitor);
143+
144+ monitor_geo.OffsetPosition(XY_OFFSET, XY_OFFSET);
145+ monitor_geo.OffsetSize(WH_OFFSET, WH_OFFSET);
146+
147+ return monitor_geo;
148+}
149+
150 void Controller::Impl::ConstructView()
151 {
152 if (view_ || !model_)
153@@ -451,10 +467,22 @@
154 view_->background_color = bg_color_;
155 view_->monitor = obj_->monitor_;
156
157+ view_->hide_request.connect(sigc::mem_fun(this, &Controller::Impl::Hide));
158+
159+ view_->switcher_mouse_up.connect([this] (int icon_index, int button) {
160+ if (button == 3)
161+ InitiateDetail(true);
162+ });
163+
164+ view_->switcher_mouse_move.connect([this] (int icon_index) {
165+ if (icon_index >= 0)
166+ ResetDetailTimer(obj_->detail_timeout_length);
167+ });
168+
169 ConstructWindow();
170 main_layout_->AddView(view_.GetPointer(), 1);
171 view_window_->SetEnterFocusInputArea(view_.GetPointer());
172- view_window_->SetGeometry(workarea_);
173+ view_window_->SetGeometry(GetSwitcherViewsGeometry());
174
175 view_built.emit();
176 }
177@@ -491,6 +519,14 @@
178 }
179 }
180
181+void Controller::Impl::DetailHide()
182+{
183+ // FIXME We need to refactor SwitcherModel so we can add/remove icons without causing
184+ // a crash. If you remove the last application in the list it crashes.
185+ model_->detail_selection = false;
186+ Hide(false);
187+}
188+
189 void Controller::Impl::HideWindow()
190 {
191 main_layout_->RemoveChildObject(view_.GetPointer());
192@@ -579,7 +615,7 @@
193 obj_->detail_mode_ = DetailMode::TAB_NEXT_WINDOW;
194 }
195 else
196- {
197+ {
198 model_->detail_selection = false;
199 }
200 }
201
202=== modified file 'launcher/SwitcherController.h'
203--- launcher/SwitcherController.h 2013-06-18 18:48:31 +0000
204+++ launcher/SwitcherController.h 2013-08-09 17:51:48 +0000
205@@ -88,6 +88,7 @@
206 bool CanShowSwitcher(const std::vector<launcher::AbstractLauncherIcon::Ptr>& resutls) const;
207
208 bool Visible();
209+ nux::Geometry GetInputWindowGeometry() const;
210
211 bool StartDetailMode();
212 bool StopDetailMode();
213@@ -107,8 +108,6 @@
214
215 void SelectFirstItem();
216
217- void SetWorkspace(nux::Geometry geo, int monitor);
218-
219 nux::ObjectPtr<SwitcherView> GetView() const;
220
221 ui::LayoutWindow::Vector ExternalRenderTargets();
222
223=== modified file 'launcher/SwitcherControllerImpl.h'
224--- launcher/SwitcherControllerImpl.h 2013-06-14 00:23:34 +0000
225+++ launcher/SwitcherControllerImpl.h 2013-08-09 17:51:48 +0000
226@@ -48,6 +48,7 @@
227
228 void Show(ShowMode show, SortMode sort, std::vector<launcher::AbstractLauncherIcon::Ptr> results);
229 void Hide(bool accept_state);
230+ void DetailHide();
231
232 void Next();
233 void Prev();
234@@ -82,6 +83,7 @@
235 void ShowView();
236 void HideWindow();
237
238+ void ResetDetailTimer(int timeout_length);
239 bool OnDetailTimer();
240 void OnModelSelectionChanged(launcher::AbstractLauncherIcon::Ptr const& icon);
241 void OnBackgroundUpdate(GVariant* data);
242@@ -94,7 +96,6 @@
243 SwitcherView::Ptr view_;
244
245 // @todo move these view data into the SwitcherView class
246- nux::Geometry workarea_;
247 Controller::WindowCreator create_window_;
248 MockableBaseWindow::Ptr view_window_;
249 nux::HLayout* main_layout_;
250
251=== modified file 'launcher/SwitcherModel.cpp'
252--- launcher/SwitcherModel.cpp 2013-06-20 20:46:40 +0000
253+++ launcher/SwitcherModel.cpp 2013-08-09 17:51:48 +0000
254@@ -149,6 +149,12 @@
255 results.push_back(xid);
256 }
257
258+ if (results.empty() && detail_selection)
259+ {
260+ request_detail_hide.emit();
261+ return results;
262+ }
263+
264 std::sort(results.begin(), results.end(), [&wm](Window first, Window second) {
265 return wm.GetWindowActiveNumber(first) > wm.GetWindowActiveNumber(second);
266 });
267
268=== modified file 'launcher/SwitcherModel.h'
269--- launcher/SwitcherModel.h 2013-06-17 23:56:23 +0000
270+++ launcher/SwitcherModel.h 2013-08-09 17:51:48 +0000
271@@ -101,6 +101,7 @@
272 void Select(unsigned int index);
273
274 sigc::signal<void, launcher::AbstractLauncherIcon::Ptr const&> selection_changed;
275+ sigc::signal<void> request_detail_hide;
276
277 protected:
278 // Introspectable methods
279
280=== modified file 'launcher/SwitcherView.cpp'
281--- launcher/SwitcherView.cpp 2013-07-29 17:17:43 +0000
282+++ launcher/SwitcherView.cpp 2013-08-09 17:51:48 +0000
283@@ -21,6 +21,7 @@
284 #include "SwitcherView.h"
285 #include "unity-shared/IconRenderer.h"
286 #include "unity-shared/TimeUtil.h"
287+#include "unity-shared/UScreen.h"
288
289 #include <Nux/Nux.h>
290 #include <UnityCore/Variant.h>
291@@ -36,6 +37,8 @@
292 namespace
293 {
294 const unsigned int VERTICAL_PADDING = 45;
295+ const unsigned int SPREAD_OFFSET = 100;
296+ const unsigned int EXTRA_ICON_SPACE = 10;
297 }
298
299 NUX_IMPLEMENT_OBJECT_TYPE(SwitcherView);
300@@ -55,6 +58,8 @@
301 , spread_size(3.5f)
302 , icon_renderer_(std::make_shared<IconRenderer>())
303 , text_view_(new StaticCairoText(""))
304+ , last_icon_selected_(-1)
305+ , last_detail_icon_selected_(-1)
306 , target_sizes_set_(false)
307 {
308 icon_renderer_->pip_style = OVER_TILE;
309@@ -68,7 +73,13 @@
310 icon_size.changed.connect (sigc::mem_fun (this, &SwitcherView::OnIconSizeChanged));
311 tile_size.changed.connect (sigc::mem_fun (this, &SwitcherView::OnTileSizeChanged));
312
313+ mouse_move.connect (sigc::mem_fun(this, &SwitcherView::RecvMouseMove));
314+ mouse_down.connect (sigc::mem_fun(this, &SwitcherView::RecvMouseDown));
315+ mouse_up.connect (sigc::mem_fun(this, &SwitcherView::RecvMouseUp));
316+ mouse_wheel.connect(sigc::mem_fun(this, &SwitcherView::RecvMouseWheel));
317+
318 CaptureMouseDownAnyWhereElse(true);
319+ SetAcceptMouseWheelEvent(true);
320 ResetTimer();
321
322 animate.changed.connect([this] (bool enabled) {
323@@ -103,9 +114,32 @@
324 .add("animation-length", animation_length)
325 .add("spread-size", (float)spread_size)
326 .add("label", text_view_->GetText())
327+ .add("spread_offset", SPREAD_OFFSET)
328 .add("label_visible", text_view_->IsVisible());
329 }
330
331+debug::Introspectable::IntrospectableList SwitcherView::GetIntrospectableChildren()
332+{
333+ std::list<unity::debug::Introspectable*> introspection_results;
334+
335+ if (model_->detail_selection)
336+ {
337+ for (auto const& target : render_targets_)
338+ {
339+ introspection_results.push_back(target.get());
340+ }
341+ }
342+ else if (!last_args_.empty())
343+ {
344+ for (auto& args : last_args_)
345+ {
346+ introspection_results.push_back(&args);
347+ }
348+ }
349+
350+ return introspection_results;
351+}
352+
353 LayoutWindow::Vector SwitcherView::ExternalTargets ()
354 {
355 return render_targets_;
356@@ -118,6 +152,8 @@
357 model->detail_selection.changed.connect (sigc::mem_fun (this, &SwitcherView::OnDetailSelectionChanged));
358 model->detail_selection_index.changed.connect (sigc::mem_fun (this, &SwitcherView::OnDetailSelectionIndexChanged));
359
360+ last_icon_selected_ = -1;
361+
362 if (!model->Selection())
363 return;
364
365@@ -129,12 +165,12 @@
366
367 void SwitcherView::OnIconSizeChanged (int size)
368 {
369- icon_renderer_->SetTargetSize(tile_size, icon_size, 10);
370+ icon_renderer_->SetTargetSize(tile_size, icon_size, minimum_spacing);
371 }
372
373 void SwitcherView::OnTileSizeChanged (int size)
374 {
375- icon_renderer_->SetTargetSize(tile_size, icon_size, 10);
376+ icon_renderer_->SetTargetSize(tile_size, icon_size, minimum_spacing);
377 vertical_size = tile_size + VERTICAL_PADDING * 2;
378 }
379
380@@ -167,6 +203,8 @@
381 {
382 text_view_->SetVisible(!detail);
383
384+ last_detail_icon_selected_ = -1;
385+
386 if (!detail)
387 {
388 text_view_->SetText(model_->Selection()->tooltip_text());
389@@ -186,6 +224,181 @@
390 QueueDraw();
391 }
392
393+nux::Point CalculateMouseMonitorOffset(int x, int y)
394+{
395+ int monitor = unity::UScreen::GetDefault()->GetMonitorWithMouse();
396+ nux::Geometry const& geo = unity::UScreen::GetDefault()->GetMonitorGeometry(monitor);
397+
398+ return {geo.x + x, geo.y + y};
399+}
400+
401+void SwitcherView::RecvMouseMove(int x, int y, int /*dx*/, int /*dy*/, unsigned long /*button_flags*/, unsigned long /*key_flags*/)
402+{
403+ if (model_->detail_selection)
404+ {
405+ HandleDetailMouseMove(x, y);
406+ }
407+ else
408+ {
409+ HandleMouseMove(x, y);
410+ }
411+}
412+
413+void SwitcherView::HandleDetailMouseMove(int x, int y)
414+{
415+ nux::Point const& mouse_pos = CalculateMouseMonitorOffset(x, y);
416+ int detail_icon_index = DetailIconIdexAt(mouse_pos.x, mouse_pos.y);
417+
418+ if (detail_icon_index >= 0 && detail_icon_index != last_detail_icon_selected_)
419+ {
420+ model_->detail_selection_index = detail_icon_index;
421+ last_detail_icon_selected_ = detail_icon_index;
422+ }
423+}
424+
425+void SwitcherView::HandleMouseMove(int x, int y)
426+{
427+ int icon_index = IconIndexAt(x, y);
428+
429+ if (icon_index >= 0)
430+ {
431+ if (icon_index != last_icon_selected_)
432+ {
433+ if (icon_index != model_->SelectionIndex())
434+ {
435+ model_->Select(icon_index);
436+ }
437+
438+ last_icon_selected_ = icon_index;
439+ }
440+
441+ switcher_mouse_move.emit(icon_index);
442+ }
443+}
444+
445+void SwitcherView::RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long /*key_flags*/)
446+{
447+ int button = nux::GetEventButton(button_flags);
448+
449+ if (!CheckMouseInsideBackground(x, y))
450+ hide_request.emit(false);
451+
452+ if (model_->detail_selection)
453+ {
454+ HandleDetailMouseDown(x, y, button);
455+ }
456+ else
457+ {
458+ HandleMouseDown(x, y, button);
459+ }
460+}
461+
462+void SwitcherView::HandleDetailMouseDown(int x, int y, int button)
463+{
464+ nux::Point const& mouse_pos = CalculateMouseMonitorOffset(x, y);
465+ int detail_icon_index = DetailIconIdexAt(mouse_pos.x, mouse_pos.y);
466+
467+ last_detail_icon_selected_ = detail_icon_index;
468+
469+ switcher_mouse_down.emit(detail_icon_index, button);
470+}
471+
472+void SwitcherView::HandleMouseDown(int x, int y, int button)
473+{
474+ int icon_index = IconIndexAt(x,y);
475+
476+ last_icon_selected_ = icon_index;
477+
478+ switcher_mouse_down.emit(icon_index, button);
479+}
480+
481+void SwitcherView::RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long /*key_flags*/)
482+{
483+ int button = nux::GetEventButton(button_flags);
484+
485+ if (model_->detail_selection)
486+ {
487+ HandleDetailMouseUp(x, y, button);
488+ }
489+ else
490+ {
491+ HandleMouseUp(x, y, button);
492+ }
493+}
494+
495+void SwitcherView::HandleDetailMouseUp(int x, int y, int button)
496+{
497+ nux::Point const& mouse_pos = CalculateMouseMonitorOffset(x, y);
498+ int detail_icon_index = DetailIconIdexAt(mouse_pos.x, mouse_pos.y);
499+
500+ switcher_mouse_up.emit(detail_icon_index, button);
501+
502+ if (button == 1)
503+ {
504+ if (detail_icon_index >= 0 && detail_icon_index == last_detail_icon_selected_)
505+ {
506+ model_->detail_selection_index = detail_icon_index;
507+ hide_request.emit(true);
508+ }
509+ }
510+ else if (button == 3)
511+ {
512+ model_->detail_selection = false;
513+ }
514+}
515+
516+void SwitcherView::HandleMouseUp(int x, int y, int button)
517+{
518+ int icon_index = IconIndexAt(x,y);
519+
520+ switcher_mouse_up.emit(icon_index, button);
521+
522+ if (button == 1)
523+ {
524+ if (icon_index >= 0 && icon_index == last_icon_selected_)
525+ {
526+ model_->Select(icon_index);
527+ hide_request.emit(true);
528+ }
529+ }
530+}
531+
532+void SwitcherView::RecvMouseWheel(int /*x*/, int /*y*/, int wheel_delta, unsigned long /*button_flags*/, unsigned long /*key_flags*/)
533+{
534+ if (model_->detail_selection)
535+ {
536+ HandleDetailMouseWheel(wheel_delta);
537+ }
538+ else
539+ {
540+ HandleMouseWheel(wheel_delta);
541+ }
542+}
543+
544+void SwitcherView::HandleDetailMouseWheel(int wheel_delta)
545+{
546+ if (wheel_delta > 0)
547+ {
548+ model_->NextDetail();
549+ }
550+ else
551+ {
552+ model_->PrevDetail();
553+ }
554+}
555+
556+void SwitcherView::HandleMouseWheel(int wheel_delta)
557+{
558+ if (wheel_delta > 0)
559+ {
560+ model_->Next();
561+ }
562+ else
563+ {
564+ model_->Prev();
565+ }
566+}
567+
568 SwitcherModel::Ptr SwitcherView::GetModel()
569 {
570 return model_;
571@@ -420,7 +633,7 @@
572 nux::Geometry const& spread_bounds = UpdateRenderTargets(progress);
573 ResizeRenderTargets(spread_bounds, progress);
574 // remove extra space consumed by spread
575- spread_padded_width = spread_bounds.width + 100;
576+ spread_padded_width = spread_bounds.width + SPREAD_OFFSET;
577 max_width -= spread_padded_width - tile_size;
578
579 int expansion = std::max(0, spread_bounds.height - icon_size);
580@@ -551,7 +764,7 @@
581
582 if (!target_sizes_set_)
583 {
584- icon_renderer_->SetTargetSize(tile_size, icon_size, 10);
585+ icon_renderer_->SetTargetSize(tile_size, icon_size, minimum_spacing);
586 target_sizes_set_ = true;
587 }
588
589@@ -640,9 +853,9 @@
590 }
591 }
592
593-int SwitcherView::IconIndexAt(int x, int y)
594+int SwitcherView::IconIndexAt(int x, int y) const
595 {
596- int half_size = icon_size.Get() / 2;
597+ int half_size = icon_size.Get() / 2 + EXTRA_ICON_SPACE;
598 int icon_index = -1;
599
600 // Taking icon rotation into consideration will make selection more
601@@ -673,5 +886,27 @@
602 return icon_index;
603 }
604
605+int SwitcherView::DetailIconIdexAt(int x, int y) const
606+{
607+ int index = -1;
608+
609+ for (unsigned int i = 0; i < render_targets_.size(); ++i)
610+ {
611+ if (render_targets_[i]->result.IsPointInside(x + SPREAD_OFFSET, y + SPREAD_OFFSET))
612+ return i;
613+ }
614+
615+ return index;
616+}
617+
618+bool SwitcherView::CheckMouseInsideBackground(int x, int y) const
619+{
620+ nux::Point p(x,y);
621+ if (last_background_.IsInside(p))
622+ return true;
623+
624+ return false;
625+}
626+
627 }
628 }
629
630=== modified file 'launcher/SwitcherView.h'
631--- launcher/SwitcherView.h 2013-02-13 01:45:00 +0000
632+++ launcher/SwitcherView.h 2013-08-09 17:51:48 +0000
633@@ -25,6 +25,7 @@
634 #include "unity-shared/StaticCairoText.h"
635 #include "unity-shared/LayoutSystem.h"
636 #include "unity-shared/BackgroundEffectHelper.h"
637+#include "unity-shared/Introspectable.h"
638 #include "unity-shared/UnityWindowView.h"
639
640 #include <Nux/View.h>
641@@ -70,13 +71,24 @@
642
643 // Returns the index of the icon at the given position, in window coordinates.
644 // If there's no icon there, -1 is returned.
645- int IconIndexAt(int x, int y);
646-
647+ int IconIndexAt(int x, int y) const;
648+ int DetailIconIdexAt(int x, int y) const;
649+
650+ /* void; int icon_index, int button*/
651+ sigc::signal<void, int, int> switcher_mouse_down;
652+ sigc::signal<void, int, int> switcher_mouse_up;
653+
654+ /* void; int icon_index */
655+ sigc::signal<void, int> switcher_mouse_move;
656+
657+ /* void; bool visible */
658+ sigc::signal<void, bool> hide_request;
659
660 protected:
661 // Introspectable methods
662 std::string GetName() const;
663 void AddProperties(GVariantBuilder* builder);
664+ IntrospectableList GetIntrospectableChildren();
665
666 void PreDraw(nux::GraphicsEngine& GfxContext, bool force_draw);
667 void DrawOverlay(nux::GraphicsEngine& GfxContext, bool force_draw, nux::Geometry const& clip);
668@@ -88,7 +100,24 @@
669 std::list<ui::RenderArg> RenderArgsFlat(nux::Geometry& background_geo, int selection, float progress);
670
671 ui::RenderArg CreateBaseArgForIcon(launcher::AbstractLauncherIcon::Ptr const& icon);
672+
673 private:
674+ void RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags);
675+ void HandleDetailMouseMove(int x, int y);
676+ void HandleMouseMove(int x, int y);
677+
678+ void RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags);
679+ void HandleDetailMouseDown(int x, int y, int button);
680+ void HandleMouseDown(int x, int y, int button);
681+
682+ void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags);
683+ void HandleDetailMouseUp(int x, int y, int button);
684+ void HandleMouseUp(int x, int y, int button);
685+
686+ void RecvMouseWheel(int x, int y, int wheel_delta, unsigned long button_flags, unsigned long key_flags);
687+ void HandleDetailMouseWheel(int wheel_delta);
688+ void HandleMouseWheel(int wheel_delta);
689+
690 void OnSelectionChanged(launcher::AbstractLauncherIcon::Ptr const& selection);
691 void OnDetailSelectionChanged (bool detail);
692 void OnDetailSelectionIndexChanged (unsigned int index);
693@@ -111,14 +140,17 @@
694 void ResetTimer();
695 void SaveLast();
696
697+ bool CheckMouseInsideBackground(int x, int y) const;
698+
699 SwitcherModel::Ptr model_;
700 ui::LayoutSystem layout_system_;
701 ui::AbstractIconRenderer::Ptr icon_renderer_;
702 nux::ObjectPtr<StaticCairoText> text_view_;
703
704+ int last_icon_selected_;
705+ int last_detail_icon_selected_;
706 bool target_sizes_set_;
707
708-
709 std::list<ui::RenderArg> last_args_;
710 std::list<ui::RenderArg> saved_args_;
711
712
713=== modified file 'plugins/unityshell/src/unityshell.cpp'
714--- plugins/unityshell/src/unityshell.cpp 2013-08-07 13:27:56 +0000
715+++ plugins/unityshell/src/unityshell.cpp 2013-08-09 17:51:48 +0000
716@@ -971,6 +971,19 @@
717 return false;
718 }
719
720+LayoutWindow::Ptr UnityScreen::GetSwitcherDetailLayoutWindow(Window window) const
721+{
722+ LayoutWindow::Vector const& targets = switcher_controller_->ExternalRenderTargets();
723+
724+ for (LayoutWindow::Ptr const& target : targets)
725+ {
726+ if (target->xid == window)
727+ return target;
728+ }
729+
730+ return nullptr;
731+}
732+
733 void UnityWindow::enterShowDesktop ()
734 {
735 if (!mShowdesktopHandler)
736@@ -1169,7 +1182,8 @@
737 handled = true;
738 }
739 else if (event->xbutton.button == Button2 &&
740- GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root))
741+ (GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root) ||
742+ GetLayoutWindowGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root)))
743 {
744 middle_clicked_ = true;
745 handled = true;
746@@ -1190,7 +1204,9 @@
747 if (was_pressed)
748 {
749 if (close_button_geo_.IsPointInside(event->xbutton.x_root, event->xbutton.y_root))
750+ {
751 window->close(0);
752+ }
753
754 handled = true;
755 }
756@@ -1198,7 +1214,8 @@
757 if (middle_clicked_)
758 {
759 if (event->xbutton.button == Button2 &&
760- GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root))
761+ (GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root) ||
762+ GetLayoutWindowGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root)))
763 {
764 window->close(0);
765 }
766@@ -1492,6 +1509,14 @@
767 if (CompWindow *w = screen->findWindow(ss->getSelectedWindow()))
768 skip_other_plugins = UnityWindow::get(w)->handleEvent(event);
769 }
770+ else if (switcher_controller_->IsDetailViewShown())
771+ {
772+ Window win = switcher_controller_->GetCurrentSelection().window_;
773+ CompWindow* w = screen->findWindow(win);
774+
775+ if (w)
776+ skip_other_plugins = UnityWindow::get(w)->handleEvent(event);
777+ }
778 break;
779 case ButtonPress:
780 if (super_keypressed_)
781@@ -1505,6 +1530,14 @@
782 if (CompWindow *w = screen->findWindow(ss->getSelectedWindow()))
783 skip_other_plugins = UnityWindow::get(w)->handleEvent(event);
784 }
785+ else if (switcher_controller_->IsDetailViewShown())
786+ {
787+ Window win = switcher_controller_->GetCurrentSelection().window_;
788+ CompWindow* w = screen->findWindow(win);
789+
790+ if (w)
791+ skip_other_plugins = UnityWindow::get(w)->handleEvent(event);
792+ }
793
794 if (dash_controller_->IsVisible())
795 {
796@@ -1532,8 +1565,7 @@
797 }
798 }
799 }
800-
801- if (hud_controller_->IsVisible())
802+ else if (hud_controller_->IsVisible())
803 {
804 nux::Point pt(event->xbutton.x_root, event->xbutton.y_root);
805 nux::Geometry const& hud_geo = hud_controller_->GetInputWindowGeometry();
806@@ -1547,24 +1579,26 @@
807 hud_controller_->HideHud(false);
808 }
809 }
810+ else if (switcher_controller_->Visible())
811+ {
812+ nux::Point pt(event->xbutton.x_root, event->xbutton.y_root);
813+ nux::Geometry const& switcher_geo = switcher_controller_->GetInputWindowGeometry();
814+
815+ if (!switcher_geo.IsInside(pt))
816+ {
817+ switcher_controller_->Hide(false);
818+ }
819+ }
820 break;
821 case ButtonRelease:
822- if (switcher_controller_ && switcher_controller_->Visible())
823+
824+ if (switcher_controller_->IsDetailViewShown())
825 {
826- XButtonEvent *bev = reinterpret_cast<XButtonEvent*>(event);
827- if (bev->time - last_scroll_event_ > 150)
828- {
829- if (bev->button == Button4 || bev->button == local::SCROLL_UP_BUTTON)
830- {
831- switcher_controller_->Prev();
832- last_scroll_event_ = bev->time;
833- }
834- else if (bev->button == Button5 || bev->button == local::SCROLL_DOWN_BUTTON)
835- {
836- switcher_controller_->Next();
837- last_scroll_event_ = bev->time;
838- }
839- }
840+ Window win = switcher_controller_->GetCurrentSelection().window_;
841+ CompWindow* w = screen->findWindow(win);
842+
843+ if (w)
844+ skip_other_plugins = UnityWindow::get(w)->handleEvent(event);
845 }
846 else if (wm.IsScaleActive())
847 {
848@@ -1686,7 +1720,7 @@
849 }
850
851 if (!skip_other_plugins &&
852- screen->otherGrabExist("deco", "move", "switcher", "resize", "unity-switcher", nullptr))
853+ screen->otherGrabExist("deco", "move", "switcher", "resize", nullptr))
854 {
855 wt->ProcessForeignEvent(event, nullptr);
856 }
857@@ -1890,7 +1924,7 @@
858 bool UnityScreen::altTabInitiateCommon(CompAction* action, switcher::ShowMode show_mode)
859 {
860 if (!grab_index_)
861- grab_index_ = screen->pushGrab (screen->invisibleCursor(), "unity-switcher");
862+ grab_index_ = screen->pushGrab (screen->normalCursor(), "unity-switcher");
863
864 screen->addAction(&optionGetAltTabRight());
865 screen->addAction(&optionGetAltTabDetailStart());
866@@ -1921,18 +1955,6 @@
867
868 void UnityScreen::SetUpAndShowSwitcher(switcher::ShowMode show_mode)
869 {
870- // maybe check launcher position/hide state?
871-
872- auto uscreen = UScreen::GetDefault();
873- int monitor = uscreen->GetMonitorWithMouse();
874- auto monitor_geo = uscreen->GetMonitorGeometry(monitor);
875-
876- monitor_geo.x += 100;
877- monitor_geo.y += 100;
878- monitor_geo.width -= 200;
879- monitor_geo.height -= 200;
880- switcher_controller_->SetWorkspace(monitor_geo, monitor);
881-
882 RaiseInputWindows();
883
884 auto results = launcher_controller_->GetAltTabIcons(show_mode == switcher::ShowMode::CURRENT_VIEWPORT,
885@@ -3819,15 +3841,31 @@
886 if (state != ScaleScreen::Wait && state != ScaleScreen::Out)
887 return;
888
889- auto const& scaled_geo = GetScaledGeometry();
890+ nux::Geometry const& scale_geo = GetScaledGeometry();
891+
892 auto const& pos = scale_win->getCurrentPosition();
893
894 bool highlighted = (ss->getSelectedWindow() == window->id());
895- paintFakeDecoration(scaled_geo, attrib, transform, mask, highlighted, pos.scale);
896+ paintFakeDecoration(scale_geo, attrib, transform, mask, highlighted, pos.scale);
897+}
898+
899+nux::Geometry UnityWindow::GetLayoutWindowGeometry()
900+{
901+ auto const& layout_window = UnityScreen::get(screen)->GetSwitcherDetailLayoutWindow(window->id());
902+
903+ if (layout_window)
904+ return layout_window->result;
905+
906+ return nux::Geometry();
907 }
908
909 nux::Geometry UnityWindow::GetScaledGeometry()
910 {
911+ WindowManager& wm = WindowManager::Default();
912+
913+ if (!wm.IsScaleActive())
914+ return nux::Geometry();
915+
916 ScaleWindow* scale_win = ScaleWindow::get(window);
917
918 ScalePosition const& pos = scale_win->getCurrentPosition();
919
920=== modified file 'plugins/unityshell/src/unityshell.h'
921--- plugins/unityshell/src/unityshell.h 2013-08-07 13:27:56 +0000
922+++ plugins/unityshell/src/unityshell.h 2013-08-09 17:51:48 +0000
923@@ -193,6 +193,8 @@
924
925 bool DoesPointIntersectUnityGeos(nux::Point const& pt);
926
927+ ui::LayoutWindow::Ptr GetSwitcherDetailLayoutWindow(Window window) const;
928+
929 protected:
930 std::string GetName() const;
931 void AddProperties(GVariantBuilder* builder);
932@@ -392,6 +394,7 @@
933 bool place(CompPoint& pos);
934 CompPoint tryNotIntersectUI(CompPoint& pos);
935 nux::Geometry GetScaledGeometry();
936+ nux::Geometry GetLayoutWindowGeometry();
937
938 void paintThumbnail(nux::Geometry const& bounding, float parent_alpha, float alpha, float scale_ratio, unsigned deco_height, bool selected);
939
940
941=== modified file 'tests/autopilot/unity/emulators/switcher.py'
942--- tests/autopilot/unity/emulators/switcher.py 2013-05-10 05:16:07 +0000
943+++ tests/autopilot/unity/emulators/switcher.py 2013-08-09 17:51:48 +0000
944@@ -234,6 +234,20 @@
945 class SwitcherView(UnityIntrospectionObject):
946 """An emulator class for interacting with with SwitcherView."""
947
948+ @property
949+ def icon_args(self):
950+ return self.get_children_by_type(RenderArgs);
951+
952+ @property
953+ def detail_icons(self):
954+ return self.get_children_by_type(LayoutWindow);
955+
956+
957+class RenderArgs(UnityIntrospectionObject):
958+ """An emulator class for interacting with the RenderArgs class."""
959+
960+class LayoutWindow(UnityIntrospectionObject):
961+ """An emulator class for interacting with the LayoutWindows class."""
962
963 class SwitcherModel(UnityIntrospectionObject):
964 """An emulator class for interacting with the SwitcherModel."""
965
966=== modified file 'tests/autopilot/unity/tests/test_switcher.py'
967--- tests/autopilot/unity/tests/test_switcher.py 2013-06-15 02:24:01 +0000
968+++ tests/autopilot/unity/tests/test_switcher.py 2013-08-09 17:51:48 +0000
969@@ -512,3 +512,102 @@
970 self.addCleanup(self.unity.switcher.terminate)
971
972 self.assertThat(self.unity.switcher.visible, Eventually(Equals(False)))
973+
974+class SwitcherDetailsMouseTests(SwitcherTestCase):
975+ """ Test the interactions with the mouse and the switcher. """
976+
977+ def setUp(self):
978+ super(SwitcherDetailsMouseTests, self).setUp()
979+ self.set_timeout_setting(False)
980+
981+ def test_mouse_highlights_switcher_icons(self):
982+ """ Tests that the mouse can hightlight all the switcher icons. """
983+
984+ self.process_manager.start_app("Character Map")
985+
986+ self.unity.switcher.initiate()
987+ self.addCleanup(self.unity.switcher.terminate)
988+
989+ icon_args = self.unity.switcher.view.icon_args
990+ offset = self.unity.switcher.view.spread_offset
991+ icon_cords = []
992+
993+ # Must collect the cords before moving mouse
994+ for args in icon_args:
995+ x = args.logical_center_x + offset
996+ y = args.logical_center_y + offset
997+ icon_cords.append((x,y))
998+
999+ index = 0;
1000+ for cords in icon_cords:
1001+ self.mouse.move(cords[0], cords[1])
1002+ self.assertThat(index, Equals(self.unity.switcher.selection_index))
1003+ index += 1
1004+
1005+ def test_mouse_clicks_activate_icon(self):
1006+ """
1007+ Opens 2 different applications, CharMap being opened before TextEditor.
1008+ Then we get the index of the CharMap, and click on it, asserting CharMap is focused.
1009+ """
1010+
1011+ char_win1, char_win2 = self.start_applications("Character Map", "Text Editor")
1012+ self.assertVisibleWindowStack([char_win2, char_win1])
1013+ self.assertProperty(char_win1, is_focused=False)
1014+
1015+ self.unity.switcher.initiate()
1016+ self.addCleanup(self.unity.switcher.terminate)
1017+
1018+ index = self.unity.switcher.selection_index
1019+ offset = self.unity.switcher.view.spread_offset
1020+
1021+ icon_arg = self.unity.switcher.view.icon_args[index]
1022+ x = icon_arg.logical_center_x + offset
1023+ y = icon_arg.logical_center_y + offset
1024+ self.mouse.move(x, y)
1025+ self.mouse.click()
1026+
1027+ self.assertProperty(char_win1, is_focused=True)
1028+
1029+ def test_mouse_highlights_switcher_deatil_icons_motion(self):
1030+ """
1031+ Gather the cords of all the detail icons, move the mouse through each
1032+ asserting the index of each icon we move through.
1033+ """
1034+
1035+ self.start_applications("Character Map", "Character Map", "Character Map")
1036+
1037+ self.unity.switcher.initiate(SwitcherMode.DETAIL)
1038+ self.addCleanup(self.unity.switcher.terminate)
1039+
1040+ offset = self.unity.switcher.view.spread_offset
1041+ cords = []
1042+
1043+ for icon in self.unity.switcher.view.detail_icons:
1044+ cords.append((icon.x + offset, icon.y + offset))
1045+
1046+ index = 0;
1047+ for cord in cords:
1048+ self.mouse.move(cord[0], cord[1])
1049+ self.assertThat(index, Equals(self.unity.switcher.detail_selection_index))
1050+ index += 1
1051+
1052+ def test_mouse_click_will_activate_detail_icon(self):
1053+ """
1054+ Start 2 application of the same type, then click on index 0 in detail mode. This
1055+ will cause the focus from char_win2 to move to char_win1, showing clicking wokrs.
1056+ """
1057+
1058+ char_win1, char_win2 = self.start_applications("Character Map", "Character Map")
1059+ self.assertVisibleWindowStack([char_win2, char_win1])
1060+
1061+ self.unity.switcher.initiate(SwitcherMode.DETAIL)
1062+ self.addCleanup(self.unity.switcher.terminate)
1063+
1064+ offset = self.unity.switcher.view.spread_offset
1065+ x = self.unity.switcher.view.detail_icons[0].x + offset
1066+ y = self.unity.switcher.view.detail_icons[0].y + offset
1067+
1068+ self.mouse.move(x,y)
1069+ self.mouse.click()
1070+
1071+ self.assertProperty(char_win1, is_focused=True)
1072
1073=== modified file 'tests/test_switcher_controller.cpp'
1074--- tests/test_switcher_controller.cpp 2013-06-21 18:58:17 +0000
1075+++ tests/test_switcher_controller.cpp 2013-08-09 17:51:48 +0000
1076@@ -272,3 +272,31 @@
1077 EXPECT_EQ(mock_window_->GetOpacity(), 0.0f);
1078 Mock::VerifyAndClearExpectations(mock_window_.GetPointer());
1079 }
1080+
1081+TEST_F(TestSwitcherController, TestRightClickedReceived)
1082+{
1083+ controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_);
1084+
1085+ auto const& view = controller_->GetView();
1086+ auto const& model = view->GetModel();
1087+
1088+ ASSERT_FALSE(model->detail_selection());
1089+
1090+ view->switcher_mouse_up.emit(-1, 3);
1091+ view->switcher_mouse_down.emit(-1, 3);
1092+
1093+ ASSERT_TRUE(model->detail_selection());
1094+}
1095+
1096+TEST_F(TestSwitcherController, TestHideRequest)
1097+{
1098+ controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_);
1099+
1100+ auto const& view = controller_->GetView();
1101+
1102+ ASSERT_TRUE(controller_->Visible());
1103+
1104+ view->hide_request.emit(false);
1105+
1106+ ASSERT_FALSE(controller_->Visible());
1107+}
1108
1109=== modified file 'unity-shared/AbstractIconRenderer.h'
1110--- unity-shared/AbstractIconRenderer.h 2013-04-12 23:33:52 +0000
1111+++ unity-shared/AbstractIconRenderer.h 2013-08-09 17:51:48 +0000
1112@@ -22,8 +22,11 @@
1113
1114 #include <Nux/Nux.h>
1115
1116+#include "Introspectable.h"
1117 #include "IconTextureSource.h"
1118
1119+#include <UnityCore/Variant.h>
1120+
1121 namespace unity
1122 {
1123 namespace ui
1124@@ -35,7 +38,7 @@
1125 OVER_TILE,
1126 };
1127
1128-class RenderArg
1129+class RenderArg : public debug::Introspectable
1130 {
1131 public:
1132 RenderArg()
1133@@ -91,6 +94,17 @@
1134 bool colorify_background;
1135 int window_indicators;
1136 char shortcut_label;
1137+
1138+protected:
1139+ // Introspectable methods
1140+ std::string GetName() const { return "RenderArgs"; }
1141+ void AddProperties(GVariantBuilder* builder)
1142+ {
1143+ unity::variant::BuilderWrapper(builder)
1144+ .add("logical_center_x", logical_center.x)
1145+ .add("logical_center_y", logical_center.y)
1146+ .add("logical_center_z", logical_center.z);
1147+ }
1148 };
1149
1150 class AbstractIconRenderer
1151
1152=== modified file 'unity-shared/LayoutSystem.cpp'
1153--- unity-shared/LayoutSystem.cpp 2013-06-17 23:56:23 +0000
1154+++ unity-shared/LayoutSystem.cpp 2013-08-09 17:51:48 +0000
1155@@ -19,6 +19,8 @@
1156
1157 #include "LayoutSystem.h"
1158
1159+#include <UnityCore/Variant.h>
1160+
1161 namespace unity {
1162 namespace ui {
1163
1164@@ -290,5 +292,17 @@
1165 }
1166 }
1167
1168+// Introspectable methods
1169+std::string LayoutWindow::GetName() const
1170+{
1171+ return "LayoutWindow";
1172+}
1173+
1174+void LayoutWindow::AddProperties(GVariantBuilder* builder)
1175+{
1176+ unity::variant::BuilderWrapper(builder)
1177+ .add(result);
1178+}
1179+
1180 }
1181 }
1182
1183=== modified file 'unity-shared/LayoutSystem.h'
1184--- unity-shared/LayoutSystem.h 2013-06-14 00:23:34 +0000
1185+++ unity-shared/LayoutSystem.h 2013-08-09 17:51:48 +0000
1186@@ -24,13 +24,15 @@
1187 #include <sigc++/sigc++.h>
1188 #include <Nux/Nux.h>
1189
1190+#include "unity-shared/Introspectable.h"
1191 #include "unity-shared/WindowManager.h"
1192
1193 namespace unity {
1194 namespace ui {
1195
1196-struct LayoutWindow
1197+class LayoutWindow : public debug::Introspectable
1198 {
1199+public:
1200 typedef std::shared_ptr<LayoutWindow> Ptr;
1201 typedef std::vector<LayoutWindow::Ptr> Vector;
1202
1203@@ -45,6 +47,11 @@
1204 bool selected;
1205 float aspect_ratio;
1206 float alpha;
1207+
1208+protected:
1209+ // Introspectable methods
1210+ std::string GetName() const;
1211+ void AddProperties(GVariantBuilder* builder);
1212 };
1213
1214 class LayoutSystem