Merge lp:~3v1n0/unity/quicklist-keynav-fixes into lp:unity

Proposed by Marco Trevisan (Treviño)
Status: Merged
Approved by: Marco Trevisan (Treviño)
Approved revision: no longer in the source branch.
Merged at revision: 2310
Proposed branch: lp:~3v1n0/unity/quicklist-keynav-fixes
Merge into: lp:unity
Diff against target: 1067 lines (+461/-148)
14 files modified
plugins/unityshell/src/AbstractLauncherIcon.h (+1/-1)
plugins/unityshell/src/LauncherIcon.cpp (+3/-3)
plugins/unityshell/src/LauncherIcon.h (+1/-1)
plugins/unityshell/src/MockLauncherIcon.h (+1/-1)
plugins/unityshell/src/QuicklistMenuItem.cpp (+9/-23)
plugins/unityshell/src/QuicklistMenuItem.h (+3/-4)
plugins/unityshell/src/QuicklistMenuItemSeparator.cpp (+6/-0)
plugins/unityshell/src/QuicklistMenuItemSeparator.h (+2/-0)
plugins/unityshell/src/QuicklistView.cpp (+165/-95)
plugins/unityshell/src/QuicklistView.h (+4/-2)
tests/autopilot/autopilot/emulators/unity/quicklist.py (+46/-7)
tests/autopilot/autopilot/keybindings.py (+11/-4)
tests/autopilot/autopilot/tests/test_quicklist.py (+205/-7)
tests/unit/TestQuicklistMenuitems.cpp (+4/-0)
To merge this branch: bzr merge lp:~3v1n0/unity/quicklist-keynav-fixes
Reviewer Review Type Date Requested Status
Thomi Richards (community) Approve
Review via email: mp+101623@code.launchpad.net

Commit message

QuicklistView: fixed the quicklist key navigation, to make it consistent with Gtk menus navigation.

The prelight of the items is now completely controlled by QuicklistView and never by QuicklistMenuItem, the children can define now when they are slectable.

Description of the change

Quicklist key navigation was broken in multiple aspect, fixed the attached bugs to make it consistent with Gtk menu navigation.

The prelight of the items is now completely controlled by QuicklistView and never by QuicklistMenuItem, the children can define now when they are slectable.

Included Autopilot tests for the QL key navigation, and updated unit-tests for QuicklistMenuItem.

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

Hi,

82 +bool
83 +QuicklistMenuItem::GetSelectable()

199 +bool
200 +QuicklistMenuItemSeparator::GetSelectable()

238 +void
239 +QuicklistView::SelectItem(int index)

263 bool
264 -QuicklistView::IsMenuItemSeperator(int index)
265 +QuicklistView::IsMenuItemSelectable(int index)

Coding style says these should be on the same name.

667 + def get_items(self, visible_only=True):
668 + """Returns the quicklist items"""
669 + if visible_only:
670 + return self.get_children_by_type(QuicklistMenuItem, visible=True)
671 + else:
672 + return self.get_children_by_type(QuicklistMenuItem)
673 +
674 @property
675 def items(self):
676 """Individual items in the quicklist."""
677 - return self.get_children_by_type(QuicklistMenuItem)
678 + return self.get_items()
679 +
680 + @property
681 + def selectable_items(self):
682 + """Items that can be selected in the quicklist"""
683 + return filter(lambda it: it.selectable == True, self.items)

This is a bit inconsistent - you have 'get_items' and 'selectable_items' - one a method, one a property. Please make them both the same (either methods or properties, I don't mind which). Also, please don't use 'filter' in autopilot. In most places, list comprehensions are clearer. However, in this case, you don't need either - just pass more keyword filters to get_children_by_type (like this):

@property
def selectable_items(self):
    return self.get_children_by_type(QuicklistMenuItem, visible=True, selectable=True)

704 + Mouse().move(target_x, target_y, animate=False)

Unless you have good reason to, please don't pass 'animate=false'
 - this is only intended to be used in exceptional circumstances.

856 + self.assertThat(self.quicklist, Not(Is(None)))
857 + self.assertThat(self.quicklist.selected_item, Not(Is(None)))

Please avoid negative assertions. Instead of saying "make sure foo is not None", say "Make sure foo is XYZ" - it makes your tests more specific and robust.

A few notes on docstrings:
 1. All your docstrings need a full-stop (period) at the end.
 2. Please try and make your docstrings PEP257 compliant: http://www.python.org/dev/peps/pep-0257/
 3. You can rewrite many of your docstrings to be shorter, and more forceful. For example, instead of writing this:

"""Tests that the quicklist Home key selects the first valid item when
no item is selected
"""

write this:

"""Home key MUST select the first selectable item in a quicklist."""

Note that this uses the word "must" which is better than "should", "might", or "will", and follows more closely the code of the tests themselves.

Other than that, these look good.

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

> Hi,
>
> 82 +bool
> 83 +QuicklistMenuItem::GetSelectable()
>
> Coding style says these should be on the same name.

Yes I know and I generally do that, but not when I would be inconsistent with the style currently used on the class I'm editing, so I'd prefer not to change it here not to include unneeded changes.
This is something that should be fixed globally on a different coding-style-fixes branch (for Q).

> 667 + def get_items(self, visible_only=True):
> 668 + """Returns the quicklist items"""
> 669 + if visible_only:
> 670 + return self.get_children_by_type(QuicklistMenuItem, visible=True)
> 671 + else:
> 672 + return self.get_children_by_type(QuicklistMenuItem)
> 673 +
> 674 @property
> 675 def items(self):
> 676 """Individual items in the quicklist."""
> 677 - return self.get_children_by_type(QuicklistMenuItem)
> 678 + return self.get_items()
> 679 +
> 680 + @property
> 681 + def selectable_items(self):
> 682 + """Items that can be selected in the quicklist"""
> 683 + return filter(lambda it: it.selectable == True, self.items)
>
>
> This is a bit inconsistent - you have 'get_items' and 'selectable_items' - one
> a method, one a property. Please make them both the same (either methods or
> properties, I don't mind which).

Well, I wanted to keep both for a reason: the get_items() also allow you to get the invisible items of a quicklist, while the property will give you only the "real" result.

> 704 + Mouse().move(target_x, target_y, animate=False)
>
>
> Unless you have good reason to, please don't pass 'animate=false'
> - this is only intended to be used in exceptional circumstances.

I used it to avoid that a quicklist item would have been selected when performing that, adding a possibility to get an invalid result.

> 856 + self.assertThat(self.quicklist, Not(Is(None)))
> 857 + self.assertThat(self.quicklist.selected_item, Not(Is(None)))
>
> Please avoid negative assertions. Instead of saying "make sure foo is not
> None", say "Make sure foo is XYZ" - it makes your tests more specific and
> robust.

I wouldn't have used, but it's just a copy and paste from your code of the same class that I used not to be wrong :D

Thanks for the review, I'll update the branch soon.

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

> > Hi,
> >
> > 82 +bool
> > 83 +QuicklistMenuItem::GetSelectable()
> >
> > Coding style says these should be on the same name.
>
> Yes I know and I generally do that, but not when I would be inconsistent with
> the style currently used on the class I'm editing, so I'd prefer not to change
> it here not to include unneeded changes.
> This is something that should be fixed globally on a different coding-style-
> fixes branch (for Q).

OK, fine with me.
> >
> > This is a bit inconsistent - you have 'get_items' and 'selectable_items' -
> one
> > a method, one a property. Please make them both the same (either methods or
> > properties, I don't mind which).
>
> Well, I wanted to keep both for a reason: the get_items() also allow you to
> get the invisible items of a quicklist, while the property will give you only
> the "real" result.
>

You can still have both - just either make them both properties, or both methods. Probably making them both properties makes more sense.

> > 704 + Mouse().move(target_x, target_y, animate=False)
> >
> >
> > Unless you have good reason to, please don't pass 'animate=false'
> > - this is only intended to be used in exceptional circumstances.
>
> I used it to avoid that a quicklist item would have been selected when
> performing that, adding a possibility to get an invalid result.
>

Fair enough.

> > 856 + self.assertThat(self.quicklist, Not(Is(None)))
> > 857 + self.assertThat(self.quicklist.selected_item, Not(Is(None)))
> >
> > Please avoid negative assertions. Instead of saying "make sure foo is not
> > None", say "Make sure foo is XYZ" - it makes your tests more specific and
> > robust.
>
> I wouldn't have used, but it's just a copy and paste from your code of the
> same class that I used not to be wrong :D
>

Yeah, my code's not perfect either. Negative assertions are something that I've come to realise are really bad very recently, so older code is likely to be wrong.

Cheers,

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

> This is a bit inconsistent - you have 'get_items' and 'selectable_items' - one
> a method, one a property. Please make them both the same (either methods or
> properties, I don't mind which).

Well, I did that because I also wanted to include a method to fetch the invisible items, but I'll remove it since no one uses it yet.

> Also, please don't use 'filter' in autopilot.

Yes you're right, I used it only because at the begginning selectable was a property that I defined only on the emulator, then I moved it to the introspection too, and I forgot to update the code.

I've updated the branch as well, I hope it's fine now ;)

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :
Download full text (3.2 KiB)

Hi,

There are still some issues with this I'm afraid:

672 + """Items that can be selected in the quicklist"""

Needs a full-stop at the end of the docstring.

727 + def mouse_click(self, button=1):
728 + logger.debug("Clicking on quicklist item %r", self)
729 + self.mouse_move_to()
730 + sleep(.25)
731 + assert(self.visible)
732 + self._mouse.click(press_duration=.1)
733 + sleep(.25)

Several issues here:
 1) The assert should be at the start of this method, not half way through.
 2) You don't need the sleep() statements, please remove them.
 3) You don't need to specify the click duration. Please just use "mouse.click()".

66 + for icon in self.launcher.model.get_launcher_icons():
867 + if (icon.tooltip_text != self.ql_app.name):
868 + self.ql_launcher.key_nav_next()
869 + else:
870 + self.keybinding("launcher/keynav/open-quicklist")
871 + self.addCleanup(self.keybinding, "launcher/keynav/close-quicklist")
872 + break

Instead of iterating over the model icons, please use the method on the launcher model, like this:

icon = self.launcher.model.get_icon_by_desktop_id(...)

If you use the desktop Id then we won't have issues in didfferent locales.

885 + self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
899 + self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
908 + self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
917 + self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
931 + self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
940 + self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
949 + self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
958 + self.assertTrue(item.selected)
959 + self.assertThat(self.quicklist.selected_item.id, Equals(item.id))
979 + self.assertTrue(mouse_item.selected)
984 + self.assertTrue(expected_item.selected)
985 + self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
995 + self.assertTrue(mouse_item.selected)
1000 + self.assertTrue(expected_item.selected)
1001 + self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
1010 + self.assertTrue(mouse_item.selected)
1017 + self.assertTrue(key_item.selected)
1018 + self.assertThat(self.quicklist.selected_item.id, Equals(key_item.id))
1023 + self.assertThat(self.quicklist.selected_item.id, Equals(key_item.id))
1027 + self.assertThat(self.quicklist.selected_item.id, Equals(key_item.id))
1032 + self.assertTrue(mouse_item.selected)
1033 + self.assertThat(self.quicklist.selected_item.id, Equals(mouse_item.id))

Please use the new Eventually() matcher for all of these. To import it:

from autopilot.matchers import Eventually

to use it, take this:

self.assertThat(self.quicklist.selected_item.id, Equals(mouse_item.id))

and turn it into this:

self.assertThat(self.quicklist.selected_item.id, Eventually(Equals(mouse_item.id)))

Eventually takes any mater, so you can also do things like this:

self.assertThat(self.quicklist.selected_item.id, Eventually(LessThan(mouse_item.id)))

Finally, please remove the sleep() statements. Using the Eventually(...) matcher removes the n...

Read more...

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

> Needs a full-stop at the end of the docstring.

Ouch!

> 727 + def mouse_click(self, button=1):
> Several issues here:
> 1) The assert should be at the start of this method, not half way through.
> 2) You don't need the sleep() statements, please remove them.
> 3) You don't need to specify the click duration. Please just use
> "mouse.click()".

Done

> 66 + for icon in self.launcher.model.get_launcher_icons():

> Instead of iterating over the model icons, please use the method on the
> launcher model, like this:
>
> icon = self.launcher.model.get_icon_by_desktop_id(...)

I can't... That was a keyboard selection... I must do that.

> If you use the desktop Id then we won't have issues in didfferent locales.

Using the bamf name we don't have these issue... Tested here, with Italian locale ;)

> 885 + self.assertThat(self.quicklist.selected_item.id,
> Equals(expected_item.id))

> Please use the new Eventually() matcher for all of these. To import it:
> Finally, please remove the sleep() statements. Using the Eventually(...)
> matcher removes the need for random sleep statements in your tests.

Done ;)

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) :
review: Approve
Revision history for this message
Unity Merger (unity-merger) wrote :

There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'plugins/unityshell/src/AbstractLauncherIcon.h'
--- plugins/unityshell/src/AbstractLauncherIcon.h 2012-03-30 15:24:34 +0000
+++ plugins/unityshell/src/AbstractLauncherIcon.h 2012-04-19 00:02:18 +0000
@@ -128,7 +128,7 @@
128128
129 virtual void SetSortPriority(int priority) = 0;129 virtual void SetSortPriority(int priority) = 0;
130130
131 virtual bool OpenQuicklist(bool default_to_first_item = false, int monitor = -1) = 0;131 virtual bool OpenQuicklist(bool select_first_item = false, int monitor = -1) = 0;
132132
133 virtual void SetCenter(nux::Point3 center, int monitor, nux::Geometry parent_geo) = 0;133 virtual void SetCenter(nux::Point3 center, int monitor, nux::Geometry parent_geo) = 0;
134134
135135
=== modified file 'plugins/unityshell/src/LauncherIcon.cpp'
--- plugins/unityshell/src/LauncherIcon.cpp 2012-04-10 00:39:14 +0000
+++ plugins/unityshell/src/LauncherIcon.cpp 2012-04-19 00:02:18 +0000
@@ -537,7 +537,7 @@
537 _tooltip->ShowWindow(false);537 _tooltip->ShowWindow(false);
538}538}
539539
540bool LauncherIcon::OpenQuicklist(bool default_to_first_item, int monitor)540bool LauncherIcon::OpenQuicklist(bool select_first_item, int monitor)
541{541{
542 std::list<DbusmenuMenuitem*> menus = Menus();542 std::list<DbusmenuMenuitem*> menus = Menus();
543543
@@ -583,8 +583,8 @@
583 _quicklist->AddMenuItem(ql_item);583 _quicklist->AddMenuItem(ql_item);
584 }584 }
585585
586 if (default_to_first_item)586 if (select_first_item)
587 _quicklist->DefaultToFirstItem();587 _quicklist->SelectFirstItem();
588588
589 if (monitor < 0)589 if (monitor < 0)
590 {590 {
591591
=== modified file 'plugins/unityshell/src/LauncherIcon.h'
--- plugins/unityshell/src/LauncherIcon.h 2012-04-03 22:18:36 +0000
+++ plugins/unityshell/src/LauncherIcon.h 2012-04-19 00:02:18 +0000
@@ -86,7 +86,7 @@
8686
87 void ShowTooltip();87 void ShowTooltip();
8888
89 bool OpenQuicklist(bool default_to_first_item = false, int monitor = -1);89 bool OpenQuicklist(bool select_first_item = false, int monitor = -1);
9090
91 void SetCenter(nux::Point3 center, int parent_monitor, nux::Geometry parent_geo);91 void SetCenter(nux::Point3 center, int parent_monitor, nux::Geometry parent_geo);
9292
9393
=== modified file 'plugins/unityshell/src/MockLauncherIcon.h'
--- plugins/unityshell/src/MockLauncherIcon.h 2012-03-30 15:24:34 +0000
+++ plugins/unityshell/src/MockLauncherIcon.h 2012-04-19 00:02:18 +0000
@@ -120,7 +120,7 @@
120120
121 void SetSortPriority(int priority) { sort_priority_ = priority; }121 void SetSortPriority(int priority) { sort_priority_ = priority; }
122122
123 bool OpenQuicklist(bool default_to_first_item = false, int monitor = -1)123 bool OpenQuicklist(bool select_first_item = false, int monitor = -1)
124 {124 {
125 return false;125 return false;
126 }126 }
127127
=== modified file 'plugins/unityshell/src/QuicklistMenuItem.cpp'
--- plugins/unityshell/src/QuicklistMenuItem.cpp 2012-03-14 06:24:18 +0000
+++ plugins/unityshell/src/QuicklistMenuItem.cpp 2012-04-19 00:02:18 +0000
@@ -89,10 +89,8 @@
89 this);89 this);
90 }90 }
9191
92 mouse_down.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseDown));
93 mouse_up.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseUp));92 mouse_up.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseUp));
94 mouse_click.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseClick));93 mouse_click.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseClick));
95 mouse_move.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseMove));
96 mouse_drag.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseDrag));94 mouse_drag.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseDrag));
97 mouse_enter.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseEnter));95 mouse_enter.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseEnter));
98 mouse_leave.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseLeave));96 mouse_leave.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseLeave));
@@ -215,6 +213,12 @@
215 DBUSMENU_MENUITEM_PROP_VISIBLE);213 DBUSMENU_MENUITEM_PROP_VISIBLE);
216}214}
217215
216bool
217QuicklistMenuItem::GetSelectable()
218{
219 return GetVisible() && GetEnabled();
220}
221
218void QuicklistMenuItem::ItemActivated()222void QuicklistMenuItem::ItemActivated()
219{223{
220 if (_debug)224 if (_debug)
@@ -333,11 +337,6 @@
333 //self->ItemActivated ();337 //self->ItemActivated ();
334}338}
335339
336void QuicklistMenuItem::RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags)
337{
338
339}
340
341void QuicklistMenuItem::RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags)340void QuicklistMenuItem::RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags)
342{341{
343 sigMouseReleased.emit(this, x, y);342 sigMouseReleased.emit(this, x, y);
@@ -352,11 +351,6 @@
352 sigMouseClick.emit(this, x, y);351 sigMouseClick.emit(this, x, y);
353}352}
354353
355void QuicklistMenuItem::RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags)
356{
357
358}
359
360void QuicklistMenuItem::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags)354void QuicklistMenuItem::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags)
361{355{
362 sigMouseDrag.emit(this, x, y);356 sigMouseDrag.emit(this, x, y);
@@ -364,13 +358,11 @@
364358
365void QuicklistMenuItem::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags)359void QuicklistMenuItem::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags)
366{360{
367 _prelight = true;
368 sigMouseEnter.emit(this);361 sigMouseEnter.emit(this);
369}362}
370363
371void QuicklistMenuItem::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags)364void QuicklistMenuItem::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags)
372{365{
373 _prelight = false;
374 sigMouseLeave.emit(this);366 sigMouseLeave.emit(this);
375}367}
376368
@@ -484,20 +476,14 @@
484476
485void QuicklistMenuItem::AddProperties(GVariantBuilder* builder)477void QuicklistMenuItem::AddProperties(GVariantBuilder* builder)
486{478{
487 nux::Geometry abs_geo = GetAbsoluteGeometry();
488
489 unity::variant::BuilderWrapper(builder)479 unity::variant::BuilderWrapper(builder)
490 .add("absolute_x", abs_geo.x)480 .add(GetAbsoluteGeometry())
491 .add("absolute_y", abs_geo.y)
492 .add("text", _text)481 .add("text", _text)
493 .add("x", GetBaseX())
494 .add("y", GetBaseY())
495 .add("width", GetBaseWidth())
496 .add("height", GetBaseHeight())
497 .add("enabled", GetEnabled())482 .add("enabled", GetEnabled())
498 .add("active", GetActive())483 .add("active", GetActive())
499 .add("visible", GetVisible())484 .add("visible", GetVisible())
500 .add("lit", _prelight);485 .add("selectable", GetSelectable())
486 .add("selected", _prelight);
501}487}
502488
503} //NAMESPACE489} //NAMESPACE
504490
=== modified file 'plugins/unityshell/src/QuicklistMenuItem.h'
--- plugins/unityshell/src/QuicklistMenuItem.h 2012-03-14 06:24:18 +0000
+++ plugins/unityshell/src/QuicklistMenuItem.h 2012-04-19 00:02:18 +0000
@@ -85,11 +85,13 @@
85 virtual bool GetEnabled();85 virtual bool GetEnabled();
86 virtual bool GetActive();86 virtual bool GetActive();
87 virtual bool GetVisible();87 virtual bool GetVisible();
88 virtual bool GetSelectable();
8889
90protected:
89 // Introspection91 // Introspection
90 std::string GetName() const;92 std::string GetName() const;
91 void AddProperties(GVariantBuilder* builder);93 void AddProperties(GVariantBuilder* builder);
92protected:94
93 static const int ITEM_INDENT_ABS = 16;95 static const int ITEM_INDENT_ABS = 16;
94 static const int ITEM_CORNER_RADIUS_ABS = 3;96 static const int ITEM_CORNER_RADIUS_ABS = 3;
95 static const int ITEM_MARGIN = 4;97 static const int ITEM_MARGIN = 4;
@@ -116,10 +118,8 @@
116118
117 void RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags);119 void RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags);
118 void RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags);120 void RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags);
119 void RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags);
120 void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags);121 void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags);
121 void RecvMouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags);122 void RecvMouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags);
122 void RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags);
123 void RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags);123 void RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags);
124124
125 sigc::signal<void, QuicklistMenuItem*> sigMouseEnter;125 sigc::signal<void, QuicklistMenuItem*> sigMouseEnter;
@@ -134,7 +134,6 @@
134 nux::Color _color; //!< Item rendering color factor.134 nux::Color _color; //!< Item rendering color factor.
135 bool _debug;135 bool _debug;
136136
137
138 bool _prelight; //!< True when the mouse is over the item.137 bool _prelight; //!< True when the mouse is over the item.
139138
140 void DrawText(nux::CairoGraphics* cairo, int width, int height, nux::Color const& color);139 void DrawText(nux::CairoGraphics* cairo, int width, int height, nux::Color const& color);
141140
=== modified file 'plugins/unityshell/src/QuicklistMenuItemSeparator.cpp'
--- plugins/unityshell/src/QuicklistMenuItemSeparator.cpp 2012-03-14 06:24:18 +0000
+++ plugins/unityshell/src/QuicklistMenuItemSeparator.cpp 2012-04-19 00:02:18 +0000
@@ -59,6 +59,12 @@
59{59{
60}60}
6161
62bool
63QuicklistMenuItemSeparator::GetSelectable()
64{
65 return false;
66}
67
62void68void
63QuicklistMenuItemSeparator::PreLayoutManagement()69QuicklistMenuItemSeparator::PreLayoutManagement()
64{70{
6571
=== modified file 'plugins/unityshell/src/QuicklistMenuItemSeparator.h'
--- plugins/unityshell/src/QuicklistMenuItemSeparator.h 2012-03-14 06:24:18 +0000
+++ plugins/unityshell/src/QuicklistMenuItemSeparator.h 2012-04-19 00:02:18 +0000
@@ -43,6 +43,8 @@
4343
44 ~QuicklistMenuItemSeparator();44 ~QuicklistMenuItemSeparator();
4545
46 virtual bool GetSelectable();
47
46protected:48protected:
4749
48 void PreLayoutManagement();50 void PreLayoutManagement();
4951
=== modified file 'plugins/unityshell/src/QuicklistView.cpp'
--- plugins/unityshell/src/QuicklistView.cpp 2012-03-14 06:24:18 +0000
+++ plugins/unityshell/src/QuicklistView.cpp 2012-04-19 00:02:18 +0000
@@ -69,7 +69,7 @@
69 , _bottom_padding_correction_single_item(-4)69 , _bottom_padding_correction_single_item(-4)
70 , _offset_correction(-1)70 , _offset_correction(-1)
71 , _cairo_text_has_changed(true)71 , _cairo_text_has_changed(true)
72 , _current_item_index(0)72 , _current_item_index(-1)
73{73{
74 SetGeometry(nux::Geometry(0, 0, 1, 1));74 SetGeometry(nux::Geometry(0, 0, 1, 1));
7575
@@ -138,13 +138,35 @@
138{138{
139}139}
140140
141void
142QuicklistView::SelectItem(int index)
143{
144 CancelItemsPrelightStatus();
145 int target_item = -1;
146
147 if (IsMenuItemSelectable(index))
148 {
149 QuicklistMenuItem* menu_item = GetNthItems(index);
150
151 if (menu_item)
152 {
153 target_item = index;
154 menu_item->_prelight = true;
155 }
156 }
157
158 if (_current_item_index != target_item)
159 {
160 _current_item_index = target_item;
161 selection_change.emit();
162 QueueDraw();
163 }
164}
165
141bool166bool
142QuicklistView::IsMenuItemSeperator(int index)167QuicklistView::IsMenuItemSelectable(int index)
143{168{
144 QuicklistMenuItem* menu_item = NULL;169 QuicklistMenuItem* menu_item = nullptr;
145 DbusmenuMenuitem* item = NULL;
146 const gchar* label = NULL;
147 bool result = false;
148170
149 if (index < 0)171 if (index < 0)
150 return false;172 return false;
@@ -153,17 +175,7 @@
153 if (!menu_item)175 if (!menu_item)
154 return false;176 return false;
155177
156 item = menu_item->_menuItem;178 return menu_item->GetSelectable();
157 if (!item)
158 return false;
159
160 label = dbusmenu_menuitem_property_get(item, DBUSMENU_MENUITEM_PROP_LABEL);
161 if (!label)
162 result = true;
163 else
164 result = false;
165
166 return result;
167}179}
168180
169void181void
@@ -175,56 +187,103 @@
175{187{
176 switch (key_sym)188 switch (key_sym)
177 {189 {
190 // home or page up (highlight the first menu-hitem)
191 case NUX_VK_PAGE_UP:
192 case NUX_VK_HOME:
193 {
194 int num_items = GetNumItems();
195 int target_index = -1;
196
197 do
198 {
199 ++target_index;
200 }
201 while (!IsMenuItemSelectable(target_index) && target_index < num_items);
202
203 if (target_index < num_items)
204 SelectItem(target_index);
205
206 break;
207 }
208 // end or page down (highlight the last menu-hitem)
209 case NUX_VK_PAGE_DOWN:
210 case NUX_VK_END:
211 {
212 int target_index = GetNumItems();
213
214 do
215 {
216 --target_index;
217 }
218 while (!IsMenuItemSelectable(target_index) && target_index >= 0);
219
220 if (target_index >= 0)
221 SelectItem(target_index);
222
223 break;
224 }
178 // up (highlight previous menu-item)225 // up (highlight previous menu-item)
179 case NUX_VK_UP:226 case NUX_VK_UP:
180 case NUX_KP_UP:227 case NUX_KP_UP:
181 // protect against edge-case of first item being a separator228 {
182 if (_current_item_index == 1 && IsMenuItemSeperator(0))229 int target_index = _current_item_index;
183 break;230 bool loop_back = false;
184231
185 if (_current_item_index > 0)232 if (target_index <= 0)
233 target_index = GetNumItems();
234
235 do
186 {236 {
187 GetNthItems(_current_item_index)->_prelight = false;237 --target_index;
188 _current_item_index--;238
189239 // If the first item is not selectable, we must loop from the last one
190 while (IsMenuItemSeperator(_current_item_index))240 if (!loop_back && target_index == 0 && !IsMenuItemSelectable(target_index))
191 _current_item_index--;241 {
192242 loop_back = true;
193 selection_change.emit();243 target_index = GetNumItems() - 1;
194244 }
195 GetNthItems(_current_item_index)->_prelight = true;
196 QueueDraw();
197 }245 }
246 while (!IsMenuItemSelectable(target_index) && target_index >= 0);
247
248 if (target_index >= 0)
249 SelectItem(target_index);
250
198 break;251 break;
252 }
199253
200 // down (highlight next menu-item)254 // down (highlight next menu-item)
201 case NUX_VK_DOWN:255 case NUX_VK_DOWN:
202 case NUX_KP_DOWN:256 case NUX_KP_DOWN:
203 // protect against edge-case of last item being a separator257 {
204 if (_current_item_index == (GetNumItems() - 1) && IsMenuItemSeperator(GetNumItems()))258 int target_index = _current_item_index;
205 break;259 int num_items = GetNumItems();
206260 bool loop_back = false;
207 if (_current_item_index < GetNumItems() - 1)261
262 if (target_index >= num_items - 1)
263 target_index = -1;
264
265 do
208 {266 {
209 GetNthItems(_current_item_index)->_prelight = false;267 ++target_index;
210 _current_item_index++;268
211269 // If the last item is not selectable, we must loop from the first one
212 while (IsMenuItemSeperator(_current_item_index))270 if (!loop_back && target_index == num_items - 1 && !IsMenuItemSelectable(target_index))
213 _current_item_index++;271 {
214272 loop_back = true;
215 selection_change.emit();273 target_index = 0;
216274 }
217 GetNthItems(_current_item_index)->_prelight = true;
218 QueueDraw();
219 }275 }
276 while (!IsMenuItemSelectable(target_index) && target_index < num_items);
277
278 if (target_index < num_items)
279 SelectItem(target_index);
280
220 break;281 break;
282 }
221283
222 // left (close quicklist, go back to laucher key-nav)284 // left (close quicklist, go back to laucher key-nav)
223 case NUX_VK_LEFT:285 case NUX_VK_LEFT:
224 case NUX_KP_LEFT:286 case NUX_KP_LEFT:
225 _current_item_index = 0;
226 selection_change.emit();
227 GetNthItems(_current_item_index)->_prelight = true;
228 Hide();287 Hide();
229 // inform Launcher we switch back to Launcher key-nav288 // inform Launcher we switch back to Launcher key-nav
230 ubus_server_send_message(ubus_server_get_default(),289 ubus_server_send_message(ubus_server_get_default(),
@@ -234,32 +293,23 @@
234293
235 // esc (close quicklist, exit key-nav)294 // esc (close quicklist, exit key-nav)
236 case NUX_VK_ESCAPE:295 case NUX_VK_ESCAPE:
237 _current_item_index = 0;
238 selection_change.emit();
239 GetNthItems(_current_item_index)->_prelight = true;
240 Hide();296 Hide();
241 // inform UnityScreen we leave key-nav completely297 // inform UnityScreen we leave key-nav completely
242 ubus_server_send_message(ubus_server_get_default(),298 ubus_server_send_message(ubus_server_get_default(),
243 UBUS_LAUNCHER_END_KEY_NAV,299 UBUS_LAUNCHER_END_KEY_NAV,
244 NULL);300 NULL);
245 selection_change.emit();
246 break;301 break;
247302
248 // <SPACE>, <RETURN> (activate selected menu-item)303 // <SPACE>, <RETURN> (activate selected menu-item)
249 case NUX_VK_SPACE:304 case NUX_VK_SPACE:
250 case NUX_VK_ENTER:305 case NUX_VK_ENTER:
251 case NUX_KP_ENTER:306 case NUX_KP_ENTER:
252 if (_current_item_index >= 0 && _current_item_index < GetNumItems() &&307 if (IsMenuItemSelectable(_current_item_index))
253 GetNthItems(_current_item_index)->GetEnabled())
254 {308 {
255
256 dbusmenu_menuitem_handle_event(GetNthItems(_current_item_index)->_menuItem,309 dbusmenu_menuitem_handle_event(GetNthItems(_current_item_index)->_menuItem,
257 "clicked",310 "clicked",
258 NULL,311 NULL,
259 0);312 0);
260 _current_item_index = 0;
261 selection_change.emit();
262 GetNthItems(_current_item_index)->_prelight = true;
263 Hide();313 Hide();
264 }314 }
265 break;315 break;
@@ -365,6 +415,12 @@
365 UnGrabKeyboard();415 UnGrabKeyboard();
366 //EnableInputWindow (false);416 //EnableInputWindow (false);
367 ShowWindow(false);417 ShowWindow(false);
418
419 if (_current_item_index != -1)
420 {
421 selection_change.emit();
422 _current_item_index = -1;
423 }
368 }424 }
369}425}
370426
@@ -618,54 +674,53 @@
618void QuicklistView::RecvItemMouseDrag(QuicklistMenuItem* item, int x, int y)674void QuicklistView::RecvItemMouseDrag(QuicklistMenuItem* item, int x, int y)
619{675{
620 nux::Geometry geo;676 nux::Geometry geo;
621 std::list<QuicklistMenuItem*>::iterator it;677
622 for (it = _item_list.begin(); it != _item_list.end(); it++)678 for (auto it : _item_list)
623 {679 {
624 if (!(*it)->GetVisible())680 int item_index = GetItemIndex(it);
681
682 if (!IsMenuItemSelectable(item_index))
625 continue;683 continue;
626684
627 geo = (*it)->GetGeometry();685 geo = it->GetGeometry();
628 geo.width = _item_layout->GetBaseWidth();686 geo.width = _item_layout->GetBaseWidth();
629687
630 if (geo.IsPointInside(x + item->GetBaseX(), y + item->GetBaseY()))688 if (geo.IsPointInside(x + item->GetBaseX(), y + item->GetBaseY()))
631 {689 {
632 (*it)->_prelight = true;690 SelectItem(item_index);
633 }
634 else
635 {
636 (*it)->_prelight = false;
637 }691 }
638 }692 }
639693
640 for (it = _default_item_list.begin(); it != _default_item_list.end(); it++)694 for (auto it : _default_item_list)
641 {695 {
642 if (!(*it)->GetVisible())696 int item_index = GetItemIndex(it);
697
698 if (!IsMenuItemSelectable(item_index))
643 continue;699 continue;
644700
645 geo = (*it)->GetGeometry();701 geo = it->GetGeometry();
646 geo.width = _default_item_layout->GetBaseWidth();702 geo.width = _default_item_layout->GetBaseWidth();
647703
648 if (geo.IsPointInside(x + item->GetBaseX(), y + item->GetBaseY()))704 if (geo.IsPointInside(x + item->GetBaseX(), y + item->GetBaseY()))
649 {705 {
650 (*it)->_prelight = true;706 SelectItem(item_index);
651 }
652 else
653 {
654 (*it)->_prelight = false;
655 }707 }
656 }708 }
657
658 NeedRedraw();
659}709}
660710
661void QuicklistView::RecvItemMouseEnter(QuicklistMenuItem* item)711void QuicklistView::RecvItemMouseEnter(QuicklistMenuItem* item)
662{712{
663 NeedRedraw();713 int item_index = GetItemIndex(item);
714
715 SelectItem(item_index);
664}716}
665717
666void QuicklistView::RecvItemMouseLeave(QuicklistMenuItem* item)718void QuicklistView::RecvItemMouseLeave(QuicklistMenuItem* item)
667{719{
668 NeedRedraw();720 int item_index = GetItemIndex(item);
721
722 if (item_index < 0 || item_index == _current_item_index)
723 SelectItem(-1);
669}724}
670725
671void QuicklistView::RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags)726void QuicklistView::RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags)
@@ -786,14 +841,37 @@
786 if (_item_list.size() > 0)841 if (_item_list.size() > 0)
787 i = _item_list.size() - 1;842 i = _item_list.size() - 1;
788 std::list<QuicklistMenuItem*>::iterator it;843 std::list<QuicklistMenuItem*>::iterator it;
789 for (it = _item_list.begin(); it != _item_list.end(); i++, it++)844 for (it = _default_item_list.begin(); it != _default_item_list.end(); i++, it++)
790 {845 {
791 if (i == index)846 if (i == index)
792 return *it;847 return *it;
793 }848 }
794 }849 }
795850
796 return 0;851 return nullptr;
852}
853
854int QuicklistView::GetItemIndex(QuicklistMenuItem* item)
855{
856 int index = -1;
857
858 for (auto it : _item_list)
859 {
860 ++index;
861
862 if (it == item)
863 return index;
864 }
865
866 for (auto it : _default_item_list)
867 {
868 ++index;
869
870 if (it == item)
871 return index;
872 }
873
874 return index;
797}875}
798876
799QuicklistMenuItemType QuicklistView::GetNthType(int index)877QuicklistMenuItemType QuicklistView::GetNthType(int index)
@@ -801,6 +879,7 @@
801 QuicklistMenuItem* item = GetNthItems(index);879 QuicklistMenuItem* item = GetNthItems(index);
802 if (item)880 if (item)
803 return item->GetItemType();881 return item->GetItemType();
882
804 return MENUITEM_TYPE_UNKNOWN;883 return MENUITEM_TYPE_UNKNOWN;
805}884}
806885
@@ -809,13 +888,9 @@
809 return _item_list;888 return _item_list;
810}889}
811890
812void QuicklistView::DefaultToFirstItem()891void QuicklistView::SelectFirstItem()
813{892{
814 if (GetNumItems() >= 1)893 SelectItem(0);
815 {
816 GetNthItems(0)->_prelight = true;
817 QueueDraw();
818 }
819}894}
820895
821void ql_tint_dot_hl(cairo_t* cr,896void ql_tint_dot_hl(cairo_t* cr,
@@ -1400,15 +1475,10 @@
14001475
1401void QuicklistView::AddProperties(GVariantBuilder* builder)1476void QuicklistView::AddProperties(GVariantBuilder* builder)
1402{1477{
1403 nux::Geometry abs_geo = GetAbsoluteGeometry();
1404
1405 variant::BuilderWrapper(builder)1478 variant::BuilderWrapper(builder)
1406 .add("x", abs_geo.x)1479 .add(GetAbsoluteGeometry())
1407 .add("y", abs_geo.y)
1408 .add("base_x", GetBaseX())1480 .add("base_x", GetBaseX())
1409 .add("base_y", GetBaseY())1481 .add("base_y", GetBaseY())
1410 .add("width", GetBaseWidth())
1411 .add("height", GetBaseHeight())
1412 .add("active", IsVisible());1482 .add("active", IsVisible());
1413}1483}
14141484
14151485
=== modified file 'plugins/unityshell/src/QuicklistView.h'
--- plugins/unityshell/src/QuicklistView.h 2012-03-14 06:24:18 +0000
+++ plugins/unityshell/src/QuicklistView.h 2012-04-19 00:02:18 +0000
@@ -62,8 +62,9 @@
62 int GetNumItems();62 int GetNumItems();
63 QuicklistMenuItem* GetNthItems(int index);63 QuicklistMenuItem* GetNthItems(int index);
64 QuicklistMenuItemType GetNthType(int index);64 QuicklistMenuItemType GetNthType(int index);
65 int GetItemIndex(QuicklistMenuItem* item);
65 std::list<QuicklistMenuItem*> GetChildren();66 std::list<QuicklistMenuItem*> GetChildren();
66 void DefaultToFirstItem();67 void SelectFirstItem();
6768
68 void TestMenuItems(DbusmenuMenuitem* root);69 void TestMenuItems(DbusmenuMenuitem* root);
6970
@@ -130,7 +131,8 @@
130 //! Check the mouse up event sent by an item. Detect the item where the mous is and emit the appropriate signal.131 //! Check the mouse up event sent by an item. Detect the item where the mous is and emit the appropriate signal.
131 void CheckAndEmitItemSignal(int x, int y);132 void CheckAndEmitItemSignal(int x, int y);
132133
133 bool IsMenuItemSeperator(int index);134 void SelectItem(int index);
135 bool IsMenuItemSelectable(int index);
134136
135 //nux::CairoGraphics* _cairo_graphics;137 //nux::CairoGraphics* _cairo_graphics;
136 int _anchorX;138 int _anchorX;
137139
=== modified file 'tests/autopilot/autopilot/emulators/unity/quicklist.py'
--- tests/autopilot/autopilot/emulators/unity/quicklist.py 2012-03-06 21:26:11 +0000
+++ tests/autopilot/autopilot/emulators/unity/quicklist.py 2012-04-19 00:02:18 +0000
@@ -23,7 +23,12 @@
23 @property23 @property
24 def items(self):24 def items(self):
25 """Individual items in the quicklist."""25 """Individual items in the quicklist."""
26 return self.get_children_by_type(QuicklistMenuItem)26 return self.get_children_by_type(QuicklistMenuItem, visible=True)
27
28 @property
29 def selectable_items(self):
30 """Items that can be selected in the quicklist."""
31 return self.get_children_by_type(QuicklistMenuItem, visible=True, selectable=True)
2732
28 def get_quicklist_item_by_text(self, text):33 def get_quicklist_item_by_text(self, text):
29 """Returns a QuicklistMenuItemLabel object with the given text, or None."""34 """Returns a QuicklistMenuItemLabel object with the given text, or None."""
@@ -40,17 +45,51 @@
40 if not isinstance(item, QuicklistMenuItem):45 if not isinstance(item, QuicklistMenuItem):
41 raise TypeError("Item must be a subclass of QuicklistMenuItem")46 raise TypeError("Item must be a subclass of QuicklistMenuItem")
4247
43 logger.debug("Clicking on quicklist item %r", item)48 item.mouse_click()
44 mouse = Mouse()49
45 mouse.move(self.x + item.x + (item.width / 2),50 def move_mouse_to_right(self):
46 self.y + item.y + (item.height / 2))51 """Moves the mouse outside the quicklist"""
47 sleep(0.25)52 logger.debug("Moving mouse outside the quicklist %r", self)
48 mouse.click()53 target_x = self.x + self.width + 10
54 target_y = self.y + self.height / 2
55 Mouse().move(target_x, target_y, animate=False)
56
57 @property
58 def selected_item(self):
59 items = self.get_children_by_type(QuicklistMenuItem, selected=True)
60 assert(len(items) <= 1)
61 return items[0] if items else None
62
63 @property
64 def geometry(self):
65 """Returns a tuple of (x,y,w,h) for the quicklist."""
66 return (self.x, self.y, self.width, self.height)
4967
5068
51class QuicklistMenuItem(UnityIntrospectionObject):69class QuicklistMenuItem(UnityIntrospectionObject):
52 """Represents a single item in a quicklist."""70 """Represents a single item in a quicklist."""
5371
72 def __init__(self, *args, **kwargs):
73 super(QuicklistMenuItem, self).__init__(*args, **kwargs)
74 self._mouse = Mouse()
75
76 @property
77 def geometry(self):
78 """Returns a tuple of (x,y,w,h) for the quicklist item."""
79 return (self.x, self.y, self.width, self.height)
80
81 def mouse_move_to(self):
82 assert(self.visible)
83 logger.debug("Moving mouse over quicklist item %r", self)
84 target_x = self.x + self.width / 2
85 target_y = self.y + self.height / 2
86 self._mouse.move(target_x, target_y, rate=20, time_between_events=0.005)
87
88 def mouse_click(self, button=1):
89 logger.debug("Clicking on quicklist item %r", self)
90 self.mouse_move_to()
91 self._mouse.click()
92
5493
55class QuicklistMenuItemLabel(QuicklistMenuItem):94class QuicklistMenuItemLabel(QuicklistMenuItem):
56 """Represents a text label inside a quicklist."""95 """Represents a text label inside a quicklist."""
5796
=== modified file 'tests/autopilot/autopilot/keybindings.py'
--- tests/autopilot/autopilot/keybindings.py 2012-04-06 01:28:24 +0000
+++ tests/autopilot/autopilot/keybindings.py 2012-04-19 00:02:18 +0000
@@ -56,7 +56,14 @@
56 "launcher/switcher/prev": "Shift+Tab",56 "launcher/switcher/prev": "Shift+Tab",
57 "launcher/switcher/down": "Down",57 "launcher/switcher/down": "Down",
58 "launcher/switcher/up": "Up",58 "launcher/switcher/up": "Up",
59 # Panel59 # Quicklist:
60 "quicklist/keynav/first": "Home",
61 "quicklist/keynav/last": "End",
62 "quicklist/keynav/next": "Down",
63 "quicklist/keynav/prev": "Up",
64 "quicklist/keynav/activate": "Enter",
65 "quicklist/keynav/exit": "Escape",
66 # Panel:
60 "panel/show_menus": "Alt",67 "panel/show_menus": "Alt",
61 "panel/open_first_menu": ('unityshell', 'panel_first_menu'),68 "panel/open_first_menu": ('unityshell', 'panel_first_menu'),
62 "panel/next_indicator": "Right",69 "panel/next_indicator": "Right",
@@ -65,12 +72,12 @@
65 "dash/reveal": "Super",72 "dash/reveal": "Super",
66 "dash/lens/next": "Ctrl+Tab",73 "dash/lens/next": "Ctrl+Tab",
67 "dash/lens/prev": "Ctrl+Shift+Tab",74 "dash/lens/prev": "Ctrl+Shift+Tab",
68 # Lenses75 # Lenses:
69 "lens_reveal/command": ("unityshell", "execute_command"),76 "lens_reveal/command": ("unityshell", "execute_command"),
70 "lens_reveal/apps": "Super+a",77 "lens_reveal/apps": "Super+a",
71 "lens_reveal/files": "Super+f",78 "lens_reveal/files": "Super+f",
72 "lens_reveal/music": "Super+m",79 "lens_reveal/music": "Super+m",
73 # Hud80 # Hud:
74 "hud/reveal": ("unityshell", "show_hud"),81 "hud/reveal": ("unityshell", "show_hud"),
75 # Switcher:82 # Switcher:
76 "switcher/reveal_normal": ("unityshell", "alt_tab_forward"),83 "switcher/reveal_normal": ("unityshell", "alt_tab_forward"),
@@ -96,7 +103,7 @@
96 "workspace/move_right": ("wall", "right_key"),103 "workspace/move_right": ("wall", "right_key"),
97 "workspace/move_up": ("wall", "up_key"),104 "workspace/move_up": ("wall", "up_key"),
98 "workspace/move_down": ("wall", "down_key"),105 "workspace/move_down": ("wall", "down_key"),
99 # Window management106 # Window management:
100 "window/show_desktop" : ("core", "show_desktop_key"),107 "window/show_desktop" : ("core", "show_desktop_key"),
101 "window/minimize": ("core", "minimize_window_key"),108 "window/minimize": ("core", "minimize_window_key"),
102 "window/maximize": ("core", "maximize_window_key"),109 "window/maximize": ("core", "maximize_window_key"),
103110
=== modified file 'tests/autopilot/autopilot/tests/test_quicklist.py'
--- tests/autopilot/autopilot/tests/test_quicklist.py 2012-04-02 20:18:58 +0000
+++ tests/autopilot/autopilot/tests/test_quicklist.py 2012-04-19 00:02:18 +0000
@@ -1,16 +1,19 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2# Copyright 2012 Canonical2# Copyright 2012 Canonical
3# Author: Thomi Richards3# Author: Thomi Richards,
4# Marco Trevisan (Treviño)
4#5#
5# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
7# by the Free Software Foundation.8# by the Free Software Foundation.
89
9import os.path10import os.path
10from testtools.matchers import Not, Is, Contains11from testtools.matchers import Contains, Equals, NotEquals
11from xdg.DesktopEntry import DesktopEntry12from xdg.DesktopEntry import DesktopEntry
13from time import sleep
1214
13from autopilot.emulators.unity.quicklist import QuicklistMenuItemLabel15from autopilot.emulators.unity.quicklist import QuicklistMenuItemLabel
16from autopilot.matchers import Eventually
14from autopilot.tests import AutopilotTestCase17from autopilot.tests import AutopilotTestCase
1518
1619
@@ -26,22 +29,23 @@
26 self.start_app(self.app_name)29 self.start_app(self.app_name)
2730
28 # load the desktop file from disk:31 # load the desktop file from disk:
29 desktop_file = os.path.join('/usr/share/applications',32 desktop_id = self.KNOWN_APPS[self.app_name]['desktop-file']
30 self.KNOWN_APPS[self.app_name]['desktop-file']33 desktop_file = os.path.join('/usr/share/applications', desktop_id)
31 )
32 de = DesktopEntry(desktop_file)34 de = DesktopEntry(desktop_file)
33 # get the launcher icon from the launcher:35 # get the launcher icon from the launcher:
34 launcher_icon = self.launcher.model.get_icon_by_tooltip_text(de.getName())36 launcher_icon = self.launcher.model.get_icon_by_desktop_id(desktop_id)
35 self.assertThat(launcher_icon, Not(Is(None)))37 self.assertThat(launcher_icon, NotEquals(None))
3638
37 # open the icon quicklist, and get all the text labels:39 # open the icon quicklist, and get all the text labels:
38 launcher = self.launcher.get_launcher_for_monitor(0)40 launcher = self.launcher.get_launcher_for_monitor(0)
39 launcher.click_launcher_icon(launcher_icon, button=3)41 launcher.click_launcher_icon(launcher_icon, button=3)
42 self.addCleanup(self.keyboard.press_and_release, "Escape")
40 ql = launcher_icon.get_quicklist()43 ql = launcher_icon.get_quicklist()
41 ql_item_texts = [i.text for i in ql.items if type(i) is QuicklistMenuItemLabel]44 ql_item_texts = [i.text for i in ql.items if type(i) is QuicklistMenuItemLabel]
4245
43 # iterate over all the actions from the desktop file, make sure they're46 # iterate over all the actions from the desktop file, make sure they're
44 # present in the quicklist texts.47 # present in the quicklist texts.
48 # FIXME, this doesn't work using a locale other than English.
45 actions = de.getActions()49 actions = de.getActions()
46 for action in actions:50 for action in actions:
47 key = 'Desktop Action ' + action51 key = 'Desktop Action ' + action
@@ -50,3 +54,197 @@
50 self.assertThat(ql_item_texts, Contains(name))54 self.assertThat(ql_item_texts, Contains(name))
5155
5256
57class QuicklistKeyNavigationTests(AutopilotTestCase):
58 """Tests for the quicklist key navigation."""
59
60 def setUp(self):
61 super(QuicklistKeyNavigationTests, self).setUp()
62
63 self.ql_app = self.start_app("Text Editor")
64
65 self.ql_launcher_icon = self.launcher.model.get_icon_by_desktop_id(self.ql_app.desktop_file)
66 self.assertThat(self.ql_launcher_icon, NotEquals(None))
67
68 self.ql_launcher = self.launcher.get_launcher_for_monitor(0)
69
70 def open_quicklist_with_mouse(self):
71 """Opens a quicklist with the mouse."""
72 self.ql_launcher.click_launcher_icon(self.ql_launcher_icon, button=3)
73 self.addCleanup(self.keyboard.press_and_release, "Escape")
74 self.quicklist = self.ql_launcher_icon.get_quicklist()
75 self.assertThat(self.quicklist, NotEquals(None))
76 self.quicklist.move_mouse_to_right()
77 self.assertThat(self.quicklist.selected_item, Equals(None))
78
79 def open_quicklist_with_keyboard(self):
80 """Opens a quicklist using the keyboard."""
81 self.screen_geo.move_mouse_to_monitor(0)
82 self.ql_launcher.key_nav_start()
83 self.addCleanup(self.ql_launcher.key_nav_cancel)
84
85 for icon in self.launcher.model.get_launcher_icons():
86 if icon.tooltip_text != self.ql_app.name:
87 self.ql_launcher.key_nav_next()
88 else:
89 self.keybinding("launcher/keynav/open-quicklist")
90 self.addCleanup(self.keybinding, "launcher/keynav/close-quicklist")
91 break
92
93 self.quicklist = self.ql_launcher_icon.get_quicklist()
94 self.assertThat(self.quicklist, NotEquals(None))
95 self.assertThat(self.quicklist.selected_item, NotEquals(None))
96
97 def test_keynav_selects_first_item_when_unselected(self):
98 """Home key MUST select the first selectable item in a quicklist."""
99 self.open_quicklist_with_mouse()
100
101 self.keybinding("quicklist/keynav/first")
102
103 expected_item = self.quicklist.selectable_items[0]
104 self.assertThat(expected_item.selected, Eventually(Equals(True)))
105 self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
106
107 def test_keynav_selects_first_item_when_selected(self):
108 """Home key MUST select the first selectable item in a quicklist when
109 another item is selected.
110 """
111 self.open_quicklist_with_mouse()
112 mouse_item = self.quicklist.selectable_items[-1]
113 mouse_item.mouse_move_to()
114 self.assertThat(mouse_item.selected, Eventually(Equals(True)))
115
116 self.keybinding("quicklist/keynav/first")
117
118 expected_item = self.quicklist.selectable_items[0]
119 self.assertThat(expected_item.selected, Eventually(Equals(True)))
120 self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
121
122 def test_keynav_next_selects_first_item_when_unselected(self):
123 """Down key MUST select the first valid item when nothing is selected."""
124 self.open_quicklist_with_mouse()
125
126 self.keybinding("quicklist/keynav/next")
127
128 expected_item = self.quicklist.selectable_items[0]
129 self.assertThat(expected_item.selected, Eventually(Equals(True)))
130 self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
131
132 def test_keynav_selects_last_item_when_unselected(self):
133 """End key MUST select the last selectable item in a quicklist."""
134 self.open_quicklist_with_mouse()
135
136 self.keybinding("quicklist/keynav/last")
137
138 expected_item = self.quicklist.selectable_items[-1]
139 self.assertThat(expected_item.selected, Eventually(Equals(True)))
140 self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
141
142 def test_keynav_selects_last_item_when_selected(self):
143 """End key MUST select the last selectable item in a quicklist when
144 another item is selected.
145 """
146 self.open_quicklist_with_mouse()
147 mouse_item = self.quicklist.selectable_items[0]
148 mouse_item.mouse_move_to()
149 self.assertThat(mouse_item.selected, Eventually(Equals(True)))
150
151 self.keybinding("quicklist/keynav/last")
152
153 expected_item = self.quicklist.selectable_items[-1]
154 self.assertThat(expected_item.selected, Eventually(Equals(True)))
155 self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
156
157 def test_keynav_prev_selects_last_item_when_unselected(self):
158 """Up key MUST select the last valid item when nothing is selected."""
159 self.open_quicklist_with_mouse()
160
161 self.keybinding("quicklist/keynav/prev")
162
163 expected_item = self.quicklist.selectable_items[-1]
164 self.assertThat(expected_item.selected, Eventually(Equals(True)))
165 self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
166
167 def test_launcher_keynav_selects_first_item(self):
168 """The first selectable item of the quicklist must be selected when
169 opening the quicklist using the launcher key navigation.
170 """
171 self.open_quicklist_with_keyboard()
172
173 expected_item = self.quicklist.selectable_items[0]
174 self.assertThat(expected_item.selected, Eventually(Equals(True)))
175 self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
176
177 def test_keynav_next_selection_works(self):
178 """Down key MUST select the next valid item."""
179 self.open_quicklist_with_mouse()
180
181 for item in self.quicklist.selectable_items:
182 self.keybinding("quicklist/keynav/next")
183 self.assertThat(item.selected, Eventually(Equals(True)))
184 self.assertThat(self.quicklist.selected_item.id, Equals(item.id))
185
186 def test_keynav_prev_selection_works(self):
187 """Up key MUST select the previous valid item."""
188 self.open_quicklist_with_mouse()
189
190 for item in reversed(self.quicklist.selectable_items):
191 self.keybinding("quicklist/keynav/prev")
192 self.assertThat(item.selected, Eventually(Equals(True)))
193 self.assertThat(self.quicklist.selected_item.id, Equals(item.id))
194
195 def test_keynav_prev_is_cyclic(self):
196 """Up key MUST select the last item, when the first one is selected."""
197 self.open_quicklist_with_mouse()
198
199 mouse_item = self.quicklist.selectable_items[0]
200 mouse_item.mouse_move_to()
201 self.assertThat(mouse_item.selected, Eventually(Equals(True)))
202
203 self.keybinding("quicklist/keynav/prev")
204 expected_item = self.quicklist.selectable_items[-1]
205 self.assertThat(expected_item.selected, Eventually(Equals(True)))
206 self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
207
208 def test_keynav_next_is_cyclic(self):
209 """Down key MUST select the first item, when the last one is selected."""
210 self.open_quicklist_with_mouse()
211
212 mouse_item = self.quicklist.selectable_items[-1]
213 mouse_item.mouse_move_to()
214 self.assertThat(mouse_item.selected, Eventually(Equals(True)))
215
216 self.keybinding("quicklist/keynav/next")
217 expected_item = self.quicklist.selectable_items[0]
218 self.assertThat(expected_item.selected, Eventually(Equals(True)))
219 self.assertThat(self.quicklist.selected_item.id, Equals(expected_item.id))
220
221 def test_keynav_mouse_interaction(self):
222 """Tests that the interaction between key-navigation and mouse works as
223 expected. See bug #911561.
224 """
225 self.open_quicklist_with_mouse()
226 mouse_item = self.quicklist.selectable_items[-1]
227 mouse_item.mouse_move_to()
228 self.assertThat(mouse_item.selected, Eventually(Equals(True)))
229
230 self.keybinding("quicklist/keynav/prev")
231 sleep(.1)
232 self.keybinding("quicklist/keynav/prev")
233
234 key_item = self.quicklist.selectable_items[-3]
235 self.assertThat(key_item.selected, Eventually(Equals(True)))
236 self.assertThat(self.quicklist.selected_item.id, Equals(key_item.id))
237
238 # Moving the mouse horizontally doesn't change the selection
239 self.mouse.move(mouse_item.x + mouse_item.width - 10, mouse_item.y + mouse_item.height / 2)
240 self.assertThat(self.quicklist.selected_item.id, Equals(key_item.id))
241
242 # Moving the mouse outside doesn't change the selection
243 self.mouse.move(mouse_item.x + mouse_item.width + 50, mouse_item.y + mouse_item.height / 2)
244 self.assertThat(self.quicklist.selected_item.id, Equals(key_item.id))
245
246 # Moving the mouse to another entry, changes the selection
247 mouse_item = self.quicklist.selectable_items[-2]
248 mouse_item.mouse_move_to()
249 self.assertThat(mouse_item.selected, Eventually(Equals(True)))
250 self.assertThat(self.quicklist.selected_item.id, Equals(mouse_item.id))
53251
=== modified file 'tests/unit/TestQuicklistMenuitems.cpp'
--- tests/unit/TestQuicklistMenuitems.cpp 2012-03-14 06:24:18 +0000
+++ tests/unit/TestQuicklistMenuitems.cpp 2012-04-19 00:02:18 +0000
@@ -94,6 +94,7 @@
94 g_assert_cmpstr(qlCheckmarkItem->GetLabel(), == , "Unchecked");94 g_assert_cmpstr(qlCheckmarkItem->GetLabel(), == , "Unchecked");
95 g_assert_cmpint(qlCheckmarkItem->GetEnabled(), == , false);95 g_assert_cmpint(qlCheckmarkItem->GetEnabled(), == , false);
96 g_assert_cmpint(qlCheckmarkItem->GetActive(), == , false);96 g_assert_cmpint(qlCheckmarkItem->GetActive(), == , false);
97 g_assert_cmpint(qlCheckmarkItem->GetSelectable(), == , false);
97 g_assert_cmpint(qlCheckmarkItem->IsMarkupEnabled(), == , false);98 g_assert_cmpint(qlCheckmarkItem->IsMarkupEnabled(), == , false);
9899
99 //qlCheckmarkItem->sigChanged.connect (sigc::mem_fun (pointerToCallerClass,100 //qlCheckmarkItem->sigChanged.connect (sigc::mem_fun (pointerToCallerClass,
@@ -129,6 +130,7 @@
129130
130 g_assert_cmpstr(qlLabelItem->GetLabel(), == , "A Label");131 g_assert_cmpstr(qlLabelItem->GetLabel(), == , "A Label");
131 g_assert_cmpint(qlLabelItem->GetEnabled(), == , true);132 g_assert_cmpint(qlLabelItem->GetEnabled(), == , true);
133 g_assert_cmpint(qlLabelItem->GetSelectable(), == , true);
132 g_assert_cmpint(qlLabelItem->IsMarkupEnabled(), == , true);134 g_assert_cmpint(qlLabelItem->IsMarkupEnabled(), == , true);
133135
134 //qlLabelItem->sigChanged.connect (sigc::mem_fun (pointerToCallerClass,136 //qlLabelItem->sigChanged.connect (sigc::mem_fun (pointerToCallerClass,
@@ -169,6 +171,7 @@
169 g_assert_cmpstr(qlRadioItem->GetLabel(), == , "Radio Active");171 g_assert_cmpstr(qlRadioItem->GetLabel(), == , "Radio Active");
170 g_assert_cmpint(qlRadioItem->GetEnabled(), == , true);172 g_assert_cmpint(qlRadioItem->GetEnabled(), == , true);
171 g_assert_cmpint(qlRadioItem->GetActive(), == , true);173 g_assert_cmpint(qlRadioItem->GetActive(), == , true);
174 g_assert_cmpint(qlRadioItem->GetSelectable(), == , true);
172 g_assert_cmpint(qlRadioItem->IsMarkupEnabled(), == , true);175 g_assert_cmpint(qlRadioItem->IsMarkupEnabled(), == , true);
173176
174 //qlRadioItem->sigChanged.connect (sigc::mem_fun (pointerToCallerClass,177 //qlRadioItem->sigChanged.connect (sigc::mem_fun (pointerToCallerClass,
@@ -198,6 +201,7 @@
198 qlSeparatorItem = new QuicklistMenuItemSeparator(item, true);201 qlSeparatorItem = new QuicklistMenuItemSeparator(item, true);
199202
200 g_assert_cmpint(qlSeparatorItem->GetEnabled(), == , true);203 g_assert_cmpint(qlSeparatorItem->GetEnabled(), == , true);
204 g_assert_cmpint(qlSeparatorItem->GetSelectable(), == , false);
201205
202 qlSeparatorItem->Dispose();206 qlSeparatorItem->Dispose();
203 g_object_unref(item);207 g_object_unref(item);