Merge lp:~azzar1/unity/focus-highlight-category-headers into lp:unity

Proposed by Andrea Azzarone
Status: Merged
Approved by: Andrea Azzarone
Approved revision: no longer in the source branch.
Merged at revision: 1921
Proposed branch: lp:~azzar1/unity/focus-highlight-category-headers
Merge into: lp:unity
Diff against target: 753 lines (+265/-92)
12 files modified
plugins/unityshell/src/DashStyle.cpp (+1/-1)
plugins/unityshell/src/DashStyle.h (+4/-0)
plugins/unityshell/src/DashView.cpp (+3/-1)
plugins/unityshell/src/FilterBar.cpp (+0/-1)
plugins/unityshell/src/IconTexture.cpp (+2/-2)
plugins/unityshell/src/LensBar.cpp (+2/-2)
plugins/unityshell/src/LensView.cpp (+5/-4)
plugins/unityshell/src/PlacesGroup.cpp (+173/-68)
plugins/unityshell/src/PlacesGroup.h (+27/-12)
plugins/unityshell/src/SearchBar.cpp (+2/-1)
tests/autopilot/autopilot/emulators/unity.py (+10/-0)
tests/autopilot/autopilot/tests/test_dash.py (+36/-0)
To merge this branch: bzr merge lp:~azzar1/unity/focus-highlight-category-headers
Reviewer Review Type Date Requested Status
Thomi Richards (community) Approve
Alex Launi Pending
John Lea design Pending
Review via email: mp+91917@code.launchpad.net

This proposal supersedes a proposal from 2012-02-06.

Commit message

Implements the new design for the focus/over state for the category headers. Fixes bug #843026 and #919559 too.

Description of the change

For the design review:
[*] http://ubuntuone.com/1clc4aATtV34wTjwD46hXx
[*] http://ubuntuone.com/1wEHHqplJXGYV5qqtpcYCl

P.S: sometimes backward navigation doesn't work. Not my fault. Someone broke it in trunk.

To post a comment you must log in.
Revision history for this message
Alex Launi (alexlauni) wrote : Posted in a previous version of this proposal

Needs testing. It should be simple enough to write an autopilot test for this.

review: Needs Fixing
Revision history for this message
John Lea (johnlea) wrote : Posted in a previous version of this proposal

Reviewed and signed off by design. Thx!

review: Approve (design)
Revision history for this message
Andrea Azzarone (azzar1) wrote : Posted in a previous version of this proposal

> Needs testing. It should be simple enough to write an autopilot test for this.

We're using the nux key navigation that is already auto-tested. The bug #843026 was caused by a uninitialized variable...

Revision history for this message
Andrea Azzarone (azzar1) wrote : Posted in a previous version of this proposal

Added an AP test btw..

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Your code has lots of integer literals in it:

30 + width += 19 + 40; // add the left padding and the group plugin padding
362 + _focus_layer = dash::Style::Instance().FocusOverlay(geo.width - 16, kFocusHighlightHeight);
398 + base.x + base.width - 10, base.y + base.height - 1,
408 + geo.x = base.x + 11;
409 + geo.width = base.width - 16;

Can we put these integer literals into a const int? these are just meaningless numbers rigth now.

In IconTExture.cpp, in the constructor, instead of doing this:

_accept_key_nav_focus = false;

please use the class initialiser list.

In the PlacesGroup class, the following methods should all be const:

int GetHeaderHeight()
bool HeaderHasKeyFocus();
bool ShouldBeHighlighted();

All member variables should have a trailing underscore, not a leading one. The style guide says:

"""Variable names are all lowercase, with underscores between words. Class member variables have trailing underscores. For instance: my_exciting_local_variable, my_exciting_member_variable_. """

Please add a docstring to this test:

663 + def test_category_header_keynav(self):

that describes what it's testing.

Overall, this looks good, but there are a few small issues that need to be fixed.

Thanks

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

> Your code has lots of integer literals in it:
>
> 30 + width += 19 + 40; // add the left padding and the group plugin
> padding
> 362 + _focus_layer = dash::Style::Instance().FocusOverlay(geo.width - 16,
> kFocusHighlightHeight);
> 398 + base.x + base.width - 10, base.y + base.height - 1,
> 408 + geo.x = base.x + 11;
> 409 + geo.width = base.width - 16;
>

Done.

>
> Can we put these integer literals into a const int? these are just meaningless
> numbers rigth now.
>
> In IconTExture.cpp, in the constructor, instead of doing this:
>
> _accept_key_nav_focus = false;
>
> please use the class initialiser list.
>

Done.

>
> In the PlacesGroup class, the following methods should all be const:
>
> int GetHeaderHeight()
> bool HeaderHasKeyFocus();
> bool ShouldBeHighlighted();
>

Done. Added const to GetExpanded too...

> All member variables should have a trailing underscore, not a leading one. The
> style guide says:
>
> """Variable names are all lowercase, with underscores between words. Class
> member variables have trailing underscores. For instance:
> my_exciting_local_variable, my_exciting_member_variable_. """
>

PlacesGroup use the old style so for the moment I prefer to be consistent.

> Please add a docstring to this test:
>
> 663 + def test_category_header_keynav(self):
>
> that describes what it's testing.
>

Done.

>
>
> Overall, this looks good, but there are a few small issues that need to be
> fixed.
>
> Thanks

Thank you too.

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Looks much better, thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/unityshell/src/DashStyle.cpp'
2--- plugins/unityshell/src/DashStyle.cpp 2012-02-07 20:12:24 +0000
3+++ plugins/unityshell/src/DashStyle.cpp 2012-02-07 22:51:21 +0000
4@@ -1615,7 +1615,7 @@
5 width,
6 height);
7
8- cairo_set_source_rgba(cr, nux::color::White * 0.50f);
9+ cairo_set_source_rgba(cr, nux::Color(1.0f, 1.0f, 1.0f, 0.2f));
10 cairo_fill(cr);
11
12 // Create the texture layer
13
14=== modified file 'plugins/unityshell/src/DashStyle.h'
15--- plugins/unityshell/src/DashStyle.h 2012-02-07 20:12:24 +0000
16+++ plugins/unityshell/src/DashStyle.h 2012-02-07 22:51:21 +0000
17@@ -192,6 +192,10 @@
18 nux::BaseTexture* GetGroupUnexpandIcon();
19 nux::BaseTexture* GetGroupExpandIcon();
20
21+ // Paddings
22+ static const int FILTERS_LEFT_PADDING = 0;
23+ static const int FILTERS_RIGHT_PADDING = 8;
24+
25 sigc::signal<void> changed;
26
27 private:
28
29=== modified file 'plugins/unityshell/src/DashView.cpp'
30--- plugins/unityshell/src/DashView.cpp 2012-02-07 12:00:04 +0000
31+++ plugins/unityshell/src/DashView.cpp 2012-02-07 22:51:21 +0000
32@@ -149,6 +149,7 @@
33 content_layout_->AddView(lenses_layout_, 1, nux::MINOR_POSITION_LEFT);
34
35 home_view_ = new LensView(home_lens_);
36+ AddChild(home_view_);
37 active_lens_view_ = home_view_;
38 lens_views_[home_lens_->id] = home_view_;
39 lenses_layout_->AddView(home_view_);
40@@ -219,7 +220,7 @@
41
42 width = MAX(width, tile_width * 6);
43
44- width += 19 + 32; // add the left padding and the group plugin padding
45+ width += 19 + 32 + dash::Style::FILTERS_LEFT_PADDING + dash::Style::FILTERS_RIGHT_PADDING; // add the left padding and the group plugin padding
46
47 height = search_bar_->GetGeometry().height;
48 height += tile_height * 3;
49@@ -375,6 +376,7 @@
50 lens_bar_->AddLens(lens);
51
52 LensView* view = new LensView(lens);
53+ AddChild(view);
54 view->SetVisible(false);
55 view->uri_activated.connect(sigc::mem_fun(this, &DashView::OnUriActivated));
56 lenses_layout_->AddView(view, 1);
57
58=== modified file 'plugins/unityshell/src/FilterBar.cpp'
59--- plugins/unityshell/src/FilterBar.cpp 2012-02-04 05:28:23 +0000
60+++ plugins/unityshell/src/FilterBar.cpp 2012-02-07 22:51:21 +0000
61@@ -139,4 +139,3 @@
62
63 } // namespace dash
64 } // namespace unity
65-
66
67=== modified file 'plugins/unityshell/src/IconTexture.cpp'
68--- plugins/unityshell/src/IconTexture.cpp 2012-02-07 07:42:12 +0000
69+++ plugins/unityshell/src/IconTexture.cpp 2012-02-07 22:51:21 +0000
70@@ -45,6 +45,7 @@
71
72 IconTexture::IconTexture(nux::BaseTexture* texture, guint width, guint height)
73 : TextureArea(NUX_TRACKER_LOCATION),
74+ _accept_key_nav_focus(false),
75 _icon_name(NULL),
76 _size(height),
77 _texture_cached(texture),
78@@ -54,12 +55,11 @@
79 _opacity(1.0f)
80 {
81 SetMinMaxSize(width, height);
82-
83- _accept_key_nav_focus = false;
84 }
85
86 IconTexture::IconTexture(const char* icon_name, unsigned int size, bool defer_icon_loading)
87 : TextureArea(NUX_TRACKER_LOCATION),
88+ _accept_key_nav_focus(false),
89 _icon_name(NULL),
90 _size(size),
91 _texture_width(0),
92
93=== modified file 'plugins/unityshell/src/LensBar.cpp'
94--- plugins/unityshell/src/LensBar.cpp 2012-02-07 12:00:04 +0000
95+++ plugins/unityshell/src/LensBar.cpp 2012-02-07 22:51:21 +0000
96@@ -84,7 +84,7 @@
97 icons_.push_back(icon);
98 layout_->AddView(icon, 0, nux::eCenter, nux::MINOR_SIZE_FULL);
99
100- icon->mouse_click.connect([&, icon] (int x, int y, unsigned long button, unsigned long keyboard) { SetActive(icon); });
101+ icon->mouse_click.connect([&, icon] (int x, int y, unsigned long button, unsigned long keyboard) { SetActive(icon); QueueDraw(); });
102 icon->mouse_enter.connect([&] (int x, int y, unsigned long button, unsigned long keyboard) { QueueDraw(); });
103 icon->mouse_leave.connect([&] (int x, int y, unsigned long button, unsigned long keyboard) { QueueDraw(); });
104 icon->mouse_down.connect([&] (int x, int y, unsigned long button, unsigned long keyboard) { QueueDraw(); });
105@@ -100,7 +100,7 @@
106 icons_.push_back(icon);
107 layout_->AddView(icon, 0, nux::eCenter, nux::eFix);
108
109- icon->mouse_click.connect([&, icon] (int x, int y, unsigned long button, unsigned long keyboard) { SetActive(icon); });
110+ icon->mouse_click.connect([&, icon] (int x, int y, unsigned long button, unsigned long keyboard) { SetActive(icon); QueueDraw(); });
111 icon->mouse_enter.connect([&] (int x, int y, unsigned long button, unsigned long keyboard) { QueueDraw(); });
112 icon->mouse_leave.connect([&] (int x, int y, unsigned long button, unsigned long keyboard) { QueueDraw(); });
113 icon->mouse_down.connect([&] (int x, int y, unsigned long button, unsigned long keyboard) { QueueDraw(); });
114
115=== modified file 'plugins/unityshell/src/LensView.cpp'
116--- plugins/unityshell/src/LensView.cpp 2012-02-07 12:00:04 +0000
117+++ plugins/unityshell/src/LensView.cpp 2012-02-07 22:51:21 +0000
118@@ -38,6 +38,7 @@
119 namespace
120 {
121 nux::logging::Logger logger("unity.dash.lensview");
122+
123 }
124
125 // This is so we can access some protected members in scrollview.
126@@ -144,8 +145,6 @@
127 void LensView::SetupViews()
128 {
129 layout_ = new nux::HLayout(NUX_TRACKER_LOCATION);
130-
131- layout_->SetHorizontalExternalMargin(8);
132
133 scroll_view_ = new LensScrollView(new PlacesVScrollBar(NUX_TRACKER_LOCATION),
134 NUX_TRACKER_LOCATION);
135@@ -164,6 +163,7 @@
136 layout_->AddView(fscroll_view_, 1);
137
138 fscroll_layout_ = new nux::VLayout();
139+ fscroll_layout_->SetLeftAndRightPadding(dash::Style::FILTERS_LEFT_PADDING, dash::Style::FILTERS_RIGHT_PADDING);
140 fscroll_view_->SetLayout(fscroll_layout_);
141
142 filter_bar_ = new FilterBar();
143@@ -214,6 +214,7 @@
144 << ", " << boost::lexical_cast<int>(index) << ")";
145
146 PlacesGroup* group = new PlacesGroup();
147+ AddChild(group);
148 group->SetName(name.c_str());
149 group->SetIcon(icon_hint.c_str());
150 group->SetExpanded(false);
151@@ -345,8 +346,8 @@
152 filter_bar_->AddFilter(filter);
153
154 int width = dash::Style::Instance().GetTileWidth();
155- fscroll_view_->SetMinimumWidth(width*2);
156- fscroll_view_->SetMaximumWidth(width*2);
157+ fscroll_view_->SetMinimumWidth(width * 2 + dash::Style::FILTERS_LEFT_PADDING + dash::Style::FILTERS_RIGHT_PADDING);
158+ fscroll_view_->SetMaximumWidth(width * 2 + dash::Style::FILTERS_LEFT_PADDING + dash::Style::FILTERS_RIGHT_PADDING);
159
160 can_refine_search = true;
161 }
162
163=== modified file 'plugins/unityshell/src/PlacesGroup.cpp'
164--- plugins/unityshell/src/PlacesGroup.cpp 2012-02-04 05:28:23 +0000
165+++ plugins/unityshell/src/PlacesGroup.cpp 2012-02-07 22:51:21 +0000
166@@ -29,8 +29,6 @@
167
168 #include "StaticCairoText.h"
169
170-#include "Introspectable.h"
171-
172 #include <sigc++/trackable.h>
173 #include <sigc++/signal.h>
174 #include <sigc++/functors/ptr_fun.h>
175@@ -41,24 +39,61 @@
176 #include <glib/gi18n-lib.h>
177
178 #include <Nux/Utils.h>
179-
180+#include <UnityCore/Variant.h>
181 #include "DashStyle.h"
182
183+namespace unity
184+{
185 namespace
186 {
187-const nux::Color kExpandDefaultTextColor(1.0f, 1.0f, 1.0f, 0.5f);
188+
189+const nux::Color kExpandDefaultTextColor(1.0f, 1.0f, 1.0f, 1.0f);
190 const nux::Color kExpandHoverTextColor(1.0f, 1.0f, 1.0f, 1.0f);
191-const float kExpandDefaultIconOpacity = 0.5f;
192+const float kExpandDefaultIconOpacity = 1.0f;
193 const float kExpandHoverIconOpacity = 1.0f;
194+
195+// Category header highlight
196+const int kHighlightHeight = 24;
197+const int kHighlightWidthSubtractor = 16;
198+const int kHighlightLeftPadding = 11;
199+
200+// Line Separator
201+const int kSeparatorLeftPadding = 16;
202+const int kSeparatorWidthSubtractor = 10;
203+
204+class HeaderView : public nux::View
205+{
206+public:
207+ HeaderView(NUX_FILE_LINE_DECL)
208+ : nux::View(NUX_FILE_LINE_PARAM)
209+ {
210+ }
211+
212+protected:
213+ void Draw(nux::GraphicsEngine& graphics_engine, bool force_draw)
214+ {
215+ };
216+
217+ void DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw)
218+ {
219+ if (GetLayout())
220+ GetLayout()->ProcessDraw(graphics_engine, force_draw);
221+ }
222+
223+ bool AcceptKeyNavFocus()
224+ {
225+ return false;
226+ }
227+};
228+
229 }
230
231-namespace unity
232-{
233 NUX_IMPLEMENT_OBJECT_TYPE(PlacesGroup);
234
235 PlacesGroup::PlacesGroup()
236 : View(NUX_TRACKER_LOCATION),
237- _child_view(NULL),
238+ _child_view(nullptr),
239+ _focus_layer(nullptr),
240 _idle_id(0),
241 _is_expanded(true),
242 _n_visible_items_in_unexpand_mode(0),
243@@ -69,14 +104,17 @@
244
245 _cached_name = NULL;
246 _group_layout = new nux::VLayout("", NUX_TRACKER_LOCATION);
247- _group_layout->SetHorizontalExternalMargin(12);
248+ _group_layout->SetHorizontalExternalMargin(20);
249 _group_layout->SetVerticalExternalMargin(1);
250
251 _group_layout->AddLayout(new nux::SpaceLayout(15,15,15,15), 0);
252
253+ _header_view = new HeaderView(NUX_TRACKER_LOCATION);
254+ _group_layout->AddView(_header_view, 0, nux::MINOR_POSITION_TOP, nux::MINOR_SIZE_FIX);
255+
256 _header_layout = new nux::HLayout(NUX_TRACKER_LOCATION);
257 _header_layout->SetHorizontalInternalMargin(10);
258- _group_layout->AddLayout(_header_layout, 0, nux::MINOR_POSITION_TOP, nux::MINOR_SIZE_FIX);
259+ _header_view->SetLayout(_header_layout);
260
261 _icon = new IconTexture("", 24);
262 _icon->SetMinMaxSize(24, 24);
263@@ -85,7 +123,7 @@
264 _text_layout = new nux::HLayout(NUX_TRACKER_LOCATION);
265 _text_layout->SetHorizontalInternalMargin(15);
266 _header_layout->AddLayout(_text_layout, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_MATCHCONTENT);
267-
268+
269 _name = new nux::StaticCairoText("", NUX_TRACKER_LOCATION);
270 _name->SetTextEllipsize(nux::StaticCairoText::NUX_ELLIPSIZE_END);
271 _name->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_LEFT);
272@@ -99,9 +137,6 @@
273 _expand_label->SetTextEllipsize(nux::StaticCairoText::NUX_ELLIPSIZE_END);
274 _expand_label->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_LEFT);
275 _expand_label->SetTextColor(kExpandDefaultTextColor);
276- _expand_label->SetAcceptKeyNavFocus(true);
277- _expand_label->key_nav_focus_activate.connect(sigc::mem_fun(this, &PlacesGroup::OnLabelActivated));
278- _expand_label->key_nav_focus_change.connect(sigc::mem_fun(this, &PlacesGroup::OnLabelFocusChanged));
279
280 _expand_layout->AddView(_expand_label, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FIX);
281
282@@ -113,19 +148,27 @@
283
284 SetLayout(_group_layout);
285
286- /* don't need to disconnect these signals as they are disconnected when this object destroys the contents */
287+ // don't need to disconnect these signals as they are disconnected when this object destroys the contents
288+ _header_view->mouse_enter.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseEnter));
289+ _header_view->mouse_leave.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseLeave));
290+ _header_view->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick));
291 _icon->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick));
292 _icon->mouse_enter.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseEnter));
293 _icon->mouse_leave.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseLeave));
294+ _icon->key_nav_focus_change.connect(sigc::mem_fun(this, &PlacesGroup::OnLabelFocusChanged));
295 _name->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick));
296 _name->mouse_enter.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseEnter));
297 _name->mouse_leave.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseLeave));
298+ _name->key_nav_focus_change.connect(sigc::mem_fun(this, &PlacesGroup::OnLabelFocusChanged));
299 _expand_label->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick));
300 _expand_label->mouse_enter.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseEnter));
301 _expand_label->mouse_leave.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseLeave));
302+ _expand_label->key_nav_focus_activate.connect(sigc::mem_fun(this, &PlacesGroup::OnLabelActivated));
303+ _expand_label->key_nav_focus_change.connect(sigc::mem_fun(this, &PlacesGroup::OnLabelFocusChanged));
304 _expand_icon->mouse_click.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseClick));
305 _expand_icon->mouse_enter.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseEnter));
306 _expand_icon->mouse_leave.connect(sigc::mem_fun(this, &PlacesGroup::RecvMouseLeave));
307+ _expand_icon->key_nav_focus_change.connect(sigc::mem_fun(this, &PlacesGroup::OnLabelFocusChanged));
308 }
309
310 PlacesGroup::~PlacesGroup()
311@@ -135,6 +178,8 @@
312
313 if (_cached_name != NULL)
314 g_free(_cached_name);
315+
316+ delete _focus_layer;
317 }
318
319 void
320@@ -146,17 +191,9 @@
321 void
322 PlacesGroup::OnLabelFocusChanged(nux::Area* label, bool has_focus, nux::KeyNavDirection direction)
323 {
324- if (_expand_label->HasKeyFocus() || _expand_icon->HasKeyFocus())
325- {
326- _expand_label->SetTextColor(kExpandHoverTextColor);
327- _expand_icon->SetOpacity(kExpandHoverIconOpacity);
328- }
329- else if (!IsMouseInside())
330- {
331- _expand_label->SetTextColor(kExpandDefaultTextColor);
332- _expand_icon->SetOpacity(kExpandDefaultIconOpacity);
333- }
334+ QueueDraw();
335 }
336+
337 void
338 PlacesGroup::SetName(const char* name)
339 {
340@@ -248,21 +285,20 @@
341
342 _expand_label->SetText(final);
343 _expand_label->SetVisible(_n_visible_items_in_unexpand_mode < _n_total_items);
344- _expand_label->SetAcceptKeyNavFocus((_n_visible_items_in_unexpand_mode < _n_total_items) ? true : false);
345-
346- if (_expand_label->IsVisible() == false)
347- {
348- if (_expand_icon->IsVisible())
349- {
350- _expand_icon->SetAcceptKeyNavFocus(true);
351- _expand_icon->key_nav_focus_change.connect(sigc::mem_fun(this, &PlacesGroup::OnLabelFocusChanged));
352- }
353- }
354-
355- if (_expand_icon->IsVisible() == false)
356- {
357- _expand_icon->SetAcceptKeyNavFocus(false);
358- }
359+
360+ _icon->SetAcceptKeyNavFocus(false);
361+ _name->SetAcceptKeyNavFocus(false);
362+ _expand_label->SetAcceptKeyNavFocus(false);
363+ _expand_icon->SetAcceptKeyNavFocus(false);
364+
365+ if (_expand_label->IsVisible())
366+ _expand_label->SetAcceptKeyNavFocus(true);
367+ else if (_expand_icon->IsVisible())
368+ _expand_icon->SetAcceptKeyNavFocus(true);
369+ else if (_name->IsVisible())
370+ _name->SetAcceptKeyNavFocus(true);
371+ else if (_icon->IsVisible())
372+ _icon->SetAcceptKeyNavFocus(true);
373
374 QueueDraw();
375
376@@ -302,39 +338,80 @@
377 return FALSE;
378 }
379
380-void PlacesGroup::Draw(nux::GraphicsEngine& GfxContext,
381+long PlacesGroup::ComputeContentSize()
382+{
383+ long ret = nux::View::ComputeContentSize();
384+
385+ nux::Geometry const& geo = GetGeometry();
386+
387+ if (_cached_geometry != geo)
388+ {
389+ if (_focus_layer)
390+ delete _focus_layer;
391+
392+ _focus_layer = dash::Style::Instance().FocusOverlay(geo.width - kHighlightWidthSubtractor, kHighlightHeight);
393+
394+ _cached_geometry = geo;
395+ }
396+
397+ return ret;
398+}
399+
400+void PlacesGroup::Draw(nux::GraphicsEngine& graphics_engine,
401 bool forceDraw)
402 {
403- nux::Geometry base = GetGeometry();
404- GfxContext.PushClippingRectangle(base);
405-
406- nux::Color col(0.15f, 0.15f, 0.15f, 0.15f);
407+ nux::Geometry const& base = GetGeometry();
408+ graphics_engine.PushClippingRectangle(base);
409+
410+ nux::GetPainter().PaintBackground(graphics_engine, base);
411+
412+ graphics_engine.GetRenderStates().SetColorMask(true, true, true, false);
413+ graphics_engine.GetRenderStates().SetBlend(true);
414+ graphics_engine.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER);
415
416 if (_draw_sep)
417 {
418- GfxContext.GetRenderStates().SetColorMask(true, true, true, true);
419- GfxContext.GetRenderStates().SetBlend(true);
420- GfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER);
421- nux::GetPainter().Draw2DLine(GfxContext,
422- base.x + 15, base.y + base.height - 1,
423- base.x + base.width - 15, base.y + base.height - 1,
424+ nux::Color col(0.15f, 0.15f, 0.15f, 0.15f);
425+
426+ nux::GetPainter().Draw2DLine(graphics_engine,
427+ base.x + kSeparatorLeftPadding, base.y + base.height - 1,
428+ base.x + base.width - kSeparatorWidthSubtractor, base.y + base.height - 1,
429 col);
430 }
431
432- GfxContext.PopClippingRectangle();
433+ graphics_engine.GetRenderStates().SetColorMask(true, true, true, true);
434+
435+ if (ShouldBeHighlighted())
436+ {
437+ nux::Geometry geo(_header_layout->GetGeometry());
438+ geo.x = base.x + kHighlightLeftPadding;
439+ geo.width = base.width - kHighlightWidthSubtractor;
440+
441+ _focus_layer->SetGeometry(geo);
442+ _focus_layer->Renderlayer(graphics_engine);
443+ }
444+
445+ graphics_engine.PopClippingRectangle();
446 }
447
448 void
449-PlacesGroup::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw)
450+PlacesGroup::DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw)
451 {
452- nux::Geometry base = GetGeometry();
453- GfxContext.PushClippingRectangle(base);
454-
455- _group_layout->ProcessDraw(GfxContext, force_draw);
456- GfxContext.PopClippingRectangle();
457+ nux::Geometry const& base = GetGeometry();
458+
459+ graphics_engine.PushClippingRectangle(base);
460+
461+ if (ShouldBeHighlighted() && !IsFullRedraw())
462+ {
463+ nux::GetPainter().PushLayer(graphics_engine, _focus_layer->GetGeometry(), _focus_layer);
464+ }
465+
466+ _group_layout->ProcessDraw(graphics_engine, force_draw);
467+
468+ graphics_engine.PopClippingRectangle();
469 }
470
471-void PlacesGroup::PostDraw(nux::GraphicsEngine& GfxContext,
472+void PlacesGroup::PostDraw(nux::GraphicsEngine& graphics_engine,
473 bool forceDraw)
474 {
475 }
476@@ -349,7 +426,7 @@
477 }
478
479 bool
480-PlacesGroup::GetExpanded()
481+PlacesGroup::GetExpanded() const
482 {
483 return _is_expanded;
484 }
485@@ -385,22 +462,17 @@
486 void
487 PlacesGroup::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags)
488 {
489- _expand_label->SetTextColor(kExpandHoverTextColor);
490- _expand_icon->SetOpacity(kExpandHoverIconOpacity);
491+ QueueDraw();
492 }
493
494 void
495 PlacesGroup::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags)
496 {
497- if (!_expand_label->HasKeyFocus())
498- {
499- _expand_label->SetTextColor(kExpandDefaultTextColor);
500- _expand_icon->SetOpacity(kExpandDefaultIconOpacity);
501- }
502+ QueueDraw();
503 }
504
505 int
506-PlacesGroup::GetHeaderHeight()
507+PlacesGroup::GetHeaderHeight() const
508 {
509 return _header_layout->GetGeometry().height;
510 }
511@@ -413,6 +485,20 @@
512 QueueDraw();
513 }
514
515+bool PlacesGroup::HeaderHasKeyFocus() const
516+{
517+ return (_icon && _icon->HasKeyFocus()) ||
518+ (_name && _name->HasKeyFocus()) ||
519+ (_expand_label && _expand_label->HasKeyFocus()) ||
520+ (_expand_icon && _expand_icon->HasKeyFocus());
521+}
522+
523+bool PlacesGroup::ShouldBeHighlighted() const
524+{
525+ return (_header_view && _header_view->IsMousePointerInside()) ||
526+ HeaderHasKeyFocus();
527+}
528+
529 //
530 // Key navigation
531 //
532@@ -422,4 +508,23 @@
533 return false;
534 }
535
536+//
537+// Introspection
538+//
539+std::string PlacesGroup::GetName() const
540+{
541+ return "PlacesGroup";
542+}
543+
544+void PlacesGroup::AddProperties(GVariantBuilder* builder)
545+{
546+ unity::variant::BuilderWrapper wrapper(builder);
547+
548+ wrapper.add("header-x", _header_view->GetAbsoluteX());
549+ wrapper.add("header-y", _header_view->GetAbsoluteY());
550+ wrapper.add("header-width", _header_view->GetAbsoluteWidth());
551+ wrapper.add("header-height", _header_view->GetAbsoluteHeight());
552+ wrapper.add("header-has-keyfocus", HeaderHasKeyFocus());
553+}
554+
555 } // namespace unity
556
557=== modified file 'plugins/unityshell/src/PlacesGroup.h'
558--- plugins/unityshell/src/PlacesGroup.h 2012-02-04 05:28:23 +0000
559+++ plugins/unityshell/src/PlacesGroup.h 2012-02-07 22:51:21 +0000
560@@ -17,8 +17,8 @@
561 * Authored by: Gordon Allott <gord.allott@canonical.com>
562 */
563
564-#ifndef PLACES_GROUP_H
565-#define PLACES_GROUP_H
566+#ifndef UNITYSHELL_PLACES_GROUP_H
567+#define UNITYSHELL_PLACES_GROUP_H
568
569 #include <Nux/Nux.h>
570 #include <Nux/VLayout.h>
571@@ -28,12 +28,18 @@
572 #include <sigc++/sigc++.h>
573
574 #include "IconTexture.h"
575+#include "Introspectable.h"
576 #include "StaticCairoText.h"
577
578+namespace nux
579+{
580+class AbstractPaintLayer;
581+}
582+
583 namespace unity
584 {
585
586-class PlacesGroup : public nux::View
587+class PlacesGroup : public nux::View , public debug::Introspectable
588 {
589 NUX_DECLARE_OBJECT_TYPE(PlacesGroup, nux::View);
590 public:
591@@ -47,7 +53,7 @@
592 nux::StaticCairoText* GetLabel();
593 nux::StaticCairoText* GetExpandLabel();
594
595- void SetChildView(nux::View* view);
596+ void SetChildView(nux::View* view);
597 nux::View* GetChildView();
598
599 void SetChildLayout(nux::Layout* layout);
600@@ -57,27 +63,34 @@
601 void SetCounts(guint n_visible_items_in_unexpand_mode, guint n_total_items);
602
603 void SetExpanded(bool is_expanded);
604- bool GetExpanded();
605+ bool GetExpanded() const;
606
607- int GetHeaderHeight();
608+ int GetHeaderHeight() const;
609
610 void SetDrawSeparator(bool draw_it);
611
612 sigc::signal<void, PlacesGroup*> expanded;
613
614 protected:
615+ long ComputeContentSize();
616+ void Draw(nux::GraphicsEngine& graphics_engine, bool force_draw);
617+ void DrawContent(nux::GraphicsEngine& graphics_engine, bool force_draw);
618+ void PostDraw (nux::GraphicsEngine &graphics_engine, bool force_draw);
619+
620 // Key navigation
621 virtual bool AcceptKeyNavFocus();
622
623+ // Introspection
624+ virtual std::string GetName() const;
625+ virtual void AddProperties(GVariantBuilder* builder);
626+
627 private:
628 void Refresh();
629-
630- void Draw(nux::GraphicsEngine& GfxContext, bool force_draw);
631- void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw);
632- void PostDraw (nux::GraphicsEngine &GfxContext, bool force_draw);
633-
634 static gboolean OnIdleRelayout(PlacesGroup* self);
635
636+ bool HeaderHasKeyFocus() const;
637+ bool ShouldBeHighlighted() const;
638+
639 void RecvMouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags);
640 void RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags);
641 void RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags);
642@@ -87,10 +100,12 @@
643
644 private:
645 nux::VLayout* _group_layout;
646+ nux::View* _header_view;
647 nux::HLayout* _header_layout;
648 nux::HLayout* _text_layout;
649 nux::HLayout* _expand_layout;
650 nux::View* _child_view;
651+ nux::AbstractPaintLayer* _focus_layer;
652
653 IconTexture* _icon;
654 nux::StaticCairoText* _name;
655@@ -104,7 +119,7 @@
656 guint _n_total_items;
657 char* _cached_name;
658 bool _draw_sep;
659-
660+ nux::Geometry _cached_geometry;
661 };
662
663 }
664
665=== modified file 'plugins/unityshell/src/SearchBar.cpp'
666--- plugins/unityshell/src/SearchBar.cpp 2012-02-07 07:42:12 +0000
667+++ plugins/unityshell/src/SearchBar.cpp 2012-02-07 22:51:21 +0000
668@@ -166,7 +166,7 @@
669 filter_layout_ = new nux::HLayout();
670 filter_layout_->SetHorizontalInternalMargin(8);
671 filter_layout_->SetHorizontalExternalMargin(6);
672- filter_space_ = new nux::SpaceLayout(100, 10000, 0, 1);
673+ filter_space_ = new nux::SpaceLayout(1, 10000, 0, 1);
674 filter_layout_->AddLayout(filter_space_, 1);
675 filter_layout_->AddView(show_filters_, 0, nux::MINOR_POSITION_CENTER);
676
677@@ -181,6 +181,7 @@
678
679 layout_->AddView(filter_layout_, 1, nux::MINOR_POSITION_RIGHT, nux::MINOR_SIZE_FULL);
680 }
681+
682 sig_manager_.Add(new Signal<void, GtkSettings*, GParamSpec*>
683 (gtk_settings_get_default(),
684 "notify::gtk-font-name",
685
686=== modified file 'tests/autopilot/autopilot/emulators/unity.py'
687--- tests/autopilot/autopilot/emulators/unity.py 2012-02-07 19:18:45 +0000
688+++ tests/autopilot/autopilot/emulators/unity.py 2012-02-07 22:51:21 +0000
689@@ -372,3 +372,13 @@
690 def reveal_command_lens(self):
691 """Reveal the 'run command' lens."""
692 self._keyboard.press_and_release('Alt+F2')
693+
694+ def get_focused_category(self):
695+ """Returns the current focused category. """
696+ groups = self.get_state("//PlacesGroup[header-has-keyfocus=True]")
697+
698+ if len(groups) >= 1:
699+ return groups[0]
700+ else:
701+ return None
702+
703
704=== modified file 'tests/autopilot/autopilot/tests/test_dash.py'
705--- tests/autopilot/autopilot/tests/test_dash.py 2012-02-07 12:00:04 +0000
706+++ tests/autopilot/autopilot/tests/test_dash.py 2012-02-07 22:51:21 +0000
707@@ -10,6 +10,7 @@
708
709 from autopilot.emulators.unity import Dash
710 from autopilot.emulators.X11 import Keyboard
711+from autopilot.emulators.X11 import Mouse
712 from autopilot.tests import AutopilotTestCase
713 from autopilot.glibrunner import GlibRunner
714
715@@ -100,6 +101,41 @@
716 # ... the lens bar should lose the key focus
717 self.assertEqual(self.dash.get_focused_lens_icon(), "")
718
719+ def test_category_header_keynav(self):
720+ """ This test makes sure that:
721+ 1. A category header can get the focus.
722+ 2. A category header stays highlight when it loses the focus
723+ and mouse is close to it (but not inside).
724+ """
725+ self.dash.ensure_hidden()
726+ self.dash.reveal_application_lens()
727+
728+ kb = Keyboard()
729+ mouse = Mouse()
730+
731+ # Make sure that a category have the focus.
732+ kb.press_and_release("Down")
733+ category = self.dash.get_focused_category()
734+ self.assertIsNot(category, None)
735+
736+ # Make sure that the category is highlighted.
737+ self.assertTrue(category['header-is-highlighted'], True)
738+
739+ # Get the geometry of that category header.
740+ x = category['header-x']
741+ y = category['header-y']
742+ w = category['header-width']
743+ h = category['header-height']
744+
745+ # Move the mouse close the view, and press down.
746+ mouse.move(x+w+10, y+h/2, True)
747+ sleep(1)
748+ kb.press_and_release("Down")
749+
750+ category = self.dash.get_focused_category()
751+ self.assertEqual(category, None)
752+
753+
754
755
756