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: [ 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: [ 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; } }