flutter_desktop: custom image quality
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
59f0ffa82f
commit
4b9805b0f3
@ -427,7 +427,45 @@ class CustomAlertDialog extends StatelessWidget {
|
|||||||
void msgBox(
|
void msgBox(
|
||||||
String type, String title, String text, OverlayDialogManager dialogManager,
|
String type, String title, String text, OverlayDialogManager dialogManager,
|
||||||
{bool? hasCancel}) {
|
{bool? hasCancel}) {
|
||||||
var wrap = (String text, void Function() onPressed) => ButtonTheme(
|
dialogManager.dismissAll();
|
||||||
|
List<Widget> buttons = [];
|
||||||
|
if (type != "connecting" && type != "success" && !type.contains("nook")) {
|
||||||
|
buttons.insert(
|
||||||
|
0,
|
||||||
|
getMsgBoxButton(translate('OK'), () {
|
||||||
|
dialogManager.dismissAll();
|
||||||
|
// https://github.com/fufesou/rustdesk/blob/5e9a31340b899822090a3731769ae79c6bf5f3e5/src/ui/common.tis#L263
|
||||||
|
if (!type.contains("custom")) {
|
||||||
|
closeConnection();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
hasCancel ??= !type.contains("error") &&
|
||||||
|
!type.contains("nocancel") &&
|
||||||
|
type != "restarting";
|
||||||
|
if (hasCancel) {
|
||||||
|
buttons.insert(
|
||||||
|
0,
|
||||||
|
getMsgBoxButton(translate('Cancel'), () {
|
||||||
|
dialogManager.dismissAll();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// TODO: test this button
|
||||||
|
if (type.contains("hasclose")) {
|
||||||
|
buttons.insert(
|
||||||
|
0,
|
||||||
|
getMsgBoxButton(translate('Close'), () {
|
||||||
|
dialogManager.dismissAll();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
dialogManager.show((setState, close) => CustomAlertDialog(
|
||||||
|
title: Text(translate(title), style: TextStyle(fontSize: 21)),
|
||||||
|
content: Text(translate(text), style: TextStyle(fontSize: 15)),
|
||||||
|
actions: buttons));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getMsgBoxButton(String text, void Function() onPressed) {
|
||||||
|
return ButtonTheme(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
//limits the touch area to the button area
|
//limits the touch area to the button area
|
||||||
@ -439,44 +477,14 @@ void msgBox(
|
|||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
child:
|
child:
|
||||||
Text(translate(text), style: TextStyle(color: MyTheme.accent))));
|
Text(translate(text), style: TextStyle(color: MyTheme.accent))));
|
||||||
|
}
|
||||||
|
|
||||||
|
void msgBoxCommon(OverlayDialogManager dialogManager, String title,
|
||||||
|
Widget content, List<Widget> buttons) {
|
||||||
dialogManager.dismissAll();
|
dialogManager.dismissAll();
|
||||||
List<Widget> buttons = [];
|
|
||||||
if (type != "connecting" && type != "success" && type.indexOf("nook") < 0) {
|
|
||||||
buttons.insert(
|
|
||||||
0,
|
|
||||||
wrap(translate('OK'), () {
|
|
||||||
dialogManager.dismissAll();
|
|
||||||
// https://github.com/fufesou/rustdesk/blob/5e9a31340b899822090a3731769ae79c6bf5f3e5/src/ui/common.tis#L263
|
|
||||||
if (type.indexOf("custom") < 0) {
|
|
||||||
closeConnection();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
if (hasCancel == null) {
|
|
||||||
// hasCancel = type != 'error';
|
|
||||||
hasCancel = type.indexOf("error") < 0 &&
|
|
||||||
type.indexOf("nocancel") < 0 &&
|
|
||||||
type != "restarting";
|
|
||||||
}
|
|
||||||
if (hasCancel) {
|
|
||||||
buttons.insert(
|
|
||||||
0,
|
|
||||||
wrap(translate('Cancel'), () {
|
|
||||||
dialogManager.dismissAll();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
// TODO: test this button
|
|
||||||
if (type.indexOf("hasclose") >= 0) {
|
|
||||||
buttons.insert(
|
|
||||||
0,
|
|
||||||
wrap(translate('Close'), () {
|
|
||||||
dialogManager.dismissAll();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
dialogManager.show((setState, close) => CustomAlertDialog(
|
dialogManager.show((setState, close) => CustomAlertDialog(
|
||||||
title: Text(translate(title), style: TextStyle(fontSize: 21)),
|
title: Text(translate(title), style: TextStyle(fontSize: 21)),
|
||||||
content: Text(translate(text), style: TextStyle(fontSize: 15)),
|
content: content,
|
||||||
actions: buttons));
|
actions: buttons));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,13 +503,13 @@ const G = M * K;
|
|||||||
|
|
||||||
String readableFileSize(double size) {
|
String readableFileSize(double size) {
|
||||||
if (size < K) {
|
if (size < K) {
|
||||||
return size.toStringAsFixed(2) + " B";
|
return "${size.toStringAsFixed(2)} B";
|
||||||
} else if (size < M) {
|
} else if (size < M) {
|
||||||
return (size / K).toStringAsFixed(2) + " KB";
|
return "${(size / K).toStringAsFixed(2)} KB";
|
||||||
} else if (size < G) {
|
} else if (size < G) {
|
||||||
return (size / M).toStringAsFixed(2) + " MB";
|
return "${(size / M).toStringAsFixed(2)} MB";
|
||||||
} else {
|
} else {
|
||||||
return (size / G).toStringAsFixed(2) + " GB";
|
return "${(size / G).toStringAsFixed(2)} GB";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,10 @@ const String kAppTypeDesktopPortForward = "port forward";
|
|||||||
const String kTabLabelHomePage = "Home";
|
const String kTabLabelHomePage = "Home";
|
||||||
const String kTabLabelSettingPage = "Settings";
|
const String kTabLabelSettingPage = "Settings";
|
||||||
|
|
||||||
const int kDefaultDisplayWidth = 1280;
|
const int kMobileDefaultDisplayWidth = 720;
|
||||||
const int kDefaultDisplayHeight = 720;
|
const int kMobileDefaultDisplayHeight = 1280;
|
||||||
|
|
||||||
|
const int kDesktopDefaultDisplayWidth = 1080;
|
||||||
|
const int kDesktopDefaultDisplayHeight = 720;
|
||||||
|
|
||||||
const kInvalidValueStr = "InvalidValueStr";
|
const kInvalidValueStr = "InvalidValueStr";
|
||||||
|
@ -61,6 +61,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
final args = jsonDecode(call.arguments);
|
final args = jsonDecode(call.arguments);
|
||||||
final id = args['id'];
|
final id = args['id'];
|
||||||
window_on_top(windowId());
|
window_on_top(windowId());
|
||||||
|
ConnectionTypeState.init(id);
|
||||||
tabController.add(TabInfo(
|
tabController.add(TabInfo(
|
||||||
key: id,
|
key: id,
|
||||||
label: id,
|
label: id,
|
||||||
@ -108,7 +109,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
},
|
},
|
||||||
tabBuilder: (key, icon, label, themeConf) => Obx(() {
|
tabBuilder: (key, icon, label, themeConf) => Obx(() {
|
||||||
final connectionType = ConnectionTypeState.find(key);
|
final connectionType = ConnectionTypeState.find(key);
|
||||||
if (!ConnectionTypeState.find(key).isValid()) {
|
if (!connectionType.isValid()) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
@ -689,11 +689,11 @@ class ImagePaint extends StatelessWidget {
|
|||||||
width: c.getDisplayWidth() * s,
|
width: c.getDisplayWidth() * s,
|
||||||
height: c.getDisplayHeight() * s,
|
height: c.getDisplayHeight() * s,
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
painter: new ImagePainter(image: m.image, x: 0, y: 0, scale: s),
|
painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s),
|
||||||
));
|
));
|
||||||
return Center(
|
return Center(
|
||||||
child: NotificationListener<ScrollNotification>(
|
child: NotificationListener<ScrollNotification>(
|
||||||
onNotification: (_notification) {
|
onNotification: (notification) {
|
||||||
final percentX = _horizontal.position.extentBefore /
|
final percentX = _horizontal.position.extentBefore /
|
||||||
(_horizontal.position.extentBefore +
|
(_horizontal.position.extentBefore +
|
||||||
_horizontal.position.extentInside +
|
_horizontal.position.extentInside +
|
||||||
@ -716,8 +716,8 @@ class ImagePaint extends StatelessWidget {
|
|||||||
width: c.size.width,
|
width: c.size.width,
|
||||||
height: c.size.height,
|
height: c.size.height,
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
painter: new ImagePainter(
|
painter:
|
||||||
image: m.image, x: c.x / s, y: c.y / s, scale: s),
|
ImagePainter(image: m.image, x: c.x / s, y: c.y / s, scale: s),
|
||||||
));
|
));
|
||||||
return _buildListener(imageWidget);
|
return _buildListener(imageWidget);
|
||||||
}
|
}
|
||||||
@ -771,7 +771,7 @@ class CursorPaint extends StatelessWidget {
|
|||||||
// final adjust = m.adjustForKeyboard();
|
// final adjust = m.adjustForKeyboard();
|
||||||
var s = c.scale;
|
var s = c.scale;
|
||||||
return CustomPaint(
|
return CustomPaint(
|
||||||
painter: new ImagePainter(
|
painter: ImagePainter(
|
||||||
image: m.image,
|
image: m.image,
|
||||||
x: m.x * s - m.hotx + c.x,
|
x: m.x * s - m.hotx + c.x,
|
||||||
y: m.y * s - m.hoty + c.y,
|
y: m.y * s - m.hoty + c.y,
|
||||||
@ -796,15 +796,16 @@ class ImagePainter extends CustomPainter {
|
|||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
if (image == null) return;
|
if (image == null) return;
|
||||||
|
if (x.isNaN || y.isNaN) return;
|
||||||
canvas.scale(scale, scale);
|
canvas.scale(scale, scale);
|
||||||
// https://github.com/flutter/flutter/issues/76187#issuecomment-784628161
|
// https://github.com/flutter/flutter/issues/76187#issuecomment-784628161
|
||||||
// https://api.flutter-io.cn/flutter/dart-ui/FilterQuality.html
|
// https://api.flutter-io.cn/flutter/dart-ui/FilterQuality.html
|
||||||
var paint = new Paint();
|
var paint = Paint();
|
||||||
paint.filterQuality = FilterQuality.medium;
|
paint.filterQuality = FilterQuality.medium;
|
||||||
if (scale > 10.00000) {
|
if (scale > 10.00000) {
|
||||||
paint.filterQuality = FilterQuality.high;
|
paint.filterQuality = FilterQuality.high;
|
||||||
}
|
}
|
||||||
canvas.drawImage(image!, new Offset(x, y), paint);
|
canvas.drawImage(image!, Offset(x, y), paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -97,6 +97,9 @@ class MenuConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class MenuEntryBase<T> {
|
abstract class MenuEntryBase<T> {
|
||||||
|
bool dismissOnClicked;
|
||||||
|
|
||||||
|
MenuEntryBase({this.dismissOnClicked = false});
|
||||||
List<mod_menu.PopupMenuEntry<T>> build(BuildContext context, MenuConfig conf);
|
List<mod_menu.PopupMenuEntry<T>> build(BuildContext context, MenuConfig conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,9 +115,19 @@ class MenuEntryDivider<T> extends MenuEntryBase<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef RadioOptionsGetter = List<Tuple2<String, String>> Function();
|
class MenuEntryRadioOption {
|
||||||
|
String text;
|
||||||
|
String value;
|
||||||
|
bool dismissOnClicked;
|
||||||
|
|
||||||
|
MenuEntryRadioOption(
|
||||||
|
{required this.text, required this.value, this.dismissOnClicked = false});
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef RadioOptionsGetter = List<MenuEntryRadioOption> Function();
|
||||||
typedef RadioCurOptionGetter = Future<String> Function();
|
typedef RadioCurOptionGetter = Future<String> Function();
|
||||||
typedef RadioOptionSetter = Future<void> Function(String);
|
typedef RadioOptionSetter = Future<void> Function(
|
||||||
|
String oldValue, String newValue);
|
||||||
|
|
||||||
class MenuEntryRadioUtils<T> {}
|
class MenuEntryRadioUtils<T> {}
|
||||||
|
|
||||||
@ -129,24 +142,28 @@ class MenuEntryRadios<T> extends MenuEntryBase<T> {
|
|||||||
{required this.text,
|
{required this.text,
|
||||||
required this.optionsGetter,
|
required this.optionsGetter,
|
||||||
required this.curOptionGetter,
|
required this.curOptionGetter,
|
||||||
required this.optionSetter}) {
|
required this.optionSetter,
|
||||||
|
dismissOnClicked = false})
|
||||||
|
: super(dismissOnClicked: dismissOnClicked) {
|
||||||
() async {
|
() async {
|
||||||
_curOption.value = await curOptionGetter();
|
_curOption.value = await curOptionGetter();
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Tuple2<String, String>> get options => optionsGetter();
|
List<MenuEntryRadioOption> get options => optionsGetter();
|
||||||
RxString get curOption => _curOption;
|
RxString get curOption => _curOption;
|
||||||
setOption(String option) async {
|
setOption(String option) async {
|
||||||
await optionSetter(option);
|
await optionSetter(_curOption.value, option);
|
||||||
|
if (_curOption.value != option) {
|
||||||
final opt = await curOptionGetter();
|
final opt = await curOptionGetter();
|
||||||
if (_curOption.value != opt) {
|
if (_curOption.value != opt) {
|
||||||
_curOption.value = opt;
|
_curOption.value = opt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod_menu.PopupMenuEntry<T> _buildMenuItem(
|
mod_menu.PopupMenuEntry<T> _buildMenuItem(
|
||||||
BuildContext context, MenuConfig conf, Tuple2<String, String> opt) {
|
BuildContext context, MenuConfig conf, MenuEntryRadioOption opt) {
|
||||||
return mod_menu.PopupMenuItem(
|
return mod_menu.PopupMenuItem(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
height: conf.height,
|
height: conf.height,
|
||||||
@ -157,7 +174,7 @@ class MenuEntryRadios<T> extends MenuEntryBase<T> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
opt.item1,
|
opt.text,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontSize: MenuConfig.fontSize,
|
fontSize: MenuConfig.fontSize,
|
||||||
@ -169,7 +186,7 @@ class MenuEntryRadios<T> extends MenuEntryBase<T> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 20.0,
|
width: 20.0,
|
||||||
height: 20.0,
|
height: 20.0,
|
||||||
child: Obx(() => opt.item2 == curOption.value
|
child: Obx(() => opt.value == curOption.value
|
||||||
? Icon(
|
? Icon(
|
||||||
Icons.check,
|
Icons.check,
|
||||||
color: conf.commonColor,
|
color: conf.commonColor,
|
||||||
@ -180,9 +197,10 @@ class MenuEntryRadios<T> extends MenuEntryBase<T> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (opt.item2 != curOption.value) {
|
if (opt.dismissOnClicked && Navigator.canPop(context)) {
|
||||||
setOption(opt.item2);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
|
setOption(opt.value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -206,24 +224,28 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
|||||||
{required this.text,
|
{required this.text,
|
||||||
required this.optionsGetter,
|
required this.optionsGetter,
|
||||||
required this.curOptionGetter,
|
required this.curOptionGetter,
|
||||||
required this.optionSetter}) {
|
required this.optionSetter,
|
||||||
|
dismissOnClicked = false})
|
||||||
|
: super(dismissOnClicked: dismissOnClicked) {
|
||||||
() async {
|
() async {
|
||||||
_curOption.value = await curOptionGetter();
|
_curOption.value = await curOptionGetter();
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Tuple2<String, String>> get options => optionsGetter();
|
List<MenuEntryRadioOption> get options => optionsGetter();
|
||||||
RxString get curOption => _curOption;
|
RxString get curOption => _curOption;
|
||||||
setOption(String option) async {
|
setOption(String option) async {
|
||||||
await optionSetter(option);
|
await optionSetter(_curOption.value, option);
|
||||||
|
if (_curOption.value != option) {
|
||||||
final opt = await curOptionGetter();
|
final opt = await curOptionGetter();
|
||||||
if (_curOption.value != opt) {
|
if (_curOption.value != opt) {
|
||||||
_curOption.value = opt;
|
_curOption.value = opt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod_menu.PopupMenuEntry<T> _buildSecondMenu(
|
mod_menu.PopupMenuEntry<T> _buildSecondMenu(
|
||||||
BuildContext context, MenuConfig conf, Tuple2<String, String> opt) {
|
BuildContext context, MenuConfig conf, MenuEntryRadioOption opt) {
|
||||||
return mod_menu.PopupMenuItem(
|
return mod_menu.PopupMenuItem(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
height: conf.height,
|
height: conf.height,
|
||||||
@ -234,7 +256,7 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
opt.item1,
|
opt.text,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontSize: MenuConfig.fontSize,
|
fontSize: MenuConfig.fontSize,
|
||||||
@ -246,7 +268,7 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 20.0,
|
width: 20.0,
|
||||||
height: 20.0,
|
height: 20.0,
|
||||||
child: Obx(() => opt.item2 == curOption.value
|
child: Obx(() => opt.value == curOption.value
|
||||||
? Icon(
|
? Icon(
|
||||||
Icons.check,
|
Icons.check,
|
||||||
color: conf.commonColor,
|
color: conf.commonColor,
|
||||||
@ -257,9 +279,10 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (opt.item2 != curOption.value) {
|
if (opt.dismissOnClicked && Navigator.canPop(context)) {
|
||||||
setOption(opt.item2);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
|
setOption(opt.value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -303,7 +326,8 @@ typedef SwitchSetter = Future<void> Function(bool);
|
|||||||
abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
||||||
final String text;
|
final String text;
|
||||||
|
|
||||||
MenuEntrySwitchBase({required this.text});
|
MenuEntrySwitchBase({required this.text, required dismissOnClicked})
|
||||||
|
: super(dismissOnClicked: dismissOnClicked);
|
||||||
|
|
||||||
RxBool get curOption;
|
RxBool get curOption;
|
||||||
Future<void> setOption(bool option);
|
Future<void> setOption(bool option);
|
||||||
@ -333,11 +357,20 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
|||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: Obx(() => Switch(
|
child: Obx(() => Switch(
|
||||||
value: curOption.value,
|
value: curOption.value,
|
||||||
onChanged: (v) => setOption(v),
|
onChanged: (v) {
|
||||||
|
if (super.dismissOnClicked &&
|
||||||
|
Navigator.canPop(context)) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
setOption(v);
|
||||||
|
},
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
])),
|
])),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
if (super.dismissOnClicked && Navigator.canPop(context)) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
setOption(!curOption.value);
|
setOption(!curOption.value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -352,8 +385,11 @@ class MenuEntrySwitch<T> extends MenuEntrySwitchBase<T> {
|
|||||||
final RxBool _curOption = false.obs;
|
final RxBool _curOption = false.obs;
|
||||||
|
|
||||||
MenuEntrySwitch(
|
MenuEntrySwitch(
|
||||||
{required String text, required this.getter, required this.setter})
|
{required String text,
|
||||||
: super(text: text) {
|
required this.getter,
|
||||||
|
required this.setter,
|
||||||
|
dismissOnClicked = false})
|
||||||
|
: super(text: text, dismissOnClicked: dismissOnClicked) {
|
||||||
() async {
|
() async {
|
||||||
_curOption.value = await getter();
|
_curOption.value = await getter();
|
||||||
}();
|
}();
|
||||||
@ -379,8 +415,11 @@ class MenuEntrySwitch2<T> extends MenuEntrySwitchBase<T> {
|
|||||||
final SwitchSetter setter;
|
final SwitchSetter setter;
|
||||||
|
|
||||||
MenuEntrySwitch2(
|
MenuEntrySwitch2(
|
||||||
{required String text, required this.getter, required this.setter})
|
{required String text,
|
||||||
: super(text: text);
|
required this.getter,
|
||||||
|
required this.setter,
|
||||||
|
dismissOnClicked = false})
|
||||||
|
: super(text: text, dismissOnClicked: dismissOnClicked);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RxBool get curOption => getter();
|
RxBool get curOption => getter();
|
||||||
@ -394,10 +433,7 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
|||||||
final String text;
|
final String text;
|
||||||
final List<MenuEntryBase<T>> entries;
|
final List<MenuEntryBase<T>> entries;
|
||||||
|
|
||||||
MenuEntrySubMenu({
|
MenuEntrySubMenu({required this.text, required this.entries});
|
||||||
required this.text,
|
|
||||||
required this.entries,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<mod_menu.PopupMenuEntry<T>> build(
|
List<mod_menu.PopupMenuEntry<T>> build(
|
||||||
@ -438,10 +474,11 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
|||||||
final Widget Function(TextStyle? style) childBuilder;
|
final Widget Function(TextStyle? style) childBuilder;
|
||||||
Function() proc;
|
Function() proc;
|
||||||
|
|
||||||
MenuEntryButton({
|
MenuEntryButton(
|
||||||
required this.childBuilder,
|
{required this.childBuilder,
|
||||||
required this.proc,
|
required this.proc,
|
||||||
});
|
dismissOnClicked = false})
|
||||||
|
: super(dismissOnClicked: dismissOnClicked);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<mod_menu.PopupMenuEntry<T>> build(
|
List<mod_menu.PopupMenuEntry<T>> build(
|
||||||
@ -461,6 +498,9 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
|||||||
fontWeight: FontWeight.normal),
|
fontWeight: FontWeight.normal),
|
||||||
)),
|
)),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
if (super.dismissOnClicked && Navigator.canPop(context)) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
proc();
|
proc();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -290,9 +290,9 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
proc: () {
|
proc: () {
|
||||||
Navigator.pop(context);
|
|
||||||
bind.sessionRefresh(id: widget.id);
|
bind.sessionRefresh(id: widget.id);
|
||||||
},
|
},
|
||||||
|
dismissOnClicked: true,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
displayMenu.add(MenuEntryButton<String>(
|
displayMenu.add(MenuEntryButton<String>(
|
||||||
@ -301,9 +301,9 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
proc: () {
|
proc: () {
|
||||||
Navigator.pop(context);
|
|
||||||
showSetOSPassword(widget.id, false, widget.ffi.dialogManager);
|
showSetOSPassword(widget.id, false, widget.ffi.dialogManager);
|
||||||
},
|
},
|
||||||
|
dismissOnClicked: true,
|
||||||
));
|
));
|
||||||
|
|
||||||
if (!isWebDesktop) {
|
if (!isWebDesktop) {
|
||||||
@ -314,7 +314,6 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
proc: () {
|
proc: () {
|
||||||
Navigator.pop(context);
|
|
||||||
() async {
|
() async {
|
||||||
ClipboardData? data =
|
ClipboardData? data =
|
||||||
await Clipboard.getData(Clipboard.kTextPlain);
|
await Clipboard.getData(Clipboard.kTextPlain);
|
||||||
@ -323,6 +322,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
},
|
},
|
||||||
|
dismissOnClicked: true,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,9 +332,9 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
proc: () {
|
proc: () {
|
||||||
Navigator.pop(context);
|
|
||||||
widget.ffi.cursorModel.reset();
|
widget.ffi.cursorModel.reset();
|
||||||
},
|
},
|
||||||
|
dismissOnClicked: true,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,9 +346,9 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
proc: () {
|
proc: () {
|
||||||
Navigator.pop(context);
|
|
||||||
bind.sessionCtrlAltDel(id: widget.id);
|
bind.sessionCtrlAltDel(id: widget.id);
|
||||||
},
|
},
|
||||||
|
dismissOnClicked: true,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,9 +358,9 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
proc: () {
|
proc: () {
|
||||||
Navigator.pop(context);
|
|
||||||
bind.sessionLockScreen(id: widget.id);
|
bind.sessionLockScreen(id: widget.id);
|
||||||
},
|
},
|
||||||
|
dismissOnClicked: true,
|
||||||
));
|
));
|
||||||
|
|
||||||
if (pi.platform == 'Windows') {
|
if (pi.platform == 'Windows') {
|
||||||
@ -371,13 +371,13 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
style: style,
|
style: style,
|
||||||
)),
|
)),
|
||||||
proc: () {
|
proc: () {
|
||||||
Navigator.pop(context);
|
|
||||||
RxBool blockInput = BlockInputState.find(widget.id);
|
RxBool blockInput = BlockInputState.find(widget.id);
|
||||||
bind.sessionToggleOption(
|
bind.sessionToggleOption(
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
value: '${blockInput.value ? "un" : ""}block-input');
|
value: '${blockInput.value ? "un" : ""}block-input');
|
||||||
blockInput.value = !blockInput.value;
|
blockInput.value = !blockInput.value;
|
||||||
},
|
},
|
||||||
|
dismissOnClicked: true,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -392,9 +392,9 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
proc: () {
|
proc: () {
|
||||||
Navigator.pop(context);
|
|
||||||
showRestartRemoteDevice(pi, widget.id, gFFI.dialogManager);
|
showRestartRemoteDevice(pi, widget.id, gFFI.dialogManager);
|
||||||
},
|
},
|
||||||
|
dismissOnClicked: true,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,44 +406,54 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
MenuEntryRadios<String>(
|
MenuEntryRadios<String>(
|
||||||
text: translate('Ratio'),
|
text: translate('Ratio'),
|
||||||
optionsGetter: () => [
|
optionsGetter: () => [
|
||||||
Tuple2<String, String>(translate('Scale original'), 'original'),
|
MenuEntryRadioOption(
|
||||||
Tuple2<String, String>(translate('Scale adaptive'), 'adaptive'),
|
text: translate('Scale original'), value: 'original'),
|
||||||
|
MenuEntryRadioOption(
|
||||||
|
text: translate('Scale adaptive'), value: 'adaptive'),
|
||||||
],
|
],
|
||||||
curOptionGetter: () async {
|
curOptionGetter: () async {
|
||||||
return await bind.sessionGetOption(
|
return await bind.sessionGetOption(
|
||||||
id: widget.id, arg: 'view-style') ??
|
id: widget.id, arg: 'view-style') ??
|
||||||
'adaptive';
|
'adaptive';
|
||||||
},
|
},
|
||||||
optionSetter: (String v) async {
|
optionSetter: (String oldValue, String newValue) async {
|
||||||
await bind.sessionPeerOption(
|
await bind.sessionPeerOption(
|
||||||
id: widget.id, name: "view-style", value: v);
|
id: widget.id, name: "view-style", value: newValue);
|
||||||
widget.ffi.canvasModel.updateViewStyle();
|
widget.ffi.canvasModel.updateViewStyle();
|
||||||
}),
|
}),
|
||||||
MenuEntryDivider<String>(),
|
MenuEntryDivider<String>(),
|
||||||
MenuEntryRadios<String>(
|
MenuEntryRadios<String>(
|
||||||
text: translate('Scroll Style'),
|
text: translate('Scroll Style'),
|
||||||
optionsGetter: () => [
|
optionsGetter: () => [
|
||||||
Tuple2<String, String>(translate('ScrollAuto'), 'scrollauto'),
|
MenuEntryRadioOption(
|
||||||
Tuple2<String, String>(translate('Scrollbar'), 'scrollbar'),
|
text: translate('ScrollAuto'), value: 'scrollauto'),
|
||||||
|
MenuEntryRadioOption(
|
||||||
|
text: translate('Scrollbar'), value: 'scrollbar'),
|
||||||
],
|
],
|
||||||
curOptionGetter: () async {
|
curOptionGetter: () async {
|
||||||
return await bind.sessionGetOption(
|
return await bind.sessionGetOption(
|
||||||
id: widget.id, arg: 'scroll-style') ??
|
id: widget.id, arg: 'scroll-style') ??
|
||||||
'';
|
'';
|
||||||
},
|
},
|
||||||
optionSetter: (String v) async {
|
optionSetter: (String oldValue, String newValue) async {
|
||||||
await bind.sessionPeerOption(
|
await bind.sessionPeerOption(
|
||||||
id: widget.id, name: "scroll-style", value: v);
|
id: widget.id, name: "scroll-style", value: newValue);
|
||||||
widget.ffi.canvasModel.updateScrollStyle();
|
widget.ffi.canvasModel.updateScrollStyle();
|
||||||
}),
|
}),
|
||||||
MenuEntryDivider<String>(),
|
MenuEntryDivider<String>(),
|
||||||
MenuEntryRadios<String>(
|
MenuEntryRadios<String>(
|
||||||
text: translate('Image Quality'),
|
text: translate('Image Quality'),
|
||||||
optionsGetter: () => [
|
optionsGetter: () => [
|
||||||
Tuple2<String, String>(translate('Good image quality'), 'best'),
|
MenuEntryRadioOption(
|
||||||
Tuple2<String, String>(translate('Balanced'), 'balanced'),
|
text: translate('Good image quality'), value: 'best'),
|
||||||
Tuple2<String, String>(
|
MenuEntryRadioOption(
|
||||||
translate('Optimize reaction time'), 'low'),
|
text: translate('Balanced'), value: 'balanced'),
|
||||||
|
MenuEntryRadioOption(
|
||||||
|
text: translate('Optimize reaction time'), value: 'low'),
|
||||||
|
MenuEntryRadioOption(
|
||||||
|
text: translate('Custom'),
|
||||||
|
value: 'custom',
|
||||||
|
dismissOnClicked: true),
|
||||||
],
|
],
|
||||||
curOptionGetter: () async {
|
curOptionGetter: () async {
|
||||||
String quality =
|
String quality =
|
||||||
@ -451,8 +461,43 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
if (quality == '') quality = 'balanced';
|
if (quality == '') quality = 'balanced';
|
||||||
return quality;
|
return quality;
|
||||||
},
|
},
|
||||||
optionSetter: (String v) async {
|
optionSetter: (String oldValue, String newValue) async {
|
||||||
await bind.sessionSetImageQuality(id: widget.id, value: v);
|
if (oldValue != newValue) {
|
||||||
|
await bind.sessionSetImageQuality(id: widget.id, value: newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newValue == 'custom') {
|
||||||
|
final btnCancel = getMsgBoxButton(translate('Cancel'), () {
|
||||||
|
widget.ffi.dialogManager.dismissAll();
|
||||||
|
});
|
||||||
|
final quality =
|
||||||
|
await bind.sessionGetCustomImageQuality(id: widget.id);
|
||||||
|
final double initValue = quality != null && quality.isNotEmpty
|
||||||
|
? quality[0].toDouble()
|
||||||
|
: 50.0;
|
||||||
|
// final slider = _ImageCustomQualitySlider(
|
||||||
|
// id: widget.id, v: RxDouble(initValue));
|
||||||
|
final RxDouble sliderValue = RxDouble(initValue);
|
||||||
|
final slider = Obx(() => Slider(
|
||||||
|
value: sliderValue.value,
|
||||||
|
max: 100,
|
||||||
|
label: sliderValue.value.round().toString(),
|
||||||
|
onChanged: (double value) {
|
||||||
|
() async {
|
||||||
|
await bind.sessionSetCustomImageQuality(
|
||||||
|
id: widget.id, value: value.toInt());
|
||||||
|
final quality = await bind.sessionGetCustomImageQuality(
|
||||||
|
id: widget.id);
|
||||||
|
sliderValue.value =
|
||||||
|
quality != null && quality.isNotEmpty
|
||||||
|
? quality[0].toDouble()
|
||||||
|
: 50.0;
|
||||||
|
}();
|
||||||
|
},
|
||||||
|
));
|
||||||
|
msgBoxCommon(widget.ffi.dialogManager, 'Custom Image Quality',
|
||||||
|
slider, [btnCancel]);
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
MenuEntryDivider<String>(),
|
MenuEntryDivider<String>(),
|
||||||
MenuEntrySwitch<String>(
|
MenuEntrySwitch<String>(
|
||||||
|
@ -7,6 +7,7 @@ import 'dart:ui' as ui;
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/generated_bridge.dart';
|
import 'package:flutter_hbb/generated_bridge.dart';
|
||||||
import 'package:flutter_hbb/models/ab_model.dart';
|
import 'package:flutter_hbb/models/ab_model.dart';
|
||||||
import 'package:flutter_hbb/models/chat_model.dart';
|
import 'package:flutter_hbb/models/chat_model.dart';
|
||||||
@ -499,8 +500,8 @@ class CanvasModel with ChangeNotifier {
|
|||||||
|
|
||||||
_scale = 1.0;
|
_scale = 1.0;
|
||||||
if (style == 'adaptive') {
|
if (style == 'adaptive') {
|
||||||
final s1 = size.width / (parent.target?.ffiModel.display.width ?? 720);
|
final s1 = size.width / getDisplayWidth();
|
||||||
final s2 = size.height / (parent.target?.ffiModel.display.height ?? 1280);
|
final s2 = size.height / getDisplayHeight();
|
||||||
_scale = s1 < s2 ? s1 : s2;
|
_scale = s1 < s2 ? s1 : s2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,11 +530,17 @@ class CanvasModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int getDisplayWidth() {
|
int getDisplayWidth() {
|
||||||
return parent.target?.ffiModel.display.width ?? 1080;
|
final defaultWidth = (isDesktop || isWebDesktop)
|
||||||
|
? kDesktopDefaultDisplayWidth
|
||||||
|
: kMobileDefaultDisplayWidth;
|
||||||
|
return parent.target?.ffiModel.display.width ?? defaultWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getDisplayHeight() {
|
int getDisplayHeight() {
|
||||||
return parent.target?.ffiModel.display.height ?? 720;
|
final defaultHeight = (isDesktop || isWebDesktop)
|
||||||
|
? kDesktopDefaultDisplayHeight
|
||||||
|
: kMobileDefaultDisplayHeight;
|
||||||
|
return parent.target?.ffiModel.display.height ?? defaultHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
Size get size {
|
Size get size {
|
||||||
|
@ -143,14 +143,6 @@ pub fn session_get_toggle_option_sync(id: String, arg: String) -> SyncReturn<boo
|
|||||||
SyncReturn(res)
|
SyncReturn(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn session_get_image_quality(id: String) -> Option<String> {
|
|
||||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
|
||||||
Some(session.get_image_quality())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn session_get_option(id: String, arg: String) -> Option<String> {
|
pub fn session_get_option(id: String, arg: String) -> Option<String> {
|
||||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
Some(session.get_option(arg))
|
Some(session.get_option(arg))
|
||||||
@ -190,12 +182,34 @@ pub fn session_toggle_option(id: String, value: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn session_get_image_quality(id: String) -> Option<String> {
|
||||||
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
|
Some(session.get_image_quality())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_set_image_quality(id: String, value: String) {
|
pub fn session_set_image_quality(id: String, value: String) {
|
||||||
if let Some(session) = SESSIONS.write().unwrap().get_mut(&id) {
|
if let Some(session) = SESSIONS.write().unwrap().get_mut(&id) {
|
||||||
session.save_image_quality(value);
|
session.save_image_quality(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn session_get_custom_image_quality(id: String) -> Option<Vec<i32>> {
|
||||||
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
|
Some(session.get_custom_image_quality())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn session_set_custom_image_quality(id: String, value: i32) {
|
||||||
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
|
session.set_custom_image_quality(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_lock_screen(id: String) {
|
pub fn session_lock_screen(id: String) {
|
||||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
session.lock_screen();
|
session.lock_screen();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user