diff --git a/lib/bluetooth/connection.dart b/lib/bluetooth/connection.dart index ffcf29d..0b9d218 100644 --- a/lib/bluetooth/connection.dart +++ b/lib/bluetooth/connection.dart @@ -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) { diff --git a/lib/pages/button_edit.dart b/lib/pages/button_edit.dart index a7f77f2..227ff6a 100644 --- a/lib/pages/button_edit.dart +++ b/lib/pages/button_edit.dart @@ -32,6 +32,22 @@ class ButtonEditPage extends StatefulWidget { class _ButtonEditPageState extends State { 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 _actionSubscription; @@ -52,6 +68,7 @@ class _ButtonEditPageState extends State { setState(() { _keyPair = keyPair; }); + _triggerBump(); } } }); @@ -83,7 +100,7 @@ class _ButtonEditPageState extends State { 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 { 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 { ), 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 { ), ); } + + List _buildTrainerConnectionActions(List 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 { diff --git a/lib/pages/customize.dart b/lib/pages/customize.dart index c81dc32..8c7e8cc 100644 --- a/lib/pages/customize.dart +++ b/lib/pages/customize.dart @@ -97,10 +97,11 @@ class _CustomizeState extends State { 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(() {}); } diff --git a/lib/utils/actions/base_actions.dart b/lib/utils/actions/base_actions.dart index 58fe74a..20a346c 100644 --- a/lib/utils/actions/base_actions.dart +++ b/lib/utils/actions/base_actions.dart @@ -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); } } diff --git a/lib/utils/keymap/buttons.dart b/lib/utils/keymap/buttons.dart index b10b8ee..2ab0ddf 100644 --- a/lib/utils/keymap/buttons.dart +++ b/lib/utils/keymap/buttons.dart @@ -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), diff --git a/lib/utils/keymap/keymap.dart b/lib/utils/keymap/keymap.dart index 129ebac..93018d8 100644 --- a/lib/utils/keymap/keymap.dart +++ b/lib/utils/keymap/keymap.dart @@ -80,6 +80,21 @@ class Keymap { return allButtons.firstWhere((b) => b.name == name); } } + + void addNewButtons(List 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 {