Improve cursor detection
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -12,24 +12,27 @@ const winampChannel = MethodChannel("winamp");
|
||||
/// name, so hover regions can switch to them. Best-effort: failures are
|
||||
/// ignored because the native side may not be ready yet.
|
||||
Future<void> registerSkinCursors(ClassicSkin skin) async {
|
||||
for (final entry in skin.cursors.entries) {
|
||||
final png = await _cursorPng(entry.value.image);
|
||||
try {
|
||||
await winampChannel.invokeMethod(
|
||||
"registerCursor",
|
||||
{
|
||||
"name": entry.key,
|
||||
"png": png,
|
||||
"hotspotX": entry.value.hotspotX,
|
||||
"hotspotY": entry.value.hotspotY,
|
||||
},
|
||||
);
|
||||
} on PlatformException {
|
||||
// ignore: best-effort
|
||||
} on MissingPluginException {
|
||||
// ignore: best-effort
|
||||
}
|
||||
}
|
||||
// IMPROVEMENT: Load and register all cursors in parallel instead of sequentially.
|
||||
await Future.wait(
|
||||
skin.cursors.entries.map((entry) async {
|
||||
final png = await _cursorPng(entry.value.image);
|
||||
try {
|
||||
await winampChannel.invokeMethod(
|
||||
"registerCursor",
|
||||
{
|
||||
"name": entry.key,
|
||||
"png": png,
|
||||
"hotspotX": entry.value.hotspotX,
|
||||
"hotspotY": entry.value.hotspotY,
|
||||
},
|
||||
);
|
||||
} on PlatformException {
|
||||
// ignore: best-effort
|
||||
} on MissingPluginException {
|
||||
// ignore: best-effort
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Uint8List> _cursorPng(ui.Image image) async {
|
||||
@@ -62,10 +65,9 @@ class SkinCursorsGate extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SkinCursorsGateState extends State<SkinCursorsGate> {
|
||||
/// Serializes cursor swaps so a slow native call from a stale enter/exit
|
||||
/// event (e.g. during a drag when the window moves under the cursor) can
|
||||
/// never leave the wrong cursor stuck after the latest request.
|
||||
int _cursorSeq = 0;
|
||||
// Track the current cursor locally to prevent flooding the platform channel
|
||||
String _activeCursorName = "arrow";
|
||||
bool _isHovering = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -78,25 +80,42 @@ class _SkinCursorsGateState extends State<SkinCursorsGate> {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.skin != widget.skin) {
|
||||
unawaited(registerSkinCursors(widget.skin));
|
||||
|
||||
// If the skin changes while the mouse is already inside the widget,
|
||||
// we need to re-evaluate what cursor should be showing.
|
||||
if (_isHovering) {
|
||||
_updateCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _updateCursor() {
|
||||
final hasCustom = widget.skin.cursors.containsKey("normal");
|
||||
_setCursor(hasCustom ? "normal" : "arrow");
|
||||
}
|
||||
|
||||
void _setCursor(String name) {
|
||||
if (_activeCursorName == name) return;
|
||||
|
||||
_activeCursorName = name;
|
||||
unawaited(setSkinCursor(name));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hasCustom = widget.skin.cursors.containsKey("normal");
|
||||
return MouseRegion(
|
||||
onEnter: (_) => _setCursor(hasCustom ? "normal" : "arrow"),
|
||||
onExit: (_) => _setCursor("arrow"),
|
||||
hitTestBehavior: HitTestBehavior.translucent,
|
||||
// onHover is intentionally omitted. We only need to know when the
|
||||
// mouse enters or exits the boundary to trigger the cursor change.
|
||||
onEnter: (_) {
|
||||
_isHovering = true;
|
||||
_updateCursor();
|
||||
},
|
||||
onExit: (_) {
|
||||
_isHovering = false;
|
||||
_setCursor("arrow");
|
||||
},
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _setCursor(String name) async {
|
||||
final seq = ++_cursorSeq;
|
||||
await setSkinCursor(name);
|
||||
// A newer enter/exit won the race; leave that cursor in place.
|
||||
if (seq != _cursorSeq) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user