Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Eliminating flicker in custom list-item control? 1

Status
Not open for further replies.

djjd47130

Programmer
Nov 1, 2010
480
US
I'm building a custom control which consists of a list of items which are positioned either horizontally or vertically (extra stretching/fitting for each item). While things do appear to work fine, I have one problem where rapid mouse movement (or otherwise rapid painting) causes it to flicker. I am using an intermittent Bitmap for all my drawing, and copying it over to the main control canvas when necessary (using BitBlt). I'm afraid the code is too large and complex to write a simplified sample of my problem, especially since the complexity is probably what's causing it to flicker.

Can someone take a look at my control and let me know how to prevent this flicker? If I enabled "DoubleBuffered" it shows fine with no flicker, but I shouldn't have to do this. Even on good performing computers it flickers, and I'm scared to see how it flickers on a poor computer.

Just create a new instance of this control and add 5 or 6 items via the "Items" property. Then, in run-time, quickly move the mouse back and forth over all the items in this control. It flashes white periodically.

PS - This control is not completely done, but is workable. You will find some unimplemented things such as the Image List.

Code:
unit JD.Breadcrumb;

interface

uses
  Winapi.Windows, Winapi.Messages,
  System.Classes, System.SysUtils,
  Vcl.Graphics, Vcl.Controls, Vcl.ImgList;

type
  TJDBreadcrumb = class;
  TJDCustomBreadcrumb = class;
  TJDBreadcrumbItem = class;
  TJDBreadcrumbItems = class;
  TJDBreadcrumbItemStyle = class;

  TPoints = array of TPoint;

  //General style of breadcrumb items
  TJDBreadcrumbStyle = (bsArrows, bsItemIcons, bsStateIcons, bsText);

  //How to display icon images in breadcrumb items
  TJDBreadcrumbIcons = (biNone, biCenter, biLeft, biRight, biTop, biBottom);

  //Direction or Orientation of breadcrumb list
  TBreadcrumbDir = (bdHorizontal, bdVertical);

  //How to size each breadcrumb item
  TBreadcrumbSize = (bsManual, bsAuto, bsStretch);

  //Current state of a breadcrumb item
  TBreadcrumbState = (stPending, stSelected, stPassed, stDisabled);

  TBreadcrumbItemEvent = procedure(Sender: TObject; var Item: TJDBreadcrumbItem) of object;

  TBreadcrumbItemSelectEvent = procedure(Sender: TObject;
    var Item: TJDBreadcrumbItem; var Accept: Bool) of object;

  TBreadcrumbItemMouseEvent = procedure(Sender: TObject; var Item: TJDBreadcrumbItem;
    const Button: TMouseButton; const Shift: TShiftState; const X, Y: Integer) of object;

  TJDBreadcrumbItemStyle = class(TPersistent)
  private
    FColor: TColor;
    FCaptionFont: TFont;
    FSubCaptionFont: TFont;
    FOnChange: TNotifyEvent;
    FBorderColor: TColor;
    FBorderWidth: Integer;
    procedure FontChanged(Sender: TObject);
    procedure SetColor(const Value: TColor);
    procedure SetCaptionFont(const Value: TFont);
    procedure SetSubCaptionFont(const Value: TFont);
    procedure SetBorderColor(const Value: TColor);
    procedure SetBorderWidth(const Value: Integer);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Changed;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  published
    property Color: TColor read FColor write SetColor;
    property CaptionFont: TFont read FCaptionFont write SetCaptionFont;
    property SubCaptionFont: TFont read FSubCaptionFont write SetSubCaptionFont;
    property BorderWidth: Integer read FBorderWidth write SetBorderWidth;
    property BorderColor: TColor read FBorderColor write SetBorderColor;
  end;

  TJDBreadcrumbItem = class(TCollectionItem)
  private
    FSubCaption: TCaption;
    FCaption: TCaption;
    FLength: Integer;
    FEnabled: Boolean;
    FIndex: Integer;
    function GetOwner: TJDBreadcrumb;
    function GetLength: Integer;
    function GetCaptionRect: TRect;
    function GetItemRect: TRect;
    procedure SetCaption(const Value: TCaption);
    procedure SetSubCaption(const Value: TCaption);
    procedure SetLength(const Value: Integer);
    procedure SetEnabled(const Value: Boolean);
    function GetItemState: TBreadcrumbState;
    function GetColor: TColor;
    function GetFont: TFont;
    function GetPoints: TPoints;
    function GetBorderColor: TColor;
    function GetBorderWidth: Integer;
  public
    constructor Create(AOwner: TCollection); override;
    destructor Destroy; override;
    procedure Invalidate;
    property Breadcrumb: TJDBreadcrumb read GetOwner;
    property ItemState: TBreadcrumbState read GetItemState;
    property Index: Integer read FIndex;
    property Font: TFont read GetFont;
    property Color: TColor read GetColor;
    property CaptionRect: TRect read GetCaptionRect;
    property ItemRect: TRect read GetItemRect;
    property Points: TPoints read GetPoints;
    property BorderWidth: Integer read GetBorderWidth;
    property BorderColor: TColor read GetBorderColor;
  published
    property Caption: TCaption read FCaption write SetCaption;
    property SubCaption: TCaption read FSubCaption write SetSubCaption;
    property Length: Integer read GetLength write SetLength;
    property Enabled: Boolean read FEnabled write SetEnabled;
  end;

  TJDBreadcrumbItems = class(TOwnedCollection)
  private
    function GetItem(Index: Integer): TJDBreadcrumbItem;
    procedure SetItem(Index: Integer; const Value: TJDBreadcrumbItem);
  public
    constructor Create(AOwner: TComponent); reintroduce;
    destructor Destroy; override;
    function Add: TJDBreadcrumbItem;
    procedure Delete(const Index: Integer);
    procedure Invalidate;
    property Items[Index: Integer]: TJDBreadcrumbItem read GetItem write SetItem; default;
  published
  end;

  TJDCustomBreadcrumb = class(TCustomControl)
  private
    FDrawing: Bool;
    FBitmap: TBitmap;
    FItems: TJDBreadcrumbItems;
    FDirection: TBreadcrumbDir;
    FItemIndex: Integer;
    FMouseIndex: Integer;
    FMouseDownIndex: Integer;
    FMouseDownPos: TPoint;
    FKeyState: TKeyboardState;
    FShift: TShiftState;
    FStylePending: TJDBreadcrumbItemStyle;
    FStylePassed: TJDBreadcrumbItemStyle;
    FStyleDisabled: TJDBreadcrumbItemStyle;
    FStyleSelected: TJDBreadcrumbItemStyle;
    FStyle: TJDBreadcrumbStyle;
    FShowDisabled: Boolean;
    FShowSubCaptions: Boolean;
    FGap: Integer;
    FIconStyle: TJDBreadcrumbIcons;
    FSizing: TBreadcrumbSize;
    FIndent: Integer;
    FMargin: Integer;
    FImages: TImageList;
    FOnItemMouseDown: TBreadcrumbItemMouseEvent;
    FOnItemMouseUp: TBreadcrumbItemMouseEvent;
    FOnSelectItem: TBreadcrumbItemSelectEvent;
    procedure GetShiftState;
    function DoSelectItem: Bool;
    procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
    procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
    procedure WMSysKeyDown(var Message: TWMSysKeyDown); message WM_SYSKEYDOWN;
    procedure WMKeyUp(var Message: TWMKeyUp); message WM_KEYUP;
    procedure WMSysKeyUp(var Message: TWMSysKeyUp); message WM_SYSKEYUP;
    procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
    procedure WMRButtonDown(var Message: TWMRButtonDown); message WM_RBUTTONDOWN;
    procedure WMMButtonDown(var Message: TWMMButtonDown); message WM_MBUTTONDOWN;
    procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
    procedure WMRButtonUp(var Message: TWMRButtonUp); message WM_RBUTTONUP;
    procedure WMMButtonUp(var Message: TWMMButtonUp); message WM_MBUTTONUP;
    procedure CMMouseEnter(var Msg: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Msg: TMessage); message CM_MOUSELEAVE;
    procedure Changed(Sender: TObject);
    procedure SetItems(const Value: TJDBreadcrumbItems);
    procedure SetDirection(const Value: TBreadcrumbDir);
    procedure SetColor(const Value: TColor);
    procedure SetItemIndex(const Value: Integer);
    procedure SetStyleDisabled(const Value: TJDBreadcrumbItemStyle);
    procedure SetStylePassed(const Value: TJDBreadcrumbItemStyle);
    procedure SetStylePending(const Value: TJDBreadcrumbItemStyle);
    procedure SetStyleSelected(const Value: TJDBreadcrumbItemStyle);
    procedure SetStyle(const Value: TJDBreadcrumbStyle);
    procedure SetShowDisabled(const Value: Boolean);
    procedure SetShowSubCaptions(const Value: Boolean);
    procedure SetGap(const Value: Integer);
    procedure SetIconStyle(const Value: TJDBreadcrumbIcons);
    procedure SetSizing(const Value: TBreadcrumbSize);
    procedure SetIndent(const Value: Integer);
    procedure SetMargin(const Value: Integer);
    function GetColor: TColor;
    procedure SetImages(const Value: TImageList);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function HasImages: Boolean;
    property Style: TJDBreadcrumbStyle read FStyle write SetStyle;
    property Items: TJDBreadcrumbItems read FItems write SetItems;
    property Direction: TBreadcrumbDir read FDirection write SetDirection default bdHorizontal;
    property ItemIndex: Integer read FItemIndex write SetItemIndex;
    property IconStyle: TJDBreadcrumbIcons read FIconStyle write SetIconStyle;
    property Sizing: TBreadcrumbSize read FSizing write SetSizing;
    property Margin: Integer read FMargin write SetMargin;
    property Gap: Integer read FGap write SetGap;
    property Color: TColor read GetColor write SetColor;
    property Indent: Integer read FIndent write SetIndent;
    property StylePending: TJDBreadcrumbItemStyle read FStylePending write SetStylePending;
    property StyleSelected: TJDBreadcrumbItemStyle read FStyleSelected write SetStyleSelected;
    property StylePassed: TJDBreadcrumbItemStyle read FStylePassed write SetStylePassed;
    property StyleDisabled: TJDBreadcrumbItemStyle read FStyleDisabled write SetStyleDisabled;
    property ShowDisabled: Boolean read FShowDisabled write SetShowDisabled;
    property ShowSubCaptions: Boolean read FShowSubCaptions write SetShowSubCaptions;
    property Images: TImageList read FImages write SetImages;
    property MouseIndex: Integer read FMouseIndex;

    property OnItemMouseDown: TBreadcrumbItemMouseEvent
      read FOnItemMouseDown write FOnItemMouseDown;
    property OnItemMouseUp: TBreadcrumbItemMouseEvent
      read FOnItemMouseUp write FOnItemMouseUp;
    property OnSelectItem: TBreadcrumbItemSelectEvent
      read FOnSelectItem write FOnSelectItem;
  end;

  TJDBreadcrumb = class(TJDCustomBreadcrumb)
  published
    property Items;
    property IconStyle default biNone;
    property Sizing default bsStretch;
    property ItemIndex;
    property Indent;
    property Margin;
    property Gap;
    property Direction default bdHorizontal;
    property Color;
    property StylePending;
    property StyleSelected;
    property StylePassed;
    property StyleDisabled;
    property Style default bsArrows;
    property ShowDisabled default True;
    property ShowSubCaptions default False;
    property Images;
    property Align;
    property Anchors;
    property DoubleBuffered default False;
    property ParentColor;
    property Height;
    property Width;
    property OnItemMouseDown;
    property OnItemMouseUp;
    property OnSelectItem;
  end;


implementation


function PolyRect(const Points: TPoints): TRect;
var
  X: Integer;
  P: TPoint;
begin
  if Length(Points) > 0 then begin
    Result:= Rect(MAXINT, MAXINT, 0, 0);
    for X := 0 to Length(Points) - 1 do begin
      P:= Points[X];
      if P.X < Result.Left then
        Result.Left:= P.X;
      if P.X > Result.Right then
        Result.Right:= P.X;
      if P.Y < Result.Top then
        Result.Top:= P.Y;
      if P.Y > Result.Bottom then
        Result.Bottom:= P.Y;
    end;
  end else begin
    Result:= Rect(0, 0, 0, 0);
  end;
end;

{ TJDCustomBreadcrumb }

constructor TJDCustomBreadcrumb.Create(AOwner: TComponent);
begin
  inherited;
  ControlStyle:= ControlStyle - [csDesignInteractive] + [csCaptureMouse];
  FBitmap:= TBitmap.Create;
  FItems:= TJDBreadcrumbItems.Create(Self);
  FImages:= nil;
  FDrawing:= False;
  FStylePending:= TJDBreadcrumbItemStyle.Create;
  FStylePending.OnChange:= Changed;
  FStylePending.Color:= clWhite;
  FStylePending.CaptionFont.Style:= [fsBold];
  FStylePending.CaptionFont.Size:= 14;
  FStyleSelected:= TJDBreadcrumbItemStyle.Create;
  FStyleSelected.OnChange:= Changed;
  FStyleSelected.Color:= clBlue;
  FStyleSelected.CaptionFont.Style:= [fsBold];
  FStyleSelected.CaptionFont.Size:= 14;
  FStyleSelected.CaptionFont.Color:= clWhite;
  FStylePassed:= TJDBreadcrumbItemStyle.Create;
  FStylePassed.OnChange:= Changed;
  FStylePassed.Color:= clSkyBlue;
  FStylePassed.CaptionFont.Style:= [fsBold];
  FStylePassed.CaptionFont.Size:= 14;
  FStyleDisabled:= TJDBreadcrumbItemStyle.Create;
  FStyleDisabled.OnChange:= Changed;
  FStyleDisabled.Color:= clGray;
  FStyleDisabled.CaptionFont.Style:= [fsBold];
  FStyleDisabled.CaptionFont.Size:= 14;
  Width:= 300;
  Height:= 75;
  FMouseIndex:= -1;
  inherited Color:= clBtnFace;
  FShowDisabled:= True;
  FShowSubCaptions:= False;
  FSizing:= bsStretch;
  FIconStyle:= biNone;
  FGap:= 6;
  FIndent:= 20;
  Invalidate;
end;

destructor TJDCustomBreadcrumb.Destroy;
begin
  FStylePending.Free;
  FStyleSelected.Free;
  FStylePassed.Free;
  FStyleDisabled.Free;
  FItems.Free;
  FBitmap.Free;
  inherited;
end;

function TJDCustomBreadcrumb.DoSelectItem: Bool;
var
  A: Bool;
  I: TJDBreadcrumbItem;
begin
  Result:= True;
  A:= True;
  if (FMouseDownIndex >= 0) and (FMouseDownIndex < FItems.Count) then begin
    I:= FItems[FMouseDownIndex];
    if assigned(FOnSelectItem) then begin
      FOnSelectItem(Self, I, A);
      Result:= A;
    end;
    if A then begin
      FItemIndex:= FMouseDownIndex;
      Invalidate;
    end;
  end else begin
    Result:= False;
  end;
end;

function TJDCustomBreadcrumb.GetColor: TColor;
begin
  Result:= inherited Color;
end;

function TJDCustomBreadcrumb.HasImages: Boolean;
begin
  Result:= Assigned(FImages);
end;

procedure TJDCustomBreadcrumb.Paint;
var
  X: Integer;
  I: TJDBreadcrumbItem;
  Dst: Integer;
  Wid: Integer;
  Coords: TPoints;
  H: Integer;
  Col: TColor;
  R: TRect;
  Can: TCanvas;
begin
  //if not FDrawing then begin
    //FDrawing:= True;
    //try
      if FBitmap.Width <> ClientWidth then
        FBitmap.Width:= ClientWidth;
      if FBitmap.Height <> ClientHeight then
        FBitmap.Height:= ClientHeight;
      Can:= FBitmap.Canvas;
      Can.Brush.Style:= bsSolid;
      Can.Pen.Style:= psClear;
      Can.Brush.Color:= Color;
      Can.FillRect(Can.ClipRect);
      Dst:= FMargin;
      case FDirection of
        bdHorizontal: begin
          Wid:= ClientHeight - (FMargin * 2);
        end;
        bdVertical: begin
          Wid:= ClientWidth - (FMargin * 2);
        end;
      end;
      H:= Wid div 2;
      for X := 0 to Items.Count - 1 do begin
        I:= Items[X];
        I.FIndex:= X;
        if ((I.Enabled = False) and (FShowDisabled)) or (I.Enabled) then begin
          Col:= I.Color;
          Can.Pen.Style:= psClear;
          Can.Brush.Style:= bsSolid;
          Can.Brush.Color:= Col;
          Can.Font.Assign(I.Font);
          Coords:= I.Points;
          R:= I.ItemRect;
          Dst:= Dst + I.Length - FIndent + FGap;
          Can.Polygon(Coords);
          Can.Brush.Style:= bsClear;
          if FMouseIndex < 0 then begin     //Mouse is not over anything
            if I.BorderWidth > 0 then begin
              Can.Pen.Style:= psSolid;
              Can.Pen.Color:= I.BorderColor;
              Can.Pen.Width:= I.BorderWidth;
              Can.Polygon(Coords);
            end;
          end else begin                    //Mouse is over something
            if FMouseIndex = X then begin   //Mouse is over this item
              Can.Pen.Style:= psSolid;
              Can.Pen.Color:= clBlack;
              Can.Pen.Width:= 2;
              Can.Polygon(Coords);
            end else begin                  //Mouse is NOT over this item
              if I.BorderWidth > 0 then begin
                Can.Pen.Style:= psSolid;
                Can.Pen.Color:= I.BorderColor;
                Can.Pen.Width:= I.BorderWidth;
                Can.Polygon(Coords);
              end;
            end;
          end;
          InflateRect(R, -FIndent, -FMargin);
          DrawText(Can.Handle, PChar(I.Caption), Length(I.Caption),
            R, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
        end;
      end;
      BitBlt(Canvas.Handle, 0, 0, ClientWidth, ClientHeight,
        FBitmap.Canvas.Handle, 0, 0, SRCCOPY);
    //finally
      //FDrawing:= False;
    //end;
  //end;
end;

procedure TJDCustomBreadcrumb.Changed(Sender: TObject);
begin
  Invalidate;
end;

procedure TJDCustomBreadcrumb.SetColor(const Value: TColor);
begin
  if Value <> Color then begin
    inherited Color := Value;
    Invalidate;
  end;
end;

procedure TJDCustomBreadcrumb.SetDirection(const Value: TBreadcrumbDir);
begin
  if Value <> FDirection then begin
    FDirection := Value;
    Invalidate;
  end;
end;

procedure TJDCustomBreadcrumb.SetGap(const Value: Integer);
begin
  if Value <> FGap then begin
    FGap := Value;
    Invalidate;
  end;
end;

procedure TJDCustomBreadcrumb.SetIconStyle(const Value: TJDBreadcrumbIcons);
begin
  if Value <> FIconStyle then begin
    FIconStyle := Value;
    Invalidate;
  end;
end;

procedure TJDCustomBreadcrumb.SetImages(const Value: TImageList);
begin
  FImages:= Value;
  Invalidate;
end;

procedure TJDCustomBreadcrumb.SetIndent(const Value: Integer);
begin
  if Value <> FIndent then begin
    FIndent := Value;
    Invalidate;
  end;
end;

procedure TJDCustomBreadcrumb.SetItemIndex(const Value: Integer);
begin
  if Value <> FItemIndex then begin
    FItemIndex := Value;
    Invalidate;
  end;
end;

procedure TJDCustomBreadcrumb.SetItems(const Value: TJDBreadcrumbItems);
begin
  FItems.Assign(Value);
  Invalidate;
end;

procedure TJDCustomBreadcrumb.SetMargin(const Value: Integer);
begin
  if Value <> FMargin then begin
    FMargin := Value;
    Invalidate;
  end;
end;

procedure TJDCustomBreadcrumb.SetShowDisabled(const Value: Boolean);
begin
  if Value <> FShowDisabled then begin
    FShowDisabled := Value;
    Invalidate;
  end;
end;

procedure TJDCustomBreadcrumb.SetShowSubCaptions(const Value: Boolean);
begin
  if Value <> FShowSubCaptions then begin
    FShowSubCaptions := Value;
    Invalidate;
  end;
end;

procedure TJDCustomBreadcrumb.SetSizing(const Value: TBreadcrumbSize);
begin
  if Value <> FSizing then begin
    FSizing := Value;
    Invalidate;
  end;
end;

procedure TJDCustomBreadcrumb.SetStyle(const Value: TJDBreadcrumbStyle);
begin
  if Value <> FStyle then begin
    FStyle := Value;
    Invalidate;
  end;
end;

procedure TJDCustomBreadcrumb.SetStyleDisabled(
  const Value: TJDBreadcrumbItemStyle);
begin
  FStyleDisabled.Assign(Value);
  Invalidate;
end;

procedure TJDCustomBreadcrumb.SetStylePassed(
  const Value: TJDBreadcrumbItemStyle);
begin
  FStylePassed.Assign(Value);
  Invalidate;
end;

procedure TJDCustomBreadcrumb.SetStylePending(
  const Value: TJDBreadcrumbItemStyle);
begin
  FStylePending.Assign(Value);
  Invalidate;
end;

procedure TJDCustomBreadcrumb.SetStyleSelected(
  const Value: TJDBreadcrumbItemStyle);
begin
  FStyleSelected.Assign(Value);
  Invalidate;
end;

function PointInPolygon(Point: TPoint; const Polygon: array of TPoint): Boolean;
var
  rgn: HRGN;
begin
  rgn := CreatePolygonRgn(Polygon[0], Length(Polygon), WINDING);
  Result := PtInRegion(rgn, Point.X, Point.Y);
  DeleteObject(rgn);
end;

procedure TJDCustomBreadcrumb.CMMouseEnter(var Msg: TMessage);
begin
  Invalidate;
  inherited;
end;

procedure TJDCustomBreadcrumb.CMMouseLeave(var Msg: TMessage);
begin
  FMouseIndex:= -1;
  Invalidate;
  inherited;
end;

procedure TJDCustomBreadcrumb.WMNCHitTest(var Message: TWMNCHitTest);
var
  P: TPoint;
  Poly: TPoints;
  X: Integer;
  I: TJDBreadcrumbItem;
  OldIndex: Integer;
begin
  if csDesigning in ComponentState then
    Message.Result := HTCLIENT
  else begin
    P.X:= Message.Pos.X;
    P.Y:= Message.Pos.Y;
    P:= ScreenToClient(P);
    OldIndex:= FMouseIndex;
    FMouseIndex:= -1;
    for X := 0 to Items.Count - 1 do begin
      I:= Items[X];
      Poly:= I.Points;
      if PointInPolygon(P, Poly) then begin
        FMouseIndex:= X;
        Break;
      end;
    end;
    if FMouseIndex <> OldIndex then Invalidate;
  end;
  inherited;
end;

procedure TJDCustomBreadcrumb.GetShiftState;
var
  S: TShiftState;
begin
  GetKeyboardState(FKeyState);
  S:= [];
  if (FKeyState[vk_Shift] and 128) <> 0 then
    FShift:= FShift + [ssShift];
  if (FKeyState[vk_Control] and 128) <> 0 then
    FShift:= FShift + [ssCtrl];
  if (FKeyState[vk_Menu] and 128) <> 0 then
    FShift:= FShift + [ssAlt];
  if (FKeyState[VK_LBUTTON] and 128) <> 0 then
    FShift:= FShift + [ssLeft];
  if (FKeyState[VK_MBUTTON] and 128) <> 0 then
    FShift:= FShift + [ssMiddle];
  if (FKeyState[VK_RBUTTON] and 128) <> 0 then
    FShift:= FShift + [ssRight];
  if (FKeyState[VK_SELECT] and 128) <> 0 then
    FShift:= FShift + [ssDouble];

//ssDouble, ssTouch, ssPen, ssCommand
end;

procedure TJDCustomBreadcrumb.WMKeyDown(var Message: TWMKeyDown);
begin
  GetShiftState;

  inherited;
end;

procedure TJDCustomBreadcrumb.WMKeyUp(var Message: TWMKeyUp);
begin
  GetShiftState;

  inherited;
end;

procedure TJDCustomBreadcrumb.WMSysKeyDown(var Message: TWMSysKeyDown);
begin

  inherited;
end;

procedure TJDCustomBreadcrumb.WMSysKeyUp(var Message: TWMSysKeyUp);
begin

  inherited;
end;

procedure TJDCustomBreadcrumb.WMLButtonDown(var Message: TWMLButtonDown);
var
  P: TPoint;
  X: Integer;
  I: TJDBreadcrumbItem;
begin
  GetShiftState;
  P:= Point(Message.XPos, Message.YPos);
  FMouseDownPos:= P;
  FMouseDownIndex:= -1;
  for X := 0 to FItems.Count - 1 do begin
    I:= FItems[X];
    if PointInPolygon(P, I.Points) then begin
      FMouseDownIndex:= X;
      DoSelectItem;
      if assigned(FOnItemMouseDown) then
        FOnItemMouseDown(Self, I, mbLeft, FShift, P.X, P.Y);
      Break;
    end;
  end;
  inherited;
end;

procedure TJDCustomBreadcrumb.WMLButtonUp(var Message: TWMLButtonUp);
var
  P: TPoint;
  I: TJDBreadcrumbItem;
begin
  GetShiftState;
  P:= ScreenToClient(Point(Message.XPos, Message.YPos));
  I:= FItems[FMouseDownIndex];
  FMouseDownIndex:= -1;
  if assigned(FOnItemMouseUp) then
    FOnItemMouseUp(Self, I, mbLeft, FShift, P.X, P.Y);
  inherited;
end;

procedure TJDCustomBreadcrumb.WMMButtonDown(var Message: TWMMButtonDown);
begin
  inherited;
end;

procedure TJDCustomBreadcrumb.WMMButtonUp(var Message: TWMMButtonUp);
begin
  inherited;
end;

procedure TJDCustomBreadcrumb.WMRButtonDown(var Message: TWMRButtonDown);
begin
  inherited;
end;

procedure TJDCustomBreadcrumb.WMRButtonUp(var Message: TWMRButtonUp);
begin
  inherited;
end;

{ TJDBreadcrumbItem }

constructor TJDBreadcrumbItem.Create(AOwner: TCollection);
begin
  inherited Create(AOwner);
  FIndex:= 0;
  FLength:= 150;
  FCaption:= 'New Item';
  FSubCaption:= '';
  FEnabled:= True;
end;

destructor TJDBreadcrumbItem.Destroy;
begin

  inherited;
end;

function TJDBreadcrumbItem.GetBorderColor: TColor;
begin
  case ItemState of
    stPending:  Result:= Breadcrumb.StylePending.BorderColor;
    stSelected: Result:= Breadcrumb.StyleSelected.BorderColor;
    stPassed:   Result:= Breadcrumb.StylePassed.BorderColor;
    stDisabled: Result:= Breadcrumb.StyleDisabled.BorderColor;
  end;
end;

function TJDBreadcrumbItem.GetBorderWidth: Integer;
begin
  case ItemState of
    stPending:  Result:= Breadcrumb.StylePending.BorderWidth;
    stSelected: Result:= Breadcrumb.StyleSelected.BorderWidth;
    stPassed:   Result:= Breadcrumb.StylePassed.BorderWidth;
    stDisabled: Result:= Breadcrumb.StyleDisabled.BorderWidth;
  end;
end;

function TJDBreadcrumbItem.GetCaptionRect: TRect;
var
  R: TRect;
  W: Integer;
begin
  R:= Rect(0, 0, 10, 50);
  Breadcrumb.Canvas.Font.Assign(Font);
  W:= DrawText(Breadcrumb.Canvas.Handle, Caption, System.Length(Caption),
    R, DT_SINGLELINE or DT_CALCRECT);
  Result:= R;
  R:= ItemRect;
  //Result.Left:= (Result.Width div 2) -
end;

function TJDBreadcrumbItem.GetColor: TColor;
begin
  case ItemState of
    stPending:  Result:= Breadcrumb.StylePending.Color;
    stSelected: Result:= Breadcrumb.StyleSelected.Color;
    stPassed:   Result:= Breadcrumb.StylePassed.Color;
    stDisabled: Result:= Breadcrumb.StyleDisabled.Color;
  end;
end;

function TJDBreadcrumbItem.GetFont: TFont;
begin
  case ItemState of
    stPending:  Result:= Breadcrumb.StylePending.CaptionFont;
    stSelected: Result:= Breadcrumb.StyleSelected.CaptionFont;
    stPassed:   Result:= Breadcrumb.StylePassed.CaptionFont;
    stDisabled: Result:= Breadcrumb.StyleDisabled.CaptionFont;
  end;
end;

function TJDBreadcrumbItem.GetItemRect: TRect;
var
  X: Integer;
  C: Integer;
  I: TJDBreadcrumbItem;
begin
  C:= Breadcrumb.Gap;
  case Breadcrumb.Direction of
    bdHorizontal: begin
      for X := 0 to Collection.Count - 1 do begin
        if X = Index then Break;
        I:= TJDBreadcrumbItem(Collection.Items[X]);
        C:= C + Breadcrumb.Gap + I.Length - Breadcrumb.Indent;
      end;
      Result.Left:= C - Breadcrumb.Indent;
      Result.Top:= Breadcrumb.Margin;
      Result.Right:= Result.Left + Length;
      Result.Bottom:= Breadcrumb.ClientHeight - Breadcrumb.Margin;
    end;
    bdVertical: begin
      for X := 0 to Collection.Count - 1 do begin
        if X = Index then Break;
        I:= TJDBreadcrumbItem(Collection.Items[X]);
        C:= C + Breadcrumb.Gap + I.Length - Breadcrumb.Indent;
      end;
      Result.Left:= Breadcrumb.Margin;
      Result.Top:= C - Breadcrumb.Indent;
      Result.Right:= Breadcrumb.ClientWidth - Breadcrumb.Margin;
      Result.Bottom:= Result.Top + Length;
    end;
  end;
end;

function TJDBreadcrumbItem.GetItemState: TBreadcrumbState;
var
  I: Integer;
begin
  if Enabled then begin
    I:= Breadcrumb.ItemIndex;
    if (Index > I) then
      Result:= stPending
    else if (Index = I) then
      Result:= stSelected
    else if (Index < I) then
      Result:= stPassed;
  end else begin
    Result:= stDisabled;
  end;
end;

function TJDBreadcrumbItem.GetLength: Integer;
begin
  case Breadcrumb.Sizing of
    bsManual: begin
      Result:= FLength;
    end;
    bsAuto: begin
      //Calculate text width

      Result:= FLength;

    end;
    bsStretch: begin
      case Breadcrumb.FDirection of
        bdHorizontal: Result:=
          ((Breadcrumb.ClientWidth) div Collection.Count) +
          Breadcrumb.Indent - Breadcrumb.Gap;
        bdVertical:   Result:=
          ((Breadcrumb.ClientHeight) div Collection.Count) +
          Breadcrumb.Indent - Breadcrumb.Gap;
      end;
    end;
  end;
end;

function TJDBreadcrumbItem.GetOwner: TJDBreadcrumb;
begin
  Result:= TJDBreadcrumb(Collection.Owner);
end;

function TJDBreadcrumbItem.GetPoints: TPoints;
var
  Half: Integer;
  Dist: Integer;
  Br: TJDBreadcrumb;
  Wid: Integer;
begin
  //Return polygon points
  Br:= Breadcrumb;
  if Index = 0 then
    System.SetLength(Result, 6)
  else
    System.SetLength(Result, 7);
  case Br.Direction of
    bdHorizontal: begin
      Dist:= ItemRect.Left;
      Wid:= Breadcrumb.ClientHeight - (Br.Margin * 2);
      Half:= Wid div 2;
      Result[0]:= Point(Br.Margin+Dist,                   Br.Margin);
      Result[1]:= Point(Br.Margin+Dist+Length-Br.Indent,  Br.Margin);
      Result[2]:= Point(Br.Margin+Dist+Length,            BR.Margin+Half);
      Result[3]:= Point(Br.Margin+Dist+Length-Br.Indent,  Br.Margin+Wid);
      Result[4]:= Point(Br.Margin+Dist,                   Br.Margin+Wid);
      if Index = 0 then begin
        Result[5]:= Point(Br.Margin+Dist,                 Br.Margin);
      end else begin
        Result[5]:= Point(Br.Margin+Dist+Br.Indent,       BR.Margin+Half);
        Result[6]:= Point(Br.Margin+Dist,                 Br.Margin);
      end;
    end;
    bdVertical: begin
      Dist:= Self.ItemRect.Top;
      Wid:= Breadcrumb.ClientWidth - (Br.Margin * 2);
      Half:= Wid div 2;
      if Index = 0 then begin
        Result[0]:= Point(Br.Margin,              Br.Margin+Dist);
        Result[1]:= Point(Br.Margin+Wid,          Br.Margin+Dist);
        Result[2]:= Point(Br.Margin+Wid,          Br.Margin+Dist+Length-Br.Indent);
        Result[3]:= Point(Br.Margin+Half,         Br.Margin+Dist+Length);
        Result[4]:= Point(Br.Margin,              Br.Margin+Dist+Length-Br.Indent);
        Result[5]:= Point(Br.Margin,              Br.Margin+Dist);
      end else begin
        Result[0]:= Point(Br.Margin,              Br.Margin+Dist);
        Result[1]:= Point(Br.Margin+Half,         Br.Margin+Dist+Br.Indent);
        Result[2]:= Point(Br.Margin+Wid,          Br.Margin+Dist);
        Result[3]:= Point(Br.Margin+Wid,          Br.Margin+Dist+Length-Br.Indent);
        Result[4]:= Point(Br.Margin+Half,         Br.Margin+Dist+Length);
        Result[5]:= Point(Br.Margin,              Br.Margin+Dist+Length-Br.Indent);
        Result[6]:= Point(Br.Margin,              Br.Margin+Dist);
      end;
    end;
  end;
end;

procedure TJDBreadcrumbItem.Invalidate;
begin
  TJDBreadcrumb(TJDBreadcrumbItems(Self.Collection).Owner).Invalidate;
end;

procedure TJDBreadcrumbItem.SetCaption(const Value: TCaption);
begin
  if Value <> FCaption then begin
    FCaption := Value;
    Invalidate;
  end;
end;

procedure TJDBreadcrumbItem.SetEnabled(const Value: Boolean);
begin
  if Value <> FEnabled then begin
    FEnabled := Value;
    Invalidate;
  end;
end;

procedure TJDBreadcrumbItem.SetSubCaption(const Value: TCaption);
begin
  if Value <> FSubCaption then begin
    FSubCaption := Value;
    Invalidate;
  end;
end;

procedure TJDBreadcrumbItem.SetLength(const Value: Integer);
begin
  if Value <> FLength then begin
    FLength := Value;
    //Breadcrumb.Sizing:= bsManual;
    Invalidate;
  end;
end;

{ TJDBreadcrumbItems }

constructor TJDBreadcrumbItems.Create(AOwner: TComponent);
begin
  inherited Create(AOwner, TJDBreadcrumbItem);
end;

destructor TJDBreadcrumbItems.Destroy;
begin
  inherited;
end;

function TJDBreadcrumbItems.Add: TJDBreadcrumbItem;
begin
  Result:= TJDBreadcrumbItem(inherited Add);
  Result.FIndex:= 0;
  Result.FLength:= 150;
  Result.FCaption:= 'New Item';
  Result.FSubCaption:= '';
  Result.FEnabled:= True;
  Invalidate;
end;

procedure TJDBreadcrumbItems.Delete(const Index: Integer);
begin
  inherited Delete(Index);
  Invalidate;
end;

function TJDBreadcrumbItems.GetItem(Index: Integer): TJDBreadcrumbItem;
begin
  Result:= nil;
  if (Index >= 0) and (Index < Count) then begin
    Result:= TJDBreadcrumbItem(inherited Items[Index]);
  end;
end;

procedure TJDBreadcrumbItems.Invalidate;
var
  X: Integer;
begin
  for X := 0 to Count - 1 do
    Items[X].FIndex:= X;
  TJDBreadcrumb(Owner).Invalidate;
end;

procedure TJDBreadcrumbItems.SetItem(Index: Integer;
  const Value: TJDBreadcrumbItem);
begin
  if (Index >= 0) and (Index < Count) then begin
    inherited Items[Index]:= Value;
    Invalidate;
  end;
end;

{ TJDBreadcrumbItemStyle }

procedure TJDBreadcrumbItemStyle.Changed;
begin
  if assigned(FOnChange) then
    FOnChange(Self);
end;

constructor TJDBreadcrumbItemStyle.Create;
begin
  FCaptionFont:= TFont.Create;
  FCaptionFont.OnChange:= FontChanged;
  FSubCaptionFont:= TFont.Create;
  FSubCaptionFont.OnChange:= FontChanged;
  FColor:= clBlue;
  FBorderWidth:= 1;
  FBorderColor:= clGray;
end;

destructor TJDBreadcrumbItemStyle.Destroy;
begin
  FCaptionFont.Free;
  FSubCaptionFont.Free;
  inherited;
end;

procedure TJDBreadcrumbItemStyle.FontChanged(Sender: TObject);
begin
  Changed;
end;

procedure TJDBreadcrumbItemStyle.SetColor(const Value: TColor);
begin
  if Value <> FColor then begin
    FColor := Value;
    Changed;
  end;
end;

procedure TJDBreadcrumbItemStyle.SetSubCaptionFont(const Value: TFont);
begin
  FSubCaptionFont := Value;
  Changed;
end;

procedure TJDBreadcrumbItemStyle.SetBorderColor(const Value: TColor);
begin
  if Value <> FBorderColor then begin
    FBorderColor := Value;
    Changed;
  end;
end;

procedure TJDBreadcrumbItemStyle.SetBorderWidth(const Value: Integer);
begin
  if Value <> FBorderWidth then begin
    FBorderWidth := Value;
    Changed;
  end;
end;

procedure TJDBreadcrumbItemStyle.SetCaptionFont(const Value: TFont);
begin
  FCaptionFont.Assign(Value);
  Changed;
end;

end.

JD Solutions
 
Whenever I see "Delphi, control and flicker" together I usually see it paired with: BeginUpdate/EndUpdate and/or DoubleBuffered.
 
Indeed, using "DoubleBuffered" solves the flicker, but I was hoping to find a solution which didn't require that. I would like to know that my drawing is efficient and snappy without double-buffering.

JD Solutions
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top