Merge lp:~3v1n0/unity/shortcut-hint-escaping into lp:unity
- shortcut-hint-escaping
- Merge into trunk
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 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrea Azzarone (community) | Approve | ||
Thomi Richards (community) | Approve | ||
Review via email: mp+95307@code.launchpad.net |
Commit message
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 : | # |
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)) |
Tests look good to me!