Delphi 中的 RectTracker - 原創


本文算是副產品,正品是利用 FFmpeg 從任意視頻中生成GIF片段的小程序,寫完了就發。

V2G 正品已出爐,雖然不大像樣,但好歹是能用,請見:用 Delphi 7 實現基於 FFMS2 的視頻轉 GIF 工具

因為要對視頻畫面進行框選,再生成 GIF,所以得有個框選的控件,可 Delphi 里沒有啊,只好自己寫一個了。

聲明

本文參考的是盒子網的 RectTracker,原作者署名 xwwaw,發布於2007年5月28日。主要的修改之處是增加了邊框檢測,因為我覺得讓選框超出父控件是不合邏輯的。
最開始參考的是 Anthony Scott 的 TStretchHandle,可是怎么改都不好用,遂放棄。以下是 TStretchHandle 的網站介紹截圖:

是的,你沒看錯!TStretchHandle v.2.0 在 Windows 3.1 和 Windows 95 下測試通過!看到這2個詞,我瞬間石化。頓時想起了畢業前去光盤市場淘了張 Windows 95 的預覽版,想着去了工作單位也許能用的上。結果上了班才發現,干活的是 Sco Unix,辦公還都是 Windows 3.2,而且品牌機全都自帶操作系統。

什么是 RectTracker

直譯是“橡皮筋”,竊以為不好理解,還是稱其為框選控件,說白了就是在屏幕上畫個虛線框供選中區域,8 個方向都有可以拉伸的控制柄,類似 QQ 的屏幕截圖功能。在 MFC 里有個CRectTracker可用,可參考 CRectTracker源碼學習筆記
在微軟官方的文檔 GetHandleMask里,8 個控制柄是有編號的:

當然我們就不必這么講究了。

主要思路

  • 覆蓋Paint方法:畫框,包括畫 8 個控制柄(小黑塊)
  • 響應WMMouseMove消息:修改光標形狀,邊界檢測(不論移動還是拉伸都不超出父控件),最小尺寸檢測
  • 響應WMLButtonDown消息:開始拖動
  • 響應WMLButtonUp消息:停止拖動

常量

const
  DefaultSize=65;            //默認的控件大小
  DefaultHandleSize=5;  //默認的控制柄大小  
  DefaultBorderWidth=1;//默認的邊框寬度(暫時沒用,因為超過 1 就畫不出虛線框)

主要成員變量

TDXRectTracker = class(TGraphicControl)
private
  FDragging: boolean;      //是否處於拖動狀態(鼠標左鍵保持按下)
  FHandleSize: integer;    //控制柄大小
  FBorderWidth: integer;  //邊框寬度(暫時沒用)
  FMinSize: integer;         //控件最小尺寸
  FTrackerType: TMousePosType;  //當前控件拖動類型
  FX,FY: integer;             //當前光標位置(相對於本控件,在拖動狀態下可能是負值)

Paint 方法

一圖解千惑:

繪制8個控制柄和虛線框還是簡單的。但是有一點,如果Pen.Width>1,是無法繪制出虛線的,不知哪位高人能解。

WMLButtonDown 消息處理

在收到鼠標左鍵按下的消息時,表示要啟動拖動狀態,為后續的WMMouseMove消息處理做准備。

  FDragging:= true;     //啟動拖動狀態
  Fx:= Message.XPos;  //記錄光標當前橫位置
  Fy:= Message.YPos;  //記錄光標當前縱位置
  FTrackerType:= GetMousePos(Fx, Fy);  //根據光標位置設置鼠標光標類型
  inherited;

本控件全部區域都是可拖動范圍,所以鼠標左鍵按下即表示要開始拖動。如果鼠標位於控制柄上,表示要拉伸邊框;如果鼠標位於控件內部,表示要移動整個控件;如果鼠標位於控件之外,則不會接收到鼠標左鍵按下事件。

WMLButtonUp 消息處理

在收到鼠標左鍵抬起的消息時,表示拖動狀態結束,做狀態清理:

  FDragging:= false;
  Fx:= -1;
  Fy:= -1;
  FTrackerType:= mpOutBox;
  inherited;

WMMouseMove 消息

本控件最“重”的處理就是在MouseMove消息上了。為了能在鼠標拖動邊框或整個控件時,能實時顯示位置,必須計算出目標位置。

  1. 根據WMLButtonDown消息處理時記錄的光標初始值(Fx, Fy)計算偏移量(dx, dy);
  2. 根據WMLButtonDown消息處理時記錄的拖動類型(FTrackerType)計算控件外框相對於父控件的坐標值(x1, x2, y1, y2);
  3. 修正控件外框坐標,將控件限制在父控件的Client區域內部,拖動或者拉伸均不能越界。且拉伸也不能小於最小尺寸(FMinSize);
  4. 根據當前光標位置,設置鼠標光標形狀。

以下是最關鍵的計算控件外框坐標的代碼:

  case FTrackerType of
    mpLeft:
      begin
        inc(x1, dx);
      end;
    mpRight:
      begin
        inc(x2, dx);
        Fx:= Message.XPos;
      end;
    mpTop:
      begin
        inc(y1, dy);
      end;
    mpBottom:
      begin
        inc(y2, dy);
        Fy:= Message.YPos;
      end;
    mpLeftTop:
      begin
        inc(x1, dx);
        inc(y1, dy);
      end;
    mpRightBottom:
      begin
        inc(x2, dx);
        inc(y2, dy);
        Fx:= Message.XPos;
        Fy:= Message.YPos;
      end;
    mpLeftBottom:
      begin
        inc(x1, dx);
        inc(y2, dy);
        Fy:= message.YPos;
      end;
    mpRightTop:
      begin
        inc(x2, dx);
        inc(y1, dy);
        Fx:= message.XPos;
      end;
    mpInBox:  //只是移動,不做拉伸
      begin
        inc(x1, dx);
        inc(y1, dy);
        inc(x2, dx);
        inc(y2, dy);
      end;
  end;

請注意,WMMouseMove消息帶入的是相對於父控件的坐標,光標坐標(message.XPos, message.YPos)可能會小於0,也可能會大於當前控件的WidthHeight值。因為在鼠標保持按下狀態時,即使光標位置移出了當前控件的邊界,控件仍然會接收到WMMouseMove消息。向左向上移出,坐標就會出現負值。向下向右移出,坐標則會大於當前控件的Width及Height值。以下是示意圖:

中間是子控件,外圍是父控件。

源碼

DXRectTracker.zip


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM