Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental: Support touchpad #399

Open
wants to merge 7 commits into
base: v0_8
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions source/creator/core/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import creator.widgets.dialog;
import creator.widgets.modal;
import creator.backend.gl;
import creator.io.autosave;
import creator.io.touchpad;

import std.exception;

Expand Down Expand Up @@ -237,6 +238,10 @@ void incOpenWindow() {
version(Windows) enforce(imSupport != ImGuiSupport.badLibrary, "Bad cimgui library found!");
}

// Allow touch events. The SDL_HINT_MOUSE_TOUCH_EVENTS option appears to be broken in bindbc.sdl.
// just hardcode c_str for now
SDL_SetHint("SDL_MOUSE_TOUCH_EVENTS", "1");

SDL_Init(SDL_INIT_EVERYTHING);

version(Windows) {
Expand Down Expand Up @@ -680,6 +685,18 @@ void incBeginLoop() {
incExit();
break;

case SDL_MULTIGESTURE:
incUpdateTouchpad(event.mgesture.x, event.mgesture.y, event.mgesture.dDist);
break;

case SDL_FINGERDOWN:
incUpdateTouchpadDown();
break;

case SDL_FINGERUP:
incUpdateTouchpadUp();
break;

case SDL_DROPFILE:
files ~= cast(string)event.drop.file.fromStringz;
SDL_RaiseWindow(window);
Expand Down
3 changes: 3 additions & 0 deletions source/creator/core/settings.d
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ void incSettingsLoad() {
// Always ask the user whether to preserve the folder structure during import
// also see incGetKeepLayerFolder()
settings["KeepLayerFolder"] = "Ask";

// Default Disable Touchpad, until ensure it works well on most devices
settings["TouchpadEnabled"] = false;
}

/**
Expand Down
116 changes: 116 additions & 0 deletions source/creator/io/touchpad.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
Copyright © 2020-2024, Inochi2D Project
Distributed under the 2-Clause BSD License, see LICENSE file.

Authors: Lin, Yong Xiang <[email protected]>
*/

module creator.io.touchpad;
import creator.core;
import std.math.algebraic: abs;
import inochi2d;

// This const works well for macbook touchpad
// but i don't know it for other OS or touchpad
const float INC_MACBOOK_TOUCHPAD_SENSITIVITY = 0.040f;
const float INC_MACBOOK_TOUCHPAD_PINCH_MULTIPLIER = 70.0f;
const float INC_MACBOOK_TOUCHPAD_XY_MULTIPLIER = 2500.0f;
const float INC_MACBOOK_TOUCHPAD_XY_SENSITIVITY = 4.0f;

struct Touchpad {
vec2 xy, deltaXY;
vec2 startXY;
float startDist;
float dDist;
bool isZooming;
TouchpadState state = TouchpadState.Up;
};

enum TouchpadState
{
Up,
DownInit,
Down,
Started,
}

private {
Touchpad incTouchpad;
bool incTouchpadUpdated;

float incTouchpadSensitivity = INC_MACBOOK_TOUCHPAD_SENSITIVITY;
float incTouchpadPinchMultiplier = INC_MACBOOK_TOUCHPAD_PINCH_MULTIPLIER;
float incTouchpadXYMultiplier = INC_MACBOOK_TOUCHPAD_XY_MULTIPLIER;
float incTouchpadXYSensitivity = INC_MACBOOK_TOUCHPAD_XY_SENSITIVITY;
}

void incUpdateTouchpad(float x, float y, float dDist) {
incTouchpadUpdated = true;

// state machine
if (incTouchpad.state == TouchpadState.DownInit) {
incTouchpad.deltaXY = vec2(0.0f, 0.0f);
incTouchpad.startXY = vec2(x, y);
incTouchpad.startDist = dDist;
incTouchpad.state = TouchpadState.Down;
} else if (incTouchpad.state == TouchpadState.Down) {
// just trigger when more then Sensitivity
if (abs(incTouchpad.startDist) > incTouchpadSensitivity) {
incTouchpad.state = TouchpadState.Started;
incTouchpad.isZooming = true;
} else if ((incTouchpad.startXY - vec2(x, y)).length() * incTouchpadPinchMultiplier > incTouchpadXYSensitivity) {
incTouchpad.state = TouchpadState.Started;
incTouchpad.isZooming = false;
} else {
incTouchpad.startDist += dDist;
incTouchpad.deltaXY = vec2(x - incTouchpad.xy.x, y - incTouchpad.xy.y);
}
} else if (incTouchpad.state == TouchpadState.Started) {
incTouchpad.deltaXY = vec2(x - incTouchpad.xy.x, y - incTouchpad.xy.y);
}

incTouchpad.dDist = dDist;
incTouchpad.xy = vec2(x, y);
}

vec2 incGetTouchpadDeltaXY() {
if (!incTouchpadUpdated || incTouchpad.state != TouchpadState.Started || incTouchpad.isZooming)
return vec2(0.0f, 0.0f);
return incTouchpad.deltaXY * incTouchpadXYMultiplier;
}

void incClearTouchpad() {
incTouchpad.deltaXY = vec2(0.0f, 0.0f);
incTouchpadUpdated = false;
}

float incGetPinchDistance() {
if (!incTouchpadUpdated || incTouchpad.state != TouchpadState.Started || !incTouchpad.isZooming)
return 0.0f;
return incTouchpad.dDist * incTouchpadPinchMultiplier;
}

bool incIsTouchpadUpdated() {
return incTouchpadUpdated && incIsTouchpadEnabled();
}

void incUpdateTouchpadUp() {
incClearTouchpad();
incTouchpad.state = TouchpadState.Up;
}

bool incIsTouchpadDown() {
return incTouchpad.state != TouchpadState.Up && incIsTouchpadEnabled();
}

void incUpdateTouchpadDown() {
incTouchpad.state = TouchpadState.DownInit;
}

bool incIsTouchpadEnabled() {
if (incSettingsCanGet("TouchpadEnabled"))
return incSettingsGet!bool("TouchpadEnabled");
else
// default is disabled, also see incSettingsLoad()
return false;
}
94 changes: 70 additions & 24 deletions source/creator/viewport/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import creator.viewport.test;
import creator.widgets.viewport;
import creator.widgets.label;
import creator.widgets.tooltip;
import creator.io.touchpad;
import i18n;
import bindbc.imgui;
import std.algorithm.sorting;
Expand Down Expand Up @@ -863,6 +864,8 @@ private {
float csx, csy;
bool isMovingPart;

bool prevIsTouchpad = false;

void incViewportMovement(ImGuiIO* io, Camera camera) {
float uiScale = incGetUIScale();

Expand All @@ -889,48 +892,91 @@ private {
incViewportTargetPosition = camera.position;
}

// move viewport for touchpad
if (incIsTouchpadUpdated()) {
vec2 xy = incGetTouchpadDeltaXY();
csx = camera.position.x;
csy = camera.position.y;

camera.position = vec2(
csx+((xy.x)/incViewportZoom)*uiScale,
csy+((xy.y)/incViewportZoom)*uiScale
);

incViewportTargetPosition = camera.position;
}

// HANDLE ZOOM
string zoomMode = incGetCurrentViewportZoomMode();
if (zoomMode == "legacy-zooming")
incViewportZoomLegacy(io, camera, uiScale);
else if (zoomMode == "normal")
incViewportZoomNew(io, camera, uiScale);

incClearTouchpad();
}

/**
This function auto chooses wheel or touchpad
*/
float incMouseTouchpadWheel(ImGuiIO* io) {
// return touchpad first if touchpad is down
if (incIsTouchpadUpdated()) {
prevIsTouchpad = true;
return incGetPinchDistance();
}

// fixed the issue when touchpad released detected as mouse wheel
if (prevIsTouchpad) {
prevIsTouchpad = false;
return 0;
}

if (incIsTouchpadDown())
return 0;

return io.MouseWheel;
}

void incViewportZoomNew(ImGuiIO* io, Camera camera, float uiScale) {
// This value changes the zoom speed
float speed = incGetViewportZoomSpeed();
if (io.MouseWheel != 0) {
float prevZoom = incViewportZoom;
incViewportZoom += (io.MouseWheel*speed/50)*incViewportZoom*uiScale;
incViewportZoom = clamp(incViewportZoom, incVIEWPORT_ZOOM_MIN, incVIEWPORT_ZOOM_MAX);
camera.scale = vec2(incViewportZoom);
incViewportTargetZoom = incViewportZoom;
float zoomDelta = incMouseTouchpadWheel(io);

// Get canvas size and xy
int uiWidth, uiHeight;
inGetViewport(uiWidth, uiHeight);
ImVec2 panelPos;
igGetItemRectMin(&panelPos);
if (zoomDelta == 0)
return;

// Taking the canvas as the center point, calculate the relative position
vec2 relatedMousePos = vec2(
io.MousePos.x - (panelPos.x + cast(float) uiWidth / 2),
io.MousePos.y - (panelPos.y + cast(float) uiHeight / 2)
);
// Calculate zooming
float prevZoom = incViewportZoom;
incViewportZoom += (zoomDelta*speed/50)*incViewportZoom*uiScale;
incViewportZoom = clamp(incViewportZoom, incVIEWPORT_ZOOM_MIN, incVIEWPORT_ZOOM_MAX);
camera.scale = vec2(incViewportZoom);
incViewportTargetZoom = incViewportZoom;

// Get canvas size and xy
int uiWidth, uiHeight;
inGetViewport(uiWidth, uiHeight);
ImVec2 panelPos;
igGetItemRectMin(&panelPos);

// Taking the canvas as the center point, calculate the relative position
vec2 relatedMousePos = vec2(
io.MousePos.x - (panelPos.x + cast(float) uiWidth / 2),
io.MousePos.y - (panelPos.y + cast(float) uiHeight / 2)
);

// Calculate the relative value to the center point before and after scaling
vec2 afterScaleVec = relatedMousePos / incViewportZoom * uiScale;
vec2 beforeScaleVec = relatedMousePos / prevZoom * uiScale;
camera.position -= beforeScaleVec - afterScaleVec;
incViewportTargetPosition = camera.position;
}
// Calculate the relative value to the center point before and after scaling
vec2 afterScaleVec = relatedMousePos / incViewportZoom * uiScale;
vec2 beforeScaleVec = relatedMousePos / prevZoom * uiScale;
camera.position -= beforeScaleVec - afterScaleVec;
incViewportTargetPosition = camera.position;
}

void incViewportZoomLegacy(ImGuiIO* io, Camera camera, float uiScale) {
float speed = incGetViewportZoomSpeed();
if (io.MouseWheel != 0) {
incViewportZoom += (io.MouseWheel/50*speed)*incViewportZoom*uiScale;
float zoomDelta = incMouseTouchpadWheel(io);
if (zoomDelta != 0) {
incViewportZoom += (zoomDelta/50*speed)*incViewportZoom*uiScale;
incViewportZoom = clamp(incViewportZoom, incVIEWPORT_ZOOM_MIN, incVIEWPORT_ZOOM_MAX);
camera.scale = vec2(incViewportZoom);
incViewportTargetZoom = incViewportZoom;
Expand Down
6 changes: 6 additions & 0 deletions source/creator/windows/settings.d
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module creator.windows.settings;
import creator.windows;
import creator.viewport;
import creator.widgets;
import creator.io.touchpad;
import creator.core;
import creator.core.i18n;
import creator.io;
Expand Down Expand Up @@ -219,6 +220,11 @@ protected:
igEndCombo();
}

bool touchpadEnabled = incIsTouchpadEnabled();
if (igCheckbox(__("Enable Touchpad (Experimental)"), &touchpadEnabled)) {
incSettingsSet("TouchpadEnabled", touchpadEnabled);
}

float zoomSpeed = cast(float)incGetViewportZoomSpeed();
if (igDragFloat(__("Zoom Speed"), &zoomSpeed, 0.1, 1, 50, "%f")) {
incSetViewportZoomSpeed(zoomSpeed);
Expand Down
Loading