{ ------------------------------------------------------------- }
{ Purpose : Next Collection 6                                   }
{ By      : Bojan Nikolic                                       }
{ For     : BergSoft                                            }
{ ------------------------------------------------------------- }
{ Copyright (C) BergSoft.net 2015. All Rights Reserved.         }
{ ------------------------------------------------------------- }
{$I '..\NxSuite6.inc'}
unit UxControls;

interface

uses
  { Standard }
  Classes, Types, Windows, Graphics, Controls, Messages, ImgList, StdCtrls, ExtCtrls,
  {$IFDEF UI_TYPES}UITypes,{$ENDIF}
  { Next Standard 6 }
  NxIntf6, NxTypes6, NxClasses6, NxConsts6, NxControls6, NxEdit6,
  NxGraphics6;

const
//  clXBtnBorderHot = $00cfcfcf;
  clXBtnBorderPressed = $00B8B8B8;
  clXBtnFace = $00e6e6e6;
  clXControlBorder = $00212121;
  clXScrollBar = $00f2f2f2;
  clXScrollBarThumb = $00c2c2c2;
  clXScrollBarThumbHot = $00919191;
  clXScrollBarThumbPressed = $00616161;

  clModernControlBorder = $00212121;
  clModernHighlight = $00ebd4a9;

  clModernBlueColor = $00E3CCA1;

  clBtnFaceDark = $001F1F1F;
  clCloseBtnHot = $002311E8;
  clCloseBtnPressed = $007A70F1;

  clEdit = $00f0f0f0;
  clEditBorder = $007a7a7a;
  clEditBorderFocused = $00cc9329;
  clEditBorderHot = $00171717;

  clSelectedHotPressed = $00E0BE7F;

  clModernBtnDefault = $00cccccc;

  clModernBtnPressed = $00919191;

  clModernCtrlBorder = $00747474;

  { Button }
  clModernBtnBorder = $00e6e6e6;
  clModernBtnBorderPressed = $00cccccc;

type
  { TUxControl }

  TUxControl = class(TNxUserControl6)
  private
    FBorderColor: TColor;
    FBorderSize: Integer;
    procedure SetBorderColor(const Value: TColor);
    procedure SetBorderSize(const Value: Integer);
  protected
    function GetBorderColor: TColor; virtual;
    procedure Paint; override;
    { Messages }
    procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
    procedure WMNCPaint(var Message: TMessage); message WM_NCPAINT;
  public
    constructor Create(AOwner: TComponent); override;
    procedure InvalidateBorder;
  published
    property BorderColor: TColor read FBorderColor write SetBorderColor default clModernCtrlBorder;
    property BorderSize: Integer read FBorderSize write SetBorderSize default 2;
  end;

  { TUxScrollBarView }

  TUxScrollControl = class;

  TUxScrollBarView = class
  private
    FBoundsRect: TRect;
    FButtonState: array[TNxScrollButtonKind] of TNxButtonState;
    FCaptured: Boolean;
    FDragging: Boolean;
    FOnChange: TNotifyEvent;
    FOwner: INxViewOwner;
    FScrollBar: TNxPersistentScrollBar6;
    function GetButtonRect(const Index: TNxScrollButtonKind): TRect;
    function GetButtonState(const Index: TNxScrollButtonKind): TNxButtonState;
    function GetClientRect: TRect;
    function GetPageRect: TRect;
    procedure SetButtonState(const Index: TNxScrollButtonKind; const Value: TNxButtonState);
    procedure SetScrollBar(const Value: TNxPersistentScrollBar6);
  protected
    { Event Handlers }
    procedure DoVertScrollBarChange(Sender: TObject);
    function HasPressed: Boolean;
    { Protected Properties }
    property ButtonRect[const Index: TNxScrollButtonKind]: TRect read GetButtonRect;
    property ButtonState[const Index: TNxScrollButtonKind]: TNxButtonState read GetButtonState
      write SetButtonState;

    property ClientRect: TRect read GetClientRect;
    property PageRect: TRect read GetPageRect;
  public
    constructor Create(AOwner: INxViewOwner);
    function GetStateColor(State: TNxButtonState): TColor;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); virtual;
    procedure MouseLeave; virtual;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); virtual;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); virtual;
    procedure Paint;
    procedure PaintButton(ButttonRect: TRect; State: TNxButtonState);
    property BoundsRect: TRect read FBoundsRect write FBoundsRect;
    property Captured: Boolean read FCaptured write FCaptured;
    property ScrollBar: TNxPersistentScrollBar6 read FScrollBar write SetScrollBar;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

  { TUxScrollControl }

  TUxScrollControl = class(TUxControl)
  private
    FScrollBarThumbState: TNxButtonState;
    FScrollBarView: TUxScrollBarView;
    FVertScrollBar: TNxPersistentScrollBar6;
    FVertScrollBarHide: TTimer;
    function GetScrollBarRect: TRect;
  protected
    { Event Listeners }
    procedure DoVertScrollBarChange(Sender: TObject);
    procedure DoVertScrollBarTimer(Sender: TObject);
    { Keyboard & Mouse }
    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;
    { Painting }
    procedure Paint; override;
    procedure PaintScrollBar; virtual;
    procedure DoScrollBarChange; virtual; abstract;
    { Messages }
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    procedure WMNCMouseLeave(var Message: TMessage); message WM_NCMOUSELEAVE;
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
    { Protected Properties }
    property ScrollBarRect: TRect read GetScrollBarRect;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { Methods }
    function PtInScrollBar(X, Y: Integer): Boolean;
    procedure ShowScrollBars;
    { Public Properties }
    property VertScrollBar: TNxPersistentScrollBar6 read FVertScrollBar;
  end;

  { TUxButton }

  TUxButtonKind = (bkCollection, bkStandalone);

  TUxButton = class(TUxControl)
  private
    FAlignment: TAlignment;
    FGlyph: TPicture;
    FOrientation: TNxOrientation;
    FPressedBorderColor: TColor;
    FPressedColor: TColor;
    FSpacing: Integer;
    FState: TNxButtonState;
    FSubCaption: WideString;
    function GetContentRect: TRect;
    function GetContentSize: TSize;
    procedure SetAlignment(const Value: TAlignment);
    procedure SetOrientation(const Value: TNxOrientation);
    procedure SetSpacing(const Value: Integer);
    procedure SetState(const Value: TNxButtonState);
    procedure SetSubCaption(const Value: WideString);
    procedure SetGlyph(const Value: TPicture);
  protected
    function GetBorderColor: TColor; override;
    { Event Listeners }
    procedure DoGlyphChange(Sender: TObject);
    procedure DoPaddingChange(Sender: TObject); override;
    { Mouse & Keyboard }
    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;
    { Delphi Messages }
    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
    { Inherited Methods }
    procedure Paint; override;
    procedure PaintBackground(ACanvas: TCanvas);
    procedure PaintContent(ACanvas: TCanvas; Dest: TRect); virtual;
    procedure PaintDesignIndicator;
    { Protected Properties }
    property ContentRect: TRect read GetContentRect;
    property ContentSize: TSize read GetContentSize;
    property State: TNxButtonState read FState write SetState;
  public
    procedure Assign(Source: TPersistent); override;
    constructor Create(AOwner: TComponent); override;
    function HasGlyph: Boolean;
    function HasText: Boolean;
    { Public Properties }
  published
    property Alignment: TAlignment read FAlignment write SetAlignment default Classes.taCenter;
    property BorderColor default clModernBtnBorder;
    property Caption;
    property Color default clModernBtnDefault;
    property Glyph: TPicture read FGlyph write SetGlyph;
    property Orientation: TNxOrientation read FOrientation write SetOrientation default orVertical;
    property PressedBorderColor: TColor read FPressedBorderColor write FPressedBorderColor default clModernBtnBorderPressed;
    property PressedColor: TColor read FPressedColor write FPressedColor default clNone;
    property Spacing: Integer read FSpacing write SetSpacing default 8;
    property SubCaption: WideString read FSubCaption write SetSubCaption;
  end;

  { TUxEdit }

  TUxEdit = class(TNxEdit6)
  private
    FBorderColor: TColor;
    FHot: Boolean;
    procedure SetBorderColor(const Value: TColor);
    procedure SetHot(const Value: Boolean);
  protected
    function ActualColor: TColor;
    function CanAutoSize(var NewWidth, NewHeight: Integer): Boolean; override;
    procedure SetAutoSize(Value: Boolean); override;
    { Delphi Messages }
    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    { Winapi Messages }
    procedure WMEraseBkGnd(var Message: TWMEraseBkGnd); message WM_ERASEBKGND;
    procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS;
    procedure WMNCMouseLeave(var Message: TMessage); message WM_NCMOUSELEAVE;
    procedure WMNCPaint(var Message: TMessage); message WM_NCPAINT;
    procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
    { Properties }
    property Hot: Boolean read FHot write SetHot;
  public
    constructor Create(AOwner: TComponent); override;
  published
    { Properties }
    property BevelInner default bvNone;
    property BevelKind default bkNone;
    property BevelOuter default bvNone;
    property BorderColor: TColor read FBorderColor write SetBorderColor default clEditBorder;
    property BorderWidth;
  end;

  { TUxExecuteButton }

  TUxExecuteButton = class(TNxEditButton)

  end;

  { TUxListBox }

  TUxListBox = class;

  TUxStringList = class(TStringList)
  private
    FListBox: TUxListBox;
  protected
    procedure InsertItem(Index: Integer; const S: string; AObject: TObject); override;
  public
    constructor Create(ListBox: TUxListBox); overload;
  end;

  TUxShiftDirection = (diNone, diUp, diDown);

  TUxListBox = class(TUxScrollControl)
  private
    FHotIndex: Integer;
    FIndent: Integer;
    FItemHeight: Integer;
    FItemIndex: Integer;
    FItems: TStrings;
    FPressedIndex: Integer;
    FSelectionColor: TColor;
    function GetDisplayText(const Index: Integer): WideString;
    function GetTopIndex: Integer;
    procedure SetHotIndex(const Value: Integer);
    procedure SetIndent(const Value: Integer);
    procedure SetItemHeight(const Value: Integer);
    procedure SetItemIndex(const Value: Integer);
    procedure SetItems(const Value: TStrings);
    procedure SetPressedIndex(const Value: Integer);
    procedure SetSelectonColor(const Value: TColor);
    procedure SetTopIndex(const Value: Integer);
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    { Keyboard & Mouse }
    procedure KeyDown(var Key: Word; Shift: TShiftState); 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;
    { Wheel Methods }
    function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    { Painting Methods }
    procedure InvalidateItem(const Index: Integer);
    procedure Paint; override;
    procedure PaintItem(const Index: Integer; ItemRect: TRect); virtual;
    procedure PaintItems;
    { ScrollBar }
    procedure DoScrollBarChange; override;
    { Protected Prop. }
    property HotIndex: Integer read FHotIndex write SetHotIndex;
    property PressedIndex: Integer read FPressedIndex write SetPressedIndex;
    { Messages }
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
    procedure WMNCMouseLeave(var Message: TMessage); message WM_NCMOUSELEAVE;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { Public Methods }
    function ItemAtPos(Pos: TPoint): Integer;
    function ItemRect(Index: Integer): TRect;
    procedure RefreshItem(Index: Integer; Shift: TUxShiftDirection);
    { Public Properties }
    property DisplayText[const Index: Integer]: WideString read GetDisplayText;
    property TopIndex: Integer read GetTopIndex write SetTopIndex;
  published
    property BorderColor default clModernBtnBorder;
    property BorderSize default 2;
    property Indent: Integer read FIndent write SetIndent default 16;
    property ItemHeight: Integer read FItemHeight write SetItemHeight default 60;
    property ItemIndex: Integer read FItemIndex write SetItemIndex default -1;
    property Items: TStrings read FItems write SetItems;
    property ParentColor default False;
    property SelectionColor: TColor read FSelectionColor write SetSelectonColor default clModernHighlight;
  end;

  TUxPanelButton = class(TUxButton)
  private
    FLabel: TLabel;
    FButton: TButton;
  protected
    procedure DoChildMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure WMParentNotify(var Message: TWMParentNotify); message WM_PARENTNOTIFY;
  public
    constructor Create(AOwner: TComponent); override;
  end;

  { TUxNotificationControl6 }

  TUxIconSource = (soPicture, soImageList);
  TNxNotificationControlOptions = set of (noCloseButton);
  TUxNotificationControlState = (ncsShowing, ncsHidden, ncsSlidingIn, ncsSlidingOut);

  TUxNotificationControl = class(TNxControl6)
  private
    FAutoClose: Boolean;
    FCaption: WideString;
    FCloseButtonState: TNxButtonState;
    FDelayTimer: TTimer;
    FIconSource: TUxIconSource;
    FImageIndex: TImageIndex;
    FImages: TImageList;
    FOptions: TNxNotificationControlOptions;
    FPicture: TPicture;
    FPictureSpacing: Integer;
    FPopupAnchor: TNxPopupAnchor;
    FSlidingTimer: TTimer;
    FState: TUxNotificationControlState;
    FTextSpacing: Integer;
    function GetCaptionRect: TRect;
    function GetCloseButtonRect: TRect;
    function GetCloseButtonSize: TSize;
    function GetPictureRect: TRect;
    function GetSlidingStep: Integer;
    function GetTextLeft: Integer;
    function GetTextRect: TRect;
    procedure SetButtonState(const Value: TNxButtonState);
    procedure SetCaption(const Value: WideString);
    procedure SetPicture(const Value: TPicture);
    procedure SetState(const Value: TUxNotificationControlState);
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    { Event Handlers }
    procedure DoDelayTimer(Sender: TObject);
    procedure DoSlidingTimer(Sender: TObject);
    { Mouse }
    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;
    { Methods }
    procedure Paint; override;
    procedure PaintCloseButton;
    { Messages }
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    { Properties }
    property CaptionRect: TRect read GetCaptionRect;
    property CloseButtonRect: TRect read GetCloseButtonRect;
    property CloseButtonSize: TSize read GetCloseButtonSize;
    property CloseButtonState: TNxButtonState read FCloseButtonState write SetButtonState;
    property PictureRect: TRect read GetPictureRect;
    property SlidingStep: Integer read GetSlidingStep;
    property TextLeft: Integer read GetTextLeft;
    property TextRect: TRect read GetTextRect;
    { Properties }
    property State: TUxNotificationControlState read FState write SetState;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { Methods }
    function GetBestSize: TSize;
    function HasIcon: Boolean;
    procedure Close;
    procedure Popup; overload;
    procedure Popup(const ACaption, AText: WideString; AHeight: Integer = 0); overload;
  published
    property AutoClose: Boolean read FAutoClose write FAutoClose default True;
    property Caption: WideString read FCaption write SetCaption;
    property Font;
    property IconSource: TUxIconSource read FIconSource write FIconSource default soPicture;
    property ImageIndex: TImageIndex read FImageIndex write FImageIndex default -1;
    property Images: TImageList read FImages write FImages;
    property Options: TNxNotificationControlOptions read FOptions write FOptions default [];
    property Color default clBtnFaceDark;
    property Padding;
    property Picture: TPicture read FPicture write SetPicture;
    property PictureSpacing: Integer read FPictureSpacing write FPictureSpacing default 8;
    property PopupAnchor: TNxPopupAnchor read FPopupAnchor write FPopupAnchor default paRight;
    property Text;
    property TextSpacing: Integer read FTextSpacing write FTextSpacing default 4;
  end;

  { TUxNotificationsManager }

  TUxNotificationsManager = class(TComponent)
  private
    FAutoClose: Boolean;
    FColor: TColor;
    FHeight: Integer;
    FImages: TImageList;
    FOptions: TNxNotificationControlOptions;
    FPopupAnchor: TNxPopupAnchor;
  protected
    procedure SetControlProperties(const Control: TUxNotificationControl);
  public
    constructor Create(AOwner: TComponent); override;
    procedure Popup(Caption, Text: WideString; Picture: TPicture = nil); overload;
    procedure Popup(Caption, Text: WideString; ImageIndex: TImageIndex); overload;
  published
    property AutoClose: Boolean read FAutoClose write FAutoClose default True;
    property Color: TColor read FColor write FColor default clBtnFaceDark;
    property Height: Integer read FHeight write FHeight default 0;
    property Images: TImageList read FImages write FImages;
    property Options: TNxNotificationControlOptions read FOptions write FOptions default [];
    property PopupAnchor: TNxPopupAnchor read FPopupAnchor write FPopupAnchor default paRight;
  end;

implementation

uses
  SysUtils, Math, Forms;

const
  ScrollBtnSize = 12;
  ScrollBarHideInterval = 2000;

type
  TControlAccess = class(TControl);

var
  FLastTop: Integer;

procedure FillPadding(DC: HDC; const Rect: TRect; Color: TColor;
  Paddint: TPadding);
var
  Brush: HBRUSH;
  PaddingRect: TRect;
begin
  Brush := CreateSolidBrush(ColorToRGB(Color));
//  Brush := CreateSolidBrush(clLime);
  SelectObject(DC, Brush);
  try
    PaddingRect := Rect;
    PaddingRect.Bottom := PaddingRect.Top + Paddint.Top;
    FillRect(DC, PaddingRect, Brush);

    PaddingRect := Rect;
    PaddingRect.Top := PaddingRect.Top + Paddint.Top;
    PaddingRect.Right := PaddingRect.Left + Paddint.Left;
    PaddingRect.Bottom := PaddingRect.Bottom - Paddint.Bottom;
    FillRect(DC, PaddingRect, Brush);

    PaddingRect := Rect;
    PaddingRect.Top := PaddingRect.Top + Paddint.Top;
    PaddingRect.Left := PaddingRect.Right - Paddint.Right;
    PaddingRect.Bottom := PaddingRect.Bottom - Paddint.Bottom;
    FillRect(DC, PaddingRect, Brush);

    PaddingRect := Rect;
    PaddingRect.Top := Rect.Bottom - Paddint.Bottom;
    FillRect(DC, PaddingRect, Brush);
  finally
    DeleteObject(Brush);
  end;
end;

{ TUxControl6 }

constructor TUxControl.Create(AOwner: TComponent);
begin
  inherited;
  FBorderSize := 2;
  FBorderColor := clModernCtrlBorder;
  BorderWidth := 0;
  ControlStyle := ControlStyle - [csOpaque];
end;

function TUxControl.GetBorderColor: TColor;
begin
  Result := FBorderColor;
end;

procedure TUxControl.InvalidateBorder;
var
  R: TRect;
begin
  R := ClientRect;
  RedrawWindow(Handle, @R, 0, RDW_FRAME or RDW_INVALIDATE);
  ExcludeClipRect(Canvas, R);
end;

procedure TUxControl.Paint;
begin
  inherited;

end;

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

procedure TUxControl.SetBorderSize(const Value: Integer);
begin
  if Value <> FBorderSize then
  begin
    FBorderSize := Value;
    InvalidateBorder;
  end;
end;

procedure TUxControl.WMNCCalcSize(var Message: TWMNCCalcSize);
var
  Params: PNCCalcSizeParams;
  Pixels: Integer;
begin
  inherited;
  Params := Message.CalcSize_Params;
  with Params^ do
  begin
    Pixels := DotToPixel(BorderSize);
    InflateRect(rgrc[0], -Integer(Pixels), -Integer(Pixels));
  end;
end;

procedure TUxControl.WMNCPaint(var Message: TMessage);
var
  Device: HDC;
  Brush: HBRUSH;
  i: Integer;
  R: TRect;
begin
  inherited;
  if BorderSize > 0 then
  begin
    Device := GetWindowDC(Handle);
    try
      Brush := CreateSolidBrush(ColorToRGB(GetBorderColor));
      SelectObject(Device, Brush);
      try
        GetWindowRect(Handle, R);
        OffsetRect(R, -R.Left, -R.Top);

        for i := 1 to DotToPixel(BorderSize) do
        begin
          FrameRect(Device, R, Brush);
          InflateRect(R, -1, -1);
        end;

      finally
        DeleteObject(Brush);
      end;

    finally
      ReleaseDC(Handle, Device);
    end;
  end;
end;

{ TNxXScrollBarView }

constructor TUxScrollBarView.Create(AOwner: INxViewOwner);
begin
  FOwner := AOwner;
end;

procedure TUxScrollBarView.DoVertScrollBarChange(Sender: TObject);
begin
  FOwner.InvalidateRect(ClientRect);
  if Assigned(FOnChange) then FOnChange(Self);
  WritelnDebug('Position: ' + IntToStr(FScrollBar.Position) + ' / ' +  IntToStr(FScrollBar.Max));
end;

function TUxScrollBarView.GetButtonRect(const Index: TNxScrollButtonKind): TRect;
var
  ButtonSize: Integer;
begin
  ButtonSize := DotToPixel(ScrollBtnSize);
  case Index of
    skPage: Result := PageRect;
    skPrior: Result := Bounds(BoundsRect.Left, BoundsRect.Top, ButtonSize, ButtonSize);
    skNext: Result := Bounds(BoundsRect.Left, BoundsRect.Bottom - ButtonSize, ButtonSize, ButtonSize);
  end;
end;

function TUxScrollBarView.GetButtonState(const Index: TNxScrollButtonKind): TNxButtonState;
begin
  Result := FButtonState[Index];
end;

function TUxScrollBarView.GetClientRect: TRect;
begin
  Result := BoundsRect;
  InflateRect(Result, 0, -DotToPixel(ScrollBtnSize));
end;

function TUxScrollBarView.GetPageRect: TRect;
var
  Height, T: Integer;
  PageRatio, ZX: Double;
begin
  Height := RectHeight(ClientRect);
  if ScrollBar.Max > ScrollBar.PageSize then
  begin
    PageRatio := FScrollBar.PageSize / FScrollBar.Max;

    Result := ClientRect;
    Result.Bottom := Result.Top + Round(Height * PageRatio);


    // Dobijemo jedinicu
    ZX := (Height - RectHeight(Result)) / (FScrollBar.Max - FScrollBar.PageSize);

    T := Round(ZX * FScrollBar.Position);

    OffsetRect(Result, 0, T);
  end;
end;

function TUxScrollBarView.GetStateColor(State: TNxButtonState): TColor;
begin
  Result := clNone;
  if bsHover in State then Result := clXScrollBarThumbHot;
  if bsPressed in State then Result := clXScrollBarThumbPressed;
end;

function TUxScrollBarView.HasPressed: Boolean;
begin
  Result := (bsPressed in FButtonState[skNext])
    or (bsPressed in FButtonState[skPage])
    or (bsPressed in FButtonState[skPrior]);
end;

procedure TUxScrollBarView.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if PtInRect(PageRect, Point(X, Y)) then
  begin
    ButtonState[skPage] := ButtonState[skPage] + [bsPressed];
    FCaptured := True;
  end
  else if PtInRect(ButtonRect[skPrior], Point(X, Y)) then
  begin
    ButtonState[skPrior] := ButtonState[skPrior] + [bsPressed];
    FScrollBar.Prior;
  end
  else if PtInRect(ButtonRect[skNext], Point(X, Y)) then
  begin
    ButtonState[skNext] := ButtonState[skNext] + [bsPressed];
    FScrollBar.Next;
  end
  else if Y < ButtonRect[skPage].Top then
  begin
    FScrollBar.PageUp;
  end else if Y > ButtonRect[skPage].Bottom then
  begin
    FScrollBar.PageDown;
  end;

  FCaptured := HasPressed;
end;

procedure TUxScrollBarView.MouseLeave;
var
  i: TNxScrollButtonKind;
begin
  for i := skNext to skPage do
    ButtonState[i] := ButtonState[i] - [bsHover];
end;

procedure TUxScrollBarView.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  if Captured then Exit;

  if PtInRect(ButtonRect[skPrior], Point(X, Y)) then
  begin
    ButtonState[skPrior] := ButtonState[skPrior] + [bsHover];
  end
  else
    ButtonState[skPrior] := ButtonState[skPrior] - [bsHover];

  if PtInRect(ButtonRect[skNext], Point(X, Y)) then
  begin
    ButtonState[skNext] := ButtonState[skNext] + [bsHover];
  end
  else
    ButtonState[skNext] := ButtonState[skNext] - [bsHover];

  if bsPressed in ButtonState[skPage] then
  begin
//    ScrollBar.Position :=
    Exit;
  end;

  if PtInRect(PageRect, Point(X, Y)) then
  begin
    ButtonState[skPage] := ButtonState[skPage] + [bsHover];
  end
  else
  begin
    ButtonState[skPage] := ButtonState[skPage] - [bsHover];

  end;
end;

procedure TUxScrollBarView.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  i: TNxScrollButtonKind;
begin
  FCaptured := False;
  for i := skNext to skPage do
    ButtonState[i] := ButtonState[i] - [bsPressed];
end;

procedure TUxScrollBarView.Paint;
begin
  with FOwner.Canvas do
  begin
    { Background (Max) }
    Brush.Color := clXScrollBar;
    FillRect(BoundsRect);

    { Prior }
    PaintButton(ButtonRect[skPrior], ButtonState[skPrior]);

    { PageSize }
    if ScrollBar.Max > ScrollBar.PageSize then
    begin
      Brush.Color := clXScrollBarThumb;
      if bsHover in ButtonState[skPage] then Brush.Color := clXScrollBarThumbHot;
      if bsPressed in ButtonState[skPage] then Brush.Color := clXScrollBarThumbPressed;
      FillRect(PageRect);
    end;

    { Next }
    PaintButton(ButtonRect[skNext], ButtonState[skNext]);
  end;
end;

procedure TUxScrollBarView.PaintButton(ButttonRect: TRect; State: TNxButtonState);
begin
  with FOwner.Canvas do
  begin
    Brush.Color := GetStateColor(State);
    if Brush.Color <> clNone then
    begin
      FillRect(ButttonRect);
    end;
  end;
end;

procedure TUxScrollBarView.SetButtonState(const Index: TNxScrollButtonKind;
  const Value: TNxButtonState);
begin
  if Value <> FButtonState[Index] then
  begin
    FButtonState[Index] := Value;
    FOwner.InvalidateRect(ButtonRect[Index]);
  end;
end;

procedure TUxScrollBarView.SetScrollBar(const Value: TNxPersistentScrollBar6);
begin
  FScrollBar := Value;
  FScrollBar.OnChange := DoVertScrollBarChange;
end;

{ TNxXScrollControl6 }

procedure TUxScrollControl.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  FScrollBarView.MouseLeave;
end;

constructor TUxScrollControl.Create(AOwner: TComponent);
begin
  inherited;
  FVertScrollBar := TNxPersistentScrollBar6.Create;
  FVertScrollBar.Visible := False;
  FVertScrollBar.SmallChange := 10;
  FScrollBarView := TUxScrollBarView.Create(Self);
  FScrollBarView.OnChange := DoVertScrollBarChange;
  FScrollBarView.ScrollBar := FVertScrollBar;
end;

destructor TUxScrollControl.Destroy;
begin
  FScrollBarView.Free;
  FVertScrollBar.Free;
  inherited;
end;

procedure TUxScrollControl.DoVertScrollBarChange(Sender: TObject);
begin
  Invalidate;
end;

procedure TUxScrollControl.DoVertScrollBarTimer(Sender: TObject);
begin
  if VertScrollBar.Visible then
  begin
    FVertScrollBar.Visible := False;
    InvalidateRect(ScrollBarRect);
    FreeAndNil(FVertScrollBarHide);
  end;
end;

function TUxScrollControl.GetScrollBarRect: TRect;
const
  BarSize = 12;
var
  BarSizePixels: Integer;
begin
  BarSizePixels := DotToPixel(BarSize);
  Result := Bounds(ClientWidth - BarSizePixels, 0, BarSizePixels, ClientHeight);
end;

procedure TUxScrollControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if PtInScrollBar(X, Y) then
  begin
    FScrollBarView.MouseDown(Button, Shift, X, Y);
  end;
end;

procedure TUxScrollControl.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  FScrollBarView.MouseMove(Shift, X, Y);
  ShowScrollBars;
end;

procedure TUxScrollControl.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  FScrollBarView.MouseUp(Button, Shift, X, Y);
end;

procedure TUxScrollControl.Paint;
begin
  inherited;
  if VertScrollBar.Visible then PaintScrollBar;
  DoScrollBarChange;
end;

procedure TUxScrollControl.PaintScrollBar;
begin
  with Canvas do
  begin
    FScrollBarView.BoundsRect := ScrollBarRect;
    FScrollBarView.Paint;
  end;
end;

function TUxScrollControl.PtInScrollBar(X, Y: Integer): Boolean;
begin
  Result := VertScrollBar.Visible and PtInRect(FScrollBarView.BoundsRect, Point(X, Y));
end;

procedure TUxScrollControl.ShowScrollBars;
begin
  if not VertScrollBar.Visible then
  begin
    VertScrollBar.Visible := True;
    InvalidateRect(ScrollBarRect);
  end;
  FreeAndNil(FVertScrollBarHide);
  FVertScrollBarHide := TTimer.Create(nil);
  FVertScrollBarHide.Interval := ScrollBarHideInterval;
  FVertScrollBarHide.OnTimer := DoVertScrollBarTimer;
end;

procedure TUxScrollControl.WMNCMouseLeave(var Message: TMessage);
begin
  inherited;
  FScrollBarView.MouseLeave;
end;

procedure TUxScrollControl.WMSize(var Message: TWMSize);
begin
  inherited;
  FScrollBarView.BoundsRect := ScrollBarRect;
end;

{ TUxButton6 }

procedure TUxButton.Assign(Source: TPersistent);
begin
  inherited;
  if Source is TUxButton then
  begin
    Alignment := TUxButton(Source).Alignment;
    Glyph := TUxButton(Source).Glyph;
    SubCaption := TUxButton(Source).SubCaption;
  end;
end;

procedure TUxButton.CMMouseEnter(var Message: TMessage);
begin
  inherited;
  State := State + [bsHover];
end;

procedure TUxButton.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  State := State - [bsHover];
end;

procedure TUxButton.CMTextChanged(var Message: TMessage);
begin
  inherited;
  Invalidate;
end;

constructor TUxButton.Create(AOwner: TComponent);
begin
  inherited;
  FAlignment := Classes.taCenter;
  FBorderColor := clModernBtnBorder;
  FGlyph := TPicture.Create;
  FGlyph.OnChange := DoGlyphChange;
  FOrientation := orVertical;
  FPressedBorderColor := clModernBtnBorderPressed;
  FPressedColor := clNone;
  FSpacing := 8;
  FState := [];
  FSubCaption := '';
  ControlStyle := ControlStyle - [csOpaque];
  ParentColor := True;
  Padding.OnChange := DoPaddingChange;
  Height := 100;
  Width := 100;
end;

procedure TUxButton.DoGlyphChange;
begin
  Invalidate;
end;

procedure TUxButton.DoPaddingChange(Sender: TObject);
begin
  inherited;
  Invalidate;
end;

function TUxButton.GetBorderColor: TColor;
begin
  Result := Color;
  if (bsHover in State) or (csDesigning in ComponentState) then
  begin
    Result := BorderColor;
    if bsPressed in State then
    begin
      Result := PressedBorderColor;
      if Result = clNone then Result := PressedColor;
    end;
  end;
end;

function TUxButton.GetContentRect: TRect;
begin
  Result := ClientRect;
  Inc(Result.Top, Padding.Top);
  Inc(Result.Left, Padding.Left);
  Dec(Result.Right, Padding.Right);
  Dec(Result.Bottom, Padding.Bottom);
end;

function TUxButton.GetContentSize: TSize;
var
  CaptionSize, SubCaptionSize: TSize;
begin
  { Reset to cx: 0, cy: 0 }
  Unset(Result);

  Canvas.Font.Assign(Font);

  if Orientation = orVertical then
  begin
    { Glyph }
    if HasGlyph then
    begin
      Result := Size(Glyph.Graphic.Width, Glyph.Graphic.Height);

      if HasText then
      begin
        Inc(Result.cy, DotToPixel(Spacing));
      end;
    end;

    { Caption & SubCaption }
    if HasText then
    begin
      { Caption }
      CaptionSize := GetTextSize(Canvas, Caption);
      Inc(Result.cy, CaptionSize.cy);

      { SubCaption }
      if SubCaption <> '' then
      begin
        Canvas.Font.Size := Canvas.Font.Size - 2;
        SubCaptionSize := GetTextSize(Canvas.Handle, Rect(0, 0, RectWidth(ContentRect), 0),
          SubCaption, wkWordWrap);
        Inc(Result.cy, SubCaptionSize.cy);
      end;
    end;
  end;
end;

function TUxButton.HasGlyph: Boolean;
begin
  Result := Assigned(FGlyph.Graphic) and not FGlyph.Graphic.Empty;
end;

function TUxButton.HasText: Boolean;
begin
  Result := (Caption <> '') or (SubCaption <> '');
end;

procedure TUxButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if Button = mbLeft then State := State + [bsPressed];
end;

procedure TUxButton.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;

end;

procedure TUxButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  State := State - [bsPressed];
end;

procedure TUxButton.Paint;
var
  Buffer: TBitmap;
  BufferRect: TRect;
  Inflation: Integer;
begin
  inherited;
  Buffer := TBitmap.Create;
  Buffer.Width := ClientWidth;
  Buffer.Height := ClientHeight;

  PaintBackground(Buffer.Canvas);

  BufferRect := Bounds(0, 0, Buffer.Width, Buffer.Height);

  try

    PaintContent(Buffer.Canvas, BufferRect);

    BufferRect := ContentRect;

    if bsPressed in State then
    begin
      Inflation := DotToPixel(pdDouble);
      InflateRect(BufferRect, -Inflation, -Inflation);
    end;

    Canvas.StretchDraw(BufferRect, Buffer);

  finally
    FreeAndNil(Buffer);
  end;

  if csDesigning in ComponentState then PaintDesignIndicator;
end;

procedure TUxButton.PaintBackground(ACanvas: TCanvas);
begin
  with ACanvas do
  begin
    Brush.Color := Color;
    if (bsPressed in State) and (PressedColor <> clNone) then Brush.Color := PressedColor;
    FillRect(ClientRect);
  end;
end;

procedure TUxButton.PaintContent(ACanvas: TCanvas; Dest: TRect);
var
  Position, GlyphPosition: TPoint;
  CaptionSize: TSize;
  CaptionRect, SubCaptionRect: TRect;
begin
  Position := Dest.TopLeft;

  with ACanvas do
  begin
    Font.Assign(Self.Font);

    if Orientation = orVertical then
    begin
      Position := NxGraphics6.PosInRect(ContentSize.cx, ContentSize.cy,
        Dest, Classes.taCenter, taVerticalCenter);

      { Glyph }
      if HasGlyph then
      begin
        GlyphPosition := NxGraphics6.PosInRect(Glyph.Graphic.Width, Glyph.Graphic.Height,
          Rect(Dest.Left, Position.Y, Dest.Right, Height), Classes.taCenter, taAlignTop);

        Draw(GlyphPosition.X, GlyphPosition.Y, Glyph.Graphic);

        Inc(Position.Y, Glyph.Height);

        Inc(Position.Y, DotToPixel(Spacing));
      end;

      { Caption }
      if Caption <> '' then
      begin
        CaptionRect := Rect(Dest.Left, Position.Y, Dest.Right, Height);
        CaptionSize := GetTextSize(ACanvas, Caption);

        DrawTextRect(ACanvas, CaptionRect, Classes.taCenter, taAlignTop, Caption, wkEllipsis);

        Inc(Position.Y, CaptionSize.cy);
      end;

      { SubCaption }
      if SubCaption <> '' then
      begin
        Font.Color := clGrayText;
        Font.Size := Font.Size - 2;

        SubCaptionRect := Rect(Dest.Left, Position.Y, Dest.Right, Height);
        DrawTextRect(ACanvas, SubCaptionRect, Classes.taCenter, taAlignTop, SubCaption, wkWordWrap);
      end;

    end else
    begin

      { Glyph }
      if HasGlyph then
      begin
        GlyphPosition := Position;
        Draw(GlyphPosition.X, GlyphPosition.Y, Glyph.Graphic);

        Inc(Position.X, Glyph.Width);
        Inc(Position.X, DotToPixel(Spacing));
      end;

      { Caption }
      if Caption <> '' then
      begin
        CaptionRect := Rect(Position.X, Position.Y, Dest.Right, Height);
        CaptionSize := GetTextSize(ACanvas, Caption);

        DrawTextRect(ACanvas, CaptionRect, taLeftJustify, taAlignTop, Caption, wkEllipsis);

        Inc(Position.Y, CaptionSize.cy);
      end;

      { SubCaption }
      if SubCaption <> '' then
      begin
        Font.Size := Font.Size - 2;
        Font.Color := clGrayText;

        SubCaptionRect := Rect(Position.X, Position.Y, Dest.Right, Height);

        DrawTextRect(ACanvas, SubCaptionRect, taLeftJustify, taAlignTop, SubCaption, wkWordWrap);
      end;
    end;
  end;
end;

procedure TUxButton.PaintDesignIndicator;
begin
  with Canvas do
  begin
    Pen.Color := clModernBlueColor;
    Pen.Style := psDot;
    Brush.Style := bsClear;
    Rectangle(ContentRect);
    Brush.Style := bsSolid;
    Pen.Style := psSolid;
  end;
end;

procedure TUxButton.SetAlignment(const Value: TAlignment);
begin
  if Value <> FAlignment then
  begin
    FAlignment := Value;
    Invalidate;
  end;
end;

procedure TUxButton.SetGlyph(const Value: TPicture);
begin
  FGlyph.Assign(Value);
  FGlyph.OnChange := DoGlyphChange;
end;

procedure TUxButton.SetOrientation(const Value: TNxOrientation);
begin
  if Value <> FOrientation then
  begin
    FOrientation := Value;
    Invalidate;
  end;
end;

procedure TUxButton.SetSpacing(const Value: Integer);
begin
  if Value <> FSpacing then
  begin
    FSpacing := Value;
    Invalidate;
  end;
end;

procedure TUxButton.SetState(const Value: TNxButtonState);
begin
  if Value <> FState then
  begin
    FState := Value;
    InvalidateBorder;
    Invalidate;
  end;
end;

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

{ TUxEdit }

function TUxEdit.ActualColor: TColor;
begin
  Result := Color;
end;

function TUxEdit.CanAutoSize(var NewWidth, NewHeight: Integer): Boolean;
begin
  Result := inherited CanAutoSize(NewWidth, NewHeight);
  NewWidth := NewWidth + Padding.Left + Padding.Right;
  NewHeight := NewHeight + Padding.Top + Padding.Bottom;
end;

procedure TUxEdit.CMMouseEnter(var Message: TMessage);
begin
  inherited;
  Hot := True;
end;

procedure TUxEdit.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  Hot := False;
end;

constructor TUxEdit.Create(AOwner: TComponent);
begin
  inherited;
  ControlStyle := ControlStyle - [csOpaque];
  FBorderColor := clEditBorder;
  FHot := False;
  BevelInner := bvNone;
  BevelOuter := bvNone;
  BevelKind := bkNone;
  BorderStyle := bsNone;
  BorderWidth := 2;
  Padding.Bottom := 2;
  Padding.Left := 2;
  Padding.Right := 2;
  Padding.Top := 2;
end;

procedure TUxEdit.SetAutoSize(Value: Boolean);
begin
  inherited;
  if Value then AdjustSize;

end;

procedure TUxEdit.SetBorderColor(const Value: TColor);
begin
  FBorderColor := Value;
end;

procedure TUxEdit.SetHot(const Value: Boolean);
begin
  if Value <> FHot then
  begin
    FHot := Value;
    Color := ActualColor;
  end;
end;

procedure TUxEdit.WMEraseBkGnd(var Message: TWMEraseBkGnd);
begin
  Message.Result := 1;
end;

procedure TUxEdit.WMKillFocus(var Msg: TWMKillFocus);
begin
  inherited;
  Color := ActualColor;
end;

procedure TUxEdit.WMNCMouseLeave(var Message: TMessage);
begin
  inherited;

end;

procedure TUxEdit.WMNCPaint(var Message: TMessage);
var
  Device: HDC;
  Brush: HBRUSH;
  i: Integer;
  R: TRect;
  BrushColor: TColor;
begin
  if csDesigning in ComponentState then
  begin
    inherited;
  end;

  Device := GetWindowDC(Handle);
  try
    GetWindowRect(Handle, R);
    OffsetRect(R, -R.Left, -R.Top);
    if BorderWidth > 0 then
    begin
      if Focused then BrushColor := clEditBorderFocused else
      begin
        if FHot then BrushColor := clEditBorderHot
        else BrushColor := BorderColor;
      end;

      Brush := CreateSolidBrush(ColorToRGB(BrushColor));
      try
        SelectObject(Device, Brush);
        for i := 1 to DotToPixel(BorderWidth) do
        begin
          FrameRect(Device, R, Brush);
          InflateRect(R, -1, -1);
        end;
      finally
        DeleteObject(Brush);
      end;
    end;

    FillPadding(Device, R, Color, Padding);
  finally
    ReleaseDC(Handle, Device);
  end;
end;

procedure TUxEdit.WMSetFocus(var Message: TWMSetFocus);
begin
  inherited;
  Color := ActualColor;
end;

{ TUxStringList }

constructor TUxStringList.Create(ListBox: TUxListBox);
begin
  FListBox := ListBox;
end;

procedure TUxStringList.InsertItem(Index: Integer; const S: string; AObject: TObject);
begin
  inherited;
  FListBox.InvalidateItem(Index);
end;

{ TUxListBox6 }

procedure TUxListBox.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  HotIndex := -1;
end;

constructor TUxListBox.Create(AOwner: TComponent);
begin
  inherited;
  FBorderColor := clModernControlBorder;
  FIndent := 20;
  FHotIndex := -1;
  FItemHeight := 60;
  FItemIndex := -1;
  FItems := TUxStringList.Create(Self);
  FPressedIndex := -1;
  FSelectionColor := clModernHighlight;
  Color := clWindow;
  ParentColor := False;
  Width := 240;
  Height := 240;
end;

procedure TUxListBox.CreateParams(var Params: TCreateParams);
begin
  inherited;
  with Params do
  begin
    with WindowClass do Style := Style and not CS_HREDRAW and not CS_VREDRAW;
  end;
end;

destructor TUxListBox.Destroy;
begin
  FItems.Free;
  inherited;
end;

function TUxListBox.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean;
begin
  Result := True;
  VertScrollBar.Next;
end;

function TUxListBox.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean;
begin
  Result := True;
  VertScrollBar.Prior;
end;

procedure TUxListBox.DoScrollBarChange;
begin
  VertScrollBar.Max := DotToPixel(ItemHeight) * Items.Count;
  VertScrollBar.PageSize := ClientHeight;
end;

function TUxListBox.GetDisplayText(const Index: Integer): WideString;
begin
  Result := FItems[Index];
end;

function TUxListBox.GetTopIndex: Integer;
begin
  Result := 0;
end;

procedure TUxListBox.InvalidateItem(const Index: Integer);
begin
  if InRange(Index, 0, Pred(Items.Count)) then
  begin
    InvalidateRect(ItemRect(Index));
  end;
end;

function TUxListBox.ItemAtPos(Pos: TPoint): Integer;
var
  i: Integer;
  HitRect: TRect;
begin
  Unset(HitRect);
  Dec(HitRect.Top, VertScrollBar.Position);
  for i := TopIndex to Pred(Items.Count) do
  begin
    HitRect := Bounds(0, HitRect.Top, ClientWidth, DotToPixel(ItemHeight));
    if PtInRect(HitRect, Pos) then
    begin
      Result := i;
      Exit;
    end;
    OffsetRect(HitRect, 0, RectHeight(HitRect));
  end;
  Result := -1;
end;

function TUxListBox.ItemRect(Index: Integer): TRect;
var
  i: Integer;
begin
  Unset(Result);
  Dec(Result.Top, VertScrollBar.Position);
  for i := TopIndex to Index do
  begin
    Result := Bounds(0, Result.Top, ClientWidth, DotToPixel(ItemHeight));
    if i = Index then Exit;
    OffsetRect(Result, 0, RectHeight(Result));
  end;
end;

procedure TUxListBox.KeyDown(var Key: Word; Shift: TShiftState);
begin
  inherited;
  case Key of
    VK_DOWN: ItemIndex := ItemIndex + 1;
    VK_UP: ItemIndex := ItemIndex - 1;
  end;
end;

procedure TUxListBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if CanFocus and CanFocusParent then
  begin
    SetFocus;
  end;

  if Button = mbLeft then
  begin
    PressedIndex := ItemAtPos(Point(X, Y));
  end;
end;

procedure TUxListBox.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if not PtInScrollBar(X, Y) then
  begin
    if not FScrollBarView.Captured then
    begin
      HotIndex := ItemAtPos(Point(X, Y));
    end;

  end else HotIndex := -1;
end;

procedure TUxListBox.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  Index: Integer;
begin
  inherited;
  if not PtInScrollBar(X, Y) then
  begin
    Index := ItemAtPos(Point(X, Y));
    if Index = PressedIndex then ItemIndex := Index;
  end;
end;

procedure TUxListBox.Paint;
begin
  PaintItems;
  { Paint ScrollBar }
  inherited;
end;

procedure TUxListBox.PaintItem(const Index: Integer; ItemRect: TRect);
begin
  with Canvas do
  begin
    DrawTextRect(Canvas, ItemRect, taLeftJustify, taVerticalCenter, DisplayText[Index], wkEllipsis);
  end;
end;

procedure TUxListBox.PaintItems;
var
  i: Integer;
  FillColor: TColor;
  ContentRect, ItemRect: TRect;
  State: TNxButtonState;
begin
  ItemRect := Bounds(0, -VertScrollBar.Position, ClientWidth, DotToPixel(ItemHeight));

  with Canvas do
  begin
    Font.Assign(Self.Font);

    for i := TopIndex to Pred(Items.Count) do
    begin
      FillColor := Color;
      if i = ItemIndex then
      begin
        FillColor := SelectionColor;
      end;

      if i = FHotIndex then
      begin
        if i = ItemIndex then
        begin
          FillColor := BlendColor(FillColor, clBlack, 245);
        end
        else FillColor := clXBtnFace;
      end;

      Brush.Color := FillColor;
      FillRect(ItemRect);

      ContentRect := ItemRect;
      Inc(ContentRect.Left, DotToPixel(Indent));

      PaintItem(i, ContentRect);
      OffsetRect(ItemRect, 0, RectHeight(ItemRect));
    end;

    Brush.Color := Color;
    FillRect(Rect(0, ItemRect.Top, ClientWidth, ClientHeight));
  end;
end;

procedure TUxListBox.RefreshItem(Index: Integer; Shift: TUxShiftDirection);
begin
  InvalidateItem(Index);
end;

procedure TUxListBox.SetHotIndex(const Value: Integer);
var
  OldValue: Integer;
begin
  if Value <> FHotIndex then
  begin
    OldValue := FHotIndex;
    FHotIndex := Value;
    InvalidateItem(OldValue);
    InvalidateItem(FHotIndex);
  end;
end;

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

procedure TUxListBox.SetItemHeight(const Value: Integer);
begin
  if Value <> FItemHeight then
  begin
    FItemHeight := Value;
    Invalidate;
  end;
end;

procedure TUxListBox.SetItemIndex(const Value: Integer);
begin
  if Value <> FItemIndex then
  begin
    if InRange(Value, 0, Pred(Items.Count)) then
    begin
      FItemIndex := Value;
      Invalidate;
    end;
  end;
end;

procedure TUxListBox.SetItems(const Value: TStrings);
begin
  FItems.Assign(Value);
end;

procedure TUxListBox.SetPressedIndex(const Value: Integer);
begin
  if Value <> FPressedIndex then
  begin
    FPressedIndex := Value;
  end;
end;

procedure TUxListBox.SetSelectonColor(const Value: TColor);
begin
  if Value <> FSelectionColor then
  begin
    FSelectionColor := Value;
    Invalidate;
  end;
end;

procedure TUxListBox.SetTopIndex(const Value: Integer);
begin

end;

procedure TUxListBox.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
  inherited;
  Message.Result := Message.Result or DLGC_WANTCHARS or DLGC_WANTARROWS;
end;

procedure TUxListBox.WMNCMouseLeave(var Message: TMessage);
begin
  inherited;
  HotIndex := -1;
end;

{ TUxPanelButton6 }

constructor TUxPanelButton.Create(AOwner: TComponent);
begin
  inherited;
  ControlStyle := ControlStyle + [csAcceptsControls];
  FLabel := TLabel.Create(Self);
//  FLabel.SetSubComponent(True);
  FLabel.SetBounds(10, 10, 100, 20);
  FLabel.Caption := 'Caption';
  FLabel.Parent := Self;
//  TControlAccess(FLabel).OnMouseDown := DoChildMouseDown;
//  TControlAccess(FChild).OnMouseDown := DoChildMouseUp;
  FButton := TButton.Create(Self);
  FButton.SetBounds(10, 60, 80, 20);
  FButton.Caption := 'Button';
  FButton.Parent := Self;
end;

procedure TUxPanelButton.DoChildMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  MouseDown(Button, Shift, X, Y);
end;

procedure TUxPanelButton.WMParentNotify(var Message: TWMParentNotify);
var
  FChild: TControl;
begin
  inherited;
  if Message.Event = BN_CLICKED then
  begin
    MouseUp(mbLeft, [], Message.XPos, Message.YPos);

  end;
  if Message.Event = WM_LBUTTONDOWN then
  begin
    MouseDown(mbLeft, [], Message.XPos, Message.YPos);
//    FChild := ControlAtPos(Point(Message.XPos, Message.YPos), False, True);
//    if FChild <> nil then
//    begin
//      TControlAccess(FChild).OnMouseDown := DoChildMouseDown;
//      TControlAccess(FChild).OnMouseDown := DoChildMouseUp;
//      FPrevActiveControl := ActiveControl;
//      FOldChildOnMouseMove := TControlAccess(FChild).OnMouseMove;
//      TControlAccess(FChild).OnMouseMove := ChildMouseMove;
//      FOldChildOnMouseUp := TControlAccess(FChild).OnMouseUp;
  end;
end;

{ TUxNotificationControl6 }

procedure TUxNotificationControl.Close;
begin
  State := ncsSlidingOut;
end;

procedure TUxNotificationControl.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  CloseButtonState := CloseButtonState - [bsHover];
end;

constructor TUxNotificationControl.Create(AOwner: TComponent);
begin
  inherited;
  FAutoClose := True;
  FDelayTimer := TTimer.Create(Self);
  FDelayTimer.Enabled := False;
  FDelayTimer.Interval := 2000;
  FDelayTimer.OnTimer := DoDelayTimer;
  FIconSource := soPicture;
  FImageIndex := -1;
  FImages := nil;
  FOptions := [];
  FPicture := TPicture.Create;
  FPictureSpacing := 8;
  FPopupAnchor := paLeft;
  FSlidingTimer := TTimer.Create(Self);
  FSlidingTimer.Enabled := False;
  FSlidingTimer.Interval := 10;
  FSlidingTimer.OnTimer := DoSlidingTimer;
  FState := ncsHidden;
  FTextSpacing := 4;

  BorderColor := $00484848;
  BorderSize := 1;
  Color := clBtnFaceDark;
  Padding.SetBounds(11, 11, 11, 11);
  Font.Name := 'Segoe UI';
  Font.Size := 12;
  Height := 75;
  Width := 360;
end;

procedure TUxNotificationControl.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
  	if not (csDesigning in ComponentState) then
    begin
	    WndParent := GetDesktopWindow;
   	  Style := WS_CLIPSIBLINGS or WS_CHILD;
	 	  ExStyle := WS_EX_TOPMOST or WS_EX_TOOLWINDOW;
   	  WindowClass.Style := CS_DBLCLKS or CS_SAVEBITS and not(CS_HREDRAW or CS_VREDRAW);
	  end;
end;

destructor TUxNotificationControl.Destroy;
begin
  FPicture.Free;
  inherited;
end;

procedure TUxNotificationControl.DoDelayTimer(Sender: TObject);
begin
  FDelayTimer.Enabled := False;
  if AutoClose then State := ncsSlidingOut;
end;

procedure TUxNotificationControl.DoSlidingTimer(Sender: TObject);
var
  Delta: Integer;
begin
  case State of

    ncsSlidingIn:
    begin
      Delta := -10;
      case PopupAnchor of
        paRight:
        begin
          Left := Left + Delta;
          if Left <= Screen.Width - Width then State := ncsShowing;
        end;
        paLeft: Left := Left - Delta;
      end;
    end;

    ncsSlidingOut:
    begin

      case PopupAnchor of
        paRight:
        begin
          Delta := 10;
          Left := Left + Delta;
          if Left > Screen.Width then
          begin
            FState := ncsHidden;
          end;
        end;
        paLeft: Left := Left - Delta;
      end;

    end;

  end;
end;

function TUxNotificationControl.GetBestSize: TSize;
begin
  Canvas.Font.Assign(Font);
  Result := Size(Width, 0);
  Inc(Result.cy, Padding.Top + Padding.Bottom + BorderSize * 2);
  Inc(Result.cy, RectHeight(CaptionRect));
  if Text <> '' then
  begin
    Inc(Result.cy, TextSpacing);
    Inc(Result.cy, RectHeight(TextRect));
  end;
end;

function TUxNotificationControl.GetCaptionRect: TRect;
begin
  Result := PaddingRect;
  Result.Left := TextLeft;
  Result.Bottom := Result.Top + GetTextSize(Canvas, Caption).cy;
end;

function TUxNotificationControl.GetCloseButtonRect: TRect;
begin
  Result := ClientRect;
  Result.Left := Result.Right - CloseButtonSize.cx;
  Result.Bottom := Result.Top + CloseButtonSize.cy;
end;

function TUxNotificationControl.GetCloseButtonSize: TSize;
begin
  Result := DotToPixel(Size(30, 20));
end;

function TUxNotificationControl.GetPictureRect: TRect;
begin
  Result := PaddingRect;
  if HasIcon then
  begin
    case IconSource of
      soPicture: Result.Right := Result.Left + Picture.Graphic.Width;
      else Result.Right := Result.Left + Images.Width;
    end;
  end;
end;

function TUxNotificationControl.GetSlidingStep: Integer;
begin
  Result := 10;
  case PopupAnchor of
    paLeft: ;
    paRight: ;
  end;
end;

function TUxNotificationControl.GetTextLeft: Integer;
begin
  Result := PaddingRect.Left;
  if HasIcon then
  begin
    Result := PictureRect.Right + PictureSpacing;
  end;
end;

function TUxNotificationControl.GetTextRect: TRect;
begin
  Result := PaddingRect;
  Result.Left := TextLeft;
  Result.Top := CaptionRect.Bottom + TextSpacing;
  Result.Bottom := Result.Top + GetTextHeight(Canvas, Result.Right - Result.Left, Text, wkWordWrap);
end;

function TUxNotificationControl.HasIcon: Boolean;
begin
  case IconSource of
    soPicture: Result := Assigned(Picture.Graphic) and not Picture.Graphic.Empty;
    else Result := Assigned(Images) and InRange(ImageIndex, 0, Pred(Images.Count));
  end;
end;

procedure TUxNotificationControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if PtInRect(CloseButtonRect, Point(X, Y)) then
  begin
    CloseButtonState := CloseButtonState + [bsPressed];
  end;
end;

procedure TUxNotificationControl.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if PtInRect(CloseButtonRect, Point(X, Y)) then
  begin
    CloseButtonState := CloseButtonState + [bsHover];
  end else
  begin
    CloseButtonState := CloseButtonState - [bsHover];
  end;
end;

procedure TUxNotificationControl.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if noCloseButton in Options then
    if PtInRect(CloseButtonRect, Point(X, Y))
      and (bsPressed in FCloseButtonState) then
    begin
      Close;
    end;
end;

procedure TUxNotificationControl.Paint;
begin
  inherited;

  Canvas.Font.Assign(Font);

  Canvas.Brush.Color := Color;
  Canvas.FillRect(ClientRect);

  { Picture }
  if HasIcon then
  begin
    case IconSource of
      soPicture: Canvas.Draw(PictureRect.Left, PictureRect.Top, Picture.Graphic);
      else Images.Draw(Canvas, PictureRect.Left, PictureRect.Top, ImageIndex);
    end;
  end;

  Canvas.Font.Color := clWhite;
  DrawTextRect(Canvas, CaptionRect, taLeftJustify, taAlignTop, Caption, wkNone);

  Canvas.Font.Color := $00A5A5A5;
  DrawTextRect(Canvas, TextRect, taLeftJustify, taAlignTop, Text, wkWordWrap);

  if noCloseButton in Options then PaintCloseButton;
end;

procedure TUxNotificationControl.PaintCloseButton;

  procedure PaintCloseSign(X, Y: Integer);
  begin
    with Canvas do
    begin
      Polygon([
        Point(X, Y),
        Point(X + 8, Y + 8),
        Point(X + 9, Y + 8),
        Point(X + 1, Y + 0),
        Point(X, Y)
      ]);
      Polygon([
        Point(X, Y + 8),
        Point(X + 8, Y),
        Point(X + 9, Y),
        Point(X + 1, Y + 8),
        Point(X, Y + 8)
      ]);
    end;
  end;

begin
  with Canvas do
  begin
    with CenteredRect(CloseButtonRect, Bounds(0, 0, 10, 10)) do
    begin
      Pen.Color := clWhite;
      if bsHover in FCloseButtonState then Pen.Color := $00797979;
      if bsPressed in FCloseButtonState then Pen.Color := $00a5a5a5;
      Brush.Color := Pen.Color;
      PaintCloseSign(Left, Top);
    end;
  end;
end;

procedure TUxNotificationControl.Popup(const ACaption, AText: WideString;
  AHeight: Integer);
begin
  Caption := ACaption;
  Text := AText;
  if AHeight = 0 then
  begin
    Height := GetBestSize.cy;
  end;

  { Call overl. procedure }
  Popup;
end;

procedure TUxNotificationControl.Popup;
var
  Location: TPoint;
begin
  Location.Y := FLastTop - Height;

  case PopupAnchor of
    paLeft: Location := Point(0, Location.Y);
    paRight: Location := Point(Screen.Width, Location.Y);
  end;

  SetWindowPos(Handle, HWND_TOPMOST, Location.X, Location.Y,
    Width, Height, SWP_SHOWWINDOW);

  { Start }
  State := ncsSlidingIn;
end;

procedure TUxNotificationControl.SetButtonState(const Value: TNxButtonState);
begin
  if Value <> FCloseButtonState then
  begin
    FCloseButtonState := Value;
    InvalidateRect(CloseButtonRect);
  end;
end;

procedure TUxNotificationControl.SetCaption(const Value: WideString);
begin
  FCaption := Value;
end;

procedure TUxNotificationControl.SetPicture(const Value: TPicture);
begin
  FPicture.Assign(Value);
end;

procedure TUxNotificationControl.SetState(const Value: TUxNotificationControlState);
begin
  if Value <> FState then
  begin
    FSlidingTimer.Enabled := False;
    FState := Value;
    case FState of
      ncsShowing:
      begin
        FDelayTimer.Enabled := True;
      end;
      ncsSlidingIn:
      begin
        FSlidingTimer.Enabled := True;
      end;
      ncsSlidingOut:
      begin
        FSlidingTimer.Enabled := True;
      end;
      ncsHidden:
      begin
        Hide;
      end;
    end;
  end;
end;

{ TUxNotificationsManager6 }

constructor TUxNotificationsManager.Create(AOwner: TComponent);
begin
  inherited;
  FAutoClose := True;
  FColor := clBtnFaceDark;
  FHeight := 0;
  FImages := nil;
  FPopupAnchor := paRight;

  { Global Var. }
  FLastTop := Screen.Height;
  if GetTaskBarAlign = alBottom then Dec(FLastTop, GetTaskBarSize.cy);
end;

procedure TUxNotificationsManager.Popup(Caption, Text: WideString;
  Picture: TPicture);
var
  Control: TUxNotificationControl;
begin
  Control := TUxNotificationControl.Create(Self);
  SetControlProperties(Control);

  Control.IconSource := soPicture;
  Control.Picture := Picture;

  Control.Popup(Caption, Text, Height);

  FLastTop := Control.Top;
end;

procedure TUxNotificationsManager.Popup(Caption, Text: WideString; ImageIndex: TImageIndex);
var
  Control: TUxNotificationControl;
begin
  Control := TUxNotificationControl.Create(Self);
  SetControlProperties(Control);

  Control.IconSource := soImageList;
  Control.Images := Images;
  Control.ImageIndex := ImageIndex;

  Control.Popup(Caption, Text, Height);

  FLastTop := Control.Top;
end;

procedure TUxNotificationsManager.SetControlProperties(const Control: TUxNotificationControl);
begin
  with Control do
  begin
    AutoClose := Self.AutoClose;
    Color := Self.Color;
    PopupAnchor := Self.PopupAnchor;
    Options := Self.Options;
  end;
end;

end.
