mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-15 00:41:01 +03:00
Fix predefined cursor density and scrollbar centering
This commit is contained in:
@@ -1113,7 +1113,11 @@ class _ImagePaintState extends State<ImagePaint> {
|
|||||||
mouseRegion({child}) => Obx(() {
|
mouseRegion({child}) => Obx(() {
|
||||||
double getCursorScale() {
|
double getCursorScale() {
|
||||||
final cursor = Provider.of<CursorModel>(context);
|
final cursor = Provider.of<CursorModel>(context);
|
||||||
final peerDpr = cursor.cache?.pixelRatio ?? 0;
|
// Predefined artwork must not inherit the cached remote bitmap's DPI.
|
||||||
|
final cache = keyboardEnabled.isTrue
|
||||||
|
? cursor.cache ?? preDefaultCursor.cache
|
||||||
|
: preForbiddenCursor.cache;
|
||||||
|
final peerDpr = cache?.pixelRatio ?? 0;
|
||||||
if (!isWeb && isViewScaled() && !zoomCursor.value && peerDpr > 0) {
|
if (!isWeb && isViewScaled() && !zoomCursor.value && peerDpr > 0) {
|
||||||
return (isWindows ? dpr : 1.0) / peerDpr;
|
return (isWindows ? dpr : 1.0) / peerDpr;
|
||||||
}
|
}
|
||||||
@@ -1417,9 +1421,10 @@ class CursorPaint extends StatelessWidget {
|
|||||||
// Pan offsets can be stale after leaving the image; scrollbars do not use them.
|
// Pan offsets can be stale after leaving the image; scrollbars do not use them.
|
||||||
final imageWidth = rect.width * c.scale;
|
final imageWidth = rect.width * c.scale;
|
||||||
final imageHeight = rect.height * c.scale;
|
final imageHeight = rect.height * c.scale;
|
||||||
cx = (c.size.width > imageWidth ? (c.size.width - imageWidth) / 2 : 0) -
|
// Match the integer centering in _buildCrossScrollbarFromLayout.
|
||||||
|
cx = (c.size.width > imageWidth ? (c.size.width - imageWidth) ~/ 2 : 0) -
|
||||||
imageWidth * c.scrollX;
|
imageWidth * c.scrollX;
|
||||||
cy = (c.size.height > imageHeight ? (c.size.height - imageHeight) / 2 : 0) -
|
cy = (c.size.height > imageHeight ? (c.size.height - imageHeight) ~/ 2 : 0) -
|
||||||
imageHeight * c.scrollY;
|
imageHeight * c.scrollY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'dart:ui' as ui;
|
|||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_custom_cursor/cursor_manager.dart' show CursorManager;
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/remote_page.dart';
|
import 'package:flutter_hbb/desktop/pages/remote_page.dart';
|
||||||
import 'package:flutter_hbb/models/input_model.dart';
|
import 'package:flutter_hbb/models/input_model.dart';
|
||||||
@@ -167,6 +168,9 @@ void main() {
|
|||||||
test('live DPR changes invalidate a cached native cursor',
|
test('live DPR changes invalidate a cached native cursor',
|
||||||
() => _checkDprChange(view, registrations));
|
() => _checkDprChange(view, registrations));
|
||||||
for (final style in [kRemoteViewStyleAdaptive, kRemoteViewStyleCustom]) {
|
for (final style in [kRemoteViewStyleAdaptive, kRemoteViewStyleCustom]) {
|
||||||
|
testWidgets('$style forbidden cursor ignores remote DPR', (tester) =>
|
||||||
|
tester.runAsync(() => _checkPolicy(tester,
|
||||||
|
(style, false, 2, 0.25, 0.5), registrations, dpr: 1)));
|
||||||
testWidgets('Linux $style zoom follows video pixels', (tester) => tester.runAsync(
|
testWidgets('Linux $style zoom follows video pixels', (tester) => tester.runAsync(
|
||||||
() => _checkPolicy(tester, (style, true, 2, 1.0, windows ? 1.0 : 0.5),
|
() => _checkPolicy(tester, (style, true, 2, 1.0, windows ? 1.0 : 0.5),
|
||||||
registrations, linux: true)));
|
registrations, linux: true)));
|
||||||
@@ -176,9 +180,10 @@ void main() {
|
|||||||
Future<void> _checkPolicy(
|
Future<void> _checkPolicy(
|
||||||
WidgetTester tester,
|
WidgetTester tester,
|
||||||
(String, bool, int, double, double) testCase,
|
(String, bool, int, double, double) testCase,
|
||||||
List<Map<dynamic, dynamic>> registrations, {bool linux = false}) async {
|
List<Map<dynamic, dynamic>> registrations,
|
||||||
|
{bool linux = false, double dpr = 2}) async {
|
||||||
final (style, zoom, density, viewScale, expectedScale) = testCase;
|
final (style, zoom, density, viewScale, expectedScale) = testCase;
|
||||||
tester.view.devicePixelRatio = 2;
|
tester.view.devicePixelRatio = dpr;
|
||||||
final data = await _data(density, '$style-$zoom-$density');
|
final data = await _data(density, '$style-$zoom-$density');
|
||||||
final originalBytes = data.data;
|
final originalBytes = data.data;
|
||||||
// A stale cached DPR must not affect the cursor when the window moves.
|
// A stale cached DPR must not affect the cursor when the window moves.
|
||||||
@@ -187,8 +192,9 @@ Future<void> _checkPolicy(
|
|||||||
ffi.ffiModel.isPeerLinux = linux;
|
ffi.ffiModel.isPeerLinux = linux;
|
||||||
if (linux) ffi.ffiModel.pi.displays.add(_LinuxDisplay());
|
if (linux) ffi.ffiModel.pi.displays.add(_LinuxDisplay());
|
||||||
final cursor = _Cursor(data, ffi);
|
final cursor = _Cursor(data, ffi);
|
||||||
|
final keyboardEnabled = true.obs;
|
||||||
await tester.pumpWidget(MediaQuery(
|
await tester.pumpWidget(MediaQuery(
|
||||||
data: const MediaQueryData(devicePixelRatio: 2),
|
data: MediaQueryData(devicePixelRatio: dpr),
|
||||||
child: MultiProvider(
|
child: MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider<ImageModel>(create: (_) => _Image()),
|
ChangeNotifierProvider<ImageModel>(create: (_) => _Image()),
|
||||||
@@ -200,10 +206,17 @@ Future<void> _checkPolicy(
|
|||||||
id: 'dpi-policy',
|
id: 'dpi-policy',
|
||||||
zoomCursor: zoom.obs,
|
zoomCursor: zoom.obs,
|
||||||
cursorOverImage: true.obs,
|
cursorOverImage: true.obs,
|
||||||
keyboardEnabled: true.obs,
|
keyboardEnabled: keyboardEnabled,
|
||||||
remoteCursorMoved: false.obs,
|
remoteCursorMoved: false.obs,
|
||||||
)),
|
)),
|
||||||
));
|
));
|
||||||
|
final revoke = !zoom && style != kRemoteViewStyleOriginal;
|
||||||
|
if (revoke) {
|
||||||
|
await Future.wait(cursor.cachedKeys
|
||||||
|
.map(CursorManager.instance.ensureCursorRegistered));
|
||||||
|
keyboardEnabled.value = false;
|
||||||
|
await tester.pump();
|
||||||
|
}
|
||||||
await tester.pumpWidget(const SizedBox.shrink());
|
await tester.pumpWidget(const SizedBox.shrink());
|
||||||
for (final key in cursor.cachedKeys) {
|
for (final key in cursor.cachedKeys) {
|
||||||
await deleteCustomCursor(key);
|
await deleteCustomCursor(key);
|
||||||
@@ -215,11 +228,17 @@ Future<void> _checkPolicy(
|
|||||||
expect(data.hotx, closeTo(data.hotxOrigin * expectedScale, 1e-9));
|
expect(data.hotx, closeTo(data.hotxOrigin * expectedScale, 1e-9));
|
||||||
expect(data.hoty, closeTo(data.hotyOrigin * expectedScale, 1e-9));
|
expect(data.hoty, closeTo(data.hotyOrigin * expectedScale, 1e-9));
|
||||||
expect(data.data, same(originalBytes));
|
expect(data.data, same(originalBytes));
|
||||||
|
if (revoke) {
|
||||||
|
final args = registrations.last;
|
||||||
|
expect(args['name'], contains('_${kPreForbiddenCursorId}_'));
|
||||||
|
expect((args['width'], args['height']), (32, 32));
|
||||||
|
expect((args['hotX'], args['hotY']), (0.0, 0.0));
|
||||||
|
}
|
||||||
if (style == kRemoteViewStyleAdaptive && !zoom && density > 0) {
|
if (style == kRemoteViewStyleAdaptive && !zoom && density > 0) {
|
||||||
final args = registrations.single;
|
final args = registrations.first;
|
||||||
expect((args['width'], args['height']), (Platform.isLinux ? 36 : 18, 36));
|
expect((args['width'], args['height']), ((Platform.isLinux ? 18 : 9) * dpr, 18 * dpr));
|
||||||
expect((args['hotX'], args['hotY']), (8.0, 18.0));
|
expect((args['hotX'], args['hotY']), (4 * dpr, 9 * dpr));
|
||||||
expect(args['imagePixelRatio'], 2.0);
|
expect(args['imagePixelRatio'], dpr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,16 +118,18 @@ class _CanvasModel extends ChangeNotifier implements CanvasModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ScrollbarCanvasModel extends _CanvasModel {
|
class _ScrollbarCanvasModel extends _CanvasModel {
|
||||||
_ScrollbarCanvasModel(String style) : super(style, 2, true) {
|
_ScrollbarCanvasModel(String style, {Size? frame, double scale = 2})
|
||||||
|
: super(style, scale, true) {
|
||||||
imageOverflow.value = true;
|
imageOverflow.value = true;
|
||||||
|
if (frame != null) _ffi.ffiModel.rect = Offset.zero & frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ScrollStyle get scrollStyle => ScrollStyle.scrollbar;
|
ScrollStyle get scrollStyle => ScrollStyle.scrollbar;
|
||||||
@override
|
@override
|
||||||
double get scrollX => 0.1;
|
double get scrollX => _ffi.ffiModel.rect.width * scale > size.width ? 0.1 : 0;
|
||||||
@override
|
@override
|
||||||
double get scrollY => 0.2;
|
double get scrollY => _ffi.ffiModel.rect.height * scale > size.height ? 0.2 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Canvas extends Fake implements Canvas {
|
class _Canvas extends Fake implements Canvas {
|
||||||
@@ -249,12 +251,20 @@ void main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (final style in [kRemoteViewStyleOriginal, kRemoteViewStyleCustom]) {
|
for (final style in [kRemoteViewStyleOriginal, kRemoteViewStyleCustom]) {
|
||||||
testWidgets('$style painted cursor follows scrollbar position',
|
for (final (frame, scale, offset) in [
|
||||||
(tester) async {
|
(_viewport, 2.0, const Offset(-40, -64)),
|
||||||
final painter = await _paintCursor(tester, _ScrollbarCanvasModel(style));
|
(const Size(199, 320), 1.0, const Offset(0, -64)),
|
||||||
final target = _remotePosition * 2 -
|
(const Size(198, 320), 1.0, const Offset(1, -64)),
|
||||||
Offset(_viewport.width * 2 * 0.1, _viewport.height * 2 * 0.2);
|
(const Size(400, 159), 1.0, const Offset(-40, 0)),
|
||||||
expect((Offset(painter.x, painter.y) + _hotspot) * painter.scale, target);
|
(const Size(400, 158), 1.0, const Offset(-40, 1)),
|
||||||
});
|
]) {
|
||||||
|
testWidgets('$style frame=$frame painted cursor follows scrollbar layout',
|
||||||
|
(tester) async {
|
||||||
|
final painter = await _paintCursor(
|
||||||
|
tester, _ScrollbarCanvasModel(style, frame: frame, scale: scale));
|
||||||
|
final target = _remotePosition * scale + offset;
|
||||||
|
expect((Offset(painter.x, painter.y) + _hotspot) * painter.scale, target);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user