1
// BeezelDrawingArea.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;
29
using Do.Interface.CairoUtils;
33
namespace Do.Interface.AnimationBase
35
public enum PointLocation {
42
public enum HUDStyle {
47
public class BezelDrawingArea : Gtk.DrawingArea
57
static IPreferences prefs = Services.Preferences.Get<BezelDrawingArea> ();
58
public static event EventHandler ThemeChanged;
60
public static bool Animated {
62
return prefs.Get<bool> ("Animated", true);
65
prefs.Set<bool> ("Animated", value);
69
public static string TitleRenderer {
71
return prefs.Get<string> ("TitleRenderer", "default");
74
prefs.Set<string> ("TitleRenderer", value);
79
public static string PaneRenderer {
81
return prefs.Get<string> ("PaneRenderer", "default");
84
prefs.Set<string> ("PaneRenderer", value);
89
public static string WindowRenderer {
91
return prefs.Get<string> ("WindowRenderer", "default");
94
prefs.Set<string> ("WindowRenderer", value);
99
public static string BgColor {
101
return prefs.Get<string> ("BackgroundColor", "default");
104
prefs.Set<string> ("BackgroundColor", value);
109
public static int RoundingRadius {
111
return prefs.Get<int> ("WindowRadius", -1);
114
prefs.Set<int> ("WindowRadius", Math.Max (-1, value));
119
public static bool DrawShadow {
121
return prefs.Get<bool> ("Shadow", true);
124
prefs.Set<bool> ("Shadow", value);
129
public static void SetDefaultStyle ()
132
prefs.Set<int> ("WindowRadius", -1);
135
public static void ResetBackgroundStyle ()
137
prefs.Set<string> ("BackgroundColor", "default");
141
private static void OnThemeChanged ()
143
if (ThemeChanged != null)
144
ThemeChanged (new System.Object (), new EventArgs ());
148
public const int TextHeight = 11;
152
const int BoxLineWidth = 1;
153
const int BorderWidth = 15;
154
const int fade_ms = 150;
155
const int ShadowRadius = 10;
157
IBezelTitleBarRenderElement titleBarRenderer;
158
IBezelWindowRenderElement backgroundRenderer;
159
IBezelPaneRenderElement paneOutlineRenderer;
160
IBezelOverlayRenderElement textModeOverlayRenderer;
161
IBezelDefaults bezelDefaults;
165
IDoController controller;
168
BezelGlassResults bezel_results;
170
bool third_pane_visible, preview;
171
BezelDrawingContext context, old_context;
172
PixbufSurfaceCache surface_cache;
177
Gdk.Rectangle drawing_area;
180
double text_box_scale, window_fade = 1, window_scale=1;
182
double[] icon_fade = new double [] {1, 1, 1};
183
bool[] entry_mode = new bool[3];
185
public Cairo.Color BackgroundColor {
187
Gdk.Color color = new Gdk.Color ();
188
if (Gdk.Color.Parse ("#" + BgColor.Substring (0, 6), ref color)) {
189
if (BgColor.Length == 8) {
190
double alpha = Convert.ToInt32 (BgColor.Substring (6, 2), 16);
192
return color.ConvertToCairo (alpha);
194
return color.ConvertToCairo (backgroundRenderer.BackgroundColor.A);
196
return backgroundRenderer.BackgroundColor;
200
public int BoxWidth {
201
get { return PaneOutlineRenderer.Width; }
204
public int BoxHeight {
205
get { return PaneOutlineRenderer.Height; }
209
get { return focus; }
218
public BezelColors Colors {
224
public TextRenderer TextUtility { get; set; }
226
public int IconSize {
228
return paneOutlineRenderer.IconSize;
232
public int InternalHeight {
234
if (BezelDefaults.RenderDescriptionText)
235
return BoxHeight + 2 * WindowBorder + TextHeight + TitleBarHeight;
236
return BoxHeight + 2 * WindowBorder + TitleBarHeight;
240
public BezelGlassResults Results {
242
if (bezel_results == null)
243
bezel_results = new BezelGlassResults (controller, Math.Min (TwoPaneWidth - 2 * WindowRadius, 360), style, colors);
244
return bezel_results;
248
public int TextModeOffset {
250
return Math.Max (TitleBarHeight, WindowRadius);
254
public bool ThirdPaneVisible {
255
get { return third_pane_visible; }
257
third_pane_visible = value;
258
if (third_pane_visible)
263
public int ThreePaneWidth {
265
return (2 * WindowBorder - BorderWidth) + (BoxWidth + BorderWidth) * 3;
269
public int TitleBarHeight {
271
return TitleBarRenderer.Height;
275
public int TwoPaneWidth {
277
return (2 * WindowBorder) - BorderWidth + ((BoxWidth + (BorderWidth)) * 2);
281
public string HighlightFormat {
283
if (colors.Background.B == colors.Background.G && colors.Background.B == colors.Background.R)
284
return BezelDefaults.HighlightFormat;
286
return "<span underline=\"single\"><b>{0}</b></span>";
290
public int WindowBorder {
292
return BezelDefaults.WindowBorder;
296
public int WindowHeight {
298
return InternalHeight + 2 * ShadowRadius;
302
public int WindowRadius {
304
if (RoundingRadius <= -1)
305
return BezelDefaults.WindowRadius;
306
return Math.Max (1, prefs.Get<int> ("WindowRadius", -1));
310
public int WindowWidth {
312
return (2 * WindowBorder - BorderWidth) + ((BoxWidth + BorderWidth) * 3) + 2 * ShadowRadius;
316
private bool AnimationNeeded {
318
return ExpandNeeded ||
326
private bool ExpandNeeded {
328
bool shouldBeExpanded = ThirdPaneVisible || entry_mode [(int) Focus];
329
bool isNotExpanded = drawing_area.Width != ThreePaneWidth;
330
return shouldBeExpanded && isNotExpanded;
334
private bool ShrinkNeeded {
336
bool shouldNotBeExpanded = !ThirdPaneVisible && !entry_mode [(int) focus];
337
return shouldNotBeExpanded && (drawing_area.Width != TwoPaneWidth) && (Focus != Pane.Third);
341
private bool FadeNeeded {
343
return icon_fade [0] != 1 ||
344
icon_fade [1] != 1 ||
349
private bool TextScaleNeeded {
351
bool textBoxNeedsExpand = entry_mode [(int) Focus] && text_box_scale != 1;
352
bool textBoxNeedsShrink = !entry_mode [(int) Focus] && text_box_scale != 0;
353
return textBoxNeedsExpand || textBoxNeedsShrink;
357
private bool WindowFadeNeeded {
359
return window_fade != 1;
363
private BezelDrawingContext Context {
366
context = new BezelDrawingContext ();
371
private BezelDrawingContext OldContext {
373
if (old_context == null)
374
old_context = new BezelDrawingContext ();
379
public IBezelTitleBarRenderElement TitleBarRenderer {
380
get { return titleBarRenderer; }
383
public IBezelWindowRenderElement BackgroundRenderer {
384
get { return backgroundRenderer; }
387
public IBezelPaneRenderElement PaneOutlineRenderer {
388
get { return paneOutlineRenderer; }
391
public IBezelOverlayRenderElement TextModeOverlayRenderer {
392
get { return textModeOverlayRenderer; }
395
public IBezelDefaults BezelDefaults {
396
get { return bezelDefaults; }
399
public event EventHandler GtkThemeChanged;
401
public BezelDrawingArea (IDoController controller, IRenderTheme theme, bool preview) : base ()
403
this.controller = controller;
404
TextUtility = new TextRenderer (this);
406
DoubleBuffered = false;
407
prefs = Services.Preferences.Get<BezelDrawingArea> ();
408
this.preview = preview;
411
if (theme.Name == "Nouveau") //this is a temporary hack
412
style = HUDStyle.HUD;
414
style = HUDStyle.Classic; //gets us the classic results list
419
BezelDrawingArea.ThemeChanged += OnThemeChanged;
420
Realized += delegate {
421
GdkWindow.SetBackPixmap (null, false);
424
StyleSet += delegate {
426
GdkWindow.SetBackPixmap (null, false);
430
private void SetDrawingArea ()
432
if (preview && TwoPaneWidth > 400) {
433
window_scale = 400.0 / TwoPaneWidth;
435
SetSizeRequest ((int) Math.Floor (WindowWidth * window_scale), (int) Math.Floor (WindowHeight * window_scale));
436
drawing_area = new Gdk.Rectangle ((WindowWidth - TwoPaneWidth) / 2, ShadowRadius, TwoPaneWidth, InternalHeight);
438
drawing_area.X = ShadowRadius;
441
private void ResetRenderStyle ()
446
colors = new BezelColors (BackgroundColor);
448
colors.RebuildColors (BackgroundColor);
453
private void BuildRenderers ()
455
bezelDefaults = theme.GetDefaults (this);
456
titleBarRenderer = theme.GetTitleBar (this);
457
textModeOverlayRenderer = theme.GetOverlay (this);
458
backgroundRenderer = theme.GetWindow (this);
459
paneOutlineRenderer = theme.GetPane (this);
462
public PixbufSurfaceCache SurfaceCache {
464
if (surface_cache == null) {
465
using (Context cr = CairoHelper.Create (GdkWindow))
466
surface_cache = new PixbufSurfaceCache (50, IconSize, IconSize, cr.Target);
468
return surface_cache;
472
private void AnimatedDraw ()
474
if (!IsDrawable || timer > 0)
482
delta_time = DateTime.Now;
483
timer = GLib.Timeout.Add (1000 / 65, OnDrawTimeoutElapsed);
486
private DrawState PaneDrawState (Pane pane)
488
if (pane == Pane.Third && !ThirdPaneVisible)
489
return DrawState.None;
491
if (Context.GetPaneTextMode (pane))
492
return DrawState.Text;
494
if (Context.GetPaneObject (pane) != null)
495
return DrawState.Normal;
497
if (!string.IsNullOrEmpty (Context.GetPaneQuery (pane))) {
498
return DrawState.NoResult;
501
return DrawState.None;
504
public void BezelSetPaneObject (Pane pane, Do.Universe.Item obj)
506
if (Context.GetPaneObject (pane) == obj && obj != null)
509
OldContext.SetPaneObject (pane, Context.GetPaneObject (pane));
510
Context.SetPaneObject (pane, obj);
512
icon_fade [(int) pane] = 0;
516
public void BezelSetTextMode (Pane pane, bool textMode)
518
if (Context.GetPaneTextMode (pane) == textMode)
521
OldContext.SetPaneTextMode (pane, Context.GetPaneTextMode (pane));
522
Context.SetPaneTextMode (pane, textMode);
527
public void BezelSetEntryMode (Pane pane, bool entryMode)
529
entry_mode [(int) pane] = entryMode;
533
public void BezelSetQuery (Pane pane, string query)
535
if (Context.GetPaneQuery (pane) == query)
538
OldContext.SetPaneQuery (pane, Context.GetPaneQuery (pane));
539
Context.SetPaneQuery (pane, query);
545
context = new BezelDrawingContext ();
546
old_context = new BezelDrawingContext ();
547
entry_mode = new bool [3];
560
Cairo.Context cr2 = Gdk.CairoHelper.Create (GdkWindow);
562
//Much kudos to Ian McIntosh
564
surface = cr2.Target.CreateSimilar (cr2.Target.Content, WindowWidth, WindowHeight);
566
Context cr = new Context (surface);
570
using (Gtk.Style rcstyle = Gtk.Rc.GetStyle (this)) {
571
bgColor = rcstyle.Backgrounds[(int) StateType.Normal];
573
cr.Color = bgColor.ConvertToCairo (1);
575
cr.Color = new Cairo.Color (0, 0, 0, 0);
577
cr.Operator = Cairo.Operator.Source;
579
cr.Operator = Cairo.Operator.Over;
581
BackgroundRenderer.RenderItem (cr, drawing_area);
585
if (text_box_scale > 0) {
587
RenderTextModeOverlay (cr);
588
if (text_box_scale == 1) {
589
RenderTextModeText (cr);
594
if (BezelDefaults.RenderDescriptionText)
595
RenderDescriptionText (cr);
596
//--------------First Pane---------------
597
RenderPane (Pane.First, cr);
598
//------------Second Pane----------------
599
RenderPane (Pane.Second, cr);
600
//------------Third Pane-----------------
601
if (ThirdPaneVisible) {
602
RenderPane (Pane.Third, cr);
605
if (text_box_scale > 0) {
606
RenderTextModeOverlay (cr);
612
Util.Appearance.DrawShadow (cr, drawing_area.X, drawing_area.Y, drawing_area.Width,
613
drawing_area.Height, WindowRadius, new Util.ShadowParameters (.5, ShadowRadius));
615
if (window_scale != 1) //we are likely in preview mode, though this can be set on the fly
616
cr2.Scale (window_scale, window_scale);
617
cr2.SetSourceSurface (surface, 0, 0);
618
cr2.Operator = Operator.Source;
621
(cr2.Target).Destroy ();
622
((IDisposable)cr2.Target).Dispose ();
623
((IDisposable)cr2).Dispose ();
625
((IDisposable)cr).Dispose ();
628
protected override void OnDestroyed ()
631
BezelDrawingArea.ThemeChanged -= OnThemeChanged;
634
context = old_context = null;
637
protected bool OnDrawTimeoutElapsed ()
639
double change = (Animated) ? DateTime.Now.Subtract (delta_time).TotalMilliseconds / fade_ms : 10;
641
delta_time = DateTime.Now;
643
drawing_area.Width += (int) ((ThreePaneWidth - TwoPaneWidth) * change);
644
drawing_area.Width = (drawing_area.Width > ThreePaneWidth) ? ThreePaneWidth : drawing_area.Width;
645
drawing_area.X = (WindowWidth - drawing_area.Width) / 2;
646
} else if (ShrinkNeeded) {
647
drawing_area.Width -= (int) ((ThreePaneWidth - TwoPaneWidth) * change);
648
drawing_area.Width = (drawing_area.Width < TwoPaneWidth) ? TwoPaneWidth : drawing_area.Width;
649
drawing_area.X = (WindowWidth - drawing_area.Width) / 2;
652
if (TextScaleNeeded) {
653
if (entry_mode [(int) Focus]) {
654
text_box_scale += change;
655
text_box_scale = (text_box_scale > 1) ? 1 : text_box_scale;
657
text_box_scale -= change;
658
text_box_scale = (text_box_scale < 0) ? 0 : text_box_scale;
663
if (text_box_scale == 1) {
664
icon_fade [0] = icon_fade [1] = icon_fade [2] = 1;
666
icon_fade [0] += change;
667
icon_fade [1] += change;
668
icon_fade [2] += change;
670
icon_fade [0] = (icon_fade [0] > 1) ? 1 : icon_fade [0];
671
icon_fade [1] = (icon_fade [1] > 1) ? 1 : icon_fade [1];
672
icon_fade [2] = (icon_fade [2] > 1) ? 1 : icon_fade [2];
675
if (WindowFadeNeeded) {
676
window_fade += (change * .1) + (window_fade / 2);
677
window_fade = Math.Min (window_fade, 1);
682
if (AnimationNeeded) {
690
protected override bool OnExposeEvent (EventExpose evnt)
692
bool ret = base.OnExposeEvent (evnt);
697
protected override void OnStyleSet (Gtk.Style previous_style)
699
if (GtkThemeChanged != null)
700
GtkThemeChanged (this, System.EventArgs.Empty);
701
Colors.RebuildColors (BackgroundColor);
702
base.OnStyleSet (previous_style);
705
private void OnThemeChanged (object o, System.EventArgs args)
711
public int PaneOffset (Pane pane)
713
return WindowBorder + ((int) pane * (BoxWidth + BorderWidth));
716
private void RenderPane (Pane pane, Context cr)
718
if (pane == Pane.Third) {
719
cr.Rectangle (drawing_area.X, drawing_area.Y, drawing_area.Width, drawing_area.Height);
722
RenderPaneOutline (pane, cr);
724
switch (PaneDrawState (pane)) {
725
case DrawState.Normal:
726
RenderPixbuf (pane, cr);
727
RenderPaneText (pane, cr);
729
case DrawState.NoResult:
730
RenderPixbuf (pane, cr, "gtk-question-dialog", 1);
731
RenderPaneText (pane, cr, string.Format (Catalog.GetString ("No results for: {0}"), Context.GetPaneQuery (pane)));
734
if (text_box_scale < 1) {
735
RenderPixbuf (pane, cr, "gnome-mime-text", .1);
736
RenderPaneText (pane, cr);
740
if (pane == Pane.First) {
741
RenderPixbuf (pane, cr, "search", 1);
742
RenderPaneText (pane, cr, Catalog.GetString ("Type To Search"));
746
if (pane == Pane.Third)
750
private void RenderPaneOutline (Pane pane, Context cr)
752
Gdk.Rectangle render_region = new Gdk.Rectangle () {
755
X = drawing_area.X + PaneOffset (pane),
756
Y = drawing_area.Y + WindowBorder + TitleBarHeight,
758
PaneOutlineRenderer.RenderItem (cr, render_region, (Focus == pane));
761
private void RenderPixbuf (Pane pane, Context cr)
763
Do.Universe.Item obj = Context.GetPaneObject (pane);
764
RenderPixbuf (pane, cr, obj.Icon, 1);
767
private void RenderPixbuf (Pane pane, Context cr, string icon, double alpha)
769
int offset = PaneOffset (pane);
770
if (!SurfaceCache.ContainsKey (icon)) {
771
BufferIcon (cr, icon);
774
string sec_icon = "";
775
if (OldContext.GetPaneObject (pane) != null)
776
sec_icon = OldContext.GetPaneObject (pane).Icon;
778
double calc_alpha = (sec_icon != icon) ? icon_fade[(int) pane] : 1;
781
if (PaneOutlineRenderer.StackIconText) {
782
x = drawing_area.X + offset + (BoxWidth / 2 - IconSize / 2);
783
y = drawing_area.Y + WindowBorder + TitleBarHeight + 3;
785
x = drawing_area.X + offset + 5;
786
y = drawing_area.Y + WindowBorder + TitleBarHeight + ((BoxHeight - IconSize) / 2);
788
cr.SetSource (SurfaceCache.GetSurface (icon), x, y);
789
cr.PaintWithAlpha (calc_alpha * alpha);
790
if (string.IsNullOrEmpty (sec_icon) || calc_alpha < 1)
793
if (!SurfaceCache.ContainsKey (OldContext.GetPaneObject (pane).Icon)) {
794
BufferIcon (cr, OldContext.GetPaneObject (pane).Icon);
796
cr.SetSource (SurfaceCache.GetSurface (OldContext.GetPaneObject (pane).Icon), x, y);
797
cr.PaintWithAlpha (alpha * (1 - calc_alpha));
800
private void BufferIcon (Context cr, string icon)
802
SurfaceCache.AddPixbufSurface (icon, icon);
805
void RenderDescriptionText (Context cr)
807
if (Context.GetPaneObject (Focus) == null)
809
string text = GLib.Markup.EscapeText (Context.GetPaneObject (Focus).Description);
810
int x = drawing_area.X + 10;
811
int y = drawing_area.Y + InternalHeight - WindowBorder - 4;
812
TextUtility.RenderLayoutText (cr, text, x, y, drawing_area.Width - 20, TextHeight);
815
void RenderPaneText (Pane pane, Context cr)
817
if (Context.GetPaneObject (pane) != null)
818
RenderPaneText (pane, cr, GLib.Markup.EscapeText (Context.GetPaneObject (pane).Name));
821
void RenderPaneText (Pane pane, Context cr, string text)
823
if (text.Length == 0) return;
825
if (Context.GetPaneTextMode (pane)) {
826
Pango.Color color = new Pango.Color ();
827
color.Blue = color.Green = color.Red = ushort.MaxValue;
828
int y = drawing_area.Y + WindowBorder + TitleBarHeight + 6;
829
int x = drawing_area.X + PaneOffset (pane) + 5;
831
TextUtility.RenderLayoutText (cr, text, x, y, BoxWidth - 10, TextHeight, color,
832
Pango.Alignment.Left, Pango.EllipsizeMode.None);
834
string query = Context.GetPaneQuery (pane);
835
if (!string.IsNullOrEmpty (query)) {
836
text = Util.FormatCommonSubstrings (text, query, HighlightFormat);
839
if (PaneOutlineRenderer.StackIconText) {
840
int y = drawing_area.Y + WindowBorder + TitleBarHeight + BoxHeight - TextHeight - 9;
841
int x = drawing_area.X + PaneOffset (pane) + 5;
842
TextUtility.RenderLayoutText (cr, text, x, y, BoxWidth - 10, TextHeight);
844
int y = drawing_area.Y + WindowBorder + TitleBarHeight + (int)(BoxHeight/2);
845
int x = drawing_area.X + PaneOffset (pane) + IconSize + 10;
846
TextUtility.RenderLayoutText (cr, text, x, y, BoxWidth - IconSize - 20, TextHeight);
851
void RenderTextModeText (Context cr)
853
Pango.Color color = new Pango.Color ();
854
color.Blue = color.Red = color.Green = (ushort) (ushort.MaxValue * text_box_scale);
855
Gdk.Rectangle cursor = TextUtility.RenderLayoutText (cr, GLib.Markup.EscapeText (Context.GetPaneQuery (Focus)),
856
drawing_area.X + 10, drawing_area.Y + TextModeOffset + 5,
857
drawing_area.Width - 20, 18, color,
858
Pango.Alignment.Left, Pango.EllipsizeMode.None);
859
if (cursor.X == cursor.Y && cursor.X == 0) return;
861
cr.Rectangle (cursor.X, cursor.Y, 2, cursor.Height);
862
cr.Color = new Cairo.Color (.4, .5, 1, .85);
866
void RenderTextModeOverlay (Context cr)
868
TextModeOverlayRenderer.RenderItem (cr, drawing_area, text_box_scale);
871
void RenderTitleBar (Context cr)
873
TitleBarRenderer.RenderItem (cr, drawing_area);
876
public PointLocation GetPointLocation (Gdk.Point point)
878
if (BackgroundRenderer.GetPointLocation (drawing_area, point) == PointLocation.Outside)
879
return PointLocation.Outside;
880
return TitleBarRenderer.GetPointLocation (drawing_area, point);