summaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
authorgraemeg <graemeg@ae50a9b5-8222-0410-bf8d-8a13f76226bf>2007-11-06 20:50:26 +0000
committergraemeg <graemeg@ae50a9b5-8222-0410-bf8d-8a13f76226bf>2007-11-06 20:50:26 +0000
commit6b34d0d7654981ea7c9bfbc732df1fc1c6e12326 (patch)
treef3182ea936bd2b1c84090b89915f65ad099bcce7 /src/gui
parentab910baffbf9585f5376f6a40f70490a5c765257 (diff)
downloadfpGUI-6b34d0d7654981ea7c9bfbc732df1fc1c6e12326.tar.xz
* Added new TfpgGauge component. Thanks to Giuliano Colla.
* Added new Gauges demo program - thanks to Giuliano Colla.
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/fpgui_package.lpk14
-rw-r--r--src/gui/fpgui_package.pas2
-rw-r--r--src/gui/gui_gauge.pas543
3 files changed, 553 insertions, 6 deletions
diff --git a/src/gui/fpgui_package.lpk b/src/gui/fpgui_package.lpk
index f65ccde6..80dc2a9a 100644
--- a/src/gui/fpgui_package.lpk
+++ b/src/gui/fpgui_package.lpk
@@ -26,7 +26,7 @@
<License Value="Modified LGPL
"/>
<Version Minor="5" Release="1"/>
- <Files Count="27">
+ <Files Count="28">
<Item1>
<Filename Value="gui_button.pas"/>
<UnitName Value="gui_button"/>
@@ -135,15 +135,19 @@
<Filename Value="gui_popupcalendar.pas"/>
<UnitName Value="gui_popupcalendar"/>
</Item27>
+ <Item28>
+ <Filename Value="gui_gauge.pas"/>
+ <UnitName Value="gui_gauge"/>
+ </Item28>
</Files>
<RequiredPkgs Count="2">
<Item1>
- <PackageName Value="fpgfx_package"/>
- <MinVersion Minor="5" Valid="True"/>
- </Item1>
- <Item2>
<PackageName Value="FCL"/>
<MinVersion Major="1" Valid="True"/>
+ </Item1>
+ <Item2>
+ <PackageName Value="fpgfx_package"/>
+ <MinVersion Minor="5" Valid="True"/>
</Item2>
</RequiredPkgs>
<UsageOptions>
diff --git a/src/gui/fpgui_package.pas b/src/gui/fpgui_package.pas
index bc3ab192..ad9e692a 100644
--- a/src/gui/fpgui_package.pas
+++ b/src/gui/fpgui_package.pas
@@ -11,7 +11,7 @@ uses
gui_listbox, gui_memo, gui_scrollbar, gui_bevel, gui_checkbox,
gui_radiobutton, gui_trackbar, gui_tab, gui_basegrid, gui_listview,
gui_customgrid, gui_progressbar, gui_menu, gui_style, gui_grid, gui_tree,
- gui_iniutils, gui_mru, fpgui_db, gui_popupcalendar;
+ gui_iniutils, gui_mru, fpgui_db, gui_popupcalendar, gui_gauge;
implementation
diff --git a/src/gui/gui_gauge.pas b/src/gui/gui_gauge.pas
new file mode 100644
index 00000000..68c4da2e
--- /dev/null
+++ b/src/gui/gui_gauge.pas
@@ -0,0 +1,543 @@
+{
+ fpGUI - Free Pascal GUI Library
+
+ Copyright (C) 2006 - 2007 See the file AUTHORS.txt, included in this
+ distribution, for details of the copyright.
+
+ See the file COPYING.modifiedLGPL, included in this distribution,
+ for details about redistributing fpGUI.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ Description:
+ A Gauge component that supports different display styles. eg: Needle,
+ Dial, Pie etc.
+}
+
+unit gui_gauge;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, math,
+ fpgfx,
+ gfxbase,
+ gfx_widget;
+
+type
+
+ TGaugeKind = (gkText, gkHorizontalBar, gkVerticalBar, gkPie, gkNeedle, gkDial);
+
+ TBorderStyle = (bsNone, bsSingle, bsDouble, bsRaisedPanel, bsSunkenPanel,
+ bsRaised3D, bsSunken3D, bsEtched, bsEmmbossed);
+
+
+ TfpgGauge = class(TfpgWidget)
+ private
+ FFont: TfpgFont;
+ FClientRect: TfpgRect;
+ FMin: Longint;
+ FMax: Longint;
+ FPosition: Longint;
+ FKind: TGaugeKind;
+ FShowText: Boolean;
+ {TODO _ Implement Border style }
+ FBorderStyle: TBorderStyle;
+ FColor: TfpgColor; // Background color
+ { Currently little used colors, should be derived from style and possibly
+ overriden by user TODO - How to deal with gradients? Starting color and compute ending,
+ or give pair? }
+ FFirstColor: TfpgColor; // Text and Needle color
+ FSecondColor: TfpgColor; // Bar, Pie etc. main color
+ { Currently unused - TODO - Implement Low Watermark and High Watermark }
+ FLWMColor: TfpgColor; // Low Watermark Color
+ FLWMValue: Longint; // Low Watermark Value
+ FHWMColor: TfpgColor; // High Watermark Color
+ FHWMValue: Longint; // High Watermark Color
+ procedure BackgroundDraw;
+ procedure TextDraw;
+ procedure BarDraw;
+ procedure PieDraw;
+ procedure NeedleDraw;
+ procedure DialDraw;
+ procedure SetGaugeKind(AValue: TGaugeKind);
+ procedure SetShowText(AValue: Boolean);
+ procedure SetBorderStyle(AValue: TBorderStyle);
+ procedure SetFirstColor(AValue: TfpgColor);
+ procedure SetSecondColor(AValue: TfpgColor);
+ procedure SetMin(AValue: Longint);
+ procedure SetMax(AValue: Longint);
+ procedure SetProgress(AValue: Longint);
+ function GetPercentage: Longint;
+ protected
+ procedure HandlePaint; override;
+ public
+ constructor Create(AOwner: TComponent); override;
+ procedure AddProgress(AValue: Longint);
+ property Percentage: Longint read GetPercentage;
+ published
+ property Align;
+ property Anchors;
+ property SecondColor: TfpgColor read FSecondColor write SetSecondColor default clWhite;
+ property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle default bsSingle;
+ property Color: TfpgColor read FColor write FColor default clButtonFace;
+ property Enabled;
+ property FirstColor: TfpgColor read FFirstColor write SetFirstColor default clBlack;
+ property Font: TfpgFont read FFont;
+ property Kind: TGaugeKind read FKind write SetGaugeKind default gkHorizontalBar;
+ property MinValue: Longint read FMin write SetMin default 0;
+ property MaxValue: Longint read FMax write SetMax default 100;
+ property Progress: Longint read FPosition write SetProgress;
+ property ShowText: Boolean read FShowText write SetShowText default True;
+ property Visible;
+ end;
+
+
+// A convenience function to quickly create a gauge from code
+function CreateGauge (AOwner: TComponent; ALeft, ATop, AWidth,
+ AHeight: TfpgCoord; AKind: TGaugeKind ): TfpgGauge;
+
+
+implementation
+
+{ This procedure draws a filled arc with a color gradient -
+ to be moved in CanvasBase? }
+procedure FillArcGradient(canvas: TfpgCanvas; X,Y,W,H: TfpgCoord; a1,a2: double; Astart,Astop: TfpgColor);
+var
+ RGBStart: TRGBTriple;
+ RGBStop: TRGBTriple;
+ RDiff, GDiff, BDiff: Integer;
+ count: Integer;
+ i: Integer;
+ newcolor: TRGBTriple;
+begin
+ if Astart = Astop then
+ begin { No gradient, just solid color}
+ canvas.SetColor(Astart);
+ canvas.FillArc(X, Y, W, H, a1, a2);
+ Exit; //==>
+ end;
+
+ RGBStart := fpgColorToRGBTriple(fpgColorToRGB(AStart));
+ RGBStop := fpgColorToRGBTriple(fpgColorToRGB(AStop));
+
+ count := min(H,W);
+ count := count div 2;
+ count := count -2 ;
+
+ RDiff := RGBStop.Red - RGBStart.Red;
+ GDiff := RGBStop.Green - RGBStart.Green;
+ BDiff := RGBStop.Blue - RGBStart.Blue;
+
+
+ { X11 draws arcs at one pixel distance without leaving out pixels, so Line Width
+ of 1 would be appropriate, but GDI doesn't, and therefore Line Width 2 is
+ required to make both work}
+
+ //canvas.SetLineStyle(1,lsSolid);
+ canvas.SetLineStyle(2,lsSolid);
+ for i := 0 to count do
+ begin
+ X := X + 1;
+ Y := Y + 1;
+ W := W - 2;
+ H := H - 2;
+ newcolor.Red := RGBStart.Red + (i * RDiff) div count;
+ newcolor.Green := RGBStart.Green + (i * GDiff) div count;
+ newcolor.Blue := RGBStart.Blue + (i * BDiff) div count;
+ canvas.SetColor(RGBTripleTofpgColor(newcolor));
+ canvas.DrawArc(X, Y, W, H, a1, a2);
+ end;
+end;
+
+function CreateGauge(AOwner: TComponent; ALeft, ATop, AWidth, AHeight: TfpgCoord;
+ AKind: TGaugeKind): TfpgGauge;
+begin
+ Result := TfpgGauge.Create(AOwner);
+ Result.Left := ALeft;
+ Result.Top := ATop;
+ Result.Width := AWidth;
+ Result.Height := AHeight;
+ Result.Kind := AKind;
+end;
+
+{ TfpgGauge }
+
+{ Drawing procedures - they're called from HandlePaint, which takes care of
+ canvas.BeginDraw and Canvas.EndDraw - Shouldn't be used otherwise. }
+procedure TfpgGauge.BackgroundDraw;
+begin
+ {common Background for all kinds }
+
+ {Client area is Widget area, to start with}
+ FClientRect.SetRect(0, 0, Width, Height);
+ Canvas.ClearClipRect;
+ Canvas.Clear(Color);
+ { This must be adjusted according the selected style }
+ Canvas.SetColor(TfpgColor($999999));
+ Canvas.SetLineStyle(1, lsSolid);
+ Canvas.DrawRectangle(FClientRect);
+ { This must be completed and adjusted with border style }
+ InflateRect(FClientRect, -1, -1);
+ with FClientRect do
+ begin
+ { Kind specific Bacground }
+ case FKind of
+ { Currently Text doesn't require additional Bacground }
+ { And so horizontal and vertical bar - Unless style requires it}
+ gkHorizontalBar,
+ gkVerticalBar:
+ begin
+ Canvas.SetLineStyle(1, lsSolid); // just in case background changed that
+ end;
+ gkPie:
+ begin
+ { Round frame for the Pie }
+ Canvas.SetLineStyle(2, lsSolid);
+ Canvas.SetColor(TfpgColor($98b2ed));
+ Canvas.DrawArc(Left, Top, Width, Height , 0, 360);
+ end;
+ gkNeedle:
+ begin
+ { Half a filled circle background for needle }
+ FillArcGradient (Canvas,Left, Top, Width, Height * 2 -1, 0, 180,TfpgColor($425d9b),TfpgColor($98b2ed));
+ Canvas.SetLineStyle(2, lsSolid);
+ //Canvas.SetColor(TfpgColor($3b4c71));
+ Canvas.SetColor(TfpgColor($98b2ed));
+ Canvas.DrawArc(Left, Top, Width, Height * 2 - 1, 0, 180);
+ Canvas.SetLineStyle(1, lsSolid);
+ Canvas.SetColor(TfpgColor($3b4c71));
+ Canvas.DrawLine(Left, Bottom,Left + Width, Bottom);
+ end;
+ gkDial:
+ begin
+ { 270° pie shaped background for Dial }
+ FillArcGradient (Canvas,Left, Top, Width, Height , 225, -270 ,TfpgColor($425d9b),TfpgColor($98b2ed));
+ Canvas.SetLineStyle(2, lsSolid);
+ //Canvas.SetColor(TfpgColor($3b4c71));
+ Canvas.SetColor(TfpgColor($98b2ed));
+ Canvas.DrawArc(Left,Top,Width,Height,225,-270);
+ end;
+ end;
+ end; { with }
+end;
+
+procedure TfpgGauge.TextDraw;
+var
+ S: string;
+ X, Y: Integer;
+begin
+ S := Format('%d%%', [Percentage]);
+ with FClientRect do
+ begin
+ X := (Width - FFont.TextWidth(S)) div 2;
+ Y := (Height - FFont.Height) div 2;
+ if Kind = gkDial then
+ Y := Y + (Y div 2);
+ end;
+{ If contrast is poor we might use a Xor function }
+ Canvas.SetTextColor(FirstColor);
+ Canvas.Font := FFont;
+ Canvas.DrawString(x, y, S);
+end;
+
+procedure TfpgGauge.BarDraw;
+var
+ BarLength: Longint;
+ SavedRect: TfpgRect;
+begin
+ SavedRect := FClientRect; // save client rect for text !!
+ with FClientRect do
+ begin
+ case FKind of
+ gkHorizontalBar:
+ begin
+ BarLength := Longint(Trunc( (Width * Percentage) / 100.0 ) );
+ if BarLength > 0 then
+ begin
+ if BarLength > Width then
+ BarLength := Width;
+ Width := BarLength;
+ // left top
+ Canvas.SetColor(TfpgColor($98b2ed));
+ Canvas.DrawLine(Left, Bottom, Left, Top); // left
+ Canvas.DrawLine(Left, Top, Right, Top); // top
+ // right bottom
+ Canvas.SetColor(TfpgColor($3b4c71));
+ Canvas.DrawLine(Right, Top, Right, Bottom); // right
+ Canvas.DrawLine(Right, Bottom, Left-1, Bottom); // bottom
+ // inside gradient fill
+ InflateRect(FClientRect, -1, -1);
+ Canvas.GradientFill(FClientRect, TfpgColor($425d9b), TfpgColor($97b0e8), gdVertical);
+ end; { if }
+ end;
+ gkVerticalBar:
+ begin
+ BarLength := Longint(Trunc( (Height * Percentage) / 100.0 ) );
+ if BarLength > 0 then
+ begin
+ if BarLength > Height then
+ BarLength := Height;
+ Top := Height - BarLength;
+ Height := BarLength;
+ // left top
+ Canvas.SetColor(TfpgColor($98b2ed));
+ Canvas.DrawLine(Left, Bottom, Left, Top); // left
+ Canvas.DrawLine(Left, Top, Right, Top); // top
+ // right bottom
+ Canvas.SetColor(TfpgColor($3b4c71));
+ Canvas.DrawLine(Right, Top, Right, Bottom); // right
+ Canvas.DrawLine(Right, Bottom, Left-1, Bottom); // bottom
+ // inside gradient fill
+ InflateRect(FClientRect, -1, -1);
+ Canvas.GradientFill(FClientRect, TfpgColor($425d9b), TfpgColor($97b0e8), gdHorizontal);
+ end;
+ end; { if }
+ end; { case }
+ end; { with }
+ FClientRect := SavedRect;
+end;
+
+procedure TfpgGauge.PieDraw;
+var
+ Angle: Double;
+begin
+ with FClientRect do
+ begin
+ Angle := Percentage;
+ Angle := Angle * 3.6; // Percentage to degrees
+ Canvas.SetColor(TfpgColor($425d9b));
+ FillArcGradient (Canvas,Left, Top, Width, Height , 90, -Angle,TfpgColor($425d9b),TfpgColor($98b2ed));
+ end;
+end;
+
+procedure TfpgGauge.NeedleDraw;
+var
+ Center: TPoint;
+ Radius: TPoint;
+ Angle: Double;
+begin
+ with FClientRect do
+ begin
+ if Percentage > 0 then
+ begin
+ { Compute the center }
+ Center := CenterPoint(Rect(Left,Top,Width,Height));
+ { Make needle 4 pixel shorter than gauge radius to accomodate border }
+ Radius.X := Center.X - 4;
+ Radius.Y := (Bottom - 4);
+ Canvas.SetLineStyle(2,lsSolid);
+ Angle := (Pi * ((Percentage / 100.0))); // percentage to radiants
+ Canvas.SetColor(TfpgColor($3b4c71));
+ Canvas.SetLineStyle(2,lsSolid);
+ Canvas.DrawLine(Center.X, FClientRect.Bottom,
+ Integer(round(Center.X - (Radius.X * Cos(Angle)))),
+ Integer(round((FClientRect.Bottom) - (Radius.Y * Sin(Angle)))));
+ end;
+ end;
+end;
+
+procedure TfpgGauge.DialDraw;
+var
+ Center: TPoint;
+ Radius: TPoint;
+ Angle: Double;
+ CenterDot: Integer;
+begin
+ with FClientRect do
+ begin
+ if Percentage >= 0 then
+ begin
+ { Compute the center }
+ Center := CenterPoint(Rect(Left,Top,Width,Height));
+ { Make needle 3 pixel shorter than gauge radius }
+ Radius.X := Center.X -3;
+ Radius.Y := Center.Y -3;
+ {compute centre circle size}
+ CenterDot := (Width + Height) div 40; // approx. scaled to 1/10 of widget size:
+ if CenterDot < 2 then
+ CenterDot := 2;
+ { draw needle centre circle }
+ Canvas.SetColor(TfpgColor($3b4c71));
+ Canvas.FillArc(Center.X - CenterDot, Center.Y - CenterDot,CenterDot * 2, CenterDot * 2,0,360);
+ { draw needle }
+ Angle := (Pi * ((Percentage / (100 * 2 / 3)) + -0.25));
+ Canvas.SetLineStyle(2,lsSolid);
+ Canvas.DrawLine(Center.X, Center.Y,
+ Integer(round(Center.X - ( Radius.X * cos(Angle)))),
+ Integer(round((Center.Y) - (Radius.Y * Sin(Angle)))));
+ end; { if }
+ end; { with }
+end;
+
+procedure TfpgGauge.HandlePaint;
+begin
+ inherited HandlePaint;
+ Canvas.BeginDraw(True);
+ {Paint Background and adjust FClientRect according style and BorderStyle}
+ BackgroundDraw;
+ {Paint foreground according selected Kind}
+ case FKind of
+ gkHorizontalBar,
+ gkVerticalBar:
+ BarDraw;
+ gkPie:
+ PieDraw;
+ gkNeedle:
+ NeedleDraw;
+ gkDial:
+ DialDraw;
+ end;
+ {Add Text if required}
+ if ShowText then
+ TextDraw;
+ Canvas.EndDraw;
+end;
+
+procedure TfpgGauge.SetGaugeKind(AValue: TGaugeKind);
+begin
+ if AValue <> FKind then
+ begin
+ FKind := AValue;
+ RePaint;
+ end;
+end;
+
+procedure TfpgGauge.SetShowText(AValue: Boolean);
+begin
+ if AValue <> FShowText then
+ begin
+ FShowText := AValue;
+ RePaint;
+ end;
+end;
+
+procedure TfpgGauge.SetBorderStyle(AValue: TBorderStyle);
+begin
+ if AValue <> FBorderStyle then
+ begin
+ FBorderStyle := AValue;
+ {TODO - Implement Border style }
+ // Graeme: Wouldn't descending from TfpgBevel give you this functionality already?
+ // It could be a option.
+ //RePaint;
+ end;
+end;
+
+procedure TfpgGauge.SetFirstColor(AValue: TfpgColor);
+begin
+ if AValue <> FFirstColor then
+ begin
+ FFirstColor := AValue;
+ {TODO - allow user colors}
+ //RePaint;
+ end;
+end;
+
+procedure TfpgGauge.SetSecondColor(AValue: TfpgColor);
+begin
+ if AValue <> FSecondColor then
+ begin
+ FSecondColor := AValue;
+ {TODO - allow user colors}
+ //RePaint;
+ end;
+end;
+
+procedure TfpgGauge.SetMin(AValue: Longint);
+begin
+ if AValue <> FMin then
+ begin
+ // correct input errors
+ if AValue > FMax then
+ if not (csLoading in ComponentState) then
+ FMax := AValue + 1;
+ if FPosition < AValue then
+ FPosition := AValue;
+ // then update
+ FMin := AValue;
+ RePaint;
+ end;
+end;
+
+procedure TfpgGauge.SetMax(AValue: Longint);
+begin
+ if AValue <> FMax then
+ begin
+ // correct input errors
+ if AValue < FMin then
+ if not (csLoading in ComponentState) then
+ FMin := AValue - 1;
+ if FPosition > AValue then
+ FPosition := AValue;
+ // then update
+ FMax := AValue;
+ RePaint;
+ end;
+end;
+
+procedure TfpgGauge.SetProgress(AValue: Longint);
+var
+ CurrPercentage: Longint;
+ MustRepaint: Boolean;
+begin
+ CurrPercentage := GetPercentage;
+ MustRepaint := False;
+
+ if AValue < FMin then
+ AValue := FMin
+ else if AValue > FMax then
+ AValue := FMax;
+
+ if FPosition <> AValue then
+ begin // Value has changed
+ FPosition := AValue;
+ if CurrPercentage <> Percentage then // Visible value has changed
+ MustRepaint := True;
+ { TODO Check against low and high watermarks }
+ end;
+ if MustRepaint then
+ RePaint;
+end;
+
+function TfpgGauge.GetPercentage: Longint;
+Var
+ V,T: Longint;
+begin
+ T := FMax - FMin;
+ V := FPosition - FMin;
+ if T = 0 then
+ Result := 0
+ else
+ Result := Longint(Trunc( (V * 100.0) / T ));
+end;
+
+constructor TfpgGauge.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ Focusable := False;
+ FWidth := 100;
+ FHeight := 25;
+ FKind := gkHorizontalBar;
+ FSecondColor := clWhite;
+ FFirstColor := clBlack;
+ FColor := TfpgColor($c4c4c4); //clInactiveWgFrame;
+ FMax := 100;
+ FMin := 0;
+ FPosition := 0;
+ FShowText := True;
+ FBorderStyle := bsNone;
+ FFont := fpgStyle.DefaultFont;
+end;
+
+procedure TfpgGauge.AddProgress(AValue: Longint);
+begin
+ Progress := FPosition + AValue;
+end;
+
+end.
+