Merge pull request #2478 from fufesou/refact_remote_menubar_draggable
remote menu, draggable hide widget
This commit is contained in:
commit
e4c1e9113a
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user