Merge pull request #2478 from fufesou/refact_remote_menubar_draggable

remote menu, draggable hide widget
This commit is contained in:
RustDesk 2022-12-07 15:21:33 +08:00 committed by GitHub
commit e4c1e9113a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 157 additions and 63 deletions

View File

@ -11,7 +11,7 @@ import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart' as rxdart; import 'package:debounce_throttle/debounce_throttle.dart';
import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:window_size/window_size.dart' as window_size; import 'package:window_size/window_size.dart' as window_size;
@ -119,10 +119,11 @@ class RemoteMenubar extends StatefulWidget {
} }
class _RemoteMenubarState extends State<RemoteMenubar> { class _RemoteMenubarState extends State<RemoteMenubar> {
final Rx<Color> _hideColor = Colors.white12.obs; late Debouncer<int> _debouncerHide;
final _rxHideReplay = rxdart.ReplaySubject<int>();
bool _isCursorOverImage = false; bool _isCursorOverImage = false;
window_size.Screen? _screen; window_size.Screen? _screen;
final _fractionX = 0.5.obs;
final _dragging = false.obs;
int get windowId => stateGlobal.windowId; int get windowId => stateGlobal.windowId;
@ -139,23 +140,26 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
initState() { initState() {
super.initState(); super.initState();
_debouncerHide = Debouncer<int>(
Duration(milliseconds: 5000),
onChanged: _debouncerHideProc,
initialValue: 0,
);
widget.onEnterOrLeaveImageSetter((enter) { widget.onEnterOrLeaveImageSetter((enter) {
if (enter) { if (enter) {
_rxHideReplay.add(0); _debouncerHide.value = 0;
_isCursorOverImage = true; _isCursorOverImage = true;
} else { } else {
_isCursorOverImage = false; _isCursorOverImage = false;
} }
}); });
}
_rxHideReplay _debouncerHideProc(int v) {
.throttleTime(const Duration(milliseconds: 5000), if (!pin && show.isTrue && _isCursorOverImage && _dragging.isFalse) {
trailing: true, leading: false) show.value = false;
.listen((int v) { }
if (!pin && show.isTrue && _isCursorOverImage) {
show.value = false;
}
});
} }
@override @override
@ -169,36 +173,29 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Align( return Align(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
child: Obx( child: Obx(() => show.value
() => show.value ? _buildMenubar(context) : _buildShowHide(context)), ? _buildMenubar(context)
: _buildDraggableShowHide(context)),
); );
} }
Widget _buildShowHide(BuildContext context) { Widget _buildDraggableShowHide(BuildContext context) {
return Obx(() => Tooltip( return Obx(() {
message: translate(show.value ? 'Hide Menubar' : 'Show Menubar'), if (show.isTrue && _dragging.isFalse) {
child: SizedBox( _debouncerHide.value = 1;
width: 100, }
height: 13, return Align(
child: TextButton( alignment: FractionalOffset(_fractionX.value, 0),
onHover: (bool v) { child: Offstage(
_hideColor.value = v ? Colors.white60 : Colors.white24; offstage: _dragging.isTrue,
}, child: _DraggableShowHide(
onPressed: () { dragging: _dragging,
show.value = !show.value; fractionX: _fractionX,
_hideColor.value = Colors.white24; show: show,
if (show.isTrue) { ),
_updateScreen(); ),
} );
}, });
child: Obx(() => Container(
decoration: BoxDecoration(
color: _hideColor.value,
border: Border.all(color: MyTheme.border),
borderRadius: BorderRadius.all(Radius.circular(5.0)),
),
).marginOnly(bottom: 8.0)),
))));
} }
_updateScreen() async { _updateScreen() async {
@ -255,13 +252,12 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
border: Border.all(color: MyTheme.border), border: Border.all(color: MyTheme.border),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
), ),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: menubarItems, children: menubarItems,
)), )),
_buildShowHide(context), _buildDraggableShowHide(context),
])); ]));
} }
@ -831,15 +827,13 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
qualityInitValue = qualityMaxValue; qualityInitValue = qualityMaxValue;
} }
final RxDouble qualitySliderValue = RxDouble(qualityInitValue); final RxDouble qualitySliderValue = RxDouble(qualityInitValue);
final qualityRxReplay = rxdart.ReplaySubject<double>(); final debouncerQuanlity = Debouncer<double>(
qualityRxReplay Duration(milliseconds: 1000),
.throttleTime(const Duration(milliseconds: 1000), onChanged: (double v) {
trailing: true, leading: false) setCustomValues(quality: v);
.listen((double v) { },
() async { initialValue: qualityInitValue,
await setCustomValues(quality: v); );
}();
});
final qualitySlider = Obx(() => Row( final qualitySlider = Obx(() => Row(
children: [ children: [
Slider( Slider(
@ -849,7 +843,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
divisions: 90, divisions: 90,
onChanged: (double value) { onChanged: (double value) {
qualitySliderValue.value = value; qualitySliderValue.value = value;
qualityRxReplay.add(value); debouncerQuanlity.value = value;
}, },
), ),
SizedBox( SizedBox(
@ -869,15 +863,13 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
fpsInitValue = 30; fpsInitValue = 30;
} }
final RxDouble fpsSliderValue = RxDouble(fpsInitValue); final RxDouble fpsSliderValue = RxDouble(fpsInitValue);
final fpsRxReplay = rxdart.ReplaySubject<double>(); final debouncerFps = Debouncer<double>(
fpsRxReplay Duration(milliseconds: 1000),
.throttleTime(const Duration(milliseconds: 1000), onChanged: (double v) {
trailing: true, leading: false) setCustomValues(fps: v);
.listen((double v) { },
() async { initialValue: qualityInitValue,
await setCustomValues(fps: v); );
}();
});
bool? direct; bool? direct;
try { try {
direct = ConnectionTypeState.find(widget.id).direct.value == direct = ConnectionTypeState.find(widget.id).direct.value ==
@ -898,7 +890,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
divisions: 22, divisions: 22,
onChanged: (double value) { onChanged: (double value) {
fpsSliderValue.value = value; fpsSliderValue.value = value;
fpsRxReplay.add(value); debouncerFps.value = value;
}, },
))), ))),
SizedBox( SizedBox(
@ -1352,3 +1344,91 @@ void showAuditDialog(String id, dialogManager) async {
); );
}); });
} }
class _DraggableShowHide extends StatefulWidget {
final RxDouble fractionX;
final RxBool dragging;
final RxBool show;
const _DraggableShowHide({
Key? key,
required this.fractionX,
required this.dragging,
required this.show,
}) : super(key: key);
@override
State<_DraggableShowHide> createState() => __DraggableShowHideState();
}
class __DraggableShowHideState extends State<_DraggableShowHide> {
Offset position = Offset.zero;
Size size = Size.zero;
Widget _buildDraggable(BuildContext context) {
return Draggable(
axis: Axis.horizontal,
child: Icon(
Icons.drag_indicator,
size: 15,
),
feedback: widget,
onDragStarted: (() {
final RenderObject? renderObj = context.findRenderObject();
if (renderObj != null) {
final RenderBox renderBox = renderObj as RenderBox;
size = renderBox.size;
position = renderBox.localToGlobal(Offset.zero);
}
widget.dragging.value = true;
}),
onDragEnd: (details) {
final mediaSize = MediaQueryData.fromWindow(ui.window).size;
widget.fractionX.value +=
(details.offset.dx - position.dx) / (mediaSize.width - size.width);
if (widget.fractionX.value < 0.35) {
widget.fractionX.value = 0.35;
}
if (widget.fractionX.value > 0.65) {
widget.fractionX.value = 0.65;
}
widget.dragging.value = false;
},
);
}
@override
Widget build(BuildContext context) {
final ButtonStyle buttonStyle = ButtonStyle(
minimumSize: MaterialStateProperty.all(const Size(0, 0)),
padding: MaterialStateProperty.all(EdgeInsets.zero),
);
final child = Row(
mainAxisSize: MainAxisSize.min,
children: [
_buildDraggable(context),
TextButton(
onPressed: () => setState(() {
widget.show.value = !widget.show.value;
}),
child: Obx((() => Icon(
widget.show.isTrue ? Icons.expand_less : Icons.expand_more,
size: 15,
))),
),
],
);
return TextButtonTheme(
data: TextButtonThemeData(style: buttonStyle),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: MyTheme.border),
),
child: SizedBox(
height: 15,
child: child,
),
),
);
}
}

View File

@ -246,6 +246,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.0.15" version: "0.0.15"
debounce_throttle:
dependency: "direct main"
description:
name: debounce_throttle
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
desktop_drop: desktop_drop:
dependency: "direct main" dependency: "direct main"
description: description:
@ -842,7 +849,7 @@ packages:
source: hosted source: hosted
version: "1.0.1" version: "1.0.1"
rxdart: rxdart:
dependency: "direct main" dependency: transitive
description: description:
name: rxdart name: rxdart
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
@ -885,6 +892,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.3" version: "1.0.3"
simple_observable:
dependency: transitive
description:
name: simple_observable
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

View File

@ -79,7 +79,7 @@ dependencies:
contextmenu: ^3.0.0 contextmenu: ^3.0.0
desktop_drop: ^0.3.3 desktop_drop: ^0.3.3
scroll_pos: ^0.3.0 scroll_pos: ^0.3.0
rxdart: ^0.27.5 debounce_throttle: ^2.0.0
file_picker: ^5.1.0 file_picker: ^5.1.0
flutter_svg: ^1.1.5 flutter_svg: ^1.1.5
flutter_improved_scrolling: ^0.0.3 flutter_improved_scrolling: ^0.0.3