unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls,
  Graphics, ImgList,  pngimage,
  NxEdit,                        {where is TNxRadioButton6?}
  NxControls6, NxCollection6, NxEdit6;

const
   //tile properties
   TILE_PADDING = 5;
   TILE_MARGINS = 5;
   TILE_HEIGHT  = 80;
   TILE_WIDTH   = 120;
   TILE_WIDE    = (TILE_WIDTH + TILE_PADDING)*2;
   //other
   MAX_TILES    = 10;  //1-based
   DRAG_THRESHOLD = 10;
   DEFAULT_POS: TPoint = (X: -1; Y: -1);

   TileColours: array[0..9] of Integer = (Ord(clAqua),        //clAqua
                                          Ord(clOlive),       //clOlive
                                          $006BB7BD,          //clWebDarkKhaki
                                          $00B3DEF5,          //clWebWheat
                                          $00A09E5F,          //clWebCadetBlue
                                          $00E6D8AD,          //clWebLightBlue
                                          $FAE6E6,            //clWebLavender
                                          $00008CFF,          //clWebDarkOrange
                                          $0087B8DE,          //clWebBurlywood
                                          $ED9564  );         //clWebCornFlowerBlue

   //Green colour scheme
   PANEL_COLOUR = $00929212;    //lighter than clTeal
   BACKG_COLOUR = clTeal;
   HEADER_COLOUR = $00707000;   //darker than clTeal

type
   TMouseActions  = (maClicking, maDragging, maNeither);
   TIconOptions   = (ioNone, ioSmall, ioLarge);
   TIconPositions = (ipLeft, ipCentre, ipRight);

type
  TForm1 = class(TForm)
    TopPanel: TPanel;
    boxLayoutDirection: TGroupBox;
    radioLeftRight: TNxRadioButton;
    radioUpDown: TNxRadioButton;
    cbAllowDragDrop: TNxCheckBox6;
    boxCaptionPosition: TGroupBox;
    radioTopLeft: TNxRadioButton;
    radioBottomRight: TNxRadioButton;
    LTransfer: TLabel;
    LTileCount: TLabel;
    LMouse: TLabel;
    LTileClick: TLabel;
    boxTileIcons: TGroupBox;
    NxTrackBar61: TNxTrackBar6;
    LSliderPosn: TLabel;
    HeaderPanel: TNxPanel6;
    LSectionA: TLabel;
    LSectionB: TLabel;
    CentrePanel: TNxPanel6;
    btnAddTileA: TNxTile6;
    btnAddTileB: TNxTile6;
    FlowPanel1: TFlowPanel;
    NxTile60: TNxTile6;
    NxTile61: TNxTile6;
    NxTile62: TNxTile6;
    FlowPanel2: TFlowPanel;
    boxIconPosition: TGroupBox;
    NxTrackBar62: TNxTrackBar6;
    boxFlowpanels: TGroupBox;
    BottomPanel: TPanel;
    ArrowImage: TImage;
    TileImages: TImageList;
    SliderImages: TImageList;
    AlignmentImages: TImageList;
    TileBigImages: TImageList;
    procedure FormCreate(Sender: TObject);
    procedure radiosLayoutChange(Sender: TObject);
    procedure radioCaptionPositionChange(Sender: TObject);
    procedure btnAddTileClick(Sender: TObject);
    procedure NxTileMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
      Y: Integer);
    procedure NxTileMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure NxTileMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
      Y: Integer);
    procedure NxTileStartDrag(Sender: TObject; var DragObject: TDragObject);
    procedure NxTrackBar61Change(Sender: TObject);
    procedure NxTrackBar62Change(Sender: TObject);
    procedure FlowPanelDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState;
      var Accept: Boolean);
    procedure FlowPanelDragDrop(Sender, Source: TObject; X, Y: Integer);
  private
    fAction: TMouseActions;  //fDragging: Boolean;
    fLeftButtonDown: Boolean;
    fLeftDownPos: TPoint;
    fDragParent: String;
    procedure SetTileIcons(Option: TIconOptions);
    procedure SetSliderLabelCaption(const Index: Integer);
    procedure SetIconPosn(NewPosn: TIconPositions; InPanel: TFlowPanel);
    procedure SetIconPosnInTile(ATile: TNxTile6; NewPosn: TIconPositions);
    procedure CreateNewTile(const NewIndex: Integer; InPanel: TFlowPanel);
    function TileCount: Integer;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


{--------------------------------Set up Form-----------------------------------------------}

procedure TForm1.FormCreate(Sender: TObject);
begin
   //colour scheme
   HeaderPanel.Color := HEADER_COLOUR;
   FlowPanel1.Color  := PANEL_COLOUR;
   FlowPanel2.Color  := PANEL_COLOUR;
   CentrePanel.Color := BACKG_COLOUR;
   BottomPanel.Color := BACKG_COLOUR;
   //drag and drop
   fAction         := maNeither;   //not Clicking nor Dragging           //fDragging       := False;
   fLeftButtonDown := False;
   fLeftDownPos    := DEFAULT_POS;
   //UI
   SetSliderLabelCaption(Trunc(NxTrackBar61.Position));
   case Trunc(NxTrackBar61.Position) of
   1:   SetTileIcons(ioNone);
   2:   SetTileIcons(ioSmall);
   3:   SetTileIcons(ioLarge);
   end;
end;



{------------------------------Controls in Header Panel -------------------------------------}

procedure TForm1.radiosLayoutChange(Sender: TObject);
begin
   if radioLeftRight.Checked then
   begin
      FlowPanel1.FlowStyle := fsLeftRightTopBottom;
      FlowPanel2.FlowStyle := fsLeftRightTopBottom;
   end else
   begin
      FlowPanel1.FlowStyle := fsTopBottomLeftRight;
      FlowPanel2.FlowStyle := fsTopBottomLeftRight;
   end;
end;


procedure TForm1.radioCaptionPositionChange(Sender: TObject);
var
   i : integer;
begin
   for i:= 0 to FlowPanel1.ControlCount -1 do
      with TNxTile6(FlowPanel1.Controls[i]) do
      begin
         if radioTopLeft.Checked     then CaptionLocation.Anchors := [akLeft, akTop];
         if radioBottomRight.Checked then CaptionLocation.Anchors := [akRight, akBottom];
      end;
   for i:= 0 to FlowPanel2.ControlCount -1 do
      with TNxTile6(FlowPanel2.Controls[i]) do
      begin
         if radioTopLeft.Checked     then CaptionLocation.Anchors := [akLeft, akTop];
         if radioBottomRight.Checked then CaptionLocation.Anchors := [akRight, akBottom];
      end;
end;


procedure TForm1.NxTrackBar61Change(Sender: TObject);
begin
   SetSliderLabelCaption(Trunc(TNxTrackBar6(Sender).Position));
   case Trunc(TNxTrackBar6(Sender).Position) of
   1:   SetTileIcons(ioNone);
   2:   SetTileIcons(ioSmall);
   3:   SetTileIcons(ioLarge);
   end;
end;

procedure TForm1.SetSliderLabelCaption(const Index: Integer);
begin
   case Index of     //is 1-based
   1:   LSliderPosn.Caption := 'No icons';
   2:   LSliderPosn.Caption := 'Small icons';
   3:   LSliderPosn.Caption := 'Large icons';
   end;
end;


procedure TForm1.NxTrackBar62Change(Sender: TObject);
begin
   case Trunc(TNxTrackBar6(Sender).Position) of
   1:   begin SetIconPosn(ipLeft,   FlowPanel1);
              SetIconPosn(ipLeft,   FlowPanel2); end;
   2:   begin SetIconPosn(ipCentre, FlowPanel1);
              SetIconPosn(ipCentre, FlowPanel2); end;
   3:   begin SetIconPosn(ipRight,  FlowPanel1);
              SetIconPosn(ipRight,  FlowPanel2); end;
   end;
end;


procedure TForm1.SetIconPosn(NewPosn: TIconPositions; InPanel: TFlowPanel);
var
   i : integer;
begin
   for i:= 0 to InPanel.ControlCount -1 do
       SetIconPosnInTile(TNxTile6(InPanel.Controls[i]), NewPosn);
end;


procedure TForm1.SetIconPosnInTile(ATile: TNxTile6; NewPosn: TIconPositions);
var
   IconSize: Integer;
begin
   case NewPosn of
      ipLeft:   begin
                ATile.PictureLocation.Anchors := [akLeft, akTop];
                ATile.PictureLocation.X := 0;
                ATile.PictureLocation.Y := 0;
                end;
      ipCentre: begin
                ATile.PictureLocation.Anchors := [akLeft, akTop];
                IconSize := ATile.Picture.Graphic.Height;   //ie 32 or 64
                ATile.PictureLocation.X := (ATile.Width div 2)  - (IconSize div 2) - TILE_PADDING;
                ATile.PictureLocation.Y := (ATile.Height div 2) - (IconSize div 2) - TILE_PADDING;
                end;
      ipRight:  begin
                ATile.PictureLocation.Anchors := [akRight, akBottom];
                ATile.PictureLocation.X := 0;
                ATile.PictureLocation.Y := 0;
                end;
   end;
end;



function ImageListGetGraphic(ImageList: TCustomImageList; ImageIndex: Integer): TGraphic;
var
//    ico: HICON;
//  png: TPngImage;
   icon: TIcon;
//  bmp: TBitmap;
begin
{   //Doesn't work, loses alpha channel
    icon := TIcon.Create;
    ImageList.GetIcon(ImageIndex, icon, dsTransparent, itImage);
    Result := icon;
    }

{   //Doesn't work, loses alpha channel
    bmp := TBitmap.Create;
    bmp.PixelFormat := pf32bit;
    Imagelist.GetBitmap(ImageIndex, bmp);
    Result := bmp;
    }

{   //Fails: cannot assign a TIcon to a TPngImage
    icon := TIcon.Create;
    ImageList.GetIcon(ImageIndex, icon, ImgList.dsNormal, itImage);
    png := TPngImage.Create;
    png.Assign(icon);
    Result := png;
    icon.Free;
    }

   //Working failure; doesn't support stretched drawing (e.g. TImage.Stretch = true)
    icon := TIcon.Create;
    ImageList.GetIcon(ImageIndex, icon, ImgList.dsNormal, itImage);
    Result := icon;
end;



procedure TForm1.SetTileIcons(Option: TIconOptions);
var
   i : integer;
   g: TGraphic;
   iList: TImageList;
begin
   iList := nil;   //to keep compiler happy
   for i:= 0 to FlowPanel1.ControlCount -1 do
      with TNxTile6(FlowPanel1.Controls[i]) do
      begin
         Picture.Graphic := nil;  //clear previous image
         case Option of
            ioNone:    {do nothing};
            ioSmall:   iList :=  TileImages;
            ioLarge:   iList :=  TileBigImages;
         end;
         if Option <> ioNone then
         begin
            //L.GetBitmap(Tag, Picture.Bitmap);  --doesn't recognize Transparency
            g := ImageListGetGraphic(iList, Tag);
            //assignment of TGraphic does a copy
            Picture.Graphic := g;
            g.Free;
         end;
      end;
end;



{----------------------------------TNxTile6 events-------------------------------------------}


procedure TForm1.NxTileMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  if (fAction = maDragging) then LMouse.Caption := 'MouseMove';
  //Have we starting dragging? (Only used until MouseUp or drag activiated)
  if FLeftButtonDown and (fAction <> maDragging) then
    if (Abs(X - fLeftDownPos.X) > DRAG_THRESHOLD) or
       (Abs(Y - fLeftDownPos.Y) > DRAG_THRESHOLD) then
    begin                          //Mouse movement exceeds drag threshold
       if cbAllowDragDrop.Checked then
       begin
          fAction := maDragging;
         (Sender as TNxTile6).BeginDrag(True);    //Immediately start drag
       end
       else fAction := maNeither;
    end;
end;


procedure TForm1.NxTileMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
   LMouse.Caption := 'MouseDown';
   //NOTE: the BeginDrag method fires the OnMouseUp event immediately; this means that the OnClick
   // event fires immediately instead of its normal behaviour of waiting for the mouse to come up.
   {cannot use (Sender as TNxTile6).BeginDrag(False, 10); }
   if (Button = mbLeft) then
  begin
    fAction := maClicking;
    fLeftButtonDown := True;             //Left button is down for click
    fLeftDownPos    := Point(X, Y);      //Store position at MouseDown
  end;
end;

procedure TForm1.NxTileMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
   LMouse.Caption := 'MouseUp';
   if Button = mbLeft then
   begin
      FLeftButtonDown := False;           //reset
      if  (fAction = maClicking) then
      begin
         { pseudo-click event code goes here }
         LTileClick.Caption := (Sender as TNxTile6).Name + '  clicked';
      end;
      LTransfer.Caption := '';
   end;
end;

procedure TForm1.NxTileStartDrag(Sender: TObject; var DragObject: TDragObject);
begin
   LMouse.Caption := 'StartDrag';
   fDragParent := (Sender as TNxTile6).Parent.Name;
   LTransfer.Caption := fDragParent;
end;



{-------------------------------Flowpanel events-------------------------------------------}


procedure TForm1.FlowPanelDragDrop(Sender, Source: TObject; X, Y: Integer);
var
   TileName: String;
   TileIdx:  Integer;
begin
   if Source is TNxTile6 then
   begin
      fAction         := maNeither;         //end of dragging
      fLeftButtonDown := False;             //left button no longer down
      fLeftDownPos    := DEFAULT_POS;       //reset stored MouseDown position
      LMouse.Caption  := 'DragDrop';
      //which tile was being dragged, and what is it's index in the tile set?
      TileName := TNxTile6(Source).Name;
      TileIdx  := TNxTile6(Source).Tag;
      //delete it
      TNxTile6(Source).Free;
      //recreate it in this flowpanel
      CreateNewtile(TileIdx, TFlowPanel(Sender));
   end;
end;

procedure TForm1.FlowPanelDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState;
  var Accept: Boolean);
  {docwiki: The Source is the object being dragged, the Sender is the potential drop site,
            and X and Y are screen coordinates in pixels}
begin
   Accept := cbAllowDragDrop.Checked and (Source is TNxTile6);
   case State of
      dsDragEnter: LTransfer.Caption := fDragParent + ' -> '+ TFlowPanel(Sender).Name;
      dsDragLeave: ;
      dsDragMove: ;
   end;
end;


{-------------------------------Other control events-------------------------------------------}


procedure TForm1.btnAddTileClick(Sender: TObject);
var
   CurrentIndex: Integer;
   InPanel: TFlowPanel;
begin
   InPanel := FlowPanel1;  //to keep compiler happy
   if TNxTile6(Sender).Name = 'btnAddTileA' then InPanel := FlowPanel1;
   if TNxTile6(Sender).Name = 'btnAddTileB' then InPanel := FlowPanel2;
   CurrentIndex := TileCount -1;
   if CurrentIndex = MAX_TILES -1 then exit;    //max for this demo
   CreateNewtile(CurrentIndex +1, InPanel);
   LTileCount.Caption := Format('Tile Count:  %d / %d',[TileCount, MAX_TILES]);
end;


function TForm1.TileCount: Integer;
begin
   Result := FlowPanel1.ControlCount + FlowPanel2.ControlCount;
end;


procedure TForm1.CreateNewTile(const NewIndex: Integer; InPanel: TFlowPanel);
{NewIndex is 0-based; InPanel is either FlowPanel1 or FlowPanel2}

  procedure AddIcon(ATile: TNxTile6; TileIdx: Integer);
  var iList: TImageList; g: TGraphic;
  begin
     iList := nil;  //to keep compiler happy
     case Trunc(NxTrackBar61.Position) of
      1:   exit;    {do nothing}
      2:   iList := TileImages;
      3:   iList := TileBigImages;
     end;
      g := ImageListGetGraphic(iList, TileIdx);
      //assignment of TGraphic does a copy
      ATile.Picture.Graphic := g;
  end;

var
   NewTile: TNxTile6;
begin
   NewTile := TNxTile6.Create(Form1);
   NewTile.Name   := 'NxTile6'+IntToStr(NewIndex);
   NewTile.Parent := InPanel;         //eg FlowPanel1;
   //dimensions
   NewTile.Height := TILE_HEIGHT;
   NewTile.Width  := TILE_WIDTH;
   NewTile.Padding.Top    := TILE_PADDING;
   NewTile.Padding.Left   := TILE_PADDING;
   NewTile.Padding.Right  := TILE_PADDING;
   NewTile.Padding.Bottom := TILE_PADDING;
   NewTile.Margins.Top    := TILE_MARGINS;
   NewTile.Margins.Left   := TILE_MARGINS;
   NewTile.Margins.Right  := TILE_MARGINS;
   NewTile.Margins.Bottom := TILE_MARGINS;
   //other properties
   NewTile.Caption          := 'NxTile6-'+IntToStr(NewIndex);
   if radioBottomRight.Checked  //default is [akLeft, akTop]
   then NewTile.CaptionLocation.Anchors := [akRight, akBottom];
   AddIcon(NewTile, NewIndex);
   case Trunc(NxTrackBar62.Position) of
   1:   SetIconPosnInTile(NewTile, ipLeft  );
   2:   SetIconPosnInTile(NewTile, ipCentre);
   3:   SetIconPosnInTile(NewTile, ipRight );
   end;
   NewTile.AlignWithMargins := True;
   NewTile.BorderSize := 1;
   NewTile.Color := TileColours[NewIndex];
   NewTile.Tag   := NewIndex;
   //events
   NewTile.OnMouseDown := NxTileMouseDown;
   NewTile.OnMouseMove := NxTileMouseMove;
   NewTile.OnStartDrag := NxTileStartDrag;
   NewTile.OnMouseUp   := NxTileMouseUp;
end;




end.
