menu
  Home  ==>  articles  ==>  vcl_rtl  ==>  ecran_tactile_delphi_multi_touch   

Ecran Tactile Delphi Multi Touch - John COLIBRI.

  • résumé : gestion Delphi des écrans tactiles multi-points: type d'écrans, test, messages souris, déplacement d'objets, message wm_touch, moteur d'inertie Windows
  • mots clé : multi-touch, écran tactile, RegisterTouchWindow, wm_Touch, PhysicalToLogicalPoint, iInertiaProcessor, iManipulationProcessor
  • logiciel utilisé : Windows XP personnel (pour compiler), Windows 7 (pour exécuter), Delphi Xe3
  • matériel utilisé : Pentium 2.800 Mhz, 512 Meg de mémoire, 250 Giga disque dur, PackBell (Windows 7), écran iiYama t2250Mts
  • champ d'application : Delphi 2010 et supérieurs
  • niveau : développeur Delphi Windows
  • plan :


1 - Ecrans tactiles Windows Multi Touch

Pour pouvoir recevoir les messages souris Windows correspondant au toucher, il faut disposer d'un équipement capable de détecter ces touchers, et installer les pilotes Windows correspondants.

Parmi les équipement, mentionnons

  • les portables tel que le HP TouchSmart (non essayé)
  • les PC-Ecrans en un seul appareil (Sony Vaio L - non essayé)
  • les touch-pads comme Dell Inspiron Mini 10 (non essayé)
  • les moniteurs séparés. Par exemple le iiYama t2250Mts (utilisé pour cet article)



2 - Ecran tactile iiYama Touch Screen

Pour faire nos essais tactiles, nous avons utilisé un écran iiYama ProLite T2250Mts.

Sur Amazon.fr, il nous a coûté 240 Euros TTC, livré en 2 jours.



2.1 - Installation

Pour l'installation
  • connecter la prise écran
  • connecter AUSSI la prise USB
  • connecter la prise secteur


2.2 - Vérifications

Pour Windows 7, en cliquant sur "Ordinateur | Propriétés", dans la section "Système" la présence de fonctionnalités tactiles est confirmée:

window_7_touch_capabilities

Nous pouvons aussi vérifier que l'écran tactile est bien reconnu comme moniteur:

win7_touch_monitor

Si toutefois ces deux conditions ne sont pas remplies, il est possible de télécharger le pilote iiYama pour le T2250Mts depuis le site iiYama.

driver_iiyama_t2250mts




3 - Principe Touch Screen

Une fois l'écran installé, nous pourrons utiliser l'écran avec le doigt de la même façon que nous utilisons la souris:
  • toucher l'écran a le même effet qu'un clic
  • toucher et traîner le doigt se comporte comme un tirer-glisser (drag and drop)


Nous pouvons le vérifier simplement en chargeant dans le bloc note (NOTEPAD.EXE) quelques lignes, et de sélectionner quelques lignes avec le doigt:

tou_03_notepad_touch_trial

Et pour simuler le toucher avec deux doigts, nous pouvons charger MSPAINT.EXE, initialiser une nouvelle image ("Fichier | Nouveau"), puis tirer-glisser deux doigts sur l'écran:

u_04_paintbrush_touch_trial



3.1 - Type d'écran

Il existe fondamentalement deux générations d'écrans tactiles
  • les écrans à toucher simple (single touch) pour lequel chaque toucher sera converti en message souris
  • les écrans à toucher multiple (multi-touch) qui intercepteront de un à 10 actions de toucher. Dans le cas de notre iiYama, il est capable de détecter deux toucher.


Sur Window avant Windows 7, les toucher étaient traduits en message souris usuels.

Pour pouvoir distinguer les messages MouseMove des messages souris, le type tShiftState a été redéfini comme suit:

TShiftState = Set Of (ssShiftssAltssCtrlssLeftssRightssMiddlessDouble
    ssTouchssPenssCommand

Nous pouvons donc détecter si OnMouseMove, par, exemple, a été provoqué par la souris ou par le toucher.



Voici un premier exemple utilisant le toucher simple qui affiche simplement les messages souris
   créez une nouvelle applications Vcl
   créez les messages souris et affichez les informations souris correspondants

Type t_shift_type= (ssShiftssAltssCtrl,                                                                                                 //
        ssLeftssRightssMiddlessDoublessTouchssPenssCommand);
     t_shift_type_setSet Of t_shift_type;

Function f_shift_type_name(p_shift_typet_shift_type): String;
  Begin
    Result:= GetEnumName(TypeInfo(t_shift_type), Integer(p_shift_type));
  End;

Function f_shift_state_set(p_shift_state_settShiftState): String;
  Var l_shift_typet_shift_type;
      l_shift_type_sett_shift_type_set;
  Begin
    Result:= '';

    Move(p_shift_state_setl_shift_type_setSizeOf(t_shift_type_set));

    For l_shift_type:= ssShift To ssCommand Do
      If l_shift_type In l_shift_type_set
        Then Result:= Resultf_shift_type_name(l_shift_type)+ ' ';
  End// f_shift_state_set

Procedure display(p_textString);
  Begin
    Form1.Memo1.Lines.Add(p_text);
  End;

Var g_event_countInteger= 0;

Procedure TForm1.FormMouseDown(SenderTObjectButtonTMouseButton;
    ShiftTShiftStateXYInteger);
  Begin
    display(Format('%3d %-15s %4d %4d %s',
        [g_event_count'MouseDown'XYf_shift_state_set(Shift)]));
    Inc(g_event_count);
  End;

Procedure TForm1.FormMouseMove(SenderTObjectShiftTShiftStateX,
    YInteger);
  Begin
    display(Format('%3d %-15s %4d %4d %s',
        [g_event_count'MouseMove'XYf_shift_state_set(Shift)]));
    Inc(g_event_count);
  End;

Procedure TForm1.FormMouseUp(SenderTObjectButtonTMouseButton;
    ShiftTShiftStateXYInteger);
  Begin
    display(Format('%3d %-15s %4d %4d %s',
        [g_event_count'MouseUp'XYf_shift_state_set(Shift)]));
    Inc(g_event_count);
  End;

Procedure TForm1.FormClick(SenderTObject);
  Begin
    display(Format('%3d %-15s ',
        [g_event_count'Click']));
    Inc(g_event_count);
  End;

Procedure TForm1.FormDblClick(SenderTObject);
  Begin
    display(Format('%3d %-15s ',
        [g_event_count'DblClick']));
    Inc(g_event_count);
  End;

   compilez

   voici un exemple d'exécution:

single_touch



Notez que

  • comme tShiftState utilise une définition sans passer par la définition préalable de l'énuméré, nous avons créé une définition "parallèle" pour pouvoir afficher les énumérés (cf le code)
  • une fois que nous avons touché, la position courante de la souris est celle du dernier toucher. Et même si nous ne touchons plus à l'écran ou la souris, nous continuons à recevoir des messages OnMouseMove. Pour arrêter ces messages, il suffit de déplacer le point sensible de la souris (avec la souris ou avec le doigt) en dehors du composant qui gère OnMouseMove


Et si nous tapotons deux fois l'écran, voici le résultat

single_touch_double_clic



4 - Mise en oeuvre Multi Touch Delphi

Nous pouvons aussi provoquer un OnClic sur un bouton par tapotement:
   créez une nouvelle applications Vcl
   créez les messages souris et affichez les informations souris correspondants

Procedure TForm1.Button1Click(SenderTObject);                                                                                            //
  Begin
    display(Format('%3d %-15s ',
        [g_event_count'BtnClick']));
    Inc(g_event_count);
  End;

   compilez

   voici un exemple d'exécution:

button_clic



Notez que
  • lorsque nous touchons Button1, un message OnClic est bien généré
  • toutefois le clic ne provoque pas l'effet visuel d'enfoncement, comme pour un clic souris.
  • pour apercevoir l'effet d'enfoncement il faut déplacer le doigt lors du toucher.


4.1 - Toucher multiple (multi touch)

Sur Windows 7, les touchers sont traduits en un message wm_Touch qui comporte de nombreuses informations sur les touchers.



Pour utiliser wm_Touch, il faut

  • avoir un écran tactile
  • utiliser Windows 7 ou supérieur
  • enregistrer (et dé-enregistrer) chaque fenêtre censée recevoir wm_Touch
  • créer un gestionnaire d'événements wm_Touch


4.1.1 - Enregistrement de la fenêtre

Cet enregistrement est effectué en appelant RegisterTouchWindow en fournissant la poignée de la fenêtre. Cet appel pourra donc se faire dans CreateWnd ou dans tForm.OnCreate. Par exemple :

Procedure TForm1.FormCreate(SendertObject);
  Begin
    RegisterTouchWindow(Handle, 0);
  End// FormCreate



Et lorsque nous n'avons plus besoin de recevoir les événements wm_Touch, nous appellerons UnregisterTouchWindow(Handle). Par Exemple dans DestroyWnd ou dans OnDestroy



4.1.2 - Gestionnaire wm_Touch

Nous créons donc un gestionnaire d'événements

Procedure WMTouch(Var MessageTMessage); message WM_TOUCH;

et pour ce message:

  • Message.lParam est est la poignée pour les informations touch
  • Message.wParam est le nombre de touchers (1 doigt, 2 doigts, ... 10 doigts) - doigt ou "pen"


Lorsque cet événement est appelé, nous devons
  • déclare un tableau d'informations toucher
  • allouer un tableau pour récupérer les informations de chaque doigt
  • transférer les informations dans notre tableau par GetTouchInputInfo
  • lire les information pour chaque doigt
Var l_touchinput_arrayArray Of TTouchInput;
    l_touchinputTTouchInput;

  SetLength(l_touchinput_arrayMessage.WParam);

  GetTouchInputInfo(Message.LParamMessage.WParam
      @l_touchinput_array[0], SizeOf(TTouchInput));

  For l_touchinput In l_touchinput_array Do
    ...

tTouchInput est défini dans Winapi.Windows par

Type tTouchInput =
         Record
           xInteger;
           yInteger;
           hSourceTHandle;
           dwIDDWORD;
           dwFlagsDWORD;
           dwMaskDWORD;
           dwTimeDWORD;
           dwExtraInfoULONG_PTR;
           cxContactDWORD;
           cyContactDWORD;
         End;

et

  • x et y sont les coordonnées du point de toucher en "centièmes de pixel physique d'écran" (pour accomoder la haute définition)
  • hSource: la poignée du périphérique de saisie
  • dwID: un identificateur dont la valeur restera la même pendant toute la durée du toucher
  • dwFlags, dwMask: des informations sur le type de toucher (presser, relever, déplacement etc)
  • dwTime: la date de cet événement
  • dwExtraInfo
  • cxContact, cyContact: la taille de la zone de contact


Il faudra en général convertir les "centièmes de pixel physique d'écran" en pixel logiques en appelant PhysicalToLogicalPoint

Function PhysicalToLogicalPoint(hWndHWNDVar lpPointTPoint): BOOLStdcall

et

  • handle est la poignée du contrôle windows (la fenêtre ou un autre tWinControl)
  • lpPoint est notre point physique en entrée, et point logique en sortie (paramètre Var)


Comme de plus les coordonnées sont relatives à l'écran, nous appelons aussi, en général ScreenToClient pour avoir des coordonnées relatives à notre fenêtre



Voici une application qui affiche le contenu de wm_touch
   créez une application VCL
   créez les événements qui enregistrent et dé-enregistrent la réception des événements wm_Touch

Type                                                                                                                                                 //
  TForm1 = 
    Class(TForm)
      // ...
      Private
        Procedure CreateWndOverride;
        Procedure DestroyWndOverride;
      Public
      End;

Var
  Form1TForm1;

Implementation
  Uses Vcl.Touch.Gestures;

  {$R *.dfm}

    Procedure TForm1.CreateWnd;
      Begin
        Inherited;
        RegisterTouchWindow(Handle, 0);
      End// CreateWnd

    Procedure TForm1.DestroyWnd;
      Begin
        Inherited;
        UnregisterTouchWindow(Handle);
      End// DestroyWnd

End

   créez l'événement OnMouseMove pour vérifier les informations wm_touch

Type                                                                                                                                                 //
  TForm1 =
      Class(TForm)
        Procedure FormMouseMove(SenderTObjectShiftTShiftStateXYInteger);
        // ...
      End;

    Var g_mousemove_Xg_mousemove_YInteger;

    Procedure TForm1.FormMouseMove(SenderTObjectShiftTShiftState;
        XYInteger);
      Begin
        Panel1.Caption:= Format('  X=%3d Y=%3d', [XY]);

        g_mousemove_X:= X;
        g_mousemove_Y:= Y;
      End// FormMouseMove

End

   créez un gestionnaire des messages wm_Touch qui affiche la position de chaque doigt :

Type
  TForm1 =
      Class(TForm)
      Public
        Procedure handle_wm_touch(Var MessageTMessage); message WM_TOUCH;
      End;

    Procedure display(p_textString);
      Begin
        Form1.Memo1.Lines.Add(p_text);
      End;

    Procedure TForm1.handle_wm_touch(Var MessageTMessage);

      Function f_touchpoint_to_logical_point(Const p_touch_pointTTouchInput): TPoint;
          // -- converts the physical (hi def) coordinates into physical screen pixels
        Begin
          Result := Point(p_touch_point.X Div 100, p_touch_point.Y Div 100);
          PhysicalToLogicalPoint(HandleResult);
        End// f_touchpoint_to_logical_point

      Var l_touchinput_arrayArray Of TTouchInput;
          l_touchinputTTouchInput;
          l_handledBoolean;
          l_logical_pointTPoint;
          l_screen_pointtPoint;

      Begin // handle_wm_touch
        l_handled := False;
        display('wm_touch, count 'IntToStr(Message.WParam));

        SetLength(l_touchinput_arrayMessage.WParam);

        GetTouchInputInfo(Message.LParamMessage.WParam, @l_touchinput_array[0],
            SizeOf(TTouchInput));

        Try
          For l_touchinput In l_touchinput_array Do
          Begin
            l_logical_point:= f_touchpoint_to_logical_point(l_touchinput);
            l_screen_point:= ScreenToClient(l_logical_point);
            display(Format ('    scr X=%3d Y=%3d   screen_X=%3d Y=%3d   move_X=%3d Y=%3d %s',
                [l_logical_point.Xl_logical_point.Y,
                 l_screen_point.Xl_screen_point.Y,
                 g_mousemove_Xg_mousemove_Y'<']));
          End// for l_touchinput

          l_handled := True;
        Finally
          If l_handled Then
            CloseTouchInputHandle(Message.LParam)
          Else
            Inherited;
        End;
      End// handle_wm_touch

   compilez et exécutez
   voici un exemple d'affichage avec 1 doigt :

wm_touch_one_finger

   voici un exemple d'affichage avec 2 doigts :

wm_touch_two_finger



Nous pouvons exploiter wm_Touch pour déplacer un élément à l'écran. Nous pouvons, par exemple, déplacer un cercle à l'écran :

\Var g_center_xg_center_yInteger;

Procedure TForm1.FormPaint(SenderTObject);
  Begin
    Canvas.Ellipse(g_center_xk_radiusg_center_yk_radius,
        g_center_xk_radiusg_center_yk_radius);
  End;

Procedure TForm1.handle_wm_touch(Var MessageTMessage);

  // ooo

  Begin // handle_wm_touch
    // ooo

    Try
      For l_touchinput In l_touchinput_array Do
      Begin
        l_logical_point:= f_touchpoint_to_logical_point(l_touchinput);
        l_screen_point:= ScreenToClient(l_logical_point);

        If (l_touchinput.dwFlags And TouchEventF_Move)<> 0
          Then Begin
              g_center_x:= l_screen_point.X;
              g_center_y:= l_screen_point.Y;
              OnPaint(Nil);
            End;

      End// for l_touchinput

      // ooo
  End// handle_wm_touch

et voici un exemple d'exécution :

tou_05_wm_touch_move_object

Notez que :

  • nous avons volontairement appelé OnPaint pour afficher un nouveau cercle, ce qui laisse les cercles précédents à l'écran. Pour ne visualiser que la position courante, il faut appeler wla nouvelle
  • nous pouvons aussi utiliser les autres constantes telles que TouchEventF_Up etc pour traiter les autres types d'actions
  • pour déplacer le cercle uniquement si le doigt est dans le cercle, il faudrait tester la position du doigt par rapport au cercle
  • comme nous pouvons détecter 2 doigts, nous pourrions aussi tester l'action des deux doigts (agrandir le cercle etc)


4.2 - Toucher et Inertie

Windows 7 propose aussi un "Manipulation Engine" et "Inertia Engine" qui permettent des effets tels que des continuation ou des amortissements de mouvements.

Ces moteurs peuvent être utilisés sans le toucher, mais dans l'exemple qui suit, nous allons utiliser le toucher d'un point pour déplacer un cercle à l'écran.

Pour utiliser ces moteurs:

  • nous créons une Classe Delphi descendante de _IManipulationEvents
  • cette Classe crée les deux moteurs, sous forme d'objets COM
  • il faut ensuite implémenter les méthodes exigée par _IManipulationEvents.
Au niveau fonctionnement:
  • l'objet COM manipulateur
    • est informé des événements souris (toucher) par les méthodes ProcessDown, ProcessMove, ProcessUp
    • peut fournir des information par des méthodes telles que GetVelocityX
  • l'objet COM inertie
    • est initialisé par des limites de déplacement (put_BoundaryLeft ...)
    • peut utiliser des lignes d'élasticité (rebondissement) (put_ElasticMarginLeft ...)
    • l'inertie est lancée par put_InitialVelocityX, et put_InitialOriginX
    • peut fournir des informations en interrogeant Process


A titre d'exemple
  • nous déplaçons avec inertie un cercle à l'écran
  • nous définissons deux rectangles à l'écran:
    • un rectangle intérieur où le cercle ne doit pas pénétrer'
    • un rectangle englobant le précédent sur les paroi duquel le cercle rebondira
  • nous trainerons le doigt à l'écran, ce qui lancera le mouvement, et, dès que nous lèverons le doit, le cercle continuera sur sa lancée, en rebondissant sur la paroi elastique, avec un certain ralentissement
Au niveau Delphi
  • le cercle, avec ses moteurs COM sera initialisé dans OnCreate
  • les déplacement souris appelleront les mises à jour du manipulateur (down, move, up), qui afficheront le cercle
  • lorsque nous relèverons le doigt, le moteur d'inertie sera lancé. Pour visualiser le déplacement du cercle, nous utiliserons un tTimer qui affichera la position du cercle durant cette fin de trajectoire
Comme pour le déplacement précédent, nous conserverons les différents affichages à l'écran :
  • le premier toucher réinitialise l'affichage
  • lors du déplacement souris, le cercle sera vert
  • lors de mouvement piloté par l'inertie, le cercle sera jaune
  • la position finale sera rouge


Voici le code commenté
  • la Classe est déclarée par:

    Uses ClassesManipulationsSysUtilsGraphicsForms;

    Type c_bouncing_ball =
             Class(TInterfacedObject_IManipulationEvents)
               Private
                 FInertiaIInertiaProcessor;
                 FManipulatorIManipulationProcessor;

                 FInertiaCookieFManipulatorCookieLongInt;

                 m_iniertia_completedLongBool;
                 m_c_canvastCanvas;
                 m_mouse_is_downBoolean;
                 m_boundary_widthm_boundary_heightInteger;
               Public
                 m_xm_ym_radiusInteger;
                 // -- IS is required by manipulator
                 m_idInteger;

                 m_with_inertiam_with_manipulatorBoolean;

                 { _IManipulationEvents }
                 Function ManipulationStarted(p_xSinglep_ySingle): HRESULTStdcall;
                 Function ManipulationDelta(p_xSinglep_ySingle
                   translationDeltaXSingle;
                   translationDeltaYSinglescaleDeltaSingleexpansionDeltaSingle;
                   rotationDeltaSinglecumulativeTranslationXSingle;
                   cumulativeTranslationYSinglecumulativeScaleSingle;
                   cumulativeExpansionSinglecumulativeRotationSingle): HRESULT;
                   Stdcall;
                 Function ManipulationCompleted(p_xSinglep_ySingle;
                   cumulativeTranslationXSinglecumulativeTranslationYSingle;
                   cumulativeScaleSinglecumulativeExpansionSingle;
                   cumulativeRotationSingle): HRESULTStdcall;
               Public
                 Constructor create_bouncing_ball(p_widthp_heightInteger;
                     p_inertiap_manipulatorBoolean;
                     p_c_canvastCanvas);
                 Procedure draw_ball(p_colorInteger);

                 Procedure do_mouse_down(p_xp_yInteger);
                 Procedure do_mouse_move(p_xp_yInteger);
                 Procedure do_mouse_up(p_xp_yInteger);

                 Procedure disconnect_engines;
                 Procedure process_inertia;

                 Destructor DestroyOverride;
             End// c_bouncing_ball

  • le Constructor initialise les objets COM et les paramètres des parois

    Constructor c_bouncing_ball.create_bouncing_ball(p_widthp_heightInteger;
        p_inertiap_manipulatorBooleanp_c_canvastCanvas);
    Begin
      Inherited Create;

      m_boundary_width:= p_width;
      m_boundary_height:= p_height;

      m_with_inertia:= p_inertia;
      m_with_manipulator:= p_manipulator;
      m_c_canvas:= p_c_canvas;

      m_x := p_width Div 2;
      m_y := p_height Div 2;
      m_radius := 20;
      m_id := 1;

      m_iniertia_completed := True;

      FInertia := CreateComObject(CLSID_IInertiaProcessorAs IInertiaProcessor;
      FManipulator := CreateComObject(CLSID_IManipulationProcessor
          As IManipulationProcessor;

      InterfaceConnect(FInertia_IManipulationEventsSelfFInertiaCookie);
      InterfaceConnect(FManipulator_IManipulationEventsSelfFManipulatorCookie);

      FInertia.put_DesiredDeceleration(0.001);

      FInertia.put_BoundaryLeft(200);
      FInertia.put_BoundaryTop(200);
      FInertia.put_BoundaryRight(m_boundary_width- 100);
      FInertia.put_BoundaryBottom(m_boundary_height- 100);

      FInertia.put_ElasticMarginLeft(100);
      FInertia.put_ElasticMarginTop(100);
      FInertia.put_ElasticMarginRight(100);
      FInertia.put_ElasticMarginBottom(100);
    End// create_bouncing_ball

  • les événements exigés par _IManipulationEvents sont

    Function c_bouncing_ball.ManipulationStarted(p_xp_ySingle): HRESULT;
      Begin
        Result := S_OK;
      End;

    Function c_bouncing_ball.ManipulationDelta(p_xp_ytranslationDeltaXtranslationDeltaY,
        scaleDeltaexpansionDeltarotationDeltacumulativeTranslationX,
        cumulativeTranslationYcumulativeScalecumulativeExpansion,
        cumulativeRotationSingle): HRESULT;
      Begin
        m_x:= Round(p_x);
        m_y := Round(p_y);
        Result:= S_OK;
      End;

    Function c_bouncing_ball.ManipulationCompleted(p_xp_ycumulativeTranslationX,
        cumulativeTranslationYcumulativeScalecumulativeExpansion,
        cumulativeRotationSingle): HRESULT;
      Begin
        Result := S_OK;
      End;

    ManipulationDelta mettra à jour la position du cercle à partir des information du manipulateur

  • nos déplacements souris appelleront

    Procedure c_bouncing_ball.do_mouse_down(p_xp_yInteger);
      Begin
        FManipulator.ProcessDown(m_idp_xp_y);
        draw_ball(clBlue);
        m_mouse_is_down:= True;
      End// do_mouse_down

    Procedure c_bouncing_ball.do_mouse_move(p_xp_yInteger);
      Begin
        If m_mouse_is_down
          Then Begin
              FManipulator.ProcessMove(m_idp_xp_y);
              draw_ball(clLime);
            End;
      End// do_mouse_move

    Procedure c_bouncing_ball.do_mouse_up(p_xp_yInteger);
      Var l_vxl_vySingle;
      Begin
        FManipulator.ProcessUp(m_idp_xp_y);
        draw_ball(clGreen);

        If m_with_inertia
          Then Begin
              FManipulator.GetVelocityX(l_Vx);
              FManipulator.GetVelocityY(l_Vy);

              FInertia.put_InitialVelocityX(l_Vx);
              FInertia.put_InitialVelocityY(l_Vy);

              FInertia.put_InitialOriginX(m_x);
              FInertia.put_InitialOriginY(m_y);
            End;

        m_iniertia_completed := False;
        m_mouse_is_down:= False;
      End// do_mouse_up

    Ici, c'est ProcessMove qui renseigne le moteur sur la position de notre doigt. Puis ManipulationDelta mettra à jour la position de notre cercle

  • le traitement inertiel est le suivant :

    Procedure c_bouncing_ball.process_inertia;
      Var l_x_velocitysingle;
      Begin
        If m_with_inertia
          Then Begin
              If m_iniertia_completed
                Then draw_ball(clRed)
                Else Begin
                    fInertia.Process(m_iniertia_completed);
                    draw_ball(clYellow);
                  End;
            End;

        FManipulator.GetVelocityX (l_x_velocity);
        If Not m_iniertia_completed
          Then display('vel_x  'FloatToStr(l_x_velocity));
      End// process_inertia

  • et l'affichage du cercle est simplement

    Procedure c_bouncing_ball.draw_ball(p_colorInteger);
      Begin
        m_c_canvas.Brush.Color := p_color;
        m_c_canvas.Ellipse(m_xm_radiusm_ym_radiusm_xm_radiusm_ym_radius);
        DrawFocusRect(m_c_canvas.Handle,
            Rect (100, 100, m_boundary_widthm_boundary_height) );
        DrawFocusRect(m_c_canvas.Handle,
            Rect (200, 200, m_boundary_width- 100, m_boundary_height- 100));
      End;

A présent, voici la forme:
  • la création de la forme et du cercle :

    Var g_area_widthInteger= 0;
        g_c_bouncing_ballc_bouncing_ballNil;

    Procedure TForm1.FormCreate(SenderTObject);
      Begin
        initialize_display(Memo1.Lines);

        // -- disable div by 0 exceptions for the inertia processor
        Set8087CW($133F);

        g_area_width:= Width - Memo1.Width;

        g_c_bouncing_ball :=
            c_bouncing_ball.create_bouncing_ball(
              g_area_width- 100, Height- 100,
              inertia_.Checkedmanipulator_.CheckedCanvas);
      End// FormCreate

  • les événements souris (provoqués par la souris, ou, dans notre cas, le doigt):

    Procedure TForm1.FormMouseDown(SenderTObjectButtonTMouseButton;
        ShiftTShiftStateXYInteger);
      Begin
        display('> FormMouseDown');
        Timer1.Enabled := False;
        g_c_bouncing_ball.do_mouse_down(XY);

        // -- clear the scree before each move
        Invalidate;
      End// FormMouseDown

    Procedure TForm1.FormMouseMove(SenderTObjectShiftTShiftStateX,
        YInteger);
      Begin
        display('  FormMouseMove');
        g_c_bouncing_ball.do_mouse_move(XY);
        If invalidate_.Checked
          Then Invalidate;
      End// FormMouseMove

    Procedure TForm1.FormMouseUp(SenderTObjectButtonTMouseButton;
        ShiftTShiftStateXYInteger);
      Begin
        display('< FormMouseUP');
        g_c_bouncing_ball.do_mouse_up(XY);

        If invalidate_.Checked
          Then Invalidate;

        Timer1.Enabled := True;
      End// FormMouseUp

  • et le traitement du timer qui affiche la balle en jaune et interroge la vélocité inertielle :

    Procedure TForm1.Timer1Timer(SenderTObject);
      Begin
        g_c_bouncing_ball.process_inertia;

        If invalidate_.Checked
          Then Invalidate
      End// Timer1Timer



Voici un exemple ou nous faisons un arc en accélérant de la droite vers la gauche (vert), et l'inertie qui poursuit le mouvement en ralentissant (jaune) :

touch_manipulator_inertia_engine



Notez que

  • plus les points sont rapprochés, plus le mouvement est lent



5 - Améliorations et Remarques

Il reste d'autres points à examiner
  • la gestion de Direct2d et les WIC images. Ces possibilités permettent les manipulations d'images à l'écran (déplacements, agrandissements etc)
  • la gestion du clavier à l'écran


Mentionnons aussi que nous avions commencé à essayer nos programmes de gestes en Delphi XE2, puis avons basculé en XE3 pour le multi-touch. Certaines adaptations ont été nécessaires dans les primitives de conversion wm_Touch. Il est possible que ces librairies subissent d'autres changements pour leur adaptation / intégration à FireMonkey, iOs, Android etc.



Ces techniques s'avèreront fondamentales pour la gestion des téléphones et des tablettes.




6 - Télécharger le code source Delphi

Vous pouvez télécharger:

Comme d'habitude:
  • nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections qui en résulteront pourront aider les prochains lecteurs
  • tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même les bienvenus à jcolibri@jcolibri.com.
  • plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en cliquant "envoyer" :
    Nom :
    E-mail :
    Commentaires * :
     

  • et si vous avez apprécié cet article, faites connaître notre site, ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.



7 - Références

Les codes sources ont été inspirés par les articles suivants;

Mentionnons aussi que nous avions commencé par tester les gestes simples
  • Gestes Delphi
      John COLIBRI - Déc 2012
    • (== "Delphi Gestures" =="mouvements delphi") : gestion Delphi des mouvements effectués par un utilisateur avec le doigt sur un éran tactile



8 - L'auteur

John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le conseil (composants, architecture, test) et la formation. Son site contient des articles avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client.
Created: jan-04. Last updated: mar-2020 - 250 articles, 620 .ZIP sources, 3303 figures
Contact : John COLIBRI - Tel: 01.42.83.69.36 / 06.87.88.23.91 - email:jcolibri@jcolibri.com
Copyright © J.Colibri   http://www.jcolibri.com - 2001 - 2020
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
    + web_internet_sockets
    + services_web_
    + prog_objet_composants
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
    + graphique
    + delphi
    + outils
    + firemonkey
    + vcl_rtl
      – gestes_delphi
      – delphi_vcl_styles
      – anonymous_methods
      – ecran_tactile_delphi
    + colibri_helpers
    + colibri_skelettons
    + admin
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog