zoukankan      html  css  js  c++  java
  • 一种居于JvMouseGesture.pas的鼠标手势系统

    尽管高版本的Delphi已经提供强悍的手势功能,也非常好用,我还是没能用上,所以自己结合实际,参阅多个组件源码,改造了JvMouseGesture.pas单元,弄出一个实用的鼠标手势管理功能,记在这里,以免硬盘坏了,又要重来。

    改造过的JvMouseGesture.pas单元代码:

    unit JvMouseGesture;
    
    {$I jvcl.inc}
    
    interface
    
    uses
      {$IFDEF UNITVERSIONING}
      JclUnitVersioning,
      {$ENDIF UNITVERSIONING}
      SysUtils, Classes, Controls, Windows, Messages,Forms,Graphics,
      JvComponentBase;
    
    type
      { Description
        Defines, whether or not the hook will be activated automatically or not.
      }
      TJvActivationMode = (amAppStart, amManual);
    
      { Description
        Defines a complex gesture (two or more letters event)
    
      }
      TOnMouseGestureCustomInterpretation = procedure(Sender: TObject;const AGesture: string) of object;
    
      { Description
        This class implements the basic interpreter. It can be used
        to enhance single components, too. E.g., if you want to
        enable a grid with gesture feature. For this purpose you have
        to do 4 steps:
    
        1) Fill the "OnMouseDown" event with code like
    
    
        <CODE>
          if Button = mbRight then
            JvMouseGesture1.StartMouseGesture(x,y);
        </CODE>
    
    
        2) Fill the OnMouseMove event with something like
    
    
        <CODE>
          if JvMouseGesture1.TrailActive then
            JvMouseGesture1.TrailMouseGesture(x,y);
        </CODE>
    
    
        3) Now fill the OnMouseUp event
    
    
        <CODE>
          if JvMouseGesture1.TrailActive then
            JvMouseGesture1.EndMouseGesture;
        </CODE>
    
    
        4) Last but not least fill components
    
        OnJvMouseGestureCustomInterpretation
    
        XOR
    
        OnJvMouseGesture<xyz>
    
        event
    
        Note:
    
    
        If CustomInterpreation is filled the other events are not
        fired!
    
        See Also
    
        TJvMouseGestureHook
      }
      {$IFDEF RTL230_UP}
      [ComponentPlatformsAttribute(pidWin32 or pidWin64 or pidOSX32)]
      {$ENDIF RTL230_UP}
      TJvMouseGesture = class(TJvComponent)
      private
        FForm: TForm;
        FActive: Boolean;
        FHided: Boolean;
        FTrailX: Integer;
        FTrailY: Integer;
        FTrailLength: Integer;
        FTrailActive: Boolean;
        FTrailStartTime: TDateTime;
        FdTolerance: Integer;
        FTrailLimit: Integer;
        FTrackWidth: Cardinal;
        FTrackColor: TColor;
        FDelay: Integer;
        FTrailInterval: Integer;
        FGrid: Integer; // tolerance for diagonal movement. See TrailMouseGesture
        FGridHalf: Integer; // half of grid, needed for performance
        FLastPushed: String;
        FGesture: string;
        FGestureList: TStringList;
        FLastWndProc: TWndMethod;
    
        FOnMouseGestureRight: TNotifyEvent;
        FOnMouseGestureLeft: TNotifyEvent;
        FOnMouseGestureUp: TNotifyEvent;
        FOnMouseGestureDown: TNotifyEvent;
        FOnMouseGestureLeftLowerEdge: TNotifyEvent;
        FOnMouseGestureRightUpperEdge: TNotifyEvent;
        FOnMouseGestureLeftUpperEdge: TNotifyEvent;
        FOnMouseGestureRightLowerEdge: TNotifyEvent;
        FOnMouseGestureCancelled: TNotifyEvent;
        FOnTrailingMouseGesture: TNotifyEvent;
        FOnMouseGestureCustomInterpretation: TOnMouseGestureCustomInterpretation;
        { Description
          Adds a detected sub gesture to gesture string
        }
        procedure AddGestureChar(AChar: String);
        procedure SetTrailLimit(const Value: Integer);
        procedure SetTrailInterval(const Value: Integer);
        procedure SetDelay(const Value: Integer);
        procedure SetGrid(const Value: Integer);
        procedure SetTrackColor(const Value: TColor);
        { Description
          Loads the known gestures for matching events
    
          Note:
          In this version only evaluation of simple mouse gestures are implemented
        }
        procedure LoadGestureTable;
        { Description
          Standard setter method for Active
        }
        procedure SetActive(const Value: Boolean);
        procedure Hide; // 内部函数,用来隐藏当前窗体(Internal function to hide the form)
        procedure AdjustSize;
        procedure WndProc(var Msg: TMessage);
      protected
        procedure DoMouseGestureRight; virtual;
        procedure DoMouseGestureLeft; virtual;
        procedure DoMouseGestureUp; virtual;
        procedure DoMouseGestureDown; virtual;
        procedure DoMouseGestureLeftLowerEdge; virtual;
        procedure DoMouseGestureRightUpperEdge; virtual;
        procedure DoMouseGestureLeftUpperEdge; virtual;
        procedure DoMouseGestureRightLowerEdge; virtual;
        procedure DoMouseGestureCancelled; virtual;
        procedure DoOnTrailingMouseGesture; virtual;
        function DoMouseGestureCustomInterpretation(const AGesture: string): Boolean; virtual;
      public
        { Description
          Standard constructor
        }
        constructor Create(AOwner: TComponent); override;
        { Description
          Standard destructor
        }
        destructor Destroy; override;
        { Description
          Starts the mouse gesture interpretation
    
          Parameters:
          AMouseX: X coordinate of mouse cursor
          AMouseY: Y coordinate of mouse cursor
        }
        procedure StartMouseGesture(AMouseX, AMouseY: Integer);
        { Description
          Continues the mouse gesture interpretation during mouse move
    
          Parameters:
          AMouseX: X coordinate of mouse cursor
          AMouseY: Y coordinate of mouse cursor
        }
        procedure TrailMouseGesture(AMouseX, AMouseY: Integer);
        { Description
          Ends the mouse gesture interpretation and fires an event if a gesture
          was found
        }
        procedure EndMouseGesture(AMouseX, AMouseY: Integer);
        { Description
          The actual length of trail (not of gesture string!!!)
        }
        procedure DrawGestureText(GText:String);
        property TrailLength: Integer read FTrailLength;
        { Description
          TRUE, if in detection, otherwise FALSE
        }
        property TrailActive: Boolean read FTrailActive;
        { Description
          The gesture string. For string content see description of unit.
        }
        property Gesture: string read FGesture;
      published
        { Description
          The maximum length of trail (not of gesture string!!!)
          Normally never been changed
        }
        property TrailLimit: Integer read FTrailLimit write SetTrailLimit;
        { Description
          Trail interval
          Normally never been changed
        }
        property TrailInterval: Integer read FTrailInterval write SetTrailInterval;
        { Description
          Grid size for detection
          Normally never been changed
        }
        property Grid: Integer read FGrid write SetGrid;
        { Description
          The maximum delay before cancelling a gesture
          Normally never been changed
        }
        property Delay: Integer read FDelay write SetDelay;
        { Description
          TRUE if component is active, otherwise FALSE
        }
        property Active: Boolean read FActive write SetActive;
        { Description
          Event for own evaluation of detected gesture. If this event is used all
          others will be ignored!
        }
        property TrackColor
            : TColor read FTrackColor write SetTrackColor default clRed;
        // 轨迹宽度,默认5px
        property TrackWidth: Cardinal read FTrackWidth write FTrackWidth default 5;
        property OnMouseGestureCustomInterpretation: TOnMouseGestureCustomInterpretation read
          FOnMouseGestureCustomInterpretation write FOnMouseGestureCustomInterpretation;
        { Description
          Event for a simple MOUSE UP gesture
        }
        property OnMouseGestureCancelled: TNotifyEvent read FOnMouseGestureCancelled write FOnMouseGestureCancelled;
        property OnMouseGestureUp: TNotifyEvent read FOnMouseGestureUp write FOnMouseGestureUp;
        { Description
          Event for a simple MOUSE DOWN gesture
        }
        property OnMouseGestureDown: TNotifyEvent read FOnMouseGestureDown write FOnMouseGestureDown;
        { Description
          Event for a simple MOUSE LEFT gesture
        }
        property OnMouseGestureLeft: TNotifyEvent read FOnMouseGestureLeft write FOnMouseGestureLeft;
        { Description
          Event for a simple MOUSE RIGHT gesture
        }
        property OnMouseGestureRight: TNotifyEvent read FOnMouseGestureRight write FOnMouseGestureRight;
        { Description
          Event for a simple diagonally MOUSE LEFT LOWER EDGE (point 1 in grid) gesture
        }
        property OnMouseGestureLeftLowerEdge: TNotifyEvent read FOnMouseGestureLeftLowerEdge write
          FOnMouseGestureLeftLowerEdge;
        { Description
          Event for a simple diagonally MOUSE RIGHT LOWER EDGE (point 3 in grid) gesture
        }
        property OnMouseGestureRightLowerEdge: TNotifyEvent read FOnMouseGestureRightLowerEdge write
          FOnMouseGestureRightLowerEdge;
        { Description
          Event for a simple diagonally MOUSE LEFT UPPER EDGE (point 7 in grid) gesture
        }
        property OnMouseGestureLeftUpperEdge: TNotifyEvent read FOnMouseGestureLeftUpperEdge write
          FOnMouseGestureLeftUpperEdge;
        { Description
          Event for a simple diagonally MOUSE RIGHT UPPER EDGE (point 9 in grid) gesture
        }
        property OnMouseGestureRightUpperEdge: TNotifyEvent read FOnMouseGestureRightUpperEdge write
          FOnMouseGestureRightUpperEdge;
        property OnTrailingMouseGesture: TNotifyEvent  read FOnTrailingMouseGesture write FOnTrailingMouseGesture;
      end;
    
      { Description
        This class implements a application wide mouse hook for mouse gestures.
        Programmers get only one event for a detected mouse gesture:
    
        OnMouseGestureCustomInterpretation
    
        See Also
        TJvMouseGesture
      }
      {$IFDEF RTL230_UP}
      [ComponentPlatformsAttribute(pidWin32 or pidWin64 or pidOSX32)]
      {$ENDIF RTL230_UP}
      TJvMouseGestureHook = class(TJvComponent)
      private
        FTrailLimit: Integer;
        FTrackWidth: Cardinal;
        FTrackColor: TColor;
        FDelay: Integer;
        FTrailInterval: Integer;
        FGrid: Integer;
        { Description
          True if a hook is installed
        }
        FHookInstalled: Boolean;
        { Description
          Field for hook handle
        }
        FCurrentHook: HHook;
        { Description
          Field for method pointer
        }
        FOnMouseGestureCustomInterpretation: TOnMouseGestureCustomInterpretation;
        { Description
          Field for active state of component
        }
        FOnCustomTrailingMouseGesture: TNotifyEvent;
        FActive: Boolean;
        { Description
          Field for mouse key
        }
        FMouseButton: TMouseButton;
        { Description
          Field for activation mode
        }
        FActivationMode: TJvActivationMode;
        { Description
          Standard setter method for evaluation of detected gesture
        }
        { Description
          Standard setter method for Active
        }
        procedure SetActive(const Value: Boolean);
        { Description
          Standard setter method for MouseButton
        }
        procedure SetMouseButton(const Value: TMouseButton);
        { Description
          Standard setter method for ActivationMode
        }
        procedure SetTrailLimit(const Value: Integer);
        procedure SetTrailInterval(const Value: Integer);
        procedure SetDelay(const Value: Integer);
        procedure SetGrid(const Value: Integer);
        procedure SetTrackColor(const Value: TColor);
        procedure SetTrackWidth(const Value: Cardinal);
        procedure SetActivationMode(const Value: TJvActivationMode);
        procedure SetMouseGestureCustomInterpretation(const Value: TOnMouseGestureCustomInterpretation);
        procedure SetTrailingMouseGesture(const Value: TNotifyEvent);
        function GetMouseGesture: TJvMouseGesture;
      protected
        { Description
          Create the hook. Maybe used in a later version as a new constructor
          to enable system wide hooks ...
        }
        procedure CreateForThreadOrSystem(AOwner: TComponent; ADwThreadID: Cardinal);
        function DoMouseGestureCustomInterpretation(const AGesture: string): Boolean; virtual;
      public
        { Description
          Standard constructor
        }
        constructor Create(AOwner: TComponent); override;
        { Description
          Standard destructor
        }
        destructor Destroy; override;
        { Description
          TRUE if hook was installed successfully
        }
        property HookInstalled: Boolean read FHookInstalled; //True if a hook is installed
        { Description
          handle of hook
        }
        property CurrentHook: HHook read FCurrentHook; //contains the handle of the currently installed hook
        property MouseGesture: TJvMouseGesture read GetMouseGesture;
      published
        property TrailLimit:Integer  read FTrailLimit write SetTrailLimit;
        property TrackWidth:Cardinal  read FTrackWidth write SetTrackWidth;
        property TrackColor:TColor  read FTrackColor write SetTrackColor;
        property Delay:Integer  read FDelay write SetDelay;
        property TrailInterval:Integer  read FTrailInterval write SetTrailInterval;
        property Grid:Integer  read FGrid write SetGrid;
    
        { Description
          TRUE if component is active, otherwise FALSE. Can be changed during runtime
        }
        property Active: Boolean read FActive write SetActive;
        { Description
          If property is set to <code>JvOnAppStart</code> then component will be
          activated on start of application, with <code>JvManually</code> you
          have to activate detection on your own
        }
        property ActivationMode: TJvActivationMode read FActivationMode write SetActivationMode;
        { Description
          Set the mouse key to be used for start/stop gesture
    
          See Also
          TMouseButton
        }
        property MouseButton: TMouseButton read FMouseButton write SetMouseButton default mbRight;
        { Description
          Set the event to be executed if a gesture will be detected
        }
        property OnMouseGestureCustomInterpretation: TOnMouseGestureCustomInterpretation read FOnMouseGestureCustomInterpretation write SetMouseGestureCustomInterpretation;
        property OnCustomTrailingMouseGesture: TNotifyEvent  read FOnCustomTrailingMouseGesture write SetTrailingMouseGesture;
      end;
    
    
      { Description
        Hook call back function.
        DO NOT USE EXTERN!
      }
    function JvMouseGestureHook(Code: Integer; wParam: Word; lParam: Longword): Longword; stdcall;
    
    
    
    {$IFDEF UNITVERSIONING}
    const
      UnitVersioning: TUnitVersionInfo = (
        RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/branches/JVCL3_47_PREPARATION/run/JvMouseGesture.pas $';
        Revision: '$Revision: 13104 $';
        Date: '$Date: 2011-09-07 08:50:43 +0200 (mer. 07 sept. 2011) $';
        LogPath: 'JVCL
    un'
      );
    {$ENDIF UNITVERSIONING}
    
    implementation
    
    uses
      JvResources, JvTypes;
    
    const
      JVMG_LEFT = 0;
      JVMG_RIGHT = 1;
      JVMG_UP = 2;
      JVMG_DOWN = 3;
      JVMG_LEFTUPPER = 4;
      JVMG_RIGHTUPPER = 5;
      JVMG_LEFTLOWER = 6;
      JVMG_RIGHTLOWER = 7;
    
    var
      { Description
        Object pointer to interpreter class used by hook
      }
      JvMouseGestureInterpreter: TJvMouseGesture;
      { Description
        Some global vars to be accessed by call back function ...
      }
      JvMouseGestureHookAlreadyInstalled: Boolean = False;
      //<combine JvMouseGestureHookAlreadyInstalled>
      JvMouseGestureHookActive: Boolean = False;
      //<combine JvMouseGestureHookAlreadyInstalled>
      JvMouseButtonDown: Cardinal = WM_RBUTTONDOWN;
      //<combine JvMouseGestureHookAlreadyInstalled>
      JvMouseButtonUp: Cardinal = WM_RBUTTONUP;
    
      JvCurrentHook: HHook = 0; //contains the handle of the currently installed hook
    
    //=== { TJvMouseGesture } ====================================================
    
    constructor TJvMouseGesture.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      FGestureList := TStringList.Create;
      FGestureList.Sorted := True;
    
      FDelay := 500;
      FTrailLimit := 1000;
      FTrailInterval := 2;
      FGrid := 15;
      FTrackColor := clRed;
      FTrackWidth := 5;
      FGridHalf := FGrid div 2;
      FTrailActive := False;
      FdTolerance := 75; // tolerance for diagonal movement. see processCoordinates()
      begin
        FForm := TForm.Create(Self);
        FForm.TransparentColor := True;
        FForm.TransparentColorValue := clBlack;
        FForm.BorderStyle := bsNone;
        FForm.FormStyle := fsStayOnTop;
        FForm.DoubleBuffered := True;
        FForm.Color := clBlack;
        FLastWndProc := FForm.WindowProc;
        FForm.WindowProc := WndProc;
        AdjustSize;
        FForm.Canvas.Brush.Color := FForm.TransparentColorValue;
        FForm.Canvas.FillRect(FForm.ClientRect);
        ShowWindow(FForm.Handle,SW_SHOWNOACTIVATE);
        Hide;
        FHided := True;
      end;
      LoadGestureTable;
    
      FActive := not (csDesigning in ComponentState);
    end;
    
    destructor TJvMouseGesture.Destroy;
    begin
      FTrailActive := False;
      FreeAndNil(FGestureList);
      FForm.free;
      inherited Destroy;
    end;
    
    procedure TJvMouseGesture.LoadGestureTable;
    begin
      with FGestureList do
      begin
        AddObject('向左', TObject(JVMG_LEFT));
        AddObject('向右', TObject(JVMG_RIGHT));
        AddObject('向上', TObject(JVMG_UP));
        AddObject('向下', TObject(JVMG_DOWN));
        AddObject('向左斜下', TObject(JVMG_LEFTLOWER));
        AddObject('向右斜下', TObject(JVMG_RIGHTLOWER));
        AddObject('向左斜上', TObject(JVMG_LEFTUPPER));
        AddObject('向右斜上', TObject(JVMG_RIGHTUPPER));
      end;
    end;
    
    procedure TJvMouseGesture.SetActive(const Value: Boolean);
    begin
      if csDesigning in ComponentState then
        FActive := False
      else
        FActive := Value;
    end;
    
    procedure TJvMouseGesture.Hide;
    begin
      if not FHided then
      begin
        FForm.Canvas.Brush.Color := FForm.TransparentColorValue;
        FForm.Canvas.FillRect(FForm.ClientRect);
        FHided := True;
      end;
    end;
    
    procedure TJvMouseGesture.AdjustSize;
    begin
      if not (csDesigning in ComponentState) then
      FForm.SetBounds(Screen.DesktopLeft, Screen.DesktopTop, Screen.DesktopWidth,
        Screen.DesktopWidth)
      else FForm.SetBounds(Screen.DesktopLeft, Screen.DesktopTop, 0,
           0);
    end;
    
    procedure TJvMouseGesture.WndProc(var Msg: TMessage);
    begin
      if Msg.Msg = WM_NCHITTEST then
        Msg.Result := HTTRANSPARENT
      else if Msg.Msg = (WM_APP + 1) then
        AdjustSize
      else if Msg.Msg = (WM_APP + 2) then
      begin
    
      end
      else
      begin
        FLastWndProc(Msg);
        if Msg.Msg = WM_DISPLAYCHANGE then
          PostMessage(FForm.Handle, WM_APP + 1, 0, 0)
        else if Msg.Msg = WM_WINDOWPOSCHANGED then //保持窗口在最前,以保证能够覆盖绘制轨迹,
          PostMessage(FForm.Handle, WM_APP + 2, 0, 0);
      end;
    end;
    
    procedure TJvMouseGesture.SetTrailLimit(const Value: Integer);
    begin
      FTrailLimit := Value;
      if (FTrailLimit < 100) or (FTrailLimit > 10000) then
        FTrailLimit := 1000;
    end;
    
    procedure TJvMouseGesture.SetTrailInterval(const Value: Integer);
    begin
      FTrailInterval := Value;
      if (FTrailInterval < 1) or (FTrailInterval > 100) then
        FTrailInterval := 2;
    end;
    
    procedure TJvMouseGesture.SetDelay(const Value: Integer);
    begin
      FDelay := Value;
      if FDelay < 500 then
        FDelay := 500;
    end;
    
    procedure TJvMouseGesture.SetGrid(const Value: Integer);
    begin
      FGrid := Value;
      if (FGrid < 10) or (FGrid > 500) then
        FGrid := 15;
    
      FGridHalf := FGrid div 2;
    end;
    
    procedure TJvMouseGesture.SetTrackColor(const Value: TColor);
    begin
      if FTrackColor <> Value then
      begin
        FTrackColor := Value;
        if FTrackColor = clBlack then
          FForm.Color := clWhite
        else
          FForm.Color := clBlack;
        FForm.TransparentColorValue := FForm.Color;
      end;
    end;
    
    procedure TJvMouseGesture.AddGestureChar(AChar: String);
    begin
      if AChar <> FLastPushed then
      begin
        FGesture := FGesture +''+ AChar;
        FLastPushed := AChar;
      end;
    end;
    
    procedure TJvMouseGesture.StartMouseGesture(AMouseX, AMouseY: Integer);
    begin
      if not FActive then
        Exit;
      FForm.Show;
      FForm.BringToFront;
      FForm.Canvas.MoveTo(AMouseX, AMouseY);
      FLastPushed := #0;
      FGesture := '';
      FTrailActive := True;
      FTrailLength := 0;
      FTrailX := AMouseX;
      FTrailY := AMouseY;
      FTrailStartTime := now;
      FHided:=False;
    end;
    
    procedure TJvMouseGesture.TrailMouseGesture(AMouseX, AMouseY: Integer);
    var
      locX: Integer;
      locY: Integer;
      x_dir: Integer;
      y_dir: Integer;
      tolerancePercent: Double;
      x_divide_y: Double;
      y_divide_x: Double;
    
      function InBetween(AValue, AMin, AMax: Double): Boolean;
      begin
        Result := (AValue >= AMin) and (AValue <= AMax);
      end;
    
    begin
      if not FActive then
        Exit;
    
      if (not FTrailActive) or (FTrailLength > FTrailLimit) then
      begin
        FTrailActive := False;
        Exit;
      end;
    
      try
        x_dir := AMouseX - FTrailX;
        y_dir := AMouseY - FTrailY;
        locX := abs(x_dir);
        locY := abs(y_dir);
    
        // process each half-grid
        if (locX >= FGridHalf) or (locY >= FGridHalf) then
        begin
          // diagonal movement:
          // dTolerance = 75 means that a movement is recognized as diagonal when
          // x/y or y/x is between 0.25 and 1
          if (GetTopWindow(0) <> FForm.Handle) and Application.Active then
          FForm.BringToFront;
          FForm.Canvas.Pen.Color := FTrackColor;
          FForm.Canvas.Pen.Width := FTrackWidth;
          FForm.Canvas.LineTo(AMouseX, AMouseY);
    
          tolerancePercent := 1 - FdTolerance / 100;
          if locY <> 0 then
            x_divide_y := locX / locY
          else
            x_divide_y := 0;
          if locX <> 0 then
            y_divide_x := locY / locX
          else
            y_divide_x := 0;
          if (FdTolerance <> 0) and
            (InBetween(x_divide_y, tolerancePercent, 1) or
            InBetween(y_divide_x, tolerancePercent, 1)) then
          begin
            if (x_dir < -9) and (y_dir > 9) then
            begin
              AddGestureChar('向左斜下');
            end
            else
            begin
              if (x_dir > 9) and (y_dir > 9) then
                AddGestureChar('向右斜下')
              else
              begin
                if (x_dir < -9) and (y_dir < -9) then
                  AddGestureChar('向左斜上')
                else
                begin
                  if (x_dir > 9) and (y_dir < -9) then
                    AddGestureChar('向右斜上');
                end;
              end;
            end;
          end // of diaognal
          else
          begin
            // horizontal movement:
            if locX > locY then
            begin
              if x_dir > 0 then
                AddGestureChar('向右')
              else
              begin
                if x_dir < 0 then
                  AddGestureChar('向左');
              end;
            end
            else
            begin
              // vertical movement:
              if locX < locY then
              begin
                if y_dir > 0 then
                  AddGestureChar('向下')
                else
                begin
                  if y_dir < 0 then
                    AddGestureChar('向上');
                end;
              end;
            end;
          end;
        end; // of half grid
      finally
        FTrailX := AMouseX;
        FTrailY := AMouseY;
      end;
      DoOnTrailingMouseGesture;
    end;
    
    procedure TJvMouseGesture.DrawGestureText(GText:String);
    begin
      FForm.Canvas.TextOut(300,300,GText);
    end;
    
    procedure TJvMouseGesture.EndMouseGesture(AMouseX, AMouseY: Integer);
    var
      Index: Integer;
    begin
      Hide;
      if not FActive then
        Exit;
    
      FTrailActive := False;
    
      if FGesture = '' then
      begin
        DoMouseGestureCancelled;
        Exit;
      end;
    
      // check for custom interpretation first
      if DoMouseGestureCustomInterpretation(FGesture) then
        Exit
      else Hide;
    
      // if no custom interpretation is implemented we chaeck for known gestures
      // and matching events
      // CASE indexes are stored sequence independent. So we have to find gesture
      // first and get CASE INDEX stored as TObject in Object property. It's a
      // simple trick, but works fine ...
      Index := FGestureList.IndexOf(FGesture);
      if Index > -1 then
        Index := Integer(FGestureList.Objects[Index]);
      case Index of
        JVMG_LEFT:
          begin
            DoMouseGestureLeft;
          end;
        JVMG_RIGHT:
          begin
            DoMouseGestureRight;
          end;
        JVMG_UP:
          begin
            DoMouseGestureUp;
          end;
        JVMG_DOWN:
          begin
            DoMouseGestureDown;
          end;
        JVMG_LEFTLOWER:
          begin
            DoMouseGestureLeftLowerEdge;
          end;
        JVMG_RIGHTLOWER:
          begin
            DoMouseGestureRightLowerEdge;
          end;
        JVMG_LEFTUPPER:
          begin
            DoMouseGestureLeftUpperEdge;
          end;
        JVMG_RIGHTUPPER:
          begin
            DoMouseGestureRightUpperEdge;
          end;
      end;
    end;
    
    procedure TJvMouseGesture.DoMouseGestureCancelled;
    begin
      if Assigned(FOnMouseGestureCancelled) then
        FOnMouseGestureCancelled(Self);
    end;
    
    procedure TJvMouseGesture.DoOnTrailingMouseGesture;
    begin
      if Assigned(FOnTrailingMouseGesture) then
        FOnTrailingMouseGesture(Self);
    end;
    
    function TJvMouseGesture.DoMouseGestureCustomInterpretation(const AGesture: string): Boolean;
    begin
       Result := Assigned(FOnMouseGestureCustomInterpretation);
       if Result then
       begin
          FOnMouseGestureCustomInterpretation(Self,FGesture);
       end;
       Hide;
    end;
    
    procedure TJvMouseGesture.DoMouseGestureDown;
    begin
      if Assigned(FOnMouseGestureDown) then
        FOnMouseGestureDown(Self);
    end;
    
    procedure TJvMouseGesture.DoMouseGestureLeft;
    begin
      if Assigned(FOnMouseGestureLeft) then
        FOnMouseGestureLeft(Self);
    end;
    
    procedure TJvMouseGesture.DoMouseGestureLeftLowerEdge;
    begin
      if Assigned(FOnMouseGestureLeftLowerEdge) then
        FOnMouseGestureLeftLowerEdge(Self);
    end;
    
    procedure TJvMouseGesture.DoMouseGestureLeftUpperEdge;
    begin
      if Assigned(FOnMouseGestureLeftUpperEdge) then
        FOnMouseGestureLeftUpperEdge(Self);
    end;
    
    procedure TJvMouseGesture.DoMouseGestureRight;
    begin
      if Assigned(FOnMouseGestureRight) then
        FOnMouseGestureRight(Self);
    end;
    
    procedure TJvMouseGesture.DoMouseGestureRightLowerEdge;
    begin
      if Assigned(FOnMouseGestureRightLowerEdge) then
        FOnMouseGestureRightLowerEdge(Self);
    end;
    
    procedure TJvMouseGesture.DoMouseGestureRightUpperEdge;
    begin
      if Assigned(FOnMouseGestureRightUpperEdge) then
        FOnMouseGestureRightUpperEdge(Self);
    end;
    
    procedure TJvMouseGesture.DoMouseGestureUp;
    begin
      if Assigned(FOnMouseGestureUp) then
        FOnMouseGestureUp(Self);
    end;
    
    //=== { TJvMouseGestureHook } ================================================
    
    constructor TJvMouseGestureHook.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      FDelay := 500;
      FTrailLimit := 1000;
      FTrailInterval := 2;
      FGrid := 15;
      FTrackColor := clRed;
      FTrackWidth := 5;
      CreateForThreadOrSystem(AOwner, MainThreadID); // hook for complete application
      JvMouseGestureInterpreter.Delay:=FDelay;
      JvMouseGestureInterpreter.Grid:=FGrid;
      JvMouseGestureInterpreter.TrackWidth:=FTrackWidth;
      JvMouseGestureInterpreter.TrackColor:=FTrackColor;
      JvMouseGestureInterpreter.TrailLimit:=FTrailLimit;
      JvMouseGestureInterpreter.TrailInterval:=FTrailInterval;
    end;
    
    destructor TJvMouseGestureHook.Destroy;
    
    begin
      FreeAndNil(JvMouseGestureInterpreter);
    
      if JvMouseGestureHookAlreadyInstalled then
        JvMouseGestureHookAlreadyInstalled := UnhookWindowsHookEx(JvCurrentHook);
      inherited Destroy;
    end;
    
    procedure TJvMouseGestureHook.SetTrailLimit(const Value: Integer);
    begin
      FTrailLimit := Value;
      if (FTrailLimit < 100) or (FTrailLimit > 10000) then
        FTrailLimit := 1000;
      JvMouseGestureInterpreter.TrailLimit:=FTrailLimit;
    end;
    
    procedure TJvMouseGestureHook.SetTrailInterval(const Value: Integer);
    begin
      FTrailInterval := Value;
      if (FTrailInterval < 1) or (FTrailInterval > 100) then
        FTrailInterval := 2;
      JvMouseGestureInterpreter.TrailInterval:=FTrailInterval;
    end;
    
    procedure TJvMouseGestureHook.SetDelay(const Value: Integer);
    begin
      FDelay := Value;
      if FDelay < 500 then
        FDelay := 500;
      JvMouseGestureInterpreter.Delay:=FDelay;
    end;
    
    procedure TJvMouseGestureHook.SetGrid(const Value: Integer);
    begin
      FGrid := Value;
      if (FGrid < 10) or (FGrid > 500) then
        FGrid := 15;
      JvMouseGestureInterpreter.Grid:=FGrid;
    end;
    
    procedure TJvMouseGestureHook.SetTrackColor(const Value: TColor);
    begin
      if FTrackColor <> Value then
      begin
        FTrackColor := Value;
        JvMouseGestureInterpreter.TrackColor:=FTrackColor;
        if FTrackColor = clBlack then
          JvMouseGestureInterpreter.FForm.Color := clWhite
        else
          JvMouseGestureInterpreter.FForm.Color := clBlack;
        JvMouseGestureInterpreter.FForm.TransparentColorValue := JvMouseGestureInterpreter.FForm.Color;
      end;
    end;
    
    procedure TJvMouseGestureHook.SetTrackWidth(const Value: Cardinal);
    begin
      FTrackWidth:=Value;
      JvMouseGestureInterpreter.TrackWidth:=FTrackWidth;
    end;
    
    procedure TJvMouseGestureHook.CreateForThreadOrSystem(AOwner: TComponent; ADwThreadID: Cardinal);
    
    begin
      if JvMouseGestureHookAlreadyInstalled then
        raise EJVCLException.CreateRes(@RsECannotHookTwice);
    
      JvMouseGestureInterpreter := TJvMouseGesture.Create(nil);
      FMouseButton := mbRight;
    
      if csDesigning in ComponentState then
      begin
        FActive := False;
        Exit;
      end;
    
      FActive := FActivationMode = amAppStart;
    
      //install hook
      FCurrentHook := SetWindowsHookEx(WH_MOUSE, @JvMouseGestureHook, 0, ADwThreadID);
    
      //return True if it worked (read only for user). User should never see a
      //global var like MouseGestureHookAlreadyInstalled
      FHookInstalled := FCurrentHook <> 0;
    
      // global remember, internal use only
      JvMouseGestureHookAlreadyInstalled := FHookInstalled;
      JvCurrentHook := FCurrentHook;
    
      // map event
      if Assigned(FOnMouseGestureCustomInterpretation) then
        JvMouseGestureInterpreter.OnMouseGestureCustomInterpretation :=
          FOnMouseGestureCustomInterpretation
      else
        JvMouseGestureInterpreter.OnMouseGestureCustomInterpretation := nil;
    end;
    
    function TJvMouseGestureHook.DoMouseGestureCustomInterpretation(const AGesture: string): Boolean;
    begin
      Result := Assigned(FOnMouseGestureCustomInterpretation);
      if Result then
        FOnMouseGestureCustomInterpretation(Self, AGesture);
    end;
    
    procedure TJvMouseGestureHook.SetActivationMode(const Value: TJvActivationMode);
    begin
      FActivationMode := Value;
    end;
    
    procedure TJvMouseGestureHook.SetActive(const Value: Boolean);
    begin
      if csDesigning in ComponentState then
        FActive := False
      else
        FActive := Value;
    
      JvMouseGestureHookActive := FActive;
    end;
    
    procedure TJvMouseGestureHook.SetMouseButton(const Value: TMouseButton);
    begin
      FMouseButton := Value;
      case Value of
        mbLeft:
          begin
            JvMouseButtonDown := WM_LBUTTONDOWN;
            JvMouseButtonUp := WM_LBUTTONUP;
          end;
        mbMiddle:
          begin
            JvMouseButtonDown := WM_MBUTTONDOWN;
            JvMouseButtonUp := WM_MBUTTONUP;
          end;
        mbRight:
          begin
            JvMouseButtonDown := WM_RBUTTONDOWN;
            JvMouseButtonUp := WM_RBUTTONUP;
          end;
      end;
    end;
    
    procedure TJvMouseGestureHook.SetMouseGestureCustomInterpretation(
      const Value: TOnMouseGestureCustomInterpretation);
    begin
      FOnMouseGestureCustomInterpretation := Value;
      if Assigned(JvMouseGestureInterpreter) then
        JvMouseGestureInterpreter.OnMouseGestureCustomInterpretation := Value;
    end;
    
    procedure TJvMouseGestureHook.SetTrailingMouseGesture(const Value: TNotifyEvent);
    begin
      FOnCustomTrailingMouseGesture:=Value;
      if Assigned(JvMouseGestureInterpreter) then
        JvMouseGestureInterpreter.OnTrailingMouseGesture := Value;
    end;
    
    function TJvMouseGestureHook.GetMouseGesture: TJvMouseGesture;
    begin
      Result := JvMouseGestureInterpreter;
    end;
    
    //============================================================================
    
    
    function JvMouseGestureHook(Code: Integer; wParam: Word; lParam: Longword): Longword; stdcall;
    var
      locY: Integer;
      locX: Integer;
    begin
      if (Code >= 0) and (JvMouseGestureHookActive) then
      begin
        with PMouseHookStruct(lParam)^ do
        begin
          locX := pt.X;
          locY := pt.Y;
        end;
        if wParam = WM_MOUSEMOVE then
        begin
          JvMouseGestureInterpreter.TrailMouseGesture(locX, locY);
        end;
        if wParam = JvMouseButtonDown then
        begin
          JvMouseGestureInterpreter.StartMouseGesture(locX, locY);
        end
        else
        if wParam = JvMouseButtonUp then
        begin
          JvMouseGestureInterpreter.EndMouseGesture(locX, locY);
        end;
    
      end;
      Result := CallNextHookEx(JvCurrentHook, Code, wParam, lParam);
    end;
    
    
    
    
    {$IFDEF UNITVERSIONING}
    initialization
      RegisterUnitVersion(HInstance, UnitVersioning);
    
    finalization
      UnregisterUnitVersion(HInstance);
    {$ENDIF UNITVERSIONING}
    
    end.
    改造过的JvMouseGesture

    增加了几个东西:

    FForm: TForm:用于绘制显示手势规矩

    FTrackWidth: Cardinal;手势轨迹宽度
    FTrackColor: TColor;手势轨迹颜色

    此外主要改造了以下几个过程、函数

    constructor TJvMouseGesture.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      FGestureList := TStringList.Create;
      FGestureList.Sorted := True;
    
      FDelay := 500;
      FTrailLimit := 1000;
      FTrailInterval := 2;
      FGrid := 15;
      FTrackColor := clRed;
      FTrackWidth := 5;
      FGridHalf := FGrid div 2;
      FTrailActive := False;
      FdTolerance := 75; // tolerance for diagonal movement. see processCoordinates()
      begin
        FForm := TForm.Create(Self);
        FForm.TransparentColor := True;
        FForm.TransparentColorValue := clBlack;
        FForm.BorderStyle := bsNone;
        FForm.FormStyle := fsStayOnTop;
        FForm.DoubleBuffered := True;
        FForm.Color := clBlack;
        FLastWndProc := FForm.WindowProc;
        FForm.WindowProc := WndProc;
        AdjustSize;
        FForm.Canvas.Brush.Color := FForm.TransparentColorValue;
        FForm.Canvas.FillRect(FForm.ClientRect);
        ShowWindow(FForm.Handle,SW_SHOWNOACTIVATE);
        Hide;
        FHided := True;
      end;
      LoadGestureTable;
    
      FActive := not (csDesigning in ComponentState);
    end;
    
    procedure TJvMouseGesture.LoadGestureTable;
    begin
      with FGestureList do
      begin
        AddObject('向左', TObject(JVMG_LEFT));
        AddObject('向右', TObject(JVMG_RIGHT));
        AddObject('向上', TObject(JVMG_UP));
        AddObject('向下', TObject(JVMG_DOWN));
        AddObject('向左斜下', TObject(JVMG_LEFTLOWER));
        AddObject('向右斜下', TObject(JVMG_RIGHTLOWER));
        AddObject('向左斜上', TObject(JVMG_LEFTUPPER));
        AddObject('向右斜上', TObject(JVMG_RIGHTUPPER));
      end;
    end;

    此处替换了原来的手势名称,改为中文,这样用户才看得懂

    procedure TJvMouseGesture.StartMouseGesture(AMouseX, AMouseY: Integer);
    begin
      if not FActive then
        Exit;
      FForm.Show;
      FForm.BringToFront;
      FForm.Canvas.MoveTo(AMouseX, AMouseY);
      FLastPushed := #0;
      FGesture := '';
      FTrailActive := True;
      FTrailLength := 0;
      FTrailX := AMouseX;
      FTrailY := AMouseY;
      FTrailStartTime := now;
      FHided:=False;
    end;
    
    procedure TJvMouseGesture.TrailMouseGesture(AMouseX, AMouseY: Integer);
    var
      locX: Integer;
      locY: Integer;
      x_dir: Integer;
      y_dir: Integer;
      tolerancePercent: Double;
      x_divide_y: Double;
      y_divide_x: Double;
    
      function InBetween(AValue, AMin, AMax: Double): Boolean;
      begin
        Result := (AValue >= AMin) and (AValue <= AMax);
      end;
    
    begin
      if not FActive then
        Exit;
    
      if (not FTrailActive) or (FTrailLength > FTrailLimit) then
      begin
        FTrailActive := False;
        Exit;
      end;
    
      try
        x_dir := AMouseX - FTrailX;
        y_dir := AMouseY - FTrailY;
        locX := abs(x_dir);
        locY := abs(y_dir);
    
        // process each half-grid
        if (locX >= FGridHalf) or (locY >= FGridHalf) then
        begin
          // diagonal movement:
          // dTolerance = 75 means that a movement is recognized as diagonal when
          // x/y or y/x is between 0.25 and 1
          if (GetTopWindow(0) <> FForm.Handle) and Application.Active then
          FForm.BringToFront;
          FForm.Canvas.Pen.Color := FTrackColor;
          FForm.Canvas.Pen.Width := FTrackWidth;
          FForm.Canvas.LineTo(AMouseX, AMouseY);
    
          tolerancePercent := 1 - FdTolerance / 100;
          if locY <> 0 then
            x_divide_y := locX / locY
          else
            x_divide_y := 0;
          if locX <> 0 then
            y_divide_x := locY / locX
          else
            y_divide_x := 0;
          if (FdTolerance <> 0) and
            (InBetween(x_divide_y, tolerancePercent, 1) or
            InBetween(y_divide_x, tolerancePercent, 1)) then
          begin
            if (x_dir < -9) and (y_dir > 9) then
            begin
              AddGestureChar('向左斜下');
            end
            else
            begin
              if (x_dir > 9) and (y_dir > 9) then
                AddGestureChar('向右斜下')
              else
              begin
                if (x_dir < -9) and (y_dir < -9) then
                  AddGestureChar('向左斜上')
                else
                begin
                  if (x_dir > 9) and (y_dir < -9) then
                    AddGestureChar('向右斜上');
                end;
              end;
            end;
          end // of diaognal
          else
          begin
            // horizontal movement:
            if locX > locY then
            begin
              if x_dir > 0 then
                AddGestureChar('向右')
              else
              begin
                if x_dir < 0 then
                  AddGestureChar('向左');
              end;
            end
            else
            begin
              // vertical movement:
              if locX < locY then
              begin
                if y_dir > 0 then
                  AddGestureChar('向下')
                else
                begin
                  if y_dir < 0 then
                    AddGestureChar('向上');
                end;
              end;
            end;
          end;
        end; // of half grid
      finally
        FTrailX := AMouseX;
        FTrailY := AMouseY;
      end;
      DoOnTrailingMouseGesture;
    end;
    StartMouseGesture

    这个地方也改造了

    此外还定义了一个新的组件,以方便用户自定义鼠标手势,其代码如下:

    unit UWSGestureREC;
    
    interface
    
    uses
      Windows,SysUtils, Messages ,Classes, Controls,Graphics,GraphUtil,
      Generics.Collections,Math,Dialogs;
    
    type
      TGesturePoints = TList<TPoint>;
    
      TOnMouseGestureCustomInterpretation = procedure(Sender: TObject;const AGesture: string) of object;
    
      TCustomUWSGestureRecord = class(TCustomControl)
      private
        { Private declarations }
        FGesture:string;
        FGestureLineColor: TColor;
        FGesturePointColor: TColor;
        FLastDrawnPoint: Integer;
        FPoints: TGesturePointArray;
        FRecordedPoints: TGesturePoints;
        FRecording: Boolean;
        FPlaying:Boolean ;
        FCaption: string;
        FLastPushed: String;
        FTrailX: Integer;
        FTrailY: Integer;
        FTrailLength: Integer;
        FTrailActive: Boolean;
        FTrailStartTime: TDateTime;
        FdTolerance: Integer;
        FTrailLimit: Integer;
        FGridHalf: Integer;
        FStandardGestures:TStringList;
        FBasicGestures:TStringList;
        FGestureFileName:string;
        FOnMouseGestureCustomInterpretation: TOnMouseGestureCustomInterpretation;
        FOnTrailingGesture: TOnMouseGestureCustomInterpretation;
    
        procedure AddGesturePoint(const LastPoint, NextPoint: TPoint);
        function PointsToArray(Source: TGesturePoints): TGesturePointArray;
        procedure SetCaption(const Value: string);
        procedure SetGestureLineColor(const Value: TColor);
        procedure SetGesturePointColor(const Value: TColor);
        procedure ShortGesture;
      protected
        { Protected declarations }
        procedure DrawPoint(const Point: TPoint); virtual;
        procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
        procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
        procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
        procedure Paint; override;
        procedure WndProc(var Message: TMessage); override;
        function DoMouseGestureCustomInterpretation(const AGesture: string): Boolean; virtual;
        function DoTrailingGesture(const AGesture: string): Boolean; virtual;
      public
        constructor Create(AOwner: TComponent); override;
        destructor Destroy; override;
        function NormalizePoints(const Points: array of TPoint): TGesturePointArray;
        procedure AddGestureChar(AChar: String);
        procedure StartMouseGesture(AMouseX, AMouseY: Integer);
        procedure TrailMouseGesture(AMouseX, AMouseY: Integer);
        procedure EndMouseGesture(AMouseX, AMouseY: Integer);
        procedure Play;
        procedure PlayStandard(aGesture:String);
        procedure ReRestSize;
        procedure PlayFromFile(aGestureFile:String);
        procedure SaveGesturePointtoFile(aGPFile:String);
        function IsStandardGesture(aGesture:String):Boolean;
        function GesturetoGestureFileName(aGesture:String):string;
        procedure ExpoertStandardGesture2List(Items:TStrings);
        property RecordedPoints: TGesturePoints read FRecordedPoints write FRecordedPoints;
        property Caption: string read FCaption write SetCaption;
        property Gesture:string read FGesture write FGesture;
        property GestureLineColor: TColor read FGestureLineColor
          write SetGestureLineColor default clBlue;
        property GesturePointColor: TColor read FGesturePointColor
          write SetGesturePointColor default clBlue;
        property GestureFileName:string read FGestureFileName;
        property OnMouseGestureCustomInterpretation: TOnMouseGestureCustomInterpretation read
          FOnMouseGestureCustomInterpretation write FOnMouseGestureCustomInterpretation;
        property OnTrailingGesture: TOnMouseGestureCustomInterpretation read
          FOnTrailingGesture write FOnTrailingGesture;
        property StandardGestures:TStringList read FStandardGestures;
      published
        { Published declarations }
      end;
    
      TUWSGestureRecord = class(TCustomUWSGestureRecord)
      private
        { Private declarations }
      protected
        { Protected declarations }
      public
        { Public declarations }
      published
        { Published declarations }
        property Align;
        property Anchors;
        property BevelEdges;
        property BevelInner;
        property BevelOuter;
        property BevelKind default bkNone;
        property BevelWidth;
        property BiDiMode;
        property Caption;
        property Color;
        property Constraints;
        property Ctl3D;
        property DoubleBuffered default True;
        property DragCursor;
        property DragKind;
        property DragMode;
        property Enabled;
        property Font;
        property GestureLineColor;
        property GesturePointColor;
        property Height default 200;
        property ParentBiDiMode;
        property ParentColor;
        property ParentDoubleBuffered default False;
        property ParentFont;
        property ParentShowHint;
        property PopupMenu;
        property ShowHint;
        property Visible;
        property Width default 200;
        property OnClick;
        property OnContextPopup;
        property OnEndDock;
        property OnEndDrag;
        property OnGesture;
        property OnDragDrop;
        property OnDragOver;
        property OnMouseActivate;
        property OnMouseDown;
        property OnMouseEnter;
        property OnMouseLeave;
        property OnMouseMove;
        property OnMouseUp;
        property OnResize;
        property OnStartDock;
        property OnStartDrag;
        property OnMouseGestureCustomInterpretation;
        property OnTrailingGesture;
      end;
    
    procedure Register;
    
    implementation
    
    procedure Register;
    begin
      RegisterComponents('uws Used', [TUWSGestureRecord]);
    end;
    
    constructor TCustomUWSGestureRecord.Create(AOwner: TComponent);
    begin
      inherited;
      AlignWithMargins:=True;
      Margins.Top:=7;
      Margins.Bottom:=7;
      Margins.Left:=7;
      Margins.Right:=7;
      FGesture :='';
      FGestureLineColor := clBlue;
      FGesturePointColor := clBlue;
      FRecordedPoints := TGesturePoints.Create;
      FTrailLimit := 1000;
      FGridHalf := 8;
      FTrailActive := False;
      FdTolerance := 75; // tol
      FRecording := False;
      Height := 200;
      Width := 200;
      ControlStyle := ControlStyle - [csGestures];
      DoubleBuffered := True;
      ParentDoubleBuffered := False;
      FStandardGestures:=TStringList.Create;
      FStandardGestures.Add('→向右');
      FStandardGestures.Add('→向左');
      FStandardGestures.Add('→向上');
      FStandardGestures.Add('→向下');
      FStandardGestures.Add('→向右斜上');
      FStandardGestures.Add('→向右斜下');
      FStandardGestures.Add('→向左斜上');
      FStandardGestures.Add('→向左斜下');
      FStandardGestures.Add('→向下→向右');
      FStandardGestures.Add('→向下→向左');
      FStandardGestures.Add('→向上→向右');
      FStandardGestures.Add('→向上→向左');
      FStandardGestures.Add('→向右→向下');
      FStandardGestures.Add('→向左→向下');
      FStandardGestures.Add('→向右→向上');
      FStandardGestures.Add('→向左→向上');
      FStandardGestures.Add('→向右→向左');
      FStandardGestures.Add('→向左→向右');
      FStandardGestures.Add('→向下→向上');
      FStandardGestures.Add('→向上→向下');
      FBasicGestures:=TStringList.Create;
      FBasicGestures.Add('向右');
      FBasicGestures.Add('向左');
      FBasicGestures.Add('向上');
      FBasicGestures.Add('向下');
      FBasicGestures.Add('向右斜上');
      FBasicGestures.Add('向右斜下');
      FBasicGestures.Add('向左斜上');
      FBasicGestures.Add('向左斜下');
    end;
    
    destructor TCustomUWSGestureRecord.Destroy;
    begin
      FreeAndNil(FRecordedPoints);
      FreeAndNil(FStandardGestures);
      FreeAndNil(FBasicGestures);
      inherited;
    end;
    
    procedure TCustomUWSGestureRecord.ReRestSize;
    begin
      if Height<200 then
      Height:=200;
      if Width<200 then
      Width:=200; 
      if Height<>width then
      Height:=Width ;
    end;
    
    procedure TCustomUWSGestureRecord.Play;
    var
      I:Integer;
      LRect: TRect;
    begin
      FPlaying:=True;
      LRect := ClientRect;
      Canvas.Brush.Color := Color;
      Canvas.FillRect(LRect);
      if FRecordedPoints.Count>0 then
      begin
        Canvas.MoveTo(FRecordedPoints[0].X, FRecordedPoints[0].Y);
        for I := 0 to FRecordedPoints.Count - 1 do
        begin
          DrawPoint(FRecordedPoints[I]);
          Sleep(10);
        end;
      end;
      FPlaying:=False;
    end;
    
    procedure TCustomUWSGestureRecord.PlayStandard(aGesture:String);
    var
      I,K,CC:Integer;
      LRect: TRect;
    begin
      if aGesture='' then Exit;
      FPlaying:=True;
      CC:=Min(Width,Height);
      LRect := ClientRect;
      Canvas.Brush.Color := Color;
      Canvas.FillRect(LRect);
      if aGesture='→向右' then
      begin
        Canvas.MoveTo(20, Height div 2);
        for I := 20 to Width-20 do
        begin
          DrawPoint(Point(I,Height div 2));
          Sleep(1);
        end;
      end
      else if aGesture='→向左' then
      begin
        Canvas.MoveTo(Width-20, Height div 2);
        for I := Width-20 downto 20 do
        begin
          DrawPoint(Point(I,Height div 2));
          Sleep(1);
        end;
      end
      else if aGesture='→向下' then
      begin
        Canvas.MoveTo(Width div 2, 20);
        for I := 20 to Height-20 do
        begin
          DrawPoint(Point(Width div 2,I));
          Sleep(1);
        end;
      end
      else if aGesture='→向上' then
      begin
        Canvas.MoveTo(Width div 2, Height-20);
        for I := Height-20 downto 20 do
        begin
          DrawPoint(Point(Width div 2,I));
          Sleep(1);
        end;
      end
      else if aGesture='→向右斜下' then
      begin
        Canvas.MoveTo(20, 20);
        for I := 20 to CC-20 do
        begin
          DrawPoint(Point(I,I));
          Sleep(1);
        end;
      end
      else if aGesture='→向右斜上' then
      begin
        Canvas.MoveTo(20, Height-20);
        for I := 20 to CC-20 do
        begin
          DrawPoint(Point(I,Height-I));
          Sleep(1);
        end;
      end
      else if aGesture='→向左斜下' then
      begin
        Canvas.MoveTo(Width-20, 20);
        for I := 20 to CC-20 do
        begin
          DrawPoint(Point(Width-I,I));
          Sleep(1);
        end;
      end
      else if aGesture='→向左斜上' then
      begin
        Canvas.MoveTo(Width-20, Height-20);
        for I := 20 to CC-20 do
        begin
          DrawPoint(Point(Width-I,Height-I));
          Sleep(1);
        end;
      end
      else if aGesture='→向下→向右' then
      begin
        Canvas.MoveTo(60, 60);
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(60,I));
          Sleep(1);
        end;
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(I,Height-60));
          Sleep(1);
        end;
      end
      else if aGesture='→向下→向左' then
      begin
        Canvas.MoveTo(width-60,60);
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(width-60,I));
          Sleep(1);
        end;
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(width-I,Height-60));
          Sleep(1);
        end;
      end
      else if aGesture='→向上→向右' then
      begin
        Canvas.MoveTo(60,Height-60);
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(60,Height-I));
          Sleep(1);
        end;
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(I,60));
          Sleep(1);
        end;
      end
      else if aGesture='→向上→向左' then
      begin
        Canvas.MoveTo(Width-60,Height-60);
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(Width-60,Height-I));
          Sleep(1);
        end;
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(Width-I,60));
          Sleep(1);
        end;
      end
      else if aGesture='→向左→向上' then
      begin
        Canvas.MoveTo(Width-60,Height-60);
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(Width-I,Height-60));
          Sleep(1);
        end;
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(60,Height-I));
          Sleep(1);
        end;
      end
      else if aGesture='→向左→向下' then
      begin
        Canvas.MoveTo(Width-60,60);
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(Width-I,60));
          Sleep(1);
        end;
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(60,I));
          Sleep(1);
        end;
      end
      else if aGesture='→向右→向上' then
      begin
        Canvas.MoveTo(60,Height-60);
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(I,Height-60));
          Sleep(1);
        end;
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(Width-60,Height-I));
          Sleep(1);
        end;
      end
      else if aGesture='→向右→向下' then
      begin
        Canvas.MoveTo(60,60);
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(I,60));
          Sleep(1);
        end;
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(Width-60,I));
          Sleep(1);
        end;
      end
      else if aGesture='→向右→向左' then
      begin
        Canvas.MoveTo(60,Height div 2);
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(I,Height div 2));
          Sleep(1);
        end;
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(Width-I,Height div 2));
          Sleep(1);
        end;
      end
      else if aGesture='→向左→向右' then
      begin
        Canvas.MoveTo(Width-60,Height div 2);
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(Width-I,Height div 2));
          Sleep(1);
        end;
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(I,Height div 2));
          Sleep(1);
        end;
      end
      else if aGesture='→向下→向上' then
      begin
        Canvas.MoveTo(Width div 2,60);
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(Width div 2,I));
          Sleep(1);
        end;
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(Width div 2,Height-I));
          Sleep(1);
        end;
      end
      else if aGesture='→向上→向下' then
      begin
        Canvas.MoveTo(Width div 2,Height-60);
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(Width div 2,Height-I));
          Sleep(1);
        end;
        for I := 60 to CC-60 do
        begin
          DrawPoint(Point(Width div 2,I));
          Sleep(1);
        end;
      end;
      FPlaying:=False;
    end;
    
    function TCustomUWSGestureRecord.IsStandardGesture(aGesture:String):Boolean;
    begin
      Result:=False ;
      if aGesture='' then Exit;
      Result:=(FStandardGestures.IndexOf(aGesture)<>-1);
    end;
    
    function TCustomUWSGestureRecord.GesturetoGestureFileName(aGesture:String):string;
    var
      Temp:TStringList;
      I,ID:Integer;
    begin
      Result:='';
      if aGesture='' then Exit;
      Temp:=TStringList.Create;
      try
        Temp.Delimiter:='';
        Temp.DelimitedText:=aGesture;
        for I := 0 to Temp.Count-1 do
        begin
          if Temp[I]<>'' then
          begin
            ID:=FBasicGestures.IndexOf(Temp[I]);
            Result:=Result+InttoStr(ID);
          end;
        end;
      finally
        Temp.Free;
      end;
      Result:=Result+'.GPS';
    end;
    
    procedure TCustomUWSGestureRecord.ExpoertStandardGesture2List(Items:TStrings);
    begin
      Items.Assign(FStandardGestures);
    end;
    
    procedure TCustomUWSGestureRecord.SaveGesturePointtoFile(aGPFile:String);
    var
      I:Integer;
      Temp:TStringList;
    begin
      if aGPFile='' then
      aGPFile:='123.GPS';
      if FRecordedPoints.Count<1 then Exit;
      Temp:=TStringList.Create ;
      try
        for I := 0 to FRecordedPoints.Count-1 do
        begin
          Temp.Add(Format('X%d=%d',[I,FRecordedPoints[I].X]));
          Temp.Add(Format('Y%d=%d',[I,FRecordedPoints[I].Y]));
        end;
        Temp.SaveToFile(aGPFile);
      finally
        Temp.Free;
      end;
    end;
    
    procedure TCustomUWSGestureRecord.PlayFromFile(aGestureFile:String);
    var
      I,CC,X,Y:Integer ;
      Temp:TStringList;
      LRect: TRect;
    begin
      if aGestureFile='' then Exit;
      if not FileExists(aGestureFile) then Exit;
      LRect := ClientRect;
      Canvas.Brush.Color := Color;
      Canvas.FillRect(LRect);
      Temp:=TStringList.Create;
      try
        try
          Temp.LoadFromFile(aGestureFile);
        except
        end;
        if Temp.Count>1 then
        begin
          CC:=Temp.Count div 2;
          X:=0;
          Y:=0;
          try
            X:=StrToInt(Temp.Values['X0']);
            Y:=StrToInt(Temp.Values['Y0']);
          except
          end;
          Canvas.MoveTo(X,Y);
          for I := 0 to CC-1 do
          begin
            X:=0;
            Y:=0;
            try
              X:=StrToInt(Temp.Values[Format('X%d',[I])]);
              Y:=StrToInt(Temp.Values[Format('Y%d',[I])]);
            except
            end;
            DrawPoint(Point(X,Y));
            Sleep(10);
          end;
        end;
      finally
        Temp.Free;
      end;
    end;
    
    procedure TCustomUWSGestureRecord.AddGestureChar(AChar: String);
    begin
      if AChar <> FLastPushed then
      begin
        FGesture := FGesture +''+ AChar;
        FLastPushed := AChar;
      end;
    end;
    
    procedure TCustomUWSGestureRecord.AddGesturePoint(const LastPoint, NextPoint: TPoint);
    var
      StepX, StepY: Single;
      I, DeltaX, DeltaY: Integer;
      CountX, CountY, Count: Integer;
    begin
      // Determine distance between points
      DeltaX := Abs(NextPoint.X - LastPoint.X);
      DeltaY := Abs(NextPoint.Y - LastPoint.Y);
    
      // If points are too close together discard the new point
      if (DeltaX < 4) and (DeltaY < 4) then
        Exit;
    
      // If points are too far apart insert intermediate points
      if (DeltaX > 8) or (DeltaY > 8) then
      begin
        // Determine how many points to insert
        CountX := DeltaX div 5;
        if (DeltaX mod 5) = 0 then
          Dec(CountX);
        CountY := DeltaY div 5;
        if (DeltaY mod 5) = 0 then
          Dec(CountY);
        Count := Max(CountX, CountY);
    
        // Determine spacing between inserted points
        StepX := (NextPoint.X - LastPoint.X) / Count;
        StepY := (NextPoint.Y - LastPoint.Y) / Count;
    
        // Insert points
        for I := 1 to Count - 1 do
          FRecordedPoints.Add(Point(LastPoint.X + Round(StepX * I),
            LastPoint.Y + Round(StepY * I)));
      end;
    
      // Add captured point
      FRecordedPoints.Add(NextPoint);
    end;
    
    function TCustomUWSGestureRecord.PointsToArray(Source: TGesturePoints): TGesturePointArray;
    var
      I: Integer;
    begin
      SetLength(Result, Source.Count);
      for I := 0 to Source.Count - 1 do
        Result[I] := Source[I];
    end;
    
    procedure TCustomUWSGestureRecord.SetCaption(const Value: string);
    begin
      if Value <> FCaption then
      begin
        FCaption := Value;
        Invalidate;
      end;
    end;
    
    procedure TCustomUWSGestureRecord.StartMouseGesture(AMouseX, AMouseY: Integer);
    begin
      // Set recording mode
      FRecording := True;
      Invalidate;
      // Clear list of points
      FRecordedPoints.Clear;
      FRecordedPoints.Add(Point(AMouseX, AMouseY));
      DrawPoint(FRecordedPoints[0]);
      FLastDrawnPoint := 0;
      FLastPushed := #0;
      FGesture := '';
      FTrailActive := True;
      FTrailLength := 0;
      FTrailX := AMouseX;
      FTrailY := AMouseY;
      FTrailStartTime := now;
    end;
    
    procedure TCustomUWSGestureRecord.ShortGesture;
    var
      TempStr:string;
      Temp:TStringList;
      I:Integer;
    begin
      Temp:=TStringList.Create;
      try
        Temp.Delimiter:='';
        Temp.DelimitedText:=FGesture;
        if Temp.Count>8 then
        begin
          for I := 1 to 8 do
          TempStr:=TempStr+''+temp[I];
          FGesture:=TempStr;
        end;
      finally
        Temp.Free;
      end;
    end;
    
    procedure TCustomUWSGestureRecord.EndMouseGesture(AMouseX, AMouseY: Integer);
    var
      Index: Integer;
    begin
      if not FRecording  then
        Exit;
      FTrailActive := False;
      FRecording := False;
    
      // Add new gesture point
      AddGesturePoint(FRecordedPoints[FRecordedPoints.Count - 1], Point(AMouseX, AMouseY));
    
      // Normalize list of points
      FPoints := NormalizePoints(PointsToArray(FRecordedPoints));
      ShortGesture;
      FCaption:=FGesture ;
    
    
      // Trigger OnRecorded event if more than 1 point was recorded
      if (Length(FPoints) > 1) then
      begin
    
      end;
      FGestureFileName:=GesturetoGestureFileName(FGesture);
      DoMouseGestureCustomInterpretation(FGesture);
      // Force repaint
      Invalidate;
    end;
    
    procedure TCustomUWSGestureRecord.TrailMouseGesture(AMouseX, AMouseY: Integer);
    var
      locX: Integer;
      locY: Integer;
      x_dir: Integer;
      y_dir: Integer;
      tolerancePercent: Double;
      x_divide_y: Double;
      y_divide_x: Double;
      I:Integer;
      function InBetween(AValue, AMin, AMax: Double): Boolean;
      begin
        Result := (AValue >= AMin) and (AValue <= AMax);
      end;
    
    begin
      if not FRecording then
        Exit;
    
      // Add new gesture point
      AddGesturePoint(FRecordedPoints[FRecordedPoints.Count - 1], Point(AMouseX, AMouseY));
      for I := FLastDrawnPoint to FRecordedPoints.Count - 1 do
        DrawPoint(FRecordedPoints[I]);
      FLastDrawnPoint := FRecordedPoints.Count - 1;
    
      if (not FTrailActive) or (FTrailLength > FTrailLimit) then
      begin
        FTrailActive := False;
        Exit;
      end;
    
      try
        x_dir := AMouseX - FTrailX;
        y_dir := AMouseY - FTrailY;
        locX := abs(x_dir);
        locY := abs(y_dir);
    
        // process each half-grid
        if (locX >= FGridHalf) or (locY >= FGridHalf) then
        begin
          // diagonal movement:
          // dTolerance = 75 means that a movement is recognized as diagonal when
          // x/y or y/x is between 0.25 and 1
    
          tolerancePercent := 1 - FdTolerance / 100;
          if locY <> 0 then
            x_divide_y := locX / locY
          else
            x_divide_y := 0;
          if locX <> 0 then
            y_divide_x := locY / locX
          else
            y_divide_x := 0;
          if (FdTolerance <> 0) and
            (InBetween(x_divide_y, tolerancePercent, 1) or
            InBetween(y_divide_x, tolerancePercent, 1)) then
          begin
            if (x_dir < -6) and (y_dir > 6) then
            begin
              AddGestureChar('向左斜下');
            end
            else
            begin
              if (x_dir > 6) and (y_dir > 6) then
                AddGestureChar('向右斜下')
              else
              begin
                if (x_dir < -6) and (y_dir < -6) then
                  AddGestureChar('向左斜上')
                else
                begin
                  if (x_dir > 6) and (y_dir < -6) then
                    AddGestureChar('向右斜上');
                end;
              end;
            end;
          end // of diaognal
          else
          begin
            // horizontal movement:
            if locX > locY then
            begin
              if x_dir > 0 then
                AddGestureChar('向右')
              else
              begin
                if x_dir < 0 then
                  AddGestureChar('向左');
              end;
            end
            else
            begin
              // vertical movement:
              if locX < locY then
              begin
                if y_dir > 0 then
                  AddGestureChar('向下')
                else
                begin
                  if y_dir < 0 then
                    AddGestureChar('向上');
                end;
              end;
            end;
          end;
        end; // of half grid
      finally
        FTrailX := AMouseX;
        FTrailY := AMouseY;
      end;
      DoTrailingGesture(FGesture);
    end;
    
    procedure TCustomUWSGestureRecord.SetGestureLineColor(const Value: TColor);
    begin
      if Value <> FGestureLineColor then
      begin
        FGestureLineColor := Value;
        Invalidate;
      end;
    end;
    
    procedure TCustomUWSGestureRecord.SetGesturePointColor(const Value: TColor);
    begin
      if Value <> FGesturePointColor then
      begin
        FGesturePointColor := Value;
        Invalidate;
      end;
    end;
    
    procedure TCustomUWSGestureRecord.DrawPoint(const Point: TPoint);
    begin
      Canvas.Brush.Style := bsClear;
      Canvas.Pen.Width:=17;
      Canvas.Pen.Color := FGesturePointColor;
      Canvas.Ellipse(Point.X - 2, Point.Y - 2, Point.X + 3, Point.Y + 3);
    
      Canvas.Pen.Color := FGestureLineColor;
      if FRecordedPoints.Count = 1 then
        Canvas.MoveTo(Point.X, Point.Y)
      else
        Canvas.LineTo(Point.X, Point.Y);
    end;
    
    procedure TCustomUWSGestureRecord.MouseDown(Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
      if Button<>mbLeft then Exit;
      StartMouseGesture(X,Y);
    end;
    
    procedure TCustomUWSGestureRecord.MouseMove(Shift: TShiftState; X, Y: Integer);
    begin
      TrailMouseGesture(X,Y);
    end;
    
    function TCustomUWSGestureRecord.NormalizePoints(
      const Points: array of TPoint): TGesturePointArray;
    var
      Index, SmallestX, SmallestY: Integer;
    begin
      SetLength(Result, Length(Points));
      // Find the delta.
      SmallestX := MaxInt;
      SmallestY := MaxInt;
    
      for Index := 0 to Length(Points) - 1 do
      begin
        if SmallestX > Points[Index].X then
          SmallestX := Points[Index].X;
    
        if SmallestY > Points[Index].Y then
          SmallestY := Points[Index].Y;
      end;
    
      // Apply the delta.
      SetLength(Result, Length(Points));
      for Index := 0 to Length(Points) - 1 do
        Result[Index] := Point(Points[Index].X - SmallestX, Points[Index].Y - SmallestY);
    end;
    
    procedure TCustomUWSGestureRecord.MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer);
    begin
      EndMouseGesture(X,Y);
    end;
    
    procedure TCustomUWSGestureRecord.WndProc(var Message: TMessage);
    begin
      inherited WndProc(Message);
    end;
    
    procedure TCustomUWSGestureRecord.Paint;
    var
      LRect: TRect;
      LText: string;
      I, LTextHeight: Integer;
    begin
      LRect := ClientRect;
      Canvas.Brush.Color := Color;
      Canvas.FillRect(LRect);
    
      if (not FRecording) and (not FPlaying) then
      begin
        // Draw instructions
        Canvas.Font := Self.Font;
        Canvas.Brush.Style := bsClear;
        if FCaption='' then
        FCaption:=FGesture ; 
        LText := FCaption;
        if (csDesigning in ComponentState) and (LText = '') then
          LText := Name;
    
        InflateRect(LRect, -25, 0);
        LRect.Top := 0;
        LRect.Bottom := 0;
        Canvas.TextRect(LRect, LText, [tfCalcRect, tfWordBreak]);
        LRect.Right := Width - 25;
        LTextHeight := LRect.Bottom - LRect.Top;
        LRect.Top := (Height - LTextHeight) div 2;
        Inc(LRect.Bottom, LRect.Top);
        Canvas.TextRect(LRect, LText, [tfCenter, tfWordBreak]);
      end
      else
      begin
        // Draw points
        for I := 0 to FRecordedPoints.Count - 1 do
          DrawPoint(FRecordedPoints[I])
      end;
    end;
    
    function TCustomUWSGestureRecord.DoMouseGestureCustomInterpretation(const AGesture: string): Boolean;
    begin
       Result := Assigned(FOnMouseGestureCustomInterpretation);
       if Result then
       begin
          FOnMouseGestureCustomInterpretation(Self,FGesture);
       end;
    end;
    
    function TCustomUWSGestureRecord.DoTrailingGesture(const AGesture: string): Boolean;
    begin
       Result := Assigned(FOnTrailingGesture);
       if Result then
       begin
          FOnTrailingGesture(Self,FGesture);
       end;
    end;
    
    end.
    管理单元代码

     改造过的JvMouseGesture.pas和鼠标手势定义组件下载:

    JvMouseGesture.pas

    UWSGestureREC

    效果图

    暂时就记录到这里

     代码在Delphi XE中测试通过,其他版本未经测试

  • 相关阅读:
    【拓扑排序】【堆】CH Round #57
    【数论】【筛法求素数】【欧拉函数】bzoj2818 Gcd
    【数论】【枚举约数】【欧拉函数】bzoj2705 [SDOI2012]Longge的问题
    【暴力】vijos P1897 学姐吃牛排
    【kruscal】【最小生成树】【并查集扩展】bzoj3714 [PA2014]Kuglarz
    【set】bzoj2761 [JLOI2011]不重复数字
    【尺取法】【Multiset】bzoj1342 [Baltic2007]Sound静音问题
    【神奇の做法】bzoj2456 mode
    【博弈论】bzoj2463 [中山市选2009]谁能赢呢?
    【set】bzoj3715 [PA2014]Lustra
  • 原文地址:https://www.cnblogs.com/uws2056/p/3525426.html
Copyright © 2011-2022 走看看