fix(jellyfin): Add and Jisho on the word's own line, entries unfold after 0.6 s
The Watch screen's entries carry Jisho's link and Add at the end of the headword's line, aligned right, rather than beside the senses; and an entry unfolds over the list once the pointer has rested on it for 0.6 s, as 0.3 s opened entries the pointer only passed over. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
8fb5e105c9
commit
747d73b867
3 changed files with 61 additions and 48 deletions
|
|
@ -11,13 +11,14 @@ import 'package:url_launcher/url_launcher.dart';
|
|||
/// A word as a dictionary lists it: its headword and reading, [progress]
|
||||
/// beside them if given, its senses one to a line with their grammar notes
|
||||
/// small, grey and slanted, cut short at two lines unless [whole], its
|
||||
/// level and whether it is common; [trailing] at its end, and [onTap] for
|
||||
/// the whole row.
|
||||
/// level and whether it is common; [trailing] at its end, [actions] at the
|
||||
/// end of the headword's own line, and [onTap] for the whole row.
|
||||
class const DictionaryWordTile({
|
||||
required final DictionaryWord word,
|
||||
final Widget? trailing,
|
||||
final VoidCallback? onTap,
|
||||
final Widget? progress,
|
||||
final List<Widget> actions = const [],
|
||||
final bool whole = false,
|
||||
super.key,
|
||||
}) extends StatelessWidget {
|
||||
|
|
@ -31,27 +32,37 @@ class const DictionaryWordTile({
|
|||
];
|
||||
return ListTile(
|
||||
onTap: onTap,
|
||||
title: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(text: word.headword, style: theme.textTheme.titleMedium),
|
||||
if (word.reading case final reading?)
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text: ' $reading',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: word.headword,
|
||||
style: theme.textTheme.titleMedium,
|
||||
),
|
||||
if (word.reading case final reading?)
|
||||
TextSpan(
|
||||
text: ' $reading',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
if (progress case final progress?)
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: progress,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (progress case final progress?)
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: progress,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
...actions,
|
||||
],
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
|
|||
|
|
@ -607,39 +607,40 @@ class const _ChosenWord({required final Dictionary dictionary})
|
|||
return const SizedBox();
|
||||
}
|
||||
final translation = state.translationOf(selected.line);
|
||||
Widget? trailing(DictionaryWord entry, String form) =>
|
||||
state.known.contains(entry.ref)
|
||||
? null
|
||||
: FilledButton.tonal(
|
||||
onPressed: () async {
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
await cubit().add(entry, form: form);
|
||||
messenger.showSnackBar(
|
||||
SnackBar(content: Text(l10n.watchAdded(entry.headword))),
|
||||
);
|
||||
},
|
||||
child: Text(l10n.watchAdd),
|
||||
);
|
||||
Widget beside(DictionaryWord word) => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (state.known.contains(word.ref))
|
||||
_ProgressChip(schedule: state.progress[word.ref]),
|
||||
_JishoLink(word: word),
|
||||
],
|
||||
);
|
||||
// On the headword's line, at its end (decided 2026-10-13): Jisho, and
|
||||
// Add for a word the studyset lacks.
|
||||
List<Widget> actions(DictionaryWord entry, String form) => [
|
||||
_JishoLink(word: entry),
|
||||
if (!state.known.contains(entry.ref))
|
||||
FilledButton.tonal(
|
||||
style: FilledButton.styleFrom(
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
onPressed: () async {
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
await cubit().add(entry, form: form);
|
||||
messenger.showSnackBar(
|
||||
SnackBar(content: Text(l10n.watchAdded(entry.headword))),
|
||||
);
|
||||
},
|
||||
child: Text(l10n.watchAdd),
|
||||
),
|
||||
];
|
||||
Widget? beside(DictionaryWord word) => state.known.contains(word.ref)
|
||||
? _ProgressChip(schedule: state.progress[word.ref])
|
||||
: null;
|
||||
Widget row(DictionaryWord entry, String form) => _Unfolding(
|
||||
word: entry,
|
||||
folded: DictionaryWordTile(
|
||||
word: entry,
|
||||
progress: beside(entry),
|
||||
trailing: trailing(entry, form),
|
||||
actions: actions(entry, form),
|
||||
),
|
||||
whole: DictionaryWordTile(
|
||||
word: entry,
|
||||
whole: true,
|
||||
progress: beside(entry),
|
||||
trailing: trailing(entry, form),
|
||||
actions: actions(entry, form),
|
||||
),
|
||||
);
|
||||
return ListView(
|
||||
|
|
@ -703,8 +704,9 @@ class const _Unfolding({
|
|||
required final Widget folded,
|
||||
required final Widget whole,
|
||||
}) extends StatefulWidget {
|
||||
/// How long the pointer rests on an entry before it unfolds.
|
||||
static const rest = Duration(milliseconds: 300);
|
||||
/// How long the pointer rests on an entry before it unfolds (decided
|
||||
/// 2026-10-13: 0.3 s opened entries the pointer only passed over).
|
||||
static const rest = Duration(milliseconds: 600);
|
||||
|
||||
@override
|
||||
State<_Unfolding> createState() => _UnfoldingState();
|
||||
|
|
@ -769,7 +771,7 @@ class _UnfoldingState() extends State<_Unfolding> {
|
|||
Widget build(BuildContext context) => LayoutBuilder(
|
||||
builder: (context, box) {
|
||||
// The senses' room: the row less its padding and what is beside it.
|
||||
if (!_folds(context, box.maxWidth - 140)) {
|
||||
if (!_folds(context, box.maxWidth - 40)) {
|
||||
return widget.folded;
|
||||
}
|
||||
final lit = _VideoColour.litOf(context);
|
||||
|
|
|
|||
|
|
@ -534,7 +534,7 @@ void main() {
|
|||
await mouse.moveTo(
|
||||
tester.getCenter(find.textContaining('sense 1 of a long').first),
|
||||
);
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
await tester.pump(const Duration(milliseconds: 700));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(VocabularySensesView), findsOneWidget);
|
||||
expect(find.text('station, as in sense 6 of a long entry'), findsOneWidget);
|
||||
|
|
|
|||
Loading…
Reference in a new issue