Merge lp:~3v1n0/unity/shortcut-hint-escaping into lp:unity

Proposed by Marco Trevisan (Treviño)
Status: Merged
Approved by: Andrea Azzarone
Approved revision: no longer in the source branch.
Merged at revision: 2087
Proposed branch: lp:~3v1n0/unity/shortcut-hint-escaping
Merge into: lp:unity
Diff against target: 752 lines (+465/-56)
10 files modified
manual-tests/SuperTab.txt (+0/-23)
plugins/unityshell/src/ShortcutController.cpp (+25/-6)
plugins/unityshell/src/ShortcutController.h (+7/-1)
plugins/unityshell/src/unityshell.cpp (+37/-24)
plugins/unityshell/src/unityshell.h (+8/-2)
tests/autopilot/autopilot/emulators/X11.py (+3/-0)
tests/autopilot/autopilot/emulators/unity/shortcut_hint.py (+44/-0)
tests/autopilot/autopilot/keybindings.py (+6/-0)
tests/autopilot/autopilot/tests/test_launcher.py (+13/-0)
tests/autopilot/autopilot/tests/test_shortcut_hint.py (+322/-0)
To merge this branch: bzr merge lp:~3v1n0/unity/shortcut-hint-escaping
Reviewer Review Type Date Requested Status
Andrea Azzarone (community) Approve
Thomi Richards (community) Approve
Review via email: mp+95307@code.launchpad.net

Description of the change

Add support to hide the shortcut hint view by Escape key.

I've made some work to handle the Escape keypress events for both the Launcher
Switcher and the Shortcut hint view, using this policy:
 - If the Launcher switcher is active, Escape terminates it
 - If the Shortcut hint is active, Escape terminates it
 - If both are active, the first Escape terminates the Super+Tab,
   the second one will hide the shortcut hint.

Added autopilot support for the Shortcut hint controller, and a first bunch of tests.

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

Tests look good to me!

review: Approve
Revision history for this message
Andrea Azzarone (azzar1) wrote :

Code 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 'manual-tests/SuperTab.txt'
2--- manual-tests/SuperTab.txt 2012-02-29 16:58:59 +0000
3+++ manual-tests/SuperTab.txt 2012-03-06 02:02:19 +0000
4@@ -22,29 +22,6 @@
5 neeeded.
6
7
8-Super Tab switcher interaction with Shortcut Hint
9--------------------------------------------------
10-This test shows the interaction between the shortcut hint overlay and the
11-Super+Tab switcher.
12-
13-#. Start with a clean screen
14-#. Press Super to make the shortcuts-overlay to show
15-#. Then press Tab to initiate the Super+Tab launcher switcher
16-
17-Outcome:
18- The Super+Tab switcher is initialized, and the shortcut hint overlay is still
19- showing. Pressing also Shift to reverse the switcher direction doesn't hide
20- the overlay, that will be hidden once Super is released.
21-
22-#. Start with a clean screen
23-#. Press Super+Tab quickly to make the launcher switcher to initialize without
24- making the shortcut overlay to show.
25-
26-Outcome:
27- Super+Tab switcher is initialized and the shortcut hint overlay is not shown
28- even keeping only super pressed until releasing it and pressing it again.
29-
30-
31 Escaping from Super Tab switcher
32 --------------------------------
33 This test shows how to escape from the Super+Tab switcher.
34
35=== modified file 'plugins/unityshell/src/ShortcutController.cpp'
36--- plugins/unityshell/src/ShortcutController.cpp 2012-02-07 07:42:12 +0000
37+++ plugins/unityshell/src/ShortcutController.cpp 2012-03-06 02:02:19 +0000
38@@ -28,6 +28,7 @@
39 namespace
40 {
41 const unsigned int SUPER_TAP_DURATION = 650;
42+const unsigned int FADE_DURATION = 100;
43 } // anonymouse namespace;
44
45 Controller::Controller(std::list<AbstractHint*>& hints)
46@@ -35,8 +36,8 @@
47 , visible_(false)
48 , enabled_(true)
49 , show_timer_(0)
50- , fade_in_animator_(100)
51- , fade_out_animator_(100)
52+ , fade_in_animator_(FADE_DURATION)
53+ , fade_out_animator_(FADE_DURATION)
54 {
55 bg_color_ = nux::Color(0.0, 0.0, 0.0, 0.5);
56
57@@ -185,16 +186,18 @@
58
59 visible_ = false;
60
61+ if (show_timer_)
62+ {
63+ g_source_remove(show_timer_);
64+ show_timer_ = 0;
65+ }
66+
67 if (view_window_)
68 {
69 view_->SetupBackground(false);
70 fade_in_animator_.Stop();
71 fade_out_animator_.Start(1.0 - view_window_->GetOpacity());
72 }
73-
74- if (show_timer_)
75- g_source_remove(show_timer_);
76- show_timer_ = 0;
77 }
78
79 bool Controller::Visible()
80@@ -212,5 +215,21 @@
81 enabled_ = enabled;
82 }
83
84+std::string Controller::GetName() const
85+{
86+ return "ShortcutController";
87+}
88+
89+void Controller::AddProperties(GVariantBuilder* builder)
90+{
91+ unity::variant::BuilderWrapper(builder)
92+ .add(workarea_)
93+ .add("timeout_duration", SUPER_TAP_DURATION + FADE_DURATION)
94+ .add("enabled", IsEnabled())
95+ .add("about_to_show", (Visible() && !fade_out_animator_.IsRunning() && view_window_->GetOpacity() != 1.0f))
96+ .add("about_to_hide", (Visible() && !fade_in_animator_.IsRunning() && view_window_->GetOpacity() != 1.0f))
97+ .add("visible", (Visible() && view_window_ && view_window_->GetOpacity() == 1.0f));
98+}
99+
100 } // namespace shortcut
101 } // namespace unity
102
103=== modified file 'plugins/unityshell/src/ShortcutController.h'
104--- plugins/unityshell/src/ShortcutController.h 2012-01-15 14:58:57 +0000
105+++ plugins/unityshell/src/ShortcutController.h 2012-03-06 02:02:19 +0000
106@@ -25,8 +25,10 @@
107 #include <Nux/BaseWindow.h>
108 #include <Nux/HLayout.h>
109 #include <NuxCore/Color.h>
110+#include <UnityCore/Variant.h>
111
112 #include "Animator.h"
113+#include "Introspectable.h"
114 #include "ShortcutModel.h"
115 #include "ShortcutView.h"
116 #include "UBusWrapper.h"
117@@ -36,7 +38,7 @@
118 namespace shortcut
119 {
120
121-class Controller
122+class Controller : public debug::Introspectable
123 {
124 public:
125 typedef std::shared_ptr<Controller> Ptr;
126@@ -55,6 +57,10 @@
127 void SetWorkspace(nux::Geometry const& geo);
128 void SetEnabled(bool enabled);
129
130+protected:
131+ std::string GetName() const;
132+ void AddProperties(GVariantBuilder* builder);
133+
134 private:
135 // Private Methods
136 void ConstructView();
137
138=== modified file 'plugins/unityshell/src/unityshell.cpp'
139--- plugins/unityshell/src/unityshell.cpp 2012-03-04 23:58:44 +0000
140+++ plugins/unityshell/src/unityshell.cpp 2012-03-06 02:02:19 +0000
141@@ -866,22 +866,24 @@
142 geo.height);
143 }
144
145-void UnityScreen::EnableCancelAction(bool enabled, int modifiers)
146+void UnityScreen::EnableCancelAction(CancelActionTarget target, bool enabled, int modifiers)
147 {
148 if (enabled)
149 {
150- /* Create a new keybinding for the Escape key and the current modifiers */
151+ /* Create a new keybinding for the Escape key and the current modifiers,
152+ * compiz will take of the ref-counting of the repeated actions */
153 CompAction::KeyBinding binding(9, modifiers);
154
155- _escape_action = CompActionPtr(new CompAction());
156- _escape_action->setKey(binding);
157+ CompActionPtr &escape_action = _escape_actions[target];
158+ escape_action = CompActionPtr(new CompAction());
159+ escape_action->setKey(binding);
160
161- screen->addAction(_escape_action.get());
162+ screen->addAction(escape_action.get());
163 }
164- else if (!enabled && _escape_action.get())
165+ else if (!enabled && _escape_actions[target].get())
166 {
167- screen->removeAction(_escape_action.get());
168- _escape_action = nullptr;
169+ screen->removeAction(_escape_actions[target].get());
170+ _escape_actions.erase(target);
171 }
172 }
173
174@@ -1333,7 +1335,7 @@
175 if (super_keypressed_)
176 {
177 launcher_controller_->KeyNavTerminate(false);
178- EnableCancelAction(false);
179+ EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false);
180 }
181 break;
182 case KeyPress:
183@@ -1347,17 +1349,23 @@
184 // we should just say "key_string[1] = 0" because that is the only
185 // thing that could possibly make sense here.
186 key_string[result] = 0;
187- if (super_keypressed_ && key_sym != XK_Escape)
188+ if (super_keypressed_)
189 {
190- g_idle_add([] (gpointer data) -> gboolean {
191- auto self = static_cast<UnityScreen*>(data);
192- if (!self->launcher_controller_->KeyNavIsActive())
193- {
194- self->shortcut_controller_->SetEnabled(false);
195- self->shortcut_controller_->Hide();
196- }
197- return FALSE;
198- }, this);
199+ if (key_sym != XK_Escape || (key_sym == XK_Escape && !launcher_controller_->KeyNavIsActive()))
200+ {
201+ /* We need an idle to postpone this action, after the current event
202+ * has been processed */
203+ g_idle_add([] (gpointer data) -> gboolean {
204+ auto self = static_cast<UnityScreen*>(data);
205+ if (!self->launcher_controller_->KeyNavIsActive())
206+ {
207+ self->shortcut_controller_->SetEnabled(false);
208+ self->shortcut_controller_->Hide();
209+ self->EnableCancelAction(CancelActionTarget::SHORTCUT_HINT, false);
210+ }
211+ return FALSE;
212+ }, this);
213+ }
214
215 skip_other_plugins = launcher_controller_->HandleLauncherKeyEvent(screen->dpy(), key_sym, event->xkey.keycode, event->xkey.state, key_string);
216 if (!skip_other_plugins)
217@@ -1366,7 +1374,7 @@
218 if (skip_other_plugins && launcher_controller_->KeyNavIsActive())
219 {
220 launcher_controller_->KeyNavTerminate(false);
221- EnableCancelAction(false);
222+ EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false);
223 }
224 }
225 }
226@@ -1507,7 +1515,10 @@
227 }
228
229 if (last_geo.x > monitor_geo.x and last_geo.y > monitor_geo.y)
230+ {
231+ EnableCancelAction(CancelActionTarget::SHORTCUT_HINT, true, action->key().modifiers());
232 shortcut_controller_->Show();
233+ }
234 }
235
236 return true;
237@@ -1521,14 +1532,15 @@
238 return false;
239
240 bool was_tap = state & CompAction::StateTermTapped;
241-
242 super_keypressed_ = false;
243 launcher_controller_->KeyNavTerminate(true);
244 launcher_controller_->HandleLauncherKeyRelease(was_tap);
245- EnableCancelAction(false);
246+ EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false);
247
248 shortcut_controller_->SetEnabled(enable_shortcut_overlay_);
249 shortcut_controller_->Hide();
250+ EnableCancelAction(CancelActionTarget::SHORTCUT_HINT, false);
251+
252 action->setState (action->state() & (unsigned)~(CompAction::StateTermKey));
253 return true;
254 }
255@@ -1743,7 +1755,7 @@
256 if (!launcher_controller_->KeyNavIsActive())
257 {
258 launcher_controller_->KeyNavActivate();
259- EnableCancelAction(true, action->key().modifiers());
260+ EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, true, action->key().modifiers());
261 }
262 else
263 {
264@@ -1764,7 +1776,7 @@
265 bool accept_state = (state & CompAction::StateCancel) == 0;
266 launcher_controller_->KeyNavTerminate(accept_state);
267
268- EnableCancelAction(false);
269+ EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false);
270 action->setState (action->state() & (unsigned)~(CompAction::StateTermKey));
271 return true;
272 }
273@@ -2637,6 +2649,7 @@
274 // Setup Shortcut Hint
275 InitHints();
276 shortcut_controller_.reset(new shortcut::Controller(hints_));
277+ AddChild(shortcut_controller_.get());
278
279 AddChild(dash_controller_.get());
280
281
282=== modified file 'plugins/unityshell/src/unityshell.h'
283--- plugins/unityshell/src/unityshell.h 2012-02-22 09:28:47 +0000
284+++ plugins/unityshell/src/unityshell.h 2012-03-06 02:02:19 +0000
285@@ -236,13 +236,19 @@
286 void AddProperties(GVariantBuilder* builder);
287
288 private:
289+ enum CancelActionTarget
290+ {
291+ LAUNCHER_SWITCHER,
292+ SHORTCUT_HINT
293+ };
294+
295 void initAltTabNextWindow ();
296
297 void SendExecuteCommand();
298
299 void EnsureSuperKeybindings();
300 void CreateSuperNewAction(char shortcut, impl::ActionModifiers flag);
301- void EnableCancelAction(bool enabled, int modifiers = 0);
302+ void EnableCancelAction(CancelActionTarget target, bool enabled, int modifiers = 0);
303
304 static gboolean initPluginActions(gpointer data);
305 void initLauncher();
306@@ -294,7 +300,7 @@
307 typedef std::vector<CompActionPtr> ShortcutActions;
308 ShortcutActions _shortcut_actions;
309 bool super_keypressed_;
310- CompActionPtr _escape_action;
311+ std::map<CancelActionTarget, CompActionPtr> _escape_actions;
312
313 /* keyboard-nav mode */
314 CompWindow* newFocusedWindow;
315
316=== modified file 'tests/autopilot/autopilot/emulators/X11.py'
317--- tests/autopilot/autopilot/emulators/X11.py 2012-03-01 20:16:50 +0000
318+++ tests/autopilot/autopilot/emulators/X11.py 2012-03-06 02:02:19 +0000
319@@ -323,6 +323,9 @@
320 """Get the number of monitors attached to the PC."""
321 return self._default_screen.get_n_monitors()
322
323+ def get_primary_monitor(self):
324+ return self._default_screen.get_primary_monitor()
325+
326 def get_screen_width(self):
327 return self._default_screen.get_width()
328
329
330=== added file 'tests/autopilot/autopilot/emulators/unity/shortcut_hint.py'
331--- tests/autopilot/autopilot/emulators/unity/shortcut_hint.py 1970-01-01 00:00:00 +0000
332+++ tests/autopilot/autopilot/emulators/unity/shortcut_hint.py 2012-03-06 02:02:19 +0000
333@@ -0,0 +1,44 @@
334+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
335+# Copyright 2012 Canonical
336+# Author: Marco Trevisan (Treviño)
337+#
338+# This program is free software: you can redistribute it and/or modify it
339+# under the terms of the GNU General Public License version 3, as published
340+# by the Free Software Foundation.
341+#
342+
343+import logging
344+
345+from autopilot.keybindings import KeybindingsHelper
346+from autopilot.emulators.unity import UnityIntrospectionObject
347+
348+
349+logger = logging.getLogger(__name__)
350+
351+
352+class ShortcutController(UnityIntrospectionObject, KeybindingsHelper):
353+ """ShortcutController proxy class."""
354+
355+ def show(self):
356+ logger.debug("Revealing shortcut hint with keyboard.")
357+ self.keybinding_hold("shortcuthint/reveal")
358+
359+ def hide(self):
360+ logger.debug("Un-revealing shortcut hint with keyboard.")
361+ self.keybinding_release("shortcuthint/reveal")
362+
363+ def cancel(self):
364+ logger.debug("Hide the shortcut hint with keyboard, without releasing the reveal key.")
365+ self.keybinding("shortcuthint/cancel")
366+
367+ def get_geometry(self):
368+ return (self.x, self.y, self.width, self.height)
369+
370+ def get_show_timeout(self):
371+ return self.timeout_duration / 1000.0
372+
373+ def is_enabled(self):
374+ return bool(self.enabled)
375+
376+ def is_visible(self):
377+ return bool(self.visible)
378
379=== modified file 'tests/autopilot/autopilot/keybindings.py'
380--- tests/autopilot/autopilot/keybindings.py 2012-03-04 10:18:08 +0000
381+++ tests/autopilot/autopilot/keybindings.py 2012-03-06 02:02:19 +0000
382@@ -68,6 +68,9 @@
383 "switcher/reveal_details": "Alt+`",
384 "switcher/reveal_all": ("unityshell", "alt_tab_forward_all"),
385 "switcher/cancel": "Escape",
386+ # Shortcut Hint:
387+ "shortcuthint/reveal": ('unityshell', 'show_launcher'),
388+ "shortcuthint/cancel": "Escape",
389 # These are in compiz as 'Alt+Right' and 'Alt+Left', but the fact that it
390 # lists the Alt key won't work for us, so I'm defining them manually.
391 "switcher/next": "Tab",
392@@ -85,6 +88,9 @@
393 "workspace/move_down": ("wall", "down_key"),
394 # Window management
395 "window/minimize": ("core", "minimize_window_key"),
396+ # expo plugin:
397+ "expo/start": ("expo", "expo_key"),
398+ "expo/cancel": "Escape",
399 }
400
401
402
403=== modified file 'tests/autopilot/autopilot/tests/test_launcher.py'
404--- tests/autopilot/autopilot/tests/test_launcher.py 2012-03-04 23:12:19 +0000
405+++ tests/autopilot/autopilot/tests/test_launcher.py 2012-03-06 02:02:19 +0000
406@@ -69,6 +69,19 @@
407 sleep(.5)
408 self.assertThat(self.launcher.key_nav_is_active, Equals(False))
409
410+ def test_launcher_switcher_escape_works(self):
411+ """Test that ending the launcher switcher actually works."""
412+ sleep(.5)
413+ launcher_instance = self.get_launcher()
414+ launcher_instance.start_switcher()
415+ self.addCleanup(launcher_instance.end_switcher, True)
416+ sleep(.25)
417+ self.assertThat(self.launcher.key_nav_is_active(), Equals(True))
418+ sleep(.25)
419+ self.keyboard.press_and_release("Escape")
420+ sleep(.25)
421+ self.assertThat(self.launcher.key_nav_is_active(), Equals(False))
422+
423 def test_launcher_switcher_next_works(self):
424 """Moving to the next launcher item while switcher is activated must work."""
425 sleep(.5)
426
427=== added file 'tests/autopilot/autopilot/tests/test_shortcut_hint.py'
428--- tests/autopilot/autopilot/tests/test_shortcut_hint.py 1970-01-01 00:00:00 +0000
429+++ tests/autopilot/autopilot/tests/test_shortcut_hint.py 2012-03-06 02:02:19 +0000
430@@ -0,0 +1,322 @@
431+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
432+# Copyright 2012 Canonical
433+# Authors: Marco Trevisan (Treviño)
434+#
435+# This program is free software: you can redistribute it and/or modify it
436+# under the terms of the GNU General Public License version 3, as published
437+# by the Free Software Foundation.
438+
439+from testtools.matchers import Equals
440+from time import sleep
441+
442+from autopilot.tests import AutopilotTestCase
443+from autopilot.emulators.unity.shortcut_hint import ShortcutController
444+from autopilot.emulators.X11 import ScreenGeometry
445+
446+
447+class BaseShortcutHintTests(AutopilotTestCase):
448+ """Base class for the shortcut hint tests"""
449+
450+ def setUp(self):
451+ super(BaseShortcutHintTests, self).setUp()
452+
453+ self.DEFAULT_WIDTH = 970;
454+ self.DEFAULT_HEIGHT = 680;
455+
456+ self.shortcut_hint = self.get_shortcut_controller()
457+ self.set_unity_option('shortcut_overlay', True)
458+ self.skip_if_monitor_too_small()
459+ sleep(1)
460+
461+ def skip_if_monitor_too_small(self):
462+ screen = ScreenGeometry();
463+ monitor = screen.get_primary_monitor()
464+ monitor_geo = screen.get_monitor_geometry(monitor);
465+ monitor_w = monitor_geo[2];
466+ monitor_h = monitor_geo[3];
467+ launcher_width = self.launcher.get_launcher_for_monitor(monitor).geometry[2];
468+ panel_height = 24 # TODO get it from panel
469+
470+ if ((monitor_w - launcher_width) <= self.DEFAULT_WIDTH or
471+ (monitor_h - panel_height) <= self.DEFAULT_HEIGHT):
472+ self.skipTest("This test requires a bigger screen, to show the ShortcutHint")
473+
474+ def get_shortcut_controller(self):
475+ controllers = ShortcutController.get_all_instances()
476+ self.assertThat(len(controllers), Equals(1))
477+ return controllers[0]
478+
479+ def get_launcher(self):
480+ # We could parameterise this so all tests run on both monitors (if MM is
481+ # set up), but I think it's fine to just always use monitor primary monitor:
482+ screen = ScreenGeometry();
483+ monitor = screen.get_primary_monitor()
484+ return self.launcher.get_launcher_for_monitor(monitor)
485+
486+
487+class ShortcutHintTests(BaseShortcutHintTests):
488+ """Test the shortcuthint."""
489+
490+ def test_shortcut_hint_reveal(self):
491+ """Test that the shortcut hint is shown."""
492+ sleep(.5)
493+ self.shortcut_hint.show()
494+ self.addCleanup(self.shortcut_hint.hide)
495+ sleep(2)
496+
497+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
498+
499+ def test_shortcut_hint_reveal_timeout(self):
500+ """Test that the shortcut hint is shown when it should."""
501+ sleep(.5)
502+ timeout = self.shortcut_hint.get_show_timeout()
503+ self.shortcut_hint.show()
504+ self.addCleanup(self.shortcut_hint.hide)
505+
506+ sleep(timeout/2.0)
507+ self.assertThat(self.shortcut_hint.is_visible(), Equals(False))
508+
509+ sleep(timeout/2.0)
510+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
511+
512+ def test_shortcut_hint_unreveal(self):
513+ """Test that the shortcut hint is hidden when it should."""
514+ sleep(.5)
515+ self.shortcut_hint.show()
516+ sleep(self.shortcut_hint.get_show_timeout())
517+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
518+ sleep(.25)
519+
520+ self.shortcut_hint.hide()
521+ sleep(.25)
522+ self.assertThat(self.shortcut_hint.is_visible(), Equals(False))
523+
524+ def test_shortcut_hint_cancel(self):
525+ """Test that the shortcut hint is shown when requested."""
526+ sleep(.5)
527+ self.shortcut_hint.show()
528+ self.addCleanup(self.shortcut_hint.hide)
529+ sleep(self.shortcut_hint.get_show_timeout())
530+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
531+ sleep(.25)
532+
533+ self.shortcut_hint.cancel()
534+ sleep(.25)
535+ self.assertThat(self.shortcut_hint.is_visible(), Equals(False))
536+ sleep(self.shortcut_hint.get_show_timeout())
537+ self.assertThat(self.shortcut_hint.is_visible(), Equals(False))
538+
539+ def test_shortcut_hint_geometries(self):
540+ """Test that the shortcut hint has the wanted geometries."""
541+ sleep(.5)
542+ self.shortcut_hint.show()
543+ self.addCleanup(self.shortcut_hint.hide)
544+ sleep(self.shortcut_hint.get_show_timeout())
545+
546+ (x, y, w, h) = self.shortcut_hint.get_geometry()
547+ self.assertThat(w, Equals(self.DEFAULT_WIDTH))
548+ self.assertThat(h, Equals(self.DEFAULT_HEIGHT))
549+
550+
551+class ShortcutHintInteractionsTests(BaseShortcutHintTests):
552+ """Test the shortcuthint interactions with other Unity parts."""
553+
554+ def test_shortcut_hint_hide_using_unity_shortcuts(self):
555+ """Test that the shortcut hints is hidden pressing unity shortcuts."""
556+ sleep(.5)
557+ self.shortcut_hint.show()
558+ self.addCleanup(self.shortcut_hint.hide)
559+ sleep(self.shortcut_hint.get_show_timeout())
560+
561+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
562+ self.keybinding_tap("expo/start")
563+ self.addCleanup(self.keybinding, "expo/cancel")
564+ sleep(.25)
565+ self.assertThat(self.shortcut_hint.is_visible(), Equals(False))
566+ sleep(.25)
567+ self.assertThat(self.shortcut_hint.is_visible(), Equals(False))
568+
569+ def test_launcher_switcher_next_doesnt_show_shortcut_hint(self):
570+ """Moving forward in launcher switcher must not show the shortcut hint."""
571+ sleep(.5)
572+ switcher_timeout = self.shortcut_hint.get_show_timeout()
573+ self.shortcut_hint.show()
574+ self.addCleanup(self.shortcut_hint.hide)
575+
576+ sleep(switcher_timeout * 0.2)
577+ self.assertThat(self.shortcut_hint.is_visible(), Equals(False))
578+
579+ self.keybinding("launcher/switcher/next")
580+ sleep(.25)
581+
582+ self.keybinding("launcher/switcher/next")
583+ self.addCleanup(self.keyboard.press_and_release, "Escape")
584+ sleep(switcher_timeout * 2)
585+
586+ self.assertThat(self.shortcut_hint.is_visible(), Equals(False))
587+
588+ def test_launcher_switcher_prev_doesnt_show_shortcut_hint(self):
589+ """Moving backward in launcher switcher must not show the shortcut hint."""
590+ sleep(.5)
591+ switcher_timeout = self.shortcut_hint.get_show_timeout()
592+ self.shortcut_hint.show()
593+ self.addCleanup(self.shortcut_hint.hide)
594+
595+ sleep(switcher_timeout * 0.2)
596+ self.assertThat(self.shortcut_hint.is_visible(), Equals(False))
597+
598+ self.keybinding("launcher/switcher/next")
599+ self.addCleanup(self.keyboard.press_and_release, "Escape")
600+ sleep(.25)
601+ self.assertThat(self.launcher.key_nav_is_active, Equals(True))
602+
603+ self.keybinding("launcher/switcher/next")
604+ sleep(.25)
605+
606+ self.keybinding("launcher/switcher/prev")
607+ sleep(switcher_timeout)
608+
609+ self.assertThat(self.shortcut_hint.is_visible(), Equals(False))
610+
611+ def test_launcher_switcher_next_keeps_shortcut_hint(self):
612+ """Moving forward in launcher switcher after the shortcut hint has been
613+ shown must keep the shortcuts there.
614+
615+ """
616+ sleep(.5)
617+ show_timeout = self.shortcut_hint.get_show_timeout()
618+ self.shortcut_hint.show()
619+ self.addCleanup(self.shortcut_hint.hide)
620+
621+ sleep(show_timeout)
622+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
623+
624+ launcher = self.g()
625+ launcher.start_switcher()
626+ self.addCleanup(launcher.end_switcher, True)
627+ sleep(.25)
628+ self.assertThat(self.launcher.key_nav_is_active, Equals(True))
629+
630+ launcher.switcher_next()
631+ sleep(.25)
632+ launcher.switcher_next()
633+ sleep(show_timeout)
634+
635+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
636+
637+ def test_launcher_switcher_prev_keeps_shortcut_hint(self):
638+ """Moving backward in launcher switcher after the shortcut hint has been
639+ shown must keep the shortcuts there.
640+
641+ """
642+ sleep(.5)
643+ show_timeout = self.shortcut_hint.get_show_timeout()
644+ self.shortcut_hint.show()
645+ self.addCleanup(self.shortcut_hint.hide)
646+
647+ sleep(show_timeout)
648+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
649+
650+ launcher = self.get_launcher()
651+ launcher.start_switcher()
652+ self.addCleanup(launcher.end_switcher, True)
653+ sleep(.25)
654+ self.assertThat(self.launcher.key_nav_is_active, Equals(True))
655+
656+ launcher.switcher_prev()
657+ sleep(.25)
658+ launcher.switcher_prev()
659+ sleep(show_timeout)
660+
661+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
662+
663+ def test_launcher_switcher_cancel_doesnt_hide_shortcut_hint(self):
664+ """Cancelling the launcher switcher (by Escape) should not hide the
665+ shortcut hint view.
666+
667+ """
668+ sleep(.5)
669+ show_timeout = self.shortcut_hint.get_show_timeout()
670+ self.shortcut_hint.show()
671+ self.addCleanup(self.shortcut_hint.hide)
672+
673+ sleep(show_timeout)
674+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
675+
676+ launcher = self.get_launcher()
677+ launcher.start_switcher()
678+ self.addCleanup(launcher.end_switcher, True)
679+ sleep(.25)
680+ self.assertThat(self.launcher.key_nav_is_active, Equals(True))
681+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
682+
683+ launcher.switcher_next()
684+ sleep(.25)
685+ self.keyboard.press_and_release("Escape")
686+ sleep(.25)
687+
688+ self.assertThat(self.launcher.key_nav_is_active, Equals(False))
689+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
690+ sleep(.5)
691+
692+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
693+
694+ def test_launcher_switcher_and_shortcut_hint_escaping(self):
695+ """Cancelling the launcher switcher (by Escape) should not hide the
696+ shortcut hint view, an extra keypress is needed.
697+
698+ """
699+ sleep(.5)
700+ show_timeout = self.shortcut_hint.get_show_timeout()
701+ self.shortcut_hint.show()
702+ self.addCleanup(self.shortcut_hint.hide)
703+
704+ sleep(show_timeout)
705+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
706+
707+ launcher = self.get_launcher()
708+ launcher.start_switcher()
709+ self.addCleanup(launcher.end_switcher, True)
710+ sleep(.25)
711+ self.assertThat(self.launcher.key_nav_is_active(), Equals(True))
712+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
713+
714+ launcher.switcher_next()
715+ sleep(.25)
716+ self.keyboard.press_and_release("Escape")
717+ sleep(.25)
718+
719+ self.assertThat(self.launcher.key_nav_is_active(), Equals(False))
720+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
721+ sleep(.25)
722+
723+ self.shortcut_hint.cancel()
724+ sleep(.25)
725+ self.assertThat(self.shortcut_hint.is_visible(), Equals(False))
726+
727+ def test_launcher_icons_hints_show_with_shortcut_hint(self):
728+ """When the shortcut hint is shown also the launcer's icons hints should
729+ be shown.
730+
731+ """
732+ launcher = self.get_launcher()
733+ self.shortcut_hint.show()
734+ self.addCleanup(self.shortcut_hint.hide)
735+ sleep(self.shortcut_hint.get_show_timeout())
736+
737+
738+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))
739+ self.assertThat(launcher.are_shortcuts_showing(), Equals(True))
740+
741+ def test_shortcut_hint_shows_with_launcher_icons_hints(self):
742+ """When the launcher icons hints are shown also the shortcut hint should
743+ be shown.
744+
745+ """
746+ launcher = self.get_launcher()
747+ launcher.keyboard_reveal_launcher()
748+ self.addCleanup(launcher.keyboard_unreveal_launcher)
749+ sleep(1)
750+
751+ self.assertThat(launcher.are_shortcuts_showing(), Equals(True))
752+ self.assertThat(self.shortcut_hint.is_visible(), Equals(True))