fix resetting keymap, show all touches by default

This commit is contained in:
Jonas Bark
2025-11-03 08:45:39 +01:00
parent d95d0cf8cf
commit da62fc4dc6
2 changed files with 73 additions and 64 deletions

View File

@@ -10,6 +10,7 @@ import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:swift_control/main.dart';
import 'package:swift_control/utils/keymap/apps/my_whoosh.dart';
import 'package:swift_control/widgets/button_widget.dart';
import 'package:swift_control/widgets/keymap_explanation.dart';
import 'package:swift_control/widgets/testbed.dart';
@@ -29,12 +30,12 @@ class TouchAreaSetupPage extends StatefulWidget {
}
class _TouchAreaSetupPageState extends State<TouchAreaSetupPage> {
File? _backgroundImage;
Uint8List? _backgroundImage;
final TransformationController _transformationController = TransformationController();
late Rect _imageRect;
bool _showAll = false;
bool _showFaded = true;
Future<void> _pickScreenshot() async {
final picker = ImagePicker();
@@ -44,7 +45,7 @@ class _TouchAreaSetupPageState extends State<TouchAreaSetupPage> {
final Directory tempDir = await getTemporaryDirectory();
final tempImage = File('${tempDir.path}/${actionHandler.supportedApp?.name ?? 'temp'}_screenshot.png');
await image.copy(tempImage.path);
_backgroundImage = tempImage;
_backgroundImage = tempImage.readAsBytesSync();
await _calculateBounds();
}
}
@@ -53,7 +54,7 @@ class _TouchAreaSetupPageState extends State<TouchAreaSetupPage> {
if (_backgroundImage == null) return;
// need to decode image to get its size so we can have a percentage mapping
final decodedImage = await decodeImageFromList(_backgroundImage!.readAsBytesSync());
final decodedImage = await decodeImageFromList(_backgroundImage!);
// calculate image rectangle in the current screen, given it's boxfit contain
final screenSize = MediaQuery.sizeOf(context);
final imageAspectRatio = decodedImage.width / decodedImage.height;
@@ -114,7 +115,7 @@ class _TouchAreaSetupPageState extends State<TouchAreaSetupPage> {
getTemporaryDirectory().then((tempDir) async {
final tempImage = File('${tempDir.path}/${actionHandler.supportedApp?.name ?? 'temp'}_screenshot.png');
if (tempImage.existsSync()) {
_backgroundImage = tempImage;
_backgroundImage = tempImage.readAsBytesSync();
setState(() {});
// wait a bit until device rotation is done
@@ -197,41 +198,50 @@ class _TouchAreaSetupPageState extends State<TouchAreaSetupPage> {
top: position.dy,
child: Tooltip(
message: 'Drag to reposition',
child: Draggable(
dragAnchorStrategy: (widget, context, position) {
final scale = _transformationController.value.getMaxScaleOnAxis();
final RenderBox renderObject = context.findRenderObject() as RenderBox;
return renderObject.globalToLocal(position).scale(scale, scale);
},
feedback: Material(
color: Colors.transparent,
child: AnimatedOpacity(
opacity: _showFaded && widget.keyPair != keyPair ? 0.2 : 1.0,
duration: Duration(milliseconds: 300),
child: Draggable(
dragAnchorStrategy: (widget, context, position) {
final scale = _transformationController.value.getMaxScaleOnAxis();
final RenderBox renderObject = context.findRenderObject() as RenderBox;
return renderObject.globalToLocal(position).scale(scale, scale);
},
feedback: Material(
color: Colors.transparent,
child: icon,
),
childWhenDragging: const SizedBox.shrink(),
onDragStarted: () {
// Capture the starting position to calculate drag distance later
dragStartPosition = position;
if (keyPair != widget.keyPair && _showFaded) {
setState(() {
_showFaded = false;
});
}
},
onDragEnd: (details) {
// Calculate drag distance to prevent accidental repositioning from clicks
// while allowing legitimate drags even with low velocity (e.g., when overlapping buttons)
final dragDistance = dragStartPosition != null
? (details.offset - dragStartPosition!).distance
: double.infinity;
// Only update position if dragged more than 5 pixels (prevents accidental clicks)
if (dragDistance > 5) {
final matrix = Matrix4.inverted(_transformationController.value);
final height = 0;
final sceneY = details.offset.dy - height;
final viewportPoint = MatrixUtils.transformPoint(
matrix,
Offset(details.offset.dx, sceneY) + Offset(iconSize / 2, differenceInHeight + iconSize / 2),
);
setState(() => onPositionChanged(viewportPoint));
}
},
child: icon,
),
childWhenDragging: const SizedBox.shrink(),
onDragStarted: () {
// Capture the starting position to calculate drag distance later
dragStartPosition = position;
},
onDragEnd: (details) {
// Calculate drag distance to prevent accidental repositioning from clicks
// while allowing legitimate drags even with low velocity (e.g., when overlapping buttons)
final dragDistance = dragStartPosition != null
? (details.offset - dragStartPosition!).distance
: double.infinity;
// Only update position if dragged more than 5 pixels (prevents accidental clicks)
if (dragDistance > 5) {
final matrix = Matrix4.inverted(_transformationController.value);
final height = 0;
final sceneY = details.offset.dy - height;
final viewportPoint = MatrixUtils.transformPoint(
matrix,
Offset(details.offset.dx, sceneY) + Offset(iconSize / 2, differenceInHeight + iconSize / 2),
);
setState(() => onPositionChanged(viewportPoint));
}
},
child: icon,
),
),
);
@@ -245,12 +255,11 @@ class _TouchAreaSetupPageState extends State<TouchAreaSetupPage> {
if (_backgroundImage == null && constraints.biggest != _imageRect.size) {
_imageRect = Rect.fromLTWH(0, 0, constraints.maxWidth, constraints.maxHeight);
}
final keyPairsToShow = _showAll
? actionHandler.supportedApp?.keymap.keyPairs
.where((kp) => kp.touchPosition != Offset.zero && !kp.isSpecialKey)
.toList() ??
[]
: [widget.keyPair];
final keyPairsToShow =
actionHandler.supportedApp?.keymap.keyPairs
.where((kp) => kp.touchPosition != Offset.zero && !kp.isSpecialKey)
.toList() ??
[];
return InteractiveViewer(
transformationController: _transformationController,
child: Stack(
@@ -259,7 +268,7 @@ class _TouchAreaSetupPageState extends State<TouchAreaSetupPage> {
Positioned.fill(
child: Opacity(
opacity: 0.5,
child: Image.file(
child: Image.memory(
_backgroundImage!,
fit: BoxFit.contain,
),
@@ -338,23 +347,9 @@ class _TouchAreaSetupPageState extends State<TouchAreaSetupPage> {
PopupMenuButton(
itemBuilder: (c) => [
PopupMenuItem(
child: Row(
children: [
Checkbox(
value: _showAll,
onChanged: (_) {
setState(() {
_showAll = !_showAll;
});
},
),
Text('Show all touch areas'),
],
),
child: Text('Choose another screenshot'),
onTap: () {
setState(() {
_showAll = !_showAll;
});
_pickScreenshot();
},
),
PopupMenuItem(
@@ -400,7 +395,9 @@ class KeypairExplanation extends StatelessWidget {
)
else
Icon(keyPair.icon),
if (keyPair.inGameAction != null)
if (keyPair.inGameAction != null &&
((settings.getTrainerApp() is MyWhoosh && settings.getMyWhooshLinkEnabled()) ||
(settings.getTrainerApp()?.supportsZwiftEmulation == true && settings.getZwiftEmulatorEnabled())))
_KeyWidget(
label: [
keyPair.inGameAction.toString().split('.').last,

View File

@@ -5,6 +5,7 @@ import 'package:dartx/dartx.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:swift_control/main.dart';
import 'package:swift_control/utils/keymap/apps/my_whoosh.dart';
import 'package:swift_control/utils/keymap/buttons.dart';
import '../actions/base_actions.dart';
@@ -40,7 +41,14 @@ class Keymap {
}
void reset() {
keyPairs = [];
for (final keyPair in keyPairs) {
keyPair.physicalKey = null;
keyPair.logicalKey = null;
keyPair.touchPosition = Offset.zero;
keyPair.isLongPress = false;
keyPair.inGameAction = null;
keyPair.inGameActionValue = null;
}
_updateStream.add(null);
}
@@ -90,7 +98,11 @@ class KeyPair {
PhysicalKeyboardKey.audioVolumeUp ||
PhysicalKeyboardKey.audioVolumeDown => Icons.music_note_outlined,
_ when physicalKey != null && actionHandler.supportedModes.contains(SupportedMode.keyboard) => Icons.keyboard,
_ when inGameAction != null => Icons.link,
_
when inGameAction != null &&
((settings.getTrainerApp() is MyWhoosh && settings.getMyWhooshLinkEnabled()) ||
(settings.getTrainerApp()?.supportsZwiftEmulation == true && settings.getZwiftEmulatorEnabled())) =>
Icons.link,
_ => Icons.touch_app,
};
}