5
// Aaron Bockover <abockover@novell.com>
7
// Copyright (C) 2008 Novell, Inc.
9
// Permission is hereby granted, free of charge, to any person obtaining
10
// a copy of this software and associated documentation files (the
11
// "Software"), to deal in the Software without restriction, including
12
// without limitation the rights to use, copy, modify, merge, publish,
13
// distribute, sublicense, and/or sell copies of the Software, and to
14
// permit persons to whom the Software is furnished to do so, subject to
15
// the following conditions:
17
// The above copyright notice and this permission notice shall be
18
// included in all copies or substantial portions of the Software.
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
using System.Collections.Generic;
37
namespace Hyena.Widgets
39
public class SegmentedBar : Widget
41
public delegate string BarValueFormatHandler (Segment segment);
46
private double percent;
47
private Cairo.Color color;
48
private bool show_in_bar;
50
public Segment (string title, double percent, Cairo.Color color)
51
: this (title, percent, color, true)
55
public Segment (string title, double percent, Cairo.Color color, bool showInBar)
58
this.percent = percent;
60
this.show_in_bar = showInBar;
65
set { title = value; }
68
public double Percent {
69
get { return percent; }
70
set { percent = value; }
73
public Cairo.Color Color {
75
set { color = value; }
78
public bool ShowInBar {
79
get { return show_in_bar; }
80
set { show_in_bar = value; }
83
internal int LayoutWidth;
84
internal int LayoutHeight;
88
private List<Segment> segments = new List<Segment> ();
89
private int layout_width;
90
private int layout_height;
93
private int bar_height = 26;
94
private int bar_label_spacing = 8;
95
private int segment_label_spacing = 16;
96
private int segment_box_size = 12;
97
private int segment_box_spacing = 6;
98
private int h_padding = 0;
100
private bool show_labels = true;
101
private bool reflect = true;
103
private Color remainder_color = CairoExtensions.RgbToColor (0xeeeeee);
105
private BarValueFormatHandler format_handler;
107
public SegmentedBar ()
109
WidgetFlags |= WidgetFlags.NoWindow;
112
protected override void OnRealized ()
114
GdkWindow = Parent.GdkWindow;
118
#region Size Calculations
120
protected override void OnSizeRequested (ref Requisition requisition)
122
requisition.Width = 200;
123
requisition.Height = 0;
126
protected override void OnSizeAllocated (Gdk.Rectangle allocation)
128
int _bar_height = reflect ? (int)Math.Ceiling (bar_height * 1.75) : bar_height;
131
ComputeLayoutSize ();
132
HeightRequest = Math.Max (bar_height + bar_label_spacing + layout_height, _bar_height);
133
WidthRequest = layout_width + (2 * h_padding);
135
HeightRequest = _bar_height;
136
WidthRequest = bar_height + (2 * h_padding);
139
base.OnSizeAllocated (allocation);
142
private void ComputeLayoutSize ()
144
if (segments.Count == 0) {
148
Pango.Layout layout = null;
150
layout_width = layout_height = 0;
152
for (int i = 0, n = segments.Count; i < n; i++) {
155
layout = CreateAdaptLayout (layout, false, true);
156
layout.SetText (FormatSegmentText (segments[i]));
157
layout.GetPixelSize (out aw, out ah);
159
layout = CreateAdaptLayout (layout, true, false);
160
layout.SetText (FormatSegmentValue (segments[i]));
161
layout.GetPixelSize (out bw, out bh);
163
int w = Math.Max (aw, bw);
166
segments[i].LayoutWidth = w;
167
segments[i].LayoutHeight = Math.Max (h, segment_box_size * 2);
169
layout_width += segments[i].LayoutWidth + segment_box_size + segment_box_spacing
170
+ (i < n - 1 ? segment_label_spacing : 0);
171
layout_height = Math.Max (layout_height, segments[i].LayoutHeight);
179
#region Public Methods
181
public void AddSegmentRgba (string title, double percent, uint rgbaColor)
183
AddSegment (title, percent, CairoExtensions.RgbaToColor (rgbaColor));
186
public void AddSegmentRgb (string title, double percent, uint rgbColor)
188
AddSegment (title, percent, CairoExtensions.RgbToColor (rgbColor));
191
public void AddSegment (string title, double percent, Color color)
193
AddSegment (new Segment (title, percent, color, true));
196
public void AddSegment (string title, double percent, Color color, bool showInBar)
198
AddSegment (new Segment (title, percent, color, showInBar));
201
public void AddSegment (Segment segment)
204
segments.Add (segment);
209
public void UpdateSegment (int index, double percent)
211
segments[index].Percent = percent;
217
#region Public Properties
219
public BarValueFormatHandler ValueFormatter {
220
get { return format_handler; }
221
set { format_handler = value; }
224
public Color RemainderColor {
225
get { return remainder_color; }
227
remainder_color = value;
232
public int BarHeight {
233
get { return bar_height; }
235
if (bar_height != value) {
242
public bool ShowReflection {
243
get { return reflect; }
245
if (reflect != value) {
252
public bool ShowLabels {
253
get { return show_labels; }
255
if (show_labels != value) {
262
public int SegmentLabelSpacing {
263
get { return segment_label_spacing; }
265
if (segment_label_spacing != value) {
266
segment_label_spacing = value;
271
public int SegmentBoxSize {
272
get { return segment_box_size; }
274
if (segment_box_size != value) {
275
segment_box_size = value;
281
public int SegmentBoxSpacing {
282
get { return segment_box_spacing; }
284
if (segment_box_spacing != value) {
285
segment_box_spacing = value;
291
public int BarLabelSpacing {
292
get { return bar_label_spacing; }
294
if (bar_label_spacing != value) {
295
bar_label_spacing = value;
301
public int HorizontalPadding {
302
get { return h_padding; }
304
if (h_padding != value) {
315
protected override bool OnExposeEvent (Gdk.EventExpose evnt)
317
if (evnt.Window != GdkWindow) {
318
return base.OnExposeEvent (evnt);
321
Cairo.Context cr = Gdk.CairoHelper.Create (evnt.Window);
324
CairoExtensions.PushGroup (cr);
327
cr.Operator = Operator.Over;
328
cr.Translate (Allocation.X + h_padding, Allocation.Y);
329
cr.Rectangle (0, 0, Allocation.Width - h_padding, Math.Max (2 * bar_height,
330
bar_height + bar_label_spacing + layout_height));
333
Pattern bar = RenderBar (Allocation.Width - 2 * h_padding, bar_height);
343
cr.Rectangle (0, bar_height, Allocation.Width - h_padding, bar_height);
346
Matrix matrix = new Matrix ();
347
matrix.InitScale (1, -1);
348
matrix.Translate (0, -(2 * bar_height) + 1);
349
cr.Transform (matrix);
353
LinearGradient mask = new LinearGradient (0, 0, 0, bar_height);
355
mask.AddColorStop (0.25, new Color (0, 0, 0, 0));
356
mask.AddColorStop (0.5, new Color (0, 0, 0, 0.125));
357
mask.AddColorStop (0.75, new Color (0, 0, 0, 0.4));
358
mask.AddColorStop (1.0, new Color (0, 0, 0, 0.7));
365
CairoExtensions.PopGroupToSource (cr);
370
cr.Translate ((reflect ? Allocation.X : -h_padding) + (Allocation.Width - layout_width) / 2,
371
(reflect ? Allocation.Y : 0) + bar_height + bar_label_spacing);
377
CairoExtensions.DisposeContext (cr);
382
private Pattern RenderBar (int w, int h)
384
ImageSurface s = new ImageSurface (Format.Argb32, w, h);
385
Context cr = new Context (s);
386
RenderBar (cr, w, h, h / 2);
387
// TODO Implement the new ctor - see http://bugzilla.gnome.org/show_bug.cgi?id=561394
388
#pragma warning disable 0618
389
Pattern pattern = new Pattern (s);
390
#pragma warning restore 0618
392
((IDisposable)cr).Dispose ();
396
private void RenderBar (Context cr, int w, int h, int r)
398
RenderBarSegments (cr, w, h, r);
399
RenderBarStrokes (cr, w, h, r);
402
private void RenderBarSegments (Context cr, int w, int h, int r)
404
LinearGradient grad = new LinearGradient (0, 0, w, 0);
407
foreach (Segment segment in segments) {
408
if (segment.Percent > 0) {
409
grad.AddColorStop (last, segment.Color);
410
grad.AddColorStop (last += segment.Percent, segment.Color);
414
CairoExtensions.RoundedRectangle (cr, 0, 0, w, h, r);
417
cr.Pattern.Destroy ();
419
grad = new LinearGradient (0, 0, 0, h);
420
grad.AddColorStop (0.0, new Color (1, 1, 1, 0.125));
421
grad.AddColorStop (0.35, new Color (1, 1, 1, 0.255));
422
grad.AddColorStop (1, new Color (0, 0, 0, 0.4));
426
cr.Pattern.Destroy ();
429
private void RenderBarStrokes (Context cr, int w, int h, int r)
431
LinearGradient stroke = MakeSegmentGradient (h, CairoExtensions.RgbaToColor (0x00000040));
432
LinearGradient seg_sep_light = MakeSegmentGradient (h, CairoExtensions.RgbaToColor (0xffffff20));
433
LinearGradient seg_sep_dark = MakeSegmentGradient (h, CairoExtensions.RgbaToColor (0x00000020));
438
double x = seg_w > r ? seg_w : r;
441
cr.MoveTo (x - 0.5, 1);
442
cr.LineTo (x - 0.5, h - 1);
443
cr.Pattern = seg_sep_light;
446
cr.MoveTo (x + 0.5, 1);
447
cr.LineTo (x + 0.5, h - 1);
448
cr.Pattern = seg_sep_dark;
454
CairoExtensions.RoundedRectangle (cr, 0.5, 0.5, w - 1, h - 1, r);
459
seg_sep_light.Destroy ();
460
seg_sep_dark.Destroy ();
463
private LinearGradient MakeSegmentGradient (int h, Color color)
465
return MakeSegmentGradient (h, color, false);
468
private LinearGradient MakeSegmentGradient (int h, Color color, bool diag)
470
LinearGradient grad = new LinearGradient (0, 0, 0, h);
471
grad.AddColorStop (0, CairoExtensions.ColorShade (color, 1.1));
472
grad.AddColorStop (0.35, CairoExtensions.ColorShade (color, 1.2));
473
grad.AddColorStop (1, CairoExtensions.ColorShade (color, 0.8));
477
private void RenderLabels (Context cr)
479
if (segments.Count == 0) {
483
Pango.Layout layout = null;
484
Color text_color = CairoExtensions.GdkColorToCairoColor (Style.Foreground (State));
485
Color box_stroke_color = new Color (0, 0, 0, 0.6);
489
foreach (Segment segment in segments) {
491
cr.Rectangle (x + 0.5, 2 + 0.5, segment_box_size - 1, segment_box_size - 1);
492
LinearGradient grad = MakeSegmentGradient (segment_box_size, segment.Color, true);
495
cr.Color = box_stroke_color;
499
x += segment_box_size + segment_box_spacing;
502
layout = CreateAdaptLayout (layout, false, true);
503
layout.SetText (FormatSegmentText (segment));
504
layout.GetPixelSize (out lw, out lh);
508
cr.Color = text_color;
509
PangoCairoHelper.ShowLayout (cr, layout);
512
layout = CreateAdaptLayout (layout, true, false);
513
layout.SetText (FormatSegmentValue (segment));
517
cr.Color = text_color;
518
PangoCairoHelper.ShowLayout (cr, layout);
521
x += segment.LayoutWidth + segment_label_spacing;
531
private int pango_size_normal;
533
private Pango.Layout CreateAdaptLayout (Pango.Layout layout, bool small, bool bold)
535
if (layout == null) {
536
Pango.Context context = CreatePangoContext ();
537
layout = new Pango.Layout (context);
538
layout.FontDescription = context.FontDescription;
539
pango_size_normal = layout.FontDescription.Size;
542
layout.FontDescription.Size = small
543
? (int)(layout.FontDescription.Size * Pango.Scale.Small)
546
layout.FontDescription.Weight = bold
548
: Pango.Weight.Normal;
554
private string FormatSegmentText (Segment segment)
556
return segment.Title;
559
private string FormatSegmentValue (Segment segment)
561
return format_handler == null
562
? String.Format ("{0}%", segment.Percent * 100.0)
563
: format_handler (segment);
572
[TestModule ("Segmented Bar")]
573
internal class SegmentedBarTestModule : Window
575
private SegmentedBar bar;
577
public SegmentedBarTestModule () : base ("Segmented Bar")
587
bar = new SegmentedBar ();
588
bar.HorizontalPadding = bar.BarHeight / 2;
589
bar.AddSegmentRgb ("Audio", 0.00187992456702332, 0x3465a4);
590
bar.AddSegmentRgb ("Other", 0.0788718162651326, 0xf57900);
591
bar.AddSegmentRgb ("Video", 0.0516869922033282, 0x73d216);
592
bar.AddSegment ("Free Space", 0.867561266964516, bar.RemainderColor, false);
594
bar.ValueFormatter = delegate (SegmentedBar.Segment segment) {
595
return String.Format ("{0} GB", space * segment.Percent);
598
HBox controls = new HBox ();
599
controls.Spacing = 5;
601
Label label = new Label ("Height:");
602
controls.PackStart (label, false, false, 0);
604
SpinButton height = new SpinButton (new Adjustment (bar.BarHeight, 5, 100, 1, 1, 1), 1, 0);
605
height.Activated += delegate { bar.BarHeight = height.ValueAsInt; };
606
height.Changed += delegate { bar.BarHeight = height.ValueAsInt; bar.HorizontalPadding = bar.BarHeight / 2; };
607
controls.PackStart (height, false, false, 0);
609
CheckButton reflect = new CheckButton ("Reflection");
610
reflect.Active = bar.ShowReflection;
611
reflect.Toggled += delegate { bar.ShowReflection = reflect.Active; };
612
controls.PackStart (reflect, false, false, 0);
614
CheckButton labels = new CheckButton ("Labels");
615
labels.Active = bar.ShowLabels;
616
labels.Toggled += delegate { bar.ShowLabels = labels.Active; };
617
controls.PackStart (labels, false, false, 0);
619
box.PackStart (controls, false, false, 0);
620
box.PackStart (new HSeparator (), false, false, 0);
621
box.PackStart (bar, false, false, 0);
624
SetSizeRequest (350, -1);
626
Gdk.Geometry limits = new Gdk.Geometry ();
627
limits.MinWidth = SizeRequest ().Width;
628
limits.MaxWidth = Gdk.Screen.Default.Width;
629
limits.MinHeight = -1;
630
limits.MaxHeight = -1;
631
SetGeometryHints (this, limits, Gdk.WindowHints.MaxSize | Gdk.WindowHints.MinSize);