“磁性” 窗口

来源:互联网 发布:gfxbench数据库网址 编辑:程序博客网 时间:2024/04/29 19:02
{通常,我们拖动对话框窗口的标题栏来移动窗口,但有时候,我们想通过鼠标在客户区上拖动来移动窗口。</p><p>拖动对话框窗口的标题栏来移动窗口的方案是,处理鼠标消息WM_LBUTTONDOWN和WM_LBUTTONUP。在OnLButtonUp函数中计算鼠标位置的变化,调用MoveWindow实现窗口的移动。注意,拖动标题栏移动窗口的时候,会出现一个矩形框,它提示了窗口移动的当前位置。当鼠标左键放开的时候,窗口就移动到矩形框所在位置}
unit frm_main;interfaceuses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  Dialogs;type  TForm1 = class(TForm)    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;      Shift: TShiftState; X, Y: Integer);    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);  private    procedure Magnetize(var nl, nt: Integer);    { Private declarations }  public    { Public declarations }  end;  { “ 磁性 ” 窗口    Winamp的用户都知道 , Winamp的播放列表或均衡器在被移动的时候 , 仿佛会受到一股磁力 ,每当靠近主窗口时就一下子被 “ 吸附 ” 过去 ,自动沿边对齐 。我想让我的Winamp插件也具备这种奇妙特性 , 于是琢磨出了一种 “ 磁化 ” 窗口的方法 。该法适用于Delphi的各个版本 。为了演示这种技术 , 请随我来制作一个会被Winamp “ 吸引 ” 的样板程序 。        先新建一应用程序项目 , 把主窗口Form1适当改小些 ,并将BorderStyle设为bsNone 。放一个按钮元件 , 双击它并在OnClick事件中写 “ Close;” 。待会儿就按它来结束程序 。 现在切换到代码编辑区 , 定义几个全局变量 。 }var  Form1: TForm1; // “磁性”窗口  LastX, LastY: Integer; // 记录前一次的坐标  WinampRect: Trect; // 保存Winamp窗口的矩形区域  hwnd_Winamp: HWND; // Winamp窗口的控制句柄implementation{$R *.dfm}procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;  Shift: TShiftState; X, Y: Integer);const  ClassName = 'TWinamp'; // Winamp主窗口的类名  // 如果改成ClassName=‘TAppBuilder’,你就会发现连Delphi也有引力啦!begin  // 记录当前坐标  LastX := X;  LastY := Y;  // 查找Winamp  hwnd_Winamp := FindWindow(ClassName, 'Winamp v1.X');  if hwnd_Winamp > 0 then // 找到的话,记录其窗口区域    GetWindowRect(hwnd_Winamp, WinampRect);end;procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState;  X, Y: Integer);var  nLeft, nTop: Integer; // 记录新位置的临时变量begin  // 检查鼠标左键是否按下  if HiWord(GetAsyncKeyState(VK_LBUTTON)) > 0 then  begin    // 计算新坐标    nLeft := Left + (X - LastX); // 括号里面的是移动的..    nTop := Top + (Y - LastY);    // 如果找到Winamp,就修正以上坐标,产生“磁化”效果    if hwnd_Winamp > 0 then      Magnetize(nLeft, nTop);    // 重设窗口位置    SetBounds(nLeft, nTop, width, height);  end;end;{别急着,看Magnetize()过程,先来了解一下修正坐标的原理。根据对Winamp实现效果的观察,我斗胆给所谓“磁化”下一个简单的定义,就是“在原窗口与目标窗口接近到某种预定程度,通过修正原窗口的坐标,使两窗口处于同一平面且具有公共边的过程”。依此定义,我设计了以下的“磁化”步骤。第一步,判断目标窗口(即Winamp)和我们的Form1在水平及垂直方向上的投影线是否重叠。“某方向投影线有重叠”是“需要进行坐标修正”的必要非充分条件。判断依据是两投影线段最右与最左边界的差减去它们宽度和的值的正负。第二步,判断两窗口对应边界是否靠得足够近了。肯定的话就让它们合拢。好了,下面便是“神秘”的Magnetize过程了……}
procedure TForm1.Magnetize(var nl, nt: Integer);// 内嵌两个比大小的函数  function Min(a, b: Integer): Integer;  begin    if a > b then      result := b    else      result := a;  end;  function Max(a, b: Integer): Integer;  begin    if a > b then      result := a    else      result := b;  end;var  H_Overlapped, V_Overlapped: boolean; // 记录投影线是否重叠  tw, ww, wh: Integer; // 临时变量const  MagneticForce: Integer = 50; // “磁力”的大小。  // 准确的说,就是控制窗口边缘至多相距多少像素时需要修正坐标  // 为了演示,这里用一个比较夸张的数字――50。  // 一般可以用20左右,那样比较接近Winamp的效果begin  OutputDebugString('Magnetize in');  // 判断水平方向是否有重叠投影  ww := WinampRect.Right - WinampRect.Left;  tw := Max(WinampRect.Right, nl + width) - Min(WinampRect.Left, nl);  H_Overlapped := tw <= (width + ww);  // 再判断垂直方向  wh := WinampRect.Bottom - WinampRect.Top;  tw := Max(WinampRect.Bottom, nt + height) - Min(WinampRect.Top, nt);  V_Overlapped := tw <= (height + wh);  // 足够接近的话就调整坐标  if H_Overlapped then  begin    if Abs(WinampRect.Bottom - nt) < MagneticForce then    begin      nt := WinampRect.Top;      nl := WinampRect.Right;    end    else if Abs(nt + height - WinampRect.Top) < MagneticForce then    begin      nl := WinampRect.Right;      nt := WinampRect.Top;    end;  end;  if V_Overlapped then  begin    if Abs(WinampRect.Right - nl) < MagneticForce then    begin      nt := WinampRect.Top;      nl := WinampRect.Right;    end    else if Abs(nl + width - WinampRect.Left) < MagneticForce then    begin      nt := WinampRect.Top;      nl := WinampRect.Right;    end;  end;end;end.


 

原创粉丝点击