Files
pyramid/lib/features/call_ui/view/quality_menu.dart
T

127 lines
5.0 KiB
Dart

part of '../voice_channel.dart';
void _handleLiveKitScreenShare(BuildContext context, VoiceChannelService call) async {
if (call.isScreenSharing) {
await call.stopScreenShare();
} else {
final source = await ScreenSharePicker.show(context);
if (source != null) await call.startScreenShare(source.id);
}
}
void _showLiveKitQualityMenu(BuildContext context, VoiceChannelService call, Offset pos) {
final pt = PyramidTheme.of(context);
showMenu(
context: context,
position: RelativeRect.fromLTRB(pos.dx - 210, pos.dy - 350, pos.dx, pos.dy),
color: pt.bg2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(pt.rBase)),
items: <PopupMenuEntry>[
const PopupMenuItem(enabled: false, height: 28, child: Text('SENDEN', style: TextStyle(fontSize: 10, fontWeight: FontWeight.w700, color: Colors.grey))),
for (final q in [('sd', 'SD', '360p'), ('hd', 'HD', '720p'), ('fhd', 'Full HD', '1080p'), ('4k', '4K', '2160p')])
PopupMenuItem(onTap: () => call.changeQuality(q.$1), child: _QualityItem(label: q.$2, hint: q.$3, active: call.currentQualityKey == q.$1, pt: pt)),
const PopupMenuDivider(),
const PopupMenuItem(enabled: false, height: 28, child: Text('EMPFANGEN', style: TextStyle(fontSize: 10, fontWeight: FontWeight.w700, color: Colors.grey))),
for (final q in [('auto', 'Auto', ''), ('high', 'Hoch', 'FHD'), ('medium', 'Mittel', 'HD'), ('low', 'Niedrig', 'SD')])
PopupMenuItem(onTap: () => call.setSubscribeQuality(q.$1), child: _QualityItem(label: q.$2, hint: q.$3, active: call.subscribeQualityKey == q.$1, pt: pt)),
],
);
}
void _showVoipQualityMenu(BuildContext context, CallSignalingService voip, Offset pos) {
final pt = PyramidTheme.of(context);
showMenu(
context: context,
position: RelativeRect.fromLTRB(pos.dx - 210, pos.dy - 200, pos.dx, pos.dy),
color: pt.bg2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(pt.rBase)),
items: <PopupMenuEntry>[
const PopupMenuItem(enabled: false, height: 28, child: Text('SENDEN', style: TextStyle(fontSize: 10, fontWeight: FontWeight.w700, color: Colors.grey))),
for (final q in [('sd', 'SD', '360p'), ('hd', 'HD', '720p'), ('fhd', 'Full HD', '1080p'), ('4k', '4K', '2160p')])
PopupMenuItem(onTap: () => voip.changeQuality(q.$1), child: _QualityItem(label: q.$2, hint: q.$3, active: voip.currentQualityKey == q.$1, pt: pt)),
],
);
}
class _QualityItem extends StatelessWidget {
final String label;
final String? hint;
final bool active;
final PyramidTheme pt;
const _QualityItem({required this.label, required this.active, required this.pt, this.hint});
@override
Widget build(BuildContext context) {
return Row(children: [
if (active) Icon(Icons.check_rounded, size: 16, color: pt.accent) else const SizedBox(width: 16),
const SizedBox(width: 8),
Text(label, style: TextStyle(color: active ? pt.accent : pt.fg, fontSize: 13, fontWeight: active ? FontWeight.bold : FontWeight.normal)),
if (hint != null && hint!.isNotEmpty) ...[const Spacer(), Text(hint!, style: TextStyle(color: pt.fgMuted, fontSize: 11))],
]);
}
}
class _VoiceCtrlBtn extends StatefulWidget {
final IconData icon;
final PyramidTheme pt;
final bool active;
final bool danger;
final bool isDanger;
final VoidCallback? onTap;
const _VoiceCtrlBtn({required this.icon, required this.pt, this.onTap, this.active = false, this.danger = false, this.isDanger = false});
@override
State<_VoiceCtrlBtn> createState() => _VoiceCtrlBtnState();
}
class _VoiceCtrlBtnState extends State<_VoiceCtrlBtn> {
bool _hovered = false;
@override
Widget build(BuildContext context) {
final pt = widget.pt;
Color bg;
Color iconColor;
if (widget.isDanger) { bg = pt.danger; iconColor = Colors.white; }
else if (widget.danger) { bg = pt.danger.withAlpha(30); iconColor = pt.danger; }
else if (widget.active) { bg = pt.accent.withAlpha(30); iconColor = pt.accent; }
else { bg = _hovered ? pt.bgHover : pt.bg3; iconColor = pt.fg; }
return MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (_) => setState(() => _hovered = true),
onExit: (_) => setState(() => _hovered = false),
child: GestureDetector(
onTap: widget.onTap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 150),
width: 52, height: 52,
decoration: BoxDecoration(color: bg, borderRadius: BorderRadius.circular(26)),
child: Center(child: Icon(widget.icon, size: 20, color: iconColor)),
),
),
);
}
}
class _MirrorWrapper extends StatelessWidget {
final Widget child;
final bool isLocal;
final bool isScreenShare;
const _MirrorWrapper({required this.child, required this.isLocal, required this.isScreenShare});
@override
Widget build(BuildContext context) {
if (isLocal && !isScreenShare) {
return Transform(
alignment: Alignment.center,
transform: Matrix4.identity()..rotateY(3.14159),
child: child,
);
}
return child;
}
}