Merge lp:~3v1n0/unity/globalmenu-discovery-new-apps 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: 1780
Proposed branch: lp:~3v1n0/unity/globalmenu-discovery-new-apps
Merge into: lp:unity
Diff against target: 1779 lines (+878/-289)
16 files modified
manual-tests/Panel.txt (+15/-0)
plugins/unityshell/src/Animator.cpp (+91/-82)
plugins/unityshell/src/Animator.h (+21/-23)
plugins/unityshell/src/LauncherController.cpp (+4/-1)
plugins/unityshell/src/PanelController.cpp (+33/-1)
plugins/unityshell/src/PanelController.h (+2/-0)
plugins/unityshell/src/PanelMenuView.cpp (+282/-144)
plugins/unityshell/src/PanelMenuView.h (+31/-8)
plugins/unityshell/src/PanelView.cpp (+7/-0)
plugins/unityshell/src/PanelView.h (+2/-0)
plugins/unityshell/src/unityshell.cpp (+21/-0)
plugins/unityshell/unityshell.xml.in (+41/-1)
tests/CMakeLists.txt (+3/-0)
tests/test_animator.cpp (+295/-0)
tests/test_lens.cpp (+4/-29)
tests/test_utils.h (+26/-0)
To merge this branch: bzr merge lp:~3v1n0/unity/globalmenu-discovery-new-apps
Reviewer Review Type Date Requested Status
Tim Penhey (community) Approve
Review via email: mp+83367@code.launchpad.net

Description of the change

Implemented the design bug #874254 to make a newly opened application to quickly show its menus when firstly mapped using a slightly longer fade effect (now set to 200ms to fade in and 300ms to fade out).
To work properly, this feature, requires the merge of the BAMF branch lp:~3v1n0/bamf/always-notify-view otherwise sticky applications won't be show their menus when opened.

I've also added some fixes and features to the Animator class, and improved the drawing of the fading panel entries.

Plus, I've integrated the bug #875472, introducing 5 new configuration parameters to customize the fading timings and the menu discover-ability time.

Tests for this will be done when the autopilot system will be ready.

To post a comment you must log in.
Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

If you have rights to merge lp:~3v1n0/bamf/always-notify-view yourself I think you can go ahead as that module is not being handled by unity-merger afaik, If you don't have the rights I think we need to make sure you have :-)

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

Bamf is handled by unity-merger, but it's not activated right now as make check doesn't pass on headless server. Please do not merge anything manually in bamf, make check should be fixed first.

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

Mikkel: I've not these rights :(

However, I knew about the BAMF status in the unity-merger...

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

Ok, I've fixed some issues on the configuration side, now it should work as expected.

Revision history for this message
Charles Kerr (charlesk) wrote :

I'm hesitant to suggest this because it's nitpicky, but std::set might be more appropriate than std::list for _new_apps...

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

Will that save so much computation in our case? Mh... Maybe not so much, but I can do that.

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

What is the _new_apps for exactly? std::list is fine if you are going to be constantly adding and removing items, but if speed is needed more often than addition or removal then std::vector is better due to cache-coherency. If there are a lot of items, perhaps std::set is better.

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

> Bamf is handled by unity-merger, but it's not activated right now as make
> check doesn't pass on headless server. Please do not merge anything manually
> in bamf, make check should be fixed first.

Didier, this isn't your decision to make. Yes I agree that make check needs to be fixed, but that will not block other changes.

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

> What is the _new_apps for exactly? std::list is fine if you are going to be
> constantly adding and removing items, but if speed is needed more often than
> addition or removal then std::vector is better due to cache-coherency. If
> there are a lot of items, perhaps std::set is better.

This is a list of new launched applications. Generally that is just containing one item, but if you quickly launch many applications together (i.e. you're doing something like "nautilus & baobab & gedit & shotwell &" in your terminal) we need to check that once one of the new applications has been focused, its menus are shown.
Using just a pointer for that is not good, otherwise we would control only the last opened application.

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

> plugins/unityshell/src/LauncherController.cpp

Can we move the "unity-seen" inline constant to a named
file level one?

Hmm... just looked in the Animator.h file. Since you are messing
with this anyway, I'm going to dump some fun changes on you :-)
Only because I know you can handle it.

Animator.h doesn't need <Nux/Nux.h>, please remove it.

While we could change the properties to use nux properties,
lets not do that right now, but instead you can make GetRate,
GetDuration, GetProgress and IsRunning all const methods.

Can you change the member variables to use the trailing underscore.

Why is there a class FadableObject2? 2? Really? Any idea?

The logic inside TimerTimeOut should be moved into a method of the
animator class so it can be unit tested without needing the timer
itself (or timeout events).

Also, can you add a google-test style unit test for the animator
class?

It needs to test the setters, getters, constructors, timeout, start,
stop, event generation, in fact the entire public interface to the
animator :-) I can help with direction here if you need it.

Thanks.

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

> > plugins/unityshell/src/LauncherController.cpp
>
> Can we move the "unity-seen" inline constant to a named
> file level one?

Ehm, what do you mean?
I didn't write that code, however that data parameter is used to check if that bamf application has not already been added to the launcher, and according to Jason that should survive to crashes.

> Hmm... just looked in the Animator.h file. Since you are messing
> with this anyway, I'm going to dump some fun changes on you :-)
> Only because I know you can handle it.
>
> Animator.h doesn't need <Nux/Nux.h>, please remove it.

Ok, I added it to avoid to manually include all the needed libraries and classes, that already Nux uses; so we're sure that we support them also at the linking level.
However, done!

> While we could change the properties to use nux properties,
> lets not do that right now, but instead you can make GetRate,
> GetDuration, GetProgress and IsRunning all const methods.

Fine, done.

> Can you change the member variables to use the trailing underscore.

Ok (also if the unity code actually is missing both the styles, and IMHO the prefixed underscore is better, also for quickly finding private members).

> Why is there a class FadableObject2? 2? Really? Any idea?

Oh, sorry. That was an abstract class that I initially wrote for testing purposes, but it seems that apparently I didn't remove it before committing. Sorry, I'm moving that out.

> The logic inside TimerTimeOut should be moved into a method of the
> animator class so it can be unit tested without needing the timer
> itself (or timeout events).

Ok, I've added a DoStep() method, that we could use for testing I guess.

> Also, can you add a google-test style unit test for the animator
> class?
>
> It needs to test the setters, getters, constructors, timeout, start,
> stop, event generation, in fact the entire public interface to the
> animator :-) I can help with direction here if you need it.

Ok, I do something something for this. I'll contact you directly if I've something to ask.

Then, about the glib::Object work we talked, this http://paste.ubuntu.com/755020/ seems to work, and I'm also writing some test code for that.
The fact is that actually when creating a new glib::Object we can't optionally ref it, but this is something that can be needed. Especially when we're wrapping an object passed by a signal (as in this case).
Not reffing a new object is good only if we're firstly allocating it.

> Thanks.

Thank you for your review.

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

Firstly, thanks for the changes in the variable names.

Can I also get you to move the method names and return types on to the same
line? That'd be great.

Can you move the member variables initialisation into an initialiser list?

Animator::Animator(unsigned int default_duration, unsigned int fps_rate)
 : start_time(0)
 , timeout_id(0)
 , // and the rest.

My idea behind having a DoStep to test it, was to allow us to specify a fake
time to illustrate that the animation does in fact stop :)

We could do this by having:

bool Animator::DoStep(gint64 current_time);

And then:

gboolean Animator::TimerTimeOut(Animator* self)
{
  return self->DoStep(g_get_monotonic_time()) ? TRUE : FALSE;
}

This way we can call DoStep in the tests.
To have complete control over the start_time_ member, we'd probably want to
have an optional guint64 parameter for both Start methods that defaults to 0.
Inside the start method we get the clock time if the time is 0. That way from
the tests we have complete control over the state of the object. This does
bring up a problem with having default parameters making two different
functions seem like a choice. Need to think about that.

We seem to have a rate, but it doesn't look like it is used anywhere.

Don't bother about the "unity-seen" string just now.

Instead of having the g_signal_connect ids, there is the signal manager that
Neil wrote. It handles the disconnections for you.

Hmm...

auto tmp_animator = Animator(200, 25);

This is wrong for two reasons:
 * The animator class isn't a value type, so shouldn't be copyable or
 assignable
 * Using auto in this way.

Animator tmp_animator(200, 25); // is the correct way.

You should hide the copy constructor and assignment operator.
You can do this in two ways:
* declare them private yourself
* inherit privately from boost::noncopyable (from boost/utility.hpp)

Inside the tests, you can then have better control over the simulation for
steps.

{
  guint64 start_time = g_get_monotonic_time();
  test_animator_.Start(0, start_time);
  test_animator_.DoStep(start_time + 100);
  // test progress
}

Do we ever start an animation with a non-zero progress?

review: Needs Fixing
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :
Download full text (4.0 KiB)

> Firstly, thanks for the changes in the variable names.
>
> Can I also get you to move the method names and return types on to the same
> line? That'd be great.

Done.

> Can you move the member variables initialisation into an initialiser list?

Ok

> We seem to have a rate, but it doesn't look like it is used anywhere.

As I've already explained on IRC, the rate is used to compute the timeout used by the timer.

> Don't bother about the "unity-seen" string just now.

Fine

> Instead of having the g_signal_connect ids, there is the signal manager that
> Neil wrote. It handles the disconnections for you.

Moving to that...

> Hmm...
>
> Animator tmp_animator(200, 25); // is the correct way.

Moved to that.

> You should hide the copy constructor and assignment operator.
> * inherit privately from boost::noncopyable (from boost/utility.hpp)

Done.

> My idea behind having a DoStep to test it, was to allow us to specify a fake
> time to illustrate that the animation does in fact stop :)
>
> We could do this by having:
>
> bool Animator::DoStep(gint64 current_time);
>
> And then:
>
> gboolean Animator::TimerTimeOut(Animator* self)
> {
> return self->DoStep(g_get_monotonic_time()) ? TRUE : FALSE;
> }
>
> This way we can call DoStep in the tests.
> To have complete control over the start_time_ member, we'd probably want to
> have an optional guint64 parameter for both Start methods that defaults to 0.
> Inside the start method we get the clock time if the time is 0. That way from
> the tests we have complete control over the state of the object. This does
> bring up a problem with having default parameters making two different
> functions seem like a choice. Need to think about that.
> Inside the tests, you can then have better control over the simulation for
> steps.
>
> {
> guint64 start_time = g_get_monotonic_time();
> test_animator_.Start(0, start_time);
> test_animator_.DoStep(start_time + 100);
> // test progress
> }

I'm not sure I'd like this for two reasons:
 1) Putting an unneeded value like the start time on a public interface,
    only for testing is something good on test side, but bad on the API side.
    Also because no one would need it. At least that we use that start time
    to support a "delayed start time" (basically, if it's greater than "now",
    we add a timeout to delay the start of the animation).
    So in this case would be useful in some rare cases.
 2) Also if we would allow what I've stated above, the Start function would
    also fire a timeout that could interfere with our DoStep() test, and disabing
    the timeout in some cases is something I wouldn't do.

A workaround that allows to test is to use somewhat:
  /* Using a low frame rate and a long duration we're sure that the first
   * step is not called after we call Start and before the Stop get called
   * since that should happen after one second.
   * Also when we call the DoStep() the private start time is near to the actual
   * time, so this would make DoStep() more testable as it is now. */
  test_animator_.SetDuration(2000);
  test_animator_.SetRate(1);
  test_animator_.Start();
  test_animator_.Stop();
  test_animator_.DoStep...

Read more...

Revision history for this message
Tim Penhey (thumper) :
review: Approve
Revision history for this message
Unity Merger (unity-merger) wrote :

Attempt to merge into lp:unity failed due to conflicts:

text conflict in plugins/unityshell/src/PanelMenuView.cpp
text conflict in tests/CMakeLists.txt

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'manual-tests/Panel.txt'
2--- manual-tests/Panel.txt 2011-12-08 04:14:22 +0000
3+++ manual-tests/Panel.txt 2011-12-14 17:24:33 +0000
4@@ -16,3 +16,18 @@
5 should become opaque, also if the opacity setting is set to 0.0.
6 Once all the maximized windows are closed, restored or not visible,
7 the panel should go back to its defined opacity.
8+
9+
10+Panel Menus Revelation
11+----------------------
12+This test shows how the menus should be revealed when a new application has been
13+launched.
14+
15+#. Start with a clear screen
16+#. Open a new application that has menus (i.e. gnome-terminal)
17+
18+Outcome
19+ The panel should show the menus in global menu bar for two seconds.
20+ If a new gnome-terminal window is opened, the menus are not shown again.
21+ If more than one application is open at once, the menus of each application
22+ are shown once is focused.
23
24=== modified file 'plugins/unityshell/src/Animator.cpp'
25--- plugins/unityshell/src/Animator.cpp 2011-09-27 06:52:31 +0000
26+++ plugins/unityshell/src/Animator.cpp 2011-12-14 17:24:33 +0000
27@@ -22,112 +22,121 @@
28 namespace unity
29 {
30
31-Animator::Animator(unsigned int duration, unsigned int fps_rate)
32+Animator::Animator(unsigned int default_duration, unsigned int fps_rate)
33+ : start_time_(0)
34+ , rate_(1)
35+ , duration_(0)
36+ , one_time_duration_(0)
37+ , timeout_id_(0)
38+ , start_progress_(0.0f)
39+ , progress_(0.0f)
40 {
41- _start_time = 0;
42- _timeout_id = 0;
43- _progress = 0.0f;
44- _start_progress = 0.0f;
45- _rate = 1;
46- _duration = 0;
47-
48- SetDuration(duration);
49+ SetDuration(default_duration);
50 SetRate(fps_rate);
51 }
52
53 Animator::~Animator()
54 {
55- if (_timeout_id != 0)
56- g_source_remove (_timeout_id);
57+ Stop();
58 }
59
60-void
61-Animator::SetRate(unsigned int fps_rate)
62+void Animator::SetRate(unsigned int fps_rate)
63 {
64 if (fps_rate != 0)
65- _rate = 1000 / fps_rate;
66-}
67-
68-void
69-Animator::SetDuration(unsigned int duration)
70-{
71- if (duration != 0)
72- _duration = duration * 1000;
73-}
74-
75-unsigned int
76-Animator::GetRate()
77-{
78- return _rate;
79-}
80-
81-unsigned int
82-Animator::GetDuration()
83-{
84- return _duration;
85-}
86-
87-bool
88-Animator::IsRunning()
89-{
90- return (_timeout_id != 0);
91-}
92-
93-double
94-Animator::GetProgress()
95-{
96- return _progress;
97-}
98-
99-void
100-Animator::Start(double start_progress)
101-{
102- if (_timeout_id == 0 && start_progress < 1.0f)
103+ rate_ = 1000 / fps_rate;
104+}
105+
106+void Animator::SetDuration(unsigned int duration)
107+{
108+ duration_ = duration * 1000;
109+}
110+
111+unsigned int Animator::GetRate() const
112+{
113+ if (rate_ != 0)
114+ return 1000 / rate_;
115+
116+ return rate_;
117+}
118+
119+unsigned int Animator::GetDuration() const
120+{
121+ return (one_time_duration_ > 0 ? one_time_duration_ : duration_) / 1000;
122+}
123+
124+bool Animator::IsRunning() const
125+{
126+ return (timeout_id_ != 0);
127+}
128+
129+double Animator::GetProgress() const
130+{
131+ return progress_;
132+}
133+
134+void Animator::Start(unsigned int one_time_duration, double start_progress)
135+{
136+ if (timeout_id_ == 0 && start_progress < 1.0f)
137 {
138 if (start_progress < 0.0f)
139 start_progress = 0.0f;
140
141- _start_progress = start_progress;
142- _progress = _start_progress;
143- _start_time = g_get_monotonic_time();
144- _timeout_id = g_timeout_add(_rate, (GSourceFunc) &Animator::TimerTimeOut, this);
145+ one_time_duration_ = one_time_duration * 1000;
146+ start_progress_ = start_progress;
147+ progress_ = start_progress_;
148+ start_time_ = g_get_monotonic_time();
149+ timeout_id_ = g_timeout_add(rate_, (GSourceFunc) &Animator::TimerTimeOut, this);
150+ animation_started.emit();
151 }
152 }
153
154-void
155-Animator::Stop()
156-{
157- if (_timeout_id != 0)
158+void Animator::Start(double start_progress)
159+{
160+ Start(0, start_progress);
161+}
162+
163+void Animator::Stop()
164+{
165+ if (timeout_id_ != 0)
166 {
167- g_source_remove(_timeout_id);
168- animation_updated.emit(_progress);
169+ g_source_remove(timeout_id_);
170+ animation_updated.emit(progress_);
171 animation_ended.emit();
172- animation_stopped.emit(_progress);
173- _timeout_id = 0;
174+ animation_stopped.emit(progress_);
175+ one_time_duration_ = 0;
176+ timeout_id_ = 0;
177 }
178 }
179
180-gboolean
181-Animator::TimerTimeOut(Animator *self)
182+bool Animator::DoStep()
183 {
184 const gint64 current_time = g_get_monotonic_time();
185- const gint64 end_time = self->_start_time + self->_duration;
186-
187- if (current_time < end_time && self->_progress < 1.0f)
188- {
189- const double diff_time = current_time - self->_start_time;
190- self->_progress = CLAMP(self->_start_progress + (diff_time / self->_duration), 0.0f, 1.0f);
191- self->animation_updated.emit(self->_progress);
192-
193- return TRUE;
194- } else {
195- self->_progress = 1.0f;
196- self->animation_updated.emit(1.0f);
197- self->animation_ended.emit();
198- self->_timeout_id = 0;
199-
200- return FALSE;
201- }
202+ const gint64 duration = one_time_duration_ > 0 ? one_time_duration_ : duration_;
203+ const gint64 end_time = start_time_ + duration;
204+
205+ if (current_time < end_time && progress_ < 1.0f && duration > 0)
206+ {
207+ const double diff_time = current_time - start_time_;
208+ progress_ = CLAMP(start_progress_ + (diff_time / duration), 0.0f, 1.0f);
209+ animation_updated.emit(progress_);
210+
211+ return true;
212+ }
213+ else
214+ {
215+ progress_ = 1.0f;
216+ animation_updated.emit(1.0f);
217+ animation_ended.emit();
218+ one_time_duration_ = 0;
219+ timeout_id_ = 0;
220+
221+ return false;
222+ }
223+}
224+
225+gboolean Animator::TimerTimeOut(Animator *self)
226+{
227+ return self->DoStep() ? TRUE : FALSE;
228 }
229
230 } //namespace
231
232=== modified file 'plugins/unityshell/src/Animator.h'
233--- plugins/unityshell/src/Animator.h 2011-09-23 00:31:08 +0000
234+++ plugins/unityshell/src/Animator.h 2011-12-14 17:24:33 +0000
235@@ -20,33 +20,30 @@
236 #ifndef UNITY_ANIMATOR_H_
237 #define UNITY_ANIMATOR_H_
238
239-#include <Nux/Nux.h>
240+#include <glib.h>
241+#include <cstdint>
242+#include <sigc++/sigc++.h>
243+#include <boost/utility.hpp>
244
245 namespace unity
246 {
247-
248-class FadableObject2
249-{
250-public:
251- virtual void SetOpacity(double value) = 0;
252- virtual double GetOpacity() = 0;
253-};
254-
255-class Animator
256-{
257-public:
258- Animator(unsigned int duration, unsigned int fps_rate = 30);
259+class Animator : boost::noncopyable
260+{
261+public:
262+ Animator(unsigned int default_duration, unsigned int fps_rate = 30);
263 ~Animator();
264
265 void SetRate(unsigned int fps_rate);
266 void SetDuration(unsigned int duration);
267
268- unsigned int GetRate();
269- unsigned int GetDuration();
270- double GetProgress();
271- bool IsRunning();
272+ unsigned int GetRate() const;
273+ unsigned int GetDuration() const;
274+ double GetProgress() const;
275+ bool IsRunning() const;
276
277 void Start(double start_progress = 0.0f);
278+ void Start(unsigned int one_time_duration, double start_progress = 0.0f);
279+ bool DoStep();
280 void Stop();
281
282 sigc::signal<void> animation_started;
283@@ -56,12 +53,13 @@
284 sigc::signal<void, double> animation_stopped;
285
286 private:
287- int64_t _start_time;
288- unsigned int _rate;
289- unsigned int _duration;
290- unsigned int _timeout_id;
291- double _start_progress;
292- double _progress;
293+ int64_t start_time_;
294+ unsigned int rate_;
295+ unsigned int duration_;
296+ unsigned int one_time_duration_;
297+ unsigned int timeout_id_;
298+ double start_progress_;
299+ double progress_;
300
301 static gboolean TimerTimeOut(Animator *self);
302 };
303
304=== modified file 'plugins/unityshell/src/LauncherController.cpp'
305--- plugins/unityshell/src/LauncherController.cpp 2011-11-30 03:06:44 +0000
306+++ plugins/unityshell/src/LauncherController.cpp 2011-12-14 17:24:33 +0000
307@@ -403,8 +403,11 @@
308
309 app = BAMF_APPLICATION(view);
310
311- if (g_object_get_qdata(G_OBJECT(app), g_quark_from_static_string("unity-seen")))
312+ if (bamf_view_is_sticky(view) ||
313+ g_object_get_qdata(G_OBJECT(app), g_quark_from_static_string("unity-seen")))
314+ {
315 return;
316+ }
317
318 BamfLauncherIcon* icon = new BamfLauncherIcon(self->launcher_.GetPointer(), app);
319 icon->SetIconType(LauncherIcon::TYPE_APPLICATION);
320
321=== modified file 'plugins/unityshell/src/PanelController.cpp'
322--- plugins/unityshell/src/PanelController.cpp 2011-11-16 00:09:59 +0000
323+++ plugins/unityshell/src/PanelController.cpp 2011-12-14 17:24:33 +0000
324@@ -55,6 +55,9 @@
325
326 float opacity() const;
327
328+ void SetMenuShowTimings(int fadein, int fadeout, int discovery,
329+ int discovery_fadein, int discovery_fadeout);
330+
331 private:
332 unity::PanelView* ViewForWindow(nux::BaseWindow* window);
333 void OnScreenChanged(int primary_monitor, std::vector<nux::Geometry>& monitors);
334@@ -68,6 +71,11 @@
335 float opacity_;
336 bool opacity_maximized_toggle_;
337 bool open_menu_start_received_;
338+ int menus_fadein_;
339+ int menus_fadeout_;
340+ int menus_discovery_;
341+ int menus_discovery_fadein_;
342+ int menus_discovery_fadeout_;
343 };
344
345
346@@ -147,12 +155,28 @@
347 {
348 opacity_maximized_toggle_ = enabled;
349
350- for (auto window : windows_)
351+ for (auto window: windows_)
352 {
353 ViewForWindow(window)->SetOpacityMaximizedToggle(opacity_maximized_toggle_);
354 }
355 }
356
357+void Controller::Impl::SetMenuShowTimings(int fadein, int fadeout, int discovery,
358+ int discovery_fadein, int discovery_fadeout)
359+{
360+ menus_fadein_ = fadein;
361+ menus_fadeout_ = fadeout;
362+ menus_discovery_ = discovery;
363+ menus_discovery_fadein_ = discovery_fadein;
364+ menus_discovery_fadeout_ = discovery_fadeout;
365+
366+ for (auto window: windows_)
367+ {
368+ ViewForWindow(window)->SetMenuShowTimings(fadein, fadeout, discovery,
369+ discovery_fadein, discovery_fadeout);
370+ }
371+}
372+
373 void Controller::Impl::QueueRedraw()
374 {
375 for (auto window: windows_)
376@@ -216,6 +240,8 @@
377 view->SetMaximumHeight(24);
378 view->SetOpacity(opacity_);
379 view->SetOpacityMaximizedToggle(opacity_maximized_toggle_);
380+ view->SetMenuShowTimings(menus_fadein_, menus_fadeout_, menus_discovery_,
381+ menus_discovery_fadein_, menus_discovery_fadeout_);
382 view->SetPrimary(i == primary_monitor);
383 view->SetMonitor(i);
384
385@@ -301,6 +327,12 @@
386 pimpl->SetOpacityMaximizedToggle(enabled);
387 }
388
389+void Controller::SetMenuShowTimings(int fadein, int fadeout, int discovery,
390+ int discovery_fadein, int discovery_fadeout)
391+{
392+ pimpl->SetMenuShowTimings(fadein, fadeout, discovery, discovery_fadein, discovery_fadeout);
393+}
394+
395 void Controller::QueueRedraw()
396 {
397 pimpl->QueueRedraw();
398
399=== modified file 'plugins/unityshell/src/PanelController.h'
400--- plugins/unityshell/src/PanelController.h 2011-11-16 00:09:59 +0000
401+++ plugins/unityshell/src/PanelController.h 2011-12-14 17:24:33 +0000
402@@ -48,6 +48,8 @@
403 // NOTE: nux::Property maybe?
404 void SetOpacity(float opacity);
405 void SetOpacityMaximizedToggle(bool enabled);
406+ void SetMenuShowTimings(int fadein, int fadeout, int discovery, int discovery_fadein, int discovery_fadeout);
407+
408 float opacity() const;
409
410 private:
411
412=== modified file 'plugins/unityshell/src/PanelMenuView.cpp'
413--- plugins/unityshell/src/PanelMenuView.cpp 2011-12-14 16:18:41 +0000
414+++ plugins/unityshell/src/PanelMenuView.cpp 2011-12-14 17:24:33 +0000
415@@ -49,44 +49,40 @@
416
417 #define WINDOW_TITLE_FONT_KEY "/apps/metacity/general/titlebar_font"
418
419-#define PANEL_ENTRIES_FADEIN 100
420-#define PANEL_ENTRIES_FADEOUT 120
421-
422 namespace unity
423 {
424
425-static void on_active_window_changed(BamfMatcher* matcher,
426- BamfView* old_view,
427- BamfView* new_view,
428- PanelMenuView* self);
429-
430-static void on_name_changed(BamfView* bamf_view,
431- gchar* old_name,
432- gchar* new_name,
433- PanelMenuView* self);
434-
435 PanelMenuView::PanelMenuView(int padding)
436- : _matcher(NULL),
437- _title_layer(NULL),
438+ : _matcher(bamf_matcher_get_default()),
439+ _title_layer(nullptr),
440 _util_cg(CAIRO_FORMAT_ARGB32, 1, 1),
441- _gradient_texture(NULL),
442+ _gradient_texture(nullptr),
443 _is_inside(false),
444 _is_maximized(false),
445 _is_own_window(false),
446- _last_active_view(NULL),
447+ _last_active_view(nullptr),
448+ _new_application(nullptr),
449 _last_width(0),
450 _last_height(0),
451 _places_showing(false),
452 _show_now_activated(false),
453 _we_control_active(false),
454+ _new_app_menu_shown(false),
455 _monitor(0),
456 _active_xid(0),
457 _active_moved_id(0),
458 _update_show_now_id(0),
459+ _new_app_show_id(0),
460+ _new_app_hide_id(0),
461 _place_shown_interest(0),
462 _place_hidden_interest(0),
463- _fade_in_animator(NULL),
464- _fade_out_animator(NULL)
465+ _menus_fadein(100),
466+ _menus_fadeout(120),
467+ _menus_discovery(2),
468+ _menus_discovery_fadein(200),
469+ _menus_discovery_fadeout(300),
470+ _fade_in_animator(nullptr),
471+ _fade_out_animator(nullptr)
472 {
473 WindowManager* win_manager;
474
475@@ -100,13 +96,16 @@
476 */
477 layout_ = _menu_layout;
478
479- _matcher = bamf_matcher_get_default();
480- _activate_window_changed_id = g_signal_connect(_matcher, "active-window-changed",
481- G_CALLBACK(on_active_window_changed), this);
482+ _view_opened_signal.Connect(_matcher, "view-opened",
483+ sigc::mem_fun(this, &PanelMenuView::OnViewOpened));
484+ _view_closed_signal.Connect(_matcher, "view-closed",
485+ sigc::mem_fun(this, &PanelMenuView::OnViewClosed));
486+ _active_win_changed_signal.Connect(_matcher, "active-window-changed",
487+ sigc::mem_fun(this, &PanelMenuView::OnActiveWindowChanged));
488+ _active_app_changed_signal.Connect(_matcher, "active-application-changed",
489+ sigc::mem_fun(this, &PanelMenuView::OnActiveAppChanged));
490
491 _padding = padding;
492- _name_changed_callback_instance = NULL;
493- _name_changed_callback_id = 0;
494
495 _window_buttons = new WindowButtons();
496 _window_buttons->SetParentObject(this);
497@@ -163,8 +162,8 @@
498 (UBusCallback)PanelMenuView::OnPlaceViewHidden,
499 this);
500
501- _fade_in_animator = new Animator(PANEL_ENTRIES_FADEIN);
502- _fade_out_animator = new Animator(PANEL_ENTRIES_FADEOUT);
503+ _fade_in_animator = new Animator(_menus_fadein);
504+ _fade_out_animator = new Animator(_menus_fadeout);
505
506 _fade_in_animator->animation_updated.connect(sigc::mem_fun(this, &PanelMenuView::OnFadeInChanged));
507 _fade_in_animator->animation_ended.connect(sigc::mem_fun(this, &PanelMenuView::FullRedraw));
508@@ -180,15 +179,15 @@
509
510 PanelMenuView::~PanelMenuView()
511 {
512- if (_name_changed_callback_id)
513- g_signal_handler_disconnect(_name_changed_callback_instance,
514- _name_changed_callback_id);
515- if (_activate_window_changed_id)
516- g_signal_handler_disconnect(_matcher,
517- _activate_window_changed_id);
518 if (_active_moved_id)
519 g_source_remove(_active_moved_id);
520
521+ if (_new_app_show_id)
522+ g_source_remove(_new_app_show_id);
523+
524+ if (_new_app_hide_id)
525+ g_source_remove(_new_app_hide_id);
526+
527 if (_title_layer)
528 delete _title_layer;
529
530@@ -208,6 +207,35 @@
531
532 if (_place_hidden_interest != 0)
533 ubus_server_unregister_interest(ubus, _place_hidden_interest);
534+
535+ for (auto app : _new_apps)
536+ g_object_unref(app);
537+}
538+
539+void
540+PanelMenuView::SetMenuShowTimings(int fadein, int fadeout, int discovery,
541+ int discovery_fadein, int discovery_fadeout)
542+{
543+ if (fadein > -1)
544+ {
545+ _menus_fadein = fadein;
546+ _fade_in_animator->SetDuration(_menus_fadein);
547+ }
548+
549+ if (fadeout > -1)
550+ {
551+ _menus_fadeout = fadeout;
552+ _fade_out_animator->SetDuration(_menus_fadeout);
553+ }
554+
555+ if (discovery > -1)
556+ _menus_discovery = discovery;
557+
558+ if (discovery_fadein > -1)
559+ _menus_discovery_fadein = discovery_fadein;
560+
561+ if (discovery_fadeout > -1)
562+ _menus_discovery_fadeout = discovery_fadeout;
563 }
564
565 void
566@@ -224,9 +252,9 @@
567 bool mouse_inside = TestMousePointerInclusionFilterMouseWheel(mouse_position, event_type);
568
569 if (mouse_inside == false)
570- return NULL;
571+ return nullptr;
572
573- Area* found_area = NULL;
574+ Area* found_area = nullptr;
575 if (!_we_control_active)
576 {
577 found_area = _panel_titlebar_grab_area->FindAreaUnderMouse(mouse_position, event_type);
578@@ -327,7 +355,7 @@
579 {
580 if (!_is_own_window && !_places_showing && _we_control_active)
581 {
582- if (_is_inside || _last_active_view || _show_now_activated)
583+ if (_is_inside || _last_active_view || _show_now_activated || _new_application)
584 {
585 return true;
586 }
587@@ -344,7 +372,7 @@
588
589 if (!_is_own_window && _we_control_active && _is_maximized)
590 {
591- if (_is_inside || _show_now_activated)
592+ if (_is_inside || _show_now_activated || _new_application)
593 {
594 return true;
595 }
596@@ -382,23 +410,28 @@
597 if (_title_layer && !_is_own_window)
598 {
599 guint blend_alpha = 0, blend_src = 0, blend_dest = 0;
600+ bool draw_menus = DrawMenus();
601+ bool draw_window_buttons = DrawWindowButtons();
602+ bool has_menu = false;
603 bool draw_faded_title = false;
604
605 GfxContext.GetRenderStates().GetBlend(blend_alpha, blend_src, blend_dest);
606
607- if (!DrawWindowButtons() && _we_control_active &&
608- (DrawMenus() || (GetOpacity() > 0.0f && _window_buttons->GetOpacity() == 0.0f)))
609+ for (auto entry : entries_)
610 {
611- for (auto entry : entries_)
612+ if (entry.second->IsEntryValid())
613 {
614- if (entry.second->IsEntryValid())
615- {
616- draw_faded_title = true;
617- break;
618- }
619+ has_menu = true;
620+ break;
621 }
622 }
623
624+ if (!draw_window_buttons && _we_control_active && has_menu &&
625+ (draw_menus || (GetOpacity() > 0.0f && _window_buttons->GetOpacity() == 0.0f)))
626+ {
627+ draw_faded_title = true;
628+ }
629+
630 if (draw_faded_title)
631 {
632 bool build_gradient = false;
633@@ -412,7 +445,7 @@
634 }
635 else
636 {
637- if (_gradient_texture->LockRect(0, &lockrect, NULL) != OGL_OK)
638+ if (_gradient_texture->LockRect(0, &lockrect, nullptr) != OGL_OK)
639 build_gradient = true;
640 else
641 locked = true;
642@@ -433,44 +466,55 @@
643 _gradient_texture = nux::GetGraphicsDisplay()->GetGpuDevice()->
644 CreateSystemCapableDeviceTexture(texture_data.GetWidth(),
645 texture_data.GetHeight(), 1, texture_data.GetFormat());
646- locked = (_gradient_texture->LockRect(0, &lockrect, NULL) == OGL_OK);
647+ locked = (_gradient_texture->LockRect(0, &lockrect, nullptr) == OGL_OK);
648 }
649
650+ BYTE* dest_buffer = (BYTE*) lockrect.pBits;
651 int gradient_opacity = 255.0f * GetOpacity();
652- BYTE* dest_buffer = (BYTE*) lockrect.pBits;
653+ int buttons_opacity = 255.0f * _window_buttons->GetOpacity();
654+
655+ int first_step = button_width * (factor - 1);
656+ int second_step = button_width * factor;
657
658 for (int x = 0; x < geo.width && dest_buffer && locked; x++)
659 {
660- BYTE a;
661- if (x < button_width * (factor - 1))
662+ BYTE r, g, b, a;
663+
664+ r = 223;
665+ g = 219;
666+ b = 210;
667+
668+ if (x < first_step)
669 {
670- a = 0xff;
671+ int color_increment = (first_step - x) * 4;
672+
673+ r = CLAMP(r + color_increment, r, 0xff);
674+ g = CLAMP(g + color_increment, g, 0xff);
675+ b = CLAMP(b + color_increment, b, 0xff);
676+ a = 0xff - buttons_opacity;
677 }
678- else if (x < button_width * factor)
679+ else if (x < second_step)
680 {
681- a = 0xff - gradient_opacity * (((float)x - (button_width * (factor - 1))) / (float)(button_width));
682+ a = 0xff - gradient_opacity * (((float)x - (first_step)) /
683+ (float)(button_width));
684 }
685 else
686 {
687- if (!DrawMenus())
688+ if (!draw_menus)
689 {
690 a = 0xff - gradient_opacity;
691 }
692- else if (0xff - gradient_opacity > 0x55)
693+ else
694 {
695 // If we're fading-out the title, it's better to quickly hide
696 // the transparent right-most area
697- a = 0xff - gradient_opacity - 0x55;
698- }
699- else
700- {
701- a = 0x00;
702+ a = CLAMP(0xff - gradient_opacity - 0x55, 0x00, 0xff);
703 }
704 }
705
706- *(dest_buffer + 4 * x + 0) = (223 * a) / 255; //red
707- *(dest_buffer + 4 * x + 1) = (219 * a) / 255; //green
708- *(dest_buffer + 4 * x + 2) = (210 * a) / 255; //blue
709+ *(dest_buffer + 4 * x + 0) = (r * a) / 0xff; //red
710+ *(dest_buffer + 4 * x + 1) = (g * a) / 0xff; //green
711+ *(dest_buffer + 4 * x + 2) = (b * a) / 0xff; //blue
712 *(dest_buffer + 4 * x + 3) = a;
713 }
714
715@@ -491,37 +535,39 @@
716 _title_layer->GetDeviceTexture(),
717 texxform1,
718 nux::color::White);
719-
720- // The previous blend is too aggressive on the texture and therefore there
721- // is a slight loss of clarity. This fixes that
722- geo.width = button_width * (factor - 1);
723- nux::GetPainter().PushDrawLayer(GfxContext, geo, _title_layer);
724- geo = GetGeometry();
725 }
726- else if (_window_buttons->GetOpacity() < 1.0f &&
727- _window_buttons->GetOpacity() > 0.0f && !_places_showing)
728+ else if (!_places_showing)
729 {
730- double title_opacity = 1.0f - _window_buttons->GetOpacity();
731-
732- if (!DrawWindowButtons())
733+ if (_we_control_active && _window_buttons->GetOpacity() == 0.0 &&
734+ (!has_menu || (has_menu && GetOpacity() == 0.0)))
735 {
736- // If we're fading-out the buttons/menus, let's fade-in quickly the title
737- title_opacity = CLAMP(title_opacity + 0.25f, 0.0f, 1.0f);
738+ nux::GetPainter().PushDrawLayer(GfxContext, geo, _title_layer);
739 }
740 else
741 {
742- // If we're fading-in the buttons/menus, let's fade-out quickly the title
743- title_opacity = CLAMP(title_opacity - 0.25f, 0.0f, 1.0f);
744+ double title_opacity = 1.0f;
745+
746+ if (has_menu)
747+ title_opacity -= MAX(GetOpacity(), _window_buttons->GetOpacity());
748+ else
749+ title_opacity -= _window_buttons->GetOpacity();
750+
751+ if (!draw_window_buttons && !draw_menus)
752+ {
753+ // If we're fading-out the buttons/menus, let's fade-in quickly the title
754+ title_opacity = CLAMP(title_opacity + 0.1f, 0.0f, 1.0f);
755+ }
756+ else
757+ {
758+ // If we're fading-in the buttons/menus, let's fade-out quickly the title
759+ title_opacity = CLAMP(title_opacity - 0.2f, 0.0f, 1.0f);
760+ }
761+
762+ nux::TexCoordXForm texxform;
763+ GfxContext.QRP_1Tex(geo.x, geo.y, geo.width, geo.height,
764+ _title_layer->GetDeviceTexture(), texxform,
765+ nux::color::White * title_opacity);
766 }
767-
768- nux::TexCoordXForm texxform;
769- GfxContext.QRP_1Tex(geo.x, geo.y, geo.width, geo.height,
770- _title_layer->GetDeviceTexture(), texxform,
771- nux::color::White * title_opacity);
772- }
773- else if (_window_buttons->GetOpacity() == 0.0f && _we_control_active)
774- {
775- nux::GetPainter().PushDrawLayer(GfxContext, geo, _title_layer);
776 }
777
778 GfxContext.GetRenderStates().SetBlend(blend_alpha, blend_src, blend_dest);
779@@ -549,7 +595,16 @@
780 _menu_layout->ProcessDraw(GfxContext, true);
781
782 _fade_out_animator->Stop();
783- _fade_in_animator->Start(GetOpacity());
784+
785+ if (_new_application && !_is_inside)
786+ {
787+ _fade_in_animator->Start(_menus_discovery_fadein, GetOpacity());
788+ }
789+ else
790+ {
791+ _fade_in_animator->Start(GetOpacity());
792+ _new_app_menu_shown = false;
793+ }
794 }
795 else
796 {
797@@ -563,14 +618,14 @@
798
799 _fade_in_animator->Stop();
800
801- if (_fade_out_animator->GetDuration() != PANEL_ENTRIES_FADEOUT)
802- {
803- if (_fade_out_animator->IsRunning())
804- _fade_out_animator->Stop();
805-
806- _fade_out_animator->SetDuration(PANEL_ENTRIES_FADEOUT);
807- }
808- _fade_out_animator->Start(1.0f - GetOpacity());
809+ if (!_new_app_menu_shown)
810+ {
811+ _fade_out_animator->Start(1.0f - GetOpacity());
812+ }
813+ else
814+ {
815+ _fade_out_animator->Start(_menus_discovery_fadeout, 1.0f - GetOpacity());
816+ }
817 }
818
819 if (draw_buttons)
820@@ -589,8 +644,7 @@
821 /* If we try to hide only the buttons, then use a faster fadeout */
822 if (!_fade_out_animator->IsRunning())
823 {
824- _fade_out_animator->SetDuration(PANEL_ENTRIES_FADEOUT/5);
825- _fade_out_animator->Start(1.0f - _window_buttons->GetOpacity());
826+ _fade_out_animator->Start(_menus_fadeout/3, 1.0f - _window_buttons->GetOpacity());
827 }
828 }
829
830@@ -600,7 +654,7 @@
831 gchar*
832 PanelMenuView::GetActiveViewName()
833 {
834- gchar* label = NULL;
835+ gchar* label = nullptr;
836 BamfWindow* window;
837
838 _is_own_window = false;
839@@ -659,7 +713,7 @@
840 }
841 }
842
843- if (label == NULL)
844+ if (label == nullptr)
845 {
846 BamfView* active_view;
847
848@@ -691,8 +745,8 @@
849 int increase_size
850 )
851 {
852- PangoLayout* layout = NULL;
853- PangoFontDescription* desc = NULL;
854+ PangoLayout* layout = nullptr;
855+ PangoFontDescription* desc = nullptr;
856 GtkSettings* settings = gtk_settings_get_default();
857 cairo_t* cr;
858 cairo_pattern_t* linpat;
859@@ -712,9 +766,9 @@
860
861 cr = _util_cg.GetContext();
862
863- g_object_get(settings, "gtk-xft-dpi", &dpi, NULL);
864+ g_object_get(settings, "gtk-xft-dpi", &dpi, nullptr);
865
866- font_description = gconf_client_get_string(client, WINDOW_TITLE_FONT_KEY, NULL);
867+ font_description = gconf_client_get_string(client, WINDOW_TITLE_FONT_KEY, nullptr);
868 desc = pango_font_description_from_string(font_description);
869
870 if (font_desc)
871@@ -742,7 +796,7 @@
872 pango_cairo_context_set_resolution(cxt, (float)dpi / (float)PANGO_SCALE);
873 pango_layout_context_changed(layout);
874
875- pango_layout_get_extents(layout, NULL, &log_rect);
876+ pango_layout_get_extents(layout, nullptr, &log_rect);
877 text_width = log_rect.width / PANGO_SCALE;
878 text_height = log_rect.height / PANGO_SCALE;
879
880@@ -833,7 +887,7 @@
881 y = 0;
882
883 if (label)
884- DrawText(cr, x, y, width, height, NULL, label);
885+ DrawText(cr, x, y, width, height, nullptr, label);
886
887 cairo_destroy(cr);
888
889@@ -869,7 +923,7 @@
890 {
891 if (_last_active_view == view)
892 {
893- _last_active_view = NULL;
894+ _last_active_view = nullptr;
895 }
896 }
897
898@@ -894,28 +948,139 @@
899 {
900 auto mouse = nux::GetGraphicsDisplay()->GetMouseScreenCoord();
901 _is_inside = GetAbsoluteGeometry().IsPointInside(mouse.x, mouse.y);
902- _last_active_view = NULL;
903+ _last_active_view = nullptr;
904
905 FullRedraw();
906 }
907
908 void
909-PanelMenuView::OnNameChanged(gchar* new_name, gchar* old_name)
910+PanelMenuView::OnNameChanged(BamfView* bamf_view, gchar* new_name, gchar* old_name)
911 {
912 Refresh();
913 FullRedraw();
914 }
915
916-void
917-PanelMenuView::OnActiveWindowChanged(BamfView* old_view,
918+gboolean
919+PanelMenuView::OnNewAppShow(PanelMenuView* self)
920+{
921+ self->_new_application = bamf_matcher_get_active_application(self->_matcher);
922+ self->QueueDraw();
923+
924+ if (self->_new_app_hide_id)
925+ {
926+ g_source_remove(self->_new_app_hide_id);
927+ self->_new_app_hide_id = 0;
928+ self->_new_app_menu_shown = false;
929+ }
930+
931+ self->_new_app_hide_id = g_timeout_add_seconds(self->_menus_discovery,
932+ (GSourceFunc)PanelMenuView::OnNewAppHide,
933+ self);
934+ self->_new_app_show_id = 0;
935+
936+ return FALSE;
937+}
938+
939+gboolean
940+PanelMenuView::OnNewAppHide(PanelMenuView* self)
941+{
942+ self->OnViewClosed(self->_matcher, BAMF_VIEW(self->_new_application.RawPtr()));
943+ self->_new_app_hide_id = 0;
944+ self->_new_app_menu_shown = true;
945+ self->QueueDraw();
946+
947+ return FALSE;
948+}
949+
950+void
951+PanelMenuView::OnViewOpened(BamfMatcher *matcher, BamfView *view)
952+{
953+ /* FIXME: here we should also check for if the view is also user_visible
954+ * but it seems that BAMF doesn't handle this correctly after some
955+ * stress tests (repeated launches). */
956+ if (!BAMF_IS_APPLICATION(view))
957+ return;
958+
959+ _new_apps.push_front(BAMF_APPLICATION(g_object_ref(view)));
960+}
961+
962+void
963+PanelMenuView::OnViewClosed(BamfMatcher *matcher, BamfView *view)
964+{
965+ if (!BAMF_IS_APPLICATION(view))
966+ return;
967+
968+ BamfApplication* app = BAMF_APPLICATION(view);
969+
970+ if (std::find(_new_apps.begin(), _new_apps.end(), app) != _new_apps.end())
971+ {
972+ _new_apps.remove(app);
973+ g_object_unref(app);
974+
975+ if (_new_application == app)
976+ _new_application = nullptr;
977+ }
978+}
979+
980+void
981+PanelMenuView::OnActiveAppChanged(BamfMatcher *matcher,
982+ BamfApplication* old_app,
983+ BamfApplication* new_app)
984+{
985+ if (BAMF_IS_APPLICATION(new_app))
986+ {
987+ if (std::find(_new_apps.begin(), _new_apps.end(), new_app) != _new_apps.end())
988+ {
989+ if (_new_application != new_app)
990+ {
991+ /* Add a small delay before showing the menus, this is done both
992+ * to fix the issues with applications that takes some time to loads
993+ * menus and to show the menus only when an application has been
994+ * kept active for some time */
995+
996+ if (_new_app_show_id)
997+ g_source_remove(_new_app_show_id);
998+
999+ _new_app_show_id = g_timeout_add(300,
1000+ (GSourceFunc)PanelMenuView::OnNewAppShow,
1001+ this);
1002+ }
1003+ }
1004+ else
1005+ {
1006+ if (_new_app_show_id)
1007+ {
1008+ g_source_remove(_new_app_show_id);
1009+ _new_app_show_id = 0;
1010+ }
1011+
1012+ if (_new_app_hide_id)
1013+ {
1014+ g_source_remove(_new_app_hide_id);
1015+ _new_app_hide_id = 0;
1016+ _new_app_menu_shown = false;
1017+ }
1018+
1019+ if (_new_application)
1020+ OnViewClosed(matcher, BAMF_VIEW(_new_application.RawPtr()));
1021+ }
1022+ }
1023+}
1024+
1025+void
1026+PanelMenuView::OnActiveWindowChanged(BamfMatcher *matcher,
1027+ BamfView* old_view,
1028 BamfView* new_view)
1029 {
1030 _show_now_activated = false;
1031 _is_maximized = false;
1032 _active_xid = 0;
1033+
1034 if (_active_moved_id)
1035+ {
1036 g_source_remove(_active_moved_id);
1037- _active_moved_id = 0;
1038+ _active_moved_id = 0;
1039+ }
1040
1041 if (BAMF_IS_WINDOW(new_view))
1042 {
1043@@ -944,16 +1109,11 @@
1044 }
1045
1046 // first see if we need to remove and old callback
1047- if (_name_changed_callback_id != 0)
1048- g_signal_handler_disconnect(_name_changed_callback_instance,
1049- _name_changed_callback_id);
1050+ _view_name_changed_signal.Disconnect();
1051
1052- // register callback for new view and store handler-id
1053- _name_changed_callback_instance = G_OBJECT(new_view);
1054- _name_changed_callback_id = g_signal_connect(_name_changed_callback_instance,
1055- "name-changed",
1056- (GCallback) on_name_changed,
1057- this);
1058+ // register callback for new view
1059+ _view_name_changed_signal.Connect(new_view, "name-changed",
1060+ sigc::mem_fun(this, &PanelMenuView::OnNameChanged));
1061 }
1062
1063 Refresh();
1064@@ -1129,7 +1289,7 @@
1065 {
1066 if (_places_showing)
1067 {
1068- ubus_server_send_message(ubus_server_get_default(), UBUS_PLACE_VIEW_CLOSE_REQUEST, NULL);
1069+ ubus_server_send_message(ubus_server_get_default(), UBUS_PLACE_VIEW_CLOSE_REQUEST, nullptr);
1070 }
1071 else
1072 {
1073@@ -1342,35 +1502,13 @@
1074 const gchar*
1075 PanelMenuView::GetChildsName()
1076 {
1077- return NULL;
1078+ return nullptr;
1079 }
1080
1081 void PanelMenuView::AddProperties(GVariantBuilder* builder)
1082 {
1083 }
1084
1085-/*
1086- * C code for callbacks
1087- */
1088-static void
1089-
1090-on_active_window_changed(BamfMatcher* matcher,
1091- BamfView* old_view,
1092- BamfView* new_view,
1093- PanelMenuView* self)
1094-{
1095- self->OnActiveWindowChanged(old_view, new_view);
1096-}
1097-
1098-static void
1099-on_name_changed(BamfView* bamf_view,
1100- gchar* old_name,
1101- gchar* new_name,
1102- PanelMenuView* self)
1103-{
1104- self->OnNameChanged(new_name, old_name);
1105-}
1106-
1107 void
1108 PanelMenuView::OnPlaceViewShown(GVariant* data, PanelMenuView* self)
1109 {
1110
1111=== modified file 'plugins/unityshell/src/PanelMenuView.h'
1112--- plugins/unityshell/src/PanelMenuView.h 2011-12-14 16:18:41 +0000
1113+++ plugins/unityshell/src/PanelMenuView.h 2011-12-14 17:24:33 +0000
1114@@ -31,6 +31,8 @@
1115 #include "PluginAdapter.h"
1116 #include "Animator.h"
1117
1118+#include <UnityCore/GLibWrapper.h>
1119+#include <UnityCore/GLibSignal.h>
1120 #include <libbamf/libbamf.h>
1121
1122 namespace unity
1123@@ -54,6 +56,9 @@
1124 PanelMenuView(int padding = 6);
1125 ~PanelMenuView();
1126
1127+ void SetMenuShowTimings(int fadein, int fadeout, int discovery,
1128+ int discovery_fadein, int discovery_fadeout);
1129+
1130 void FullRedraw();
1131
1132 virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw);
1133@@ -63,8 +68,11 @@
1134 void SetMousePosition(int x, int y);
1135
1136 void OnActiveChanged(PanelIndicatorEntryView* view, bool is_active);
1137- void OnActiveWindowChanged(BamfView* old_view, BamfView* new_view);
1138- void OnNameChanged(gchar* new_name, gchar* old_name);
1139+ void OnViewOpened(BamfMatcher* matcher, BamfView* view);
1140+ void OnViewClosed(BamfMatcher* matcher, BamfView* view);
1141+ void OnActiveWindowChanged(BamfMatcher* matcher, BamfView* old_view, BamfView* new_view);
1142+ void OnActiveAppChanged(BamfMatcher* matcher, BamfApplication* old_app, BamfApplication* new_app);
1143+ void OnNameChanged(BamfView* bamf_view, gchar* new_name, gchar* old_name);
1144
1145 void OnSpreadInitiate();
1146 void OnSpreadTerminate();
1147@@ -116,6 +124,8 @@
1148 void UpdateShowNow(bool ignore);
1149 static gboolean UpdateActiveWindowPosition(PanelMenuView* self);
1150 static gboolean UpdateShowNowWithDelay(PanelMenuView* self);
1151+ static gboolean OnNewAppShow(PanelMenuView* self);
1152+ static gboolean OnNewAppHide(PanelMenuView* self);
1153 void DrawText(cairo_t *cr_real,
1154 int &x, int y, int width, int height,
1155 const char* font_desc,
1156@@ -130,7 +140,7 @@
1157 void OnFadeOutChanged(double);
1158
1159 private:
1160- BamfMatcher* _matcher;
1161+ glib::Object<BamfMatcher> _matcher;
1162
1163 nux::TextureLayer* _title_layer;
1164 nux::HLayout* _menu_layout;
1165@@ -142,34 +152,47 @@
1166 bool _is_maximized;
1167 bool _is_own_window;
1168 PanelIndicatorEntryView* _last_active_view;
1169+ glib::Object<BamfApplication> _new_application;
1170
1171 WindowButtons* _window_buttons;
1172 PanelTitlebarGrabArea* _panel_titlebar_grab_area;
1173
1174 std::map<guint32, bool> _decor_map;
1175 std::set<guint32> _maximized_set;
1176+ std::list<BamfApplication*> _new_apps;
1177+
1178 int _padding;
1179- gpointer _name_changed_callback_instance;
1180- gulong _name_changed_callback_id;
1181-
1182 int _last_width;
1183 int _last_height;
1184
1185 bool _places_showing;
1186 bool _show_now_activated;
1187-
1188 bool _we_control_active;
1189+ bool _new_app_menu_shown;
1190+
1191 int _monitor;
1192 guint32 _active_xid;
1193 guint32 _active_moved_id;
1194 guint32 _update_show_now_id;
1195+ guint32 _new_app_show_id;
1196+ guint32 _new_app_hide_id;
1197 nux::Geometry _monitor_geo;
1198
1199- gulong _activate_window_changed_id;
1200+ glib::Signal<void, BamfMatcher*, BamfView*> _view_opened_signal;
1201+ glib::Signal<void, BamfMatcher*, BamfView*> _view_closed_signal;
1202+ glib::Signal<void, BamfMatcher*, BamfView*, BamfView*> _active_win_changed_signal;
1203+ glib::Signal<void, BamfMatcher*, BamfApplication*, BamfApplication*> _active_app_changed_signal;
1204+ glib::Signal<void, BamfView*, gchar*, gchar*> _view_name_changed_signal;
1205
1206 guint32 _place_shown_interest;
1207 guint32 _place_hidden_interest;
1208
1209+ int _menus_fadein;
1210+ int _menus_fadeout;
1211+ int _menus_discovery;
1212+ int _menus_discovery_fadein;
1213+ int _menus_discovery_fadeout;
1214+
1215 Animator* _fade_in_animator;
1216 Animator* _fade_out_animator;
1217 };
1218
1219=== modified file 'plugins/unityshell/src/PanelView.cpp'
1220--- plugins/unityshell/src/PanelView.cpp 2011-12-14 16:18:41 +0000
1221+++ plugins/unityshell/src/PanelView.cpp 2011-12-14 17:24:33 +0000
1222@@ -621,6 +621,13 @@
1223 }
1224
1225 void
1226+PanelView::SetMenuShowTimings(int fadein, int fadeout, int discovery,
1227+ int discovery_fadein, int discovery_fadeout)
1228+{
1229+ _menu_view->SetMenuShowTimings(fadein, fadeout, discovery, discovery_fadein, discovery_fadeout);
1230+}
1231+
1232+void
1233 PanelView::SetOpacityMaximizedToggle(bool enabled)
1234 {
1235 if (_opacity_maximized_toggle != enabled)
1236
1237=== modified file 'plugins/unityshell/src/PanelView.h'
1238--- plugins/unityshell/src/PanelView.h 2011-12-14 16:18:41 +0000
1239+++ plugins/unityshell/src/PanelView.h 2011-12-14 17:24:33 +0000
1240@@ -71,6 +71,8 @@
1241
1242 void SetOpacity(float opacity);
1243 void SetOpacityMaximizedToggle(bool enabled);
1244+ void SetMenuShowTimings(int fadein, int fadeout, int discovery,
1245+ int discovery_fadein, int discovery_fadeout);
1246
1247 void TrackMenuPointer();
1248
1249
1250=== modified file 'plugins/unityshell/src/unityshell.cpp'
1251--- plugins/unityshell/src/unityshell.cpp 2011-12-14 16:18:41 +0000
1252+++ plugins/unityshell/src/unityshell.cpp 2011-12-14 17:24:33 +0000
1253@@ -240,6 +240,11 @@
1254 optionSetUrgentAnimationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));
1255 optionSetPanelOpacityNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));
1256 optionSetPanelOpacityMaximizedToggleNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));
1257+ optionSetMenusFadeinNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));
1258+ optionSetMenusFadeoutNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));
1259+ optionSetMenusDiscoveryDurationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));
1260+ optionSetMenusDiscoveryFadeinNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));
1261+ optionSetMenusDiscoveryFadeoutNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));
1262 optionSetLauncherOpacityNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));
1263 optionSetIconSizeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));
1264 optionSetAutohideAnimationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));
1265@@ -2025,6 +2030,17 @@
1266 case UnityshellOptions::PanelOpacityMaximizedToggle:
1267 panel_controller_->SetOpacityMaximizedToggle(optionGetPanelOpacityMaximizedToggle());
1268 break;
1269+ case UnityshellOptions::MenusFadein:
1270+ case UnityshellOptions::MenusFadeout:
1271+ case UnityshellOptions::MenusDiscoveryFadein:
1272+ case UnityshellOptions::MenusDiscoveryFadeout:
1273+ case UnityshellOptions::MenusDiscoveryDuration:
1274+ panel_controller_->SetMenuShowTimings(optionGetMenusFadein(),
1275+ optionGetMenusFadeout(),
1276+ optionGetMenusDiscoveryDuration(),
1277+ optionGetMenusDiscoveryFadein(),
1278+ optionGetMenusDiscoveryFadeout());
1279+ break;
1280 case UnityshellOptions::LauncherOpacity:
1281 launcher.SetBackgroundAlpha(optionGetLauncherOpacity());
1282 break;
1283@@ -2412,6 +2428,11 @@
1284 /* Setup panel */
1285 timer.Reset();
1286 panel_controller_.reset(new panel::Controller());
1287+ panel_controller_->SetMenuShowTimings(optionGetMenusFadein(),
1288+ optionGetMenusFadeout(),
1289+ optionGetMenusDiscoveryDuration(),
1290+ optionGetMenusDiscoveryFadein(),
1291+ optionGetMenusDiscoveryFadeout());
1292 LOG_INFO(logger) << "initLauncher-Panel " << timer.ElapsedSeconds() << "s";
1293
1294 /* Setup Places */
1295
1296=== modified file 'plugins/unityshell/unityshell.xml.in'
1297--- plugins/unityshell/unityshell.xml.in 2011-12-08 04:13:56 +0000
1298+++ plugins/unityshell/unityshell.xml.in 2011-12-14 17:24:33 +0000
1299@@ -342,12 +342,52 @@
1300 </desc>
1301 </option>
1302
1303- <option name="show_desktop_icon" type="bool">
1304+ <option name="show_desktop_icon" type="bool">
1305 <_short>Show "Desktop Icon" in the launcher</_short>
1306 <_long>Enable/Disable "Show Dekstop icon" in the launcher.</_long>
1307 <default>false</default>
1308 </option>
1309
1310+ <option name="menus_fadein" type="int">
1311+ <_short>Menus Fade-in duration</_short>
1312+ <_long>Duration (in milliseconds) of the menus fade-in animation, used when the mouse goes over the top-panel.</_long>
1313+ <min>0</min>
1314+ <max>1000</max>
1315+ <default>100</default>
1316+ </option>
1317+
1318+ <option name="menus_fadeout" type="int">
1319+ <_short>Menus fade-out duration</_short>
1320+ <_long>Duration (in milliseconds) of the menus fade-out animation, used when the mouse goes over the top-panel.</_long>
1321+ <min>0</min>
1322+ <max>1000</max>
1323+ <default>120</default>
1324+ </option>
1325+
1326+ <option name="menus_discovery_duration" type="int">
1327+ <_short>Menus discovery duration</_short>
1328+ <_long>How many seconds the menus should be shown when a new application has been launched.</_long>
1329+ <min>0</min>
1330+ <max>10</max>
1331+ <default>2</default>
1332+ </option>
1333+
1334+ <option name="menus_discovery_fadein" type="int">
1335+ <_short>Menus discovery fade-in duration</_short>
1336+ <_long>Duration (in milliseconds) of the menus fade-in animation, used when the menus of a new launched application have been shown.</_long>
1337+ <min>0</min>
1338+ <max>1000</max>
1339+ <default>200</default>
1340+ </option>
1341+
1342+ <option name="menus_discovery_fadeout" type="int">
1343+ <_short>Menus discovery fade-out duration</_short>
1344+ <_long>Duration (in milliseconds) of the menus fade-out animation, used when the menus of a new launched application have been shown.</_long>
1345+ <min>0</min>
1346+ <max>1000</max>
1347+ <default>300</default>
1348+ </option>
1349+
1350 </group>
1351 </options>
1352 </plugin>
1353
1354=== modified file 'tests/CMakeLists.txt'
1355--- tests/CMakeLists.txt 2011-12-14 16:18:41 +0000
1356+++ tests/CMakeLists.txt 2011-12-14 17:24:33 +0000
1357@@ -99,6 +99,7 @@
1358
1359 # The actual test executable (xless) - do not put anything that requires X in here
1360 add_executable(test-gtest-xless
1361+ test_animator.cpp
1362 test_glib_signals.cpp
1363 test_glib_signals_utils.cpp
1364 test_glib_signals_utils.h
1365@@ -107,6 +108,8 @@
1366 test_introspection.cpp
1367 test_main_xless.cpp
1368 ${UNITY_SRC}/AbstractLauncherIcon.h
1369+ ${UNITY_SRC}/Animator.cpp
1370+ ${UNITY_SRC}/Animator.h
1371 ${UNITY_SRC}/DebugDBusInterface.h
1372 ${UNITY_SRC}/DebugDBusInterface.cpp
1373 ${UNITY_SRC}/XPathQueryPart.h
1374
1375=== added file 'tests/test_animator.cpp'
1376--- tests/test_animator.cpp 1970-01-01 00:00:00 +0000
1377+++ tests/test_animator.cpp 2011-12-14 17:24:33 +0000
1378@@ -0,0 +1,295 @@
1379+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
1380+/*
1381+ * Copyright (C) 2011 Canonical Ltd
1382+ *
1383+ * This program is free software: you can redistribute it and/or modify
1384+ * it under the terms of the GNU General Public License version 3 as
1385+ * published by the Free Software Foundation.
1386+ *
1387+ * This program is distributed in the hope that it will be useful,
1388+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1389+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1390+ * GNU General Public License for more details.
1391+ *
1392+ * You should have received a copy of the GNU General Public License
1393+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1394+ *
1395+ * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com>
1396+ */
1397+
1398+#include <gtest/gtest.h>
1399+
1400+#include "Animator.h"
1401+#include "test_utils.h"
1402+
1403+using namespace std;
1404+using namespace unity;
1405+
1406+namespace
1407+{
1408+
1409+class TestAnimator : public ::testing::Test
1410+{
1411+public:
1412+ TestAnimator() :
1413+ test_animator_(100),
1414+ n_steps_(0),
1415+ current_progress_(0.0f),
1416+ got_update_(false),
1417+ half_reached_(false),
1418+ started_(false),
1419+ stopped_(false),
1420+ ended_(false)
1421+ {
1422+ test_animator_.animation_started.connect([&started_]() {
1423+ started_ = true;
1424+ });
1425+
1426+ test_animator_.animation_ended.connect([&ended_]() {
1427+ ended_ = true;
1428+ });
1429+
1430+ test_animator_.animation_stopped.connect([&stopped_](double progress) {
1431+ stopped_ = true;
1432+ });
1433+
1434+ test_animator_.animation_updated.connect([&](double progress) {
1435+ n_steps_++;
1436+ current_progress_ = progress;
1437+ got_update_ = true;
1438+
1439+ if (progress >= 0.5f)
1440+ half_reached_ = true;
1441+ });
1442+ }
1443+
1444+protected:
1445+ void ResetValues()
1446+ {
1447+ n_steps_ = 0;
1448+ current_progress_ = 0.0f;
1449+ started_ = false;
1450+ stopped_ = false;
1451+ ended_ = false;
1452+ got_update_ = false;
1453+ half_reached_ = false;
1454+ }
1455+
1456+ Animator test_animator_;
1457+ unsigned int n_steps_;
1458+ double current_progress_;
1459+ bool got_update_;
1460+ bool half_reached_;
1461+ bool started_;
1462+ bool stopped_;
1463+ bool ended_;
1464+};
1465+
1466+TEST_F(TestAnimator, ConstructDestroy)
1467+{
1468+ bool stopped = false;
1469+ double progress = 0.0f;
1470+
1471+ {
1472+ Animator tmp_animator(200, 25);
1473+
1474+ EXPECT_EQ(tmp_animator.GetDuration(), 200);
1475+ EXPECT_EQ(tmp_animator.GetRate(), 25);
1476+
1477+ bool got_update = false;
1478+ tmp_animator.animation_updated.connect([&progress, &got_update](double p) {
1479+ progress = p;
1480+ got_update = true;
1481+ });
1482+
1483+ tmp_animator.animation_stopped.connect([&stopped](double p) {
1484+ stopped = true;
1485+ });
1486+
1487+ tmp_animator.Start();
1488+
1489+ Utils::WaitUntil(got_update);
1490+
1491+ EXPECT_EQ(tmp_animator.IsRunning(), true);
1492+ EXPECT_GT(progress, 0.0f);
1493+ EXPECT_EQ(stopped, false);
1494+ }
1495+
1496+ EXPECT_EQ(stopped, true);
1497+}
1498+
1499+TEST_F(TestAnimator, SetGetValues)
1500+{
1501+ test_animator_.SetRate(30);
1502+ EXPECT_EQ(test_animator_.GetRate(), 30);
1503+
1504+ test_animator_.SetDuration(100);
1505+ EXPECT_EQ(test_animator_.GetDuration(), 100);
1506+
1507+ EXPECT_EQ(test_animator_.GetProgress(), 0.0f);
1508+ EXPECT_EQ(test_animator_.IsRunning(), false);
1509+}
1510+
1511+TEST_F(TestAnimator, SimulateStep)
1512+{
1513+ test_animator_.DoStep();
1514+ EXPECT_EQ(test_animator_.IsRunning(), false);
1515+ EXPECT_EQ(n_steps_, 1);
1516+ EXPECT_GT(test_animator_.GetProgress(), 0.0f);
1517+ ResetValues();
1518+}
1519+
1520+TEST_F(TestAnimator, SimulateAnimation)
1521+{
1522+ test_animator_.SetRate(20);
1523+ test_animator_.SetDuration(200);
1524+ long long start_time = g_get_monotonic_time() / 1000;
1525+ test_animator_.Start();
1526+
1527+ EXPECT_EQ(started_, true);
1528+ EXPECT_EQ(test_animator_.IsRunning(), true);
1529+
1530+ Utils::WaitUntil(got_update_);
1531+ EXPECT_GT(test_animator_.GetProgress(), 0.0f);
1532+ EXPECT_EQ(test_animator_.GetProgress(), current_progress_);
1533+ EXPECT_EQ(n_steps_, 1);
1534+
1535+ Utils::WaitUntil(ended_);
1536+ EXPECT_EQ(stopped_, false);
1537+ EXPECT_EQ(ended_, true);
1538+
1539+ unsigned int expected_steps = (test_animator_.GetDuration() / 1000.0f) * test_animator_.GetRate();
1540+ EXPECT_EQ(n_steps_, ceil(expected_steps));
1541+
1542+ long long actual_time = g_get_monotonic_time() / 1000;
1543+ long long expected_end = start_time+test_animator_.GetDuration();
1544+ EXPECT_GE(actual_time, expected_end);
1545+ EXPECT_LE(actual_time-expected_end, test_animator_.GetRate()*2);
1546+
1547+ ResetValues();
1548+}
1549+
1550+TEST_F(TestAnimator, SimulateStoppedAnimation)
1551+{
1552+ test_animator_.SetRate(30);
1553+ test_animator_.SetDuration(100);
1554+ test_animator_.Start();
1555+ EXPECT_EQ(started_, true);
1556+ EXPECT_EQ(test_animator_.IsRunning(), true);
1557+
1558+ Utils::WaitUntil(half_reached_);
1559+ EXPECT_GT(test_animator_.GetProgress(), 0.5f);
1560+ EXPECT_EQ(test_animator_.GetProgress(), current_progress_);
1561+ EXPECT_EQ(test_animator_.IsRunning(), true);
1562+
1563+ test_animator_.Stop();
1564+ EXPECT_EQ(test_animator_.IsRunning(), false);
1565+ EXPECT_LT(test_animator_.GetProgress(), 1.0f);
1566+ EXPECT_EQ(stopped_, true);
1567+ EXPECT_EQ(ended_, true);
1568+
1569+ ResetValues();
1570+}
1571+
1572+TEST_F(TestAnimator, SimulateStoppedAndContinueAnimation)
1573+{
1574+ test_animator_.SetRate(30);
1575+ test_animator_.SetDuration(100);
1576+ test_animator_.Start();
1577+ EXPECT_EQ(started_, true);
1578+ EXPECT_EQ(test_animator_.IsRunning(), true);
1579+
1580+ Utils::WaitUntil(half_reached_);
1581+ test_animator_.Stop();
1582+
1583+ EXPECT_LT(test_animator_.GetProgress(), 1.0f);
1584+ EXPECT_EQ(stopped_, true);
1585+ EXPECT_EQ(ended_, true);
1586+ stopped_ = false;
1587+ ended_ = false;
1588+
1589+ long long start_time = g_get_monotonic_time() / 1000;
1590+ long long expected_end = start_time + (test_animator_.GetDuration() / current_progress_);
1591+ test_animator_.Start(test_animator_.GetProgress());
1592+ Utils::WaitUntil(ended_);
1593+ EXPECT_EQ(stopped_, false);
1594+ EXPECT_EQ(ended_, true);
1595+
1596+ long long actual_time = g_get_monotonic_time() / 1000;
1597+ EXPECT_LT(expected_end-actual_time, test_animator_.GetDuration());
1598+
1599+ ResetValues();
1600+}
1601+
1602+TEST_F(TestAnimator, SimulateOneTimeDurationStart)
1603+{
1604+ unsigned int default_duration = 100;
1605+
1606+ test_animator_.SetRate(30);
1607+ test_animator_.SetDuration(default_duration);
1608+
1609+ unsigned int one_time_duration = 75;
1610+ test_animator_.Start(one_time_duration);
1611+ EXPECT_EQ(started_, true);
1612+ EXPECT_EQ(test_animator_.IsRunning(), true);
1613+
1614+ Utils::WaitUntil(half_reached_);
1615+ EXPECT_LT(test_animator_.GetProgress(), 1.0f);
1616+ EXPECT_EQ(test_animator_.GetDuration(), one_time_duration);
1617+ EXPECT_EQ(ended_, false);
1618+
1619+ Utils::WaitUntil(ended_);
1620+ EXPECT_EQ(stopped_, false);
1621+ EXPECT_EQ(ended_, true);
1622+
1623+ EXPECT_EQ(test_animator_.GetDuration(), default_duration);
1624+
1625+ ResetValues();
1626+}
1627+
1628+TEST_F(TestAnimator, SimulateOneTimeDurationStartStop)
1629+{
1630+ unsigned int default_duration = 100;
1631+
1632+ test_animator_.SetRate(30);
1633+ test_animator_.SetDuration(default_duration);
1634+
1635+ unsigned int one_time_duration = 75;
1636+ test_animator_.Start(one_time_duration);
1637+ EXPECT_EQ(started_, true);
1638+ EXPECT_EQ(test_animator_.IsRunning(), true);
1639+
1640+ Utils::WaitUntil(half_reached_);
1641+ EXPECT_EQ(test_animator_.GetDuration(), one_time_duration);
1642+ EXPECT_EQ(ended_, false);
1643+
1644+ test_animator_.Stop();
1645+ EXPECT_EQ(stopped_, true);
1646+ EXPECT_EQ(ended_, true);
1647+ EXPECT_EQ(test_animator_.GetDuration(), default_duration);
1648+
1649+ ResetValues();
1650+}
1651+
1652+TEST_F(TestAnimator, SimulateZeroDuration)
1653+{
1654+ test_animator_.SetRate(30);
1655+ test_animator_.SetDuration(0);
1656+
1657+ EXPECT_EQ(started_, false);
1658+ EXPECT_EQ(ended_, false);
1659+ EXPECT_EQ(test_animator_.IsRunning(), false);
1660+
1661+ long long start_time = g_get_monotonic_time() / 1000;
1662+ test_animator_.Start();
1663+ EXPECT_EQ(started_, true);
1664+
1665+ Utils::WaitUntil(ended_);
1666+ EXPECT_EQ(ended_, true);
1667+
1668+ long long end_time = g_get_monotonic_time() / 1000;
1669+ EXPECT_LT(end_time - start_time, test_animator_.GetRate()*2);
1670+}
1671+
1672+
1673+} // Namespace
1674
1675=== modified file 'tests/test_lens.cpp'
1676--- tests/test_lens.cpp 2011-10-12 12:02:17 +0000
1677+++ tests/test_lens.cpp 2011-12-14 17:24:33 +0000
1678@@ -48,36 +48,11 @@
1679 {
1680 n_filters_++;
1681 }
1682-
1683- static gboolean TimeoutCallback(gpointer data)
1684- {
1685- *(bool*)data = true;
1686- return FALSE;
1687- };
1688-
1689- guint32 ScheduleTimeout(bool* timeout_reached)
1690- {
1691- return g_timeout_add_seconds(10, TimeoutCallback, timeout_reached);
1692- }
1693-
1694- void WaitUntil(bool& success)
1695- {
1696- bool timeout_reached = false;
1697- guint32 timeout_id = ScheduleTimeout(&timeout_reached);
1698-
1699- while (!success && !timeout_reached)
1700- g_main_context_iteration(g_main_context_get_thread_default(), TRUE);
1701-
1702- if (success)
1703- g_source_remove(timeout_id);
1704-
1705- EXPECT_TRUE(success);
1706- }
1707
1708 void WaitForConnected()
1709 {
1710 bool timeout_reached = false;
1711- guint32 timeout_id = ScheduleTimeout(&timeout_reached);
1712+ guint32 timeout_id = Utils::ScheduleTimeout(&timeout_reached);
1713
1714 while (!lens_->connected && !timeout_reached)
1715 {
1716@@ -93,7 +68,7 @@
1717 void WaitForModel(Model<Adaptor>* model, unsigned int n_rows)
1718 {
1719 bool timeout_reached = false;
1720- guint32 timeout_id = ScheduleTimeout(&timeout_reached);
1721+ guint32 timeout_id = Utils::ScheduleTimeout(&timeout_reached);
1722
1723 while (model->count != n_rows && !timeout_reached)
1724 {
1725@@ -229,7 +204,7 @@
1726 lens_->activated.connect(sigc::slot<void, std::string const&, HandledType,Lens::Hints const&>(activated_cb));
1727
1728 lens_->Activate(uri);
1729- WaitUntil(activated);
1730+ Utils::WaitUntil(activated);
1731 }
1732
1733 TEST_F(TestLens, TestPreview)
1734@@ -262,7 +237,7 @@
1735 lens_->preview_ready.connect(sigc::slot<void, std::string const&, Preview::Ptr>(preview_cb));
1736
1737 lens_->Preview(uri);
1738- WaitUntil(previewed);
1739+ Utils::WaitUntil(previewed);
1740 }
1741
1742 TEST_F(TestLens, TestFilterSync)
1743
1744=== modified file 'tests/test_utils.h'
1745--- tests/test_utils.h 2011-07-25 10:54:25 +0000
1746+++ tests/test_utils.h 2011-12-14 17:24:33 +0000
1747@@ -31,6 +31,32 @@
1748 if (model.count == n_rows)
1749 g_source_remove(timeout_id);
1750 }
1751+
1752+ static void WaitUntil(bool& success, unsigned int max_wait = 10)
1753+ {
1754+ bool timeout_reached = false;
1755+ guint32 timeout_id = ScheduleTimeout(&timeout_reached, max_wait);
1756+
1757+ while (!success && !timeout_reached)
1758+ g_main_context_iteration(g_main_context_get_thread_default(), TRUE);
1759+
1760+ if (success)
1761+ g_source_remove(timeout_id);
1762+
1763+ EXPECT_TRUE(success);
1764+ }
1765+
1766+ static guint32 ScheduleTimeout(bool* timeout_reached, unsigned int timeout_duration = 10)
1767+ {
1768+ return g_timeout_add_seconds(timeout_duration, TimeoutCallback, timeout_reached);
1769+ }
1770+
1771+private:
1772+ static gboolean TimeoutCallback(gpointer data)
1773+ {
1774+ *(bool*)data = true;
1775+ return FALSE;
1776+ };
1777 };
1778
1779 }