lazarus/lcl/arrow.pp
2017-12-05 21:30:57 +00:00

309 lines
7.5 KiB
ObjectPascal

{
*********************************************************************
This file is part of the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*********************************************************************
Author: H. Page-Clark
Abstract:
Show an arrow. Its size, direction, color and shadow can be adjusted.
}
unit Arrow;
{$mode objfpc}{$H+}
interface
uses
Classes, types, math, Controls, Graphics, IndustrialBase;
type
TArrowType = (atUp, atDown, atLeft, atRight);
TShadowType = (stNone, stIn, stOut, stEtchedIn, stEtchedOut, stFilled);
TTriPts = (ptA, ptB, ptC);
TTrianglePoints = array[TTriPts] of TPoint;
{ TArrow }
TArrow = class(TIndustrialBase)
private
FArrowColor: TColor;
FArrowType: TArrowType;
FArrowAngle: integer;
FShadowType: TShadowType;
FShadowColor: TColor;
FR: TRect;
FT: TTrianglePoints;
procedure CalcTrianglePoints;
procedure SetArrowAngle(AValue: integer);
procedure SetArrowColor(AValue: TColor);
procedure SetArrowType(AValue: TArrowType);
procedure SetShadowColor(AValue: TColor);
procedure SetShadowType(AValue: TShadowType);
protected
class function GetControlClassDefaultSize: TSize; override;
procedure Paint; override;
public
constructor Create(aOwner: TComponent); override;
published
property Align;
property Anchors;
property ArrowColor: TColor read FArrowColor write SetArrowColor default clBlack;
property ArrowType: TArrowType read FArrowType write SetArrowType default atLeft;
property ArrowPointerAngle: integer read FArrowAngle write SetArrowAngle default 60;
property BorderSpacing;
property Color;
property Constraints;
property Enabled;
property Hint;
property OnChangeBounds;
property OnClick;
property OnContextPopup;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDrag;
property OnMouseDown;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseMove;
property OnMouseUp;
property OnMouseWheel;
property OnMouseWheelDown;
property OnMouseWheelUp;
property OnMouseWheelHorz;
property OnMouseWheelLeft;
property OnMouseWheelRight;
property OnPaint;
property OnResize;
property OnStartDrag;
property ParentColor;
property ParentShowHint;
property PopupMenu;
property ShadowType: TShadowType read FShadowType write SetShadowType default stEtchedIn;
property ShadowColor: TColor read FShadowColor write SetShadowColor default cl3DShadow;
property ShowHint;
property Visible;
end;
procedure Register;
implementation
const
cDefaultControlSize = 20;
cMinArrowSize = 8;
cMinAngle = 20;
cMaxAngle = 160;
cShadowColors: array[TShadowType] of TColor =
(clWindow, cl3DShadow, cl3DShadow, cl3DHiLight, cl3DHiLight, clBlue{not used});
cInnerOffset = 2;
cShadowSize = 2; //must be <= cInnerOffset
procedure Register;
begin
RegisterComponents('Misc',[TArrow]);
end;
{ TArrow }
procedure TArrow.CalcTrianglePoints;
var
midY, midX: integer;
ratioNeed, ratioThis: double;
size: TSize;
begin
FR:= ClientRect;
InflateRect(FR, -cInnerOffset, -cInnerOffset);
Dec(FR.Bottom); // for "filled" shadow
midX:= (FR.Left + FR.Right) div 2;
midY:= (FR.Top + FR.Bottom) div 2;
size:= Types.Size(FR);
ratioNeed:= 2*Tan(FArrowAngle*pi/(180*2));
if FArrowType in [atLeft, atRight] then
ratioNeed:= 1/ratioNeed;
ratioThis:= size.cx/size.cy;
if ratioThis>=ratioNeed then
size.cx:= Trunc(size.cx*ratioNeed/ratioThis)
else
size.cy:= Trunc(size.cy*ratioThis/ratioNeed);
FR.Top:= midY - size.cy div 2;
FR.Bottom:= FR.Top + size.cy;
FR.Left:= midX - size.cx div 2;
FR.Right:= FR.Left + size.cx;
// angle=90: 1pixel shift appears (reason: float math)
// workaround:
if FArrowAngle=90 then
begin
if FArrowType in [atUp, atDown] then
begin
FR.Left:= midX-size.cy;
FR.Right:= midX+size.cy;
end
else
begin
FR.Top:= midY-size.cx;
FR.Bottom:= midY+size.cx;
end;
end;
case FArrowType of
atUp: begin
FT[ptC] := Point(midX, FR.Top);
FT[ptA] := Point(FR.Left, FR.Bottom);
FT[ptB] := FR.BottomRight;
end;
atDown: begin
FT[ptA] := FR.TopLeft;
FT[ptB] := Point(FR.Right, FR.Top);
FT[ptC] := Point(midX, FR.Bottom);
end;
atLeft: begin
FT[ptA] := Point(FR.Right, FR.Top);
FT[ptB] := FR.BottomRight;
FT[ptC] := Point(FR.Left, midY);
end;
atRight: begin
FT[ptA] := FR.TopLeft;
FT[ptB] := Point(FR.Right, midY);
FT[ptC] := Point(FR.Left, FR.Bottom);
end;
end;
end;
procedure TArrow.SetArrowColor(AValue: TColor);
begin
if FArrowColor=AValue then Exit;
FArrowColor:=AValue;
GraphicChanged;
end;
procedure TArrow.SetArrowType(AValue: TArrowType);
begin
if FArrowType=AValue then Exit;
FArrowType:=AValue;
GraphicChanged;
end;
procedure TArrow.SetShadowColor(AValue: TColor);
begin
if FShadowColor=AValue then Exit;
FShadowColor:= AValue;
GraphicChanged;
end;
procedure TArrow.SetArrowAngle(AValue: integer);
begin
if FArrowAngle=AValue then Exit;
FArrowAngle:=Max(Min(AValue, cMaxAngle), cMinAngle);
GraphicChanged;
end;
procedure TArrow.SetShadowType(AValue: TShadowType);
begin
if FShadowType=AValue then Exit;
FShadowType:=AValue;
GraphicChanged;
end;
class function TArrow.GetControlClassDefaultSize: TSize;
begin
Result.cx:=cDefaultControlSize;
Result.cy:=cDefaultControlSize;
end;
procedure TArrow.Paint;
procedure Offset(var ptA, ptB: TPoint);
begin
case FArrowType of
atUp: begin Inc(ptA.x); Dec(ptA.y); Inc(ptB.x); Dec(ptB.y); end;
atDown: begin Inc(ptA.x); Inc(ptA.y); Inc(ptB.x); Inc(ptB.y); end;
atLeft: begin Dec(ptA.x); Inc(ptA.y); Dec(ptB.x); Inc(ptB.y); end;
atRight: begin Inc(ptA.x); Inc(ptA.y); Inc(ptB.x); Inc(ptB.y); end;
end;
end;
procedure ShadowLine(p1, p2: TPoint);
begin
Canvas.Pen.Color:= cShadowColors[FShadowType];
Canvas.MoveTo(p1);
Canvas.LineTo(p2);
Offset(p1, p2);
Canvas.Pen.Color:= FShadowColor;
Canvas.MoveTo(p1);
Canvas.LineTo(p2);
if (Height>13) then
begin
Offset(p1, p2);
Canvas.MoveTo(p1);
Canvas.LineTo(p2);
end;
end;
procedure ShadowTriangle;
var
Pts: TTrianglePoints;
begin
Pts:= FT;
Inc(Pts[ptA].x, cShadowSize);
Inc(Pts[ptA].y, cShadowSize);
Inc(Pts[ptB].x, cShadowSize);
Inc(Pts[ptB].y, cShadowSize);
Inc(Pts[ptC].x, cShadowSize);
Inc(Pts[ptC].y, cShadowSize);
Canvas.Pen.Color:= FShadowColor;
Canvas.Brush.Color:= FShadowColor;
Canvas.Polygon(Pts);
end;
begin
CalcTrianglePoints;
Canvas.AntialiasingMode := AntiAliasingMode;
// Paint background
Canvas.Brush.Color := Color;
Canvas.FillRect(ClientRect);
// Paint shadow area
if (FShadowType=stFilled) then
ShadowTriangle;
// Paint arrow
Canvas.Pen.Color:= FArrowColor;
Canvas.Brush.Color:= FArrowColor;
Canvas.Polygon(FT);
if not (FShadowType in [stNone, stFilled]) then
ShadowLine(FT[ptB], FT[ptC]);
inherited Paint;
end;
constructor TArrow.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
Constraints.MinHeight:= cMinArrowSize;
Constraints.MinWidth:= cMinArrowSize;
FArrowType:= atLeft; // set defaults to match TArrow component
FArrowAngle:= 60; // angle of equal side triangle
FShadowType:= stEtchedIn;
FShadowColor:= cl3DShadow;
FArrowColor:= clBlack;
end;
end.