// Author: fruit.com.cn part of 'player_overlay.dart'; class _TopButton extends StatelessWidget { const _TopButton({ super.key, required this.tooltip, required this.icon, required this.onPressed, }); final String tooltip; final IconData icon; final VoidCallback onPressed; @override Widget build(BuildContext context) { return SizedBox.square( dimension: _topButtonSize, child: IconButton( tooltip: tooltip, padding: EdgeInsets.zero, style: IconButton.styleFrom( backgroundColor: const Color(0x80000000), foregroundColor: AppColors.onPrimary, ), onPressed: onPressed, icon: Icon(icon, size: 18), ), ); } } class _SpeedEntry extends StatelessWidget { const _SpeedEntry({super.key, required this.speed, required this.onTap}); final double speed; final VoidCallback onTap; @override Widget build(BuildContext context) { return Material( color: const Color(0x80000000), borderRadius: BorderRadius.circular(999), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(999), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon( Icons.speed_rounded, color: AppColors.onPrimary, size: 15, shadows: _playerTextShadows, ), const SizedBox(width: 4), Text( '${speed}x', style: const TextStyle( color: AppColors.onPrimary, fontSize: 12, fontWeight: FontWeight.w700, shadows: _playerTextShadows, ), ), ], ), ), ), ); } } class _BottomIconButton extends StatelessWidget { const _BottomIconButton({ super.key, required this.tooltip, required this.icon, required this.onPressed, }); final String tooltip; final IconData icon; final VoidCallback onPressed; @override Widget build(BuildContext context) { return SizedBox.square( dimension: 40, child: IconButton( tooltip: tooltip, padding: EdgeInsets.zero, style: IconButton.styleFrom( backgroundColor: AppColors.playerOverlayPill, foregroundColor: AppColors.onPrimary, ), onPressed: onPressed, icon: Icon(icon, size: 22), ), ); } } class _EpisodeSynopsis extends StatefulWidget { const _EpisodeSynopsis({ super.key, required this.l10n, required this.episodeNumber, required this.synopsis, required this.authorNote, }); final AppLocalizations l10n; final int episodeNumber; final String synopsis; final String authorNote; @override State<_EpisodeSynopsis> createState() => _EpisodeSynopsisState(); } class _EpisodeSynopsisState extends State<_EpisodeSynopsis> { bool _expanded = false; /// 「第 n 集」、分隔符与简介正文共用同一套灰字规格。 static const TextStyle _lineStyle = TextStyle( color: AppColors.playerMuted, fontSize: 12, fontWeight: FontWeight.w500, height: 1.35, shadows: _playerTextShadows, ); @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.baseline, textBaseline: TextBaseline.alphabetic, children: [ Text( widget.l10n.episodeLabel(widget.episodeNumber), key: const Key('player-episode-label'), maxLines: 1, overflow: TextOverflow.ellipsis, style: _lineStyle, ), Padding( padding: const EdgeInsets.symmetric(horizontal: 6), child: Text( '|', maxLines: 1, style: _lineStyle.copyWith( color: AppColors.playerMuted.withValues(alpha: 0.6), ), ), ), Flexible( child: Text( widget.synopsis, key: const Key('player-synopsis'), maxLines: _expanded ? null : 1, overflow: _expanded ? TextOverflow.clip : TextOverflow.ellipsis, style: _lineStyle, ), ), if (!_expanded) _toggle(widget.l10n.expandSynopsis), ], ), if (_expanded) ...[ if (widget.authorNote.isNotEmpty) ...[ const SizedBox(height: 4), Text( widget.authorNote, key: const Key('player-author-note'), maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.playerMuted, fontSize: 10, shadows: _playerTextShadows, ), ), ], _toggle(widget.l10n.showLess), ], ], ); } Widget _toggle(String label) { return InkWell( key: const Key('player-synopsis-toggle'), onTap: () => setState(() => _expanded = !_expanded), borderRadius: BorderRadius.circular(4), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), child: Text( label, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.blue, fontSize: 12, fontWeight: FontWeight.w700, shadows: _playerTextShadows, ), ), ), ); } }