refactor: call_ui/voice_channel.dart (1123 Zeilen) in 5 thematische part-Dateien unter view/ geteilt (M2, zeichenidentisch verifiziert)
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user