Merge lp:~nick-dedekind/unity/smart-scopes.static-resultview-focus into lp:~unity-team/unity/libunity-7.0-breakage

Proposed by Nick Dedekind
Status: Merged
Approved by: Michal Hruby
Approved revision: no longer in the source branch.
Merged at revision: 3135
Proposed branch: lp:~nick-dedekind/unity/smart-scopes.static-resultview-focus
Merge into: lp:~unity-team/unity/libunity-7.0-breakage
Diff against target: 520 lines (+244/-19)
11 files modified
dash/DashView.cpp (+6/-0)
dash/PlacesGroup.cpp (+35/-0)
dash/PlacesGroup.h (+3/-0)
dash/ResultRendererTile.cpp (+8/-5)
dash/ResultView.cpp (+9/-0)
dash/ResultView.h (+3/-0)
dash/ResultViewGrid.cpp (+22/-1)
dash/ResultViewGrid.h (+3/-1)
dash/ScopeView.cpp (+144/-10)
dash/ScopeView.h (+10/-1)
debian/control (+1/-1)
To merge this branch: bzr merge lp:~nick-dedekind/unity/smart-scopes.static-resultview-focus
Reviewer Review Type Date Requested Status
Michal Hruby (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Andrea Azzarone (community) Approve
John Lea Pending
Review via email: mp+161737@code.launchpad.net

Commit message

Save/Restore dash category focus when adding/removing/reordering categories.

Description of the change

Save/Restore dash category focus when adding/removing/reordering categories.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

requires Nux r779 or above.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote :

Fixed issue with category visibility changes.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Andrea Azzarone (azzar1) wrote :

LGTM and works here.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michal Hruby (mhr3) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'dash/DashView.cpp'
2--- dash/DashView.cpp 2013-04-25 15:31:32 +0000
3+++ dash/DashView.cpp 2013-05-03 17:13:27 +0000
4@@ -153,6 +153,12 @@
5 scopes_->scope_added.connect(sigc::mem_fun(this, &DashView::OnScopeAdded));
6 scopes_->LoadScopes();
7 }
8+
9+ // If nux resets the focus, we need to set it back to the default focus item.
10+ nux::GetWindowCompositor().key_nav_focus_change.connect([this](nux::Area *area, bool has_focus, nux::KeyNavDirection direction) {
11+ if (visible_ && !area)
12+ nux::GetWindowCompositor().SetKeyFocusArea(default_focus());
13+ });
14 }
15
16 DashView::~DashView()
17
18=== modified file 'dash/PlacesGroup.cpp'
19--- dash/PlacesGroup.cpp 2013-03-22 12:44:53 +0000
20+++ dash/PlacesGroup.cpp 2013-05-03 17:13:27 +0000
21@@ -661,5 +661,40 @@
22 wrapper.add("name-label-baseline", _name->GetBaseline());
23 }
24
25+glib::Variant PlacesGroup::GetCurrentFocus() const
26+{
27+ if (_header_view && _header_view->HasKeyFocus())
28+ {
29+ return g_variant_new_string("HeaderView");
30+ }
31+ else if (_child_view && _child_view->HasKeyFocus())
32+ {
33+ return g_variant_new("(si)", "ResultView", _child_view->GetSelectedIndex());
34+ }
35+ return glib::Variant();
36+}
37+
38+void PlacesGroup::SetCurrentFocus(glib::Variant const& variant)
39+{
40+ if (g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING))
41+ {
42+ std::string str = glib::gchar_to_string(g_variant_get_string(variant, NULL));
43+ if (str == "HeaderView" && _header_view)
44+ nux::GetWindowCompositor().SetKeyFocusArea(_header_view);
45+ }
46+ else if (g_variant_is_of_type(variant, G_VARIANT_TYPE("(si)")))
47+ {
48+ glib::String str;
49+
50+ gint32 index;
51+ g_variant_get(variant, "(si)", &str, &index);
52+ if (str.Str() == "ResultView" && _child_view)
53+ {
54+ _child_view->SetSelectedIndex(index);
55+ nux::GetWindowCompositor().SetKeyFocusArea(_child_view);
56+ }
57+ }
58+}
59+
60 } // namespace dash
61 } // namespace unity
62
63=== modified file 'dash/PlacesGroup.h'
64--- dash/PlacesGroup.h 2013-03-11 17:20:46 +0000
65+++ dash/PlacesGroup.h 2013-05-03 17:13:27 +0000
66@@ -92,6 +92,9 @@
67
68 sigc::signal<void, PlacesGroup*> expanded;
69
70+ glib::Variant GetCurrentFocus() const;
71+ void SetCurrentFocus(glib::Variant const& variant);
72+
73 protected:
74 long ComputeContentSize();
75
76
77=== modified file 'dash/ResultRendererTile.cpp'
78--- dash/ResultRendererTile.cpp 2013-01-18 17:31:00 +0000
79+++ dash/ResultRendererTile.cpp 2013-05-03 17:13:27 +0000
80@@ -267,6 +267,14 @@
81 glib::Object<GIcon> icon(g_icon_new_for_string(icon_name.c_str(), NULL));
82 TextureContainer* container = row.renderer<TextureContainer*>();
83
84+ if (container)
85+ {
86+ TextureCache& cache = TextureCache::GetDefault();
87+
88+ BaseTexturePtr texture_prelight(cache.FindTexture("resultview_prelight", style.GetTileIconHightlightWidth(), style.GetTileIconHightlightHeight(), sigc::mem_fun(this, &ResultRendererTile::DrawHighlight)));
89+ container->prelight = texture_prelight;
90+ }
91+
92 IconLoader::IconLoaderCallback slot = sigc::bind(sigc::mem_fun(this, &ResultRendererTile::IconLoaded), icon_hint, row);
93
94 if (icon.IsType(G_TYPE_ICON))
95@@ -363,18 +371,13 @@
96 {
97 TextureContainer *container = row.renderer<TextureContainer*>();
98
99- dash::Style& style = dash::Style::Instance();
100-
101 if (pixbuf && container)
102 {
103 TextureCache& cache = TextureCache::GetDefault();
104 BaseTexturePtr texture(cache.FindTexture(icon_name, max_width, max_height,
105 sigc::bind(sigc::mem_fun(this, &ResultRendererTile::CreateTextureCallback), pixbuf)));
106
107- BaseTexturePtr texture_prelight(cache.FindTexture("resultview_prelight", style.GetTileIconHightlightWidth(), style.GetTileIconHightlightHeight(), sigc::mem_fun(this, &ResultRendererTile::DrawHighlight)));
108-
109 container->icon = texture;
110- container->prelight = texture_prelight;
111 container->drag_icon = pixbuf;
112
113 NeedsRedraw.emit();
114
115=== modified file 'dash/ResultView.cpp'
116--- dash/ResultView.cpp 2013-04-24 16:47:23 +0000
117+++ dash/ResultView.cpp 2013-05-03 17:13:27 +0000
118@@ -123,6 +123,15 @@
119 }
120 }
121
122+int ResultView::GetSelectedIndex() const
123+{
124+ return -1;
125+}
126+
127+void ResultView::SetSelectedIndex(int index)
128+{
129+}
130+
131 unsigned ResultView::GetNumResults()
132 {
133 if (result_model_)
134
135=== modified file 'dash/ResultView.h'
136--- dash/ResultView.h 2013-04-24 16:47:23 +0000
137+++ dash/ResultView.h 2013-05-03 17:13:27 +0000
138@@ -86,6 +86,9 @@
139 void AddProperties(GVariantBuilder* builder);
140 IntrospectableList GetIntrospectableChildren();
141
142+ virtual int GetSelectedIndex() const;
143+ virtual void SetSelectedIndex(int index);
144+
145 virtual void Activate(LocalResult const& local_result, int index, ActivateType type) = 0;
146
147 std::vector<ResultViewTexture::Ptr> const& GetResultTextureContainers();
148
149=== modified file 'dash/ResultViewGrid.cpp'
150--- dash/ResultViewGrid.cpp 2013-04-17 15:10:39 +0000
151+++ dash/ResultViewGrid.cpp 2013-05-03 17:13:27 +0000
152@@ -942,12 +942,33 @@
153 }
154
155 int
156-ResultViewGrid::GetSelectedIndex()
157+ResultViewGrid::GetSelectedIndex() const
158 {
159 return selected_index_;
160 }
161
162 void
163+ResultViewGrid::SetSelectedIndex(int index)
164+{
165+ unsigned num_results = GetNumResults();
166+ if (num_results == 0)
167+ {
168+ focused_result_ = LocalResult();
169+ index = -1;
170+ }
171+ else
172+ {
173+ if (index >= 0 && (unsigned)index >= num_results)
174+ index = num_results-1;
175+
176+ ResultIterator iter(GetIteratorAtRow(index));
177+ focused_result_ = (*iter);
178+ }
179+
180+ selected_index_ = index;
181+}
182+
183+void
184 ResultViewGrid::UpdateRenderTextures()
185 {
186 nux::Geometry root_geo(GetAbsoluteGeometry());
187
188=== modified file 'dash/ResultViewGrid.h'
189--- dash/ResultViewGrid.h 2013-03-27 11:18:56 +0000
190+++ dash/ResultViewGrid.h 2013-05-03 17:13:27 +0000
191@@ -49,7 +49,9 @@
192
193 sigc::signal<void> selection_change;
194
195- int GetSelectedIndex();
196+ virtual int GetSelectedIndex() const;
197+ virtual void SetSelectedIndex(int index);
198+
199 virtual unsigned GetIndexAtPosition(int x, int y);
200
201 virtual void Activate(LocalResult const& local_result, int index, ActivateType type);
202
203=== modified file 'dash/ScopeView.cpp'
204--- dash/ScopeView.cpp 2013-05-02 23:07:06 +0000
205+++ dash/ScopeView.cpp 2013-05-03 17:13:27 +0000
206@@ -43,6 +43,8 @@
207 namespace dash
208 {
209 DECLARE_LOGGER(logger, "unity.dash.scopeview");
210+DECLARE_LOGGER(focus_logger, "unity.dash.scopeview.focus");
211+
212 namespace
213 {
214 const int CARD_VIEW_GAP_VERT = 24; // pixels
215@@ -158,6 +160,7 @@
216 , filter_expansion_pushed_(false)
217 , scope_connected_(scope ? scope->connected : false)
218 , search_on_next_connect_(false)
219+, current_focus_category_position_(-1)
220 {
221 SetupViews(show_filters);
222
223@@ -165,6 +168,8 @@
224 filters_expanded.changed.connect(sigc::mem_fun(this, &ScopeView::OnScopeFilterExpanded));
225 view_type.changed.connect(sigc::mem_fun(this, &ScopeView::OnViewTypeChanged));
226
227+ key_nav_focus_connection_ = nux::GetWindowCompositor().key_nav_focus_change.connect(sigc::mem_fun(this, &ScopeView::OnCompositorKeyNavFocusChanged));
228+
229 if (scope_)
230 {
231 categories_updated = scope_->categories.changed.connect([this](Categories::Ptr const& categories) { SetupCategories(categories); });
232@@ -312,17 +317,23 @@
233 categories->model.changed.connect(resync_categories);
234 resync_categories(categories->model());
235
236- scope_->category_order.changed.connect([this](std::vector<unsigned int> const& category_order) {
237- category_order_ = category_order;
238- OnCategoryOrderChanged();
239- });
240+ scope_->category_order.changed.connect(sigc::mem_fun(this, &ScopeView::OnCategoryOrderChanged));
241 }
242
243
244-void ScopeView::OnCategoryOrderChanged()
245+void ScopeView::OnCategoryOrderChanged(std::vector<unsigned int> const& order)
246 {
247 LOG_DEBUG(logger) << "Reordering categories for " << scope_->name();
248
249+ //////////////////////////////////////////////////
250+ // Find the current focus category position && result index
251+ // This is to keep the focus in the same place if categories are being added/removed/reordered
252+ PushResultFocus("reorder");
253+ key_nav_focus_connection_.block(true);
254+ //////////////////////////////////////////////////
255+
256+ category_order_ = order;
257+
258 for (auto iter = category_views_.begin(); iter != category_views_.end(); ++iter)
259 {
260 PlacesGroup::Ptr group = *iter;
261@@ -331,10 +342,6 @@
262
263 if (scope_)
264 {
265- Categories::Ptr category_model = scope_->categories();
266- if (!category_model)
267- return;
268-
269 // there should be ~10 categories, so this shouldn't be too big of a deal
270 for (unsigned i = 0; i < category_order_.size(); i++)
271 {
272@@ -346,6 +353,14 @@
273 scroll_layout_->AddView(category_views_[desired_category_index].GetPointer(), 0);
274 }
275 }
276+
277+ //////////////////////////////////////////////////
278+ // Update current focus category position && result index
279+ // This is to keep the focus in the same place if categories are being added/removed/reordered
280+ PopResultFocus("reorder");
281+ key_nav_focus_connection_.block(false);
282+ //////////////////////////////////////////////////
283+
284 QueueRelayout();
285 }
286
287@@ -418,6 +433,12 @@
288 << ", " << renderer_name
289 << ", " << boost::lexical_cast<int>(index) << ")";
290
291+ //////////////////////////////////////////////////
292+ // Find the current focus category position && result index
293+ // This is to keep the focus in the same place if categories are being added/removed/reordered
294+ PushResultFocus("add");
295+ key_nav_focus_connection_.block(true);
296+ //////////////////////////////////////////////////
297
298 PlacesGroup::Ptr group(CreatePlacesGroup(category));
299 AddChild(group.GetPointer());
300@@ -433,7 +454,9 @@
301 category_order_.push_back(index);
302 }
303 else
304+ {
305 view_index = find_view_index - category_order_.begin();
306+ }
307
308 category_views_.insert(category_views_.begin() + index, group);
309
310@@ -494,6 +517,13 @@
311 nux::MinorDimensionSize::MINOR_SIZE_FULL, 100.0f,
312 (nux::LayoutPosition)view_index);
313
314+ //////////////////////////////////////////////////
315+ // Update current focus category position && result index
316+ // This is to keep the focus in the same place if categories are being added/removed/reordered
317+ PopResultFocus("add");
318+ key_nav_focus_connection_.block(false);
319+ //////////////////////////////////////////////////
320+
321 if (reset_filter_models)
322 {
323 QueueReinitializeFilterCategoryModels(index);
324@@ -537,6 +567,13 @@
325 if (last_expanded_group_ == group)
326 last_expanded_group_.Release();
327
328+ //////////////////////////////////////////////////
329+ // Find the current focus category position && result index
330+ // This is to keep the focus in the same place if categories are being added/removed/reordered
331+ PushResultFocus("remove");
332+ key_nav_focus_connection_.block(true);
333+ //////////////////////////////////////////////////
334+
335 counts_.erase(group);
336 category_views_.erase(category_pos);
337
338@@ -547,8 +584,15 @@
339
340 scroll_layout_->RemoveChildObject(group.GetPointer());
341 RemoveChild(group.GetPointer());
342+
343+ //////////////////////////////////////////////////
344+ // Update current focus category position && result index
345+ // This is to keep the focus in the same place if categories are being added/removed/reordered
346+ PopResultFocus("remove");
347+ key_nav_focus_connection_.block(false);
348+ //////////////////////////////////////////////////
349+
350 QueueRelayout();
351-
352 if (reset_filter_models)
353 {
354 QueueReinitializeFilterCategoryModels(index);
355@@ -725,6 +769,8 @@
356
357 PlacesGroup::Ptr new_expanded_group;
358
359+ PushResultFocus("count check");
360+
361 for (auto iter = category_order_.begin(); iter != category_order_.end(); ++iter)
362 {
363 unsigned int category_index = *iter;
364@@ -757,6 +803,8 @@
365 }
366
367 last_expanded_group_ = new_expanded_group;
368+
369+ PopResultFocus("count check");
370 }
371
372 void ScopeView::HideResultsMessage()
373@@ -1102,5 +1150,91 @@
374 .add("no-results-active", no_results_active_);
375 }
376
377+void ScopeView::OnCompositorKeyNavFocusChanged(nux::Area* area, bool has_focus, nux::KeyNavDirection)
378+{
379+ if (!IsVisible())
380+ return;
381+
382+ LOG_DEBUG(focus_logger) << "Global focus changed to " << (area ? area->Type().name : "NULL");
383+
384+ if (area && has_focus)
385+ {
386+ // If we've change the focus to a places group child, then we need to update it's focus.
387+ bool found_group = false;
388+ while(area)
389+ {
390+ if (area->Type().IsDerivedFromType(PlacesGroup::StaticObjectType))
391+ {
392+ found_group = true;
393+ break;
394+ }
395+ // opimise to break out if we reach this level as it will never be a group.
396+ else if (area == this)
397+ break;
398+ area = area->GetParentObject();
399+ }
400+
401+ if (!found_group && current_focus_category_position_ != -1)
402+ {
403+ LOG_DEBUG(focus_logger) << "Resetting focus for position " << current_focus_category_position_;
404+ current_focus_category_position_ = -1;
405+ current_focus_variant_ = nullptr;
406+ }
407+ }
408+}
409+
410+void ScopeView::PushResultFocus(const char* reason)
411+{
412+ int current_category_position = 0;
413+ for (auto iter = category_order_.begin(); iter != category_order_.end(); ++iter)
414+ {
415+ unsigned category_index = *iter;
416+ if (category_views_.size() <= category_index)
417+ continue;
418+ PlacesGroup::Ptr group = category_views_[category_index];
419+ if (!group || !group->IsVisible())
420+ continue;
421+
422+ nux::Area* focus_area = nux::GetWindowCompositor().GetKeyFocusArea();
423+ while(focus_area)
424+ {
425+ if (focus_area == group.GetPointer())
426+ {
427+ current_focus_category_position_ = current_category_position;
428+ current_focus_variant_ = group->GetCurrentFocus();
429+ LOG_DEBUG(focus_logger) << "Saving focus for position " << current_focus_category_position_ << " due to '" << reason << "'";
430+ break;
431+ }
432+ // opimise to break out if we reach this level as it will never be a group.
433+ else if (focus_area == this)
434+ break;
435+ focus_area = focus_area->GetParentObject();
436+ }
437+ current_category_position++;
438+ }
439+}
440+
441+void ScopeView::PopResultFocus(const char* reason)
442+{
443+ int current_category_position = 0;
444+ for (auto iter = category_order_.begin(); iter != category_order_.end(); ++iter)
445+ {
446+ unsigned category_index = *iter;
447+ if (category_views_.size() <= category_index)
448+ continue;
449+ PlacesGroup::Ptr group = category_views_[category_index];
450+ if (!group || !group->IsVisible())
451+ continue;
452+
453+ if (current_category_position == current_focus_category_position_)
454+ {
455+ group->SetCurrentFocus(current_focus_variant_);
456+ LOG_DEBUG(focus_logger) << "Restoring focus for position " << current_focus_category_position_ << " due to '" << reason << "'";
457+ break;
458+ }
459+ current_category_position++;
460+ }
461+}
462+
463 }
464 }
465
466=== modified file 'dash/ScopeView.h'
467--- dash/ScopeView.h 2013-04-25 15:31:32 +0000
468+++ dash/ScopeView.h 2013-05-03 17:13:27 +0000
469@@ -111,7 +111,7 @@
470 void QueueReinitializeFilterCategoryModels(unsigned int index);
471 bool ReinitializeCategoryResultModels();
472 void ClearCategories();
473- void OnCategoryOrderChanged();
474+ void OnCategoryOrderChanged(std::vector<unsigned int> const& order);
475
476 void QueueCategoryCountsCheck();
477 void CheckCategoryCounts();
478@@ -119,6 +119,9 @@
479 void CheckNoResults(glib::HintsMap const& hints);
480 void HideResultsMessage();
481
482+ void PushResultFocus(const char* reason);
483+ void PopResultFocus(const char* reason);
484+
485 ResultView* GetResultViewForCategory(unsigned int category_index);
486
487 virtual PlacesGroup::Ptr CreatePlacesGroup(Category const& category);
488@@ -132,6 +135,8 @@
489 virtual std::string GetName() const;
490 virtual void AddProperties(GVariantBuilder* builder);
491
492+ void OnCompositorKeyNavFocusChanged(nux::Area*, bool, nux::KeyNavDirection);
493+
494 std::string get_search_string() const;
495
496 CategoryGroups category_views_;
497@@ -177,6 +182,10 @@
498 bool scope_connected_;
499 bool search_on_next_connect_;
500
501+ int current_focus_category_position_;
502+ glib::Variant current_focus_variant_;
503+ sigc::connection key_nav_focus_connection_;
504+
505 friend class TestScopeView;
506 };
507
508
509=== modified file 'debian/control'
510--- debian/control 2013-04-25 09:24:36 +0000
511+++ debian/control 2013-05-03 17:13:27 +0000
512@@ -28,7 +28,7 @@
513 libunity-misc-dev (>= 4.0.4),
514 libgrail-dev (>= 1.0.20),
515 libxcb-icccm4-dev,
516- libnux-4.0-dev (>= 4.0.1),
517+ libnux-4.0-dev (>= 4.0.2),
518 compiz-dev (>= 1:0.9.9~daily13.01.25bzr3586),
519 libcompizconfig0-dev (>= 1:0.9.9~daily12.12.05-0ubuntu2),
520 xsltproc,

Subscribers

People subscribed via source and target branches

to all changes: