mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-09 09:31:28 +03:00
edge scroll thickness adjustment (#13445)
Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
@@ -79,6 +79,7 @@ const String kWindowEventOpenMonitorSession = "open_monitor_session";
|
|||||||
|
|
||||||
const String kOptionViewStyle = "view_style";
|
const String kOptionViewStyle = "view_style";
|
||||||
const String kOptionScrollStyle = "scroll_style";
|
const String kOptionScrollStyle = "scroll_style";
|
||||||
|
const String kOptionEdgeScrollEdgeThickness = "edge-scroll-edge-thickness";
|
||||||
const String kOptionImageQuality = "image_quality";
|
const String kOptionImageQuality = "image_quality";
|
||||||
const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs";
|
const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs";
|
||||||
const String kOptionTextureRender = "use-texture-render";
|
const String kOptionTextureRender = "use-texture-render";
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
|
|||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
|
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
|
||||||
|
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
|
||||||
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
|
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
import 'package:flutter_hbb/models/printer_model.dart';
|
import 'package:flutter_hbb/models/printer_model.dart';
|
||||||
@@ -1738,22 +1739,39 @@ class _DisplayState extends State<_Display> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final groupValue = bind.mainGetUserDefaultOption(key: kOptionScrollStyle);
|
final groupValue = bind.mainGetUserDefaultOption(key: kOptionScrollStyle);
|
||||||
|
|
||||||
|
onEdgeScrollEdgeThicknessChanged(double value) async {
|
||||||
|
await bind.mainSetUserDefaultOption(
|
||||||
|
key: kOptionEdgeScrollEdgeThickness, value: value.round().toString());
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
return _Card(title: 'Default Scroll Style', children: [
|
return _Card(title: 'Default Scroll Style', children: [
|
||||||
_Radio(context,
|
_Radio(context,
|
||||||
value: kRemoteScrollStyleAuto,
|
value: kRemoteScrollStyleAuto,
|
||||||
groupValue: groupValue,
|
groupValue: groupValue,
|
||||||
label: 'ScrollAuto',
|
label: 'ScrollAuto',
|
||||||
onChanged: isOptFixed ? null : onChanged),
|
onChanged: isOptFixed ? null : onChanged),
|
||||||
_Radio(context,
|
|
||||||
value: kRemoteScrollStyleEdge,
|
|
||||||
groupValue: groupValue,
|
|
||||||
label: 'ScrollEdge',
|
|
||||||
onChanged: isOptFixed ? null : onChanged),
|
|
||||||
_Radio(context,
|
_Radio(context,
|
||||||
value: kRemoteScrollStyleBar,
|
value: kRemoteScrollStyleBar,
|
||||||
groupValue: groupValue,
|
groupValue: groupValue,
|
||||||
label: 'Scrollbar',
|
label: 'Scrollbar',
|
||||||
onChanged: isOptFixed ? null : onChanged),
|
onChanged: isOptFixed ? null : onChanged),
|
||||||
|
_Radio(context,
|
||||||
|
value: kRemoteScrollStyleEdge,
|
||||||
|
groupValue: groupValue,
|
||||||
|
label: 'ScrollEdge',
|
||||||
|
onChanged: isOptFixed ? null : onChanged),
|
||||||
|
Offstage(
|
||||||
|
offstage: groupValue != kRemoteScrollStyleEdge,
|
||||||
|
child: EdgeThicknessControl(
|
||||||
|
value: double.tryParse(bind.mainGetUserDefaultOption(
|
||||||
|
key: kOptionEdgeScrollEdgeThickness)) ??
|
||||||
|
100.0,
|
||||||
|
onChanged: isOptionFixed(kOptionEdgeScrollEdgeThickness)
|
||||||
|
? null
|
||||||
|
: onEdgeScrollEdgeThicknessChanged,
|
||||||
|
)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -511,7 +511,7 @@ class _MonitorMenu extends StatelessWidget {
|
|||||||
menuStyle: MenuStyle(
|
menuStyle: MenuStyle(
|
||||||
padding:
|
padding:
|
||||||
MaterialStatePropertyAll(EdgeInsets.symmetric(horizontal: 6))),
|
MaterialStatePropertyAll(EdgeInsets.symmetric(horizontal: 6))),
|
||||||
menuChildrenGetter: () => [buildMonitorSubmenuWidget(context)]);
|
menuChildrenGetter: (_) => [buildMonitorSubmenuWidget(context)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildMultiMonitorMenu(BuildContext context) {
|
Widget buildMultiMonitorMenu(BuildContext context) {
|
||||||
@@ -722,7 +722,7 @@ class _ControlMenu extends StatelessWidget {
|
|||||||
color: _ToolbarTheme.blueColor,
|
color: _ToolbarTheme.blueColor,
|
||||||
hoverColor: _ToolbarTheme.hoverBlueColor,
|
hoverColor: _ToolbarTheme.hoverBlueColor,
|
||||||
ffi: ffi,
|
ffi: ffi,
|
||||||
menuChildrenGetter: () => toolbarControls(context, id, ffi).map((e) {
|
menuChildrenGetter: (_) => toolbarControls(context, id, ffi).map((e) {
|
||||||
if (e.divider) {
|
if (e.divider) {
|
||||||
return Divider();
|
return Divider();
|
||||||
} else {
|
} else {
|
||||||
@@ -933,12 +933,13 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
_screenAdjustor.updateScreen();
|
_screenAdjustor.updateScreen();
|
||||||
menuChildrenGetter() {
|
menuChildrenGetter(_IconSubmenuButtonState state) {
|
||||||
final menuChildren = <Widget>[
|
final menuChildren = <Widget>[
|
||||||
_screenAdjustor.adjustWindow(context),
|
_screenAdjustor.adjustWindow(context),
|
||||||
viewStyle(customPercent: _customPercent),
|
viewStyle(customPercent: _customPercent),
|
||||||
scrollStyle(),
|
scrollStyle(state, colorScheme),
|
||||||
imageQuality(),
|
imageQuality(),
|
||||||
codec(),
|
codec(),
|
||||||
if (ffi.connType == ConnType.defaultConn)
|
if (ffi.connType == ConnType.defaultConn)
|
||||||
@@ -1013,14 +1014,14 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
return Column(children: [
|
return Column(children: [
|
||||||
...v.map((e) {
|
...v.map((e) {
|
||||||
final isCustom = e.value == kRemoteViewStyleCustom;
|
final isCustom = e.value == kRemoteViewStyleCustom;
|
||||||
final child = isCustom
|
final child =
|
||||||
? Text(translate('Scale custom'))
|
isCustom ? Text(translate('Scale custom')) : e.child;
|
||||||
: e.child;
|
|
||||||
// Whether the current selection is already custom
|
// Whether the current selection is already custom
|
||||||
final bool isGroupCustomSelected =
|
final bool isGroupCustomSelected =
|
||||||
e.groupValue == kRemoteViewStyleCustom;
|
e.groupValue == kRemoteViewStyleCustom;
|
||||||
// Keep menu open when switching INTO custom so the slider is visible immediately
|
// Keep menu open when switching INTO custom so the slider is visible immediately
|
||||||
final bool keepOpenForThisItem = isCustom && !isGroupCustomSelected;
|
final bool keepOpenForThisItem =
|
||||||
|
isCustom && !isGroupCustomSelected;
|
||||||
return RdoMenuButton<String>(
|
return RdoMenuButton<String>(
|
||||||
value: e.value,
|
value: e.value,
|
||||||
groupValue: e.groupValue,
|
groupValue: e.groupValue,
|
||||||
@@ -1039,7 +1040,8 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
}).toList(),
|
}).toList(),
|
||||||
// Only show a divider when custom is NOT selected
|
// Only show a divider when custom is NOT selected
|
||||||
if (!isCustomSelected) Divider(),
|
if (!isCustomSelected) Divider(),
|
||||||
_customControlsIfCustomSelected(onChanged: (v) => customPercent.value = v),
|
_customControlsIfCustomSelected(
|
||||||
|
onChanged: (v) => customPercent.value = v),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1054,12 +1056,14 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
duration: Duration(milliseconds: 220),
|
duration: Duration(milliseconds: 220),
|
||||||
switchInCurve: Curves.easeOut,
|
switchInCurve: Curves.easeOut,
|
||||||
switchOutCurve: Curves.easeIn,
|
switchOutCurve: Curves.easeIn,
|
||||||
child: isCustom ? _CustomScaleMenuControls(ffi: ffi, onChanged: onChanged) : SizedBox.shrink(),
|
child: isCustom
|
||||||
|
? _CustomScaleMenuControls(ffi: ffi, onChanged: onChanged)
|
||||||
|
: SizedBox.shrink(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollStyle() {
|
scrollStyle(_IconSubmenuButtonState state, ColorScheme colorScheme) {
|
||||||
return futureBuilder(future: () async {
|
return futureBuilder(future: () async {
|
||||||
final viewStyle =
|
final viewStyle =
|
||||||
await bind.sessionGetViewStyle(sessionId: ffi.sessionId) ?? '';
|
await bind.sessionGetViewStyle(sessionId: ffi.sessionId) ?? '';
|
||||||
@@ -1067,16 +1071,34 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
viewStyle == kRemoteViewStyleCustom;
|
viewStyle == kRemoteViewStyleCustom;
|
||||||
final scrollStyle =
|
final scrollStyle =
|
||||||
await bind.sessionGetScrollStyle(sessionId: ffi.sessionId) ?? '';
|
await bind.sessionGetScrollStyle(sessionId: ffi.sessionId) ?? '';
|
||||||
return {'visible': visible, 'scrollStyle': scrollStyle};
|
final edgeScrollEdgeThickness = await bind
|
||||||
|
.sessionGetEdgeScrollEdgeThickness(sessionId: ffi.sessionId);
|
||||||
|
return {
|
||||||
|
'visible': visible,
|
||||||
|
'scrollStyle': scrollStyle,
|
||||||
|
'edgeScrollEdgeThickness': edgeScrollEdgeThickness,
|
||||||
|
};
|
||||||
}(), hasData: (data) {
|
}(), hasData: (data) {
|
||||||
final visible = data['visible'] as bool;
|
final visible = data['visible'] as bool;
|
||||||
if (!visible) return Offstage();
|
if (!visible) return Offstage();
|
||||||
final groupValue = data['scrollStyle'] as String;
|
final groupValue = data['scrollStyle'] as String;
|
||||||
onChange(String? value) async {
|
final edgeScrollEdgeThickness = data['edgeScrollEdgeThickness'] as int;
|
||||||
|
|
||||||
|
onChangeScrollStyle(String? value) async {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
await bind.sessionSetScrollStyle(
|
await bind.sessionSetScrollStyle(
|
||||||
sessionId: ffi.sessionId, value: value);
|
sessionId: ffi.sessionId, value: value);
|
||||||
widget.ffi.canvasModel.updateScrollStyle();
|
widget.ffi.canvasModel.updateScrollStyle();
|
||||||
|
state.setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeEdgeScrollEdgeThickness(double? value) async {
|
||||||
|
if (value == null) return;
|
||||||
|
final newThickness = value.round();
|
||||||
|
await bind.sessionSetEdgeScrollEdgeThickness(
|
||||||
|
sessionId: ffi.sessionId, value: newThickness);
|
||||||
|
widget.ffi.canvasModel.updateEdgeScrollEdgeThickness(newThickness);
|
||||||
|
state.setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Obx(() => Column(children: [
|
return Obx(() => Column(children: [
|
||||||
@@ -1085,17 +1107,9 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
value: kRemoteScrollStyleAuto,
|
value: kRemoteScrollStyleAuto,
|
||||||
groupValue: groupValue,
|
groupValue: groupValue,
|
||||||
onChanged: widget.ffi.canvasModel.imageOverflow.value
|
onChanged: widget.ffi.canvasModel.imageOverflow.value
|
||||||
? (value) => onChange(value)
|
? (value) => onChangeScrollStyle(value)
|
||||||
: null,
|
|
||||||
ffi: widget.ffi,
|
|
||||||
),
|
|
||||||
RdoMenuButton<String>(
|
|
||||||
child: Text(translate('ScrollEdge')),
|
|
||||||
value: kRemoteScrollStyleEdge,
|
|
||||||
groupValue: groupValue,
|
|
||||||
onChanged: widget.ffi.canvasModel.imageOverflow.value
|
|
||||||
? (value) => onChange(value)
|
|
||||||
: null,
|
: null,
|
||||||
|
closeOnActivate: groupValue != kRemoteScrollStyleEdge,
|
||||||
ffi: widget.ffi,
|
ffi: widget.ffi,
|
||||||
),
|
),
|
||||||
RdoMenuButton<String>(
|
RdoMenuButton<String>(
|
||||||
@@ -1103,10 +1117,28 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
value: kRemoteScrollStyleBar,
|
value: kRemoteScrollStyleBar,
|
||||||
groupValue: groupValue,
|
groupValue: groupValue,
|
||||||
onChanged: widget.ffi.canvasModel.imageOverflow.value
|
onChanged: widget.ffi.canvasModel.imageOverflow.value
|
||||||
? (value) => onChange(value)
|
? (value) => onChangeScrollStyle(value)
|
||||||
|
: null,
|
||||||
|
closeOnActivate: groupValue != kRemoteScrollStyleEdge,
|
||||||
|
ffi: widget.ffi,
|
||||||
|
),
|
||||||
|
RdoMenuButton<String>(
|
||||||
|
child: Text(translate('ScrollEdge')),
|
||||||
|
value: kRemoteScrollStyleEdge,
|
||||||
|
groupValue: groupValue,
|
||||||
|
closeOnActivate: false,
|
||||||
|
onChanged: widget.ffi.canvasModel.imageOverflow.value
|
||||||
|
? (value) => onChangeScrollStyle(value)
|
||||||
: null,
|
: null,
|
||||||
ffi: widget.ffi,
|
ffi: widget.ffi,
|
||||||
),
|
),
|
||||||
|
Offstage(
|
||||||
|
offstage: groupValue != kRemoteScrollStyleEdge,
|
||||||
|
child: EdgeThicknessControl(
|
||||||
|
value: edgeScrollEdgeThickness.toDouble(),
|
||||||
|
onChanged: onChangeEdgeScrollEdgeThickness,
|
||||||
|
colorScheme: colorScheme,
|
||||||
|
)),
|
||||||
Divider(),
|
Divider(),
|
||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
@@ -1193,13 +1225,16 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
class _CustomScaleMenuControls extends StatefulWidget {
|
class _CustomScaleMenuControls extends StatefulWidget {
|
||||||
final FFI ffi;
|
final FFI ffi;
|
||||||
final ValueChanged<int>? onChanged;
|
final ValueChanged<int>? onChanged;
|
||||||
const _CustomScaleMenuControls({Key? key, required this.ffi, this.onChanged}) : super(key: key);
|
const _CustomScaleMenuControls({Key? key, required this.ffi, this.onChanged})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_CustomScaleMenuControls> createState() => _CustomScaleMenuControlsState();
|
State<_CustomScaleMenuControls> createState() =>
|
||||||
|
_CustomScaleMenuControlsState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CustomScaleMenuControlsState extends CustomScaleControls<_CustomScaleMenuControls> {
|
class _CustomScaleMenuControlsState
|
||||||
|
extends CustomScaleControls<_CustomScaleMenuControls> {
|
||||||
@override
|
@override
|
||||||
FFI get ffi => widget.ffi;
|
FFI get ffi => widget.ffi;
|
||||||
|
|
||||||
@@ -1235,7 +1270,9 @@ class _CustomScaleMenuControlsState extends CustomScaleControls<_CustomScaleMenu
|
|||||||
max: 1.0,
|
max: 1.0,
|
||||||
// Use a wide range of divisions (calculated as (CustomScaleControls.maxPercent - CustomScaleControls.minPercent)) to provide ~1% precision increments.
|
// Use a wide range of divisions (calculated as (CustomScaleControls.maxPercent - CustomScaleControls.minPercent)) to provide ~1% precision increments.
|
||||||
// This allows users to set precise scale values. Lower values would require more fine-tuning via the +/- buttons, which is undesirable for big ranges.
|
// This allows users to set precise scale values. Lower values would require more fine-tuning via the +/- buttons, which is undesirable for big ranges.
|
||||||
divisions: (CustomScaleControls.maxPercent - CustomScaleControls.minPercent).round(),
|
divisions:
|
||||||
|
(CustomScaleControls.maxPercent - CustomScaleControls.minPercent)
|
||||||
|
.round(),
|
||||||
onChanged: onSliderChanged,
|
onChanged: onSliderChanged,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -1281,6 +1318,7 @@ class _RectValueThumbShape extends SliderComponentShape {
|
|||||||
final double width;
|
final double width;
|
||||||
final double height;
|
final double height;
|
||||||
final double radius;
|
final double radius;
|
||||||
|
final String unit;
|
||||||
// Optional mapper to compute display value from normalized position [0,1]
|
// Optional mapper to compute display value from normalized position [0,1]
|
||||||
// If null, falls back to linear interpolation between min and max.
|
// If null, falls back to linear interpolation between min and max.
|
||||||
final int Function(double normalized)? displayValueForNormalized;
|
final int Function(double normalized)? displayValueForNormalized;
|
||||||
@@ -1292,6 +1330,7 @@ class _RectValueThumbShape extends SliderComponentShape {
|
|||||||
required this.height,
|
required this.height,
|
||||||
required this.radius,
|
required this.radius,
|
||||||
this.displayValueForNormalized,
|
this.displayValueForNormalized,
|
||||||
|
this.unit = '%',
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1332,12 +1371,12 @@ class _RectValueThumbShape extends SliderComponentShape {
|
|||||||
final Paint paint = Paint()..color = fillColor;
|
final Paint paint = Paint()..color = fillColor;
|
||||||
canvas.drawRRect(rrect, paint);
|
canvas.drawRRect(rrect, paint);
|
||||||
|
|
||||||
// Compute displayed percent from normalized slider value.
|
// Compute displayed value from normalized slider value.
|
||||||
final int percent = displayValueForNormalized != null
|
final int displayValue = displayValueForNormalized != null
|
||||||
? displayValueForNormalized!(value)
|
? displayValueForNormalized!(value)
|
||||||
: (min + value * (max - min)).round();
|
: (min + value * (max - min)).round();
|
||||||
final TextSpan span = TextSpan(
|
final TextSpan span = TextSpan(
|
||||||
text: '$percent%',
|
text: '$displayValue$unit',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@@ -1350,7 +1389,8 @@ class _RectValueThumbShape extends SliderComponentShape {
|
|||||||
textDirection: textDirection,
|
textDirection: textDirection,
|
||||||
);
|
);
|
||||||
tp.layout(maxWidth: width - 4);
|
tp.layout(maxWidth: width - 4);
|
||||||
tp.paint(canvas, Offset(center.dx - tp.width / 2, center.dy - tp.height / 2));
|
tp.paint(
|
||||||
|
canvas, Offset(center.dx - tp.width / 2, center.dy - tp.height / 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1696,7 +1736,7 @@ class _KeyboardMenu extends StatelessWidget {
|
|||||||
ffi: ffi,
|
ffi: ffi,
|
||||||
color: _ToolbarTheme.blueColor,
|
color: _ToolbarTheme.blueColor,
|
||||||
hoverColor: _ToolbarTheme.hoverBlueColor,
|
hoverColor: _ToolbarTheme.hoverBlueColor,
|
||||||
menuChildrenGetter: () => [
|
menuChildrenGetter: (_) => [
|
||||||
keyboardMode(),
|
keyboardMode(),
|
||||||
localKeyboardType(),
|
localKeyboardType(),
|
||||||
inputSource(),
|
inputSource(),
|
||||||
@@ -1961,7 +2001,7 @@ class _ChatMenuState extends State<_ChatMenu> {
|
|||||||
ffi: widget.ffi,
|
ffi: widget.ffi,
|
||||||
color: _ToolbarTheme.blueColor,
|
color: _ToolbarTheme.blueColor,
|
||||||
hoverColor: _ToolbarTheme.hoverBlueColor,
|
hoverColor: _ToolbarTheme.hoverBlueColor,
|
||||||
menuChildrenGetter: () => [textChat(), voiceCall()]);
|
menuChildrenGetter: (_) => [textChat(), voiceCall()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2017,7 +2057,7 @@ class _VoiceCallMenu extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
menuChildrenGetter() {
|
menuChildrenGetter(_IconSubmenuButtonState state) {
|
||||||
final audioInput = AudioInput(
|
final audioInput = AudioInput(
|
||||||
builder: (devices, currentDevice, setDevice) {
|
builder: (devices, currentDevice, setDevice) {
|
||||||
return Column(
|
return Column(
|
||||||
@@ -2217,7 +2257,7 @@ class _IconSubmenuButton extends StatefulWidget {
|
|||||||
final Widget? icon;
|
final Widget? icon;
|
||||||
final Color color;
|
final Color color;
|
||||||
final Color hoverColor;
|
final Color hoverColor;
|
||||||
final List<Widget> Function() menuChildrenGetter;
|
final List<Widget> Function(_IconSubmenuButtonState state) menuChildrenGetter;
|
||||||
final MenuStyle? menuStyle;
|
final MenuStyle? menuStyle;
|
||||||
final FFI? ffi;
|
final FFI? ffi;
|
||||||
final double? width;
|
final double? width;
|
||||||
@@ -2242,6 +2282,11 @@ class _IconSubmenuButton extends StatefulWidget {
|
|||||||
class _IconSubmenuButtonState extends State<_IconSubmenuButton> {
|
class _IconSubmenuButtonState extends State<_IconSubmenuButton> {
|
||||||
bool hover = false;
|
bool hover = false;
|
||||||
|
|
||||||
|
@override // discard @protected
|
||||||
|
void setState(VoidCallback fn) {
|
||||||
|
super.setState(fn);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
assert(widget.svg != null || widget.icon != null);
|
assert(widget.svg != null || widget.icon != null);
|
||||||
@@ -2274,7 +2319,7 @@ class _IconSubmenuButtonState extends State<_IconSubmenuButton> {
|
|||||||
),
|
),
|
||||||
child: icon))),
|
child: icon))),
|
||||||
menuChildren: widget
|
menuChildren: widget
|
||||||
.menuChildrenGetter()
|
.menuChildrenGetter(this)
|
||||||
.map((e) => _buildPointerTrackWidget(e, widget.ffi))
|
.map((e) => _buildPointerTrackWidget(e, widget.ffi))
|
||||||
.toList()));
|
.toList()));
|
||||||
return MenuBar(children: [
|
return MenuBar(children: [
|
||||||
@@ -2637,3 +2682,56 @@ Widget _buildPointerTrackWidget(Widget child, FFI? ffi) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EdgeThicknessControl extends StatelessWidget {
|
||||||
|
final double value;
|
||||||
|
final ValueChanged<double>? onChanged;
|
||||||
|
final ColorScheme? colorScheme;
|
||||||
|
|
||||||
|
const EdgeThicknessControl({
|
||||||
|
Key? key,
|
||||||
|
required this.value,
|
||||||
|
this.onChanged,
|
||||||
|
this.colorScheme,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
static const double kMin = 20;
|
||||||
|
static const double kMax = 150;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final colorScheme = this.colorScheme ?? Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
final slider = SliderTheme(
|
||||||
|
data: SliderTheme.of(context).copyWith(
|
||||||
|
activeTrackColor: colorScheme.primary,
|
||||||
|
thumbColor: colorScheme.primary,
|
||||||
|
overlayColor: colorScheme.primary.withOpacity(0.1),
|
||||||
|
showValueIndicator: ShowValueIndicator.never,
|
||||||
|
thumbShape: _RectValueThumbShape(
|
||||||
|
min: EdgeThicknessControl.kMin,
|
||||||
|
max: EdgeThicknessControl.kMax,
|
||||||
|
width: 52,
|
||||||
|
height: 24,
|
||||||
|
radius: 4,
|
||||||
|
unit: 'px',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Semantics(
|
||||||
|
value: value.toInt().toString(),
|
||||||
|
child: Slider(
|
||||||
|
value: value,
|
||||||
|
min: EdgeThicknessControl.kMin,
|
||||||
|
max: EdgeThicknessControl.kMax,
|
||||||
|
divisions:
|
||||||
|
(EdgeThicknessControl.kMax - EdgeThicknessControl.kMin).round(),
|
||||||
|
semanticFormatterCallback: (double newValue) =>
|
||||||
|
"${newValue.round()}px",
|
||||||
|
onChanged: onChanged,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return slider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1667,6 +1667,7 @@ class ImageModel with ChangeNotifier {
|
|||||||
if (isDesktop || isWebDesktop) {
|
if (isDesktop || isWebDesktop) {
|
||||||
await parent.target?.canvasModel.updateViewStyle();
|
await parent.target?.canvasModel.updateViewStyle();
|
||||||
await parent.target?.canvasModel.updateScrollStyle();
|
await parent.target?.canvasModel.updateScrollStyle();
|
||||||
|
await parent.target?.canvasModel.initializeEdgeScrollEdgeThickness();
|
||||||
}
|
}
|
||||||
if (parent.target != null) {
|
if (parent.target != null) {
|
||||||
await initializeCursorAndCanvas(parent.target!);
|
await initializeCursorAndCanvas(parent.target!);
|
||||||
@@ -1914,6 +1915,8 @@ class CanvasModel with ChangeNotifier {
|
|||||||
// scroll offset y percent
|
// scroll offset y percent
|
||||||
double _scrollY = 0.0;
|
double _scrollY = 0.0;
|
||||||
ScrollStyle _scrollStyle = ScrollStyle.scrollauto;
|
ScrollStyle _scrollStyle = ScrollStyle.scrollauto;
|
||||||
|
// edge scroll mode: trigger scrolling when the cursor is close to the edge of the view
|
||||||
|
int _edgeScrollEdgeThickness = 100;
|
||||||
// tracks whether edge scroll should be active, prevents spurious
|
// tracks whether edge scroll should be active, prevents spurious
|
||||||
// scrolling when the cursor enters the view from outside
|
// scrolling when the cursor enters the view from outside
|
||||||
EdgeScrollState _edgeScrollState = EdgeScrollState.inactive;
|
EdgeScrollState _edgeScrollState = EdgeScrollState.inactive;
|
||||||
@@ -2090,11 +2093,11 @@ class CanvasModel with ChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateScrollStyle() async {
|
Future<void> updateScrollStyle() async {
|
||||||
final style = await bind.sessionGetScrollStyle(sessionId: sessionId);
|
final style = await bind.sessionGetScrollStyle(sessionId: sessionId);
|
||||||
|
|
||||||
_scrollStyle = style != null
|
_scrollStyle = style != null
|
||||||
? ScrollStyle.fromString(style!)
|
? ScrollStyle.fromString(style)
|
||||||
: ScrollStyle.scrollauto;
|
: ScrollStyle.scrollauto;
|
||||||
|
|
||||||
if (_scrollStyle != ScrollStyle.scrollauto) {
|
if (_scrollStyle != ScrollStyle.scrollauto) {
|
||||||
@@ -2104,7 +2107,20 @@ class CanvasModel with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
update(double x, double y, double scale) {
|
Future<void> initializeEdgeScrollEdgeThickness() async {
|
||||||
|
final savedValue = await bind.sessionGetEdgeScrollEdgeThickness(sessionId: sessionId);
|
||||||
|
|
||||||
|
if (savedValue != null) {
|
||||||
|
_edgeScrollEdgeThickness = savedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateEdgeScrollEdgeThickness(int newThickness) {
|
||||||
|
_edgeScrollEdgeThickness = newThickness;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(double x, double y, double scale) {
|
||||||
_x = x;
|
_x = x;
|
||||||
_y = y;
|
_y = y;
|
||||||
_scale = scale;
|
_scale = scale;
|
||||||
@@ -2224,9 +2240,6 @@ class CanvasModel with ChangeNotifier {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger scrolling when the cursor is close to an edge
|
|
||||||
const double edgeThickness = 100;
|
|
||||||
|
|
||||||
if (_edgeScrollState == EdgeScrollState.armed) {
|
if (_edgeScrollState == EdgeScrollState.armed) {
|
||||||
// Edge scroll is armed to become active once the cursor
|
// Edge scroll is armed to become active once the cursor
|
||||||
// is observed within the rectangle interior to the
|
// is observed within the rectangle interior to the
|
||||||
@@ -2235,7 +2248,7 @@ class CanvasModel with ChangeNotifier {
|
|||||||
// doesn't happen yet.
|
// doesn't happen yet.
|
||||||
final clientArea = Rect.fromLTWH(0, 0, size.width, size.height);
|
final clientArea = Rect.fromLTWH(0, 0, size.width, size.height);
|
||||||
|
|
||||||
final innerZone = clientArea.deflate(edgeThickness);
|
final innerZone = clientArea.deflate(_edgeScrollEdgeThickness.toDouble());
|
||||||
|
|
||||||
if (innerZone.contains(Offset(x, y))) {
|
if (innerZone.contains(Offset(x, y))) {
|
||||||
_edgeScrollState = EdgeScrollState.active;
|
_edgeScrollState = EdgeScrollState.active;
|
||||||
@@ -2248,16 +2261,16 @@ class CanvasModel with ChangeNotifier {
|
|||||||
var dxOffset = 0.0;
|
var dxOffset = 0.0;
|
||||||
var dyOffset = 0.0;
|
var dyOffset = 0.0;
|
||||||
|
|
||||||
if (x < edgeThickness) {
|
if (x < _edgeScrollEdgeThickness) {
|
||||||
dxOffset = x - edgeThickness;
|
dxOffset = x - _edgeScrollEdgeThickness;
|
||||||
} else if (x >= size.width - edgeThickness) {
|
} else if (x >= size.width - _edgeScrollEdgeThickness) {
|
||||||
dxOffset = x - (size.width - edgeThickness);
|
dxOffset = x - (size.width - _edgeScrollEdgeThickness);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (y < edgeThickness) {
|
if (y < _edgeScrollEdgeThickness) {
|
||||||
dyOffset = y - edgeThickness;
|
dyOffset = y - _edgeScrollEdgeThickness;
|
||||||
} else if (y >= size.height - edgeThickness) {
|
} else if (y >= size.height - _edgeScrollEdgeThickness) {
|
||||||
dyOffset = y - (size.height - edgeThickness);
|
dyOffset = y - (size.height - _edgeScrollEdgeThickness);
|
||||||
}
|
}
|
||||||
|
|
||||||
var encroachment = Vector2(dxOffset, dyOffset);
|
var encroachment = Vector2(dxOffset, dyOffset);
|
||||||
@@ -3580,6 +3593,7 @@ class FFI {
|
|||||||
dialogManager.dismissAll();
|
dialogManager.dismissAll();
|
||||||
await canvasModel.updateViewStyle();
|
await canvasModel.updateViewStyle();
|
||||||
await canvasModel.updateScrollStyle();
|
await canvasModel.updateScrollStyle();
|
||||||
|
await canvasModel.initializeEdgeScrollEdgeThickness();
|
||||||
for (final cb in imageModel.callbacksOnFirstImage) {
|
for (final cb in imageModel.callbacksOnFirstImage) {
|
||||||
cb(id);
|
cb(id);
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule libs/hbb_common updated: a4053b929b...9b53baeffe
@@ -1976,13 +1976,24 @@ impl LoginConfigHandler {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `value` - The view style to be saved.
|
/// * `value` - The scroll style to be saved.
|
||||||
pub fn save_scroll_style(&mut self, value: String) {
|
pub fn save_scroll_style(&mut self, value: String) {
|
||||||
let mut config = self.load_config();
|
let mut config = self.load_config();
|
||||||
config.scroll_style = value;
|
config.scroll_style = value;
|
||||||
self.save_config(config);
|
self.save_config(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Save edge scroll edge thickness to the current config.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `value` - The edge thickness to be saved.
|
||||||
|
pub fn save_edge_scroll_edge_thickness(&mut self, value: i32) {
|
||||||
|
let mut config = self.load_config();
|
||||||
|
config.edge_scroll_edge_thickness = value;
|
||||||
|
self.save_config(config);
|
||||||
|
}
|
||||||
|
|
||||||
/// Set a ui config of flutter for handler's [`PeerConfig`].
|
/// Set a ui config of flutter for handler's [`PeerConfig`].
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
|||||||
@@ -273,7 +273,10 @@ pub fn session_take_screenshot(session_id: SessionID, display: usize) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn session_handle_screenshot(#[allow(unused_variables)] session_id: SessionID, action: String) -> String {
|
pub fn session_handle_screenshot(
|
||||||
|
#[allow(unused_variables)] session_id: SessionID,
|
||||||
|
action: String,
|
||||||
|
) -> String {
|
||||||
crate::client::screenshot::handle_screenshot(action)
|
crate::client::screenshot::handle_screenshot(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,6 +396,20 @@ pub fn session_set_scroll_style(session_id: SessionID, value: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn session_get_edge_scroll_edge_thickness(session_id: SessionID) -> Option<i32> {
|
||||||
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
|
Some(session.get_edge_scroll_edge_thickness())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn session_set_edge_scroll_edge_thickness(session_id: SessionID, value: i32) {
|
||||||
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
|
session.save_edge_scroll_edge_thickness(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_get_image_quality(session_id: SessionID) -> Option<String> {
|
pub fn session_get_image_quality(session_id: SessionID) -> Option<String> {
|
||||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
Some(session.get_image_quality())
|
Some(session.get_image_quality())
|
||||||
|
|||||||
@@ -238,6 +238,10 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
self.lc.read().unwrap().scroll_style.clone()
|
self.lc.read().unwrap().scroll_style.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_edge_scroll_edge_thickness(&self) -> i32 {
|
||||||
|
self.lc.read().unwrap().edge_scroll_edge_thickness
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_image_quality(&self) -> String {
|
pub fn get_image_quality(&self) -> String {
|
||||||
self.lc.read().unwrap().image_quality.clone()
|
self.lc.read().unwrap().image_quality.clone()
|
||||||
}
|
}
|
||||||
@@ -350,6 +354,10 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
self.lc.write().unwrap().save_scroll_style(value);
|
self.lc.write().unwrap().save_scroll_style(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn save_edge_scroll_edge_thickness(&self, value: i32) {
|
||||||
|
self.lc.write().unwrap().save_edge_scroll_edge_thickness(value);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn save_flutter_option(&self, k: String, v: String) {
|
pub fn save_flutter_option(&self, k: String, v: String) {
|
||||||
self.lc.write().unwrap().save_ui_flutter(k, v);
|
self.lc.write().unwrap().save_ui_flutter(k, v);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user