Merge lp:~3v1n0/unity/fading-panelmenu into lp:unity

Proposed by Marco Trevisan (Treviño)
Status: Merged
Merged at revision: 1642
Proposed branch: lp:~3v1n0/unity/fading-panelmenu
Merge into: lp:unity
Diff against target: 1253 lines (+677/-141)
12 files modified
plugins/unityshell/src/Animator.cpp (+133/-0)
plugins/unityshell/src/Animator.h (+70/-0)
plugins/unityshell/src/PanelIndicatorEntryView.cpp (+80/-26)
plugins/unityshell/src/PanelIndicatorEntryView.h (+15/-2)
plugins/unityshell/src/PanelIndicatorsView.cpp (+26/-7)
plugins/unityshell/src/PanelIndicatorsView.h (+9/-2)
plugins/unityshell/src/PanelMenuView.cpp (+274/-94)
plugins/unityshell/src/PanelMenuView.h (+10/-0)
plugins/unityshell/src/PanelView.h (+1/-1)
plugins/unityshell/src/WindowButtons.cpp (+46/-7)
plugins/unityshell/src/WindowButtons.h (+9/-2)
tests/CMakeLists.txt (+4/-0)
To merge this branch: bzr merge lp:~3v1n0/unity/fading-panelmenu
Reviewer Review Type Date Requested Status
Neil J. Patel (community) Approve
John Lea Pending
Unity Team Pending
Review via email: mp+77169@code.launchpad.net

Description of the change

As requested on bug #691776 I'm finally applying a fast fade effect when the panel switches from the title to the menus state.
The fade effect is currently set to last 100ms on fade-in and 120ms on fade-out as defined with design (in the person of John Lea) and is applied to menus, title (generating a cross-fade for maximized windows) and title's gradient (to smoothly change its values).

To perform the animations I've written a new Animator class which is based on the work of Andrea Cimitan with the ayatana scrollbars, and basically is a timer that is able to call a callback function every few ms.
I would have used the nux::TimerHandler instead (using the AddPeriodicTimerHandler method), but it doesn't seem to work as it should, and it actually results slower.

In this branch I also introduced some minor tweaks to PanelIndicatorEntryView that now is typed and has different features for Menus and pure Indicators. Menus basically don't support the scroll event and the secondary-activate so they consider the middle-click as a standard click to open a menu (as it is with the Gtk). Indicators are like before but they also now support a feedback notification for secondary activation (they reduce their opacity on mouse-down).

Crossfade effect between different titles when the active window changes is planned for the future (next cycle? :))

To post a comment you must log in.
Revision history for this message
Andrea Cimitan (cimi) wrote :

Marco, don't propose a review if you plan to continue hacking on it, wait until it's ready then proceed :-)
If you really want to propose it, put the status to "work in progress" until you're satisfied with the changes :-)

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

I know Andrea... But after that I proposed the request I finally found some bugs I didn't experience before, so I had to update the branch while ready for merging.

Sorry about that. Now, by the way, it should be fine for review :)

Revision history for this message
Andrea Cimitan (cimi) wrote :

thx Marco, even though I'll wait for the P branch.
I know my code was stable in the scrollbars, and you ported it pretty well
into unity, but considering the high chance of race conditions and pointers
that need to be correctly handled to avoid segfaults, I think merging it can
be a bit risky, even if the code could seem fine at first glance. Let's wait
my brave mates :)

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

Well, I tested this quite a lot and there are not that much more pointers than before. I never got a seg-fault with this code. Also I just added two new poiters for the animators, but they are initialized at the beginning, so they are safe.
However I submit to your decision...

Revision history for this message
Andrea Cimitan (cimi) wrote :

also, we might want to cache the text as a cairo pattern or surface, then paint it (instead redrawing it every cycle). also, you know, I'm not happy with your decision of deciding at which point starting the animation, I think it doesn0t suit perfectly if the same can be done in callback functions

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

Well, that could be a good thing. And I also would have loved to do that. Unfortunately the gradient can't be cached since it needs to change when the opacity changes not to get a bad effect (also if keeping a so fast transition, we also use the old code which cached it, but the effect it's not the same).

The start thing, istead, is just a facility. The callback can control that, but I'd need more local variables that would be more unsafe. I'd prefer to keep that value hidden.

Revision history for this message
Neil J. Patel (njpatel) :
review: Approve
Revision history for this message
Andrea Cimitan (cimi) wrote :

Marco, why we can't cache the pattern/surface? Imagine we have the pattern/surface of the menubar with opacity 1.0, and when we want to draw the same menubar at 0.5, we set the cairo source as the cached opaque pattern and paint with alpha 0.5... What did you discover that I'm missing?

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

Ok, the opacity the title can be cached and is actually cached, but the gradient that is applied to it when a menu is shown can't (easily).
So also, if I know that there was room for optimizations, I had to focus on functionality.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'plugins/unityshell/src/Animator.cpp'
2--- plugins/unityshell/src/Animator.cpp 1970-01-01 00:00:00 +0000
3+++ plugins/unityshell/src/Animator.cpp 2011-09-28 09:56:29 +0000
4@@ -0,0 +1,133 @@
5+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
6+/*
7+ * Copyright (C) 2011 Canonical Ltd
8+ *
9+ * This program is free software: you can redistribute it and/or modify
10+ * it under the terms of the GNU General Public License version 3 as
11+ * published by the Free Software Foundation.
12+ *
13+ * This program is distributed in the hope that it will be useful,
14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+ * GNU General Public License for more details.
17+ *
18+ * You should have received a copy of the GNU General Public License
19+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
20+ *
21+ * Authored by: Marco Trevisan (Treviño) <mail@3v1n0.net>
22+ */
23+
24+#include "Animator.h"
25+
26+namespace unity
27+{
28+
29+Animator::Animator(unsigned int duration, unsigned int fps_rate)
30+{
31+ _start_time = 0;
32+ _timeout_id = 0;
33+ _progress = 0.0f;
34+ _start_progress = 0.0f;
35+ _rate = 1;
36+ _duration = 0;
37+
38+ SetDuration(duration);
39+ SetRate(fps_rate);
40+}
41+
42+Animator::~Animator()
43+{
44+ if (_timeout_id != 0)
45+ g_source_remove (_timeout_id);
46+}
47+
48+void
49+Animator::SetRate(unsigned int fps_rate)
50+{
51+ if (fps_rate != 0)
52+ _rate = 1000 / fps_rate;
53+}
54+
55+void
56+Animator::SetDuration(unsigned int duration)
57+{
58+ if (duration != 0)
59+ _duration = duration * 1000;
60+}
61+
62+unsigned int
63+Animator::GetRate()
64+{
65+ return _rate;
66+}
67+
68+unsigned int
69+Animator::GetDuration()
70+{
71+ return _duration;
72+}
73+
74+bool
75+Animator::IsRunning()
76+{
77+ return (_timeout_id != 0);
78+}
79+
80+double
81+Animator::GetProgress()
82+{
83+ return _progress;
84+}
85+
86+void
87+Animator::Start(double start_progress)
88+{
89+ if (_timeout_id == 0 && start_progress < 1.0f)
90+ {
91+ if (start_progress < 0.0f)
92+ start_progress = 0.0f;
93+
94+ _start_progress = start_progress;
95+ _progress = _start_progress;
96+ _start_time = g_get_monotonic_time();
97+ _timeout_id = g_timeout_add(_rate, (GSourceFunc) &Animator::TimerTimeOut, this);
98+ }
99+}
100+
101+void
102+Animator::Stop()
103+{
104+ if (_timeout_id != 0)
105+ {
106+ g_source_remove(_timeout_id);
107+ animation_updated.emit(_progress);
108+ animation_ended.emit();
109+ animation_stopped.emit(_progress);
110+ _timeout_id = 0;
111+ }
112+}
113+
114+gboolean
115+Animator::TimerTimeOut(Animator *self)
116+{
117+ const gint64 current_time = g_get_monotonic_time();
118+ const gint64 end_time = self->_start_time + self->_duration;
119+
120+ if (current_time < end_time && self->_progress < 1.0f)
121+ {
122+ const double diff_time = current_time - self->_start_time;
123+ self->_progress = CLAMP(self->_start_progress + (diff_time / self->_duration), 0.0f, 1.0f);
124+ self->animation_updated.emit(self->_progress);
125+
126+ return TRUE;
127+ } else {
128+ self->_progress = 1.0f;
129+ self->animation_updated.emit(1.0f);
130+ self->animation_ended.emit();
131+ self->_timeout_id = 0;
132+
133+ return FALSE;
134+ }
135+}
136+
137+} //namespace
138
139=== added file 'plugins/unityshell/src/Animator.h'
140--- plugins/unityshell/src/Animator.h 1970-01-01 00:00:00 +0000
141+++ plugins/unityshell/src/Animator.h 2011-09-28 09:56:29 +0000
142@@ -0,0 +1,70 @@
143+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
144+/*
145+ * Copyright (C) 2011 Canonical Ltd
146+ *
147+ * This program is free software: you can redistribute it and/or modify
148+ * it under the terms of the GNU General Public License version 3 as
149+ * published by the Free Software Foundation.
150+ *
151+ * This program is distributed in the hope that it will be useful,
152+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
153+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
154+ * GNU General Public License for more details.
155+ *
156+ * You should have received a copy of the GNU General Public License
157+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
158+ *
159+ * Authored by: Marco Trevisan (Treviño) <mail@3v1n0.net>
160+ */
161+
162+#ifndef UNITY_ANIMATOR_H_
163+#define UNITY_ANIMATOR_H_
164+
165+#include <Nux/Nux.h>
166+
167+namespace unity
168+{
169+
170+class FadableObject2
171+{
172+public:
173+ virtual void SetOpacity(double value) = 0;
174+ virtual double GetOpacity() = 0;
175+};
176+
177+class Animator
178+{
179+public:
180+ Animator(unsigned int duration, unsigned int fps_rate = 30);
181+ ~Animator();
182+
183+ void SetRate(unsigned int fps_rate);
184+ void SetDuration(unsigned int duration);
185+
186+ unsigned int GetRate();
187+ unsigned int GetDuration();
188+ double GetProgress();
189+ bool IsRunning();
190+
191+ void Start(double start_progress = 0.0f);
192+ void Stop();
193+
194+ sigc::signal<void> animation_started;
195+ sigc::signal<void> animation_ended;
196+
197+ sigc::signal<void, double> animation_updated;
198+ sigc::signal<void, double> animation_stopped;
199+
200+private:
201+ int64_t _start_time;
202+ unsigned int _rate;
203+ unsigned int _duration;
204+ unsigned int _timeout_id;
205+ double _start_progress;
206+ double _progress;
207+
208+ static gboolean TimerTimeOut(Animator *self);
209+};
210+
211+}
212+#endif
213
214=== modified file 'plugins/unityshell/src/PanelIndicatorEntryView.cpp'
215--- plugins/unityshell/src/PanelIndicatorEntryView.cpp 2011-09-24 10:20:59 +0000
216+++ plugins/unityshell/src/PanelIndicatorEntryView.cpp 2011-09-28 09:56:29 +0000
217@@ -54,11 +54,15 @@
218
219 PanelIndicatorEntryView::PanelIndicatorEntryView(
220 indicator::Entry::Ptr const& proxy,
221- int padding)
222+ int padding,
223+ IndicatorEntryType type)
224 : TextureArea(NUX_TRACKER_LOCATION)
225 , proxy_(proxy)
226+ , type_(type)
227 , util_cg_(CAIRO_FORMAT_ARGB32, 1, 1)
228- , padding_(padding)
229+ , texture_layer_(NULL)
230+ , padding_(padding < 0 ? 0 : padding)
231+ , opacity_(1.0f)
232 , draw_active_(false)
233 , dash_showing_(false)
234 {
235@@ -71,7 +75,8 @@
236 InputArea::mouse_up.connect(sigc::mem_fun(this, &PanelIndicatorEntryView::OnMouseUp));
237
238 InputArea::SetAcceptMouseWheelEvent(true);
239- InputArea::mouse_wheel.connect(sigc::mem_fun(this, &PanelIndicatorEntryView::OnMouseWheel));
240+ if (type_ != MENU)
241+ InputArea::mouse_wheel.connect(sigc::mem_fun(this, &PanelIndicatorEntryView::OnMouseWheel));
242
243 on_panelstyle_changed_connection_ = PanelStyle::GetDefault()->changed.connect(sigc::mem_fun(this, &PanelIndicatorEntryView::Refresh));
244 Refresh();
245@@ -83,6 +88,8 @@
246 on_indicator_updated_connection_.disconnect();
247 on_panelstyle_changed_connection_.disconnect();
248 g_signal_handler_disconnect(gtk_settings_get_default(), on_font_changed_connection_);
249+ if (texture_layer_)
250+ delete texture_layer_;
251 }
252
253 void PanelIndicatorEntryView::OnActiveChanged(bool is_active)
254@@ -109,30 +116,39 @@
255 if (proxy_->active())
256 return;
257
258+ if (((proxy_->label_visible() && proxy_->label_sensitive()) ||
259+ (proxy_->image_visible() && proxy_->image_sensitive())))
260+ {
261+ int button = nux::GetEventButton(button_flags);
262+
263+ if (button == 2 && type_ == INDICATOR)
264+ SetOpacity(0.75f);
265+ else
266+ ShowMenu(button);
267+ }
268+
269+ Refresh();
270+}
271+
272+void PanelIndicatorEntryView::OnMouseUp(int x, int y, long button_flags, long key_flags)
273+{
274+ if (proxy_->active())
275+ return;
276+
277 int button = nux::GetEventButton(button_flags);
278
279- if (((proxy_->label_visible() && proxy_->label_sensitive()) ||
280- (proxy_->image_visible() && proxy_->image_sensitive())) && button != 2)
281- {
282- ShowMenu(button);
283- }
284- Refresh();
285-}
286-
287-void PanelIndicatorEntryView::OnMouseUp(int x, int y, long button_flags, long key_flags)
288-{
289- if (proxy_->active())
290- return;
291-
292 nux::Geometry geo = GetAbsoluteGeometry();
293 int px = geo.x + x;
294 int py = geo.y + y;
295
296 if (((proxy_->label_visible() && proxy_->label_sensitive()) ||
297 (proxy_->image_visible() && proxy_->image_sensitive())) &&
298- geo.IsPointInside(px, py) && nux::GetEventButton(button_flags) == 2)
299+ button == 2 && type_ == INDICATOR)
300 {
301- proxy_->SecondaryActivate(time(NULL));
302+ if (geo.IsPointInside(px, py))
303+ proxy_->SecondaryActivate(time(NULL));
304+
305+ SetOpacity(1.0f);
306 }
307
308 Refresh();
309@@ -312,7 +328,7 @@
310 cairo_paint_with_alpha(cr, proxy_->image_sensitive() ? 1.0 : 0.5);
311
312 cairo_pattern_t* pat = cairo_pop_group(cr);
313-
314+
315 cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f);
316 cairo_rectangle(cr, x, y, width, height);
317 cairo_mask(cr, pat);
318@@ -387,21 +403,43 @@
319 rop.Blend = true;
320 rop.SrcBlend = GL_ONE;
321 rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA;
322- nux::TextureLayer* texture_layer = new nux::TextureLayer(texture2D->GetDeviceTexture(),
323- texxform,
324- nux::color::White,
325- true,
326- rop);
327- SetPaintLayer(texture_layer);
328+
329+ if (texture_layer_)
330+ delete texture_layer_;
331+
332+ texture_layer_ = new nux::TextureLayer(texture2D->GetDeviceTexture(), texxform,
333+ nux::color::White, true, rop);
334+ SetPaintLayer(texture_layer_);
335
336 texture2D->UnReference();
337- delete texture_layer;
338
339 NeedRedraw();
340
341 refreshed.emit(this);
342 }
343
344+void PanelIndicatorEntryView::Draw(nux::GraphicsEngine& GfxContext, bool force_draw)
345+{
346+ if (opacity_ == 1.0f)
347+ {
348+ TextureArea::Draw(GfxContext, force_draw);
349+ return;
350+ }
351+
352+ auto geo = GetGeometry();
353+ GfxContext.PushClippingRectangle(geo);
354+
355+ if (texture_layer_)
356+ {
357+ nux::TexCoordXForm texxform;
358+ GfxContext.QRP_1Tex(geo.x, geo.y, geo.width, geo.height,
359+ texture_layer_->GetDeviceTexture(), texxform,
360+ nux::color::White * opacity_);
361+ }
362+
363+ GfxContext.PopClippingRectangle();
364+}
365+
366 void PanelIndicatorEntryView::DashShown()
367 {
368 dash_showing_ = true;
369@@ -414,6 +452,22 @@
370 Refresh();
371 }
372
373+void PanelIndicatorEntryView::SetOpacity(double opacity)
374+{
375+ opacity = CLAMP(opacity, 0.0f, 1.0f);
376+
377+ if (opacity_ != opacity)
378+ {
379+ opacity_ = opacity;
380+ NeedRedraw();
381+ }
382+}
383+
384+double PanelIndicatorEntryView::GetOpacity()
385+{
386+ return opacity_;
387+}
388+
389 const gchar* PanelIndicatorEntryView::GetName()
390 {
391 if (proxy_->IsUnused())
392
393=== modified file 'plugins/unityshell/src/PanelIndicatorEntryView.h'
394--- plugins/unityshell/src/PanelIndicatorEntryView.h 2011-09-20 16:23:55 +0000
395+++ plugins/unityshell/src/PanelIndicatorEntryView.h 2011-09-28 09:56:29 +0000
396@@ -38,8 +38,14 @@
397 class PanelIndicatorEntryView : public nux::TextureArea, public unity::Introspectable
398 {
399 public:
400- PanelIndicatorEntryView(indicator::Entry::Ptr const& proxy,
401- int padding = 5);
402+ typedef enum {
403+ INDICATOR,
404+ MENU,
405+ OTHER
406+ } IndicatorEntryType;
407+
408+ PanelIndicatorEntryView(indicator::Entry::Ptr const& proxy, int padding = 5,
409+ IndicatorEntryType type = INDICATOR);
410 ~PanelIndicatorEntryView();
411
412 void Refresh();
413@@ -47,6 +53,8 @@
414 void Activate(int button = 1);
415 void Unactivate();
416 bool GetShowNow();
417+ void SetOpacity(double alpha);
418+ double GetOpacity();
419
420 void GetGeometryForSync(indicator::EntryLocationMap& locations);
421 bool IsEntryValid() const;
422@@ -60,13 +68,18 @@
423 const gchar* GetName();
424 void AddProperties(GVariantBuilder* builder);
425
426+ virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw);
427+
428 sigc::signal<void, PanelIndicatorEntryView*, bool> active_changed;
429 sigc::signal<void, PanelIndicatorEntryView*> refreshed;
430
431 private:
432 unity::indicator::Entry::Ptr proxy_;
433+ IndicatorEntryType type_;
434 nux::CairoGraphics util_cg_;
435+ nux::TextureLayer* texture_layer_;
436 int padding_;
437+ double opacity_;
438 bool draw_active_;
439 bool dash_showing_;
440 gulong on_font_changed_connection_;
441
442=== modified file 'plugins/unityshell/src/PanelIndicatorsView.cpp'
443--- plugins/unityshell/src/PanelIndicatorsView.cpp 2011-09-26 08:38:05 +0000
444+++ plugins/unityshell/src/PanelIndicatorsView.cpp 2011-09-28 09:56:29 +0000
445@@ -41,6 +41,7 @@
446 PanelIndicatorsView::PanelIndicatorsView()
447 : View(NUX_TRACKER_LOCATION)
448 , layout_(NULL)
449+, opacity_(1.0f)
450 {
451 LOG_DEBUG(logger) << "Indicators View Added: ";
452 layout_ = new nux::HLayout("", NUX_TRACKER_LOCATION);
453@@ -223,16 +224,13 @@
454 }
455
456 PanelIndicatorEntryView *
457-PanelIndicatorsView::AddEntry(indicator::Entry::Ptr const& entry, int padding, IndicatorEntryPosition pos)
458+PanelIndicatorsView::AddEntry(indicator::Entry::Ptr const& entry, int padding,
459+ IndicatorEntryPosition pos, IndicatorEntryType type)
460 {
461- PanelIndicatorEntryView *view;
462+ auto view = new PanelIndicatorEntryView(entry, padding, type);
463 int entry_pos = pos;
464
465- if (padding > -1)
466- view = new PanelIndicatorEntryView(entry, padding);
467- else
468- view = new PanelIndicatorEntryView(entry);
469-
470+ view->SetOpacity(opacity_);
471 view->refreshed.connect(sigc::mem_fun(this, &PanelIndicatorsView::OnEntryRefreshed));
472
473 if (entry_pos == IndicatorEntryPosition::AUTO)
474@@ -320,6 +318,27 @@
475 entry.second->DashHidden();
476 }
477
478+double
479+PanelIndicatorsView::GetOpacity()
480+{
481+ return opacity_;
482+}
483+
484+void
485+PanelIndicatorsView::SetOpacity(double opacity)
486+{
487+ opacity = CLAMP(opacity, 0.0f, 1.0f);
488+
489+ for (auto entry: entries_)
490+ entry.second->SetOpacity(opacity);
491+
492+ if (opacity_ != opacity)
493+ {
494+ opacity_ = opacity;
495+ NeedRedraw();
496+ }
497+}
498+
499 const gchar* PanelIndicatorsView::GetName()
500 {
501 return "IndicatorsView";
502
503=== modified file 'plugins/unityshell/src/PanelIndicatorsView.h'
504--- plugins/unityshell/src/PanelIndicatorsView.h 2011-09-20 00:39:21 +0000
505+++ plugins/unityshell/src/PanelIndicatorsView.h 2011-09-28 09:56:29 +0000
506@@ -48,9 +48,12 @@
507 END = nux::NUX_LAYOUT_END,
508 } IndicatorEntryPosition;
509
510+ typedef PanelIndicatorEntryView::IndicatorEntryType IndicatorEntryType;
511+
512 PanelIndicatorEntryView* AddEntry(indicator::Entry::Ptr const& entry,
513- int padding = -1,
514- IndicatorEntryPosition pos = AUTO);
515+ int padding = 5,
516+ IndicatorEntryPosition pos = AUTO,
517+ IndicatorEntryType type = IndicatorEntryType::INDICATOR);
518 void RemoveEntry(std::string const& entry_id);
519
520 bool OnPointerMoved(int x, int y);
521@@ -70,6 +73,9 @@
522 void DashShown();
523 void DashHidden();
524
525+ void SetOpacity(double opacity);
526+ double GetOpacity();
527+
528 sigc::signal<void, PanelIndicatorEntryView*> on_indicator_updated;
529
530 protected:
531@@ -84,6 +90,7 @@
532 private:
533 typedef std::vector<indicator::Indicator::Ptr> Indicators;
534 Indicators indicators_;
535+ double opacity_;
536
537 std::map<indicator::Indicator::Ptr, std::vector<sigc::connection>> indicators_connections_;
538 };
539
540=== modified file 'plugins/unityshell/src/PanelMenuView.cpp'
541--- plugins/unityshell/src/PanelMenuView.cpp 2011-09-25 11:17:13 +0000
542+++ plugins/unityshell/src/PanelMenuView.cpp 2011-09-28 09:56:29 +0000
543@@ -46,6 +46,9 @@
544
545 #define WINDOW_TITLE_FONT_KEY "/apps/metacity/general/titlebar_font"
546
547+#define PANEL_ENTRIES_FADEIN 100
548+#define PANEL_ENTRIES_FADEOUT 120
549+
550 namespace unity
551 {
552
553@@ -78,7 +81,9 @@
554 _active_moved_id(0),
555 _update_show_now_id(0),
556 _place_shown_interest(0),
557- _place_hidden_interest(0)
558+ _place_hidden_interest(0),
559+ _fade_in_animator(NULL),
560+ _fade_out_animator(NULL)
561 {
562 WindowManager* win_manager;
563
564@@ -150,7 +155,19 @@
565 (UBusCallback)PanelMenuView::OnPlaceViewHidden,
566 this);
567
568+ _fade_in_animator = new Animator(PANEL_ENTRIES_FADEIN);
569+ _fade_out_animator = new Animator(PANEL_ENTRIES_FADEOUT);
570+
571+ _fade_in_animator->animation_updated.connect(sigc::mem_fun(this, &PanelMenuView::OnFadeInChanged));
572+ _fade_in_animator->animation_ended.connect(sigc::mem_fun(this, &PanelMenuView::FullRedraw));
573+ _fade_out_animator->animation_updated.connect(sigc::mem_fun(this, &PanelMenuView::OnFadeOutChanged));
574+ _fade_out_animator->animation_ended.connect(sigc::mem_fun(this, &PanelMenuView::FullRedraw));
575+
576+ SetOpacity(0.0f);
577+ _window_buttons->SetOpacity(0.0f);
578+
579 Refresh();
580+ FullRedraw();
581 }
582
583 PanelMenuView::~PanelMenuView()
584@@ -167,6 +184,12 @@
585 if (_title_layer)
586 delete _title_layer;
587
588+ if (_fade_in_animator)
589+ delete _fade_in_animator;
590+
591+ if (_fade_out_animator)
592+ delete _fade_out_animator;
593+
594 _menu_layout->UnReference();
595 _window_buttons->UnReference();
596 _panel_titlebar_grab_area->UnReference();
597@@ -311,6 +334,63 @@
598 }
599
600 void
601+PanelMenuView::OnFadeInChanged(double opacity)
602+{
603+ if (DrawMenus() && GetOpacity() != 1.0f)
604+ SetOpacity(opacity);
605+
606+ if (DrawWindowButtons() && _window_buttons->GetOpacity() != 1.0f)
607+ _window_buttons->SetOpacity(opacity);
608+
609+ NeedRedraw();
610+}
611+
612+void
613+PanelMenuView::OnFadeOutChanged(double progress)
614+{
615+ double opacity = CLAMP(1.0f - progress, 0.0f, 1.0f);
616+
617+ if (!DrawMenus() && GetOpacity() != 0.0f)
618+ SetOpacity(opacity);
619+
620+ if (!DrawWindowButtons() && _window_buttons->GetOpacity() != 0.0f)
621+ _window_buttons->SetOpacity(opacity);
622+
623+ NeedRedraw();
624+}
625+
626+bool
627+PanelMenuView::DrawMenus()
628+{
629+ if (!_is_own_window && !_places_showing && _we_control_active)
630+ {
631+ if (_is_inside || _last_active_view || _show_now_activated)
632+ {
633+ return true;
634+ }
635+ }
636+
637+ return false;
638+}
639+
640+bool
641+PanelMenuView::DrawWindowButtons()
642+{
643+ if (_places_showing)
644+ return true;
645+
646+ if (!_is_own_window && _we_control_active && _is_maximized)
647+ {
648+ if (_is_inside || _show_now_activated)
649+ {
650+ return true;
651+ }
652+ }
653+
654+ return false;
655+}
656+
657+void
658 PanelMenuView::Draw(nux::GraphicsEngine& GfxContext, bool force_draw)
659 {
660 nux::Geometry geo = GetGeometry();
661@@ -334,103 +414,155 @@
662 rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA;
663
664 nux::ColorLayer layer(nux::Color(0x00000000), true, rop);
665- gPainter.PushDrawLayer(GfxContext, GetGeometry(), &layer);
666-
667- if (_is_own_window || !_we_control_active || (_is_maximized && (_is_inside || _show_now_activated)))
668- {
669-
670- }
671- else
672- {
673- bool have_valid_entries = false;
674- for (auto entry : entries_)
675+ nux::GetPainter().PushDrawLayer(GfxContext, GetGeometry(), &layer);
676+
677+ if (_title_layer && !_is_own_window)
678+ {
679+ guint blend_alpha = 0, blend_src = 0, blend_dest = 0;
680+ bool draw_faded_title = false;
681+
682+ GfxContext.GetRenderStates().GetBlend(blend_alpha, blend_src, blend_dest);
683+
684+ if (!DrawWindowButtons() && _we_control_active &&
685+ (DrawMenus() || (GetOpacity() > 0.0f && _window_buttons->GetOpacity() == 0.0f)))
686 {
687- if (entry.second->IsEntryValid())
688+ for (auto entry : entries_)
689 {
690- have_valid_entries = true;
691- break;
692+ if (entry.second->IsEntryValid())
693+ {
694+ draw_faded_title = true;
695+ break;
696+ }
697 }
698 }
699
700- if ((_is_inside || _last_active_view || _show_now_activated) && have_valid_entries)
701+ if (draw_faded_title)
702 {
703+ bool build_gradient = false;
704+ nux::SURFACE_LOCKED_RECT lockrect;
705+ bool locked = false;
706+
707 if (_gradient_texture.IsNull())
708 {
709+ build_gradient = true;
710+ }
711+ else
712+ {
713+ if (_gradient_texture->LockRect(0, &lockrect, NULL) != OGL_OK)
714+ build_gradient = true;
715+ else
716+ locked = true;
717+
718+ if (!lockrect.pBits)
719+ {
720+ build_gradient = true;
721+
722+ if (locked)
723+ _gradient_texture->UnlockRect(0);
724+ }
725+ }
726+
727+ if (build_gradient)
728+ {
729 nux::NTextureData texture_data(nux::BITFMT_R8G8B8A8, geo.width, 1, 1);
730- nux::ImageSurface surface = texture_data.GetSurface(0);
731- nux::SURFACE_LOCKED_RECT lockrect;
732- BYTE* dest;
733- int num_row;
734-
735- _gradient_texture = nux::GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableDeviceTexture(texture_data.GetWidth(), texture_data.GetHeight(), 1, texture_data.GetFormat());
736-
737- _gradient_texture->LockRect(0, &lockrect, 0);
738-
739- dest = (BYTE*) lockrect.pBits;
740- num_row = surface.GetBlockHeight();
741-
742- for (int y = 0; y < num_row; y++)
743- {
744- for (int x = 0; x < geo.width; x++)
745- {
746- BYTE a;
747- if (x < button_width * (factor - 1))
748- {
749- a = 0xff;
750- }
751- else if (x < button_width * factor)
752- {
753- a = 255 - 255 * (((float)x - (button_width * (factor - 1))) / (float)(button_width));
754- }
755- else
756- {
757- a = 0x00;
758- }
759-
760- *(dest + y * lockrect.Pitch + 4 * x + 0) = (223 * a) / 255; //red
761- *(dest + y * lockrect.Pitch + 4 * x + 1) = (219 * a) / 255; //green
762- *(dest + y * lockrect.Pitch + 4 * x + 2) = (210 * a) / 255; //blue
763- *(dest + y * lockrect.Pitch + 4 * x + 3) = a;
764- }
765- }
766+
767+ _gradient_texture = nux::GetGraphicsDisplay()->GetGpuDevice()->
768+ CreateSystemCapableDeviceTexture(texture_data.GetWidth(),
769+ texture_data.GetHeight(), 1, texture_data.GetFormat());
770+ locked = (_gradient_texture->LockRect(0, &lockrect, NULL) == OGL_OK);
771+ }
772+
773+ int gradient_opacity = 255.0f * GetOpacity();
774+ BYTE* dest_buffer = (BYTE*) lockrect.pBits;
775+
776+ for (int x = 0; x < geo.width && dest_buffer && locked; x++)
777+ {
778+ BYTE a;
779+ if (x < button_width * (factor - 1))
780+ {
781+ a = 0xff;
782+ }
783+ else if (x < button_width * factor)
784+ {
785+ a = 0xff - gradient_opacity * (((float)x - (button_width * (factor - 1))) / (float)(button_width));
786+ }
787+ else
788+ {
789+ if (!DrawMenus())
790+ {
791+ a = 0xff - gradient_opacity;
792+ }
793+ else if (0xff - gradient_opacity > 0x55)
794+ {
795+ // If we're fading-out the title, it's better to quickly hide
796+ // the transparent right-most area
797+ a = 0xff - gradient_opacity - 0x55;
798+ }
799+ else
800+ {
801+ a = 0x00;
802+ }
803+ }
804+
805+ *(dest_buffer + 4 * x + 0) = (223 * a) / 255; //red
806+ *(dest_buffer + 4 * x + 1) = (219 * a) / 255; //green
807+ *(dest_buffer + 4 * x + 2) = (210 * a) / 255; //blue
808+ *(dest_buffer + 4 * x + 3) = a;
809+ }
810+
811+ // FIXME Nux shouldn't make unity to crash if we try to unlock a wrong rect
812+ if (locked)
813 _gradient_texture->UnlockRect(0);
814- }
815- guint alpha = 0, src = 0, dest = 0;
816
817- GfxContext.GetRenderStates().GetBlend(alpha, src, dest);
818 GfxContext.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
819
820 nux::TexCoordXForm texxform0;
821 nux::TexCoordXForm texxform1;
822
823 // Modulate the checkboard and the gradient texture
824- if (_title_layer)
825- GfxContext.QRP_2TexMod(geo.x, geo.y,
826- geo.width, geo.height,
827- _gradient_texture, texxform0,
828- nux::color::White,
829- _title_layer->GetDeviceTexture(),
830- texxform1,
831- nux::color::White);
832+ GfxContext.QRP_2TexMod(geo.x, geo.y,
833+ geo.width, geo.height,
834+ _gradient_texture, texxform0,
835+ nux::color::White,
836+ _title_layer->GetDeviceTexture(),
837+ texxform1,
838+ nux::color::White);
839
840- GfxContext.GetRenderStates().SetBlend(alpha, src, dest);
841 // The previous blend is too aggressive on the texture and therefore there
842 // is a slight loss of clarity. This fixes that
843 geo.width = button_width * (factor - 1);
844- if (_title_layer)
845- gPainter.PushDrawLayer(GfxContext, geo, _title_layer);
846+ nux::GetPainter().PushDrawLayer(GfxContext, geo, _title_layer);
847 geo = GetGeometry();
848 }
849- else
850- {
851- if (_title_layer)
852- gPainter.PushDrawLayer(GfxContext,
853- geo,
854- _title_layer);
855- }
856+ else if (!_places_showing && _window_buttons->GetOpacity() < 1.0f && _window_buttons->GetOpacity() > 0.0f)
857+ {
858+ double title_opacity = 1.0f - _window_buttons->GetOpacity();
859+
860+ if (!DrawWindowButtons())
861+ {
862+ // If we're fading-out the buttons/menus, let's fade-in quickly the title
863+ title_opacity = CLAMP(title_opacity + 0.25f, 0.0f, 1.0f);
864+ }
865+ else
866+ {
867+ // If we're fading-in the buttons/menus, let's fade-out quickly the title
868+ title_opacity = CLAMP(title_opacity - 0.25f, 0.0f, 1.0f);
869+ }
870+
871+ nux::TexCoordXForm texxform;
872+ GfxContext.QRP_1Tex(geo.x, geo.y, geo.width, geo.height,
873+ _title_layer->GetDeviceTexture(), texxform,
874+ nux::color::White * title_opacity);
875+ }
876+ else if (_window_buttons->GetOpacity() == 0.0f && _we_control_active)
877+ {
878+ nux::GetPainter().PushDrawLayer(GfxContext, geo, _title_layer);
879+ }
880+
881+ GfxContext.GetRenderStates().SetBlend(blend_alpha, blend_src, blend_dest);
882 }
883
884- gPainter.PopBackground();
885+ nux::GetPainter().PopBackground();
886
887 GfxContext.PopClippingRectangle();
888 }
889@@ -439,21 +571,54 @@
890 PanelMenuView::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw)
891 {
892 nux::Geometry geo = GetGeometry();
893+ bool draw_menus = DrawMenus();
894+ bool draw_buttons = DrawWindowButtons();
895
896 GfxContext.PushClippingRectangle(geo);
897
898- if (!_is_own_window && !_places_showing && _we_control_active)
899- {
900- if (_is_inside || _last_active_view || _show_now_activated)
901- {
902- _menu_layout->ProcessDraw(GfxContext, force_draw);
903- }
904- }
905-
906- if ((!_is_own_window && _we_control_active && _is_maximized &&
907- (_is_inside || _show_now_activated)) || _places_showing)
908- {
909- _window_buttons->ProcessDraw(GfxContext, true);
910+ if (draw_menus)
911+ {
912+ _menu_layout->ProcessDraw(GfxContext, true);
913+
914+ _fade_out_animator->Stop();
915+ _fade_in_animator->Start(GetOpacity());
916+ }
917+
918+ if (GetOpacity() != 0.0f && !draw_menus)
919+ {
920+ _menu_layout->ProcessDraw(GfxContext, true);
921+
922+ _fade_in_animator->Stop();
923+
924+ if (_fade_out_animator->GetDuration() != PANEL_ENTRIES_FADEOUT)
925+ {
926+ if (_fade_out_animator->IsRunning())
927+ _fade_out_animator->Stop();
928+
929+ _fade_out_animator->SetDuration(PANEL_ENTRIES_FADEOUT);
930+ }
931+ _fade_out_animator->Start(1.0f - GetOpacity());
932+ }
933+
934+ if (draw_buttons)
935+ {
936+ _window_buttons->ProcessDraw(GfxContext, true);
937+
938+ _fade_out_animator->Stop();
939+ _fade_in_animator->Start(_window_buttons->GetOpacity());
940+ }
941+
942+ if (_window_buttons->GetOpacity() != 0.0f && !draw_buttons)
943+ {
944+ _window_buttons->ProcessDraw(GfxContext, true);
945+ _fade_in_animator->Stop();
946+
947+ /* If we try to hide only the buttons, then use a faster fadeout */
948+ if (!_fade_out_animator->IsRunning())
949+ {
950+ _fade_out_animator->SetDuration(PANEL_ENTRIES_FADEOUT/5);
951+ _fade_out_animator->Start(1.0f - _window_buttons->GetOpacity());
952+ }
953 }
954
955 GfxContext.PopClippingRectangle();
956@@ -471,16 +636,21 @@
957 if (BAMF_IS_WINDOW(window))
958 {
959 std::vector<Window> const& our_xids = nux::XInputWindow::NativeHandleList();
960+ guint32 window_xid = bamf_window_get_xid(BAMF_WINDOW(window));
961
962- if (std::find(our_xids.begin(), our_xids.end(), bamf_window_get_xid(BAMF_WINDOW(window))) != our_xids.end())
963+ if (std::find(our_xids.begin(), our_xids.end(), window_xid) != our_xids.end())
964+ {
965 _is_own_window = true;
966- }
967-
968- if (_is_maximized)
969- {
970- BamfWindow* window = bamf_matcher_get_active_window(_matcher);
971-
972- if (BAMF_IS_WINDOW(window))
973+ return g_strdup("");
974+ }
975+
976+ if (!WindowManager::Default()->IsWindowOnCurrentDesktop(window_xid) ||
977+ WindowManager::Default()->IsWindowObscured(window_xid))
978+ {
979+ return g_strdup("");
980+ }
981+
982+ if (_is_maximized)
983 label = bamf_view_get_name(BAMF_VIEW(window));
984 }
985
986@@ -705,7 +875,7 @@
987 void
988 PanelMenuView::OnEntryAdded(unity::indicator::Entry::Ptr const& entry)
989 {
990- auto view = AddEntry(entry, 6, IndicatorEntryPosition::END);
991+ auto view = AddEntry(entry, 6, IndicatorEntryPosition::END, IndicatorEntryType::MENU);
992
993 entry->show_now_changed.connect(sigc::mem_fun(this, &PanelMenuView::UpdateShowNow));
994
995@@ -927,7 +1097,10 @@
996
997 window = bamf_matcher_get_active_window(_matcher);
998 if (BAMF_IS_WINDOW(window))
999+ {
1000 WindowManager::Default()->Close(bamf_window_get_xid(window));
1001+ NeedRedraw();
1002+ }
1003 }
1004 }
1005
1006@@ -945,7 +1118,10 @@
1007
1008 window = bamf_matcher_get_active_window(_matcher);
1009 if (BAMF_IS_WINDOW(window))
1010+ {
1011 WindowManager::Default()->Minimize(bamf_window_get_xid(window));
1012+ NeedRedraw();
1013+ }
1014 }
1015 }
1016
1017@@ -965,7 +1141,10 @@
1018
1019 window = bamf_matcher_get_active_window(_matcher);
1020 if (BAMF_IS_WINDOW(window))
1021+ {
1022 WindowManager::Default()->Restore(bamf_window_get_xid(window));
1023+ NeedRedraw();
1024+ }
1025 }
1026 }
1027
1028@@ -1113,6 +1292,7 @@
1029 * C code for callbacks
1030 */
1031 static void
1032+
1033 on_active_window_changed(BamfMatcher* matcher,
1034 BamfView* old_view,
1035 BamfView* new_view,
1036
1037=== modified file 'plugins/unityshell/src/PanelMenuView.h'
1038--- plugins/unityshell/src/PanelMenuView.h 2011-09-24 07:23:53 +0000
1039+++ plugins/unityshell/src/PanelMenuView.h 2011-09-28 09:56:29 +0000
1040@@ -30,6 +30,7 @@
1041 #include "WindowButtons.h"
1042 #include "PanelTitlebarGrabAreaView.h"
1043 #include "PluginAdapter.h"
1044+#include "Animator.h"
1045
1046 #include <libbamf/libbamf.h>
1047
1048@@ -112,6 +113,12 @@
1049 static gboolean UpdateActiveWindowPosition(PanelMenuView* self);
1050 static gboolean UpdateShowNowWithDelay(PanelMenuView* self);
1051
1052+ bool DrawMenus();
1053+ bool DrawWindowButtons();
1054+
1055+ void OnFadeInChanged(double);
1056+ void OnFadeOutChanged(double);
1057+
1058 private:
1059 BamfMatcher* _matcher;
1060
1061@@ -152,6 +159,9 @@
1062
1063 guint32 _place_shown_interest;
1064 guint32 _place_hidden_interest;
1065+
1066+ Animator* _fade_in_animator;
1067+ Animator* _fade_out_animator;
1068 };
1069
1070 }
1071
1072=== modified file 'plugins/unityshell/src/PanelView.h'
1073--- plugins/unityshell/src/PanelView.h 2011-09-20 00:39:21 +0000
1074+++ plugins/unityshell/src/PanelView.h 2011-09-28 09:56:29 +0000
1075@@ -72,7 +72,7 @@
1076 void EndFirstMenuShow();
1077
1078 void SetOpacity(float opacity);
1079-
1080+
1081 void TrackMenuPointer();
1082
1083 unsigned int GetTrayXid ();
1084
1085=== modified file 'plugins/unityshell/src/WindowButtons.cpp'
1086--- plugins/unityshell/src/WindowButtons.cpp 2011-09-26 08:38:05 +0000
1087+++ plugins/unityshell/src/WindowButtons.cpp 2011-09-28 09:56:29 +0000
1088@@ -57,7 +57,8 @@
1089 _dash_is_open(false),
1090 _mouse_is_down(false),
1091 _place_shown_interest(0),
1092- _place_hidden_interest(0)
1093+ _place_hidden_interest(0),
1094+ _opacity(1.0f)
1095 {
1096 LoadImages();
1097 UpdateDashUnmaximize();
1098@@ -104,7 +105,6 @@
1099 nux::TexCoordXForm texxform;
1100
1101 GfxContext.PushClippingRectangle(geo);
1102- nux::GetPainter().PaintBackground(GfxContext, geo);
1103
1104 if (_dash_is_open)
1105 {
1106@@ -127,9 +127,6 @@
1107 tex = _normal_tex;
1108 }
1109
1110- GfxContext.GetRenderStates().SetBlend(true);
1111- GfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER);
1112- GfxContext.GetRenderStates().SetColorMask(true, true, true, true);
1113 if (tex)
1114 GfxContext.QRP_1Tex(geo.x,
1115 geo.y,
1116@@ -137,8 +134,8 @@
1117 (float)geo.height,
1118 tex->GetDeviceTexture(),
1119 texxform,
1120- nux::color::White);
1121- GfxContext.GetRenderStates().SetBlend(false);
1122+ nux::color::White * _opacity);
1123+
1124 GfxContext.PopClippingRectangle();
1125 }
1126
1127@@ -220,6 +217,20 @@
1128 QueueDraw();
1129 }
1130
1131+ void SetOpacity(double opacity)
1132+ {
1133+ if (_opacity != opacity)
1134+ {
1135+ _opacity = opacity;
1136+ NeedRedraw();
1137+ }
1138+ }
1139+
1140+ double GetOpacity()
1141+ {
1142+ return _opacity;
1143+ }
1144+
1145 private:
1146 PanelStyle::WindowButtonType _type;
1147 nux::BaseTexture* _normal_tex;
1148@@ -232,6 +243,7 @@
1149 bool _mouse_is_down;
1150 guint32 _place_shown_interest;
1151 guint32 _place_hidden_interest;
1152+ double _opacity;
1153
1154 static void OnPlaceViewShown(GVariant* data, void* val)
1155 {
1156@@ -292,6 +304,7 @@
1157
1158 WindowButtons::WindowButtons()
1159 : HLayout("", NUX_TRACKER_LOCATION)
1160+ , _opacity(1.0f)
1161 {
1162 WindowButton* but;
1163
1164@@ -363,6 +376,32 @@
1165 return nux::HLayout::FindAreaUnderMouse(mouse_position, event_type);
1166 }
1167
1168+void
1169+WindowButtons::SetOpacity(double opacity)
1170+{
1171+ opacity = CLAMP(opacity, 0.0f, 1.0f);
1172+
1173+ for (auto area : GetChildren())
1174+ {
1175+ auto but = dynamic_cast<WindowButton*>(area);
1176+
1177+ if (but)
1178+ but->SetOpacity(opacity);
1179+ }
1180+
1181+ if (_opacity != opacity)
1182+ {
1183+ _opacity = opacity;
1184+ NeedRedraw();
1185+ }
1186+}
1187+
1188+double
1189+WindowButtons::GetOpacity()
1190+{
1191+ return _opacity;
1192+}
1193+
1194 const gchar*
1195 WindowButtons::GetName()
1196 {
1197
1198=== modified file 'plugins/unityshell/src/WindowButtons.h'
1199--- plugins/unityshell/src/WindowButtons.h 2011-09-02 14:46:22 +0000
1200+++ plugins/unityshell/src/WindowButtons.h 2011-09-28 09:56:29 +0000
1201@@ -25,7 +25,9 @@
1202
1203 #include "Introspectable.h"
1204
1205-class WindowButtons : public nux::HLayout, public unity::Introspectable
1206+namespace unity
1207+{
1208+class WindowButtons : public nux::HLayout, public Introspectable
1209 {
1210 // These are the [close][minimize][restore] buttons on the panel when there
1211 // is a maximized window
1212@@ -34,6 +36,9 @@
1213 WindowButtons();
1214 ~WindowButtons();
1215
1216+ void SetOpacity(double opacity);
1217+ double GetOpacity();
1218+
1219 sigc::signal<void> close_clicked;
1220 sigc::signal<void> minimize_clicked;
1221 sigc::signal<void> restore_clicked;
1222@@ -52,6 +57,8 @@
1223 void OnCloseClicked(nux::View *view);
1224 void OnMinimizeClicked(nux::View *view);
1225 void OnRestoreClicked(nux::View *view);
1226+
1227+ double _opacity;
1228 };
1229-
1230+}
1231 #endif
1232
1233=== modified file 'tests/CMakeLists.txt'
1234--- tests/CMakeLists.txt 2011-09-23 23:02:51 +0000
1235+++ tests/CMakeLists.txt 2011-09-28 09:56:29 +0000
1236@@ -148,6 +148,8 @@
1237 #
1238 add_executable (test-panel
1239 TestPanel.cpp
1240+ ${UNITY_SRC}/Animator.cpp
1241+ ${UNITY_SRC}/Animator.h
1242 ${UNITY_SRC}/BackgroundEffectHelper.cpp
1243 ${UNITY_SRC}/BackgroundEffectHelper.h
1244 ${UNITY_SRC}/DashSettings.cpp
1245@@ -166,6 +168,8 @@
1246 ${UNITY_SRC}/PanelTray.h
1247 ${UNITY_SRC}/Introspectable.cpp
1248 ${UNITY_SRC}/Introspectable.h
1249+ ${UNITY_SRC}/LauncherController.cpp
1250+ ${UNITY_SRC}/LauncherController.h
1251 ${UNITY_SRC}/PanelMenuView.cpp
1252 ${UNITY_SRC}/PanelMenuView.h
1253 ${UNITY_SRC}/Timer.cpp