You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
9.2 KiB
273 lines
9.2 KiB
/* |
|
* EEZ Modular Firmware |
|
* Copyright (C) 2015-present, Envox d.o.o. |
|
* |
|
* This program is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation, either version 3 of the License, or |
|
* (at your option) any later version. |
|
|
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
|
|
* You should have received a copy of the GNU General Public License |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
#include <stdio.h> |
|
|
|
#include <eez/conf.h> |
|
#include <eez/core/os.h> |
|
#include <eez/core/hmi.h> |
|
|
|
#if OPTION_MOUSE |
|
#include <eez/core/mouse.h> |
|
#endif |
|
|
|
#include <eez/core/util.h> |
|
|
|
#include <eez/gui/gui.h> |
|
#include <eez/gui/thread.h> |
|
|
|
#define CONF_GUI_LONG_TOUCH_TIMEOUT_MS 1000 |
|
#define CONF_GUI_KEYPAD_FIRST_AUTO_REPEAT_DELAY_MS 300 |
|
#define CONF_GUI_KEYPAD_NEXT_AUTO_REPEAT_DELAY_MS 50 |
|
#define CONF_GUI_EXTRA_LONG_TOUCH_TIMEOUT_MS 15000 |
|
|
|
namespace eez { |
|
namespace gui { |
|
|
|
//////////////////////////////////////////////////////////////////////////////// |
|
|
|
WidgetCursor g_activeWidget; |
|
bool g_isLongTouch; |
|
|
|
static WidgetCursor g_foundWidgetAtDown; |
|
|
|
static bool g_touchActionExecuted; |
|
static bool g_touchActionExecutedAtDown; |
|
|
|
static OnTouchFunctionType g_onTouchFunction; |
|
|
|
static bool g_longTouchGenerated; |
|
static bool g_extraLongTouchGenerated; |
|
|
|
//////////////////////////////////////////////////////////////////////////////// |
|
|
|
static void processTouchEvent(EventType type, Event &touchEvent); |
|
static void onPageTouch(const WidgetCursor &foundWidget, Event &touchEvent); |
|
static void onWidgetDefaultTouch(const WidgetCursor &widgetCursor, Event &touchEvent); |
|
static void onWidgetTouch(const WidgetCursor &widgetCursor, Event &touchEvent); |
|
|
|
//////////////////////////////////////////////////////////////////////////////// |
|
|
|
void onTouchEvent(bool pressed, int x, int y, Event &lastEvent) { |
|
auto eventType = lastEvent.type; |
|
if (pressed) { |
|
if (lastEvent.type == EVENT_TYPE_TOUCH_NONE || lastEvent.type == EVENT_TYPE_TOUCH_UP) { |
|
eventType = EVENT_TYPE_TOUCH_DOWN; |
|
} else { |
|
if (lastEvent.type == EVENT_TYPE_TOUCH_DOWN) { |
|
eventType = EVENT_TYPE_TOUCH_MOVE; |
|
} |
|
} |
|
} else { |
|
if (lastEvent.type == EVENT_TYPE_TOUCH_DOWN || lastEvent.type == EVENT_TYPE_TOUCH_MOVE) { |
|
eventType = EVENT_TYPE_TOUCH_UP; |
|
} else if (lastEvent.type == EVENT_TYPE_TOUCH_UP) { |
|
eventType = EVENT_TYPE_TOUCH_NONE; |
|
} |
|
} |
|
|
|
if (eventType != lastEvent.type || x != lastEvent.x || y != lastEvent.y) { |
|
lastEvent.type = eventType; |
|
lastEvent.x = x; |
|
lastEvent.y = y; |
|
if (lastEvent.type != EVENT_TYPE_TOUCH_NONE) { |
|
sendTouchEventToGuiThread(lastEvent); |
|
} |
|
} else { |
|
static const uint32_t SEND_TOUC_MOVE_EVENT_EVERY_MS = 20; |
|
if (lastEvent.type == EVENT_TYPE_TOUCH_MOVE) { |
|
auto time = millis(); |
|
if (time - lastEvent.time > SEND_TOUC_MOVE_EVENT_EVERY_MS) { |
|
sendTouchEventToGuiThread(lastEvent); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void processTouchEvent(Event &touchEvent) { |
|
if (g_hooks.isEventHandlingDisabled()) { |
|
return; |
|
} |
|
|
|
static uint32_t m_lastAutoRepeatEventTimeMs; |
|
static uint32_t m_touchDownTimeMs; |
|
|
|
if (touchEvent.type != EVENT_TYPE_TOUCH_NONE) { |
|
uint32_t tickCountMs = millis(); |
|
|
|
eez::hmi::noteActivity(); |
|
|
|
if (touchEvent.type == EVENT_TYPE_TOUCH_DOWN) { |
|
m_touchDownTimeMs = tickCountMs; |
|
m_lastAutoRepeatEventTimeMs = tickCountMs; |
|
g_longTouchGenerated = false; |
|
g_extraLongTouchGenerated = false; |
|
processTouchEvent(EVENT_TYPE_TOUCH_DOWN, touchEvent); |
|
} else if (touchEvent.type == EVENT_TYPE_TOUCH_MOVE) { |
|
processTouchEvent(EVENT_TYPE_TOUCH_MOVE, touchEvent); |
|
|
|
if (!g_longTouchGenerated && int32_t(tickCountMs - m_touchDownTimeMs) >= CONF_GUI_LONG_TOUCH_TIMEOUT_MS) { |
|
g_longTouchGenerated = true; |
|
processTouchEvent(EVENT_TYPE_LONG_TOUCH, touchEvent); |
|
} |
|
|
|
if (g_longTouchGenerated && !g_extraLongTouchGenerated && int32_t(tickCountMs - m_touchDownTimeMs) >= CONF_GUI_EXTRA_LONG_TOUCH_TIMEOUT_MS) { |
|
g_extraLongTouchGenerated = true; |
|
g_touchActionExecuted = true; |
|
int action = g_hooks.getExtraLongTouchAction(); |
|
if (action != ACTION_ID_NONE) { |
|
executeAction(WidgetCursor(), action); |
|
} |
|
} |
|
|
|
if (int32_t(tickCountMs - m_lastAutoRepeatEventTimeMs) >= (m_lastAutoRepeatEventTimeMs == m_touchDownTimeMs ? CONF_GUI_KEYPAD_FIRST_AUTO_REPEAT_DELAY_MS : CONF_GUI_KEYPAD_NEXT_AUTO_REPEAT_DELAY_MS)) { |
|
processTouchEvent(EVENT_TYPE_AUTO_REPEAT, touchEvent); |
|
m_lastAutoRepeatEventTimeMs = tickCountMs; |
|
} |
|
} else if (touchEvent.type == EVENT_TYPE_TOUCH_UP) { |
|
processTouchEvent(EVENT_TYPE_TOUCH_UP, touchEvent); |
|
} |
|
} |
|
} |
|
|
|
static void processTouchEvent(EventType type, Event &touchEvent) { |
|
if (type == EVENT_TYPE_TOUCH_DOWN) { |
|
g_foundWidgetAtDown = findWidget(touchEvent.x, touchEvent.y); |
|
g_onTouchFunction = getWidgetTouchFunction(g_foundWidgetAtDown); |
|
if (!g_onTouchFunction) { |
|
g_onTouchFunction = onPageTouch; |
|
} |
|
} else if (type == EVENT_TYPE_TOUCH_UP) { |
|
g_activeWidget = 0; |
|
} |
|
|
|
if (g_onTouchFunction) { |
|
Event event = touchEvent; |
|
event.type = type; |
|
g_onTouchFunction(g_foundWidgetAtDown, event); |
|
} |
|
} |
|
|
|
OnTouchFunctionType getWidgetTouchFunction(const WidgetCursor &widgetCursor) { |
|
if (widgetCursor) { |
|
auto action = getWidgetAction(widgetCursor); |
|
if (action && !widgetCursor.appContext->isWidgetActionEnabled(widgetCursor)) { |
|
return nullptr; |
|
} |
|
|
|
if (widgetCursor.currentState->hasOnTouch()) { |
|
return onWidgetTouch; |
|
} |
|
|
|
if (widgetCursor.appContext->isWidgetActionEnabled(widgetCursor)) { |
|
return onWidgetDefaultTouch; |
|
} |
|
|
|
return g_hooks.getWidgetTouchFunction(widgetCursor); |
|
} |
|
|
|
return nullptr; |
|
} |
|
|
|
static void onPageTouch(const WidgetCursor &foundWidget, Event &touchEvent) { |
|
if (foundWidget.appContext) { |
|
foundWidget.appContext->onPageTouch(foundWidget, touchEvent); |
|
} else { |
|
getRootAppContext()->onPageTouch(foundWidget, touchEvent); |
|
} |
|
} |
|
|
|
static void onWidgetDefaultTouch(const WidgetCursor &widgetCursor, Event &touchEvent) { |
|
if (!widgetCursor.widget) { |
|
return; |
|
} |
|
|
|
auto action = getWidgetAction(widgetCursor); |
|
|
|
if (touchEvent.type == EVENT_TYPE_TOUCH_DOWN) { |
|
g_touchActionExecuted = false; |
|
g_touchActionExecutedAtDown = false; |
|
|
|
if (action == ACTION_ID_DRAG_OVERLAY) { |
|
dragOverlay(touchEvent); |
|
g_activeWidget = widgetCursor; |
|
} else if (widgetCursor.appContext->testExecuteActionOnTouchDown(action)) { |
|
executeAction(widgetCursor, action); |
|
g_touchActionExecutedAtDown = true; |
|
if (widgetCursor.appContext->isAutoRepeatAction(action)) { |
|
g_activeWidget = widgetCursor; |
|
} |
|
} else { |
|
g_activeWidget = widgetCursor; |
|
} |
|
} else if (touchEvent.type == EVENT_TYPE_TOUCH_MOVE) { |
|
if (action == ACTION_ID_DRAG_OVERLAY) { |
|
dragOverlay(touchEvent); |
|
} |
|
} else if (touchEvent.type == EVENT_TYPE_AUTO_REPEAT) { |
|
if (widgetCursor.appContext->isWidgetActionEnabled(widgetCursor) && widgetCursor.appContext->isAutoRepeatAction(action)) { |
|
g_touchActionExecuted = true; |
|
executeAction(widgetCursor, action); |
|
} |
|
} else if (touchEvent.type == EVENT_TYPE_LONG_TOUCH) { |
|
g_touchActionExecuted = true; |
|
int action = widgetCursor.appContext->getLongTouchActionHook(widgetCursor); |
|
if (action != ACTION_ID_NONE) { |
|
g_isLongTouch = true; |
|
executeAction(widgetCursor, action); |
|
g_isLongTouch = false; |
|
} |
|
} else if (touchEvent.type == EVENT_TYPE_TOUCH_UP) { |
|
if (!g_touchActionExecutedAtDown) { |
|
if (!g_touchActionExecuted) { |
|
if (action == ACTION_ID_DRAG_OVERLAY) { |
|
dragOverlay(touchEvent); |
|
} else { |
|
executeAction(widgetCursor, action); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
static void onWidgetTouch(const WidgetCursor &widgetCursor, Event &touchEvent) { |
|
if (widgetCursor) { |
|
if (touchEvent.type == EVENT_TYPE_TOUCH_DOWN) { |
|
g_activeWidget = g_foundWidgetAtDown; |
|
} |
|
widgetCursor.currentState->onTouch(widgetCursor, touchEvent); |
|
} |
|
} |
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////// |
|
|
|
WidgetCursor &getFoundWidgetAtDown() { |
|
return g_foundWidgetAtDown; |
|
} |
|
|
|
void setFoundWidgetAtDown(WidgetCursor &widgetCursor) { |
|
g_foundWidgetAtDown = widgetCursor; |
|
} |
|
|
|
void clearFoundWidgetAtDown() { |
|
g_foundWidgetAtDown = 0; |
|
} |
|
|
|
} // namespace gui |
|
} // namespace eez
|
|
|