Skip to content

Commit 7c7bbe7

Browse files
authored
feat: improve hotkeys in fullwindowmode and make play and pin buttons accessible (#1294)
1 parent 75e6a57 commit 7c7bbe7

File tree

11 files changed

+208
-108
lines changed

11 files changed

+208
-108
lines changed

lib/app/view/master_tile.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import '../../common/page_ids.dart';
88
import '../../common/view/global_keys.dart';
99
import '../../common/view/icons.dart';
1010
import '../../common/view/spaced_divider.dart';
11+
import '../../common/view/theme.dart';
1112
import '../../common/view/ui_constants.dart';
1213
import '../../extensions/build_context_x.dart';
1314
import '../../library/library_model.dart';
@@ -166,9 +167,10 @@ class __PlayAbleMasterTileState extends State<_PlayAbleMasterTile> {
166167
Positioned(
167168
right: 25,
168169
top: 12,
169-
child: CircleAvatar(
170-
radius: kTinyButtonSize / 2,
170+
child: SizedBox.square(
171+
dimension: kTinyButtonSize,
171172
child: IconButton(
173+
style: tonedIconButtonStyle(context.colorScheme),
172174
onPressed: () async {
173175
final audios = await getAudiosById(widget.pageId);
174176
if (audios?.firstOrNull?.audioType == AudioType.radio) {

lib/app/view/mouse_and_keyboard_command_wrapper.dart

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:watch_it/watch_it.dart';
66
import '../../common/data/audio_type.dart';
77
import '../../common/page_ids.dart';
88
import '../../search/search_model.dart';
9+
import '../app_model.dart';
910
import 'back_gesture.dart';
1011
import 'routing_manager.dart';
1112

@@ -36,9 +37,17 @@ class MouseAndKeyboardCommandWrapper extends StatelessWidget {
3637
_SearchIntent: CallbackAction<_SearchIntent>(
3738
onInvoke: (intent) {
3839
final currentPageId = di<RoutingManager>().selectedPageId;
40+
3941
if (currentPageId == PageIDs.searchPage) {
40-
di<RoutingManager>().pop();
42+
if (di<AppModel>().fullWindowMode ?? false) {
43+
di<AppModel>().setFullWindowMode(false);
44+
} else {
45+
di<RoutingManager>().pop();
46+
}
4147
} else {
48+
if (di<AppModel>().fullWindowMode ?? false) {
49+
di<AppModel>().setFullWindowMode(false);
50+
}
4251
di<SearchModel>().setAudioType(
4352
switch (currentPageId) {
4453
PageIDs.localAudio => AudioType.local,
@@ -48,17 +57,24 @@ class MouseAndKeyboardCommandWrapper extends StatelessWidget {
4857
);
4958
di<RoutingManager>().push(pageId: PageIDs.searchPage);
5059
}
60+
5161
return null;
5262
},
5363
),
5464
_SettingsIntent: CallbackAction<_SettingsIntent>(
5565
onInvoke: (intent) {
66+
if (di<AppModel>().fullWindowMode ?? false) {
67+
di<AppModel>().setFullWindowMode(false);
68+
}
5669
di<RoutingManager>().push(pageId: PageIDs.settings);
5770
return null;
5871
},
5972
),
6073
_BackIntent: CallbackAction<_BackIntent>(
6174
onInvoke: (intent) {
75+
if (di<AppModel>().fullWindowMode ?? false) {
76+
di<AppModel>().setFullWindowMode(false);
77+
}
6278
di<RoutingManager>().pop();
6379
return null;
6480
},

lib/common/view/audio_card_vignette.dart

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import '../../extensions/taget_platform_x.dart';
66
import '../../l10n/l10n.dart';
77
import 'theme.dart';
88

9-
class AudioCardVignette extends StatelessWidget {
9+
class AudioCardVignette extends StatefulWidget {
1010
const AudioCardVignette({
1111
super.key,
1212
this.onTap,
@@ -16,6 +16,12 @@ class AudioCardVignette extends StatelessWidget {
1616
final void Function()? onTap;
1717
final IconData iconData;
1818

19+
@override
20+
State<AudioCardVignette> createState() => _AudioCardVignetteState();
21+
}
22+
23+
class _AudioCardVignetteState extends State<AudioCardVignette> {
24+
bool _focused = false;
1925
@override
2026
Widget build(BuildContext context) {
2127
final colorScheme = context.colorScheme;
@@ -27,17 +33,21 @@ class AudioCardVignette extends StatelessWidget {
2733
message: context.l10n.unPinAlbum,
2834
child: InkWell(
2935
borderRadius: borderRadius,
30-
onTap: onTap,
36+
onHover: (v) => setState(() => _focused = v),
37+
onFocusChange: (v) => setState(() => _focused = v),
38+
onTap: widget.onTap,
3139
child: Container(
3240
decoration: BoxDecoration(
33-
color: colorScheme.inverseSurface,
41+
color: _focused ? colorScheme.primary : colorScheme.inverseSurface,
3442
borderRadius: borderRadius,
3543
),
3644
width: audioCardDimension / (isMobile ? 3 : 4),
3745
height: audioCardDimension / (isMobile ? 3 : 4),
3846
child: Icon(
39-
iconData,
40-
color: colorScheme.onInverseSurface,
47+
widget.iconData,
48+
color:
49+
_focused ? colorScheme.onPrimary : colorScheme.onInverseSurface,
50+
semanticLabel: context.l10n.unPinAlbum,
4151
),
4252
),
4353
),

lib/common/view/avatar_play_button.dart

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import '../../settings/settings_model.dart';
1010
import '../data/audio.dart';
1111
import '../data/audio_type.dart';
1212
import 'icons.dart';
13-
import 'theme.dart';
13+
import 'ui_constants.dart';
1414

1515
class AvatarPlayButton extends StatelessWidget with WatchItMixin {
1616
const AvatarPlayButton({
@@ -42,17 +42,21 @@ class AvatarPlayButton extends StatelessWidget with WatchItMixin {
4242
: isMobile
4343
? 26
4444
: 23;
45-
final bigPlayButtonPadding = getBigPlayButtonPadding(useYaruTheme);
4645

46+
final label = context.l10n.playAll;
4747
return Padding(
48-
padding: bigPlayButtonPadding,
49-
child: CircleAvatar(
50-
radius: bigAvatarButtonRadius.toDouble(),
51-
backgroundColor: theme.colorScheme.inverseSurface.withAlpha(
52-
disabled ? 50 : 255,
53-
),
48+
padding: const EdgeInsets.symmetric(horizontal: kSmallestSpace),
49+
child: SizedBox.square(
50+
dimension: bigAvatarButtonRadius * 2,
5451
child: IconButton(
55-
tooltip: context.l10n.playAll,
52+
style: IconButton.styleFrom(
53+
shape: const CircleBorder(),
54+
backgroundColor: theme.colorScheme.inverseSurface,
55+
foregroundColor: theme.colorScheme.onInverseSurface,
56+
hoverColor: theme.colorScheme.primary.withValues(alpha: 0.5),
57+
focusColor: theme.colorScheme.primary.withValues(alpha: 0.5),
58+
),
59+
tooltip: label,
5660
onPressed: disabled
5761
? null
5862
: () {
@@ -84,6 +88,7 @@ class AvatarPlayButton extends StatelessWidget with WatchItMixin {
8488
icon: Icon(
8589
iconData,
8690
color: disabled ? null : theme.colorScheme.onInverseSurface,
91+
semanticLabel: label,
8792
),
8893
),
8994
),

lib/common/view/theme.dart

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,12 +248,28 @@ double getBigAvatarButtonRadius(bool useYaruTheme) => useYaruTheme
248248
? 35
249249
: 23;
250250

251+
ButtonStyle translucentIconButtonStyle(ColorScheme colorScheme) =>
252+
IconButton.styleFrom(
253+
shape: const CircleBorder(),
254+
backgroundColor: colorScheme.outline.withAlpha(80),
255+
foregroundColor: colorScheme.onSurface,
256+
hoverColor: colorScheme.primary.withAlpha(80),
257+
focusColor: colorScheme.primary.withAlpha(80),
258+
);
259+
260+
ButtonStyle tonedIconButtonStyle(ColorScheme colorScheme) =>
261+
IconButton.styleFrom(
262+
shape: const CircleBorder(),
263+
backgroundColor: colorScheme.outline
264+
.scale(lightness: colorScheme.isLight ? 0.4 : -0.1),
265+
foregroundColor: colorScheme.primary,
266+
hoverColor: colorScheme.primary.withAlpha(80),
267+
focusColor: colorScheme.primary.withAlpha(80),
268+
);
269+
251270
EdgeInsets get filterPanelPadding =>
252271
EdgeInsets.only(top: isMobile ? 10 : 0, bottom: 10);
253272

254-
EdgeInsets getBigPlayButtonPadding(bool useYaruTheme) =>
255-
EdgeInsets.symmetric(horizontal: useYaruTheme ? 2.5 : 5);
256-
257273
double get chipHeight => isMobile ? 40 : 34.0;
258274

259275
EdgeInsets get audioTilePadding => kAudioTilePadding;

lib/player/player_service.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ class PlayerService {
165165
Audio? get audio => _audio;
166166
void _setAudio(Audio value) async {
167167
if (value == _audio) return;
168+
if (_color != null && value.audioType != AudioType.local) {
169+
_setColor(null);
170+
}
168171
if (value.audioType != _audio?.audioType) {
169172
_shuffle = false;
170173
_repeatSingle = false;
@@ -450,6 +453,9 @@ class PlayerService {
450453
String? _remoteImageUrl;
451454
String? get remoteImageUrl => _remoteImageUrl;
452455
Future<void> _setRemoteImageUrl(String? url) async {
456+
if (_color != null) {
457+
_setColor(null);
458+
}
453459
_remoteImageUrl = url;
454460
_propertiesChangedController.add(true);
455461
}

lib/player/view/play_button.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,23 @@ class PlayButton extends StatelessWidget with WatchItMixin {
1111
super.key,
1212
required this.active,
1313
this.iconColor,
14+
this.buttonStyle,
1415
});
1516

1617
final bool active;
1718
final Color? iconColor;
19+
final ButtonStyle? buttonStyle;
1820

1921
@override
2022
Widget build(BuildContext context) {
2123
final playerModel = di<PlayerModel>();
2224
final pause = playerModel.pause;
2325
final playOrPause = playerModel.playOrPause;
2426
final isPlaying = watchPropertyValue((PlayerModel m) => m.isPlaying);
27+
final tooltip = isPlaying ? context.l10n.pause : context.l10n.play;
2528
return IconButton(
26-
tooltip: isPlaying ? context.l10n.pause : context.l10n.play,
29+
style: buttonStyle,
30+
tooltip: tooltip,
2731
onPressed: !active
2832
? null
2933
: () {
@@ -36,6 +40,7 @@ class PlayButton extends StatelessWidget with WatchItMixin {
3640
icon: Icon(
3741
isPlaying ? Iconz.pause : Iconz.playFilled,
3842
color: iconColor ?? context.theme.colorScheme.onSurface,
43+
semanticLabel: tooltip,
3944
),
4045
);
4146
}

lib/player/view/player_color.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:flutter/material.dart';
22
import 'package:watch_it/watch_it.dart';
3+
import 'package:yaru/theme.dart';
34

45
import '../../extensions/build_context_x.dart';
56
import '../../extensions/theme_data_x.dart';
@@ -17,7 +18,8 @@ class PlayerColor extends StatelessWidget with WatchItMixin {
1718
final baseColor =
1819
watchPropertyValue((PlayerModel m) => m.color?.withValues(alpha: 0.3));
1920

20-
final color = baseColor ?? theme.cardColor;
21+
final color = baseColor ??
22+
theme.cardColor.scale(lightness: theme.isLight ? -0.2 : 0.2);
2123
return Opacity(
2224
opacity: alpha * (theme.isLight ? 0.7 : 0.8),
2325
child: Container(

lib/player/view/player_main_controls.dart

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,27 @@ class PlayerMainControls extends StatelessWidget with WatchItMixin {
5050
final rawPlayButton = PlayButton(
5151
iconColor: iconColor ?? (theme.isLight ? Colors.white : Colors.black),
5252
active: active,
53+
buttonStyle: avatarPlayButton
54+
? IconButton.styleFrom(
55+
shape: const CircleBorder(),
56+
backgroundColor:
57+
avatarColor ?? (theme.isLight ? Colors.black : Colors.white),
58+
foregroundColor:
59+
iconColor ?? (theme.isLight ? Colors.white : Colors.black),
60+
hoverColor: theme.colorScheme.primary.withValues(alpha: 0.5),
61+
focusColor: theme.colorScheme.primary.withValues(alpha: 0.5),
62+
)
63+
: null,
5364
);
5465

5566
final useYaruTheme =
5667
watchPropertyValue((SettingsModel m) => m.useYaruTheme);
5768

5869
final radius = getBigAvatarButtonRadius(useYaruTheme);
5970
final playButton = avatarPlayButton
60-
? CircleAvatar(
61-
radius: radius,
62-
backgroundColor:
63-
avatarColor ?? (theme.isLight ? Colors.black : Colors.white),
64-
child: SizedBox.square(
65-
dimension: 2 * radius,
66-
child: rawPlayButton,
67-
),
71+
? SizedBox.square(
72+
dimension: 2 * radius,
73+
child: rawPlayButton,
6874
)
6975
: rawPlayButton;
7076

0 commit comments

Comments
 (0)