SDL2でタイマー処理を実行します。タイマー処理を用いてダブルクリックとロングクリックを実装します。
Table of Contents
1 SDL_TimerCallback
タイマー処理で実行される関数の型です。
typedef Uint32 (SDLCALL * SDL_TimerCallback) (Uint32 interval, void *param);
2 SDL_AddTimer
SDL_AddTimerでSDL_TimerCallback関数をタイマーに登録します。
SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void* param)
SDL_TimerCallback関数を実行する時間をintervalでミリ秒指定します。
paramはSDL_TimerCallback関数の第二引数に渡すアドレスです。
戻り値はSDL_RemoveTimerで利用します。
3 SDL_RemoveTimer
SDL_RemoveTimerで登録されたSDL_TimerCallback関数を削除します。
SDL_bool SDL_RemoveTimer(SDL_TimerID id)
4 SDL2のクリックイベント
マウスクリックイベントの種類は以下の通りです(event.typeを参照)。
SDL_MOUSEBUTTONDOWN | マウスクリック時に発生 |
SDL_MOUSEMOTION | マウスのモーション時に発生 |
SDL_MOUSEBUTTONUP | マウスクリックを解除した際に発生 |
座標はevent.button.xとevent.button.yで参照できます。
switch (event.type) { case SDL_MOUSEBUTTONDOWN: x = event.button.x; y = event.button.y; break; case SDL_MOUSEMOTION: break; case SDL_MOUSEBUTTONUP: break; default: break; }
タッチパネルのタッチイベントはSDL_FINGERDOWNやevent.tfinger.xを用いて実現できます。以降ではマウスクリックイベントのみを扱います。
5 ロングクリック
ロングクリックはクリックイベント発生から一定時間経過後に発生されるイベントです。ロングクリックイベント発生前にクリックが解除された場合はキャンセルされます。
ロングクリック判定用フラグを有効にするSDL_TimerCallback関数を用意し、SDL_MOUSEBUTTONDOWNでタイマーを登録、SDL_MOUSEBUTTONUPでタイマーを解除します。
6 ダブルクリック
ダブルクリックはクリックイベント発生から一定時間以内に再度クリックイベントが発生した際に発生するイベントです。
ダブルクリック判定用フラグを無効にするSDL_TimerCallback関数を用意し、SDL_MOUSEBUTTONDOWNでダブルクリック判定用フラグが無効な場合はタイマーを登録、SDL_MOUSEBUTTONDOWNでダブルクリック判定用フラグが有効な場合はタイマーを解除します。
7 サンプルプログラム
PNG画像を表示し、PNG画像をロングクリックした際はドラッグして座標を移動、PNG画像をダブルクリックした際は色調を変えるサンプルプログラムです。
イベントを受信するオブジェクトが増えた場合の為に、フレームワークは要改善ですが、説明を簡易にする為に簡略化している点をご了承下さい。
static SpriteObject *gSprite = nullptr; static Uint32 enableLongClick(Uint32 interval, void *param) { bool *longClicked = (bool *) param; *longClicked = true; return 0; } static Uint32 disableDoubleClick(Uint32 interval, void *param) { bool *doubleClicked = (bool *) param; *doubleClicked = false; return 0; } static bool contains(int x, int y) { SDL_Rect *rect = gSprite->getRect(); return x >= rect->x && x < rect->x + rect->w && y >= rect->y && y < rect->y + rect->h; } static void move(int fromX, int fromY, int toX, int toY) { SDL_Rect *rect = gSprite->getRect(); rect->x += toX - fromX; rect->y += toY - fromY; } static void reverse() { static SDL_Color dark = { 128, 128, 128, 254 }; static SDL_Color light = { 254, 254, 254, 254 }; static bool darked = false; if (darked) { darked = false; gSprite->setAlphaAndColor(light); } else { darked = true; gSprite->setAlphaAndColor(dark); } } static bool input() { static SDL_TimerID longClickID, doubleClickID; static bool longClicked = false, doubleClicked = false; static int x, y; SDL_Event event; /** NOTE: SDL_PollEvent does not sleep while SDL_WaitEvent sleep till event comes. SDL_WaitEvent is more relaxible than SDL_PollEvent. If input is combined with draw, SDL_WaitEvent cannot be used. */ while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) return true; if (!contains(event.button.x, event.button.y)) { /** focus out */ doubleClicked = false; longClicked = false; continue; } switch (event.type) { case SDL_MOUSEBUTTONDOWN: if (doubleClicked) { doubleClicked = false; /** double click event */ reverse(); SDL_RemoveTimer(doubleClickID); } else { doubleClicked = true; longClickID = SDL_AddTimer(1000, enableLongClick, &longClicked); doubleClickID = SDL_AddTimer(1000, disableDoubleClick, &doubleClicked); x = event.button.x; y = event.button.y; } break; case SDL_MOUSEMOTION: if (longClicked) { /** long click event. */ move(x, y, event.button.x, event.button.y); x = event.button.x; y = event.button.y; } break; case SDL_MOUSEBUTTONUP: longClicked = false; SDL_RemoveTimer(longClickID); break; default: break; } } return false; } int main(int argc, char *argv[]) { Window window(SDL_WINDOW_TITLE, SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT); gRenderer = window.getRenderer(); //AlphaAndColorModThread thread(gRenderer); Font::initialize(); gSprite = new SpriteObject("a.png", 10, 10, 100, 100); gRenderer->send(gSprite); while (1) if (input()) break; Font::finalize(); //thread.stop(); return 0; }