Merge lp:~3v1n0/unity/quicklist-app-name-spread-fix into lp:unity

Proposed by Marco Trevisan (Treviño)
Status: Merged
Approved by: Marco Trevisan (Treviño)
Approved revision: no longer in the source branch.
Merged at revision: 2342
Proposed branch: lp:~3v1n0/unity/quicklist-app-name-spread-fix
Merge into: lp:unity
Diff against target: 391 lines (+163/-16)
13 files modified
plugins/unityshell/src/BamfLauncherIcon.cpp (+10/-1)
plugins/unityshell/src/BamfLauncherIcon.h (+1/-0)
plugins/unityshell/src/PluginAdapter.cpp (+14/-0)
plugins/unityshell/src/PluginAdapter.h (+1/-0)
plugins/unityshell/src/WindowManager.cpp (+9/-0)
plugins/unityshell/src/WindowManager.h (+7/-9)
plugins/unityshell/src/unityshell.cpp (+1/-1)
plugins/unityshell/src/unityshell.h (+2/-2)
tests/autopilot/autopilot/emulators/unity/quicklist.py (+4/-0)
tests/autopilot/autopilot/emulators/unity/window_manager.py (+22/-0)
tests/autopilot/autopilot/keybindings.py (+3/-0)
tests/autopilot/autopilot/tests/__init__.py (+7/-0)
tests/autopilot/autopilot/tests/test_quicklist.py (+82/-3)
To merge this branch: bzr merge lp:~3v1n0/unity/quicklist-app-name-spread-fix
Reviewer Review Type Date Requested Status
Thomi Richards (community) Approve
Review via email: mp+102959@code.launchpad.net

Commit message

BamfLauncherIcon: activate the icon using the quicklist item using an Idle.

This fixes the bug #986461 that caused the spread not to initiate clicking on the quicklist application name.

Description of the change

When the quicklist application name item is clicked the spread should eventually initiate (if the application is active and visible), but this doesn't seem to work anymore.

The problem seems to be due to the fact that the quicklist view grab is still active when the "item-activated" event happens, and so compiz doesn't seem to be able to initiate the scale plugin.

Adding an idle on the activate event fixes the issue.

Fix tested using Autopilot, added introspection support to the WindowManager and PluginAdapter classes.

To post a comment you must log in.
Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Approved.

Probably want to get someone who knows the unity-side of things to look through this as well though.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/unityshell/src/BamfLauncherIcon.cpp'
2--- plugins/unityshell/src/BamfLauncherIcon.cpp 2012-04-25 00:32:14 +0000
3+++ plugins/unityshell/src/BamfLauncherIcon.cpp 2012-04-25 23:33:18 +0000
4@@ -149,6 +149,9 @@
5 if (_fill_supported_types_id != 0)
6 g_source_remove(_fill_supported_types_id);
7
8+ if (_quicklist_activated_id != 0)
9+ g_source_remove(_quicklist_activated_id);
10+
11 if (_window_moved_id != 0)
12 g_source_remove(_window_moved_id);
13
14@@ -963,7 +966,13 @@
15
16 _gsignals.Add(new glib::Signal<void, DbusmenuMenuitem*, int>(item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
17 [&] (DbusmenuMenuitem*, int) {
18- ActivateLauncherIcon(ActionArg(ActionArg::OTHER, 0));
19+ _quicklist_activated_id =
20+ g_idle_add([] (gpointer data) -> gboolean {
21+ auto self = static_cast<BamfLauncherIcon*>(data);
22+ self->ActivateLauncherIcon(ActionArg());
23+ self->_quicklist_activated_id = 0;
24+ return FALSE;
25+ }, this);
26 }));
27
28 _menu_items_extra["AppName"] = glib::Object<DbusmenuMenuitem>(item);
29
30=== modified file 'plugins/unityshell/src/BamfLauncherIcon.h'
31--- plugins/unityshell/src/BamfLauncherIcon.h 2012-04-25 00:32:14 +0000
32+++ plugins/unityshell/src/BamfLauncherIcon.h 2012-04-25 23:33:18 +0000
33@@ -126,6 +126,7 @@
34 bool _supported_types_filled;
35 guint _fill_supported_types_id;
36 guint _window_moved_id;
37+ guint _quicklist_activated_id;
38
39 std::string _remote_uri;
40 std::string _desktop_file;
41
42=== modified file 'plugins/unityshell/src/PluginAdapter.cpp'
43--- plugins/unityshell/src/PluginAdapter.cpp 2012-04-20 02:47:02 +0000
44+++ plugins/unityshell/src/PluginAdapter.cpp 2012-04-25 23:33:18 +0000
45@@ -23,6 +23,7 @@
46 #include "UScreen.h"
47
48 #include <NuxCore/Logger.h>
49+#include <UnityCore/Variant.h>
50
51 namespace
52 {
53@@ -1266,3 +1267,16 @@
54 _last_focused_window = NULL;
55 }
56
57+void
58+PluginAdapter::AddProperties(GVariantBuilder* builder)
59+{
60+ unity::variant::BuilderWrapper wrapper(builder);
61+ wrapper.add(GetScreenGeometry())
62+ .add("workspace_count", WorkspaceCount())
63+ .add("active_window", GetActiveWindow())
64+ .add("screen_grabbed", IsScreenGrabbed())
65+ .add("scale_active", IsScaleActive())
66+ .add("scale_active_for_group", IsScaleActiveForGroup())
67+ .add("expo_active", IsExpoActive())
68+ .add("viewport_switch_running", IsViewPortSwitchStarted());
69+}
70
71=== modified file 'plugins/unityshell/src/PluginAdapter.h'
72--- plugins/unityshell/src/PluginAdapter.h 2012-04-20 02:47:02 +0000
73+++ plugins/unityshell/src/PluginAdapter.h 2012-04-25 23:33:18 +0000
74@@ -165,6 +165,7 @@
75
76 protected:
77 PluginAdapter(CompScreen* screen);
78+ void AddProperties(GVariantBuilder* builder);
79
80 private:
81 std::string MatchStringForXids(std::vector<Window> *windows);
82
83=== modified file 'plugins/unityshell/src/WindowManager.cpp'
84--- plugins/unityshell/src/WindowManager.cpp 2012-04-20 02:47:02 +0000
85+++ plugins/unityshell/src/WindowManager.cpp 2012-04-25 23:33:18 +0000
86@@ -214,6 +214,10 @@
87 {
88 g_debug("%s", G_STRFUNC);
89 }
90+
91+ void AddProperties(GVariantBuilder* builder)
92+ {
93+ }
94 };
95
96 WindowManager*
97@@ -231,6 +235,11 @@
98 window_manager = manager;
99 }
100
101+std::string WindowManager::GetName() const
102+{
103+ return "WindowManager";
104+}
105+
106 #define NET_WM_MOVERESIZE_MOVE 8
107
108 void WindowManager::StartMove(guint32 xid, int x, int y)
109
110=== modified file 'plugins/unityshell/src/WindowManager.h'
111--- plugins/unityshell/src/WindowManager.h 2012-04-20 02:47:02 +0000
112+++ plugins/unityshell/src/WindowManager.h 2012-04-25 23:33:18 +0000
113@@ -19,24 +19,18 @@
114 #ifndef WINDOW_MANAGER_H
115 #define WINDOW_MANAGER_H
116
117-#include <glib.h>
118-#include <sigc++/sigc++.h>
119 #include <Nux/Nux.h>
120-#include <Nux/WindowThread.h>
121-#include <NuxGraphics/GLWindowManager.h>
122 #include <gdk/gdkx.h>
123 #include <core/core.h>
124
125-class WindowManager
126+#include "Introspectable.h"
127+
128+class WindowManager : public unity::debug::Introspectable
129 {
130 // This is a glue interface that breaks the dependancy of Unity with Compiz
131 // Basically it has a default implementation that does nothing useful, but
132 // the idea is that unity.cpp uses SetDefault() early enough in it's
133 // initialization so the things that require it get a usable implementation
134- //
135- // Currently only the Panel uses it but hopefully we'll get more of
136- // PluginAdaptor features moved into here and also get the Launcher to use
137- // it.
138
139 public:
140 WindowManager() :
141@@ -142,6 +136,10 @@
142
143 sigc::signal<void, const char*, const char*, CompOption::Vector&> compiz_event;
144
145+protected:
146+ std::string GetName() const;
147+ virtual void AddProperties(GVariantBuilder* builder) = 0;
148+
149 private:
150 Atom m_MoveResizeAtom;
151 };
152
153=== modified file 'plugins/unityshell/src/unityshell.cpp'
154--- plugins/unityshell/src/unityshell.cpp 2012-04-24 20:52:16 +0000
155+++ plugins/unityshell/src/unityshell.cpp 2012-04-25 23:33:18 +0000
156@@ -217,6 +217,7 @@
157
158 PluginAdapter::Initialize(screen);
159 WindowManager::SetDefault(PluginAdapter::Default());
160+ AddChild(PluginAdapter::Default());
161
162 StartupNotifyService::Default()->SetSnDisplay(screen->snDisplay(), screen->screenNum());
163
164@@ -243,7 +244,6 @@
165
166 wt->Run(NULL);
167 uScreen = this;
168-
169 _in_paint = false;
170
171 #ifndef USE_GLES
172
173=== modified file 'plugins/unityshell/src/unityshell.h'
174--- plugins/unityshell/src/unityshell.h 2012-04-24 20:52:16 +0000
175+++ plugins/unityshell/src/unityshell.h 2012-04-25 23:33:18 +0000
176@@ -241,7 +241,7 @@
177 internal::FavoriteStoreGSettings favorite_store_;
178
179 /* The window thread should be the last thing removed, as c++ does it in reverse order */
180- std::unique_ptr<nux::WindowThread> wt;
181+ std::unique_ptr<nux::WindowThread> wt;
182
183 /* These must stay below the window thread, please keep the order */
184 launcher::Controller::Ptr launcher_controller_;
185@@ -250,7 +250,7 @@
186 switcher::Controller::Ptr switcher_controller_;
187 hud::Controller::Ptr hud_controller_;
188 shortcut::Controller::Ptr shortcut_controller_;
189- debug::DebugDBusInterface debugger_;
190+ debug::DebugDBusInterface debugger_;
191
192 std::list<shortcut::AbstractHint::Ptr> hints_;
193 bool enable_shortcut_overlay_;
194
195=== modified file 'tests/autopilot/autopilot/emulators/unity/quicklist.py'
196--- tests/autopilot/autopilot/emulators/unity/quicklist.py 2012-04-18 23:01:46 +0000
197+++ tests/autopilot/autopilot/emulators/unity/quicklist.py 2012-04-25 23:33:18 +0000
198@@ -40,6 +40,10 @@
199
200 return matches[0] if matches else None
201
202+ def get_quicklist_application_item(self, application_name):
203+ """Returns the QuicklistMenuItemLabel for the given application_name"""
204+ return self.get_quicklist_item_by_text("<b>"+application_name+"</b>")
205+
206 def click_item(self, item):
207 """Click one of the quicklist items."""
208 if not isinstance(item, QuicklistMenuItem):
209
210=== added file 'tests/autopilot/autopilot/emulators/unity/window_manager.py'
211--- tests/autopilot/autopilot/emulators/unity/window_manager.py 1970-01-01 00:00:00 +0000
212+++ tests/autopilot/autopilot/emulators/unity/window_manager.py 2012-04-25 23:33:18 +0000
213@@ -0,0 +1,22 @@
214+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
215+# Copyright 2012 Canonical
216+# Author: Marco Trevisan (Treviño)
217+#
218+# This program is free software: you can redistribute it and/or modify it
219+# under the terms of the GNU General Public License version 3, as published
220+# by the Free Software Foundation.
221+#
222+
223+import logging
224+from autopilot.emulators.unity import UnityIntrospectionObject
225+
226+logger = logging.getLogger(__name__)
227+
228+
229+class WindowManager(UnityIntrospectionObject):
230+ """The WindowManager class."""
231+
232+ @property
233+ def screen_geometry(self):
234+ """Returns a tuple of (x,y,w,h) for the screen."""
235+ return (self.x, self.y, self.width, self.height)
236
237=== modified file 'tests/autopilot/autopilot/keybindings.py'
238--- tests/autopilot/autopilot/keybindings.py 2012-04-11 23:54:13 +0000
239+++ tests/autopilot/autopilot/keybindings.py 2012-04-25 23:33:18 +0000
240@@ -112,6 +112,9 @@
241 # expo plugin:
242 "expo/start": ("expo", "expo_key"),
243 "expo/cancel": "Escape",
244+ # spread (scale) plugin:
245+ "spread/start": ("scale", "initiate_all_key"),
246+ "spread/cancel": "Escape",
247 }
248
249
250
251=== modified file 'tests/autopilot/autopilot/tests/__init__.py'
252--- tests/autopilot/autopilot/tests/__init__.py 2012-04-17 13:34:40 +0000
253+++ tests/autopilot/autopilot/tests/__init__.py 2012-04-25 23:33:18 +0000
254@@ -27,6 +27,7 @@
255 from autopilot.emulators.unity.launcher import LauncherController
256 from autopilot.emulators.unity.panel import PanelController
257 from autopilot.emulators.unity.switcher import Switcher
258+from autopilot.emulators.unity.window_manager import WindowManager
259 from autopilot.emulators.unity.workspace import WorkspaceManager
260 from autopilot.emulators.X11 import ScreenGeometry, Keyboard, Mouse, reset_display
261 from autopilot.glibrunner import GlibRunner
262@@ -239,6 +240,7 @@
263 self.launcher = self._get_launcher_controller()
264 self.panels = self._get_panel_controller()
265 self.switcher = Switcher()
266+ self.window_manager = self._get_window_manager()
267 self.workspace = WorkspaceManager()
268 self.screen_geo = ScreenGeometry()
269 self.addCleanup(self.workspace.switch_to, self.workspace.current_workspace)
270@@ -336,6 +338,11 @@
271 self.assertThat(len(controllers), Equals(1))
272 return controllers[0]
273
274+ def _get_window_manager(self):
275+ managers = WindowManager.get_all_instances()
276+ self.assertThat(len(managers), Equals(1))
277+ return managers[0]
278+
279 def assertVisibleWindowStack(self, stack_start):
280 """Check that the visible window stack starts with the windows passed in.
281
282
283=== modified file 'tests/autopilot/autopilot/tests/test_quicklist.py'
284--- tests/autopilot/autopilot/tests/test_quicklist.py 2012-04-18 23:58:19 +0000
285+++ tests/autopilot/autopilot/tests/test_quicklist.py 2012-04-25 23:33:18 +0000
286@@ -24,6 +24,11 @@
287 ('remmina', {'app_name': 'Remmina'}),
288 ]
289
290+ def open_quicklist_for_icon(self, launcher_icon):
291+ launcher = self.launcher.get_launcher_for_monitor(0)
292+ launcher.click_launcher_icon(launcher_icon, button=3)
293+ self.addCleanup(self.keyboard.press_and_release, "Escape")
294+
295 def test_quicklist_actions(self):
296 """Test that all actions present in the destop file are shown in the quicklist."""
297 self.start_app(self.app_name)
298@@ -37,9 +42,7 @@
299 self.assertThat(launcher_icon, NotEquals(None))
300
301 # open the icon quicklist, and get all the text labels:
302- launcher = self.launcher.get_launcher_for_monitor(0)
303- launcher.click_launcher_icon(launcher_icon, button=3)
304- self.addCleanup(self.keyboard.press_and_release, "Escape")
305+ self.open_quicklist_for_icon(launcher_icon)
306 ql = launcher_icon.get_quicklist()
307 ql_item_texts = [i.text for i in ql.items if type(i) is QuicklistMenuItemLabel]
308
309@@ -53,6 +56,82 @@
310 name = de.content[key]['Name']
311 self.assertThat(ql_item_texts, Contains(name))
312
313+ def test_quicklist_application_item_focus_last_active_window(self):
314+ """This tests shows that when you activate a quicklist application item
315+ only the last focused instance of that application is rasied.
316+
317+ This is tested by opening 2 Mahjongg and a Calculator.
318+ Then we activate the Calculator quicklist item.
319+ Then we actiavte the Mahjongg launcher icon.
320+ """
321+ mahj = self.start_app("Mahjongg")
322+ [mah_win1] = mahj.get_windows()
323+ self.assertTrue(mah_win1.is_focused)
324+
325+ calc = self.start_app("Calculator")
326+ [calc_win] = calc.get_windows()
327+ self.assertTrue(calc_win.is_focused)
328+
329+ self.start_app("Mahjongg")
330+ # Sleeping due to the start_app only waiting for the bamf model to be
331+ # updated with the application. Since the app has already started,
332+ # and we are just waiting on a second window, however a defined sleep
333+ # here is likely to be problematic.
334+ # TODO: fix bamf emulator to enable waiting for new windows.
335+ sleep(1)
336+ [mah_win2] = [w for w in mahj.get_windows() if w.x_id != mah_win1.x_id]
337+ self.assertTrue(mah_win2.is_focused)
338+
339+ self.assertVisibleWindowStack([mah_win2, calc_win, mah_win1])
340+
341+ mahj_icon = self.launcher.model.get_icon_by_desktop_id(mahj.desktop_file)
342+ calc_icon = self.launcher.model.get_icon_by_desktop_id(calc.desktop_file)
343+
344+ self.open_quicklist_for_icon(calc_icon)
345+ calc_ql = calc_icon.get_quicklist()
346+ calc_ql.get_quicklist_application_item(calc.name).mouse_click()
347+ sleep(1)
348+ self.assertTrue(calc_win.is_focused)
349+ self.assertVisibleWindowStack([calc_win, mah_win2, mah_win1])
350+
351+ self.open_quicklist_for_icon(mahj_icon)
352+ mahj_ql = mahj_icon.get_quicklist()
353+ mahj_ql.get_quicklist_application_item(mahj.name).mouse_click()
354+ sleep(1)
355+ self.assertTrue(mah_win2.is_focused)
356+ self.assertVisibleWindowStack([mah_win2, calc_win, mah_win1])
357+
358+ def test_quicklist_application_item_initiate_spread(self):
359+ """This tests shows that when you activate a quicklist application item
360+ when an application window is focused, the spread is initiated.
361+ """
362+ calc = self.start_app("Calculator")
363+ [calc_win1] = calc.get_windows()
364+ self.assertTrue(calc_win1.is_focused)
365+
366+ self.start_app("Calculator")
367+ # Sleeping due to the start_app only waiting for the bamf model to be
368+ # updated with the application. Since the app has already started,
369+ # and we are just waiting on a second window, however a defined sleep
370+ # here is likely to be problematic.
371+ # TODO: fix bamf emulator to enable waiting for new windows.
372+ sleep(1)
373+ [calc_win2] = [w for w in calc.get_windows() if w.x_id != calc_win1.x_id]
374+
375+ self.assertVisibleWindowStack([calc_win2, calc_win1])
376+ self.assertTrue(calc_win2.is_focused)
377+
378+ calc_icon = self.launcher.model.get_icon_by_desktop_id(calc.desktop_file)
379+
380+ self.open_quicklist_for_icon(calc_icon)
381+ calc_ql = calc_icon.get_quicklist()
382+ app_item = calc_ql.get_quicklist_application_item(calc.name)
383+
384+ self.addCleanup(self.keybinding, "spread/cancel")
385+ app_item.mouse_click()
386+ self.assertThat(self.window_manager.scale_active, Eventually(Equals(True)))
387+ self.assertThat(self.window_manager.scale_active_for_group, Eventually(Equals(True)))
388+
389
390 class QuicklistKeyNavigationTests(AutopilotTestCase):
391 """Tests for the quicklist key navigation."""