1
// BezelResultsDrawingArea.cs
3
// Copyright (C) 2008 GNOME-Do
5
// This program is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
// GNU General Public License for more details.
15
// You should have received a copy of the GNU General Public License
16
// along with this program. If not, see <http://www.gnu.org/licenses/>.
20
using System.Collections.Generic;
21
using System.Threading;
30
namespace Do.Interface.AnimationBase
34
public class BezelResultsDrawingArea : Gtk.DrawingArea
37
const int SurfaceHeight = 20;
38
const int IconSize = 16;
39
const int FadeTime = 100;
43
Dictionary <Do.Universe.Item, Surface> surface_buffer;
44
Surface highlight_surface, backbuffer, child_inout_surface, triplebuffer;
47
double scroll_offset, highlight_offset, child_scroll_offset;
48
int cursor, prev_cursor, delta;
49
uint timer, delta_reset;
51
Cairo.Color odd_color, even_color;
53
Do.Universe.Item[] results;
56
public Do.Universe.Item[] Results {
61
if (results != null && value != null && results.GetHashCode () == value.GetHashCode ())
77
int oldStart = StartResult;
80
if (oldStart == StartResult) {
81
highlight_offset -= value-prev_cursor;
83
scroll_offset += value-prev_cursor;
89
private int StartResult {
91
int result = Math.Max (Cursor - (num_results / 2), 0);
94
while (result+num_results > results.Length && result > 1)
100
private bool AnimationNeeded {
102
return CursorMoveNeeded || ScrollNeeded || ChildScrollNeeded;
106
private bool CursorMoveNeeded {
108
return highlight_offset != 0;
112
private bool ScrollNeeded {
114
return scroll_offset != 0;
118
private bool ChildScrollNeeded {
120
return child_scroll_offset != 0;
124
public BezelResultsDrawingArea(int numberResults, int width) : base ()
126
num_results = numberResults;
127
surface_buffer = new Dictionary <Do.Universe.Item,Surface> ();
130
height = num_results * SurfaceHeight;
131
SetSizeRequest (width, height);
133
odd_color = new Cairo.Color (0.0, 0.0, 0.0, 0.0);
134
even_color = new Cairo.Color (.2, .2, .2, .3);
136
DoubleBuffered = false;
139
private void AnimatedDraw ()
141
if (!IsDrawable || timer > 0) return;
145
if (!AnimationNeeded)
148
delta_time = DateTime.Now;
149
timer = GLib.Timeout.Add (20, delegate {
150
double change = DateTime.Now.Subtract (delta_time).TotalMilliseconds / FadeTime;
151
delta_time = DateTime.Now;
153
double move = Math.Max (change*delta, change);
156
if (scroll_offset > 0)
157
scroll_offset = Math.Max (0, scroll_offset - move);
159
scroll_offset = Math.Min (0, scroll_offset + move);
162
if (CursorMoveNeeded) {
163
if (highlight_offset > 0)
164
highlight_offset = Math.Max (0, highlight_offset - move);
166
highlight_offset = Math.Min (0, highlight_offset + move);
169
if (ChildScrollNeeded) {
170
if (child_scroll_offset > 0)
171
child_scroll_offset = Math.Max (0, child_scroll_offset - change);
173
child_scroll_offset = Math.Min (0, child_scroll_offset + change);
178
if (!AnimationNeeded) {
181
GLib.Source.Remove (delta_reset);
182
delta_reset = GLib.Timeout.Add (50, delegate {
198
foreach (Surface s in surface_buffer.Values)
199
(s as IDisposable).Dispose ();
201
surface_buffer = new Dictionary<Do.Universe.Item,Surface> ();
204
private void Paint ()
206
if (!IsDrawable) return;
208
Context cr = Gdk.CairoHelper.Create (GdkWindow);
210
if (backbuffer == null) {
211
backbuffer = cr.Target.CreateSimilar (cr.Target.Content, width, height);
215
DrawContextOnSurface (backbuffer);
216
if (child_scroll_offset == 0) {
218
cr.SetSource (backbuffer);
219
cr.Operator = Operator.Source;
222
if (triplebuffer == null) {
223
triplebuffer = cr.Target.CreateSimilar (cr.Target.Content, width, height);
227
if (child_scroll_offset > 0) {
228
old_x = (int)(-width*(1-child_scroll_offset));
231
old_x = (int)(-width*(-1-child_scroll_offset));
235
DrawSlideContexts (child_inout_surface, backbuffer, triplebuffer, old_x, new_x);
237
cr.SetSource (triplebuffer);
238
cr.Operator = Operator.Source;
242
(cr as IDisposable).Dispose ();
245
private void DrawSlideContexts (Surface old_surface, Surface new_surface, Surface target_surface,
246
int old_x, int new_x)
248
Context cr = new Context (target_surface);
250
cr.Operator = Operator.Source;
251
cr.SetSource (old_surface, old_x, 0);
254
cr.Operator = Operator.Over;
255
cr.SetSource (new_surface, new_x, 0);
258
(cr as IDisposable).Dispose ();
261
private void DrawContextOnSurface (Surface sr)
263
Context cr = new Context (sr);
264
cr.Operator = Operator.Source;
265
cr.Rectangle (0, 0, width, height);
266
cr.Color = new Cairo.Color (0, 0, 0, .9);
268
cr.Operator = Operator.Over;
270
if (Results != null) {
271
int start_result = StartResult-(int) Math.Ceiling (scroll_offset);
272
RenderHighlight (cr);
273
for (int i = start_result; i < start_result+num_results+1 && i < Results.Length; i++) {
278
(cr as IDisposable).Dispose ();
286
public void InitChildInAnimation ()
288
if (child_inout_surface == null) {
289
Context cr = Gdk.CairoHelper.Create (GdkWindow);
290
child_inout_surface = cr.Target.CreateSimilar (cr.Target.Content, width, height);
291
(cr as IDisposable).Dispose ();
293
DrawContextOnSurface (child_inout_surface);
294
child_scroll_offset = 1;
297
public void InitChildOutAnimation ()
299
if (child_inout_surface == null) {
300
Context cr = Gdk.CairoHelper.Create (GdkWindow);
301
child_inout_surface = cr.Target.CreateSimilar (cr.Target.Content, width, height);
302
(cr as IDisposable).Dispose ();
304
DrawContextOnSurface (child_inout_surface);
305
child_scroll_offset = -1;
308
protected override bool OnExposeEvent (EventExpose evnt)
311
return base.OnExposeEvent (evnt);
314
void BufferItem (Do.Universe.Item item)
319
Context cr = Gdk.CairoHelper.Create (GdkWindow);
320
Surface surface = cr.Target.CreateSimilar (cr.Target.Content, width, SurfaceHeight);
321
Context cr2 = new Context (surface);
322
cr2.Rectangle (0, 0, width, SurfaceHeight);
323
cr2.Color = new Cairo.Color (0, 0, 0, 0);
324
cr2.Operator = Operator.Source;
326
cr2.Operator = Operator.Over;
328
Gdk.Pixbuf pixbuf = IconProvider.PixbufFromIconName (item.Icon, IconSize);
329
Gdk.CairoHelper.SetSourcePixbuf (cr2, pixbuf, 2, 2);
332
Pango.Layout layout = new Pango.Layout (this.PangoContext);
333
layout.Width = Pango.Units.FromPixels (width - IconSize - 10);
334
layout.Ellipsize = Pango.EllipsizeMode.End;
335
layout.SetMarkup ("<span foreground=\"#ffffff\">"+item.Name+"</span>");
336
layout.FontDescription = Pango.FontDescription.FromString ("normal bold");
337
layout.FontDescription.AbsoluteSize = Pango.Units.FromPixels (10);
339
cr2.MoveTo (IconSize + 6, 4);
340
Pango.CairoHelper.ShowLayout (cr2, layout);
342
surface_buffer[item] = surface;
344
layout.FontDescription.Dispose ();
345
(cr2 as IDisposable).Dispose ();
346
(cr as IDisposable).Dispose ();
349
Surface GetHighlightSource ()
351
if (highlight_surface == null) {
352
Context cr = Gdk.CairoHelper.Create (GdkWindow);
353
highlight_surface = cr.Target.CreateSimilar (cr.Target.Content, width, SurfaceHeight);
355
Context cr2 = new Context (highlight_surface);
356
LinearGradient grad = new LinearGradient (0, 0, 0, SurfaceHeight);
358
grad.AddColorStop (0, new Cairo.Color (.2, .48, .81, 1));
359
grad.AddColorStop (1, new Cairo.Color (0, .21, .57, 1));
362
cr2.Rectangle (0, 0, width, SurfaceHeight);
365
(cr as IDisposable).Dispose ();
366
(cr2 as IDisposable).Dispose ();
368
return highlight_surface;
371
void RenderItem (Context cr, int item)
373
int offset = (int) (SurfaceHeight*scroll_offset);
374
if (!surface_buffer.ContainsKey (Results[item])) {
375
BufferItem (Results[item]);
378
cr.Rectangle (0, offset+(item-StartResult)*SurfaceHeight, width, SurfaceHeight);
379
cr.Color = (item % 2 == 1) ? odd_color : even_color;
380
cr.Operator = Operator.DestOver;
383
cr.Operator = Operator.Over;
385
cr.SetSource (surface_buffer[Results[item]], 0, offset+(item-StartResult)*SurfaceHeight);
389
void RenderHighlight (Context cr)
391
int offset = (int) (SurfaceHeight*highlight_offset);
392
cr.Rectangle (0, offset+(Cursor-StartResult)*SurfaceHeight, width, SurfaceHeight);
393
cr.SetSource (GetHighlightSource (), 0, offset+(Cursor-StartResult)*SurfaceHeight);