Basic mappable gesture
This commit is contained in:
parent
57dc6be2dc
commit
ad29e5237e
6 changed files with 196 additions and 0 deletions
|
@ -551,6 +551,14 @@ static ConfigSetting generalSettings[] = {
|
||||||
ConfigSetting("RightAnalogPress", &g_Config.iRightAnalogPress, 0, true, true),
|
ConfigSetting("RightAnalogPress", &g_Config.iRightAnalogPress, 0, true, true),
|
||||||
ConfigSetting("RightAnalogCustom", &g_Config.bRightAnalogCustom, false, true, true),
|
ConfigSetting("RightAnalogCustom", &g_Config.bRightAnalogCustom, false, true, true),
|
||||||
ConfigSetting("RightAnalogDisableDiagonal", &g_Config.bRightAnalogDisableDiagonal, false, true, true),
|
ConfigSetting("RightAnalogDisableDiagonal", &g_Config.bRightAnalogDisableDiagonal, false, true, true),
|
||||||
|
ConfigSetting("SwipeUp", &g_Config.iSwipeUp, 0, true, true),
|
||||||
|
ConfigSetting("SwipeDown", &g_Config.iSwipeDown, 0, true, true),
|
||||||
|
ConfigSetting("SwipeLeft", &g_Config.iSwipeLeft, 0, true, true),
|
||||||
|
ConfigSetting("SwipeRight", &g_Config.iSwipeRight, 0, true, true),
|
||||||
|
ConfigSetting("SwipeSensitivity", &g_Config.fSwipeSensitivity, 1.0f, true, true),
|
||||||
|
ConfigSetting("SwipeSmoothing", &g_Config.fSwipeSmoothing, 0.3f, true, true),
|
||||||
|
ConfigSetting("DoubleTapGesture", &g_Config.iDoubleTapGesture, 0, true, true),
|
||||||
|
ConfigSetting("GestureControlEnabled", &g_Config.bGestureControlEnabled, false, true, true),
|
||||||
|
|
||||||
// "default" means let emulator decide, "" means disable.
|
// "default" means let emulator decide, "" means disable.
|
||||||
ConfigSetting("ReportingHost", &g_Config.sReportHost, "default"),
|
ConfigSetting("ReportingHost", &g_Config.sReportHost, "default"),
|
||||||
|
|
|
@ -320,6 +320,16 @@ public:
|
||||||
bool bRightAnalogCustom;
|
bool bRightAnalogCustom;
|
||||||
bool bRightAnalogDisableDiagonal;
|
bool bRightAnalogDisableDiagonal;
|
||||||
|
|
||||||
|
// Motion gesture controller
|
||||||
|
bool bGestureControlEnabled;
|
||||||
|
int iSwipeUp;
|
||||||
|
int iSwipeDown;
|
||||||
|
int iSwipeLeft;
|
||||||
|
int iSwipeRight;
|
||||||
|
float fSwipeSensitivity;
|
||||||
|
float fSwipeSmoothing;
|
||||||
|
int iDoubleTapGesture;
|
||||||
|
|
||||||
// Disable diagonals
|
// Disable diagonals
|
||||||
bool bDisableDpadDiagonals;
|
bool bDisableDpadDiagonals;
|
||||||
bool bGamepadOnlyFocused;
|
bool bGamepadOnlyFocused;
|
||||||
|
|
|
@ -729,6 +729,10 @@ void GameSettingsScreen::CreateViews() {
|
||||||
static const char *touchControlStyles[] = {"Classic", "Thin borders", "Glowing borders"};
|
static const char *touchControlStyles[] = {"Classic", "Thin borders", "Glowing borders"};
|
||||||
View *style = controlsSettings->Add(new PopupMultiChoice(&g_Config.iTouchButtonStyle, co->T("Button style"), touchControlStyles, 0, ARRAY_SIZE(touchControlStyles), co->GetName(), screenManager()));
|
View *style = controlsSettings->Add(new PopupMultiChoice(&g_Config.iTouchButtonStyle, co->T("Button style"), touchControlStyles, 0, ARRAY_SIZE(touchControlStyles), co->GetName(), screenManager()));
|
||||||
style->SetEnabledPtr(&g_Config.bShowTouchControls);
|
style->SetEnabledPtr(&g_Config.bShowTouchControls);
|
||||||
|
controlsSettings->Add(new Choice(co->T("Gesture mapping")))->OnClick.Add([=](EventParams &e) {
|
||||||
|
screenManager()->push(new GestureMappingScreen());
|
||||||
|
return UI::EVENT_DONE;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
controlsSettings->Add(new ItemHeader(co->T("Keyboard", "Keyboard Control Settings")));
|
controlsSettings->Add(new ItemHeader(co->T("Keyboard", "Keyboard Control Settings")));
|
||||||
|
@ -1989,3 +1993,32 @@ void HostnameSelectScreen::OnCompleted(DialogResult result) {
|
||||||
if (result == DR_OK)
|
if (result == DR_OK)
|
||||||
*value_ = StripSpaces(addrView_->GetText());
|
*value_ = StripSpaces(addrView_->GetText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GestureMappingScreen::CreateViews() {
|
||||||
|
using namespace UI;
|
||||||
|
|
||||||
|
auto di = GetI18NCategory("Dialog");
|
||||||
|
auto co = GetI18NCategory("Controls");
|
||||||
|
auto mc = GetI18NCategory("MappableControls");
|
||||||
|
|
||||||
|
root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
|
||||||
|
Choice *back = new Choice(di->T("Back"), "", false, new AnchorLayoutParams(130, WRAP_CONTENT, 10, NONE, NONE, 10));
|
||||||
|
root_->Add(back)->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
|
||||||
|
TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, 140, new AnchorLayoutParams(10, 0, 10, 0, false));
|
||||||
|
root_->Add(tabHolder);
|
||||||
|
ScrollView *rightPanel = new ScrollView(ORIENT_VERTICAL);
|
||||||
|
tabHolder->AddTab(co->T("Gesture"), rightPanel);
|
||||||
|
LinearLayout *vert = rightPanel->Add(new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT)));
|
||||||
|
vert->SetSpacing(0);
|
||||||
|
|
||||||
|
static const char *gestureButton[] = {"None", "L", "R", "Square", "Triangle", "Circle", "Cross", "D-pad up", "D-pad down", "D-pad left", "D-pad right", "Start", "Select"};
|
||||||
|
vert->Add(new CheckBox(&g_Config.bGestureControlEnabled, co->T("Enable gesture control")));
|
||||||
|
vert->Add(new PopupMultiChoice(&g_Config.iSwipeUp, mc->T("Swipe Up"), gestureButton, 0, ARRAY_SIZE(gestureButton), mc->GetName(), screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
|
||||||
|
vert->Add(new PopupMultiChoice(&g_Config.iSwipeDown, mc->T("Swipe Down"), gestureButton, 0, ARRAY_SIZE(gestureButton), mc->GetName(), screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
|
||||||
|
vert->Add(new PopupMultiChoice(&g_Config.iSwipeLeft, mc->T("Swipe Left"), gestureButton, 0, ARRAY_SIZE(gestureButton), mc->GetName(), screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
|
||||||
|
vert->Add(new PopupMultiChoice(&g_Config.iSwipeRight, mc->T("Swipe Right"), gestureButton, 0, ARRAY_SIZE(gestureButton), mc->GetName(), screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
|
||||||
|
vert->Add(new PopupSliderChoiceFloat(&g_Config.fSwipeSensitivity, 0.01f, 1.0f, co->T("Swipe sensitivity"), 0.01f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
|
||||||
|
vert->Add(new PopupSliderChoiceFloat(&g_Config.fSwipeSmoothing, 0.0f, 0.95f, co->T("Swipe smoothing"), 0.05f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
|
||||||
|
vert->Add(new PopupMultiChoice(&g_Config.iDoubleTapGesture, mc->T("Double tap button"), gestureButton, 0, ARRAY_SIZE(gestureButton), mc->GetName(), screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -221,3 +221,9 @@ private:
|
||||||
std::string lastResolved_ = "";
|
std::string lastResolved_ = "";
|
||||||
bool lastResolvedResult_ = false;
|
bool lastResolvedResult_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class GestureMappingScreen : public UIDialogScreenWithBackground {
|
||||||
|
public:
|
||||||
|
void CreateViews() override;
|
||||||
|
};
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
#include "Core/ControlMapper.h"
|
#include "Core/ControlMapper.h"
|
||||||
#include "UI/GamepadEmu.h"
|
#include "UI/GamepadEmu.h"
|
||||||
|
|
||||||
|
static uint32_t usedPointerMask = 0;
|
||||||
|
|
||||||
static u32 GetButtonColor() {
|
static u32 GetButtonColor() {
|
||||||
return g_Config.iTouchButtonStyle != 0 ? 0xFFFFFF : 0xc0b080;
|
return g_Config.iTouchButtonStyle != 0 ? 0xFFFFFF : 0xc0b080;
|
||||||
}
|
}
|
||||||
|
@ -97,6 +99,7 @@ void MultiTouchButton::Touch(const TouchInput &input) {
|
||||||
GamepadView::Touch(input);
|
GamepadView::Touch(input);
|
||||||
if ((input.flags & TOUCH_DOWN) && bounds_.Contains(input.x, input.y)) {
|
if ((input.flags & TOUCH_DOWN) && bounds_.Contains(input.x, input.y)) {
|
||||||
pointerDownMask_ |= 1 << input.id;
|
pointerDownMask_ |= 1 << input.id;
|
||||||
|
usedPointerMask |= 1 << input.id;
|
||||||
}
|
}
|
||||||
if (input.flags & TOUCH_MOVE) {
|
if (input.flags & TOUCH_MOVE) {
|
||||||
if (bounds_.Contains(input.x, input.y))
|
if (bounds_.Contains(input.x, input.y))
|
||||||
|
@ -106,9 +109,11 @@ void MultiTouchButton::Touch(const TouchInput &input) {
|
||||||
}
|
}
|
||||||
if (input.flags & TOUCH_UP) {
|
if (input.flags & TOUCH_UP) {
|
||||||
pointerDownMask_ &= ~(1 << input.id);
|
pointerDownMask_ &= ~(1 << input.id);
|
||||||
|
usedPointerMask &= ~(1 << input.id);
|
||||||
}
|
}
|
||||||
if (input.flags & TOUCH_RELEASE_ALL) {
|
if (input.flags & TOUCH_RELEASE_ALL) {
|
||||||
pointerDownMask_ = 0;
|
pointerDownMask_ = 0;
|
||||||
|
usedPointerMask = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +236,7 @@ void PSPDpad::Touch(const TouchInput &input) {
|
||||||
if (input.flags & TOUCH_DOWN) {
|
if (input.flags & TOUCH_DOWN) {
|
||||||
if (dragPointerId_ == -1 && bounds_.Contains(input.x, input.y)) {
|
if (dragPointerId_ == -1 && bounds_.Contains(input.x, input.y)) {
|
||||||
dragPointerId_ = input.id;
|
dragPointerId_ = input.id;
|
||||||
|
usedPointerMask |= 1 << input.id;
|
||||||
ProcessTouch(input.x, input.y, true);
|
ProcessTouch(input.x, input.y, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,6 +248,7 @@ void PSPDpad::Touch(const TouchInput &input) {
|
||||||
if (input.flags & TOUCH_UP) {
|
if (input.flags & TOUCH_UP) {
|
||||||
if (input.id == dragPointerId_) {
|
if (input.id == dragPointerId_) {
|
||||||
dragPointerId_ = -1;
|
dragPointerId_ = -1;
|
||||||
|
usedPointerMask &= ~(1 << input.id);
|
||||||
ProcessTouch(input.x, input.y, false);
|
ProcessTouch(input.x, input.y, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -389,6 +396,7 @@ void PSPStick::Touch(const TouchInput &input) {
|
||||||
centerX_ = bounds_.centerX();
|
centerX_ = bounds_.centerX();
|
||||||
centerY_ = bounds_.centerY();
|
centerY_ = bounds_.centerY();
|
||||||
__CtrlSetAnalogXY(stick_, 0.0f, 0.0f);
|
__CtrlSetAnalogXY(stick_, 0.0f, 0.0f);
|
||||||
|
usedPointerMask = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (input.flags & TOUCH_DOWN) {
|
if (input.flags & TOUCH_DOWN) {
|
||||||
|
@ -401,6 +409,7 @@ void PSPStick::Touch(const TouchInput &input) {
|
||||||
centerY_ = bounds_.centerY();
|
centerY_ = bounds_.centerY();
|
||||||
}
|
}
|
||||||
dragPointerId_ = input.id;
|
dragPointerId_ = input.id;
|
||||||
|
usedPointerMask |= 1 << input.id;
|
||||||
ProcessTouch(input.x, input.y, true);
|
ProcessTouch(input.x, input.y, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -414,6 +423,7 @@ void PSPStick::Touch(const TouchInput &input) {
|
||||||
dragPointerId_ = -1;
|
dragPointerId_ = -1;
|
||||||
centerX_ = bounds_.centerX();
|
centerX_ = bounds_.centerX();
|
||||||
centerY_ = bounds_.centerY();
|
centerY_ = bounds_.centerY();
|
||||||
|
usedPointerMask &= ~(1 << input.id);
|
||||||
ProcessTouch(input.x, input.y, false);
|
ProcessTouch(input.x, input.y, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -486,6 +496,7 @@ void PSPCustomStick::Touch(const TouchInput &input) {
|
||||||
centerY_ = bounds_.centerY();
|
centerY_ = bounds_.centerY();
|
||||||
posX_ = 0.0f;
|
posX_ = 0.0f;
|
||||||
posY_ = 0.0f;
|
posY_ = 0.0f;
|
||||||
|
usedPointerMask = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (input.flags & TOUCH_DOWN) {
|
if (input.flags & TOUCH_DOWN) {
|
||||||
|
@ -498,6 +509,7 @@ void PSPCustomStick::Touch(const TouchInput &input) {
|
||||||
centerY_ = bounds_.centerY();
|
centerY_ = bounds_.centerY();
|
||||||
}
|
}
|
||||||
dragPointerId_ = input.id;
|
dragPointerId_ = input.id;
|
||||||
|
usedPointerMask |= 1 << input.id;
|
||||||
ProcessTouch(input.x, input.y, true);
|
ProcessTouch(input.x, input.y, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,6 +523,7 @@ void PSPCustomStick::Touch(const TouchInput &input) {
|
||||||
dragPointerId_ = -1;
|
dragPointerId_ = -1;
|
||||||
centerX_ = bounds_.centerX();
|
centerX_ = bounds_.centerX();
|
||||||
centerY_ = bounds_.centerY();
|
centerY_ = bounds_.centerY();
|
||||||
|
usedPointerMask &= ~(1 << input.id);
|
||||||
ProcessTouch(input.x, input.y, false);
|
ProcessTouch(input.x, input.y, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -813,5 +826,108 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause, ControlMappe
|
||||||
addComboKey(g_Config.CustomKey8, "Custom 9 button", g_Config.touchCombo8);
|
addComboKey(g_Config.CustomKey8, "Custom 9 button", g_Config.touchCombo8);
|
||||||
addComboKey(g_Config.CustomKey9, "Custom 10 button", g_Config.touchCombo9);
|
addComboKey(g_Config.CustomKey9, "Custom 10 button", g_Config.touchCombo9);
|
||||||
|
|
||||||
|
if (g_Config.bGestureControlEnabled)
|
||||||
|
root->Add(new GestureGamepad());
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GestureGamepad::Touch(const TouchInput &input) {
|
||||||
|
static const int button[16] = {CTRL_LTRIGGER, CTRL_RTRIGGER, CTRL_SQUARE, CTRL_TRIANGLE, CTRL_CIRCLE, CTRL_CROSS, CTRL_UP, CTRL_DOWN, CTRL_LEFT, CTRL_RIGHT, CTRL_START, CTRL_SELECT};
|
||||||
|
|
||||||
|
if (usedPointerMask & (1 << input.id)) {
|
||||||
|
if (input.id == dragPointerId_)
|
||||||
|
dragPointerId_ = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.flags & TOUCH_RELEASE_ALL) {
|
||||||
|
dragPointerId_ = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.flags & TOUCH_DOWN) {
|
||||||
|
if (dragPointerId_ == -1) {
|
||||||
|
dragPointerId_ = input.id;
|
||||||
|
lastX_ = input.x;
|
||||||
|
lastY_ = input.y;
|
||||||
|
|
||||||
|
const float now = time_now_d();
|
||||||
|
if (now - lastTapRelease_ < 0.3f && !haveDoubleTapped_) {
|
||||||
|
if (g_Config.iDoubleTapGesture != 0 )
|
||||||
|
__CtrlButtonDown(button[g_Config.iDoubleTapGesture-1]);
|
||||||
|
haveDoubleTapped_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastTouchDown_ = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (input.flags & TOUCH_MOVE) {
|
||||||
|
if (input.id == dragPointerId_) {
|
||||||
|
deltaX_ += input.x - lastX_;
|
||||||
|
deltaY_ += input.y - lastY_;
|
||||||
|
lastX_ = input.x;
|
||||||
|
lastY_ = input.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (input.flags & TOUCH_UP) {
|
||||||
|
if (input.id == dragPointerId_) {
|
||||||
|
dragPointerId_ = -1;
|
||||||
|
if (time_now_d() - lastTouchDown_ < 0.3f)
|
||||||
|
lastTapRelease_ = time_now_d();
|
||||||
|
|
||||||
|
if (haveDoubleTapped_) {
|
||||||
|
if (g_Config.iDoubleTapGesture != 0)
|
||||||
|
__CtrlButtonUp(button[g_Config.iDoubleTapGesture-1]);
|
||||||
|
haveDoubleTapped_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GestureGamepad::Update() {
|
||||||
|
static const int button[16] = {CTRL_LTRIGGER, CTRL_RTRIGGER, CTRL_SQUARE, CTRL_TRIANGLE, CTRL_CIRCLE, CTRL_CROSS, CTRL_UP, CTRL_DOWN, CTRL_LEFT, CTRL_RIGHT, CTRL_START, CTRL_SELECT};
|
||||||
|
|
||||||
|
const float th = 1.0f;
|
||||||
|
float dx = deltaX_ * g_dpi_scale_x * g_Config.fSwipeSensitivity;
|
||||||
|
float dy = deltaY_ * g_dpi_scale_y * g_Config.fSwipeSensitivity;
|
||||||
|
if (g_Config.iSwipeRight != 0) {
|
||||||
|
if (dx > th) {
|
||||||
|
__CtrlButtonDown(button[g_Config.iSwipeRight-1]);
|
||||||
|
swipeRightReleased_ = false;
|
||||||
|
} else if (!swipeRightReleased_) {
|
||||||
|
__CtrlButtonUp(button[g_Config.iSwipeRight-1]);
|
||||||
|
swipeRightReleased_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (g_Config.iSwipeLeft != 0) {
|
||||||
|
if (dx < -th) {
|
||||||
|
__CtrlButtonDown(button[g_Config.iSwipeLeft-1]);
|
||||||
|
swipeLeftReleased_ = false;
|
||||||
|
} else if (!swipeLeftReleased_) {
|
||||||
|
__CtrlButtonUp(button[g_Config.iSwipeLeft-1]);
|
||||||
|
swipeLeftReleased_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (g_Config.iSwipeUp != 0) {
|
||||||
|
if (dy < -th) {
|
||||||
|
__CtrlButtonDown(button[g_Config.iSwipeUp-1]);
|
||||||
|
swipeUpReleased_ = false;
|
||||||
|
} else if (!swipeUpReleased_) {
|
||||||
|
__CtrlButtonUp(button[g_Config.iSwipeUp-1]);
|
||||||
|
swipeUpReleased_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (g_Config.iSwipeDown != 0) {
|
||||||
|
if (dy > th) {
|
||||||
|
__CtrlButtonDown(button[g_Config.iSwipeDown-1]);
|
||||||
|
swipeDownReleased_ = false;
|
||||||
|
} else if (!swipeDownReleased_) {
|
||||||
|
__CtrlButtonUp(button[g_Config.iSwipeDown-1]);
|
||||||
|
swipeDownReleased_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deltaX_ *= g_Config.fSwipeSmoothing;
|
||||||
|
deltaY_ *= g_Config.fSwipeSmoothing;
|
||||||
|
}
|
||||||
|
|
|
@ -288,3 +288,26 @@ namespace CustomKey {
|
||||||
};
|
};
|
||||||
static_assert(ARRAY_SIZE(comboKeyList) <= 64, "Too many key for a uint64_t bit mask");
|
static_assert(ARRAY_SIZE(comboKeyList) <= 64, "Too many key for a uint64_t bit mask");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GestureGamepad : public UI::View {
|
||||||
|
public:
|
||||||
|
GestureGamepad() {};
|
||||||
|
|
||||||
|
void Touch(const TouchInput &input) override;
|
||||||
|
void Update() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
float lastX_ = 0.0f;
|
||||||
|
float lastY_ = 0.0f;
|
||||||
|
float deltaX_ = 0.0f;
|
||||||
|
float deltaY_ = 0.0f;
|
||||||
|
float lastTapRelease_ = 0.0f;
|
||||||
|
float lastTouchDown_ = 0.0f;
|
||||||
|
int dragPointerId_ = -1;
|
||||||
|
bool swipeLeftReleased_ = true;
|
||||||
|
bool swipeRightReleased_ = true;
|
||||||
|
bool swipeUpReleased_ = true;
|
||||||
|
bool swipeDownReleased_ = true;
|
||||||
|
bool haveDoubleTapped_ = false;
|
||||||
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue