Implementacja przeciągania

2011-08-08 20:19

Przedstawię dzisiaj dość elementarną technikę, związaną z szeroką pojętym programowaniem grafiki – a zwłaszcza interfejsów użytkownika. Chodzi o nic innego jak przeciąganie (dragging), czyli przemieszczanie różnych elementów za pomocą myszy czy innego urządzenia wskazującego (np. palca ;]). Celowo nie dodałem tutaj drugiej połowy procesu znanego jako drag & drop – czyli upuszczania – bo nie mam w ogóle na myśli potencjalnie zaawansowanej logiki związanej z wymianą danych reprezentowanych przez przeciągany obiekt. Przeciwnie; chodzi mi wyłącznie o samo przesuwanie go po ekranie. Rezultatem (niekoniecznie jedynym, rzecz jasna) ma być po prostu zmiana jego położenia.

W zaawansowanych systemach GUI rozwiązanie sprowadza się najczęściej do instrukcji w rodzaju obj.Draggable = true;, a cała reszta odbywa się “automagicznie”. Załóżmy jednak, że nie mamy zaawansowanego systemu GUI i dysponujemy jedynie możliwością rysowania oraz odbierania zdarzeń od wskaźnika myszy. Nie jest to wcale rzadka sytuacja: żeby nie szukać daleko, wystarczy cofnąć się o tydzień do opisu elementu <canvas> z HTML5 :) W ogólności dotyczy to jakiejkolwiek “czystej” biblioteki graficznej: OpenGL, DirectX, SDL, itp.
Doprecyzujmy jeszcze to, iż “odbieranie zdarzeń od myszy” obejmuje tak naprawdę poniższe trzy zdarzenia (lub ich odpowiedniki dla innych – np. dotykowych – sposobów wskazywania):

  • wciśnięcie przycisku myszy – zdarzenie odbierane zwykle jako MouseDown
  • ruch wskaźnikiem – MouseMove
  • zwolnienie przycisku – MouseUp

Nietrudno zauważyć, że całkowicie wystarczają one do implementacji przeciągania. Jak więc miałaby ona wyglądać?

Zacznijmy od niezbędnych danych. Oprócz jednej oczywistej – przeciąganego obiektu – będziemy potrzebowali też przynajmniej ostatniej pozycji wskaźnika; pozwoli nam to na obliczanie odpowiedniego przesunięcia. W pseudokodzie odpowiada to deklaracji poniższych dwóch zmiennych:

  1. object dragged = null;
  2. point lastDragPos;

Można by do nich dodać jeszcze trzecią: boolowską flagę informującą o tym, czy w ogóle coś przeciągamy. W praktyce nie jest to potrzebne, jeśli referencja do przeciąganego obiektu (dragged) może być pusta – a tak jest w każdym sensownym języku programowania.

Przechodząc do bardziej dynamicznego kodu, zajmijmy się obsługą początku i końca przeciągania. Musimy w tym celu dysponować sposobem na określenie, w jaki obiekt (i czy jakikolwiek) trafia podany punkt na ekranie. Operacja ta jest określana jako hit test, a jej złożoność zależy głównie od kształtu naszych obiektów. Ponieważ jednak nie ma tutaj miejsca na wdawanie szczegóły, pozwolę sobie po prostu założyć, że całą pracę wykonuje dana z góry funkcja HitTest :) Wówczas obsługa zdarzenia MouseDown może wyglądać następująco:

  1. function MouseDown(int x, int y) {
  2.     dragged = HitTest(x, y);
  3.     lastDragPos = point(x, y);
  4. }

HitTest zwróci nam “trafiony” obiekt (lub null), który od tej pory będzie przeciągany. Przemieszczanie zakończy się zaś wtedy, gdy przycisk myszy zostanie zwolniony, co odpowiada zdarzeniu MouseUp:

  1. function MouseUp(int x, int y) {
  2.     dragged = null;
  3. }

Najwięcej logiki znajduje się w obsłudze zdarzenia MouseMove, bo to właśnie tam dokonuje się właściwe przesuwanie obiektu. Wymaga ono obliczenia różnicy między obecną a poprzednią pozycją kursora i uaktualnienia tej pierwszej:

  1. function MouseMove(int x, int y) {
  2.     if (dragged == null) return;
  3.  
  4.     int dx = x - lastPos.x, dy = y - lastPos.y;
  5.     dragged.x += dx;
  6.     dragged.y += dy;
  7.  
  8.     lastPos = point(x, y);
  9. }

Uprzednio należy jeszcze sprawdzić, czy faktycznie cokolwiek przeciągamy. Normalnie bowiem MouseMove uruchamia się dla każdego ruchu wskaźnika, niezależnie od stanu wciśnięcia przycisków.

Tags: , ,
Author: Xion, posted under Programming »


One comment for post “Implementacja przeciągania”.
  1. goodAdviceUncle:
    August 12th, 2011 o 19:31

    można powiedzieć, że w szoku nie jesteśmy
    ; )

Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.