Merge lp:~thumper/unity/lock-out-hud into lp:unity

Proposed by Tim Penhey
Status: Merged
Merge reported by: Didier Roche-Tolomelli
Merged at revision: not available
Proposed branch: lp:~thumper/unity/lock-out-hud
Merge into: lp:unity
Diff against target: 1083 lines (+427/-93)
17 files modified
manual-tests/Hud.txt (+14/-0)
plugins/unityshell/src/BFBLauncherIcon.cpp (+27/-1)
plugins/unityshell/src/BFBLauncherIcon.h (+5/-1)
plugins/unityshell/src/HudController.cpp (+34/-11)
plugins/unityshell/src/HudController.h (+4/-0)
plugins/unityshell/src/HudLauncherIcon.cpp (+125/-0)
plugins/unityshell/src/HudLauncherIcon.h (+60/-0)
plugins/unityshell/src/HudView.cpp (+47/-26)
plugins/unityshell/src/HudView.h (+11/-2)
plugins/unityshell/src/Launcher.cpp (+47/-34)
plugins/unityshell/src/Launcher.h (+2/-0)
plugins/unityshell/src/LauncherController.cpp (+13/-1)
plugins/unityshell/src/LauncherHideMachine.cpp (+7/-7)
plugins/unityshell/src/LauncherOptions.h (+10/-10)
plugins/unityshell/src/UBusMessages.h (+5/-0)
plugins/unityshell/src/unityshell.cpp (+5/-0)
tests/autopilot/autopilot/tests/test_hud.py (+11/-0)
To merge this branch: bzr merge lp:~thumper/unity/lock-out-hud
Reviewer Review Type Date Requested Status
Gord Allott (community) Approve
Marco Trevisan (Treviño) Approve
Mirco Müller Pending
Review via email: mp+98573@code.launchpad.net

This proposal supersedes a proposal from 2012-03-16.

Commit message

Adapt the HUD visuals to look nice with a locked out launcher.

Description of the change

Changes the behaviour of the HUD/Launcher interaction in HideMode: Never

= The Problem =
See: https://bugs.launchpad.net/ayatana-design/+bug/921506

= The Fix =
Modify the Launcher to accept a new LauncherIcon that will listen to a UBUS message dictating its icon

= Testing =
Manual test included, lock-out behaviour is not included in out AP tests so ensuring a valid AP test seems non trivial at this point, requires an AP test module that can modify compiz options

See http://people.canonical.com/~tim/hud.ogv

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote : Posted in a previous version of this proposal

Actually adding an autopilot test for this is trivial.

What we can do is to run the HUD AP tests with the launcher in each state, locked out and auto-hide. We already do something like this for launchers for each monitor. I'll tackle the HUD tests with thomi tomorrow.

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote : Posted in a previous version of this proposal

we want that for this release isn't it? can someone review it and merge it with the manual test if anyone has the AP test for it?

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

This has some conflicts with trunk, also you're using some lambda functions to connect to sigc::signal's, but in that case you need to disconnect from them in destructor or use a sigc::mem_fun instead (this doesn't apply to the ubus callback functions, of course).

Plus, I'm not sure if that's wanted, but if enabling the launcher's audohide feature, when closing the HUD, the launcher reveals and then closes again.

42 + if (!g_strcmp0(overlay_identity, "hud"))

Why not using overlay_identity.Str() == "hud" ?

 launcher_width.changed.connect([this] (int new_width) { Relayout(); });

I guess you can just pass [&] and please follow what said above. Even if the hud view shouldn't be destroyed, I guess it's better to stay safe.

review: Needs Fixing
Revision history for this message
Tim Penhey (thumper) wrote : Posted in a previous version of this proposal

As well as the launcher popping out when you close the hud, if you do the following:
  press Alt
  press Super

You get the dash with a hidden launcher.

Also, you get a peaking icon when you start the hud with a hidden launcher.

I'm fixing these now. I have a branch that fixes everything so far except making sure the icon saturation is right at the right time.

Revision history for this message
Tim Penhey (thumper) wrote : Posted in a previous version of this proposal

Also worth noting I guess, this branch doesn't fix the bug around the icon size for the hud when you don't have a fixed launcher.

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

Interactions look good to me now.

We should add AP tests to check also that the launcher status change only as it should when using the HUD...

review: Approve
Revision history for this message
Gord Allott (gordallott) wrote :

+1, but technically I'm reviewing the changes Tim made to my code. I also give my code +1 but i may be bias :)

review: Approve
Revision history for this message
Tim Penhey (thumper) wrote :

On 21/03/12 23:07, Gord Allott wrote:
> Review: Approve
>
> +1, but technically I'm reviewing the changes Tim made to my code. I also give my code +1 but i may be bias :)

I approved of Gord's work, and extended. So we should have this covered :)

Revision history for this message
Gord Allott (gordallott) wrote :

Mr Cimitan is happy with the design, so approving

review: Approve
Revision history for this message
Andrea Cimitan (cimi) wrote :

I am fine with the design as a first iteration, two stuffs I noticed and I ask to fix for 12.04:
1) animation of bfb->icon when launcher is on (maybe designers need to provide a desired solution)
2) there's a duplicated separator line between the hud rectangle and the launcher http://i.imgur.com/xgV0L.png
3) the hud icon is using two different tiles when the launcher is on (bfb-like) and the launcher is hidden (normal tile)

Revision history for this message
Andrea Cimitan (cimi) wrote :

three stuffs, of course I can count. welcome back from holiday cimi.

Revision history for this message
Andrea Cimitan (cimi) wrote :

updated screenshot of issue 2) http://i.imgur.com/zYxJ8.png

Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :

I reverted the branch in trunk, see bug #961169

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'manual-tests/Hud.txt'
2--- manual-tests/Hud.txt 2012-03-20 14:00:00 +0000
3+++ manual-tests/Hud.txt 2012-03-21 04:27:19 +0000
4@@ -56,6 +56,20 @@
5 After pressing escape in step three, the text "test" should be removed from the hud search
6 After step four, the hud should dismiss itself and not be present.
7
8+Hud Reveal Behaviour
9+---------
10+This test ensures that the hud behaves correctly with a locked out launcher
11+
12+#. Ensure the launchers hide mode is set to "Never"
13+#. Tap Alt
14+
15+Outcome
16+ The Launcher should stick be locked out.
17+ The Launcher icons should be desaturated.
18+ The top most Launcher icon should be an icon related the the focused window
19+ The BFB should not be present
20+
21+
22 Hud Sending Undo
23 ----------------
24 This test ensure that the undo action is properly handle in a text editor.
25
26=== modified file 'plugins/unityshell/src/BFBLauncherIcon.cpp'
27--- plugins/unityshell/src/BFBLauncherIcon.cpp 2012-03-20 10:18:20 +0000
28+++ plugins/unityshell/src/BFBLauncherIcon.cpp 2012-03-21 04:27:19 +0000
29@@ -32,9 +32,10 @@
30
31 UBusManager BFBLauncherIcon::ubus_manager_;
32
33-BFBLauncherIcon::BFBLauncherIcon()
34+BFBLauncherIcon::BFBLauncherIcon(LauncherHideMode hide_mode)
35 : SimpleLauncherIcon()
36 , reader_(dash::LensDirectoryReader::GetDefault())
37+ , launcher_hide_mode_(hide_mode)
38 {
39 tooltip_text = _("Dash Home");
40 icon_name = PKGDATADIR"/launcher_bfb.png";
41@@ -45,6 +46,31 @@
42 background_color_ = nux::color::White;
43
44 mouse_enter.connect([&](int m) { ubus_manager_.SendMessage(UBUS_DASH_ABOUT_TO_SHOW, NULL); });
45+ ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, sigc::bind(sigc::mem_fun(this, &BFBLauncherIcon::OnOverlayShown), true));
46+ ubus_manager_.RegisterInterest(UBUS_OVERLAY_HIDDEN, sigc::bind(sigc::mem_fun(this, &BFBLauncherIcon::OnOverlayShown), false));
47+}
48+
49+void BFBLauncherIcon::SetHideMode(LauncherHideMode hide_mode)
50+{
51+ launcher_hide_mode_ = hide_mode;
52+}
53+
54+void BFBLauncherIcon::OnOverlayShown(GVariant *data, bool visible)
55+{
56+ unity::glib::String overlay_identity;
57+ gboolean can_maximise = FALSE;
58+ gint32 overlay_monitor = 0;
59+ g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING,
60+ &overlay_identity, &can_maximise, &overlay_monitor);
61+
62+
63+ // If the hud is open, we hide the BFB iff we have a locked launcher
64+ if (!g_strcmp0(overlay_identity, "hud") &&
65+ launcher_hide_mode_ == LAUNCHER_HIDE_NEVER)
66+ {
67+ SetQuirk(QUIRK_VISIBLE, !visible);
68+ EmitNeedsRedraw();
69+ }
70 }
71
72 nux::Color BFBLauncherIcon::BackgroundColor()
73
74=== modified file 'plugins/unityshell/src/BFBLauncherIcon.h'
75--- plugins/unityshell/src/BFBLauncherIcon.h 2012-02-22 08:53:20 +0000
76+++ plugins/unityshell/src/BFBLauncherIcon.h 2012-03-21 04:27:19 +0000
77@@ -24,6 +24,7 @@
78
79 #include <UnityCore/FilesystemLenses.h>
80
81+#include "LauncherOptions.h"
82 #include "UBusWrapper.h"
83
84 namespace unity
85@@ -35,23 +36,26 @@
86 {
87
88 public:
89- BFBLauncherIcon();
90+ BFBLauncherIcon(LauncherHideMode hide_mode);
91
92 virtual nux::Color BackgroundColor();
93 virtual nux::Color GlowColor();
94
95 void ActivateLauncherIcon(ActionArg arg);
96+ void SetHideMode(LauncherHideMode hide_mode);
97
98 protected:
99 std::list<DbusmenuMenuitem*> GetMenus();
100 std::string GetName() const;
101
102 private:
103+ void OnOverlayShown(GVariant *data, bool visible);
104 static void OnMenuitemActivated(DbusmenuMenuitem* item, int time, gchar* lens);
105
106 static unity::UBusManager ubus_manager_;
107 nux::Color background_color_;
108 dash::LensDirectoryReader::Ptr reader_;
109+ LauncherHideMode launcher_hide_mode_;
110 };
111
112 }
113
114=== modified file 'plugins/unityshell/src/HudController.cpp'
115--- plugins/unityshell/src/HudController.cpp 2012-03-20 10:27:41 +0000
116+++ plugins/unityshell/src/HudController.cpp 2012-03-21 04:27:19 +0000
117@@ -39,7 +39,7 @@
118 }
119
120 Controller::Controller()
121- : launcher_width(66)
122+ : launcher_width(65)
123 , hud_service_("com.canonical.hud", "/com/canonical/hud")
124 , window_(nullptr)
125 , visible_(false)
126@@ -47,6 +47,7 @@
127 , timeline_id_(0)
128 , last_opacity_(0.0f)
129 , start_time_(0)
130+ , launcher_is_locked_out_(false)
131 , view_(nullptr)
132 {
133 LOG_DEBUG(logger) << "hud startup";
134@@ -69,6 +70,8 @@
135 }
136 });
137
138+ launcher_width.changed.connect([this] (int new_width) { Relayout(); });
139+
140 PluginAdapter::Default()->compiz_screen_ungrabbed.connect(sigc::mem_fun(this, &Controller::OnScreenUngrabbed));
141
142 hud_service_.queries_updated.connect(sigc::mem_fun(this, &Controller::OnQueriesFinished));
143@@ -154,17 +157,23 @@
144
145 nux::Geometry Controller::GetIdealWindowGeometry()
146 {
147- UScreen *uscreen = UScreen::GetDefault();
148- int primary_monitor = uscreen->GetMonitorWithMouse();
149- auto monitor_geo = uscreen->GetMonitorGeometry(primary_monitor);
150+ UScreen *uscreen = UScreen::GetDefault();
151+ int primary_monitor = uscreen->GetMonitorWithMouse();
152+ auto monitor_geo = uscreen->GetMonitorGeometry(primary_monitor);
153
154- // We want to cover as much of the screen as possible to grab any mouse events outside
155- // of our window
156- panel::Style &panel_style = panel::Style::Instance();
157- return nux::Geometry (monitor_geo.x,
158- monitor_geo.y + panel_style.panel_height,
159- monitor_geo.width,
160- monitor_geo.height - panel_style.panel_height);
161+ // We want to cover as much of the screen as possible to grab any mouse events outside
162+ // of our window
163+ panel::Style &panel_style = panel::Style::Instance();
164+ nux::Geometry geo(monitor_geo.x,
165+ monitor_geo.y + panel_style.panel_height,
166+ monitor_geo.width,
167+ monitor_geo.height - panel_style.panel_height);
168+ if (launcher_is_locked_out_)
169+ {
170+ geo.x += launcher_width;
171+ geo.width -= launcher_width;
172+ }
173+ return geo;
174 }
175
176 void Controller::Relayout(GdkScreen*screen)
177@@ -224,6 +233,16 @@
178 return visible_;
179 }
180
181+void Controller::SetLauncherIsLockedOut(bool launcher_is_locked_out)
182+{
183+ launcher_is_locked_out_ = launcher_is_locked_out;
184+ if (launcher_is_locked_out_)
185+ view_->SetHideIcon(IconHideState::HIDE);
186+ else
187+ view_->SetHideIcon(IconHideState::SHOW);
188+ Relayout();
189+}
190+
191 void Controller::ShowHud()
192 {
193 PluginAdapter* adaptor = PluginAdapter::Default();
194@@ -248,6 +267,7 @@
195 focused_app_icon_ = view_icon.Str();
196
197 LOG_DEBUG(logger) << "Taking application icon: " << focused_app_icon_;
198+ ubus.SendMessage(UBUS_HUD_ICON_CHANGED, g_variant_new_string(focused_app_icon_.c_str()));
199 view_->SetIcon(focused_app_icon_);
200
201 window_->ShowWindow(true);
202@@ -275,6 +295,7 @@
203 nux::GetWindowCompositor().SetKeyFocusArea(view_->default_focus());
204 window_->SetEnterFocusInputArea(view_->default_focus());
205 }
206+
207 void Controller::HideHud(bool restore)
208 {
209 LOG_DEBUG (logger) << "hiding the hud";
210@@ -398,6 +419,7 @@
211 {
212 LOG_DEBUG(logger) << "Selected query, " << query->formatted_text;
213 view_->SetIcon(query->icon_name);
214+ ubus.SendMessage(UBUS_HUD_ICON_CHANGED, g_variant_new_string(query->icon_name.c_str()));
215 }
216
217
218@@ -416,6 +438,7 @@
219
220 LOG_DEBUG(logger) << "setting icon to - " << icon_name;
221 view_->SetIcon(icon_name);
222+ ubus.SendMessage(UBUS_HUD_ICON_CHANGED, g_variant_new_string(icon_name.c_str()));
223 }
224
225 // Introspectable
226
227=== modified file 'plugins/unityshell/src/HudController.h'
228--- plugins/unityshell/src/HudController.h 2012-02-13 04:21:04 +0000
229+++ plugins/unityshell/src/HudController.h 2012-03-21 04:27:19 +0000
230@@ -54,6 +54,8 @@
231 void ShowHud();
232 void HideHud(bool restore_focus = true);
233 bool IsVisible();
234+ void SetLauncherIsLockedOut(bool launcher_is_locked_out);
235+
236 protected:
237 std::string GetName() const;
238 void AddProperties(GVariantBuilder* builder);
239@@ -99,6 +101,8 @@
240 guint timeline_id_;
241 float last_opacity_;
242 gint64 start_time_;
243+
244+ bool launcher_is_locked_out_;
245
246 View* view_;
247 guint ensure_id_;
248
249=== added file 'plugins/unityshell/src/HudLauncherIcon.cpp'
250--- plugins/unityshell/src/HudLauncherIcon.cpp 1970-01-01 00:00:00 +0000
251+++ plugins/unityshell/src/HudLauncherIcon.cpp 2012-03-21 04:27:19 +0000
252@@ -0,0 +1,125 @@
253+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
254+/*
255+ * Copyright (C) 2012 Canonical Ltd
256+ *
257+ * This program is free software: you can redistribute it and/or modify
258+ * it under the terms of the GNU General Public License version 3 as
259+ * published by the Free Software Foundation.
260+ *
261+ * This program is distributed in the hope that it will be useful,
262+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
263+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
264+ * GNU General Public License for more details.
265+ *
266+ * You should have received a copy of the GNU General Public License
267+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
268+ *
269+ * Authored by: Gordon Allott <gord.allott@canonical.com>
270+ */
271+
272+#include "HudLauncherIcon.h"
273+#include "Launcher.h"
274+#include "UnityCore/GLibWrapper.h"
275+#include <NuxCore/Logger.h>
276+
277+#include "UBusMessages.h"
278+
279+#include <glib/gi18n-lib.h>
280+
281+namespace unity
282+{
283+namespace launcher
284+{
285+
286+namespace
287+{
288+nux::logging::Logger logger("unity.launcher.hudlaunchericon");
289+}
290+
291+UBusManager HudLauncherIcon::ubus_manager_;
292+
293+HudLauncherIcon::HudLauncherIcon(LauncherHideMode hide_mode)
294+ : SimpleLauncherIcon()
295+ , launcher_hide_mode_(hide_mode)
296+{
297+ tooltip_text = _("HUD");
298+ icon_name = PKGDATADIR"/launcher_bfb.png";
299+ SetQuirk(QUIRK_VISIBLE, false);
300+ SetQuirk(QUIRK_RUNNING, false);
301+ SetQuirk(QUIRK_ACTIVE, true);
302+ SetIconType(TYPE_HOME);
303+
304+ background_color_ = nux::color::White;
305+
306+ ubus_manager_.RegisterInterest(UBUS_HUD_ICON_CHANGED, [&](GVariant *data)
307+ {
308+ std::string hud_icon_name;
309+ const gchar* data_string = g_variant_get_string(data, NULL);
310+ if (data_string)
311+ hud_icon_name = data_string;
312+ LOG_DEBUG(logger) << "Hud icon change: " << hud_icon_name;
313+ if (!hud_icon_name.empty()
314+ && hud_icon_name != icon_name())
315+ {
316+ icon_name = hud_icon_name;
317+ EmitNeedsRedraw();
318+ }
319+ });
320+
321+ ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, sigc::bind(sigc::mem_fun(this, &HudLauncherIcon::OnOverlayShown), true));
322+ ubus_manager_.RegisterInterest(UBUS_OVERLAY_HIDDEN, sigc::bind(sigc::mem_fun(this, &HudLauncherIcon::OnOverlayShown), false));
323+
324+ mouse_enter.connect([&](int m) { ubus_manager_.SendMessage(UBUS_DASH_ABOUT_TO_SHOW, NULL); });
325+}
326+
327+void HudLauncherIcon::SetHideMode(LauncherHideMode hide_mode)
328+{
329+ launcher_hide_mode_ = hide_mode;
330+}
331+
332+void HudLauncherIcon::OnOverlayShown(GVariant* data, bool visible)
333+{
334+ unity::glib::String overlay_identity;
335+ gboolean can_maximise = FALSE;
336+ gint32 overlay_monitor = 0;
337+ g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING,
338+ &overlay_identity, &can_maximise, &overlay_monitor);
339+
340+ // If the hud is open, we show the HUD button iff we have a locked launcher
341+ if (!g_strcmp0(overlay_identity, "hud") &&
342+ launcher_hide_mode_ == LAUNCHER_HIDE_NEVER)
343+ {
344+ SetQuirk(QUIRK_VISIBLE, visible);
345+ EmitNeedsRedraw();
346+ }
347+}
348+
349+nux::Color HudLauncherIcon::BackgroundColor()
350+{
351+ return background_color_;
352+}
353+
354+nux::Color HudLauncherIcon::GlowColor()
355+{
356+ return background_color_;
357+}
358+
359+void HudLauncherIcon::ActivateLauncherIcon(ActionArg arg)
360+{
361+ // wut? activate? noo we don't do that.
362+}
363+
364+std::list<DbusmenuMenuitem*> HudLauncherIcon::GetMenus()
365+{
366+ std::list<DbusmenuMenuitem*> result;
367+ return result;
368+}
369+
370+std::string HudLauncherIcon::GetName() const
371+{
372+ return "HudLauncherIcon";
373+}
374+
375+} // namespace launcher
376+} // namespace unity
377+
378
379=== added file 'plugins/unityshell/src/HudLauncherIcon.h'
380--- plugins/unityshell/src/HudLauncherIcon.h 1970-01-01 00:00:00 +0000
381+++ plugins/unityshell/src/HudLauncherIcon.h 2012-03-21 04:27:19 +0000
382@@ -0,0 +1,60 @@
383+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
384+/*
385+ * Copyright (C) 2012 Canonical Ltd
386+ *
387+ * This program is free software: you can redistribute it and/or modify
388+ * it under the terms of the GNU General Public License version 3 as
389+ * published by the Free Software Foundation.
390+ *
391+ * This program is distributed in the hope that it will be useful,
392+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
393+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
394+ * GNU General Public License for more details.
395+ *
396+ * You should have received a copy of the GNU General Public License
397+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
398+ *
399+ * Authored by: Gordon Allott <gord.allott@gmail.com>
400+ */
401+
402+#ifndef UNITYSHELL_HUDLAUNCHERICON_H
403+#define UNITYSHELL_HUDLAUNCHERICON_H
404+
405+#include "SimpleLauncherIcon.h"
406+
407+#include "LauncherOptions.h"
408+#include "UBusWrapper.h"
409+
410+namespace unity
411+{
412+namespace launcher
413+{
414+
415+class HudLauncherIcon : public SimpleLauncherIcon
416+{
417+
418+public:
419+ HudLauncherIcon(LauncherHideMode hide_mode);
420+
421+ virtual nux::Color BackgroundColor();
422+ virtual nux::Color GlowColor();
423+
424+ void ActivateLauncherIcon(ActionArg arg);
425+ void SetHideMode(LauncherHideMode hide_mode);
426+
427+protected:
428+ std::list<DbusmenuMenuitem*> GetMenus();
429+ std::string GetName() const;
430+
431+private:
432+ void OnOverlayShown(GVariant *data, bool visible);
433+
434+ static unity::UBusManager ubus_manager_;
435+ nux::Color background_color_;
436+ LauncherHideMode launcher_hide_mode_;
437+};
438+
439+}
440+}
441+
442+#endif // UNITYSHELL_HUDLAUNCHERICON_H
443
444=== modified file 'plugins/unityshell/src/HudView.cpp'
445--- plugins/unityshell/src/HudView.cpp 2012-03-20 15:15:18 +0000
446+++ plugins/unityshell/src/HudView.cpp 2012-03-21 04:27:19 +0000
447@@ -64,10 +64,11 @@
448 , current_height_(0)
449 , timeline_need_more_draw_(false)
450 , selected_button_(0)
451+ , icon_state_(IconHideState::SHOW)
452 , activated_signal_sent_(false)
453 {
454 renderer_.SetOwner(this);
455- renderer_.need_redraw.connect([this] () {
456+ renderer_.need_redraw.connect([this] () {
457 QueueDraw();
458 });
459
460@@ -79,7 +80,7 @@
461 SetupViews();
462 search_bar_->key_down.connect (sigc::mem_fun (this, &View::OnKeyDown));
463
464- search_bar_->activated.connect ([&]()
465+ search_bar_->activated.connect ([&]()
466 {
467 if (!activated_signal_sent_)
468 search_activated.emit(search_bar_->search_string);
469@@ -115,12 +116,12 @@
470 {
471 (*it)->fake_focused = false;
472 }
473- }
474+ }
475 }
476 });
477
478 mouse_down.connect(sigc::mem_fun(this, &View::OnMouseButtonDown));
479-
480+
481 Relayout();
482 }
483
484@@ -143,25 +144,25 @@
485 float progress = (diff - pause_before_grow_length) / grow_anim_length;
486 int last_height = last_known_height_;
487 int new_height = 0;
488-
489+
490 if (last_height < target_height)
491 {
492 // grow
493 new_height = last_height + ((target_height - last_height) * progress);
494 }
495- else
496+ else
497 {
498 //shrink
499 new_height = last_height - ((last_height - target_height) * progress);
500 }
501-
502+
503 LOG_DEBUG(logger) << "resizing to " << target_height << " (" << new_height << ")"
504 << "View height: " << GetGeometry().height;
505 current_height_ = new_height;
506 }
507-
508- QueueDraw();
509-
510+
511+ QueueDraw();
512+
513 if (diff > grow_anim_length + pause_before_grow_length)
514 {
515 // ensure we are at our final location and update last known height
516@@ -187,7 +188,6 @@
517 layout_->SetMinimumWidth(content_geo_.width);
518 layout_->SetMaximumWidth(content_geo_.width);
519 layout_->SetMaximumHeight(content_geo_.height);
520- //layout_->SetMinMaxSize(content_geo_.width, content_geo_.height);
521
522 QueueDraw();
523 }
524@@ -203,7 +203,7 @@
525 // already started, just reset the last known height
526 last_known_height_ = current_height_;
527 }
528-
529+
530 timeline_need_more_draw_ = true;
531 start_time_ = g_get_monotonic_time();
532 QueueDraw();
533@@ -276,17 +276,38 @@
534 QueueDraw();
535 }
536
537+void View::SetHideIcon(IconHideState hide_icon)
538+{
539+ LOG_DEBUG(logger) << "Hide icon called";
540+ if (hide_icon == icon_state_)
541+ return;
542+
543+ icon_state_ = hide_icon;
544+
545+ if (icon_state_ == IconHideState::HIDE)
546+ layout_->RemoveChildObject(dynamic_cast<nux::Area*>(icon_layout_.GetPointer()));
547+ else
548+ layout_->AddLayout(icon_layout_.GetPointer(), 0, nux::MINOR_POSITION_TOP, nux::MINOR_SIZE_MATCHCONTENT, 100.0f, nux::LayoutPosition::NUX_LAYOUT_BEGIN);
549+
550+ Relayout();
551+}
552+
553 // Gives us the width and height of the contents that will give us the best "fit",
554 // which means that the icons/views will not have uneccessary padding, everything will
555 // look tight
556 nux::Geometry View::GetBestFitGeometry(nux::Geometry const& for_geo)
557 {
558- //FIXME - remove magic values, replace with scalable text depending on DPI
559+ //FIXME - remove magic values, replace with scalable text depending on DPI
560 // requires smarter font settings really...
561 int width, height = 0;
562 width = 1024;
563 height = 276;
564-
565+
566+ if (icon_state_ == IconHideState::HIDE)
567+ {
568+ width -= icon_layout_->GetGeometry().width;
569+ }
570+
571 LOG_DEBUG (logger) << "best fit is, " << width << ", " << height;
572
573 return nux::Geometry(0, 0, width, height);
574@@ -321,16 +342,16 @@
575
576 void View::SetupViews()
577 {
578- nux::VLayout* super_layout = new nux::VLayout();
579+ nux::VLayout* super_layout = new nux::VLayout();
580 layout_ = new nux::HLayout();
581- {
582+ {
583 // fill icon layout with icon
584 icon_ = new Icon("", icon_size, true);
585- nux::Layout* icon_layout = new nux::VLayout();
586+ icon_layout_ = new nux::VLayout();
587 {
588- icon_layout->SetVerticalExternalMargin(icon_vertical_margin);
589- icon_layout->AddView(icon_.GetPointer(), 0, nux::MINOR_POSITION_LEFT, nux::MINOR_SIZE_FULL);
590- layout_->AddLayout(icon_layout, 0, nux::MINOR_POSITION_TOP, nux::MINOR_SIZE_MATCHCONTENT);
591+ icon_layout_->SetVerticalExternalMargin(icon_vertical_margin);
592+ icon_layout_->AddView(icon_.GetPointer(), 0, nux::MINOR_POSITION_LEFT, nux::MINOR_SIZE_FULL);
593+ layout_->AddLayout(icon_layout_.GetPointer(), 0, nux::MINOR_POSITION_TOP, nux::MINOR_SIZE_MATCHCONTENT);
594 }
595
596 // add padding to layout between icon and content
597@@ -338,11 +359,11 @@
598 spacing_between_icon_and_content,
599 spacing_between_icon_and_content,
600 spacing_between_icon_and_content), 0);
601-
602+
603 // fill the content layout
604 content_layout_ = new nux::VLayout();
605 {
606- // add the top spacing
607+ // add the top spacing
608 content_layout_->AddLayout(new nux::SpaceLayout(top_spacing,top_spacing,top_spacing,top_spacing), 0);
609
610 // add the search bar to the composite
611@@ -352,7 +373,7 @@
612 search_bar_->search_changed.connect(sigc::mem_fun(this, &View::OnSearchChanged));
613 AddChild(search_bar_.GetPointer());
614 content_layout_->AddView(search_bar_.GetPointer(), 0, nux::MINOR_POSITION_LEFT);
615-
616+
617 button_views_ = new nux::VLayout();
618 button_views_->SetMaximumWidth(content_width);
619
620@@ -365,7 +386,7 @@
621
622 layout_->AddLayout(content_layout_.GetPointer(), 1, nux::MINOR_POSITION_TOP);
623 }
624-
625+
626 super_layout->AddLayout(layout_.GetPointer(), 0);
627 SetLayout(super_layout);
628 }
629@@ -422,7 +443,7 @@
630 draw_content_geo.height = current_height_;
631
632 renderer_.DrawInner(gfx_context, draw_content_geo, absolute_window_geometry_, window_geometry_);
633-
634+
635 gfx_context.PushClippingRectangle(draw_content_geo);
636 if (IsFullRedraw())
637 {
638@@ -440,7 +461,7 @@
639
640 if (timeline_need_more_draw_ && !timeline_id_)
641 {
642- timeline_id_ = g_timeout_add(0, [] (gpointer data) -> gboolean
643+ timeline_id_ = g_timeout_add(0, [] (gpointer data) -> gboolean
644 {
645 View *self = static_cast<View*>(data);
646 self->QueueDraw();
647
648=== modified file 'plugins/unityshell/src/HudView.h'
649--- plugins/unityshell/src/HudView.h 2012-03-16 22:09:15 +0000
650+++ plugins/unityshell/src/HudView.h 2012-03-21 04:27:19 +0000
651@@ -44,6 +44,12 @@
652 namespace hud
653 {
654
655+enum IconHideState
656+{
657+ HIDE,
658+ SHOW
659+};
660+
661 class View : public nux::View, public unity::debug::Introspectable
662 {
663 NUX_DECLARE_OBJECT_TYPE(HudView, nux::View);
664@@ -59,7 +65,8 @@
665
666 void SetQueries(Hud::Queries queries);
667 void SetIcon(std::string icon_name);
668-
669+ void SetHideIcon(IconHideState hide_icon);
670+
671 void AboutToShow();
672 void AboutToHide();
673
674@@ -100,10 +107,11 @@
675 nux::ObjectPtr<nux::Layout> content_layout_;
676 nux::ObjectPtr<nux::VLayout> button_views_;
677 std::list<HudButton::Ptr> buttons_;
678-
679+
680 //FIXME - replace with dash search bar once modifications to dash search bar land
681 SearchBar::Ptr search_bar_;
682 Icon::Ptr icon_;
683+ nux::ObjectPtr<nux::Layout> icon_layout_;
684 bool visible_;
685
686 Hud::Queries queries_;
687@@ -118,6 +126,7 @@
688 int current_height_;
689 bool timeline_need_more_draw_;
690 int selected_button_;
691+ IconHideState icon_state_;
692 bool activated_signal_sent_;
693 };
694
695
696=== modified file 'plugins/unityshell/src/Launcher.cpp'
697--- plugins/unityshell/src/Launcher.cpp 2012-03-20 10:27:41 +0000
698+++ plugins/unityshell/src/Launcher.cpp 2012-03-21 04:27:19 +0000
699@@ -145,8 +145,8 @@
700 , _collection_window(NULL)
701 , _background_color(nux::color::DimGray)
702 , _dash_is_open(false)
703+ , _hud_is_open(false)
704 {
705-
706 _parent = parent;
707 _active_quicklist = nullptr;
708
709@@ -909,7 +909,7 @@
710 arg.saturation = 0.0;
711 }
712
713- if (_dash_is_open)
714+ if (IsOverlayOpen())
715 arg.active_arrow = icon->GetIconType() == AbstractLauncherIcon::TYPE_HOME;
716 else
717 arg.active_arrow = icon->GetQuirk(AbstractLauncherIcon::QUIRK_ACTIVE);
718@@ -1319,20 +1319,22 @@
719 g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING,
720 &overlay_identity, &can_maximise, &overlay_monitor);
721
722-
723- if (!g_strcmp0(overlay_identity, "dash"))
724+ std::string identity = overlay_identity.Str();
725+ if (overlay_monitor == monitor)
726 {
727- if (overlay_monitor == monitor)
728+ if (identity == "dash")
729 {
730- LauncherModel::iterator it;
731-
732 _dash_is_open = true;
733- bg_effect_helper_.enabled = true;
734 _hide_machine->SetQuirk(LauncherHideMachine::PLACES_VISIBLE, true);
735 _hover_machine->SetQuirk(LauncherHoverMachine::PLACES_VISIBLE, true);
736+ }
737+ if (identity == "hud")
738+ {
739+ _hud_is_open = true;
740+ }
741
742- DesaturateIcons();
743- }
744+ bg_effect_helper_.enabled = true;
745+ DesaturateIcons();
746 }
747 }
748
749@@ -1345,26 +1347,37 @@
750 g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING,
751 &overlay_identity, &can_maximise, &overlay_monitor);
752
753- if (!g_strcmp0(overlay_identity, "dash"))
754+ std::string identity = overlay_identity.Str();
755+ if (overlay_monitor == monitor)
756 {
757- if (!_dash_is_open)
758- return;
759-
760- LauncherModel::iterator it;
761-
762- _dash_is_open = false;
763- bg_effect_helper_.enabled = false;
764- _hide_machine->SetQuirk(LauncherHideMachine::PLACES_VISIBLE, false);
765- _hover_machine->SetQuirk(LauncherHoverMachine::PLACES_VISIBLE, false);
766-
767- // as the leave event is no more received when the place is opened
768- // FIXME: remove when we change the mouse grab strategy in nux
769- nux::Point pt = nux::GetWindowCompositor().GetMousePosition();
770-
771- SetStateMouseOverLauncher(GetAbsoluteGeometry().IsInside(pt));
772-
773- SaturateIcons();
774+ if (identity == "dash")
775+ {
776+ _hide_machine->SetQuirk(LauncherHideMachine::PLACES_VISIBLE, false);
777+ _hover_machine->SetQuirk(LauncherHoverMachine::PLACES_VISIBLE, false);
778+ _dash_is_open = false;
779+ }
780+ else if (identity == "hud")
781+ {
782+ _hud_is_open = false;
783+ }
784+
785+ // If they are both now shut, then disable the effect helper and saturate the icons.
786+ if (!_dash_is_open and !_hud_is_open)
787+ {
788+ bg_effect_helper_.enabled = false;
789+ SaturateIcons();
790+ }
791 }
792+
793+ // as the leave event is no more received when the place is opened
794+ // FIXME: remove when we change the mouse grab strategy in nux
795+ nux::Point pt = nux::GetWindowCompositor().GetMousePosition();
796+ SetStateMouseOverLauncher(GetAbsoluteGeometry().IsInside(pt));
797+}
798+
799+bool Launcher::IsOverlayOpen() const
800+{
801+ return _dash_is_open || _hud_is_open;
802 }
803
804 void Launcher::OnActionDone(GVariant* data)
805@@ -1451,7 +1464,7 @@
806 self->_collection_window->PushToBack();
807 self->_collection_window->EnableInputWindow(false, "DNDCollectionWindow");
808
809- if (self->_dash_is_open && !self->_hovered)
810+ if (self->IsOverlayOpen() && !self->_hovered)
811 self->DesaturateIcons();
812
813 self->DndReset();
814@@ -1634,7 +1647,7 @@
815 TimeUtil::SetTimeStruct(&_times[TIME_LEAVE], &_times[TIME_ENTER], ANIM_DURATION);
816 }
817
818- if (_dash_is_open && !_hide_machine->GetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE))
819+ if (IsOverlayOpen() && !_hide_machine->GetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE))
820 {
821 if (hovered && !_hover_machine->GetQuirk(LauncherHoverMachine::SHORTCUT_KEYS_VISIBLE))
822 SaturateIcons();
823@@ -1883,7 +1896,7 @@
824 pressure_color);
825 }
826
827- if (_dash_is_open)
828+ if (IsOverlayOpen())
829 {
830 if (BackgroundEffectHelper::blur_type != unity::BLUR_NONE && (bkg_box.x + bkg_box.width > 0))
831 {
832@@ -1984,7 +1997,7 @@
833 icon_renderer->RenderIcon(GfxContext, *rev_it, bkg_box, base);
834 }
835
836- if (!_dash_is_open)
837+ if (!IsOverlayOpen())
838 {
839 const double right_line_opacity = 0.15f * launcher_alpha;
840
841@@ -2604,7 +2617,7 @@
842
843 _hide_machine->SetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE, true);
844
845- if (_dash_is_open)
846+ if (IsOverlayOpen())
847 SaturateIcons();
848
849 for (auto it : _dnd_data.Uris())
850@@ -2730,7 +2743,7 @@
851
852 SetMousePosition(x - parent->GetGeometry().x, y - parent->GetGeometry().y);
853
854- if (!_dash_is_open && _mouse_position.x == 0 && _mouse_position.y <= (_parent->GetGeometry().height - _icon_size - 2 * _space_between_icons) && !_drag_edge_touching)
855+ if (!IsOverlayOpen() && _mouse_position.x == 0 && _mouse_position.y <= (_parent->GetGeometry().height - _icon_size - 2 * _space_between_icons) && !_drag_edge_touching)
856 {
857 if (_dnd_hovered_icon)
858 _dnd_hovered_icon->SendDndLeave();
859
860=== modified file 'plugins/unityshell/src/Launcher.h'
861--- plugins/unityshell/src/Launcher.h 2012-03-13 02:07:54 +0000
862+++ plugins/unityshell/src/Launcher.h 2012-03-21 04:27:19 +0000
863@@ -268,6 +268,7 @@
864
865 void OnOverlayHidden(GVariant* data);
866 void OnOverlayShown(GVariant* data);
867+ bool IsOverlayOpen() const;
868
869 void DesaturateIcons();
870 void SaturateIcons();
871@@ -406,6 +407,7 @@
872 BaseTexturePtr launcher_sheen_;
873 BaseTexturePtr launcher_pressure_effect_;
874 bool _dash_is_open;
875+ bool _hud_is_open;
876
877 ui::AbstractIconRenderer::Ptr icon_renderer;
878 BackgroundEffectHelper bg_effect_helper_;
879
880=== modified file 'plugins/unityshell/src/LauncherController.cpp'
881--- plugins/unityshell/src/LauncherController.cpp 2012-03-20 02:13:17 +0000
882+++ plugins/unityshell/src/LauncherController.cpp 2012-03-21 04:27:19 +0000
883@@ -32,6 +32,7 @@
884 #include "DeviceLauncherIcon.h"
885 #include "DeviceLauncherSection.h"
886 #include "FavoriteStore.h"
887+#include "HudLauncherIcon.h"
888 #include "Launcher.h"
889 #include "LauncherController.h"
890 #include "LauncherEntryRemote.h"
891@@ -228,7 +229,18 @@
892 FavoriteStore::GetDefault().favorite_removed.connect(sigc::mem_fun(this, &Impl::OnFavoriteStoreFavoriteRemoved));
893 FavoriteStore::GetDefault().reordered.connect(sigc::mem_fun(this, &Impl::OnFavoriteStoreReordered));
894
895- RegisterIcon(AbstractLauncherIcon::Ptr(new BFBLauncherIcon()));
896+ LauncherHideMode hide_mode = parent_->options()->hide_mode;
897+ BFBLauncherIcon* bfb = new BFBLauncherIcon(hide_mode);
898+ parent_->options()->hide_mode.changed.connect([bfb](LauncherHideMode mode) {
899+ bfb->SetHideMode(mode);
900+ });
901+ RegisterIcon(AbstractLauncherIcon::Ptr(bfb));
902+
903+ HudLauncherIcon* hud = new HudLauncherIcon(hide_mode);
904+ parent_->options()->hide_mode.changed.connect([hud](LauncherHideMode mode) {
905+ hud->SetHideMode(mode);
906+ });
907+ RegisterIcon(AbstractLauncherIcon::Ptr(hud));
908 desktop_icon_ = AbstractLauncherIcon::Ptr(new DesktopLauncherIcon());
909
910 uscreen->changed.connect(sigc::mem_fun(this, &Controller::Impl::OnScreenChanged));
911
912=== modified file 'plugins/unityshell/src/LauncherHideMachine.cpp'
913--- plugins/unityshell/src/LauncherHideMachine.cpp 2012-02-08 16:43:47 +0000
914+++ plugins/unityshell/src/LauncherHideMachine.cpp 2012-03-21 04:27:19 +0000
915@@ -134,19 +134,19 @@
916 {
917 bool should_hide;
918
919- // early check to see if we are locking to hidden
920+ if (_mode == HIDE_NEVER)
921+ {
922+ SetShouldHide(false, skip_delay);
923+ return;
924+ }
925+
926+ // early check to see if we are locking to hidden - but only if we are in non HIDE_NEVER
927 if (GetQuirk(LOCK_HIDE))
928 {
929 SetShouldHide(true, true);
930 return;
931 }
932
933- if (_mode == HIDE_NEVER)
934- {
935- SetShouldHide(false, skip_delay);
936- return;
937- }
938-
939 do
940 {
941 // first we check the condition where external DND is active and the push off has happened
942
943=== modified file 'plugins/unityshell/src/LauncherOptions.h'
944--- plugins/unityshell/src/LauncherOptions.h 2012-03-13 02:43:20 +0000
945+++ plugins/unityshell/src/LauncherOptions.h 2012-03-21 04:27:19 +0000
946@@ -33,42 +33,42 @@
947 namespace launcher
948 {
949
950-typedef enum
951+enum LauncherHideMode
952 {
953 LAUNCHER_HIDE_NEVER,
954 LAUNCHER_HIDE_AUTOHIDE,
955-} LauncherHideMode;
956+};
957
958-typedef enum
959+enum LaunchAnimation
960 {
961 LAUNCH_ANIMATION_NONE,
962 LAUNCH_ANIMATION_PULSE,
963 LAUNCH_ANIMATION_BLINK,
964-} LaunchAnimation;
965+};
966
967-typedef enum
968+enum UrgentAnimation
969 {
970 URGENT_ANIMATION_NONE,
971 URGENT_ANIMATION_PULSE,
972 URGENT_ANIMATION_WIGGLE,
973-} UrgentAnimation;
974+};
975
976-typedef enum
977+enum AutoHideAnimation
978 {
979 FADE_OR_SLIDE,
980 SLIDE_ONLY,
981 FADE_ONLY,
982 FADE_AND_SLIDE,
983-} AutoHideAnimation;
984+};
985
986-typedef enum
987+enum BacklightMode
988 {
989 BACKLIGHT_ALWAYS_ON,
990 BACKLIGHT_NORMAL,
991 BACKLIGHT_ALWAYS_OFF,
992 BACKLIGHT_EDGE_TOGGLE,
993 BACKLIGHT_NORMAL_EDGE_TOGGLE
994-} BacklightMode;
995+};
996
997 enum RevealTrigger
998 {
999
1000=== modified file 'plugins/unityshell/src/UBusMessages.h'
1001--- plugins/unityshell/src/UBusMessages.h 2012-02-07 07:42:12 +0000
1002+++ plugins/unityshell/src/UBusMessages.h 2012-03-21 04:27:19 +0000
1003@@ -64,6 +64,9 @@
1004 // Signal to force the launcher into locked mode, (b)
1005 #define UBUS_LAUNCHER_LOCK_HIDE "LAUNCHER_LOCK_HIDE"
1006
1007+// Signal to emit changes to the launcher hide mode behaviour (b), true = locked out, false = unlocked
1008+#define UBUS_LAUNCHER_HIDE_MODE_CHANGE "LAUNCHER_HIDE_MODE_CHANGE"
1009+
1010 // Signal sent when a quicklist is shown.
1011 #define UBUS_QUICKLIST_SHOWN "QUICKLIST_SHOWN"
1012
1013@@ -78,6 +81,8 @@
1014 // FIXME - fix the nux focus api so we don't need this
1015 #define UBUS_RESULT_VIEW_KEYNAV_CHANGED "RESULT_VIEW_KEYNAV_CHANGED"
1016
1017+// Sends a string datatype containing the new icon name
1018+#define UBUS_HUD_ICON_CHANGED "HUD_ICON_CHANGED"
1019 #define UBUS_HUD_CLOSE_REQUEST "HUD_CLOSE_REQUEST"
1020
1021 // Signals sent when the switcher is shown, hidden or changes selection
1022
1023=== modified file 'plugins/unityshell/src/unityshell.cpp'
1024--- plugins/unityshell/src/unityshell.cpp 2012-03-20 15:37:36 +0000
1025+++ plugins/unityshell/src/unityshell.cpp 2012-03-21 04:27:19 +0000
1026@@ -2521,8 +2521,11 @@
1027 break;
1028 }
1029 case UnityshellOptions::LauncherHideMode:
1030+ {
1031 launcher_options->hide_mode = (unity::launcher::LauncherHideMode) optionGetLauncherHideMode();
1032+ hud_controller_->SetLauncherIsLockedOut(launcher_options->hide_mode == unity::launcher::LauncherHideMode::LAUNCHER_HIDE_NEVER);
1033 break;
1034+ }
1035 case UnityshellOptions::BacklightMode:
1036 launcher_options->backlight_mode = (unity::launcher::BacklightMode) optionGetBacklightMode();
1037 break;
1038@@ -2754,6 +2757,8 @@
1039
1040 /* Setup Hud */
1041 hud_controller_.reset(new hud::Controller());
1042+ auto hide_mode = (unity::launcher::LauncherHideMode) optionGetLauncherHideMode();
1043+ hud_controller_->SetLauncherIsLockedOut(hide_mode == unity::launcher::LauncherHideMode::LAUNCHER_HIDE_NEVER);
1044 AddChild(hud_controller_.get());
1045 LOG_INFO(logger) << "initLauncher-hud " << timer.ElapsedSeconds() << "s";
1046
1047
1048=== modified file 'tests/autopilot/autopilot/tests/test_hud.py'
1049--- tests/autopilot/autopilot/tests/test_hud.py 2012-03-20 15:15:18 +0000
1050+++ tests/autopilot/autopilot/tests/test_hud.py 2012-03-21 04:27:19 +0000
1051@@ -15,8 +15,15 @@
1052
1053 class HudTests(AutopilotTestCase):
1054
1055+ scenarios = [
1056+ ('Launcher never hide', {'launcher_hide_mode': 0}),
1057+ ('Launcher autohide', {'launcher_hide_mode': 1}),
1058+ ]
1059+
1060 def setUp(self):
1061 super(HudTests, self).setUp()
1062+ self.set_unity_option('launcher_hide_mode', self.launcher_hide_mode)
1063+ sleep(0.5)
1064 self.hud = self.get_hud_controller()
1065
1066 def tearDown(self):
1067@@ -54,12 +61,16 @@
1068 def test_check_a_values(self):
1069 self.reveal_hud()
1070 self.keyboard.type('a')
1071+ # Give the HUD a second to get values.
1072+ sleep(1)
1073 self.assertThat(self.hud.num_buttons, Equals(5))
1074 self.assertThat(self.hud.selected_button, Equals(1))
1075
1076 def test_up_down_arrows(self):
1077 self.reveal_hud()
1078 self.keyboard.type('a')
1079+ # Give the HUD a second to get values.
1080+ sleep(1)
1081 self.keyboard.press_and_release('Down')
1082 self.assertThat(self.hud.selected_button, Equals(2))
1083 self.keyboard.press_and_release('Down')