{ ----------------------------------------------------------------------------} { A Gradient Fill component for Delphi. } { TGradientFill, Copyright 1995, Curtis White. All Rights Reserved. } { TNetGradient, Copyright 1997, Heiko Webers. All Rights Reserved. } { This component can be freely used and distributed in commercial and private } { environments, provided this notice is not modified in any way. } { ----------------------------------------------------------------------------} { Feel free to contact me if you have any questions, comments or suggestions } { at cwhite@teleport.com } { Or me at heikowebers@usa.net } { ----------------------------------------------------------------------------} { Date last modified: 18/10/2009 } { ----------------------------------------------------------------------------} { ----------------------------------------------------------------------------} { TNetGradient v2.1 } { ----------------------------------------------------------------------------} { Description: } { A gradient fill like in the new Netscape Communicator Options Box. } { Features: } { The begin and end colors can be any colors. } { The fill direction can be set to Right-To-Left or Left-To-Right. } { The number of colors, between 1 and 255 can be set for the fill. } { The Caption can be anything and anywhere on TNetGradient. } { ----------------------------------------------------------------------------} { ----------------------------------------------------------------------------} { Revision History: } { 1.00: Initial release } { 1.00: Changed to TNetGradient } { 1.01: Border Update by Enzo Scozzaro www.scozzaro.it www.thefox.it } { 2.00: +Caption Alignment, +Layout, +DataSource } { 2.01: +SubCaption, +Font, +MarginLeft, +MarginTop, +SubCapField } { 2.03: -Bug TextLetf } { 2.04: FillDirection: +ftTopToBottom, +ftBottomToTop } { 2.05: +Begin/EndUpdate, Fix crash in frames, Fix memory leaks, Cleanup } { 2.1: +FlatBorder, +TDBNetGradient, allow children controls, Cleanup } { ----------------------------------------------------------------------------} unit SMNetGradient; {$MODE Delphi} interface uses LCLIntf, LCLType, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, GraphType, Db, DBCtrls, LMessages; type { Direction of fill } TFillDirection = (fdLeftToRight, fdRightToLeft, ftTopToBottom, ftBottomToTop); { Range of valid colors } TNumberOfColors = 1..255; TLabelBevel = TBevelCut; TMargin = 0..MaxInt; TPointArray = array of TPoint; TCustomNetGradient = class; { TSubCaption } TSubCaption = class(TPersistent) private { Private-Deklarationen } Parent: TCustomNetGradient; FCaption: TCaption; FFont: TFont; FHotTrack: Boolean; FMarginLeft: Integer; FMarginTop: Integer; FVisible: Boolean; procedure OnFontChanged(Sender: TObject); protected { Protected declarations } procedure SetCaption(const Value: TCaption); procedure SetFont(Value: TFont); procedure SetMarginLeft(Value: Integer); procedure SetMarginTop(Value: Integer); procedure SetVisible(Value: Boolean); public { Public declarations } constructor Create(AOwner: TCustomNetGradient); overload; destructor Destroy; override; procedure Assign(Source: TPersistent); override; published { Published-Deklarationen } property Caption: TCaption read FCaption write SetCaption; property HotTrack: Boolean read FHotTrack write FHotTrack default False; property MarginLeft: Integer read FMarginLeft write SetMarginLeft default 5; property MarginTop: Integer read FMarginTop write SetMarginTop default 0; property Font: TFont read FFont write SetFont; property Visible: Boolean read FVisible write SetVisible default True; end; { TCustomNetGradient } TCustomNetGradient = class(TCustomControl) private //*** Enzo *** Bordi FBevelInner: TLabelBevel; FBevelOuter: TLabelBevel; //*** Emzp *** Allineamento Caption FAlignment : TAlignment; FBorderColor: TColor; FFlatBorder: Boolean; FLayout : TTextLayout; //FMargin: TMargin; { Variables for properties } FDirection: TFillDirection; FBeginColor: TColor; FEndColor: TColor; // FCenter: Boolean; FNumberOfColors: TNumberOfColors; FTextTop : Integer; FTextLeft: Integer; FSubCaption: TSubCaption; FUpdateCount: Integer; procedure OnFontChanged(Sender: TObject); procedure Changed; function GetBorderPoints(const R: TRect): TPointArray; procedure SetBorderColor(const Value: TColor); { Procedures for setting property values } procedure SetFillDirection(Value: TFillDirection); procedure SetBeginColor(Value: TColor); procedure SetEndColor(Value: TColor); procedure SetFlatBorder(const Value: Boolean); procedure SetNumberOfColors(Value: TNumberOfColors); procedure SetSubCaption(const Value: TSubCaption); procedure SetTextTop(Value: Integer); procedure SetTextLeft(Value: Integer); { Fill procedure } procedure GradientFill; protected procedure CMFontChanged(var Message: TLMessage); message CM_FONTCHANGED; procedure CMTextChanged(var Message: TLMessage); message CM_TEXTCHANGED; procedure Paint; override; procedure SetAlignment(Value: TAlignment); procedure SetLayout(Value: TTextLayout); procedure SetBevelInner(Value: TLabelBevel); procedure SetBevelOuter(Value: TLabelBevel); procedure WMEraseBkgnd(var Message: TLMEraseBkgnd); message LM_ERASEBKGND; property CaptionAlignment: TAlignment read FAlignment write SetAlignment default taLeftJustify; property CaptionLayout: TTextLayout read FLayout write SetLayout default tlCenter; property BevelInner: TLabelBevel read FBevelInner write SetBevelInner default bvNone; property BevelOuter: TLabelBevel read FBevelOuter write SetBevelOuter default bvRaised; { Starting color of fill } property BeginColor: TColor read FBeginColor write SetBeginColor default clBlue; property BorderColor: TColor read FBorderColor write SetBorderColor default clWhite; { Ending color of fill } property EndColor: TColor read FEndColor write SetEndColor default clBlack; property FlatBorder: Boolean read FFlatBorder write SetFlatBorder default False; { Direction of fill } property FillDirection: TFillDirection read FDirection write SetFillDirection default fdLeftToRight; { Number of colors to use in the fill (1 - 256) - default is 255. If 1 } { then it uses the Begin Color. } property NumberOfColors: TNumberOfColors read FNumberOfColors write SetNumberOfColors default 255; property TextTop: Integer read FTextTop write SetTextTop default 0; property TextLeft: Integer read FTextLeft write SetTextLeft default 0; property SubCaption: TSubCaption read FSubCaption write SetSubCaption; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure BeginUpdate; procedure EndUpdate; end; { TNetGradient } TNetGradient = class (TCustomNetGradient) published property Anchors; property BevelInner; property BevelOuter; property BeginColor; property BorderColor; property CaptionAlignment; property CaptionLayout; property EndColor; property FlatBorder; property FillDirection; property NumberOfColors; property Font; property Caption; property TextTop; property TextLeft; property SubCaption; //default properties property Align; property Anchors; property BorderSpacing; property BorderWidth; property DragCursor; property DragMode; property Enabled; property ParentShowHint; property PopupMenu; property ShowHint; property Visible; property OnClick; property OnDblClick; property OnDragDrop; property OnDragOver; property OnEndDrag; property OnMouseDown; property OnMouseMove; property OnMouseUp; end; { TDBNetGradient } TDBNetGradient = class (TCustomNetGradient) private FDataLink : TFieldDataLink; FSubCapField: Boolean; procedure DataChange(Sender: TObject); function GetDataField: String; function GetDataSource: TDataSource; procedure SetDataField(const Value: String); procedure SetDataSource(Value: TDataSource); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property DataField: String read GetDataField write SetDataField; property DataSource: TDataSource read GetDataSource write SetDataSource; property SubCapField: Boolean read FSubCapField write FSubCapField default false; property CaptionAlignment; property CaptionLayout; property BevelInner; property BevelOuter; property BeginColor; property BorderColor; property EndColor; property FlatBorder; property FillDirection; property NumberOfColors; property Font; property Caption; property TextTop; property TextLeft; property SubCaption; //default properties property Align; property Anchors; property BorderSpacing; property BorderWidth; property DragCursor; property DragMode; property Enabled; property ParentShowHint; property PopupMenu; property ShowHint; property Visible; property OnClick; property OnDblClick; property OnDragDrop; property OnDragOver; property OnEndDrag; property OnMouseDown; property OnMouseMove; property OnMouseUp; end; implementation { TCustomNetGradient } { Override the constructor to initialize variables } constructor TCustomNetGradient.Create(AOwner: TComponent); begin { Inherit original constructor } inherited Create(AOwner); ControlStyle := ControlStyle + [csAcceptsControls, csNoFocus]; { Add new initializations } FAlignment := taLeftJustify; FLayout := tlCenter; FBevelInner := bvNone; FBevelOuter := bvRaised; Height := 25; Width := 400; FBeginColor := clSilver; FEndColor := $00A56D39; FBorderColor := clWhite; FDirection := fdLeftToRight; FNumberOfColors:= 255; //FTextLeft := 0; //FTextTop := 0; FSubCaption := TSubCaption.Create(Self); end; destructor TCustomNetGradient.Destroy; begin FSubCaption.Destroy; inherited Destroy; end; { Set begin color when property is changed } procedure TCustomNetGradient.SetBeginColor(Value: TColor); begin if Value <> FBeginColor then begin FBeginColor := Value; Changed; end; end; { Set end color when property is changed } procedure TCustomNetGradient.SetEndColor(Value: TColor); begin if Value <> FEndColor then begin FEndColor := Value; Changed; end; end; procedure TCustomNetGradient.SetFlatBorder(const Value: Boolean); begin if FFlatBorder <> Value then begin FFlatBorder := Value; Changed; end; end; { Set the number of colors to be used in the fill } procedure TCustomNetGradient.SetNumberOfColors(Value: TNumberOfColors); begin if Value <> FNumberOfColors then begin FNumberOfColors := Value; Changed; end; end; procedure TCustomNetGradient.SetSubCaption(const Value: TSubCaption); begin FSubCaption.Assign(Value); end; // Set the Position of the Caption (Top) procedure TCustomNetGradient.SetTextTop(Value: Integer); begin if Value <> FTextTop then begin FTextTop := Value; Changed; end; end; // Set the Position of the Caption (Left) procedure TCustomNetGradient.SetTextLeft(Value: Integer); begin if Value <> FTextLeft then begin FTextLeft := Value; Changed; end; end; { Perform the fill when paint is called } procedure TCustomNetGradient.Paint; begin GradientFill; end; { Gradient fill procedure - the actual routine } procedure TCustomNetGradient.GradientFill; var { Set up working variables } BeginRGBValue : array[0..2] of Byte; { Begin RGB values } RGBDifference : array[0..2] of integer; { Difference between begin and end } { RGB values } ColorBand , rp: TRect; { Color band rectangular coordinates } I : Integer; { Color band index } R : Byte; { Color band Red value } G : Byte; { Color band Green value } B : Byte; { Color band Blue value } WorkBmp : TBitmap; { Off screen working bitmap } TS : TTextStyle; BorderOffset: Integer; begin { Create the working bitmap and set its width and height } WorkBmp := TBitmap.Create; WorkBmp.Width := Width; WorkBmp.Height := Height; { Use working bitmap to draw the gradient } with WorkBmp do begin Rp := GetClientRect; { Extract the begin RGB values } case FDirection of fdLeftToRight, ftTopToBottom: begin { Set the Red, Green and Blue colors } BeginRGBValue[0] := GetRValue (ColorToRGB (FBeginColor)); BeginRGBValue[1] := GetGValue (ColorToRGB (FBeginColor)); BeginRGBValue[2] := GetBValue (ColorToRGB (FBeginColor)); { Calculate the difference between begin and end RGB values } RGBDifference[0] := GetRValue (ColorToRGB (FEndColor)) - BeginRGBValue[0]; RGBDifference[1] := GetGValue (ColorToRGB (FEndColor)) - BeginRGBValue[1]; RGBDifference[2] := GetBValue (ColorToRGB (FEndColor)) - BeginRGBValue[2]; end; fdRightToLeft,ftBottomToTop: begin { Set the Red, Green and Blue colors } BeginRGBValue[0] := GetRValue (ColorToRGB (FEndColor)); BeginRGBValue[1] := GetGValue (ColorToRGB (FEndColor)); BeginRGBValue[2] := GetBValue (ColorToRGB (FEndColor)); { Calculate the difference between begin and end RGB values } RGBDifference[0] := GetRValue (ColorToRGB (FBeginColor)) - BeginRGBValue[0]; RGBDifference[1] := GetGValue (ColorToRGB (FBeginColor)) - BeginRGBValue[1]; RGBDifference[2] := GetBValue (ColorToRGB (FBeginColor)) - BeginRGBValue[2]; end; end; { Set the pen style and mode } Canvas.Pen.Style := psSolid; Canvas.Pen.Mode := pmCopy; case FDirection of { Calculate the color band's left and right coordinates } { for LeftToRight and RightToLeft fills } fdLeftToRight, fdRightToLeft: begin ColorBand.Top := 0; ColorBand.Bottom := Height; end; ftTopToBottom, ftBottomToTop: begin ColorBand.Left := 0; ColorBand.Right := Width; end; end; { Perform the fill } if FNumberOfColors = 1 then begin Canvas.Brush.Color := FBeginColor; Canvas.FillRect(rp); end else begin for I := 0 to FNumberOfColors do begin case FDirection of { Calculate the color band's left and right coordinates } fdLeftToRight, fdRightToLeft: begin ColorBand.Left := MulDiv (I, Self.Width, FNumberOfColors); ColorBand.Right := MulDiv (I + 1, Self.Width, FNumberOfColors); end; ftTopToBottom, ftBottomToTop: begin ColorBand.Top := MulDiv (I, Self.Height, FNumberOfColors); ColorBand.Bottom := MulDiv (I + 1, Self.Height, FNumberOfColors); end; end; { Calculate the color band's color } R := BeginRGBValue[0] + MulDiv (I, RGBDifference[0], FNumberOfColors - 1); G := BeginRGBValue[1] + MulDiv (I, RGBDifference[1], FNumberOfColors - 1); B := BeginRGBValue[2] + MulDiv (I, RGBDifference[2], FNumberOfColors - 1); { Select the brush and paint the color band } Canvas.Brush.Color := RGB (R, G, B); Canvas.FillRect (ColorBand); end; end; end; { Copy the working bitmap to the main canvas } Canvas.Draw(0, 0, WorkBmp); if FFlatBorder then begin BorderOffset := BorderWidth; if BorderWidth > 0 then begin Canvas.Pen.Width := BorderWidth; Canvas.Pen.EndCap := pecSquare; Canvas.Pen.Color := FBorderColor; //see if there's a better way of drawing a rectangle since // in BorderWidth >= 3 glitches occurs Canvas.Polyline(GetBorderPoints(rp)); end; end else begin BorderOffset := 0; if FBevelOuter <> bvNone then begin Canvas.Frame3D(rp, 1, FBevelOuter); Inc(BorderOffset); end; if FBevelInner <> bvNone then begin Canvas.Frame3D(rp, 1, FBevelInner); Inc(BorderOffset); end; end; Canvas.Font.Assign(Font); TS := Canvas.TextStyle; TS.Alignment := FAlignment; TS.Layout := FLayout; TS.Opaque := False; TS.SystemFont := Canvas.Font.IsDefault; if Caption <> '' then begin Rp := GetClientRect; InflateRect(Rp, -BorderOffset, -BorderOffset); case FAlignment of taLeftJustify: Inc(Rp.Left, FTextLeft); taRightJustify: Dec(Rp.Right, FTextLeft); taCenter: Inc(Rp.Left, FTextLeft * 2); end; case FLayout of tlTop: Inc(rp.Top, FTextTop); tlBottom: Dec(rp.Bottom, FTextTop); tlCenter: Inc(rp.Top, FTextTop * 2); end; Canvas.TextRect(rp, rp.Left, rp.Top, Caption, TS); end; if FSubCaption.Caption <> '' then begin Canvas.Font.Assign(FSubCaption.Font); Rp := GetClientRect; InflateRect(Rp, -BorderOffset, -BorderOffset); TS.Alignment := taRightJustify; Dec(Rp.Right, FSubCaption.MarginLeft); case FLayout of tlTop: Inc(rp.Top, FSubCaption.MarginTop); tlBottom: Dec(rp.Bottom, FSubCaption.MarginTop); tlCenter: Inc(Rp.Top, FSubCaption.MarginTop * 2); end; Canvas.TextRect(rp, rp.Left, rp.Top, FSubCaption.Caption, TS); end; { Release the working bitmap resources } WorkBmp.Free; end; procedure TCustomNetGradient.CMFontChanged(var Message: TLMessage); begin inherited; Changed; end; { Set the fill direction } procedure TCustomNetGradient.SetFillDirection(Value: TFillDirection); begin if Value <> FDirection then begin FDirection := Value; Changed; end; end; //*** Enzo *** procedure TCustomNetGradient.SetAlignment(Value: TAlignment); begin if FAlignment <> Value then begin FAlignment := Value; Changed; end; end; procedure TCustomNetGradient.SetLayout(Value: TTextLayout); begin if Value <> FLayout then begin FLayout := Value; Changed; end; end; procedure TCustomNetGradient.SetBevelInner(Value: TLabelBevel); begin if Value <> FBevelInner then begin FBevelInner := Value; Changed; end; end; procedure TCustomNetGradient.SetBevelOuter(Value: TLabelBevel); begin if Value <> FBevelOuter then begin FBevelOuter := Value; Changed; end; end; procedure TCustomNetGradient.CMTextChanged(var Message: TLMessage); begin inherited; Changed; end; procedure TCustomNetGradient.WMEraseBkgnd(var Message: TLMEraseBkgnd); begin // Do nothing. Just to avoid flicker. end; procedure TCustomNetGradient.OnFontChanged(Sender: TObject); begin Changed; end; procedure TCustomNetGradient.BeginUpdate; begin Inc(FUpdateCount); end; procedure TCustomNetGradient.Changed; begin if (FUpdateCount = 0) and not (csLoading in ComponentState) then begin Invalidate; end; end; function TCustomNetGradient.GetBorderPoints(const R: TRect): TPointArray; var Offset, Fix: Integer; begin Offset := BorderWidth div 2; Fix := BorderWidth mod 2; SetLength(Result, 5); Result[0].x := R.Left + BorderWidth - Offset - Fix; Result[0].y := R.Top + BorderWidth - Offset - Fix; Result[1].x := R.Right - BorderWidth + Offset; Result[1].y := R.Top + BorderWidth - Offset - Fix; Result[2].x := R.Right - BorderWidth + Offset; Result[2].y := R.Bottom - BorderWidth + Offset; Result[3].x := R.Left + BorderWidth - Offset - Fix; Result[3].y := R.Bottom - BorderWidth + Offset; Result[4].x := R.Left + BorderWidth - Offset - Fix; Result[4].y := R.Top + BorderWidth - Offset - Fix; end; procedure TCustomNetGradient.SetBorderColor(const Value: TColor); begin if FBorderColor <> Value then begin FBorderColor := Value; Changed; end; end; procedure TCustomNetGradient.EndUpdate; begin if FUpdateCount > 0 then Dec(FUpdateCount); Changed; end; //********** constructor TSubCaption.Create(AOwner: TCustomNetGradient); begin inherited Create; Parent := AOwner; //FCaption := ''; FFont := TFont.Create; FFont.OnChange := OnFontChanged; //FHotTrack := False; FMarginLeft := 5; //FMarginTop := 0; FVisible := True; end; destructor TSubCaption.Destroy; begin FFont.Destroy; inherited Destroy; end; // Destroy procedure TSubCaption.SetCaption(const Value: TCaption); begin if Value <> FCaption then begin FCaption := Value; Parent.Changed; end end; procedure TSubCaption.SetFont(Value: TFont); begin FFont.Assign(Value); end; procedure TSubCaption.OnFontChanged(Sender: TObject); begin Parent.Changed; end; procedure TSubCaption.Assign(Source: TPersistent); begin if Source is TSubCaption then begin FCaption := TSubCaption(Source).Caption; FHotTrack := TSubCaption(Source).HotTrack; FMarginLeft := TSubCaption(Source).MarginLeft; FMarginTop := TSubCaption(Source).MarginTop; FVisible := TSubCaption(Source).Visible; Font := TSubCaption(Source).Font; end else inherited Assign(Source); end; procedure TSubCaption.SetMarginLeft(Value: Integer); begin if Value <> FMarginLeft then begin FMarginLeft := Value; Parent.Changed; end; end; procedure TSubCaption.SetMarginTop(Value: Integer); begin if Value <> FMarginTop then begin FMarginTop := Value; Parent.Changed; end; end; procedure TSubCaption.SetVisible(Value: Boolean); begin if Value <> FVisible then begin FVisible := Value; Parent.Changed; end; end; procedure TDBNetGradient.DataChange(Sender: TObject); begin if FDataLink.DataSet.Active then begin if not FSubCapField then begin Caption := FDataLink.Field.DisplayText; end else begin fSubCaption.SetCaption( FDataLink.Field.DisplayText); //fSubCaption.SetCaption( FDataLink.Field.AsString); end; end; end; function TDBNetGradient.GetDataField: String; begin Result := FDataLink.FieldName; end; procedure TDBNetGradient.SetDataField (const Value: string); begin FDataLink.FieldName := Value; end; function TDBNetGradient.GetDataSource: TDataSource; begin Result := FDataLink.DataSource; end; procedure TDBNetGradient.SetDataSource (Value: TDataSource); procedure ChangeDataSource(AControl: TControl; Link: TDataLink; NewDataSource: TDataSource); begin if Link.DataSource=NewDataSource then exit; if Link.DataSource<>nil then Link.DataSource.RemoveFreeNotification(AControl); Link.DataSource:=NewDataSource; if Link.DataSource<>nil then Link.DataSource.FreeNotification(AControl); end; begin //* enzo //FDataLink.DataSource := Value; ChangeDataSource(Self, FDataLink, Value); // useless e {if Value <> nil then Value.FreeNotification (Value);} end; constructor TDBNetGradient.Create(AOwner: TComponent); begin inherited Create(AOwner); //*** Enzo *** FDataLink := TFieldDataLink.Create; FDataLink.Control := Self; FDataLink.OnDataChange := DataChange; (* FDataLink.OnUpdateData := @UpdateData; FDataLink.OnActiveChange := @ActiveChange; FDataLink.OnLayoutChange := @LayoutChange; *) end; destructor TDBNetGradient.Destroy; begin FDataLink.Destroy; inherited Destroy; end; end.