1
// BezelGlassResults.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;
22
using System.Threading;
29
using Do.Interface.CairoUtils;
32
namespace Do.Interface.AnimationBase
36
public class BezelGlassResults : Gtk.DrawingArea
39
IBezelResultItemRenderer ItemRenderer;
41
const int FadeTime = 100;
45
int border_width, top_border_width;
46
Dictionary <Element, Surface> surface_buffer;
47
Surface highlight_surface, backbuffer, child_inout_surface, triplebuffer, background;
50
double scroll_offset, highlight_offset, child_scroll_offset, slide_offset;
51
int cursor, prev_cursor, delta;
52
uint timer, delta_reset;
56
IDoController controller;
58
IUIContext context = null;
61
IList<Element> results;
63
public int X { get; set; }
65
int BottomBorderWidth {
70
case HUDStyle.Classic:
73
throw new NotImplementedException ();
78
int SurfaceHeight { get { return ItemRenderer.Height; } }
80
private Cairo.Color BackgroundColor
85
return colors.BackgroundDark;
86
case HUDStyle.Classic:
88
using (Gtk.Style rcstyle = Gtk.Rc.GetStyle (this)) {
89
bgColor = rcstyle.BaseColors[(int) StateType.Normal];
91
return bgColor.ConvertToCairo (1);
93
throw new NotImplementedException ();
98
public string ItemTextColor {
103
case HUDStyle.Classic:
105
using (Gtk.Style rcstyle = Gtk.Rc.GetStyle (this)) {
106
bgColor = rcstyle.TextColors[(int) StateType.Normal];
108
return bgColor.ColorToHexString ();
110
throw new NotImplementedException ();
115
public string QueryColor {
120
case HUDStyle.Classic:
123
throw new NotImplementedException ();
128
public IList<Element> Results {
133
if (results != null && value != null && results.GetHashCode () == value.GetHashCode ())
137
highlight_offset = 0;
149
int oldStart = StartResult;
150
prev_cursor = cursor;
152
if (oldStart == StartResult) {
153
highlight_offset -= value-prev_cursor;
155
scroll_offset += value-prev_cursor;
161
private int StartResult {
163
int result = Math.Max (Cursor - (num_results / 2), 0);
166
while (result+num_results > results.Count && result > 1)
172
private bool AnimationNeeded {
174
return CursorMoveNeeded || ScrollNeeded || ChildScrollNeeded || SlideNeeded;
178
private bool CursorMoveNeeded {
180
return highlight_offset != 0;
184
private bool ScrollNeeded {
186
return scroll_offset != 0;
190
private bool ChildScrollNeeded {
192
return child_scroll_offset != 0;
196
private bool SlideNeeded {
198
return (visible && slide_offset != 1 || !visible && slide_offset != 0);
202
public IUIContext Context
205
IUIContext tmp = context;
211
if (value == null || !value.Results.Any ()) {
217
if (context.ParentContext != null && tmp != null &&
218
context.ParentContext.Query == tmp.Query &&
219
tmp.Results.GetHashCode () == context.ParentContext.Results.GetHashCode ()) {
220
InitChildInAnimation ();
221
} else if (tmp.ParentContext != null && context != null &&
222
tmp.ParentContext.Query == context.Query &&
223
tmp.ParentContext.Results.GetHashCode () == context.Results.GetHashCode ()) {
224
InitChildOutAnimation ();
228
Cursor = value.Cursor;
229
Results = value.Results;
230
Secondary = value.SecondaryCursors;
236
private int InternalWidth {
238
return width - 2*border_width;
242
public int[] Secondary {
248
if (secondary.Length == value.Length) {
249
for (int i = 0; i<secondary.Length; i++) {
250
if (secondary[i] != value[i]) {
260
foreach (Surface s in surface_buffer.Values)
262
surface_buffer.Clear ();
267
public BezelGlassResults(IDoController controller, int width, HUDStyle style, BezelColors colors) : base ()
269
this.controller = controller;
271
this.colors = colors;
273
case HUDStyle.Classic:
274
ItemRenderer = new BezelFullResultItemRenderer (this);
278
ItemRenderer = new BezelHalfResultItemRenderer (this);
283
surface_buffer = new Dictionary <Element,Surface> ();
284
secondary = new int[0];
286
top_border_width = 20;
288
height = num_results * SurfaceHeight + top_border_width + BottomBorderWidth;
289
SetSizeRequest (width, height);
291
DoubleBuffered = false;
293
this.Shown += delegate {
298
BezelDrawingArea.ThemeChanged += delegate {
299
if (background != null)
300
background.Destroy ();
301
if (highlight_surface != null)
302
highlight_surface.Destroy ();
303
if (child_inout_surface != null)
304
child_inout_surface.Destroy ();
305
if (triplebuffer != null)
306
triplebuffer.Destroy ();
307
if (backbuffer != null)
308
backbuffer.Destroy ();
310
highlight_surface = backbuffer = child_inout_surface = triplebuffer = background = null;
313
Realized += delegate {
314
this.GdkWindow.SetBackPixmap (null, false);
317
StyleSet += delegate {
319
GdkWindow.SetBackPixmap (null, false);
323
private void AnimatedDraw ()
325
if (!IsDrawable || timer > 0) return;
329
if (!AnimationNeeded)
332
delta_time = DateTime.Now;
333
timer = GLib.Timeout.Add (1000/100, delegate {
334
double change = (BezelDrawingArea.Animated) ? DateTime.Now.Subtract (delta_time).TotalMilliseconds / FadeTime : 10;
335
delta_time = DateTime.Now;
337
double move = Math.Max (change*delta, change);
340
if (scroll_offset > 0)
341
scroll_offset = Math.Max (0, scroll_offset - move);
343
scroll_offset = Math.Min (0, scroll_offset + move);
346
if (CursorMoveNeeded) {
347
if (highlight_offset > 0)
348
highlight_offset = Math.Max (0, highlight_offset - move);
350
highlight_offset = Math.Min (0, highlight_offset + move);
353
if (ChildScrollNeeded) {
354
if (child_scroll_offset > 0)
355
child_scroll_offset = Math.Max (0, child_scroll_offset - change*0.7);
357
child_scroll_offset = Math.Min (0, child_scroll_offset + change*0.7);
362
slide_offset = Math.Min (1, slide_offset + change*0.7);
364
slide_offset = Math.Max (0, slide_offset - change*0.7);
369
if (!AnimationNeeded) {
372
GLib.Source.Remove (delta_reset);
373
delta_reset = GLib.Timeout.Add (50, delegate {
390
foreach (Surface s in surface_buffer.Values)
393
surface_buffer = new Dictionary<Element,Surface> ();
397
private void Paint ()
399
if (!IsDrawable) return;
400
// DateTime time = DateTime.Now;
401
Context cr = Gdk.CairoHelper.Create (GdkWindow);
403
if (slide_offset == 0) {
404
cr.Operator = Operator.Source;
405
cr.Color = new Cairo.Color (0, 0, 0, 0);
407
(cr as IDisposable).Dispose ();
411
if (backbuffer == null)
412
backbuffer = cr.Target.CreateSimilar (cr.Target.Content, width, height);
415
DrawContextOnSurface (backbuffer);
416
if (child_scroll_offset == 0) {
417
cr.SetSource (backbuffer, X, -(height*(1-slide_offset)));
418
cr.Operator = Operator.Source;
421
if (triplebuffer == null) {
422
triplebuffer = cr.Target.CreateSimilar (cr.Target.Content, width, height);
426
if (child_scroll_offset > 0) {
427
old_x = (int)(-width*(1-child_scroll_offset));
430
old_x = (int)(-width*(-1-child_scroll_offset));
434
DrawSlideContexts (child_inout_surface, backbuffer, triplebuffer, old_x, new_x);
436
cr.SetSource (triplebuffer, X, - (height * (1 - slide_offset)));
437
cr.Operator = Operator.Source;
441
(cr as IDisposable).Dispose ();
445
/// Draws two surfaces offset onto a singel surface. Useful for making left/right slide animations
447
/// <param name="old_surface">
448
/// A <see cref="Surface"/>
450
/// <param name="new_surface">
451
/// A <see cref="Surface"/>
453
/// <param name="target_surface">
454
/// A <see cref="Surface"/>
456
/// <param name="old_x">
457
/// A <see cref="System.Int32"/>
459
/// <param name="new_x">
460
/// A <see cref="System.Int32"/>
462
private void DrawSlideContexts (Surface old_surface, Surface new_surface, Surface target_surface,
463
int old_x, int new_x)
465
Context cr = new Context (target_surface);
466
cr.Operator = Operator.Source;
468
// redraw our top and bottom border separately. This makes the slide only appear to affect
470
cr.Rectangle (0, 0, width, top_border_width);
471
cr.Rectangle (0, height-BottomBorderWidth, width, BottomBorderWidth);
472
cr.SetSource (new_surface, 0, 0);
475
cr.Rectangle (0, top_border_width, width, height-top_border_width-BottomBorderWidth);
476
cr.SetSource (old_surface, old_x, 0);
479
cr.Operator = Operator.Over;
480
cr.SetSource (new_surface, new_x, 0);
483
(cr as IDisposable).Dispose ();
487
/// Draws a header. Currently this relies on being drown on top of the background surface to
490
/// <param name="cr">
491
/// A <see cref="Context"/>
493
/// <param name="radius">
494
/// A <see cref="System.Int32"/>
496
private void DrawHeaderOnContext (Context cr, int radius)
500
cr.MoveTo (0 + radius, 0);
501
cr.Arc (0 + width - radius, 0 + radius, radius, Math.PI*1.5, Math.PI*2);
502
cr.LineTo (0 + width, 0 + top_border_width);
503
cr.LineTo (0, 0 + top_border_width);
504
cr.Arc (0 + radius, 0 + radius, radius, Math.PI, Math.PI*1.5);
505
LinearGradient title_grad = new LinearGradient (0, 0, 0, top_border_width);
506
title_grad.AddColorStop (0.0, colors.TitleBarGlossLight);
507
title_grad.AddColorStop (0.5, colors.TitleBarGlossDark);
508
title_grad.AddColorStop (0.5, colors.TitleBarBase);
509
cr.Pattern = title_grad;
511
title_grad.Destroy ();
513
case HUDStyle.Classic:
514
cr.Rectangle (0.5, -0.5, width-1, top_border_width);
515
LinearGradient title_grad1 = new LinearGradient (0, 0, 0, top_border_width);
516
title_grad1.AddColorStop (0, new Cairo.Color (0.75, 0.75, 0.75));
517
title_grad1.AddColorStop (1, new Cairo.Color (0.95, 0.95, 0.95));
518
cr.Pattern = title_grad1;
520
title_grad1.Destroy ();
523
cr.Color = new Cairo.Color (0.3, 0.3, 0.3, 0.5);
530
/// Draws a footer. Currently this relies on being drawn on top of the background surface to
533
/// <param name="cr">
534
/// A <see cref="Context"/>
536
/// <param name="radius">
537
/// A <see cref="System.Int32"/>
539
private void DrawFooterOnContext (Context cr, int radius)
543
cr.MoveTo (.5, height-BottomBorderWidth+.5);
544
cr.LineTo (width-1, height-BottomBorderWidth+.5);
545
cr.Arc (width-radius-.5, height-radius-.5, radius, 0, Math.PI*.5);
546
cr.Arc (radius+.5, height-radius-.5, radius, Math.PI*.5, Math.PI);
548
cr.Color = colors.TitleBarBase;
551
cr.Color = new Cairo.Color (.6, .6, .6, .4);
554
case HUDStyle.Classic:
555
cr.Rectangle (0.5, height-BottomBorderWidth+.5, width-1, BottomBorderWidth-1);
556
LinearGradient title_grad1 = new LinearGradient (0, height-BottomBorderWidth, 0, height);
557
title_grad1.AddColorStop (0, new Cairo.Color (0.75, 0.75, 0.75));
558
title_grad1.AddColorStop (1, new Cairo.Color (0.95, 0.95, 0.95));
559
cr.Pattern = title_grad1;
561
title_grad1.Destroy ();
564
cr.Color = new Cairo.Color (0.3, 0.3, 0.3, 0.5);
571
/// Draws the background theme on the passed context
573
/// <param name="cr">
574
/// A <see cref="Context"/>
576
private void DrawBackgroundOnContext (Context cr)
578
cr.Operator = Operator.Source;
579
cr.Rectangle (0, 0, width, height);
580
cr.Color = new Cairo.Color (0, 0, 0, 0);
582
cr.Operator = Operator.Over;
584
int c_size = border_width - 2;
586
//Draw rounded rectange around whole border
589
cr.MoveTo (0.5+c_size, -1);
590
cr.Arc (width-c_size-0.5, c_size-1, c_size, Math.PI*1.5, Math.PI*2);
591
cr.Arc (width-0.5-c_size, height-c_size-0.5, c_size, 0, Math.PI*.5);
592
cr.Arc (0.5+c_size, height-c_size-0.5, c_size, Math.PI*.5, Math.PI);
593
cr.Arc (0.5+c_size, c_size-1, c_size, Math.PI, Math.PI*1.5);
595
cr.Color = BackgroundColor;
599
cr.Color = colors.BackgroundLight;
602
case HUDStyle.Classic:
603
cr.Rectangle (0.5, 0, width-1, height);
604
cr.Color = BackgroundColor;
607
cr.Color = new Cairo.Color (.3, .3, .3, .5);
613
DrawHeaderOnContext (cr, c_size);
615
cr.Rectangle (border_width, top_border_width, InternalWidth, height-top_border_width);
616
cr.Color = new Cairo.Color (.9, .9, .9, .05);
619
DrawFooterOnContext (cr, c_size);
621
cr.MoveTo (border_width + .5, top_border_width);
622
cr.LineTo (border_width + .5, height-BottomBorderWidth);
623
cr.MoveTo (width - border_width - .5, top_border_width);
624
cr.LineTo (width - border_width - .5, height-BottomBorderWidth);
625
if (style != HUDStyle.Classic) {
626
cr.MoveTo (0, height-BottomBorderWidth-.5);
627
cr.LineTo (width, height-BottomBorderWidth-.5);
631
cr.Color = new Cairo.Color (.6, .6, .6, .15);
636
/// Draws the entire view of the results window now on the surface passed in
638
/// <param name="sr">
639
/// A <see cref="Surface"/>
641
private void DrawContextOnSurface (Surface sr)
643
Context cr = new Context (sr);
644
if (background == null) {
645
background = cr.Target.CreateSimilar (cr.Target.Content, width, height);
646
Context cr2 = new Context (background);
647
DrawBackgroundOnContext (cr2);
648
(cr2 as IDisposable).Dispose ();
651
cr.Operator = Operator.Source;
652
cr.SetSource (background);
654
cr.Operator = Operator.Over;
656
if (context != null && !string.IsNullOrEmpty (context.Query))
657
RenderText (cr, new Gdk.Rectangle (10, 3, width-60, 20), 12, context.Query, QueryColor);
659
if (Results != null) {
660
string render_string = context.Cursor+1 + " of " + Results.Count + " ▸ ";
661
if (context.ParentContext != null && context.ParentContext.Selection != null) {
662
if (context.ParentContext.ParentContext != null && context.ParentContext.ParentContext.Selection != null) {
663
render_string += context.ParentContext.ParentContext.Selection.Name + " ▸ ";
665
render_string += context.ParentContext.Selection.Name + " ▸ ";
668
RenderText (cr, new Gdk.Rectangle (10, height-BottomBorderWidth+3, width-20, 20), 11, render_string);
669
int start_result = StartResult-(int) Math.Ceiling (scroll_offset);
670
RenderHighlight (cr);
671
for (int i = start_result; i < start_result+num_results+1 && i < Results.Count; i++) {
676
(cr as IDisposable).Dispose ();
684
public void InitChildInAnimation ()
686
if (child_inout_surface == null) {
687
Context cr = Gdk.CairoHelper.Create (GdkWindow);
688
child_inout_surface = cr.Target.CreateSimilar (cr.Target.Content, width, height);
689
(cr as IDisposable).Dispose ();
691
DrawContextOnSurface (child_inout_surface);
692
child_scroll_offset = 1;
695
public void InitChildOutAnimation ()
697
if (child_inout_surface == null) {
698
Context cr = Gdk.CairoHelper.Create (GdkWindow);
699
child_inout_surface = cr.Target.CreateSimilar (cr.Target.Content, width, height);
700
(cr as IDisposable).Dispose ();
702
DrawContextOnSurface (child_inout_surface);
703
child_scroll_offset = -1;
706
protected override bool OnExposeEvent (EventExpose evnt)
709
return base.OnExposeEvent (evnt);
712
void BufferItem (Element item)
716
Context cr = Gdk.CairoHelper.Create (GdkWindow);
717
Surface surface = cr.Target.CreateSimilar (cr.Target.Content, InternalWidth, SurfaceHeight);
718
Context cr2 = new Context (surface);
719
ItemRenderer.RenderElement (cr2, new Gdk.Point (border_width, 0), InternalWidth, item, controller.ElementHasChildren (item));
721
surface_buffer[item] = surface;
723
(cr2 as IDisposable).Dispose ();
724
(cr as IDisposable).Dispose ();
727
void RenderText (Context cr, Gdk.Rectangle region, int size, string text)
731
RenderText (cr, region, size, text, "ffffff");
733
case HUDStyle.Classic:
734
RenderText (cr, region, size, text, "333333");
737
throw new NotImplementedException ();
741
void RenderText (Context cr, Gdk.Rectangle region, int size, string text, string color_string)
743
Pango.Layout layout = new Pango.Layout (this.PangoContext);
744
layout.Width = Pango.Units.FromPixels (region.Width);
745
layout.Ellipsize = Pango.EllipsizeMode.End;
746
layout.SetMarkup ("<span foreground=\"#" + color_string + "\">"+GLib.Markup.EscapeText (text)+"</span>");
747
layout.FontDescription = Pango.FontDescription.FromString ("normal bold");
748
layout.FontDescription.AbsoluteSize = Pango.Units.FromPixels (size);
749
cr.MoveTo (region.X, region.Y);
750
Pango.CairoHelper.ShowLayout (cr, layout);
751
layout.Context.Dispose ();
752
layout.FontDescription.Dispose ();
756
Surface GetHighlightSource ()
758
if (highlight_surface == null) {
759
Context cr = Gdk.CairoHelper.Create (GdkWindow);
760
highlight_surface = cr.Target.CreateSimilar (cr.Target.Content, width, SurfaceHeight);
762
Context cr2 = new Context (highlight_surface);
765
LinearGradient grad = new LinearGradient (0, 0, 0, SurfaceHeight);
766
grad.AddColorStop (0, new Cairo.Color (.85, .85, .85, .2));
767
grad.AddColorStop (1, new Cairo.Color (.95, .95, .95, .2));
770
double radius=(SurfaceHeight-2)/2;
772
int r_width = width-9;
773
int r_height = SurfaceHeight-3;
775
cr2.MoveTo (x, y + radius);
776
cr2.Arc (x + radius, y + radius, radius, Math.PI, -Math.PI / 2);
777
cr2.LineTo (x + r_width - radius, y);
778
cr2.Arc (x + r_width - radius, y + radius, radius, -Math.PI / 2, 0);
779
cr2.LineTo (x + r_width, y + r_height - radius);
780
cr2.Arc (x + r_width - radius, y + r_height - radius, radius, 0, Math.PI / 2);
781
cr2.LineTo (x + radius, y + r_height);
782
cr2.Arc (x + radius, y + r_height - radius, radius, Math.PI / 2, Math.PI);
789
cr2.Color = new Cairo.Color (0.9, 0.9, 0.9, 1);
792
case HUDStyle.Classic:
793
cr2.Rectangle (0, 0, width, SurfaceHeight);
795
using (Gtk.Style rcstyle = Gtk.Rc.GetStyle (this)) {
796
gdkColor = rcstyle.BaseColors[(int) StateType.Selected];
798
cr2.Color = gdkColor.ConvertToCairo (.8);
804
(cr as IDisposable).Dispose ();
805
(cr2 as IDisposable).Dispose ();
807
return highlight_surface;
810
void RenderItem (Context cr, int item)
812
if (item >= Results.Count || item < 0)
814
int offset = (int) (SurfaceHeight*scroll_offset) + top_border_width;
815
if (!surface_buffer.ContainsKey (Results[item])) {
816
BufferItem (Results[item]);
819
cr.Rectangle (border_width, top_border_width, InternalWidth, height-top_border_width-BottomBorderWidth);
822
cr.Rectangle (border_width, offset+(item-StartResult)*SurfaceHeight, InternalWidth, SurfaceHeight);
824
cr.Color = new Cairo.Color (.2, .2, .2, .2);
825
cr.Operator = Operator.DestOver;
829
cr.Operator = Operator.Over;
830
cr.SetSource (surface_buffer[Results[item]], border_width, offset+(item-StartResult)*SurfaceHeight);
834
void RenderHighlight (Context cr)
836
int offset = (int) (SurfaceHeight*highlight_offset) + top_border_width;
837
cr.Rectangle (0, offset+(Cursor-StartResult)*SurfaceHeight, width, SurfaceHeight);
838
cr.SetSource (GetHighlightSource (), 0, offset+(Cursor-StartResult)*SurfaceHeight);
842
public void SlideIn ()
851
public void SlideOut ()