Desktop session batch 09-12: frameless chrome, bisect removal, chat/idle fixes
Frameless (window_manager, hidden titlebar): chrome bar on welcome, drag-to-move appbar + window buttons on connect, drag layer + window buttons in session controls; min size 160x90; volume-scroll off for drag; session maximize = fullscreen toggle. Verified on KDE Wayland. Delete legacy/minimal/nosheet bisect screens; session routes direct (black-screen fix verified incl. WAN). Avatar: pipeline now 128px aspect-kept PNG (was cropped JPEG); profile card centered 64px hover-to-change; member avatar drops ResizeImage downscale. NOTE open: pipeline 512 KiB check vs server 64 KiB enforcement mismatch. Fixes: idle warn/unready skipped while PLAYING; sendChat drops blanks; one chat row per send (drop textless server feed echo); chat field keeps focus after send. Playback libass true; sdk floor 3.13. Handoff 9-11: 9+10 fixed, 11 jumpy seek still open. Docs: handoff/roadmap/protocol record all of the above; few-commits preference going forward.
This commit is contained in:
1 parent
3303d88a5b
commit
ff154c5030
26 files changed
+1147
-881
No files matched your search
@@ -0,0 +1,133 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import '../state/session_state.dart';
|
||||
|
||||
class WindowButtons extends StatefulWidget {
|
||||
final FocusNode? focusNode;
|
||||
final bool fullscreen;
|
||||
final VoidCallback? onToggleFullscreen;
|
||||
const WindowButtons({
|
||||
super.key,
|
||||
this.focusNode,
|
||||
this.fullscreen = false,
|
||||
this.onToggleFullscreen,
|
||||
});
|
||||
|
||||
@override
|
||||
State<WindowButtons> createState() => _WindowButtonsState();
|
||||
}
|
||||
|
||||
class _WindowButtonsState extends State<WindowButtons> with WindowListener {
|
||||
bool _maximized = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
windowManager.addListener(this);
|
||||
windowManager.isMaximized().then((v) {
|
||||
if (mounted) setState(() => _maximized = v);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
windowManager.removeListener(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void onWindowMaximize() {
|
||||
if (mounted) setState(() => _maximized = true);
|
||||
}
|
||||
|
||||
@override
|
||||
void onWindowUnmaximize() {
|
||||
if (mounted) setState(() => _maximized = false);
|
||||
}
|
||||
|
||||
Future<void> _toggleMaximize() async {
|
||||
if (widget.onToggleFullscreen != null) {
|
||||
widget.onToggleFullscreen!();
|
||||
return;
|
||||
}
|
||||
if (await windowManager.isMaximized()) {
|
||||
await windowManager.unmaximize();
|
||||
} else {
|
||||
await windowManager.maximize();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _close() async {
|
||||
try {
|
||||
await SessionState.instance.coordinator.leave();
|
||||
await SessionState.instance.stopServer();
|
||||
} catch (_) {}
|
||||
await windowManager.close();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final focus = widget.focusNode;
|
||||
final restored = widget.fullscreen || _maximized;
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_WinBtn(
|
||||
icon: Icons.remove,
|
||||
tooltip: 'Minimize',
|
||||
focusNode: focus,
|
||||
onPressed: () => windowManager.minimize(),
|
||||
),
|
||||
_WinBtn(
|
||||
icon: restored ? Icons.filter_none : Icons.crop_square,
|
||||
tooltip: restored ? 'Restore' : 'Maximize',
|
||||
focusNode: focus,
|
||||
onPressed: _toggleMaximize,
|
||||
),
|
||||
_WinBtn(
|
||||
icon: Icons.close,
|
||||
tooltip: 'Close',
|
||||
focusNode: focus,
|
||||
hoverColor: Colors.red,
|
||||
onPressed: _close,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _WinBtn extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String tooltip;
|
||||
final VoidCallback onPressed;
|
||||
final FocusNode? focusNode;
|
||||
final Color? hoverColor;
|
||||
const _WinBtn({
|
||||
required this.icon,
|
||||
required this.tooltip,
|
||||
required this.onPressed,
|
||||
this.focusNode,
|
||||
this.hoverColor,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 40,
|
||||
height: 32,
|
||||
child: IconButton(
|
||||
focusNode: focusNode,
|
||||
tooltip: tooltip,
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints.tightFor(width: 40, height: 32),
|
||||
iconSize: 15,
|
||||
splashRadius: 12,
|
||||
hoverColor: (hoverColor ?? Colors.white).withValues(alpha: 0.12),
|
||||
highlightColor: Colors.transparent,
|
||||
icon: Icon(icon, size: 15),
|
||||
onPressed: onPressed,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user