#include "nx_touch.h" #define TAP_MOVEMENT_GAP 20 #define VERTICAL_SWIPE_HORIZONTAL_PLAY 250 #define VERTICAL_SWIPE_MINIMUM_DISTANCE 300 #define HORIZONTAL_SWIPE_VERTICAL_PLAY 250 #define HORIZONTAL_SWIPE_MINIMUM_DISTANCE 300 #define distance(x1, y1, x2, y2) (int) sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1))) struct touchInfo_s touchInfo; void touchInit(void) { touchInfo.gestureInProgress = false; touchInfo.isTap = true; touchInfo.initMenuXPos = 0; touchInfo.initMenuIndex = 0; touchInfo.lastSlideSpeed = 0; hidInitializeTouchScreen(); } void handleTappingOnApp(menu_s* menu, int px) { int i = 0; menuEntry_s *me = NULL; ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList]; for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) { int entry_start_x = layoutobj->posStart[0] + i * layoutobj->posEnd[0]; int screen_width = 1280; if (entry_start_x >= (screen_width - menu->xPos)) break; if (px >= (entry_start_x + menu->xPos) && px <= (entry_start_x + menu->xPos) + layoutobj->size[0]) { launchMenuEntryTask(me); break; } } } void handleTappingOnOpenLaunch(menu_s* menu) { if (menu->nEntries > 0) { int i; menuEntry_s* me; for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next); launchMenuEntryTask(me); } } static inline bool checkInsideTextLayoutObject(ThemeLayoutId id, int x, int y) { ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[id]; if (!layoutobj->visible) return false; return x > layoutobj->posFinal[0] && x < layoutobj->posFinal[0]+(layoutobj->touchSize[0] ? layoutobj->touchSize[0] : layoutobj->textSize[0]) && y > layoutobj->posFinal[1]-layoutobj->touchSize[1] && y < layoutobj->posFinal[1]; } void handleTouch(menu_s* menu) { ThemeLayoutObject *layoutobj = NULL; HidTouchScreenState touch = {0}; hidGetTouchScreenStates(&touch, 1); layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles]; int entries_count = layoutobj->posEnd[0]; layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList]; // On touch start. if (touch.count == 1 && !touchInfo.gestureInProgress) { touchInfo.gestureInProgress = true; touchInfo.firstTouch = touch.touches[0]; touchInfo.prevTouch = touch.touches[0]; touchInfo.isTap = true; touchInfo.initMenuXPos = menu->xPos; touchInfo.initMenuIndex = menu->curEntry; touchInfo.lastSlideSpeed = 0; menu->slideSpeed = 0; } // On touch moving. else if (touch.count >= 1 && touchInfo.gestureInProgress) { touchInfo.lastSlideSpeed = ((int)(touch.touches[0].x - touchInfo.prevTouch.x)); touchInfo.prevTouch = touch.touches[0]; if (touchInfo.isTap && (abs(touchInfo.firstTouch.x - touch.touches[0].x) > TAP_MOVEMENT_GAP || abs(touchInfo.firstTouch.y - touch.touches[0].y) > TAP_MOVEMENT_GAP)) { touchInfo.isTap = false; } if (!menuIsMsgBoxOpen() && touchInfo.firstTouch.y > layoutobj->posStart[1] && touchInfo.firstTouch.y < layoutobj->posStart[1]+layoutobj->size[1] && !touchInfo.isTap && menu->nEntries > entries_count) { if (!touchInfo.isTap) { menu->slideSpeed = touchInfo.lastSlideSpeed; } } } // On touch end. else if (touchInfo.gestureInProgress) { int x1 = touchInfo.firstTouch.x; int y1 = touchInfo.firstTouch.y; int x2 = touchInfo.prevTouch.x; int y2 = touchInfo.prevTouch.y; if (!touchInfo.isTap) { menu->slideSpeed = touchInfo.lastSlideSpeed; } bool netloader_active = menuIsNetloaderActive(); if (menuIsMsgBoxOpen() && !netloader_active) { layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator]; MessageBox currMsgBox = menuGetCurrentMsgBox(); int start_x = 1280 / 2 - currMsgBox.width / 2; int start_y = (720 / 2 - currMsgBox.height / 2) + currMsgBox.height; int end_x = start_x + currMsgBox.width; int end_y = start_y; start_y+= layoutobj->posStart[1]; if (x1 > start_x && x1 < end_x && y1 > start_y && y1 < end_y && touchInfo.isTap) { menuCloseMsgBox(); } } else if (touchInfo.isTap && !netloader_active) { // App Icons if (y1 > layoutobj->posStart[1] && y1 < layoutobj->posStart[1]+layoutobj->size[1]) { handleTappingOnApp(menu, touchInfo.prevTouch.x); } // Bottom Buttons else { // Back Button if (checkInsideTextLayoutObject(ThemeLayoutId_ButtonB, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonBText, x1, y1)) { launchMenuBackTask(); } // Open/Launch Button else if (menu->nEntries != 0 && (checkInsideTextLayoutObject(ThemeLayoutId_ButtonA, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonAText, x1, y1))) { handleTappingOnOpenLaunch(menu); } // Star Button else if (menu->nEntries != 0) { int i; menuEntry_s* me; for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next); if (me->type != ENTRY_TYPE_THEME && (checkInsideTextLayoutObject(ThemeLayoutId_ButtonX, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonXText, x1, y1))) { menuHandleXButton(); } } } } // Vertical Swipe else if (abs(x1 - x2) < VERTICAL_SWIPE_HORIZONTAL_PLAY && distance(x1, y1, x2, y2) > VERTICAL_SWIPE_MINIMUM_DISTANCE) { // Swipe up to go back if (y1 - y2 > 0) { launchMenuBackTask(); } // Swipe down to go into netloader else if (y1 - y2 < 0) { launchMenuNetloaderTask(); } } // Horizontal Swipe else if (y1 < layoutobj->posStart[1] && y2 < layoutobj->posStart[1]) { if (abs(y1 - y2) < HORIZONTAL_SWIPE_VERTICAL_PLAY && distance(x1, y1, x2, y2) > HORIZONTAL_SWIPE_MINIMUM_DISTANCE) { // Swipe left to go into theme-menu if (x1 - x2 > 0) { themeMenuStartup(); } } } touchInfo.gestureInProgress = false; } }