fix(cursor): align the remote cursor with scrollbar offsets

This commit is contained in:
fufesou
2026-09-12 06:57:15 +08:00
parent d95c3f2ec9
commit 34ceb16b63
2 changed files with 52 additions and 10 deletions

View File

@@ -1401,22 +1401,20 @@ class CursorPaint extends StatelessWidget {
final imageOffset = _softwareImageOffset(c);
double cx = imageOffset?.dx ?? c.x;
double cy = imageOffset?.dy ?? c.y;
if (c.viewStyle.style == kRemoteViewStyleOriginal &&
c.scrollStyle == ScrollStyle.scrollbar) {
if (c.imageOverflow.isTrue && c.scrollStyle == ScrollStyle.scrollbar) {
final rect = c.parent.target!.ffiModel.rect;
if (rect == null) {
// unreachable!
debugPrint('unreachable! The displays rect is null.');
return Container();
}
if (cx < 0) {
final imageWidth = rect.width * c.scale;
cx = -imageWidth * c.scrollX;
}
if (cy < 0) {
final imageHeight = rect.height * c.scale;
cy = -imageHeight * c.scrollY;
}
// Pan offsets can be stale after leaving the image; scrollbars do not use them.
final imageWidth = rect.width * c.scale;
final imageHeight = rect.height * c.scale;
cx = (c.size.width > imageWidth ? (c.size.width - imageWidth) / 2 : 0) -
imageWidth * c.scrollX;
cy = (c.size.height > imageHeight ? (c.size.height - imageHeight) / 2 : 0) -
imageHeight * c.scrollY;
}
final image = m.image ?? preDefaultCursor.image;

View File

@@ -45,6 +45,8 @@ class _Peer extends Fake implements FfiModel {
final pi = PeerInfo();
@override
bool get isPeerLinux => false;
@override
Rect get rect => Offset.zero & _viewport;
}
class _FFI extends Fake implements FFI {
@@ -82,11 +84,26 @@ class _CanvasModel extends ChangeNotifier implements CanvasModel {
final double scale;
@override
ScrollStyle get scrollStyle => ScrollStyle.scrollauto;
@override
Size get size => _viewport;
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class _ScrollbarCanvasModel extends _CanvasModel {
_ScrollbarCanvasModel(String style) : super(style, 2, true) {
imageOverflow.value = true;
}
@override
ScrollStyle get scrollStyle => ScrollStyle.scrollbar;
@override
double get scrollX => 0.1;
@override
double get scrollY => 0.2;
}
class _Canvas extends Fake implements Canvas {
double factor = 1;
Offset? position;
@@ -161,4 +178,31 @@ void main() {
expect(position.dy, closeTo(target.dy, 1e-9));
});
}
for (final style in [kRemoteViewStyleOriginal, kRemoteViewStyleCustom]) {
testWidgets('$style painted cursor follows scrollbar position',
(tester) async {
final image = (await tester.runAsync(
() => createTestImage(width: 48, height: 64)))!;
addTearDown(image.dispose);
await tester.pumpWidget(MediaQuery(
data: const MediaQueryData(devicePixelRatio: 2),
child: MultiProvider(
providers: [
ChangeNotifierProvider<CursorModel>(
create: (_) => _CursorModel(image)),
ChangeNotifierProvider<CanvasModel>(
create: (_) => _ScrollbarCanvasModel(style)),
],
child: CursorPaint(id: 'cursor-test', zoomCursor: true.obs),
),
));
final painter = tester
.widget<CustomPaint>(find.byType(CustomPaint))
.painter! as ImagePainter;
final target = _remotePosition * 2 -
Offset(_viewport.width * 2 * 0.1, _viewport.height * 2 * 0.2);
expect(
(Offset(painter.x, painter.y) + _hotspot) * painter.scale, target);
});
}
}