home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue27 / controls / Dragunit.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1997-09-17  |  7.3 KB  |  251 lines

  1. unit Dragunit;
  2.  
  3. { Main unit of sample application to demonstrate the use of the functions
  4.   FindTopMostWinControlAtPos and GetTopMostControlAtScreenCoords from the
  5.   accompanying "Controls Demystified" article.
  6.  
  7.   This application is a simple game. The user should try to figure out the
  8.   rules for him/herself but the aim is to order the button captions by
  9.   holding down the mouse button and dragging them from one button to another.
  10.   You can drop a caption on a disabled button, but you can't drag from one.
  11.   To make things a bit harder the source button's Enabled property will
  12.   always take the inverse of the Enabled property of the target button.
  13.  
  14.   Please note that Delphi comes complete with a simple event driven drag
  15.   and drop mechanism which is generally preferable to the method used here.
  16.   However, this method does give you more control and also allows you to
  17.   have disabled controls as drop targets.
  18.  
  19.   Author: Glenn Lawrence
  20.           Principal Consultant
  21.           AIMTec Pty Ltd  "Advanced Interactive Media Technologies"
  22.           www.aimtec.com.au
  23.  
  24.   Copyright (c) 1997 AIMTec Pty Ltd.
  25. }
  26.  
  27. interface
  28.  
  29. uses
  30.   Wintypes, WinProcs, Messages, SysUtils, Classes, Graphics, Controls,
  31.   Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, MMsystem;
  32.  
  33. type
  34.   TForm1 = class(TForm)
  35.     Panel1: TPanel;
  36.     Button1: TButton;
  37.     Button2: TButton;
  38.     Button3: TButton;
  39.     Button4: TButton;
  40.     Button5: TButton;
  41.     Button6: TButton;
  42.     Button7: TButton;
  43.     Button8: TButton;
  44.     Button9: TButton;
  45.     Button10: TButton;
  46.     Button11: TButton;
  47.     Button12: TButton;
  48.     PnlDisplay: TPanel;
  49.     PnlControls: TPanel;
  50.     BtnStart: TSpeedButton;
  51.     LblTimer: TLabel;
  52.     Timer1: TTimer;
  53.     procedure ButtonMouseDown(Sender: TObject; Button: TMouseButton;
  54.       Shift: TShiftState; X, Y: Integer);
  55.     procedure ButtonMouseUp(Sender: TObject; Button: TMouseButton;
  56.       Shift: TShiftState; X, Y: Integer);
  57.     procedure BtnStartClick(Sender: TObject);
  58.     procedure FormCreate(Sender: TObject);
  59.     procedure Timer1Timer(Sender: TObject);
  60.   private
  61.     { Private declarations }
  62.     MoveCount : Integer;
  63.     StartTime : Longint;
  64.     procedure CheckCompleted;
  65.   public
  66.     { Public declarations }
  67.   end;
  68.  
  69. var
  70.   Form1: TForm1;
  71.  
  72. implementation
  73.  
  74. {$R *.DFM}
  75.  
  76. { FindTopMostWinControlAtPos
  77.  
  78.   This function is described in the accompanying magazine article.
  79.  
  80.   It returns the top most windowed control at the given screen location.
  81. }
  82. function FindTopMostWinControlAtPos
  83. (
  84.   parent: TWinControl;     { The parent at the top of the control tree }
  85.   pt: TPoint               { The "hit" test point in screen coordinates }
  86. ): TWinControl;
  87. var i: integer; c: TControl;
  88. begin
  89.   Result := nil;
  90.  
  91.   if parent.Visible then
  92.   begin
  93.     i := parent.ControlCount -1;
  94.     while (i >= 0) and (Result = nil) do  { Check children first }
  95.     begin
  96.       c := parent.Controls[i];
  97.       if c is TWinControl then  { Recursively descend }
  98.         Result := FindTopMostWinControlAtPos(c as TWinControl, pt);
  99.       i := i -1;
  100.     end;
  101.  
  102.     if Result = nil then  { Check parent control last }
  103.     begin
  104.       pt := parent.ScreenToClient(pt);  { Convert point to local coords }
  105.       if
  106.         (pt.X >= 0) and (pt.X < parent.Width)
  107.         and
  108.         (pt.Y >= 0) and (pt.Y < parent.Height)
  109.       then
  110.         Result := parent; { Found it! }
  111.     end;
  112.   end;
  113. end;
  114.  
  115. { GetTopMostControlAtScreenCoords
  116.  
  117.   This function is described in the accompanying magazine article.
  118.  
  119.   It returns the top most control at the given screen location,
  120.   optionally allowing or ignoring disabled controls.
  121. }
  122. function GetTopMostControlAtScreenCoords
  123. (
  124.   pt: TPoint;              { Screen coords for "hit" test }
  125.   allow_disabled : boolean { Indicates if disabled controls are included }
  126. ): TControl;
  127. var wc : TWinControl;
  128. begin
  129.   Result := FindDragTarget(pt, allow_disabled);
  130.  
  131.   if (Result <> nil) and (Result is TWinControl) and allow_disabled then
  132.   begin
  133.     { Check for disabled child windowed controls }
  134.     wc := FindTopMostWinControlAtPos((Result as TWinControl), pt);
  135.     if wc = nil then abort; { Can't happen - honest guv! }
  136.     pt := wc.ScreenToClient(pt);   { Convert to local coords }
  137.     Result := wc.ControlAtPos(pt, True);
  138.     if Result = nil then
  139.       Result := wc;
  140.   end;
  141. end;
  142.  
  143. { All following code belongs to the example application }
  144.  
  145. procedure SwapButtonCaptions(btn1, btn2: TButton);
  146. var text: String;
  147. begin
  148.   text := btn1.Caption;
  149.   btn1.Caption := btn2.Caption;
  150.   btn2.Caption := text;
  151. end;
  152.  
  153. procedure TForm1.CheckCompleted;
  154. var i: integer; ok : Boolean;
  155. begin
  156.   { NB: This function relies on the there being *only* buttons on
  157.     Panel1 and also that their order in the DFM file reflects the
  158.     order they appear on the screen. If you were to move buttons around
  159.     then this wouldn't necessarily be the case, so I don't recommend
  160.     this as a standard technique. It does however demonstrate a use
  161.     of the Controls array property described in the magazine article. }
  162.  
  163.   ok := True;
  164.   for i := 0 to Panel1.ControlCount -1 do
  165.     ok := ok and ((Panel1.Controls[i] as TButton).Caption = IntToStr(i + 1));
  166.  
  167.   if ok then
  168.   begin
  169.     Timer1.Enabled := False;
  170.     Panel1.Enabled := False;
  171.     MessageBeep(MB_ICONEXCLAMATION);
  172.     PnlDisplay.Caption := 'You did it in ' + IntToStr(MoveCount) + ' moves !';
  173.   end;
  174. end;
  175.  
  176. { Event handlers follow ... }
  177.  
  178. procedure TForm1.FormCreate(Sender: TObject);
  179. begin
  180.   Randomize;
  181. end;
  182.  
  183. procedure TForm1.BtnStartClick(Sender: TObject);
  184. var i: integer;
  185. begin
  186.   MoveCount := 0;
  187.   BtnStart.Caption := 'Restart !';
  188.   Timer1.Enabled := True;
  189.   Panel1.Enabled := True;
  190.   PnlDisplay.Caption := 'Drag the buttons into order';
  191.  
  192.   for i := 0 to Panel1.ControlCount -1 do
  193.     Panel1.Controls[i].Enabled := True;
  194.  
  195.   for i := 0 to 500 do  { Mix 'em up! }
  196.   begin
  197.     SwapButtonCaptions
  198.     (
  199.        Panel1.Controls[Random(Panel1.ControlCount)] as TButton,
  200.        Panel1.Controls[Random(Panel1.ControlCount)] as TButton
  201.     );
  202.   end;
  203.  
  204.   StartTime := TimeGetTime;
  205. end;
  206.  
  207. procedure TForm1.ButtonMouseDown(Sender: TObject; Button: TMouseButton;
  208.   Shift: TShiftState; X, Y: Integer);
  209. begin
  210.   Screen.Cursor := crDrag;
  211.   SetCaptureControl(Sender as TControl);
  212. end;
  213.  
  214. procedure TForm1.ButtonMouseUp(Sender: TObject; Button: TMouseButton;
  215.   Shift: TShiftState; X, Y: Integer);
  216. var
  217.   drop_point: TPoint;
  218.   drop_target: TControl;
  219.   source_button, target_button : TButton;
  220. begin
  221.   Screen.Cursor := crDefault;
  222.   SetCaptureControl(nil);
  223.   drop_point.X := X;
  224.   drop_point.Y := Y;
  225.   drop_point := (Sender as TControl).ClientToScreen(drop_point);
  226.   drop_target := GetTopMostControlAtScreenCoords(drop_point, True);
  227.  
  228.   if drop_target <> Sender then
  229.   begin
  230.     if (drop_target = nil) or not (drop_target is TButton) then
  231.       MessageBeep(MB_ICONHAND)  { Not a valid drop target! }
  232.     else
  233.     begin
  234.       MoveCount := MoveCount +1;
  235.       PnlDisplay.Caption := 'Move ' + IntToStr(MoveCount);
  236.       source_button := Sender as TButton;
  237.       target_button := drop_target as TButton;
  238.       SwapButtonCaptions(source_button, target_button);
  239.       source_button.Enabled := not target_button.Enabled;
  240.       CheckCompleted;
  241.     end;
  242.   end;
  243. end;
  244.  
  245. procedure TForm1.Timer1Timer(Sender: TObject);
  246. begin
  247.   LblTimer.Caption := Format('%1.1f', [(TimeGetTime - StartTime) / 1000]);
  248. end;
  249.  
  250. end.
  251.