Voici un composant basé sur la librairie Graphics32 qui permet d'être utilisé en tant que barre de progression animée. Une option "marquee" permet de rendre son état indéterminé.
Pour compiler la source, il vous faut les librairies:
Graphics32 :
http://graphics32.org/wiki/
The GR32 Extension Components Pack :
http://code.google.com/p/gr32ex/
Pour tester l'exemple, renommer le fichier "Exemple.exec" en "Exemple.exe" (merci Nicolas)
Source / Exemple :
unit GrProgressBar;
(* ***** BEGIN LICENSE BLOCK *****
*
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
*
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
*
- The Original Code is GrProgressBar
*
- The Initial Developer of the Original Code is
- Yann Papouin <yann.papouin at @ gmail.com>
*
- ***** END LICENSE BLOCK ***** *)
{ $DEFINE DEBUG}
interface
uses
{$IfDef DEBUG}DbugIntf,{$ENDIF}
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Math,
GR32, GR32_Image, GR32_Polygons, SimpleTimer;
type
TBarDirection = (
bdRight,
bdLeft
);
TGrProgressbar = class(TCustomImage32)
private
FModulo : integer;
FInvalidateColor : boolean;
FApplyingSystemColor: boolean;
FColor1 : TColor32;
FColor2 : TColor32;
FBarDirection : TBarDirection;
FLimitRight : integer;
FLimitLeft : integer;
FBar : TPolygon32;
FAniTimer: TSimpleTimer;
FInvalidatePattern : boolean;
FFiller: TBitmapPolygonFiller;
FUseSystemColors: boolean;
FAnimated: boolean;
FMax: integer;
FMin: integer;
FPosition: integer;
FMarquee: boolean;
FMarqueeWidth: single;
FMarqueeSpeed: integer;
FColor: TColor;
FAnimAtMax: boolean;
FFlip: boolean;
procedure SetUseSystemColors(const Value: boolean);
procedure SetAnimated(const Value: boolean);
{ Déclarations privées }
procedure ComputePattern;
procedure InvalidatePattern;
procedure SetMax(const Value: integer);
procedure SetMin(const Value: integer);
procedure SetPosition(Value: integer);
procedure DoAniTimer(Sender: TObject);
procedure SetInterval(const Value: Cardinal);
function GetInterval: Cardinal;
procedure SetMarquee(const Value: boolean);
procedure SetMarqueeWidth(Value: single);
procedure SetMarqueeSpeed(const Value: integer);
procedure SetColor(const Value: TColor);
procedure SetAnimAtMax(const Value: boolean);
procedure SetFlip(const Value: boolean);
protected
procedure BuildBar;
procedure Resize; override;
procedure DoPaintBuffer; override;
function InternalPaintBuffer(aBitmap32: TBitmap32): Boolean;
procedure DrawBackground(aBitmap32: TBitmap32);
procedure CMStyleChanged( var msg: TMessage); message WM_THEMECHANGED;
procedure CMShowingChanged(var msg: TMessage); message CM_SHOWINGCHANGED;
public
{ Déclarations publiques }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Loaded; override;
published
property Align;
property AlignWithMargins;
property Anchors;
property Margins;
property AnimAtMax : boolean read FAnimAtMax write SetAnimAtMax;
property Animated : boolean read FAnimated write SetAnimated;
property Interval : Cardinal read GetInterval write SetInterval;
property UseSystemColors : boolean read FUseSystemColors write SetUseSystemColors;
property Max : integer read FMax write SetMax;
property Min : integer read FMin write SetMin;
property Position : integer read FPosition write SetPosition;
property Marquee : boolean read FMarquee write SetMarquee;
property MarqueeWidth : single read FMarqueeWidth write SetMarqueeWidth;
property MarqueeSpeed : integer read FMarqueeSpeed write SetMarqueeSpeed;
property Color : TColor read FColor write SetColor;
property Flip : boolean read FFlip write SetFlip;
end;
procedure Register;
implementation
uses
Themes, UxTheme;
var
SystemColor : TColor;
procedure Register;
begin
RegisterComponents('GLDali', [TGrProgressbar]);
end;
function Linearize(Ax, Ay, Bx, By, Value : Single): Single;
begin
if (Bx <> Ax) and (Ay <> By) then
result := Ay + (Value - Ax) / (Bx - Ax) * (By-Ay)
else
result := 0;
end;
{ TGrProgressbar }
constructor TGrProgressbar.Create(AOwner: TComponent);
begin
inherited;
Height := 15;
Width := 150;
FFlip := true;
FModulo := Height;
FMin := 0;
FMax := 100;
FMarqueeWidth := 0.25;
FMarqueeSpeed := 5;
FBar := TPolygon32.Create;
FAniTimer := TSimpleTimer.CreateEx(50, DoAniTimer);
FFiller := TBitmapPolygonFiller.Create;
ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents,
csSetCaption, csParentBackground, csDoubleClicks, csReplicatable, csPannable];
//Color := clHighlight;
Animated := true;
end;
destructor TGrProgressbar.Destroy;
begin
FAniTimer.Free;
FFiller.Free;
FBar.Free;
inherited;
end;
procedure TGrProgressbar.Loaded;
begin
inherited Loaded;
Buffer.SetSize(Width,Height);
BuildBar;
InvalidatePattern;
{$IfDef DEBUG}SendDebugFmt('Progress bar named %s loaded',[Name]);{$ENDIF}
end;
procedure TGrProgressbar.InvalidatePattern;
begin
FInvalidatePattern := true;
Changed;
end;
// The pattern is computed only once
// It need to be recomputed after a resize of the control or a color (theme) change
procedure TGrProgressbar.ComputePattern;
var
Polygon : TPolygon32;
PatternWidth : integer;
PatternHeight : integer;
begin
PatternHeight := Height;
PatternWidth := PatternHeight*2;
FModulo := PatternWidth;
if Assigned(FFiller.Pattern) then
FFiller.Pattern.Free;
// First create a rectangle with the wanted color
FFiller.Pattern := TBitmap32.Create;
FFiller.Pattern.SetSize(PatternWidth,PatternHeight);
FFiller.Pattern.FillRectTS(0,0,PatternWidth,PatternHeight, FColor1);
PatternHeight := PatternHeight;
Polygon := TPolygon32.Create;
// Next draw a transparent parallelogram (a rectangle that is skewed)
with Polygon do
begin
Antialiased := true;
Add(FixedPoint(0,0));
Add(FixedPoint(PatternWidth div 2,0));
Add(FixedPoint(PatternWidth, PatternHeight));
Add(FixedPoint(PatternWidth div 2, PatternHeight));
DrawFill(FFiller.Pattern, SetAlpha(FColor2,127));
// Mirror operation on vertical axis if needed
if Flip then
FFiller.Pattern.FlipHorz(FFiller.Pattern);
end;
Polygon.Free;
// Finally draw a crystal effect
FFiller.Pattern.FillRectTS(0, 0, PatternWidth, PatternHeight div 2, SetAlpha(CLWhite32,60));
end;
procedure TGrProgressbar.BuildBar;
var
LPosition : integer;
Offset : integer;
Inflate : integer;
DrawRect : TRect;
begin
if not Assigned(Parent) then
Exit;
// Inflate value help to reduce the bar size from it's background
Inflate := -3;
// An offset is here to correct the 3D effect
Offset := -1;
// Get X coordinate from the Min, Max and Position Values
if not Marquee then
begin
LPosition := Round(Linearize(Min, -2* Inflate + Offset, Max, Width, Position));
{$IfDef DEBUG}SendInteger('LPosition',LPosition);{$ENDIF}
end;
DrawRect := ClientRect;
// Limits are used by the marquee animation
FLimitRight := DrawRect.Right + Inflate;
FLimitLeft := DrawRect.Left - Inflate + Offset;
if not Marquee then
DrawRect.Right := LPosition
else
DrawRect.Right := Round(DrawRect.Right*MarqueeWidth); // setting the marquee width by using it's percent value
// Apply the offset
DrawRect.Top := DrawRect.Top + Offset;
DrawRect.Left := DrawRect.Left + Offset;
InflateRect(DrawRect, Inflate, Inflate);
{$IfDef DEBUG}SendInteger('DrawRect.Left',DrawRect.Left);{$ENDIF}
{$IfDef DEBUG}SendInteger('DrawRect.Right',DrawRect.Right);{$ENDIF}
// Drawing the bar polygon
with FBar do
begin
Clear;
if DrawRect.Left <> DrawRect.Right then
begin
Add(FixedPoint(DrawRect.Left,DrawRect.Top));
Add(FixedPoint(DrawRect.Right,DrawRect.Top));
Add(FixedPoint(DrawRect.Right,DrawRect.Bottom));
Add(FixedPoint(DrawRect.Left,DrawRect.Bottom));
end;
end;
end;
procedure TGrProgressbar.DrawBackground(aBitmap32: TBitmap32);
var
R: TRect;
cl: TColor;
FProgressThemeData : HTHEME;
begin
if UseThemes then
begin
// Apply the WinXP style
FProgressThemeData := OpenThemeData(Handle, 'Progress');
R := ClientRect;
DrawThemeBackground(FProgressThemeData, aBitmap32.Handle, PP_BAR, 0, R, nil);
end
else
begin
// Mimic the Win2K style by drawing a lowered borders
aBitmap32.RaiseRectTS(0,0,width,height,-50);
end;
end;
function TGrProgressbar.InternalPaintBuffer(aBitmap32: TBitmap32): Boolean;
var
Polygon : TPolygon32;
LPosition : integer;
Inflate : integer;
DrawRect : TRect;
begin
// Do not try to draw anything is there is no parent for self
if not Assigned(Parent) then
begin
aBitmap32.Clear(clRed32);
Exit;
end;
// Recalc color after a theme change
if FInvalidateColor then
begin
UseSystemColors := UseSystemColors;
FInvalidateColor := false;
end;
// Recalc pattern on resize or color change
if FInvalidatePattern then
begin
FInvalidatePattern := false;
ComputePattern;
end;
// Draw the control background (themed if we can)
DrawBackground(aBitmap32);
// Draw the bar itself by using the pattern
if ((Position > 0) and not Marquee) or Marquee then
begin
with FBar do
begin
// The pattern is automatically repeated on x-axis and y-axis
DrawFill(aBitmap32, FFiller);
DrawEdge(aBitmap32, SetAlpha(clBlack32, 100));
end;
end;
result := true;
end;
procedure TGrProgressbar.DoPaintBuffer;
begin
inherited;
InternalPaintBuffer(self.Buffer);
end;
// Recompute bar size and pattern size
procedure TGrProgressbar.Resize;
begin
inherited;
BuildBar;
InvalidatePattern;
end;
procedure TGrProgressbar.DoAniTimer(Sender: TObject);
begin
if not FMarquee then
begin
if not ((Position = Max) and not FAnimAtMax) then
begin
// Animate pattern of the bar with a scroll
FFiller.OffsetX := (FFiller.OffsetX-1) mod FModulo;
end;
end
else
if FMarquee then
begin
// Changing direction from right to left
if (FBar.Points[0][1].X >= Fixed(FLimitRight)) and (FBarDirection = bdRight) then
begin
FBarDirection := bdLeft;
end;
// Changing direction from left to right
if (FBar.Points[0][0].X <= Fixed(FLimitLeft)) and (FBarDirection = bdLeft) then
begin
FBarDirection := bdRight;
end;
// Move the bar itself without moving the pattern
case FBarDirection of
bdRight :
begin
FBar.Offset(Fixed(FMarqueeSpeed), Fixed(0));
end;
bdLeft :
begin
FBar.Offset(Fixed(-FMarqueeSpeed), Fixed(0));
end;
end;
end;
// Repaint control
Changed;
end;
// Boolean value to enable/disable animation
procedure TGrProgressbar.SetAnimated(const Value: boolean);
begin
FAnimated := Value;
if Visible then
FAniTimer.Enabled := FAnimated;
// Reset pattern position
if not FAnimated then
FFiller.OffsetX := 0;
end;
// Continue scrolling when Position = Max ?
procedure TGrProgressbar.SetAnimAtMax(const Value: boolean);
begin
FAnimAtMax := Value;
end;
function TGrProgressbar.GetInterval: Cardinal;
begin
result := FAniTimer.Interval;
end;
procedure TGrProgressbar.SetInterval(const Value: Cardinal);
begin
FAniTimer.Interval := Value;
end;
// A marquee progress bar is a progress bar with unknow min, max and position values
// It's a feedback to state to warn the user that a thing is currently doing something
procedure TGrProgressbar.SetMarquee(const Value: boolean);
begin
if FMarquee <> Value then
begin
FMarquee := Value;
if not FMarquee then
begin
Position := Min;
end;
end;
BuildBar;
end;
// The marquee width is it's width in percentage of the bar width
procedure TGrProgressbar.SetMarqueeWidth(Value: single);
begin
Value := Math.Min(1.0,Value);
Value := Math.Max(0.0,Value);
if FMarqueeWidth <> Value then
begin
FMarqueeWidth := Value;
BuildBar;
end;
end;
procedure TGrProgressbar.SetMarqueeSpeed(const Value: integer);
begin
if FMarqueeSpeed <> Value then
begin
FMarqueeSpeed := Value;
FMarqueeSpeed := Math.Min(100,FMarqueeSpeed);
FMarqueeSpeed := Math.Max(1,FMarqueeSpeed);
end;
end;
procedure TGrProgressbar.SetMax(const Value: integer);
begin
if FMax <> Value then
begin
FMax := Value;
BuildBar;
end;
Position := Position;
end;
procedure TGrProgressbar.SetMin(const Value: integer);
begin
if FMin <> Value then
begin
FMin := Value;
BuildBar;
end;
Position := Position;
end;
procedure TGrProgressbar.SetPosition(Value: integer);
begin
// First, limit the setted value to min and max position
Value := Math.Min(Max, Value);
Value := Math.Max(Min, Value);
// Disable marquee if we write a new position
FMarquee := false;
if FPosition <> Value then
begin
FPosition := Value;
BuildBar;
Changed;
end;
end;
procedure TGrProgressbar.SetUseSystemColors(const Value: boolean);
var
ABmp : TBitmap;
PickColor: TColor;
FProgressThemeData : HTHEME;
begin
FUseSystemColors := Value;
if FUseSystemColors then
begin
FApplyingSystemColor := true;
// Check if Windows Themes are enabled
if UseThemes then
begin
// A small way (not really clean) to get the color of a themed progress bar
if (SystemColor = clNone) then
begin
{$IfDef DEBUG}SendDebug('SystemColor is None');{$ENDIF}
// We create a local bitmap where we painting on it a themed progress bar
ABmp := TBitmap.Create;
ABmp.SetSize(16,16);
FProgressThemeData := OpenThemeData(Handle, 'Progress');
DrawThemeBackground(FProgressThemeData, ABmp.Canvas.Handle, PP_CHUNK, 0, ABmp.Canvas.ClipRect, nil);
CloseThemeData(FProgressThemeData);
// After painting, we pick the color at a logical position
// Just hope that there is a valid color here
PickColor := ABmp.Canvas.Pixels[2,2];
if PickColor <> CLR_INVALID then
begin
SystemColor := PickColor;
{$IfDef DEBUG}SendDebug('System color found, extracted from BITMAP');{$ENDIF}
end
else
begin
SystemColor := clHighlight;
{$IfDef DEBUG}SendDebug('System color not found, using the Highlight one');{$ENDIF}
end;
ABmp.Free;
end;
Color := SystemColor;
{$IfDef DEBUG}SendDebug('Using previous computed system color');{$ENDIF}
end
else
begin
// If theming is not enabled then use the default highlight color
Color := clHighlight;
{$IfDef DEBUG}SendDebug('No theme is used, default highlight color');{$ENDIF}
end;
FApplyingSystemColor := false;
end;
end;
procedure TGrProgressbar.SetColor(const Value: TColor);
var
H,S,L : Byte;
begin
if FColor <> Value then
begin
// Disable system color only if SetColor is not called by the UseSystemColors method
if not FApplyingSystemColor then
UseSystemColors := false;
FColor := Value;
// Extracting the Wincolor to Hue, Saturation and Luminosity values
FColor1 := Color32(FColor);
RGBtoHSL(FColor1, H, S, L);
// Small way to set the color of the slashes
if Intensity(FColor1) > 160
then
FColor2 := clBlack32
else
FColor2 := clWhite32;
// Redraw the pattern with new colors
InvalidatePattern;
end;
end;
// Property that allow the flippig of the slashes
procedure TGrProgressbar.SetFlip(const Value: boolean);
begin
FFlip := Value;
InvalidatePattern;
end;
/// Catched Windows messages
// If the theme is changed by a new theme then invalidate the system color
// It will be recomputed by the first progress bar with UseSystemColor property enabled
procedure TGrProgressbar.CMStyleChanged(var msg: TMessage);
begin
{$IfDef DEBUG}SendDebug('TGrProgressbar.CMStyleChanged');{$ENDIF}
// Update colors
SystemColor := clNone;
FInvalidateColor := true;
end;
// Stop the timer when this control is not visible, else re-enable it
procedure TGrProgressbar.CMShowingChanged(var msg: TMessage);
begin
inherited;
{$IfDef DEBUG}SendDebug('CMShowingChanged');{$ENDIF}
if Showing then
begin
if Animated and not FAniTimer.Enabled then
begin
FAniTimer.Enabled := true;
{$IfDef DEBUG}SendDebugFmt('Timer of %s started',[Name]);{$ENDIF}
end;
end
else
begin
if Animated and FAniTimer.Enabled then
begin
FAniTimer.Enabled := false;
{$IfDef DEBUG}SendDebugFmt('Timer of %s stopped',[Name]);{$ENDIF}
end;
end;
end;
initialization
SystemColor := clNone;
finalization
end.
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.