Tulip.UI.ListBoxes.pas

Source Files

{******************************************************************************}
{                                                                              }
{                        Tulip - User Interface Library                        }
{                                                                              }
{         Copyright(c) 2012 - 2013 Marcos Gomes. All rights Reserved.          }
{                                                                              }
{  --------------------------------------------------------------------------  }
{                                                                              }
{  This product is based on Asphyre Sphinx (c) 2000 - 2012  Yuriy Kotsarenko.  }
{       All rights reserved. Official web site: http://www.afterwarp.net       }
{                                                                              }
{******************************************************************************}
{                                                                              }
{  Important Notice:                                                           }
{                                                                              }
{  If you modify/use this code or one of its parts either in original or       }
{  modified form, you must comply with Mozilla Public License Version 2.0,     }
{  including section 3, "Responsibilities". Failure to do so will result in    }
{  the license breach, which will be resolved in the court. Remember that      }
{  violating author's rights either accidentally or intentionally is           }
{  considered a serious crime in many countries. Thank you!                    }
{                                                                              }
{  !! Please *read* Mozilla Public License 2.0 document located at:            }
{  http://www.mozilla.org/MPL/                                                 }
{                                                                              }
{  --------------------------------------------------------------------------  }
{                                                                              }
{  The contents of this file are subject to the Mozilla Public License         }
{  Version 2.0 (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 Tulip.UI.ListBoxes.pas.                                }
{                                                                              }
{  The Initial Developer of the Original Code is Marcos Gomes.                 }
{  Portions created by Marcos Gomes are Copyright (C) 2012, Marcos Gomes.      }
{  All Rights Reserved.                                                        }
{                                                                              }
{******************************************************************************}
{                                                                              }
{  Tulip.UI.ListBoxes.pas                               Modified: 23-Mar-2013  }
{  --------------------------------------------------------------------------  }
{                                                                              }
{                  Base Implementations for ListBox Controls                   }
{                                                                              }
{                                Version 1.03                                  }
{                                                                              }
{******************************************************************************}

unit Tulip.UI.ListBoxes;

interface

uses
  Winapi.Windows, System.SysUtils, System.Types, System.Classes, System.Math,
  // Aspryre units
  AsphyreTypes, AbstractCanvas, AsphyreFonts, AsphyreImages, AsphyreUtils,
  Vectors2,
  // Tulip UI Units
  Tulip.UI.Classes, Tulip.UI.Types, Tulip.UI.Utils, Tulip.UI.Controls,
  Tulip.UI.Forms, Tulip.UI.Helpers;

type
{$REGION 'TCustomAListBox'}
  TCustomAListBox = class(TWControl)
  private
    FAntialiased: Boolean;
    FBorder: TBorder;
    FColor: TFillColor;
    FDownButton: TBtBox;
    FFocusRect: TFocusRect;
    FFont: TEditFont;
    FImage: TImage;
    FIndex: Integer;
    FLineHeight: Integer;
    FMargin: Word;
    FScrollButton: TBtBox;
    FStrings: TAStringList;
    FTransparent: Boolean;
    FUpButton: TBtBox;

    FVirtualPos: Integer;

    function GetVirtualHeight: Integer;
    function GetVirtualWidth: Integer;

    procedure SetAntialiased(Value: Boolean);
    procedure SetBorder(Value: TBorder);
    procedure SetColor(Value: TFillColor);
    procedure SetDownButton(Value: TBtBox);
    procedure SetFocusRect(Value: TFocusRect);
    procedure SetFont(Value: TEditFont);
    procedure SetImage(Value: TImage);
    procedure SetIndex(Value: Integer);
    procedure SetLineHeight(Value: Integer);
    procedure SetMargin(Value: Word);
    procedure SetScrollButton(Value: TBtBox);
    procedure SetStrings(Value: TAStringList);
    procedure SetTransparent(Value: Boolean);
    procedure SetUpButton(Value: TBtBox);
  protected
    procedure AssignTo(Dest: TPersistent); override;
    procedure Paint; override;
    procedure PaintScrollBar;

    procedure SetHeight(Value: Integer); override;
    procedure SetWidth(Value: Integer); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure KeyDown(var Key: Word; Shift: TShiftState); override;

    procedure MouseLeave; override;

    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;

    function MouseWheelDown(Shift: TShiftState; MousePos: TPoint)
      : Boolean; override;
    function MouseWheelUp(Shift: TShiftState; MousePos: TPoint)
      : Boolean; override;

    property Antialiased: Boolean read FAntialiased write SetAntialiased;
    property Border: TBorder read FBorder write SetBorder;
    property Color: TFillColor read FColor write SetColor;
    property DownButton: TBtBox read FDownButton write SetDownButton;
    property FocusRect: TFocusRect read FFocusRect write SetFocusRect;
    property Font: TEditFont read FFont write SetFont;
    property Image: TImage read FImage write SetImage;
    property ItemIndex: Integer read FIndex write SetIndex;
    property LineHeight: Integer read FLineHeight write SetLineHeight;
    property Lines: TAStringList read FStrings write SetStrings;
    property Margin: Word read FMargin write SetMargin;
    property ScrollButton: TBtBox read FScrollButton write SetScrollButton;
    property Transparent: Boolean read FTransparent write SetTransparent;
    property UpButton: TBtBox read FUpButton write SetUpButton;
  end;
{$ENDREGION}

implementation

{$REGION 'TCustomAListBox'}
{ TCustomAListBox }

procedure TCustomAListBox.AssignTo(Dest: TPersistent);
begin
  ControlState := ControlState + [csReadingState];

  inherited AssignTo(Dest);

  if Dest is TCustomAListBox then
    with TCustomAListBox(Dest) do
    begin
      Antialiased := Self.Antialiased;
      Border := Self.Border;
      Color := Self.Color;
      DownButton := Self.DownButton;
      FocusRect := Self.FocusRect;
      Font := Self.Font;
      Image := Self.Image;
      ItemIndex := Self.ItemIndex;
      LineHeight := Self.LineHeight;
      Lines := Self.Lines;
      Margin := Self.Margin;
      ScrollButton := Self.ScrollButton;
      Transparent := Self.Transparent;
      UpButton := Self.UpButton;
    end;

  ControlState := ControlState - [csReadingState];
end;

constructor TCustomAListBox.Create(AOwner: TComponent);
var
  Num: Integer;
begin
  ControlState := ControlState + [csCreating];

  inherited Create(AOwner);

  if (AOwner <> nil) and (AOwner <> Self) and (AOwner is TWControl) then
  begin
    // Auto generate name
    Num := 1;
    while AOwner.FindComponent('ListBox' + IntToStr(Num)) <> nil do
      Inc(Num);
    Name := 'ListBox' + IntToStr(Num);
  end;

  // Set Fields
  FAntialiased := True;
  FBorder := TBorder.Create;
  FBorder.Color := $B0FFFFFF;
  FBorder.Size := 1;
  FColor := TFillColor.Create($FF4090F0, $FF4090F0, $FF6EAAF4, $FF6EAAF4);
  FDownButton := TBtBox.Create;
  FDownButton.Height := 16;
  FDownButton.Width := 16;
  FFocusRect := fDark;
  FFont := TEditFont.Create;
  FImage := TImage.Create;
  FIndex := -1;
  LineHeight := 16;
  FMargin := 1;
  FScrollButton := TBtBox.Create;
  FScrollButton.Height := 16;
  FScrollButton.Width := 16;
  FStrings := TAStringList.Create;
  FTransparent := False;
  FUpButton := TBtBox.Create;
  FUpButton.Height := 16;
  FUpButton.Width := 16;
  FVirtualPos := 0;

  // Set Properties
  Self.Left := 0;
  Self.Top := 0;
  Self.Height := 120;
  Self.Width := 120;
  Self.TabStop := True;

  ControlState := ControlState - [csCreating];
end;

destructor TCustomAListBox.Destroy;
begin
  FBorder.Free;
  FColor.Free;
  FDownButton.Free;
  FFont.Free;
  FImage.Free;
  FScrollButton.Free;
  FStrings.Free;
  FUpButton.Free;

  inherited;
end;

function TCustomAListBox.GetVirtualHeight: Integer;
begin
  Result := FLineHeight * FStrings.Count;
end;

function TCustomAListBox.GetVirtualWidth: Integer;
begin
  Result := Self.Width - (FBorder.Size * 2) - (FMargin * 2) -
    Max(Max(FUpButton.Width, FDownButton.Width), FScrollButton.Width);
end;

procedure TCustomAListBox.KeyDown(var Key: Word; Shift: TShiftState);
var
  H: Integer;
  dLines: Integer;
begin
  H := Self.Height - FMargin * 2 - FBorder.Size * 2;
  dLines := H div FLineHeight;

  if Key = VK_UP then
  begin
    if (FStrings.Count > 0) and (FIndex > 0) then
    begin
      Self.ItemIndex := FIndex - 1;

      if (Abs(FVirtualPos) > FIndex * FLineHeight) then
      begin
        FVirtualPos := -(FIndex * FLineHeight);
      end;
    end;
  end;

  // Page_Up pressed
  if Key = VK_PRIOR then
  begin
    if (FStrings.Count > 0) and (FIndex > 0) then
    begin
      Self.ItemIndex := FIndex - dLines;

      if (Abs(FVirtualPos) > FIndex * FLineHeight) then
      begin
        FVirtualPos := -(FIndex * FLineHeight);
      end;
    end;
  end;

  if Key = VK_HOME then
  begin
    if (FStrings.Count > 0) and (FIndex > 0) then
    begin
      Self.ItemIndex := 0;

      if (Abs(FVirtualPos) > FIndex * FLineHeight) then
      begin
        FVirtualPos := -(FIndex * FLineHeight);
      end;
    end;
  end;

  if Key = VK_DOWN then
  begin
    if (FStrings.Count > 0) and (FIndex < FStrings.Count - 1) then
    begin
      Self.ItemIndex := FIndex + 1;

      if ((FIndex + 1) * FLineHeight) > (dLines * FLineHeight - FVirtualPos)
      then
      begin
        FVirtualPos := -(((FIndex + 1) * FLineHeight) - (dLines * FLineHeight));
      end;
    end;
  end;

  // Page_Down pressed
  if Key = VK_NEXT then
  begin
    if (FStrings.Count > 0) and (FIndex < FStrings.Count - 1) then
    begin
      Self.ItemIndex := FIndex + dLines;

      if ((FIndex + 1) * FLineHeight) > (dLines * FLineHeight - FVirtualPos)
      then
      begin
        FVirtualPos := -(((FIndex + 1) * FLineHeight) - (dLines * FLineHeight));
      end;
    end;
  end;

  if Key = VK_END then
  begin
    if (FStrings.Count > 0) and (FIndex < FStrings.Count - 1) then
    begin
      Self.ItemIndex := FStrings.Count - 1;

      if ((FIndex + 1) * FLineHeight) > (dLines * FLineHeight - FVirtualPos)
      then
      begin
        FVirtualPos := -(((FIndex + 1) * FLineHeight) - (dLines * FLineHeight));
      end;
    end;
  end;

  inherited KeyDown(Key, Shift);
end;

procedure TCustomAListBox.MouseDown(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
var
  L, T, H, W: Integer;
  upBtL, upBtT, upBtH, upBtW: Integer;
  dnBtL, dnBtT, dnBtH, dnBtW: Integer;
  srBtL, srBtT, srBtH, srBtW: Integer;
  dLines, VPos: Integer;
begin
  // check if user clicked on client list area
  L := Self.ClientLeft + FMargin + FBorder.Size;
  T := Self.ClientTop + FMargin + FBorder.Size;
  W := Self.ClientLeft + FMargin + FBorder.Size + GetVirtualWidth;
  H := Self.ClientTop + Self.Height - FMargin - FBorder.Size;

  if PtInRect(Rect(L, T, W, H), Point(X, Y)) then
  begin
    VPos := (Y - T) - FVirtualPos;
    Self.ItemIndex := VPos div FLineHeight;
  end;

  // Check if user clicked on scroll area
  L := Self.ClientLeft + FMargin + FBorder.Size + GetVirtualWidth;
  // W := Self.ClientLeft + Self.Width - FMargin - FBorder.Size;

  // Test UpButton
  upBtL := L;
  upBtT := T;
  upBtW := L + FUpButton.Width;
  upBtH := T + FUpButton.Height;

  if PtInRect(Rect(upBtL, upBtT, upBtW, upBtH), Point(X, Y)) then
  begin
    FUpButton.ControlState := FUpButton.ControlState + [csClicked];
  end;

  // Test Down Button
  dnBtL := L;
  dnBtT := H - FDownButton.Height;
  dnBtW := L + FDownButton.Width;
  dnBtH := H;

  if PtInRect(Rect(dnBtL, dnBtT, dnBtW, dnBtH), Point(X, Y)) then
  begin
    FDownButton.ControlState := FDownButton.ControlState + [csClicked];
  end;

  // Test Scroll Button
  dLines := (H - T) div FLineHeight;

  T := T + FUpButton.Height;
  H := H - FDownButton.Height;

  srBtL := L;
  srBtT := T + Round(Abs(FVirtualPos) /
    (GetVirtualHeight - (FLineHeight * dLines)) *
    (H - T - FScrollButton.Height));
  srBtW := L + FScrollButton.Width;
  srBtH := srBtT + FScrollButton.Height;

  if PtInRect(Rect(srBtL, srBtT, srBtW, srBtH), Point(X, Y)) then
  begin
    FScrollButton.ControlState := FScrollButton.ControlState + [csClicked];
  end;

  inherited MouseDown(Button, Shift, X, Y);
end;

procedure TCustomAListBox.MouseLeave;
begin
  FUpButton.ControlState := FUpButton.ControlState - [csMouseHover];
  FDownButton.ControlState := FDownButton.ControlState - [csMouseHover];
  FScrollButton.ControlState := FScrollButton.ControlState - [csMouseHover];

  inherited MouseLeave;
end;

procedure TCustomAListBox.MouseMove(Shift: TShiftState; X, Y: Integer);
var
  L, T, H: Integer;
  upBtL, upBtT, upBtH, upBtW: Integer;
  dnBtL, dnBtT, dnBtH, dnBtW: Integer;
  srBtL, srBtT, srBtH, srBtW: Integer;
  dLines, VPos: Integer;
begin
  L := Self.ClientLeft + FMargin + FBorder.Size + GetVirtualWidth;
  T := Self.ClientTop + FMargin + FBorder.Size;
  // W := Self.ClientLeft + Self.Width - FMargin - FBorder.Size;
  H := Self.ClientTop + Self.Height - FMargin - FBorder.Size;

  // Test UpButton
  upBtL := L;
  upBtT := T;
  upBtW := L + FUpButton.Width;
  upBtH := T + FUpButton.Height;

  if PtInRect(Rect(upBtL, upBtT, upBtW, upBtH), Point(X, Y)) then
  begin
    FUpButton.ControlState := FUpButton.ControlState + [csMouseHover];
  end
  else
    FUpButton.ControlState := FUpButton.ControlState - [csMouseHover];

  // Test Down Button
  dnBtL := L;
  dnBtT := H - FDownButton.Height;
  dnBtW := L + FDownButton.Width;
  dnBtH := H;

  if PtInRect(Rect(dnBtL, dnBtT, dnBtW, dnBtH), Point(X, Y)) then
  begin
    FDownButton.ControlState := FDownButton.ControlState + [csMouseHover];
  end
  else
    FDownButton.ControlState := FDownButton.ControlState - [csMouseHover];

  // Test Scroll Button
  dLines := (H - T) div FLineHeight;

  T := T + FUpButton.Height;
  H := H - FDownButton.Height;

  if csClicked in FScrollButton.ControlState then
  begin
    if GetVirtualHeight - (FLineHeight * dLines) > 0 then
    begin
      VPos := Round((Y - (T + FScrollButton.Height div 2)) /
        (H - T - FScrollButton.Height) *
        (GetVirtualHeight - (FLineHeight * dLines)));

      FVirtualPos := -VPos;

      if FVirtualPos > 0 then
        FVirtualPos := 0;

      if (GetVirtualHeight - (FLineHeight * dLines)) < Abs(FVirtualPos) then
        FVirtualPos := -(GetVirtualHeight - (FLineHeight * dLines));
    end;
  end;

  srBtL := L;
  srBtT := T + Round(Abs(FVirtualPos) /
    (GetVirtualHeight - (FLineHeight * dLines)) *
    (H - T - FScrollButton.Height));
  srBtW := L + FScrollButton.Width;
  srBtH := srBtT + FScrollButton.Height;

  if PtInRect(Rect(srBtL, srBtT, srBtW, srBtH), Point(X, Y)) then
  begin
    FScrollButton.ControlState := FScrollButton.ControlState + [csMouseHover];
  end
  else
    FScrollButton.ControlState := FScrollButton.ControlState - [csMouseHover];

  inherited MouseMove(Shift, X, Y);
end;

procedure TCustomAListBox.MouseUp(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
var
  H: Integer;
  dLines: Integer;
begin
  H := Self.Height - FMargin * 2 - FBorder.Size * 2;
  dLines := H div FLineHeight;

  // Button Up released
  if csClicked in FUpButton.ControlState then
  begin
    if (FVirtualPos < 0) then
    begin
      FVirtualPos := FVirtualPos + FLineHeight;

      if FVirtualPos > 0 then
        FVirtualPos := 0;
    end;

    FUpButton.ControlState := FUpButton.ControlState - [csClicked];
  end;

  // Button Down Released
  if csClicked in FDownButton.ControlState then
  begin
    if (GetVirtualHeight - (FLineHeight * dLines) + FVirtualPos > 0) then
    begin
      FVirtualPos := FVirtualPos - FLineHeight;

      if (GetVirtualHeight - (FLineHeight * dLines)) < Abs(FVirtualPos) then
        FVirtualPos := -(GetVirtualHeight - (FLineHeight * dLines));
    end;

    FDownButton.ControlState := FDownButton.ControlState - [csClicked];
  end;

  // Scroll Button released always
  FScrollButton.ControlState := FScrollButton.ControlState - [csClicked];

  inherited MouseUp(Button, Shift, X, Y);
end;

function TCustomAListBox.MouseWheelDown(Shift: TShiftState;
  MousePos: TPoint): Boolean;
var
  H: Integer;
begin
  H := Self.Height - FMargin * 2 - FBorder.Size * 2;

  if (FVirtualPos + GetVirtualHeight >= H) then
  begin
    FVirtualPos := FVirtualPos - FLineHeight;
  end;

  Result := True;
end;

function TCustomAListBox.MouseWheelUp(Shift: TShiftState;
  MousePos: TPoint): Boolean;
begin
  if (FVirtualPos < 0) then
  begin
    FVirtualPos := FVirtualPos + FLineHeight;

    if FVirtualPos > 0 then
      FVirtualPos := 0;
  end;

  Result := True;
end;

procedure TCustomAListBox.Paint;
var
  I, X, Y: Integer;
  ARect: TRect;
  AImage: TAsphyreImage;
  AFont: TAsphyreFont;
  bTop, bBottom: TConstraintSize;
  L, T, H, W: Integer;
begin
  // Set initial values
  X := ClientLeft;
  Y := ClientTop;

  ControlManager.Canvas.Antialias := FAntialiased;

  // Get size Canvas
  ARect := ControlManager.Canvas.ClipRect;

  // Draw Background
  if not FTransparent then
  begin
    AImage := ControlManager.Images.Image[FImage.Image];
    if AImage <> nil then
    begin
      ControlManager.Canvas.UseImagePx(AImage, pRect4(FImage.Rect));
      ControlManager.Canvas.TexMap(pRect4(Rect(X, Y, X + Width, Y + Height)),
        cAlpha4(FColor), beNormal);
    end
    else
    begin
      ControlManager.Canvas.FillRect(Rect(X, Y, X + Width, Y + Height),
        cColor4(FColor), beNormal);
    end;
  end;

  // Draw Border
  if Border.Size > 0 then
  begin
    bTop := 0;
    bBottom := 0;

    if eTop in Border.Edges then
    begin
      ControlManager.Canvas.FillRect(Rect(X, Y, X + Width, Y + Border.Size),
        Border.Color, beNormal);
      bTop := Border.Size;
    end;

    if eBottom in Border.Edges then
    begin
      ControlManager.Canvas.FillRect(Rect(X, Y + Height - Border.Size,
        X + Width, Y + Height), Border.Color, beNormal);
      bBottom := Border.Size;
    end;

    if eLeft in Border.Edges then
      ControlManager.Canvas.FillRect(Rect(X, Y + bTop, X + Border.Size,
        Y + Height - bBottom), Border.Color, beNormal);

    if eRight in Border.Edges then
      ControlManager.Canvas.FillRect(Rect(X + Width - Border.Size, Y + bTop,
        X + Width, Y + Height - bBottom), Border.Color, beNormal);
  end;

  // Draw Focus rect
  if (ControlManager.ActiveControl = Self) and (Self.FocusRect = fLight) then
  begin
    ControlManager.Canvas.FrameRect(Rect(X - 1, Y - 1, X + Width + 1,
      Y + Height + 1), cColor4($40FFFFFF), beNormal);
  end;
  if (ControlManager.ActiveControl = Self) and (Self.FocusRect = fDark) then
  begin
    ControlManager.Canvas.FrameRect(Rect(X - 1, Y - 1, X + Width + 1,
      Y + Height + 1), cColor4($30000000), beNormal);
  end;

  PaintScrollBar;

  // Set Rect Canvas
  L := X + FBorder.Size + FMargin;
  T := Y + FBorder.Size + FMargin;
  W := L + GetVirtualWidth;
  H := T + Self.Height - FBorder.Size * 2 - FMargin * 2;

  ControlManager.Canvas.ClipRect := ShortRect(Rect(L, T, W, H), ARect);

  // test
  // ControlManager.Canvas.FillRect(Rect(L, T,W, H), $FFFFFFFF, beNormal);

  if GetVirtualHeight <= (Self.Height - (FBorder.Size * 2) - (FMargin * 2)) then
    FVirtualPos := 0;

  T := T + FVirtualPos;

  // Draw Text
  // Draw DisplayText
  AFont := ControlManager.Fonts.Font[FFont.Name];
  if (AFont <> nil) and (FStrings.Count > 0) then
  begin
    for I := 0 to FStrings.Count - 1 do
    begin
      // Draw selected rect
      if I = FIndex then
      begin
        ControlManager.Canvas.FillRect(Rect(L, T + (LineHeight * I), W,
          T + (LineHeight * (I + 1))), cColor4(FFont.SelectionColor), beNormal);
        AFont.TextOut(Point2(L, T + (LineHeight * I)), FStrings.Items[I],
          cColor2($B0FFFFFF), 1.0);
      end
      else
      begin
        AFont.TextOut(Point2(L, T + (LineHeight * I)), FStrings.Items[I],
          cColor2(FFont.Color), 1.0);
      end;
    end;
  end;

  // Set Rect Canvas
  ControlManager.Canvas.ClipRect := ARect;
end;

procedure TCustomAListBox.PaintScrollBar;
var
  X, Y: Integer;
  AImage: TAsphyreImage;
  bTop, bBottom: TConstraintSize;
  L, T, H, W: Integer;

  upBtColor: TFillColor;
  upBtImage: TImage;
  upBtBorderColor: TAColor;
  upBtL, upBtT, upBtH, upBtW: Integer;

  dnBtColor: TFillColor;
  dnBtImage: TImage;
  dnBtBorderColor: TAColor;
  dnBtL, dnBtT, dnBtH, dnBtW: Integer;

  srBtColor: TFillColor;
  srBtImage: TImage;
  srBtBorderColor: TAColor;
  srBtL, srBtT, srBtH, srBtW: Integer;

  dLines: Integer;
begin
  // Set initial values
  X := ClientLeft;
  Y := ClientTop;

  L := X + FMargin + FBorder.Size + GetVirtualWidth;
  T := Y + FMargin + FBorder.Size;
  W := X + Width - FMargin - FBorder.Size;
  H := Y + Height - FMargin - FBorder.Size;

  // Draw background shadow
  AImage := ControlManager.Images.Image[FImage.Image];
  if AImage = nil then
  begin
    ControlManager.Canvas.FillRect(Rect(L, T, W, H), cColor4($20000000),
      beNormal);
  end;

  // Draw UpButton
  if csClicked in FUpButton.ControlState then
  begin
    upBtColor := FUpButton.ColorPressed;
    upBtImage := FUpButton.ImagePressed;
    upBtBorderColor := FUpButton.Border.ColorPressed;
  end
  else if csMouseHover in FUpButton.ControlState then
  begin
    upBtColor := FUpButton.ColorHover;
    upBtImage := FUpButton.ImageHover;
    upBtBorderColor := FUpButton.Border.ColorHover;
  end
  else
  begin
    upBtColor := FUpButton.Color;
    upBtImage := FUpButton.Image;
    upBtBorderColor := FUpButton.Border.Color;
  end;

  upBtL := L;
  upBtT := T;
  upBtW := L + FUpButton.Width;
  upBtH := T + FUpButton.Height;

  AImage := ControlManager.Images.Image[upBtImage.Image];
  if AImage <> nil then
  begin
    ControlManager.Canvas.UseImagePx(AImage, pRect4(upBtImage.Rect));
    ControlManager.Canvas.TexMap(pRect4(Rect(upBtL, upBtT, upBtW, upBtH)),
      cAlpha4(upBtColor), beNormal);
  end
  else
  begin
    ControlManager.Canvas.FillRect(Rect(upBtL, upBtT, upBtW, upBtH),
      cColor4(upBtColor), beNormal);

    ControlManager.Canvas.FillTri(Point2(upBtL + (FUpButton.Width / 2),
      upBtT + (FUpButton.Border.Size + (FUpButton.Height / 8)) - 1),
      Point2(upBtL + (FUpButton.Border.Size + (FUpButton.Width / 8)),
      upBtH - (FUpButton.Border.Size + (FUpButton.Height / 8)) - 1),
      Point2(upBtW - (FUpButton.Border.Size + (FUpButton.Width / 8)),
      upBtH - (FUpButton.Border.Size + (FUpButton.Height / 8)) - 1), $20000000,
      $20000000, $20000000, beNormal);
  end;

  // Draw UpButton Border
  if FUpButton.Border.Size > 0 then
  begin
    bTop := 0;
    bBottom := 0;

    if eTop in FUpButton.Border.Edges then
    begin
      ControlManager.Canvas.FillRect(Rect(upBtL, upBtT, upBtW,
        upBtT + FUpButton.Border.Size), upBtBorderColor, beNormal);
      bTop := FUpButton.Border.Size;
    end;

    if eBottom in FUpButton.Border.Edges then
    begin
      ControlManager.Canvas.FillRect(Rect(upBtL, upBtH - FUpButton.Border.Size,
        upBtW, upBtH), upBtBorderColor, beNormal);
      bBottom := FUpButton.Border.Size;
    end;

    if eLeft in FUpButton.Border.Edges then
      ControlManager.Canvas.FillRect(Rect(upBtL, upBtT + bTop,
        upBtL + FUpButton.Border.Size, upBtH - bBottom), upBtBorderColor,
        beNormal);

    if eRight in FUpButton.Border.Edges then
      ControlManager.Canvas.FillRect(Rect(upBtW - FUpButton.Border.Size,
        upBtT + bTop, upBtW, upBtH - bBottom), upBtBorderColor, beNormal);
  end;

  // Draw DownButton
  if csClicked in FDownButton.ControlState then
  begin
    dnBtColor := FDownButton.ColorPressed;
    dnBtImage := FDownButton.ImagePressed;
    dnBtBorderColor := FDownButton.Border.ColorPressed;
  end
  else if csMouseHover in FDownButton.ControlState then
  begin
    dnBtColor := FDownButton.ColorHover;
    dnBtImage := FDownButton.ImageHover;
    dnBtBorderColor := FDownButton.Border.ColorHover;
  end
  else
  begin
    dnBtColor := FDownButton.Color;
    dnBtImage := FDownButton.Image;
    dnBtBorderColor := FDownButton.Border.Color;
  end;

  dnBtL := L;
  dnBtT := H - FDownButton.Height;
  dnBtW := L + FDownButton.Width;
  dnBtH := H;

  AImage := ControlManager.Images.Image[dnBtImage.Image];
  if AImage <> nil then
  begin
    ControlManager.Canvas.UseImagePx(AImage, pRect4(dnBtImage.Rect));
    ControlManager.Canvas.TexMap(pRect4(Rect(dnBtL, dnBtT, dnBtW, dnBtH)),
      cAlpha4(dnBtColor), beNormal);
  end
  else
  begin
    ControlManager.Canvas.FillRect(Rect(dnBtL, dnBtT, dnBtW, dnBtH),
      cColor4(dnBtColor), beNormal);
    ControlManager.Canvas.FillTri
      (Point2(dnBtL + (FDownButton.Border.Size + (FDownButton.Width / 8)),
      dnBtT + (FDownButton.Border.Size + (FDownButton.Height / 8)) + 1),
      Point2(dnBtW - (FDownButton.Border.Size + (FDownButton.Width / 8)),
      dnBtT + (FDownButton.Border.Size + (FDownButton.Height / 8)) + 1),
      Point2(dnBtL + (FDownButton.Width / 2),
      dnBtH - (FDownButton.Border.Size + (FDownButton.Height / 8)) + 1),
      $20000000, $20000000, $20000000, beNormal);
  end;

  // Draw DownButton Border
  if FDownButton.Border.Size > 0 then
  begin
    bTop := 0;
    bBottom := 0;

    if eTop in FDownButton.Border.Edges then
    begin
      ControlManager.Canvas.FillRect(Rect(dnBtL, dnBtT, dnBtW,
        dnBtT + FDownButton.Border.Size), dnBtBorderColor, beNormal);
      bTop := FDownButton.Border.Size;
    end;

    if eBottom in FDownButton.Border.Edges then
    begin
      ControlManager.Canvas.FillRect
        (Rect(dnBtL, dnBtH - FDownButton.Border.Size, dnBtW, dnBtH),
        dnBtBorderColor, beNormal);
      bBottom := FDownButton.Border.Size;
    end;

    if eLeft in FDownButton.Border.Edges then
      ControlManager.Canvas.FillRect(Rect(dnBtL, dnBtT + bTop,
        dnBtL + FDownButton.Border.Size, dnBtH - bBottom), dnBtBorderColor,
        beNormal);

    if eRight in FDownButton.Border.Edges then
      ControlManager.Canvas.FillRect(Rect(dnBtW - FDownButton.Border.Size,
        dnBtT + bTop, dnBtW, dnBtH - bBottom), dnBtBorderColor, beNormal);
  end;

  // Draw ScrollButton
  if csClicked in FScrollButton.ControlState then
  begin
    srBtColor := FScrollButton.ColorPressed;
    srBtImage := FScrollButton.ImagePressed;
    srBtBorderColor := FScrollButton.Border.ColorPressed;
  end
  else if csMouseHover in FScrollButton.ControlState then
  begin
    srBtColor := FScrollButton.ColorHover;
    srBtImage := FScrollButton.ImageHover;
    srBtBorderColor := FScrollButton.Border.ColorHover;
  end
  else
  begin
    srBtColor := FScrollButton.Color;
    srBtImage := FScrollButton.Image;
    srBtBorderColor := FScrollButton.Border.Color;
  end;

  dLines := (H - T) div FLineHeight;

  T := T + FUpButton.Height;
  H := H - FDownButton.Height;

  srBtL := L;
  srBtT := T + Round(Abs(FVirtualPos) /
    (GetVirtualHeight - (FLineHeight * dLines)) *
    (H - T - FScrollButton.Height));
  srBtW := L + FScrollButton.Width;
  srBtH := srBtT + FScrollButton.Height;

  AImage := ControlManager.Images.Image[srBtImage.Image];
  if AImage <> nil then
  begin
    ControlManager.Canvas.UseImagePx(AImage, pRect4(srBtImage.Rect));
    ControlManager.Canvas.TexMap(pRect4(Rect(srBtL, srBtT, srBtW, srBtH)),
      cAlpha4(srBtColor), beNormal);
  end
  else
  begin
    ControlManager.Canvas.FillRect(Rect(srBtL, srBtT, srBtW, srBtH),
      cColor4(srBtColor), beNormal);
  end;

  // Draw ScrollButton Border
  if FScrollButton.Border.Size > 0 then
  begin
    bTop := 0;
    bBottom := 0;

    if eTop in FScrollButton.Border.Edges then
    begin
      ControlManager.Canvas.FillRect(Rect(srBtL, srBtT, srBtW,
        srBtT + FScrollButton.Border.Size), srBtBorderColor, beNormal);
      bTop := FScrollButton.Border.Size;
    end;

    if eBottom in FScrollButton.Border.Edges then
    begin
      ControlManager.Canvas.FillRect
        (Rect(srBtL, srBtH - FScrollButton.Border.Size, srBtW, srBtH),
        srBtBorderColor, beNormal);
      bBottom := FScrollButton.Border.Size;
    end;

    if eLeft in FScrollButton.Border.Edges then
      ControlManager.Canvas.FillRect(Rect(srBtL, srBtT + bTop,
        srBtL + FScrollButton.Border.Size, srBtH - bBottom), srBtBorderColor,
        beNormal);

    if eRight in FScrollButton.Border.Edges then
      ControlManager.Canvas.FillRect(Rect(srBtW - FScrollButton.Border.Size,
        srBtT + bTop, srBtW, srBtH - bBottom), srBtBorderColor, beNormal);
  end;

end;

procedure TCustomAListBox.SetAntialiased(Value: Boolean);
begin
  FAntialiased := Value;
end;

procedure TCustomAListBox.SetBorder(Value: TBorder);
begin
  if Value <> nil then
    FBorder.Assign(Value);
end;

procedure TCustomAListBox.SetColor(Value: TFillColor);
begin
  if Value <> nil then
    FColor.Assign(Value);
end;

procedure TCustomAListBox.SetDownButton(Value: TBtBox);
begin
  if Value <> nil then
    FDownButton.Assign(Value);
end;

procedure TCustomAListBox.SetFocusRect(Value: TFocusRect);
begin
  FFocusRect := Value;
end;

procedure TCustomAListBox.SetFont(Value: TEditFont);
begin
  if Value <> nil then
    FFont.Assign(Value);
end;

procedure TCustomAListBox.SetHeight(Value: Integer);
var
  MinH: Integer;
begin
  MinH := FUpButton.Height + FDownButton.Height + FScrollButton.Height + FMargin
    * 2 + FBorder.Size * 2;

  if (Value < MinH) then
    Value := MinH;

  inherited SetHeight(Value);
end;

procedure TCustomAListBox.SetImage(Value: TImage);
begin
  if Value <> nil then
    FImage.Assign(Value);
end;

procedure TCustomAListBox.SetIndex(Value: Integer);
begin
  FIndex := Value;

  if FIndex >= FStrings.Count then
    FIndex := FStrings.Count - 1;
end;

procedure TCustomAListBox.SetLineHeight(Value: Integer);
begin
  FLineHeight := Value;

  if FLineHeight < 0 then
    FLineHeight := 1;
end;

procedure TCustomAListBox.SetMargin(Value: Word);
begin
  FMargin := Value;

  SetHeight(Self.Height);
  SetWidth(Self.Width);
end;

procedure TCustomAListBox.SetScrollButton(Value: TBtBox);
begin
  if Value <> nil then
    FScrollButton.Assign(Value);
end;

procedure TCustomAListBox.SetStrings(Value: TAStringList);
begin
  if Value <> nil then
    FStrings.Assign(Value);
end;

procedure TCustomAListBox.SetTransparent(Value: Boolean);
begin
  FTransparent := Value;
end;

procedure TCustomAListBox.SetUpButton(Value: TBtBox);
begin
  if Value <> nil then
    FUpButton.Assign(Value);
end;

procedure TCustomAListBox.SetWidth(Value: Integer);
var
  MinW: Integer;
begin
  MinW := (FBorder.Size * 2) + (FMargin * 2) +
    Max(Max(FUpButton.Width, FDownButton.Width), FScrollButton.Width);

  if Value < MinW then
    Value := MinW;

  inherited SetWidth(Value);
end;
{$ENDREGION}

end.