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.
274 lines
9.2 KiB
274 lines
9.2 KiB
2 years ago
|
/*
|
||
|
* 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
|