Skip to content

Commit 5fecdbe

Browse files
committed
Prevent touchbar jumps on tap
1 parent 45ef67a commit 5fecdbe

3 files changed

Lines changed: 41 additions & 15 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ dotnet run --project .\src\Mirabox.Emulator.Panel -c Release
9999
энкодерам, горизонтальный свайп переключает страницу/сцену. Режим выбирается
100100
в Stream Dock: у аппаратного вертикального свайпа нет отдельного input-кода,
101101
которым эмулятор мог бы переключить интерфейс приложения. В Touchbar Mode
102-
касание передаётся с момента нажатия: короткий тап запускает элемент, а поток
103-
координат горизонтального жеста прокручивает набор.
102+
короткий тап передаётся при отпускании и запускает элемент, а после начала
103+
горизонтального жеста поток координат прокручивает набор без рывка на клике.
104104
Панель также распознаёт команду режима и тип загружаемого слоя от Stream Dock.
105105
Как и на физическом N4 Pro, значки энкодеров в Touchbar Mode скрыты.
106106

docs/PROTOCOL.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,13 @@ release-пакета физический N4 Pro не формирует. В But
5252
области отправляют одиночные события `40..43` с `state=00`.
5353

5454
Touch point использует заголовок `ACK 00 ARX`; координаты big-endian находятся
55-
в байтах 10–13. В Touchbar Mode контроллер отправляет ARX-пакет в начале
56-
касания и продолжает передавать координаты при движении. Stream Dock распознаёт
57-
короткий одиночный контакт как нажатие элемента, а последовательность точек —
58-
как прокрутку. В Button Mode касание обрабатывается как одна из четырёх экранных
59-
областей, а свайпы влево/вправо имеют hardware code `38`/`39`. У физического N4 Pro
55+
в байтах 10–13. В Touchbar Mode Stream Dock распознаёт короткий одиночный
56+
контакт как нажатие элемента, а последовательность точек — как прокрутку.
57+
Виртуальная панель откладывает одиночную точку до отпускания мыши, а поток
58+
координат начинает после порога движения, чтобы небольшой дрейф курсора при
59+
клике не сдвигал содержимое. В Button Mode касание обрабатывается как одна из
60+
четырёх экранных областей, а свайпы влево/вправо имеют hardware code `38`/`39`.
61+
У физического N4 Pro
6062
вертикальный свайп обрабатывается локально прошивкой и отдельного input-кода не
6163
имеет, поэтому эмулятор не может переключить radio button приложения через HID.
6264
Режим виртуальной панели выбирается выходной командой `MOD` либо типом слоя,

src/Mirabox.Emulator.Panel/DeviceSurface.cs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ internal sealed class DeviceSurface : Control
77
{
88
private const int LogicalWidth = 800;
99
private const int LogicalHeight = 480;
10+
// Ignore ordinary mouse jitter so a click remains a tap instead of
11+
// becoming the first step of a scrolling gesture.
12+
private const int TouchDragThreshold = 8;
1013
private const int TouchReportIntervalMs = 16;
1114
private const float CanvasWidth = 800f;
1215
private const float CanvasHeight = 420f;
@@ -352,8 +355,6 @@ protected override void OnMouseDown(MouseEventArgs e)
352355
_touching = true;
353356
_touchStart = e.Location;
354357
_activeSecondary = _touchMode == TouchDisplayMode.Button ? SegmentAt(e.Location) : -1;
355-
if (_touchMode == TouchDisplayMode.TouchBar)
356-
EmitTouch(e.Location, force: true);
357358
Invalidate();
358359
}
359360
}
@@ -368,7 +369,8 @@ protected override void OnMouseMove(MouseEventArgs e)
368369
UpdateHover(e.Location);
369370
return;
370371
}
371-
if (_touchMode == TouchDisplayMode.TouchBar)
372+
if (_touchMode == TouchDisplayMode.TouchBar &&
373+
(_lastTouchLocation is not null || StartHorizontalTouchDrag(e.Location)))
372374
EmitTouch(e.Location);
373375
}
374376
UpdateHover(e.Location);
@@ -399,11 +401,13 @@ protected override void OnMouseUp(MouseEventArgs e)
399401
{
400402
if (_touchMode == TouchDisplayMode.TouchBar)
401403
{
402-
// ARX has no separate release flag. The physical controller
403-
// reports the contact from its first point through its final
404-
// point; Stream Dock derives taps and scrolling from that
405-
// sequence and the pause after it.
406-
EmitTouch(e.Location, force: true);
404+
FlushPendingTouch();
405+
if (_lastTouchLocation is null && !HasTouchDragStarted(e.Location))
406+
{
407+
// Sending this on MouseDown makes scrollable modules jump.
408+
// Wait until release so a stationary click is one point.
409+
EmitTouch(e.Location, force: true);
410+
}
407411
}
408412
else
409413
{
@@ -487,6 +491,26 @@ private int SegmentAt(Point location) =>
487491
private RectangleF TouchSegment(int index) =>
488492
new(_touchRect.Left + index * _touchRect.Width / 4, _touchRect.Top, _touchRect.Width / 4, _touchRect.Height);
489493

494+
private bool HasTouchDragStarted(Point location)
495+
{
496+
var deltaX = location.X - _touchStart.X;
497+
var deltaY = location.Y - _touchStart.Y;
498+
return deltaX * deltaX + deltaY * deltaY >= TouchDragThreshold * TouchDragThreshold;
499+
}
500+
501+
private bool StartHorizontalTouchDrag(Point location)
502+
{
503+
var deltaX = location.X - _touchStart.X;
504+
var deltaY = location.Y - _touchStart.Y;
505+
if (!HasTouchDragStarted(location) || Math.Abs(deltaX) < Math.Abs(deltaY))
506+
return false;
507+
508+
// Seed the gesture at the real press point only after it has been
509+
// classified as a drag, preventing taps from moving the content.
510+
EmitTouch(_touchStart, force: true);
511+
return true;
512+
}
513+
490514
private void EmitTouch(Point location, bool force = false)
491515
{
492516
if (_lastTouchLocation == location)

0 commit comments

Comments
 (0)