Merge lp:~townsend/unity/fix-urgent-icon-wiggle into lp:unity

Proposed by Christopher Townsend
Status: Merged
Approved by: Marco Trevisan (Treviño)
Approved revision: no longer in the source branch.
Merged at revision: 3367
Proposed branch: lp:~townsend/unity/fix-urgent-icon-wiggle
Merge into: lp:unity
Diff against target: 287 lines (+204/-0)
3 files modified
launcher/Launcher.cpp (+123/-0)
launcher/Launcher.h (+10/-0)
tests/test_launcher.cpp (+71/-0)
To merge this branch: bzr merge lp:~townsend/unity/fix-urgent-icon-wiggle
Reviewer Review Type Date Requested Status
Marco Trevisan (Treviño) Approve
PS Jenkins bot (community) continuous-integration Approve
Brandon Schaefer (community) Approve
Review via email: mp+163800@code.launchpad.net

Commit message

Add functionality to wiggle urgent icons at certain time intervals when the Launcher is hidden.

Description of the change

= Issue =
Currently, when the Launcher is hidden and an urgent icon is displayed, the only notification a user sees that the initial pop out wiggle of the icon and then nothing else. IF the user misses this, then they may be unaware of an app that needs attention.

= Fix =
Add timers and logic to pop out and wiggle the icon at certain intervals as defined in bug #893196. Also, handle the wiggle icons once the Launcher is revealed.

= Test =
This is really a visual issue and no tests automated tests are identified to handle this.
A manual test would be to have an application set an icon urgent and watch for the pop out wiggle at the defined intervals. Also, reveal the Launcher to see the icon(s) wiggle one last time.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Looks nice :)

review: Approve
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Yay

review: Approve
Revision history for this message
Christopher Townsend (townsend) wrote :

Setting the overall status to Needs Review since I'd like to get Marco's input on tests since he said it may be possible for automated tests.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

68 +void Launcher::HandleUrgentIcon(AbstractLauncherIcon::Ptr const& icon, struct timespec const& current)

Since the function is called at every redraw, why not just returning at the beginning of it in case of: if (options()->hide_mode == LAUNCHER_HIDE_NEVER) ?

187 + bool _urgent_acked;
Please include _urgent_acked in the initialization list as well. Also, even if it seems to work properly even if there's more than on icon, wouldn't probably be better to have a set of icons to wiggle instead?

124 + for (it = _model->main_begin(); it != _model->main_end(); ++it)

Using a for-each loop here is nicer...

127 + if (icon->GetQuirk(AbstractLauncherIcon::Quirk::URGENT) &&

Traling space here

153 + sources_.Remove(URGENT_TIMEOUT);

This is not needed since it's automatically done when returning false on this callback function (see line 238 of SourceManager::Add on GLibSource.cpp).

157 + SetUrgentTimer(_urgent_wiggle_time);

Mh, why the current timer is not deleted in case you set up a new timer with different period?

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

Ah, on the bug description there's written:
«- When the Launcher is visible, application alerts should should be indicated by a short wiggle (in addition to the existing blue pips). This short wiggle should not repeat.»

I wonder this applies to the case where the launcher is always visible, and it that case it should wiggle only when the urgency is set and not after a minute or so, right?

Revision history for this message
Christopher Townsend (townsend) wrote :

Hi Marco,

> 68 +void Launcher::HandleUrgentIcon(AbstractLauncherIcon::Ptr const&
> icon, struct timespec const& current)
>
> Since the function is called at every redraw, why not just returning at the
> beginning of it in case of: if (options()->hide_mode == LAUNCHER_HIDE_NEVER) ?

Yeah, that's a good point. I may put this in the if statement before calling HandleUrgentIcon().

>
> 187 + bool _urgent_acked;
> Please include _urgent_acked in the initialization list as well. Also, even if
> it seems to work properly even if there's more than on icon, wouldn't probably
> be better to have a set of icons to wiggle instead?

Sure, I can put it in the initialization list. Not sure what you really mean by your question about a set of icons though.

>
>
> 124 + for (it = _model->main_begin(); it != _model->main_end(); ++it)
>
> Using a for-each loop here is nicer...

Yes indeed.

>
> 127 + if (icon->GetQuirk(AbstractLauncherIcon::Quirk::URGENT) &&
>
> Traling space here

OK

>
>
> 153 + sources_.Remove(URGENT_TIMEOUT);
>
> This is not needed since it's automatically done when returning false on this
> callback function (see line 238 of SourceManager::Add on GLibSource.cpp).

Ah, didn't catch that.

>
>
> 157 + SetUrgentTimer(_urgent_wiggle_time);
>
> Mh, why the current timer is not deleted in case you set up a new timer with
> different period?

I thought the current time is automatically deleted when setting up a new timer with the same name.

Revision history for this message
Christopher Townsend (townsend) wrote :

> Ah, on the bug description there's written:
> «- When the Launcher is visible, application alerts should should be indicated
> by a short wiggle (in addition to the existing blue pips). This short wiggle
> should not repeat.»
>
> I wonder this applies to the case where the launcher is always visible, and it
> that case it should wiggle only when the urgency is set and not after a minute
> or so, right?

That is the way I understand this too which is the current behavior.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

Nice...

229 + EXPECT_FALSE(launcher_->_urgent_timer_running);

244 + EXPECT_EQ(launcher_->_urgent_wiggle_time, 0);
245 + EXPECT_EQ(launcher_->_urgent_finished_time.tv_sec, 0);
246 + EXPECT_EQ(launcher_->_urgent_finished_time.tv_nsec, 0);

Probably better to use ASSERT_EQ there^.

Is it now possible to also test that the icons wiggle once the launcher is shown?

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

I think we're fine now, thanks! :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'launcher/Launcher.cpp'
--- launcher/Launcher.cpp 2013-05-15 04:59:15 +0000
+++ launcher/Launcher.cpp 2013-06-10 19:12:26 +0000
@@ -96,9 +96,14 @@
96const int SCROLL_AREA_HEIGHT = 24;96const int SCROLL_AREA_HEIGHT = 24;
97const int SCROLL_FPS = 30;97const int SCROLL_FPS = 30;
9898
99const int BASE_URGENT_WIGGLE_PERIOD = 60; // In seconds
100const int MAX_URGENT_WIGGLE_DELTA = 960; // In seconds
101const int DOUBLE_TIME = 2;
102
99const std::string START_DRAGICON_TIMEOUT = "start-dragicon-timeout";103const std::string START_DRAGICON_TIMEOUT = "start-dragicon-timeout";
100const std::string SCROLL_TIMEOUT = "scroll-timeout";104const std::string SCROLL_TIMEOUT = "scroll-timeout";
101const std::string ANIMATION_IDLE = "animation-idle";105const std::string ANIMATION_IDLE = "animation-idle";
106const std::string URGENT_TIMEOUT = "urgent-timeout";
102}107}
103108
104109
@@ -145,6 +150,10 @@
145 , _launcher_drag_delta_min(0)150 , _launcher_drag_delta_min(0)
146 , _enter_y(0)151 , _enter_y(0)
147 , _last_button_press(0)152 , _last_button_press(0)
153 , _urgent_wiggle_time(0)
154 , _urgent_acked(false)
155 , _urgent_timer_running(false)
156 , _urgent_ack_needed(false)
148 , _drag_out_delta_x(0.0f)157 , _drag_out_delta_x(0.0f)
149 , _drag_gesture_ongoing(false)158 , _drag_gesture_ongoing(false)
150 , _last_reveal_progress(0.0f)159 , _last_reveal_progress(0.0f)
@@ -194,6 +203,9 @@
194 _times[i].tv_nsec = 0;203 _times[i].tv_nsec = 0;
195 }204 }
196205
206 _urgent_finished_time.tv_sec = 0;
207 _urgent_finished_time.tv_nsec = 0;
208
197 ubus_.RegisterInterest(UBUS_OVERLAY_SHOWN, sigc::mem_fun(this, &Launcher::OnOverlayShown));209 ubus_.RegisterInterest(UBUS_OVERLAY_SHOWN, sigc::mem_fun(this, &Launcher::OnOverlayShown));
198 ubus_.RegisterInterest(UBUS_OVERLAY_HIDDEN, sigc::mem_fun(this, &Launcher::OnOverlayHidden));210 ubus_.RegisterInterest(UBUS_OVERLAY_HIDDEN, sigc::mem_fun(this, &Launcher::OnOverlayHidden));
199 ubus_.RegisterInterest(UBUS_LAUNCHER_LOCK_HIDE, sigc::mem_fun(this, &Launcher::OnLockHideChanged));211 ubus_.RegisterInterest(UBUS_LAUNCHER_LOCK_HIDE, sigc::mem_fun(this, &Launcher::OnLockHideChanged));
@@ -1133,6 +1145,11 @@
1133 {1145 {
1134 RenderArg arg;1146 RenderArg arg;
1135 AbstractLauncherIcon::Ptr const& icon = *it;1147 AbstractLauncherIcon::Ptr const& icon = *it;
1148
1149 if (icon->GetQuirk(AbstractLauncherIcon::Quirk::URGENT) && options()->hide_mode == LAUNCHER_HIDE_AUTOHIDE)
1150 {
1151 HandleUrgentIcon(icon, current);
1152 }
1136 FillRenderArg(icon, arg, center, parent_abs_geo, folding_threshold, folded_size, folded_spacing,1153 FillRenderArg(icon, arg, center, parent_abs_geo, folding_threshold, folded_size, folded_spacing,
1137 autohide_offset, folded_z_distance, animation_neg_rads, current);1154 autohide_offset, folded_z_distance, animation_neg_rads, current);
1138 arg.colorify = colorify;1155 arg.colorify = colorify;
@@ -1566,6 +1583,112 @@
1566 }1583 }
1567}1584}
15681585
1586void Launcher::SetUrgentTimer(int urgent_wiggle_period)
1587{
1588 sources_.AddTimeoutSeconds(urgent_wiggle_period, sigc::mem_fun(this, &Launcher::OnUrgentTimeout), URGENT_TIMEOUT);
1589}
1590
1591void Launcher::WiggleUrgentIcon(AbstractLauncherIcon::Ptr const& icon)
1592{
1593 icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, false);
1594 icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true);
1595
1596 clock_gettime(CLOCK_MONOTONIC, &_urgent_finished_time);
1597}
1598
1599void Launcher::HandleUrgentIcon(AbstractLauncherIcon::Ptr const& icon, struct timespec const& current)
1600{
1601 struct timespec urgent_time = icon->GetQuirkTime(AbstractLauncherIcon::Quirk::URGENT);
1602 DeltaTime urgent_delta = unity::TimeUtil::TimeDelta(&urgent_time, &_urgent_finished_time);
1603
1604 // If the Launcher is hidden, then add a timer to wiggle the urgent icons at
1605 // certain intervals (1m, 2m, 4m, 8m, 16m, & 32m).
1606 if (_hidden && !_urgent_timer_running && urgent_delta > 0)
1607 {
1608 _urgent_timer_running = true;
1609 _urgent_ack_needed = true;
1610 SetUrgentTimer(BASE_URGENT_WIGGLE_PERIOD);
1611 }
1612 // If the Launcher is hidden, the timer is running, an urgent icon is newer than the last time
1613 // icons were wiggled, and the timer did not just start, then reset the timer since a new
1614 // urgent icon just showed up.
1615 else if (_hidden && _urgent_timer_running && urgent_delta > 0 && _urgent_wiggle_time != 0)
1616 {
1617 _urgent_wiggle_time = 0;
1618 SetUrgentTimer(BASE_URGENT_WIGGLE_PERIOD);
1619 }
1620 // If the Launcher is no longer hidden, then after the Launcher is fully revealed, wiggle the
1621 // urgent icon and then stop the timer (if it's running).
1622 else if (!_hidden && _urgent_ack_needed)
1623 {
1624 if (_last_reveal_progress > 0)
1625 {
1626 _urgent_acked = false;
1627 }
1628 else
1629 {
1630 if (!_urgent_acked && IconUrgentProgress(icon, current) == 1.0f)
1631 {
1632 WiggleUrgentIcon(icon);
1633 }
1634 else if (IconUrgentProgress(icon, current) < 1.0f)
1635 {
1636 if (_urgent_timer_running)
1637 {
1638 sources_.Remove(URGENT_TIMEOUT);
1639 _urgent_timer_running = false;
1640 }
1641 _urgent_acked = true;
1642 _urgent_ack_needed = false;
1643 }
1644 }
1645 }
1646}
1647
1648bool Launcher::OnUrgentTimeout()
1649{
1650 bool foundUrgent = false,
1651 continue_urgent = true;
1652
1653 if (options()->urgent_animation() == URGENT_ANIMATION_WIGGLE && _hidden)
1654 {
1655 // Look for any icons that are still urgent and wiggle them
1656 for (auto icon : *_model)
1657 {
1658 if (icon->GetQuirk(AbstractLauncherIcon::Quirk::URGENT))
1659 {
1660 WiggleUrgentIcon(icon);
1661
1662 foundUrgent = true;
1663 }
1664 }
1665 }
1666 // Update the time for when the next wiggle will occur.
1667 if (_urgent_wiggle_time == 0)
1668 {
1669 _urgent_wiggle_time = BASE_URGENT_WIGGLE_PERIOD;
1670 }
1671 else
1672 {
1673 _urgent_wiggle_time = _urgent_wiggle_time * DOUBLE_TIME;
1674 }
1675
1676 // If no urgent icons were found or we have reached the time threshold,
1677 // then let's stop the timer. Otherwise, start a new timer with the
1678 // updated wiggle time.
1679 if (!foundUrgent || (_urgent_wiggle_time > MAX_URGENT_WIGGLE_DELTA))
1680 {
1681 continue_urgent = false;
1682 _urgent_timer_running = false;
1683 }
1684 else
1685 {
1686 SetUrgentTimer(_urgent_wiggle_time);
1687 }
1688
1689 return continue_urgent;
1690}
1691
1569void Launcher::SetIconSize(int tile_size, int icon_size)1692void Launcher::SetIconSize(int tile_size, int icon_size)
1570{1693{
1571 ui::IconRenderer::DestroyShortcutTextures();1694 ui::IconRenderer::DestroyShortcutTextures();
15721695
=== modified file 'launcher/Launcher.h'
--- launcher/Launcher.h 2013-03-06 15:44:44 +0000
+++ launcher/Launcher.h 2013-06-10 19:12:26 +0000
@@ -223,6 +223,11 @@
223 bool StartIconDragTimeout(int x, int y);223 bool StartIconDragTimeout(int x, int y);
224 bool OnScrollTimeout();224 bool OnScrollTimeout();
225225
226 void SetUrgentTimer(int urgent_wiggle_period);
227 void WiggleUrgentIcon(AbstractLauncherIcon::Ptr const& icon);
228 void HandleUrgentIcon(AbstractLauncherIcon::Ptr const& icon, struct timespec const& current);
229 bool OnUrgentTimeout();
230
226 void SetMousePosition(int x, int y);231 void SetMousePosition(int x, int y);
227 void SetIconUnderMouse(AbstractLauncherIcon::Ptr const& icon);232 void SetIconUnderMouse(AbstractLauncherIcon::Ptr const& icon);
228233
@@ -380,6 +385,10 @@
380 int _enter_y;385 int _enter_y;
381 int _last_button_press;386 int _last_button_press;
382 int _drag_icon_position;387 int _drag_icon_position;
388 int _urgent_wiggle_time;
389 bool _urgent_acked;
390 bool _urgent_timer_running;
391 bool _urgent_ack_needed;
383 float _drag_out_delta_x;392 float _drag_out_delta_x;
384 bool _drag_gesture_ongoing;393 bool _drag_gesture_ongoing;
385 float _last_reveal_progress;394 float _last_reveal_progress;
@@ -396,6 +405,7 @@
396 Atom _selection_atom;405 Atom _selection_atom;
397406
398 struct timespec _times[TIME_LAST];407 struct timespec _times[TIME_LAST];
408 struct timespec _urgent_finished_time;
399409
400 BaseTexturePtr launcher_sheen_;410 BaseTexturePtr launcher_sheen_;
401 BaseTexturePtr launcher_pressure_effect_;411 BaseTexturePtr launcher_pressure_effect_;
402412
=== modified file 'tests/test_launcher.cpp'
--- tests/test_launcher.cpp 2013-04-02 18:47:58 +0000
+++ tests/test_launcher.cpp 2013-06-10 19:12:26 +0000
@@ -108,6 +108,11 @@
108 using Launcher::IconStartingPulseValue;108 using Launcher::IconStartingPulseValue;
109 using Launcher::HandleBarrierEvent;109 using Launcher::HandleBarrierEvent;
110 using Launcher::SetHidden;110 using Launcher::SetHidden;
111 using Launcher::HandleUrgentIcon;
112 using Launcher::SetUrgentTimer;
113 using Launcher::_urgent_timer_running;
114 using Launcher::_urgent_finished_time;
115 using Launcher::_urgent_wiggle_time;
111116
112 void FakeProcessDndMove(int x, int y, std::list<std::string> uris)117 void FakeProcessDndMove(int x, int y, std::list<std::string> uris)
113 {118 {
@@ -618,6 +623,72 @@
618 launcher_->ProcessDndMove(0,0,{});623 launcher_->ProcessDndMove(0,0,{});
619}624}
620625
626TEST_F(TestLauncher, UrgentIconWiggleTimerStart)
627{
628 MockMockLauncherIcon::Ptr icon(new MockMockLauncherIcon);
629 timespec current;
630
631 launcher_->SetHidden(true);
632 icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true);
633
634 clock_gettime(CLOCK_MONOTONIC, &current);
635
636 ASSERT_FALSE(launcher_->_urgent_timer_running);
637
638 launcher_->HandleUrgentIcon(icon, current);
639
640 EXPECT_TRUE(launcher_->_urgent_timer_running);
641}
642
643TEST_F(TestLauncher, UrgentIconWiggleTimerTimeout)
644{
645 MockMockLauncherIcon::Ptr icon(new MockMockLauncherIcon);
646
647 model_->AddIcon(icon);
648 launcher_->SetHidden(true);
649 icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true);
650
651 ASSERT_EQ(launcher_->_urgent_wiggle_time, 0);
652 ASSERT_EQ(launcher_->_urgent_finished_time.tv_sec, 0);
653 ASSERT_EQ(launcher_->_urgent_finished_time.tv_nsec, 0);
654
655 launcher_->SetUrgentTimer(1);
656
657 // Make sure the Urgent Timer expires before checking
658 Utils::WaitForTimeout(2);
659
660 EXPECT_THAT(launcher_->_urgent_wiggle_time, Gt(0));
661 EXPECT_THAT(launcher_->_urgent_finished_time.tv_sec, Gt(0));
662 EXPECT_THAT(launcher_->_urgent_finished_time.tv_nsec, Gt(0));
663}
664
665TEST_F(TestLauncher, WiggleUrgentIconAfterLauncherIsRevealed)
666{
667 MockMockLauncherIcon::Ptr icon(new MockMockLauncherIcon);
668 timespec current;
669
670 model_->AddIcon(icon);
671 launcher_->SetHidden(true);
672 icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true);
673
674 // Wait a bit to simulate the icon's initial wiggle
675 Utils::WaitForTimeout(1);
676
677 clock_gettime(CLOCK_MONOTONIC, &current);
678 launcher_->HandleUrgentIcon(icon, current);
679
680 ASSERT_EQ(launcher_->_urgent_finished_time.tv_sec, 0);
681 ASSERT_EQ(launcher_->_urgent_finished_time.tv_nsec, 0);
682
683 launcher_->SetHidden(false);
684
685 clock_gettime(CLOCK_MONOTONIC, &current);
686 launcher_->HandleUrgentIcon(icon, current);
687
688 EXPECT_THAT(launcher_->_urgent_finished_time.tv_sec, Gt(0));
689 EXPECT_THAT(launcher_->_urgent_finished_time.tv_nsec, Gt(0));
690}
691
621}692}
622}693}
623694