mirror of
https://github.com/jonasbark/swiftcontrol.git
synced 2026-02-18 00:17:40 +01:00
bugfix and animation
This commit is contained in:
@@ -11,13 +11,11 @@ import 'package:bike_control/gen/l10n.dart';
|
||||
import 'package:bike_control/main.dart';
|
||||
import 'package:bike_control/utils/actions/android.dart';
|
||||
import 'package:bike_control/utils/core.dart';
|
||||
import 'package:bike_control/utils/keymap/keymap.dart';
|
||||
import 'package:bike_control/utils/requirements/android.dart';
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:gamepads/gamepads.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
import 'devices/base_device.dart';
|
||||
@@ -312,20 +310,7 @@ class Connection {
|
||||
await device.connect();
|
||||
signalChange(device);
|
||||
|
||||
final newButtons = device.availableButtons.filter(
|
||||
(button) => core.actionHandler.supportedApp?.keymap.getKeyPair(button) == null,
|
||||
);
|
||||
for (final button in newButtons) {
|
||||
core.actionHandler.supportedApp?.keymap.addKeyPair(
|
||||
KeyPair(
|
||||
touchPosition: Offset.zero,
|
||||
buttons: [button],
|
||||
physicalKey: null,
|
||||
logicalKey: null,
|
||||
isLongPress: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
core.actionHandler.supportedApp?.keymap.addNewButtons(device.availableButtons);
|
||||
|
||||
_streamSubscriptions[device] = actionSubscription;
|
||||
} catch (e, backtrace) {
|
||||
|
||||
@@ -32,6 +32,22 @@ class ButtonEditPage extends StatefulWidget {
|
||||
class _ButtonEditPageState extends State<ButtonEditPage> {
|
||||
late KeyPair _keyPair;
|
||||
late final ScrollController _scrollController = ScrollController();
|
||||
final double baseHeight = 46;
|
||||
bool _bumped = false;
|
||||
|
||||
void _triggerBump() async {
|
||||
setState(() {
|
||||
_bumped = true;
|
||||
});
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 150));
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_bumped = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
late StreamSubscription<BaseNotification> _actionSubscription;
|
||||
|
||||
@@ -52,6 +68,7 @@ class _ButtonEditPageState extends State<ButtonEditPage> {
|
||||
setState(() {
|
||||
_keyPair = keyPair;
|
||||
});
|
||||
_triggerBump();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -83,7 +100,7 @@ class _ButtonEditPageState extends State<ButtonEditPage> {
|
||||
padding: const EdgeInsets.only(right: 26.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 16,
|
||||
spacing: 8,
|
||||
children: [
|
||||
SizedBox(height: 16),
|
||||
Row(
|
||||
@@ -93,7 +110,14 @@ class _ButtonEditPageState extends State<ButtonEditPage> {
|
||||
spacing: 8,
|
||||
children: [
|
||||
Text('Editing').h3,
|
||||
ButtonWidget(button: _keyPair.buttons.first),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 600),
|
||||
curve: Curves.easeOut,
|
||||
width: _keyPair.buttons.first.color != null ? baseHeight : null,
|
||||
height: _keyPair.buttons.first.color != null ? baseHeight : null,
|
||||
padding: EdgeInsets.all(_bumped ? 0 : 6.0),
|
||||
child: ButtonWidget(button: _keyPair.buttons.first),
|
||||
),
|
||||
Expanded(child: SizedBox()),
|
||||
IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
@@ -115,140 +139,29 @@ class _ButtonEditPageState extends State<ButtonEditPage> {
|
||||
),
|
||||
if (core.logic.showObpActions) ...[
|
||||
ColoredTitle(text: context.i18n.openBikeControlActions),
|
||||
Builder(
|
||||
builder: (context) => SelectableCard(
|
||||
icon: _keyPair.inGameAction?.icon ?? Icons.link,
|
||||
title: Text(
|
||||
core.logic.obpConnectedApp == null
|
||||
? 'Please connect to ${core.settings.getTrainerApp()?.name}, first.'
|
||||
: context.i18n.appIdActions(core.logic.obpConnectedApp!.appId),
|
||||
),
|
||||
isActive: core.logic.obpConnectedApp != null && _keyPair.inGameAction != null,
|
||||
onPressed: core.logic.obpConnectedApp == null
|
||||
? null
|
||||
: () {
|
||||
showDropdown(
|
||||
builder: (c) => DropdownMenu(
|
||||
children: core.logic.obpConnectedApp!.supportedActions
|
||||
.map(
|
||||
(action) => MenuButton(
|
||||
leading: action.icon != null ? Icon(action.icon) : null,
|
||||
onPressed: (_) {
|
||||
_keyPair.touchPosition = Offset.zero;
|
||||
_keyPair.physicalKey = null;
|
||||
_keyPair.logicalKey = null;
|
||||
_keyPair.inGameAction = action;
|
||||
_keyPair.inGameActionValue = null;
|
||||
widget.onUpdate();
|
||||
setState(() {});
|
||||
},
|
||||
child: Text(action.name),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
context: context,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (core.logic.obpConnectedApp == null)
|
||||
Warning(
|
||||
children: [
|
||||
Text(
|
||||
core.logic.obpConnectedApp == null
|
||||
? 'Please connect to ${core.settings.getTrainerApp()?.name}, first.'
|
||||
: context.i18n.appIdActions(core.logic.obpConnectedApp!.appId),
|
||||
),
|
||||
],
|
||||
)
|
||||
else
|
||||
..._buildTrainerConnectionActions(core.logic.obpConnectedApp!.supportedActions),
|
||||
],
|
||||
|
||||
if (core.settings.getMyWhooshLinkEnabled() && core.logic.showMyWhooshLink) ...[
|
||||
SizedBox(height: 8),
|
||||
ColoredTitle(text: context.i18n.myWhooshDirectConnectAction),
|
||||
Builder(
|
||||
builder: (context) => SelectableCard(
|
||||
icon: _keyPair.inGameAction?.icon ?? Icons.link,
|
||||
title: Text(context.i18n.myWhooshDirectConnectAction),
|
||||
isActive:
|
||||
_keyPair.inGameAction != null &&
|
||||
core.whooshLink.supportedActions.contains(_keyPair.inGameAction),
|
||||
value: [_keyPair.inGameAction.toString(), ?_keyPair.inGameActionValue?.toString()].join(' '),
|
||||
onPressed: () {
|
||||
showDropdown(
|
||||
context: context,
|
||||
builder: (c) => DropdownMenu(
|
||||
children: core.whooshLink.supportedActions.map(
|
||||
(ingame) {
|
||||
return MenuButton(
|
||||
subMenu: ingame.possibleValues
|
||||
?.map(
|
||||
(value) => MenuButton(
|
||||
child: Text(value.toString()),
|
||||
onPressed: (_) {
|
||||
_keyPair.inGameAction = ingame;
|
||||
_keyPair.inGameActionValue = value;
|
||||
widget.onUpdate();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
leading: ingame.icon != null ? Icon(ingame.icon) : null,
|
||||
child: Text(ingame.toString()),
|
||||
onPressed: (_) {
|
||||
_keyPair.inGameAction = ingame;
|
||||
_keyPair.inGameActionValue = null;
|
||||
widget.onUpdate();
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
..._buildTrainerConnectionActions(core.whooshLink.supportedActions),
|
||||
],
|
||||
if (core.logic.isZwiftBleEnabled || core.logic.isZwiftMdnsEnabled) ...[
|
||||
SizedBox(height: 8),
|
||||
ColoredTitle(text: context.i18n.zwiftControllerAction),
|
||||
Builder(
|
||||
builder: (context) => SelectableCard(
|
||||
icon: _keyPair.inGameAction?.icon ?? Icons.link,
|
||||
title: Text(context.i18n.zwiftControllerAction),
|
||||
isActive:
|
||||
_keyPair.inGameAction != null &&
|
||||
core.zwiftEmulator.supportedActions.contains(_keyPair.inGameAction),
|
||||
value: [_keyPair.inGameAction.toString(), ?_keyPair.inGameActionValue?.toString()].join(' '),
|
||||
onPressed: () {
|
||||
showDropdown(
|
||||
context: context,
|
||||
builder: (c) => DropdownMenu(
|
||||
children: core.zwiftEmulator.supportedActions.map(
|
||||
(ingame) {
|
||||
return MenuButton(
|
||||
subMenu: ingame.possibleValues
|
||||
?.map(
|
||||
(value) => MenuButton(
|
||||
child: Text(value.toString()),
|
||||
onPressed: (_) {
|
||||
_keyPair.inGameAction = ingame;
|
||||
_keyPair.inGameActionValue = value;
|
||||
widget.onUpdate();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
leading: ingame.icon != null ? Icon(ingame.icon) : null,
|
||||
onPressed: (_) {
|
||||
_keyPair.inGameAction = ingame;
|
||||
_keyPair.inGameActionValue = null;
|
||||
widget.onUpdate();
|
||||
setState(() {});
|
||||
},
|
||||
child: Text(ingame.toString()),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
..._buildTrainerConnectionActions(core.zwiftEmulator.supportedActions),
|
||||
],
|
||||
|
||||
if (core.logic.showLocalRemoteOptions) ...[
|
||||
@@ -510,6 +423,56 @@ class _ButtonEditPageState extends State<ButtonEditPage> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildTrainerConnectionActions(List<InGameAction> supportedActions) {
|
||||
return supportedActions.map((action) {
|
||||
return Builder(
|
||||
builder: (context) {
|
||||
return SelectableCard(
|
||||
icon: action.icon,
|
||||
title: Text(action.title),
|
||||
subtitle: (action.possibleValues != null && action == _keyPair.inGameAction)
|
||||
? Text(_keyPair.inGameActionValue!.toString())
|
||||
: null,
|
||||
isActive: _keyPair.inGameAction == action && supportedActions.contains(_keyPair.inGameAction),
|
||||
onPressed: () {
|
||||
if (action.possibleValues?.isNotEmpty == true) {
|
||||
showDropdown(
|
||||
context: context,
|
||||
builder: (c) => DropdownMenu(
|
||||
children: action.possibleValues!.map(
|
||||
(ingame) {
|
||||
return MenuButton(
|
||||
child: Text(ingame.toString()),
|
||||
onPressed: (_) {
|
||||
_keyPair.touchPosition = Offset.zero;
|
||||
_keyPair.physicalKey = null;
|
||||
_keyPair.logicalKey = null;
|
||||
_keyPair.inGameAction = action;
|
||||
_keyPair.inGameActionValue = ingame;
|
||||
widget.onUpdate();
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
_keyPair.touchPosition = Offset.zero;
|
||||
_keyPair.physicalKey = null;
|
||||
_keyPair.logicalKey = null;
|
||||
_keyPair.inGameAction = action;
|
||||
_keyPair.inGameActionValue = null;
|
||||
widget.onUpdate();
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
class SelectableCard extends StatelessWidget {
|
||||
|
||||
@@ -97,10 +97,11 @@ class _CustomizeState extends State<CustomizePage> {
|
||||
final customApp = CustomApp(profileName: profileName);
|
||||
core.actionHandler.init(customApp);
|
||||
await core.settings.setKeyMap(customApp);
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
} else {
|
||||
core.actionHandler.supportedApp = app;
|
||||
core.actionHandler.init(app);
|
||||
await core.settings.setKeyMap(app);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@@ -53,23 +53,8 @@ abstract class BaseActions {
|
||||
debugPrint('Supported app: ${supportedApp?.name ?? "None"}');
|
||||
|
||||
if (supportedApp != null) {
|
||||
final allButtons = core.connection.devices.map((e) => e.availableButtons).flatten().distinct();
|
||||
|
||||
final newButtons = allButtons.filter(
|
||||
(button) => supportedApp.keymap.getKeyPair(button) == null,
|
||||
);
|
||||
for (final button in newButtons) {
|
||||
supportedApp.keymap.addKeyPair(
|
||||
KeyPair(
|
||||
touchPosition: Offset.zero,
|
||||
buttons: [button],
|
||||
inGameAction: button.action,
|
||||
physicalKey: null,
|
||||
logicalKey: null,
|
||||
isLongPress: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
final allButtons = core.connection.devices.map((e) => e.availableButtons).flatten().distinct().toList();
|
||||
supportedApp.keymap.addNewButtons(allButtons);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ enum InGameAction {
|
||||
toggleUi('Toggle UI', icon: RadixIcons.iconSwitch),
|
||||
navigateLeft('Navigate Left', icon: BootstrapIcons.signTurnLeft),
|
||||
navigateRight('Navigate Right', icon: BootstrapIcons.signTurnRight),
|
||||
increaseResistance('Increase Resistance'),
|
||||
decreaseResistance('Decrease Resistance'),
|
||||
increaseResistance('Increase Resistance', icon: LucideIcons.chartNoAxesColumnIncreasing),
|
||||
decreaseResistance('Decrease Resistance', icon: LucideIcons.chartNoAxesColumnDecreasing),
|
||||
|
||||
// zwift
|
||||
openActionBar('Open Action Bar', alternativeTitle: 'Up', icon: BootstrapIcons.menuApp),
|
||||
|
||||
@@ -80,6 +80,21 @@ class Keymap {
|
||||
return allButtons.firstWhere((b) => b.name == name);
|
||||
}
|
||||
}
|
||||
|
||||
void addNewButtons(List<ControllerButton> availableButtons) {
|
||||
final newButtons = availableButtons.filter((button) => getKeyPair(button) == null);
|
||||
for (final button in newButtons) {
|
||||
addKeyPair(
|
||||
KeyPair(
|
||||
touchPosition: Offset.zero,
|
||||
buttons: [button],
|
||||
physicalKey: null,
|
||||
logicalKey: null,
|
||||
isLongPress: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class KeyPair {
|
||||
|
||||
Reference in New Issue
Block a user