“磁性” 窗口

{通常,我们拖动对话框窗口的标题栏来移动窗口,但有时候,我们想通过鼠标在客户区上拖动来移动窗口。</p><p>拖动对话框窗口的标题栏来移动窗口的方案是,处理鼠标消息WM_LBUTTONDOWN和WM_LBUTTONUP。在OnLButtonUp函数中计算鼠标位置的变化,调用MoveWindow实现窗口的移动。注意,拖动标题栏移动窗口的时候,会出现一个矩形框,它提示了窗口移动的当前位置。当鼠标左键放开的时候,窗口就移动到矩形框所在位置}
unit frm_main;

interface

uses
  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.


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值