flutter_desktop: remove animation & adjust popup menu
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
e789b2c3a8
commit
b8a382a0d8
@ -177,6 +177,12 @@ class MyTheme {
|
||||
),
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
splashFactory: isDesktop ? NoSplash.splashFactory : null,
|
||||
textButtonTheme: isDesktop
|
||||
? TextButtonThemeData(
|
||||
style: ButtonStyle(splashFactory: NoSplash.splashFactory),
|
||||
)
|
||||
: null,
|
||||
).copyWith(
|
||||
extensions: <ThemeExtension<dynamic>>[
|
||||
ColorThemeExtension.light,
|
||||
@ -192,6 +198,12 @@ class MyTheme {
|
||||
),
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
splashFactory: isDesktop ? NoSplash.splashFactory : null,
|
||||
textButtonTheme: isDesktop
|
||||
? TextButtonThemeData(
|
||||
style: ButtonStyle(splashFactory: NoSplash.splashFactory),
|
||||
)
|
||||
: null,
|
||||
).copyWith(
|
||||
extensions: <ThemeExtension<dynamic>>[
|
||||
ColorThemeExtension.dark,
|
||||
|
@ -11,7 +11,8 @@ import '../../mobile/pages/settings_page.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
|
||||
class AddressBook extends StatefulWidget {
|
||||
const AddressBook({Key? key}) : super(key: key);
|
||||
final EdgeInsets? menuPadding;
|
||||
const AddressBook({Key? key, this.menuPadding}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
@ -180,7 +181,9 @@ class _AddressBookState extends State<AddressBook> {
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: AddressBookPeersView()),
|
||||
child: AddressBookPeersView(
|
||||
menuPadding: widget.menuPadding,
|
||||
)),
|
||||
)
|
||||
],
|
||||
));
|
||||
|
@ -14,7 +14,7 @@ import '../../desktop/widgets/popup_menu.dart';
|
||||
class _PopupMenuTheme {
|
||||
static const Color commonColor = MyTheme.accent;
|
||||
// kMinInteractiveDimension
|
||||
static const double height = 25.0;
|
||||
static const double height = 20.0;
|
||||
static const double dividerHeight = 3.0;
|
||||
}
|
||||
|
||||
@ -319,8 +319,10 @@ class _PeerCardState extends State<_PeerCard>
|
||||
|
||||
abstract class BasePeerCard extends StatelessWidget {
|
||||
final Peer peer;
|
||||
final EdgeInsets? menuPadding;
|
||||
|
||||
BasePeerCard({required this.peer, Key? key}) : super(key: key);
|
||||
BasePeerCard({required this.peer, this.menuPadding, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -365,6 +367,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
isRDP: isRDP,
|
||||
);
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}
|
||||
@ -414,17 +417,25 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: () => _rdpDialog(id),
|
||||
),
|
||||
child: Transform.scale(
|
||||
scale: 0.8,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.edit),
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () {
|
||||
if (Navigator.canPop(context)) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
_rdpDialog(id);
|
||||
},
|
||||
)),
|
||||
))
|
||||
],
|
||||
)),
|
||||
proc: () {
|
||||
connect(context, id, isRDP: true);
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}
|
||||
@ -439,6 +450,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
proc: () {
|
||||
bind.mainWol(id: id);
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}
|
||||
@ -447,6 +459,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
Future<MenuEntryBase<String>> _forceAlwaysRelayAction(String id) async {
|
||||
const option = 'force-always-relay';
|
||||
return MenuEntrySwitch<String>(
|
||||
switchType: SwitchType.scheckbox,
|
||||
text: translate('Always connect via relay'),
|
||||
getter: () async {
|
||||
return (await bind.mainGetPeerOption(id: id, key: option)).isNotEmpty;
|
||||
@ -461,7 +474,8 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
}
|
||||
await bind.mainSetPeerOption(id: id, key: option, value: value);
|
||||
},
|
||||
dismissOnClicked: false,
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}
|
||||
|
||||
@ -475,6 +489,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
proc: () {
|
||||
_rename(id, isAddressBook);
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}
|
||||
@ -494,6 +509,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
await reloadFunc();
|
||||
}();
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}
|
||||
@ -508,6 +524,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
proc: () {
|
||||
bind.mainForgetPassword(id: id);
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}
|
||||
@ -528,6 +545,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
}
|
||||
}();
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}
|
||||
@ -549,6 +567,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
}
|
||||
}();
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}
|
||||
@ -616,7 +635,8 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
}
|
||||
|
||||
class RecentPeerCard extends BasePeerCard {
|
||||
RecentPeerCard({required Peer peer, Key? key}) : super(peer: peer, key: key);
|
||||
RecentPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
||||
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
@ -645,8 +665,8 @@ class RecentPeerCard extends BasePeerCard {
|
||||
}
|
||||
|
||||
class FavoritePeerCard extends BasePeerCard {
|
||||
FavoritePeerCard({required Peer peer, Key? key})
|
||||
: super(peer: peer, key: key);
|
||||
FavoritePeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
||||
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
@ -677,8 +697,8 @@ class FavoritePeerCard extends BasePeerCard {
|
||||
}
|
||||
|
||||
class DiscoveredPeerCard extends BasePeerCard {
|
||||
DiscoveredPeerCard({required Peer peer, Key? key})
|
||||
: super(peer: peer, key: key);
|
||||
DiscoveredPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
||||
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
@ -706,8 +726,8 @@ class DiscoveredPeerCard extends BasePeerCard {
|
||||
}
|
||||
|
||||
class AddressBookPeerCard extends BasePeerCard {
|
||||
AddressBookPeerCard({required Peer peer, Key? key})
|
||||
: super(peer: peer, key: key);
|
||||
AddressBookPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
||||
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
@ -748,6 +768,7 @@ class AddressBookPeerCard extends BasePeerCard {
|
||||
await gFFI.abModel.updateAb();
|
||||
}();
|
||||
},
|
||||
padding: super.menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}
|
||||
@ -762,6 +783,7 @@ class AddressBookPeerCard extends BasePeerCard {
|
||||
proc: () {
|
||||
_abEditTag(id);
|
||||
},
|
||||
padding: super.menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ abstract class BasePeersView extends StatelessWidget {
|
||||
}
|
||||
|
||||
class RecentPeersView extends BasePeersView {
|
||||
RecentPeersView({Key? key})
|
||||
RecentPeersView({Key? key, EdgeInsets? menuPadding})
|
||||
: super(
|
||||
key: key,
|
||||
name: 'recent peer',
|
||||
@ -232,6 +232,7 @@ class RecentPeersView extends BasePeersView {
|
||||
offstageFunc: (Peer peer) => false,
|
||||
peerCardBuilder: (Peer peer) => RecentPeerCard(
|
||||
peer: peer,
|
||||
menuPadding: menuPadding,
|
||||
),
|
||||
initPeers: [],
|
||||
);
|
||||
@ -245,7 +246,7 @@ class RecentPeersView extends BasePeersView {
|
||||
}
|
||||
|
||||
class FavoritePeersView extends BasePeersView {
|
||||
FavoritePeersView({Key? key})
|
||||
FavoritePeersView({Key? key, EdgeInsets? menuPadding})
|
||||
: super(
|
||||
key: key,
|
||||
name: 'favorite peer',
|
||||
@ -253,6 +254,7 @@ class FavoritePeersView extends BasePeersView {
|
||||
offstageFunc: (Peer peer) => false,
|
||||
peerCardBuilder: (Peer peer) => FavoritePeerCard(
|
||||
peer: peer,
|
||||
menuPadding: menuPadding,
|
||||
),
|
||||
initPeers: [],
|
||||
);
|
||||
@ -266,7 +268,7 @@ class FavoritePeersView extends BasePeersView {
|
||||
}
|
||||
|
||||
class DiscoveredPeersView extends BasePeersView {
|
||||
DiscoveredPeersView({Key? key})
|
||||
DiscoveredPeersView({Key? key, EdgeInsets? menuPadding})
|
||||
: super(
|
||||
key: key,
|
||||
name: 'discovered peer',
|
||||
@ -274,6 +276,7 @@ class DiscoveredPeersView extends BasePeersView {
|
||||
offstageFunc: (Peer peer) => false,
|
||||
peerCardBuilder: (Peer peer) => DiscoveredPeerCard(
|
||||
peer: peer,
|
||||
menuPadding: menuPadding,
|
||||
),
|
||||
initPeers: [],
|
||||
);
|
||||
@ -287,7 +290,7 @@ class DiscoveredPeersView extends BasePeersView {
|
||||
}
|
||||
|
||||
class AddressBookPeersView extends BasePeersView {
|
||||
AddressBookPeersView({Key? key})
|
||||
AddressBookPeersView({Key? key, EdgeInsets? menuPadding})
|
||||
: super(
|
||||
key: key,
|
||||
name: 'address book peer',
|
||||
@ -296,6 +299,7 @@ class AddressBookPeersView extends BasePeersView {
|
||||
!_hitTag(gFFI.abModel.selectedTags, peer.tags),
|
||||
peerCardBuilder: (Peer peer) => AddressBookPeerCard(
|
||||
peer: peer,
|
||||
menuPadding: menuPadding,
|
||||
),
|
||||
initPeers: _loadPeers(),
|
||||
);
|
||||
|
@ -74,10 +74,18 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
||||
translate('Address Book')
|
||||
],
|
||||
children: [
|
||||
RecentPeersView(),
|
||||
FavoritePeersView(),
|
||||
DiscoveredPeersView(),
|
||||
const AddressBook(),
|
||||
RecentPeersView(
|
||||
menuPadding: EdgeInsets.only(left: 12.0, right: 3.0),
|
||||
),
|
||||
FavoritePeersView(
|
||||
menuPadding: EdgeInsets.only(left: 12.0, right: 3.0),
|
||||
),
|
||||
DiscoveredPeersView(
|
||||
menuPadding: EdgeInsets.only(left: 12.0, right: 3.0),
|
||||
),
|
||||
const AddressBook(
|
||||
menuPadding: EdgeInsets.only(left: 12.0, right: 3.0),
|
||||
),
|
||||
],
|
||||
)),
|
||||
],
|
||||
|
@ -14,7 +14,8 @@ import 'package:flutter/material.dart';
|
||||
// void setState(VoidCallback fn) { }
|
||||
// enum Menu { itemOne, itemTwo, itemThree, itemFour }
|
||||
|
||||
const Duration _kMenuDuration = Duration(milliseconds: 300);
|
||||
// const Duration _kMenuDuration = Duration(milliseconds: 300);
|
||||
const Duration _kMenuDuration = Duration(milliseconds: 0);
|
||||
const double _kMenuCloseIntervalEnd = 2.0 / 3.0;
|
||||
const double _kMenuHorizontalPadding = 16.0;
|
||||
const double _kMenuDividerHeight = 16.0;
|
||||
@ -22,7 +23,7 @@ const double _kMenuDividerHeight = 16.0;
|
||||
const double _kMenuMinWidth = 2.0 * _kMenuWidthStep;
|
||||
const double _kMenuMaxWidth = double.infinity;
|
||||
// const double _kMenuVerticalPadding = 8.0;
|
||||
const double _kMenuVerticalPadding = 0.0;
|
||||
const double _kMenuVerticalPadding = 8.0;
|
||||
const double _kMenuWidthStep = 0.0;
|
||||
//const double _kMenuScreenPadding = 8.0;
|
||||
const double _kMenuScreenPadding = 0.0;
|
||||
|
@ -78,7 +78,8 @@ class MyPopupMenuItemState<T, W extends PopupMenuChildrenItem<T>>
|
||||
duration: kThemeChangeDuration,
|
||||
child: Container(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
constraints: BoxConstraints(minHeight: widget.height),
|
||||
constraints: BoxConstraints(
|
||||
minHeight: widget.height, maxHeight: widget.height),
|
||||
padding:
|
||||
widget.padding ?? const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: widget.child,
|
||||
@ -156,12 +157,14 @@ class MenuEntryRadios<T> extends MenuEntryBase<T> {
|
||||
final RadioCurOptionGetter curOptionGetter;
|
||||
final RadioOptionSetter optionSetter;
|
||||
final RxString _curOption = "".obs;
|
||||
final EdgeInsets? padding;
|
||||
|
||||
MenuEntryRadios({
|
||||
required this.text,
|
||||
required this.optionsGetter,
|
||||
required this.curOptionGetter,
|
||||
required this.optionSetter,
|
||||
this.padding,
|
||||
dismissOnClicked = false,
|
||||
RxBool? enabled,
|
||||
}) : super(dismissOnClicked: dismissOnClicked, enabled: enabled) {
|
||||
@ -189,8 +192,10 @@ class MenuEntryRadios<T> extends MenuEntryBase<T> {
|
||||
height: conf.height,
|
||||
child: TextButton(
|
||||
child: Container(
|
||||
padding: padding,
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
constraints: BoxConstraints(minHeight: conf.height),
|
||||
constraints:
|
||||
BoxConstraints(minHeight: conf.height, maxHeight: conf.height),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
@ -202,17 +207,22 @@ class MenuEntryRadios<T> extends MenuEntryBase<T> {
|
||||
),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
width: 20.0,
|
||||
height: 20.0,
|
||||
child: Obx(() => opt.value == curOption.value
|
||||
? Icon(
|
||||
Icons.check,
|
||||
color: conf.commonColor,
|
||||
)
|
||||
: const SizedBox.shrink())),
|
||||
)),
|
||||
alignment: Alignment.centerRight,
|
||||
child: Transform.scale(
|
||||
scale: MenuConfig.iconScale,
|
||||
child: Obx(() => opt.value == curOption.value
|
||||
? IconButton(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
8.0, 0.0, 8.0, 0.0),
|
||||
hoverColor: Colors.transparent,
|
||||
focusColor: Colors.transparent,
|
||||
onPressed: () {},
|
||||
icon: Icon(
|
||||
Icons.check,
|
||||
color: conf.commonColor,
|
||||
))
|
||||
: const SizedBox.shrink()),
|
||||
))),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -239,12 +249,14 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
||||
final RadioCurOptionGetter curOptionGetter;
|
||||
final RadioOptionSetter optionSetter;
|
||||
final RxString _curOption = "".obs;
|
||||
final EdgeInsets? padding;
|
||||
|
||||
MenuEntrySubRadios({
|
||||
required this.text,
|
||||
required this.optionsGetter,
|
||||
required this.curOptionGetter,
|
||||
required this.optionSetter,
|
||||
this.padding,
|
||||
dismissOnClicked = false,
|
||||
RxBool? enabled,
|
||||
}) : super(
|
||||
@ -275,8 +287,10 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
||||
height: conf.height,
|
||||
child: TextButton(
|
||||
child: Container(
|
||||
padding: padding,
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
constraints: BoxConstraints(minHeight: conf.height),
|
||||
constraints:
|
||||
BoxConstraints(minHeight: conf.height, maxHeight: conf.height),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
@ -289,14 +303,18 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
width: 20.0,
|
||||
height: 20.0,
|
||||
child: Transform.scale(
|
||||
scale: MenuConfig.iconScale,
|
||||
child: Obx(() => opt.value == curOption.value
|
||||
? Icon(
|
||||
Icons.check,
|
||||
color: conf.commonColor,
|
||||
)
|
||||
? IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
hoverColor: Colors.transparent,
|
||||
focusColor: Colors.transparent,
|
||||
onPressed: () {},
|
||||
icon: Icon(
|
||||
Icons.check,
|
||||
color: conf.commonColor,
|
||||
))
|
||||
: const SizedBox.shrink())),
|
||||
)),
|
||||
],
|
||||
@ -318,7 +336,7 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
||||
return [
|
||||
PopupMenuChildrenItem(
|
||||
enabled: super.enabled,
|
||||
padding: EdgeInsets.zero,
|
||||
padding: padding,
|
||||
height: conf.height,
|
||||
itemBuilder: (BuildContext context) =>
|
||||
options.map((opt) => _buildSecondMenu(context, conf, opt)).toList(),
|
||||
@ -345,22 +363,31 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
||||
}
|
||||
}
|
||||
|
||||
enum SwitchType {
|
||||
sswitch,
|
||||
scheckbox,
|
||||
}
|
||||
|
||||
typedef SwitchGetter = Future<bool> Function();
|
||||
typedef SwitchSetter = Future<void> Function(bool);
|
||||
|
||||
abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
||||
final SwitchType switchType;
|
||||
final String text;
|
||||
final EdgeInsets? padding;
|
||||
Rx<TextStyle>? textStyle;
|
||||
|
||||
MenuEntrySwitchBase({
|
||||
required this.switchType,
|
||||
required this.text,
|
||||
required dismissOnClicked,
|
||||
this.textStyle,
|
||||
this.padding,
|
||||
RxBool? enabled,
|
||||
}) : super(dismissOnClicked: dismissOnClicked, enabled: enabled);
|
||||
|
||||
RxBool get curOption;
|
||||
Future<void> setOption(bool option);
|
||||
Future<void> setOption(bool? option);
|
||||
|
||||
@override
|
||||
List<mod_menu.PopupMenuEntry<T>> build(
|
||||
@ -376,6 +403,7 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
||||
height: conf.height,
|
||||
child: TextButton(
|
||||
child: Container(
|
||||
padding: padding,
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
height: conf.height,
|
||||
child: Row(children: [
|
||||
@ -386,16 +414,33 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Obx(() => Switch(
|
||||
value: curOption.value,
|
||||
onChanged: (v) {
|
||||
if (super.dismissOnClicked &&
|
||||
Navigator.canPop(context)) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
setOption(v);
|
||||
},
|
||||
)),
|
||||
child: Transform.scale(
|
||||
scale: MenuConfig.iconScale,
|
||||
child: Obx(() {
|
||||
if (switchType == SwitchType.sswitch) {
|
||||
return Switch(
|
||||
value: curOption.value,
|
||||
onChanged: (v) {
|
||||
if (super.dismissOnClicked &&
|
||||
Navigator.canPop(context)) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
setOption(v);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return Checkbox(
|
||||
value: curOption.value,
|
||||
onChanged: (v) {
|
||||
if (super.dismissOnClicked &&
|
||||
Navigator.canPop(context)) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
setOption(v);
|
||||
},
|
||||
);
|
||||
}
|
||||
})),
|
||||
))
|
||||
])),
|
||||
onPressed: () {
|
||||
@ -416,15 +461,19 @@ class MenuEntrySwitch<T> extends MenuEntrySwitchBase<T> {
|
||||
final RxBool _curOption = false.obs;
|
||||
|
||||
MenuEntrySwitch({
|
||||
required SwitchType switchType,
|
||||
required String text,
|
||||
required this.getter,
|
||||
required this.setter,
|
||||
Rx<TextStyle>? textStyle,
|
||||
EdgeInsets? padding,
|
||||
dismissOnClicked = false,
|
||||
RxBool? enabled,
|
||||
}) : super(
|
||||
switchType: switchType,
|
||||
text: text,
|
||||
textStyle: textStyle,
|
||||
padding: padding,
|
||||
dismissOnClicked: dismissOnClicked,
|
||||
enabled: enabled,
|
||||
) {
|
||||
@ -436,11 +485,13 @@ class MenuEntrySwitch<T> extends MenuEntrySwitchBase<T> {
|
||||
@override
|
||||
RxBool get curOption => _curOption;
|
||||
@override
|
||||
setOption(bool option) async {
|
||||
await setter(option);
|
||||
final opt = await getter();
|
||||
if (_curOption.value != opt) {
|
||||
_curOption.value = opt;
|
||||
setOption(bool? option) async {
|
||||
if (option != null) {
|
||||
await setter(option);
|
||||
final opt = await getter();
|
||||
if (_curOption.value != opt) {
|
||||
_curOption.value = opt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -453,32 +504,40 @@ class MenuEntrySwitch2<T> extends MenuEntrySwitchBase<T> {
|
||||
final SwitchSetter setter;
|
||||
|
||||
MenuEntrySwitch2({
|
||||
required SwitchType switchType,
|
||||
required String text,
|
||||
required this.getter,
|
||||
required this.setter,
|
||||
Rx<TextStyle>? textStyle,
|
||||
EdgeInsets? padding,
|
||||
dismissOnClicked = false,
|
||||
RxBool? enabled,
|
||||
}) : super(
|
||||
switchType: switchType,
|
||||
text: text,
|
||||
textStyle: textStyle,
|
||||
padding: padding,
|
||||
dismissOnClicked: dismissOnClicked);
|
||||
|
||||
@override
|
||||
RxBool get curOption => getter();
|
||||
@override
|
||||
setOption(bool option) async {
|
||||
await setter(option);
|
||||
setOption(bool? option) async {
|
||||
if (option != null) {
|
||||
await setter(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
||||
final String text;
|
||||
final List<MenuEntryBase<T>> entries;
|
||||
final EdgeInsets? padding;
|
||||
|
||||
MenuEntrySubMenu({
|
||||
required this.text,
|
||||
required this.entries,
|
||||
this.padding,
|
||||
RxBool? enabled,
|
||||
}) : super(enabled: enabled);
|
||||
|
||||
@ -490,7 +549,7 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
||||
PopupMenuChildrenItem(
|
||||
enabled: super.enabled,
|
||||
height: conf.height,
|
||||
padding: EdgeInsets.zero,
|
||||
padding: padding,
|
||||
position: mod_menu.PopupMenuPosition.overSide,
|
||||
itemBuilder: (BuildContext context) => entries
|
||||
.map((entry) => entry.build(context, conf))
|
||||
@ -522,10 +581,12 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
||||
class MenuEntryButton<T> extends MenuEntryBase<T> {
|
||||
final Widget Function(TextStyle? style) childBuilder;
|
||||
Function() proc;
|
||||
final EdgeInsets? padding;
|
||||
|
||||
MenuEntryButton({
|
||||
required this.childBuilder,
|
||||
required this.proc,
|
||||
this.padding,
|
||||
dismissOnClicked = false,
|
||||
RxBool? enabled,
|
||||
}) : super(
|
||||
@ -553,8 +614,10 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
||||
}
|
||||
: null,
|
||||
child: Container(
|
||||
padding: padding,
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
constraints: BoxConstraints(minHeight: conf.height),
|
||||
constraints:
|
||||
BoxConstraints(minHeight: conf.height, maxHeight: conf.height),
|
||||
child: childBuilder(
|
||||
super.enabled!.value ? enabledStyle : disabledStyle),
|
||||
),
|
||||
|
@ -19,7 +19,7 @@ import './material_mod_popup_menu.dart' as mod_menu;
|
||||
class _MenubarTheme {
|
||||
static const Color commonColor = MyTheme.accent;
|
||||
// kMinInteractiveDimension
|
||||
static const double height = 25.0;
|
||||
static const double height = 20.0;
|
||||
static const double dividerHeight = 12.0;
|
||||
}
|
||||
|
||||
@ -367,31 +367,41 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
List<MenuEntryBase<String>> _getControlMenu(BuildContext context) {
|
||||
final pi = widget.ffi.ffiModel.pi;
|
||||
final perms = widget.ffi.ffiModel.permissions;
|
||||
|
||||
const EdgeInsets padding = EdgeInsets.only(left: 14.0, right: 5.0);
|
||||
final List<MenuEntryBase<String>> displayMenu = [];
|
||||
displayMenu.addAll([
|
||||
MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Row(
|
||||
children: [
|
||||
Text(
|
||||
translate('OS Password'),
|
||||
style: style,
|
||||
),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: () => showSetOSPassword(
|
||||
widget.id, false, widget.ffi.dialogManager),
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
childBuilder: (TextStyle? style) => Container(
|
||||
alignment: AlignmentDirectional.center,
|
||||
height: _MenubarTheme.height,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
translate('OS Password'),
|
||||
style: style,
|
||||
),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Transform.scale(
|
||||
scale: 0.8,
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: () {
|
||||
if (Navigator.canPop(context)) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
showSetOSPassword(
|
||||
widget.id, false, widget.ffi.dialogManager);
|
||||
})),
|
||||
))
|
||||
],
|
||||
)),
|
||||
proc: () {
|
||||
showSetOSPassword(widget.id, false, widget.ffi.dialogManager);
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
MenuEntryButton<String>(
|
||||
@ -402,6 +412,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
proc: () {
|
||||
connect(context, widget.id, isFileTransfer: true);
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
MenuEntryButton<String>(
|
||||
@ -409,6 +420,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
translate('TCP Tunneling'),
|
||||
style: style,
|
||||
),
|
||||
padding: padding,
|
||||
proc: () {
|
||||
connect(context, widget.id, isTcpTunneling: true);
|
||||
},
|
||||
@ -427,6 +439,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
proc: () {
|
||||
showAuditDialog(widget.id, widget.ffi.dialogManager);
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
);
|
||||
@ -443,6 +456,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
proc: () {
|
||||
bind.sessionCtrlAltDel(id: widget.id);
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
@ -459,6 +473,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
proc: () {
|
||||
showRestartRemoteDevice(pi, widget.id, gFFI.dialogManager);
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
@ -472,6 +487,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
proc: () {
|
||||
bind.sessionLockScreen(id: widget.id);
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
|
||||
@ -489,6 +505,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
value: '${blockInput.value ? "un" : ""}block-input');
|
||||
blockInput.value = !blockInput.value;
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
@ -503,6 +520,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
proc: () {
|
||||
bind.sessionRefresh(id: widget.id);
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
@ -523,6 +541,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
// }
|
||||
// }();
|
||||
// },
|
||||
// padding: padding,
|
||||
// dismissOnClicked: true,
|
||||
// ));
|
||||
// }
|
||||
@ -535,6 +554,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
proc: () {
|
||||
widget.ffi.cursorModel.reset();
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
@ -543,125 +563,155 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
}
|
||||
|
||||
List<MenuEntryBase<String>> _getDisplayMenu(dynamic futureData) {
|
||||
const EdgeInsets padding = EdgeInsets.only(left: 18.0, right: 8.0);
|
||||
final displayMenu = [
|
||||
MenuEntryRadios<String>(
|
||||
text: translate('Ratio'),
|
||||
optionsGetter: () => [
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Scale original'), value: 'original'),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Scale adaptive'), value: 'adaptive'),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetOption(
|
||||
id: widget.id, arg: 'view-style') ??
|
||||
'adaptive';
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionPeerOption(
|
||||
id: widget.id, name: "view-style", value: newValue);
|
||||
widget.ffi.canvasModel.updateViewStyle();
|
||||
}),
|
||||
text: translate('Ratio'),
|
||||
optionsGetter: () => [
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Scale original'),
|
||||
value: 'original',
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Scale adaptive'),
|
||||
value: 'adaptive',
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetOption(
|
||||
id: widget.id, arg: 'view-style') ??
|
||||
'adaptive';
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionPeerOption(
|
||||
id: widget.id, name: "view-style", value: newValue);
|
||||
widget.ffi.canvasModel.updateViewStyle();
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
MenuEntryDivider<String>(),
|
||||
MenuEntryRadios<String>(
|
||||
text: translate('Scroll Style'),
|
||||
optionsGetter: () => [
|
||||
MenuEntryRadioOption(
|
||||
text: translate('ScrollAuto'), value: 'scrollauto'),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Scrollbar'), value: 'scrollbar'),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetOption(
|
||||
id: widget.id, arg: 'scroll-style') ??
|
||||
'';
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionPeerOption(
|
||||
id: widget.id, name: "scroll-style", value: newValue);
|
||||
widget.ffi.canvasModel.updateScrollStyle();
|
||||
}),
|
||||
text: translate('Scroll Style'),
|
||||
optionsGetter: () => [
|
||||
MenuEntryRadioOption(
|
||||
text: translate('ScrollAuto'),
|
||||
value: 'scrollauto',
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Scrollbar'),
|
||||
value: 'scrollbar',
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetOption(
|
||||
id: widget.id, arg: 'scroll-style') ??
|
||||
'';
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionPeerOption(
|
||||
id: widget.id, name: "scroll-style", value: newValue);
|
||||
widget.ffi.canvasModel.updateScrollStyle();
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
MenuEntryDivider<String>(),
|
||||
MenuEntryRadios<String>(
|
||||
text: translate('Image Quality'),
|
||||
optionsGetter: () => [
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Good image quality'), value: 'best'),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Balanced'), value: 'balanced'),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Optimize reaction time'), value: 'low'),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Custom'),
|
||||
value: 'custom',
|
||||
dismissOnClicked: true),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
String quality =
|
||||
await bind.sessionGetImageQuality(id: widget.id) ?? 'balanced';
|
||||
if (quality == '') quality = 'balanced';
|
||||
return quality;
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
if (oldValue != newValue) {
|
||||
await bind.sessionSetImageQuality(id: widget.id, value: newValue);
|
||||
}
|
||||
text: translate('Image Quality'),
|
||||
optionsGetter: () => [
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Good image quality'),
|
||||
value: 'best',
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Balanced'),
|
||||
value: 'balanced',
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Optimize reaction time'),
|
||||
value: 'low',
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Custom'),
|
||||
value: 'custom',
|
||||
dismissOnClicked: true),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
String quality =
|
||||
await bind.sessionGetImageQuality(id: widget.id) ?? 'balanced';
|
||||
if (quality == '') quality = 'balanced';
|
||||
return quality;
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
if (oldValue != newValue) {
|
||||
await bind.sessionSetImageQuality(id: widget.id, value: newValue);
|
||||
}
|
||||
|
||||
if (newValue == 'custom') {
|
||||
final btnCancel = msgBoxButton(translate('Close'), () {
|
||||
widget.ffi.dialogManager.dismissAll();
|
||||
});
|
||||
final quality =
|
||||
await bind.sessionGetCustomImageQuality(id: widget.id);
|
||||
double initValue = quality != null && quality.isNotEmpty
|
||||
? quality[0].toDouble()
|
||||
: 50.0;
|
||||
const minValue = 10.0;
|
||||
const maxValue = 100.0;
|
||||
if (initValue < minValue) {
|
||||
initValue = minValue;
|
||||
}
|
||||
if (initValue > maxValue) {
|
||||
initValue = maxValue;
|
||||
}
|
||||
final RxDouble sliderValue = RxDouble(initValue);
|
||||
final rxReplay = rxdart.ReplaySubject<double>();
|
||||
rxReplay
|
||||
.throttleTime(const Duration(milliseconds: 1000),
|
||||
trailing: true, leading: false)
|
||||
.listen((double v) {
|
||||
() async {
|
||||
await bind.sessionSetCustomImageQuality(
|
||||
id: widget.id, value: v.toInt());
|
||||
}();
|
||||
});
|
||||
final slider = Obx(() {
|
||||
return Slider(
|
||||
value: sliderValue.value,
|
||||
min: minValue,
|
||||
max: maxValue,
|
||||
divisions: 90,
|
||||
onChanged: (double value) {
|
||||
sliderValue.value = value;
|
||||
rxReplay.add(value);
|
||||
},
|
||||
);
|
||||
});
|
||||
final content = Row(
|
||||
children: [
|
||||
slider,
|
||||
SizedBox(
|
||||
width: 90,
|
||||
child: Obx(() => Text(
|
||||
'${sliderValue.value.round()}% Bitrate',
|
||||
style: const TextStyle(fontSize: 15),
|
||||
)))
|
||||
],
|
||||
);
|
||||
msgBoxCommon(widget.ffi.dialogManager, 'Custom Image Quality',
|
||||
content, [btnCancel]);
|
||||
if (newValue == 'custom') {
|
||||
final btnCancel = msgBoxButton(translate('Close'), () {
|
||||
widget.ffi.dialogManager.dismissAll();
|
||||
});
|
||||
final quality =
|
||||
await bind.sessionGetCustomImageQuality(id: widget.id);
|
||||
double initValue = quality != null && quality.isNotEmpty
|
||||
? quality[0].toDouble()
|
||||
: 50.0;
|
||||
const minValue = 10.0;
|
||||
const maxValue = 100.0;
|
||||
if (initValue < minValue) {
|
||||
initValue = minValue;
|
||||
}
|
||||
}),
|
||||
if (initValue > maxValue) {
|
||||
initValue = maxValue;
|
||||
}
|
||||
final RxDouble sliderValue = RxDouble(initValue);
|
||||
final rxReplay = rxdart.ReplaySubject<double>();
|
||||
rxReplay
|
||||
.throttleTime(const Duration(milliseconds: 1000),
|
||||
trailing: true, leading: false)
|
||||
.listen((double v) {
|
||||
() async {
|
||||
await bind.sessionSetCustomImageQuality(
|
||||
id: widget.id, value: v.toInt());
|
||||
}();
|
||||
});
|
||||
final slider = Obx(() {
|
||||
return Slider(
|
||||
value: sliderValue.value,
|
||||
min: minValue,
|
||||
max: maxValue,
|
||||
divisions: 90,
|
||||
onChanged: (double value) {
|
||||
sliderValue.value = value;
|
||||
rxReplay.add(value);
|
||||
},
|
||||
);
|
||||
});
|
||||
final content = Row(
|
||||
children: [
|
||||
slider,
|
||||
SizedBox(
|
||||
width: 90,
|
||||
child: Obx(() => Text(
|
||||
'${sliderValue.value.round()}% Bitrate',
|
||||
style: const TextStyle(fontSize: 15),
|
||||
)))
|
||||
],
|
||||
);
|
||||
msgBoxCommon(widget.ffi.dialogManager, 'Custom Image Quality',
|
||||
content, [btnCancel]);
|
||||
}
|
||||
},
|
||||
padding: padding,
|
||||
),
|
||||
MenuEntryDivider<String>(),
|
||||
];
|
||||
|
||||
@ -677,30 +727,49 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
} finally {}
|
||||
if (codecs.length == 2 && (codecs[0] || codecs[1])) {
|
||||
displayMenu.add(MenuEntryRadios<String>(
|
||||
text: translate('Codec Preference'),
|
||||
optionsGetter: () {
|
||||
final list = [
|
||||
MenuEntryRadioOption(text: translate('Auto'), value: 'auto'),
|
||||
MenuEntryRadioOption(text: 'VP9', value: 'vp9'),
|
||||
];
|
||||
if (codecs[0]) {
|
||||
list.add(MenuEntryRadioOption(text: 'H264', value: 'h264'));
|
||||
}
|
||||
if (codecs[1]) {
|
||||
list.add(MenuEntryRadioOption(text: 'H265', value: 'h265'));
|
||||
}
|
||||
return list;
|
||||
},
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetOption(
|
||||
id: widget.id, arg: 'codec-preference') ??
|
||||
'auto';
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionPeerOption(
|
||||
id: widget.id, name: "codec-preference", value: newValue);
|
||||
bind.sessionChangePreferCodec(id: widget.id);
|
||||
}));
|
||||
text: translate('Codec Preference'),
|
||||
optionsGetter: () {
|
||||
final list = [
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Auto'),
|
||||
value: 'auto',
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
MenuEntryRadioOption(
|
||||
text: 'VP9',
|
||||
value: 'vp9',
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
];
|
||||
if (codecs[0]) {
|
||||
list.add(MenuEntryRadioOption(
|
||||
text: 'H264',
|
||||
value: 'h264',
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
if (codecs[1]) {
|
||||
list.add(MenuEntryRadioOption(
|
||||
text: 'H265',
|
||||
value: 'h265',
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
return list;
|
||||
},
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetOption(
|
||||
id: widget.id, arg: 'codec-preference') ??
|
||||
'auto';
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionPeerOption(
|
||||
id: widget.id, name: "codec-preference", value: newValue);
|
||||
bind.sessionChangePreferCodec(id: widget.id);
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -708,62 +777,74 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
displayMenu.add(() {
|
||||
final state = ShowRemoteCursorState.find(widget.id);
|
||||
return MenuEntrySwitch2<String>(
|
||||
text: translate('Show remote cursor'),
|
||||
getter: () {
|
||||
return state;
|
||||
},
|
||||
setter: (bool v) async {
|
||||
state.value = v;
|
||||
await bind.sessionToggleOption(
|
||||
id: widget.id, value: 'show-remote-cursor');
|
||||
});
|
||||
switchType: SwitchType.scheckbox,
|
||||
text: translate('Show remote cursor'),
|
||||
getter: () {
|
||||
return state;
|
||||
},
|
||||
setter: (bool v) async {
|
||||
state.value = v;
|
||||
await bind.sessionToggleOption(
|
||||
id: widget.id, value: 'show-remote-cursor');
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}());
|
||||
|
||||
/// Show quality monitor
|
||||
displayMenu.add(MenuEntrySwitch<String>(
|
||||
text: translate('Show quality monitor'),
|
||||
getter: () async {
|
||||
return bind.sessionGetToggleOptionSync(
|
||||
id: widget.id, arg: 'show-quality-monitor');
|
||||
},
|
||||
setter: (bool v) async {
|
||||
await bind.sessionToggleOption(
|
||||
id: widget.id, value: 'show-quality-monitor');
|
||||
widget.ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id);
|
||||
}));
|
||||
switchType: SwitchType.scheckbox,
|
||||
text: translate('Show quality monitor'),
|
||||
getter: () async {
|
||||
return bind.sessionGetToggleOptionSync(
|
||||
id: widget.id, arg: 'show-quality-monitor');
|
||||
},
|
||||
setter: (bool v) async {
|
||||
await bind.sessionToggleOption(
|
||||
id: widget.id, value: 'show-quality-monitor');
|
||||
widget.ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id);
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
|
||||
final perms = widget.ffi.ffiModel.permissions;
|
||||
final pi = widget.ffi.ffiModel.pi;
|
||||
|
||||
if (perms['audio'] != false) {
|
||||
displayMenu.add(_createSwitchMenuEntry('Mute', 'disable-audio'));
|
||||
displayMenu
|
||||
.add(_createSwitchMenuEntry('Mute', 'disable-audio', padding, true));
|
||||
}
|
||||
|
||||
if (Platform.isWindows &&
|
||||
pi.platform == 'Windows' &&
|
||||
perms['file'] != false) {
|
||||
displayMenu.add(_createSwitchMenuEntry(
|
||||
'Allow file copy and paste', 'enable-file-transfer'));
|
||||
'Allow file copy and paste', 'enable-file-transfer', padding, true));
|
||||
}
|
||||
|
||||
if (perms['keyboard'] != false) {
|
||||
if (perms['clipboard'] != false) {
|
||||
displayMenu.add(
|
||||
_createSwitchMenuEntry('Disable clipboard', 'disable-clipboard'));
|
||||
displayMenu.add(_createSwitchMenuEntry(
|
||||
'Disable clipboard', 'disable-clipboard', padding, true));
|
||||
}
|
||||
displayMenu.add(_createSwitchMenuEntry(
|
||||
'Lock after session end', 'lock-after-session-end'));
|
||||
'Lock after session end', 'lock-after-session-end', padding, true));
|
||||
if (pi.platform == 'Windows') {
|
||||
displayMenu.add(MenuEntrySwitch2<String>(
|
||||
dismissOnClicked: true,
|
||||
text: translate('Privacy mode'),
|
||||
getter: () {
|
||||
return PrivacyModeState.find(widget.id);
|
||||
},
|
||||
setter: (bool v) async {
|
||||
await bind.sessionToggleOption(
|
||||
id: widget.id, value: 'privacy-mode');
|
||||
}));
|
||||
switchType: SwitchType.scheckbox,
|
||||
text: translate('Privacy mode'),
|
||||
getter: () {
|
||||
return PrivacyModeState.find(widget.id);
|
||||
},
|
||||
setter: (bool v) async {
|
||||
await bind.sessionToggleOption(
|
||||
id: widget.id, value: 'privacy-mode');
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
}
|
||||
return displayMenu;
|
||||
@ -772,34 +853,39 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
List<MenuEntryBase<String>> _getKeyboardMenu() {
|
||||
final keyboardMenu = [
|
||||
MenuEntryRadios<String>(
|
||||
text: translate('Ratio'),
|
||||
optionsGetter: () => [
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Legacy mode'), value: 'legacy'),
|
||||
MenuEntryRadioOption(text: translate('Map mode'), value: 'map'),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetKeyboardName(id: widget.id);
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionSetKeyboardMode(
|
||||
id: widget.id, keyboardMode: newValue);
|
||||
widget.ffi.canvasModel.updateViewStyle();
|
||||
})
|
||||
text: translate('Ratio'),
|
||||
optionsGetter: () => [
|
||||
MenuEntryRadioOption(text: translate('Legacy mode'), value: 'legacy'),
|
||||
MenuEntryRadioOption(text: translate('Map mode'), value: 'map'),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
return await bind.sessionGetKeyboardName(id: widget.id);
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionSetKeyboardMode(
|
||||
id: widget.id, keyboardMode: newValue);
|
||||
widget.ffi.canvasModel.updateViewStyle();
|
||||
},
|
||||
)
|
||||
];
|
||||
|
||||
return keyboardMenu;
|
||||
}
|
||||
|
||||
MenuEntrySwitch<String> _createSwitchMenuEntry(String text, String option) {
|
||||
MenuEntrySwitch<String> _createSwitchMenuEntry(
|
||||
String text, String option, EdgeInsets? padding, bool dismissOnClicked) {
|
||||
return MenuEntrySwitch<String>(
|
||||
text: translate(text),
|
||||
getter: () async {
|
||||
return bind.sessionGetToggleOptionSync(id: widget.id, arg: option);
|
||||
},
|
||||
setter: (bool v) async {
|
||||
await bind.sessionToggleOption(id: widget.id, value: option);
|
||||
});
|
||||
switchType: SwitchType.scheckbox,
|
||||
text: translate(text),
|
||||
getter: () async {
|
||||
return bind.sessionGetToggleOptionSync(id: widget.id, arg: option);
|
||||
},
|
||||
setter: (bool v) async {
|
||||
await bind.sessionToggleOption(id: widget.id, value: option);
|
||||
},
|
||||
padding: padding,
|
||||
dismissOnClicked: dismissOnClicked,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user