Merge pull request #2446 from 21pages/tab

hide/reorder peer card
This commit is contained in:
RustDesk 2022-12-04 21:33:02 +08:00 committed by GitHub
commit f649f8dbcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 282 additions and 111 deletions

View File

@ -215,20 +215,17 @@ class MyTheme {
} }
static void changeDarkMode(ThemeMode mode) { static void changeDarkMode(ThemeMode mode) {
final preference = getThemeModePreference(); Get.changeThemeMode(mode);
if (preference != mode) { if (desktopType == DesktopType.main) {
if (mode == ThemeMode.system) { if (mode == ThemeMode.system) {
bind.mainSetLocalOption(key: kCommConfKeyTheme, value: ''); bind.mainSetLocalOption(key: kCommConfKeyTheme, value: '');
} else { } else {
bind.mainSetLocalOption( bind.mainSetLocalOption(
key: kCommConfKeyTheme, value: mode.toShortString()); key: kCommConfKeyTheme, value: mode.toShortString());
} }
Get.changeThemeMode(mode);
if (desktopType == DesktopType.main) {
bind.mainChangeTheme(dark: currentThemeMode().toShortString()); bind.mainChangeTheme(dark: currentThemeMode().toShortString());
} }
} }
}
static ThemeMode currentThemeMode() { static ThemeMode currentThemeMode() {
final preference = getThemeModePreference(); final preference = getThemeModePreference();

View File

@ -90,7 +90,7 @@ class _AddressBookState extends State<AddressBook> {
Text(translate(error)), Text(translate(error)),
TextButton( TextButton(
onPressed: () { onPressed: () {
setState(() {}); gFFI.abModel.pullAb();
}, },
child: Text(translate("Retry"))) child: Text(translate("Retry")))
], ],

View File

@ -1,36 +1,93 @@
import 'dart:convert';
import 'dart:ui' as ui;
import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/address_book.dart';
import 'package:flutter_hbb/common/widgets/peers_view.dart'; import 'package:flutter_hbb/common/widgets/peers_view.dart';
import 'package:flutter_hbb/common/widgets/peer_card.dart'; import 'package:flutter_hbb/common/widgets/peer_card.dart';
import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/desktop/widgets/material_mod_popup_menu.dart'
as mod_menu;
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../common.dart'; import '../../common.dart';
import '../../models/platform_model.dart'; import '../../models/platform_model.dart';
class PeerTabPage extends StatefulWidget { class PeerTabPage extends StatefulWidget {
final List<String> tabs; const PeerTabPage({Key? key}) : super(key: key);
final List<Widget> children;
const PeerTabPage({required this.tabs, required this.children, Key? key})
: super(key: key);
@override @override
State<PeerTabPage> createState() => _PeerTabPageState(); State<PeerTabPage> createState() => _PeerTabPageState();
} }
class _TabEntry {
final String name;
final Widget widget;
final Function() load;
_TabEntry(this.name, this.widget, this.load);
}
class _PeerTabPageState extends State<PeerTabPage> class _PeerTabPageState extends State<PeerTabPage>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
final RxInt _tabIndex = 0.obs; late final RxInt tabHiddenFlag;
late final RxString currentTab;
late final RxList<String> visibleOrderedTabs;
final List<_TabEntry> entries = [
_TabEntry(
'Recent Sessions',
RecentPeersView(
menuPadding: kDesktopMenuPadding,
),
bind.mainLoadRecentPeers),
_TabEntry(
'Favorites',
FavoritePeersView(
menuPadding: kDesktopMenuPadding,
),
bind.mainLoadFavPeers),
_TabEntry(
'Discovered',
DiscoveredPeersView(
menuPadding: kDesktopMenuPadding,
),
bind.mainDiscover),
_TabEntry(
'Address Book',
const AddressBook(
menuPadding: kDesktopMenuPadding,
),
() => {}),
];
@override @override
void initState() { void initState() {
setPeer(); tabHiddenFlag = (int.tryParse(
super.initState(); bind.getLocalFlutterConfig(k: 'hidden-peer-card'),
radix: 2) ??
0)
.obs;
currentTab = bind.getLocalFlutterConfig(k: 'current-peer-tab').obs;
visibleOrderedTabs = entries
.where((e) => !isTabHidden(e.name))
.map((e) => e.name)
.toList()
.obs;
try {
final json = jsonDecode(bind.getLocalFlutterConfig(k: 'peer-tab-order'));
if (json is List) {
final List<String> list = json.map((e) => e.toString()).toList();
if (list.length == visibleOrderedTabs.length &&
visibleOrderedTabs.every((e) => list.contains(e))) {
visibleOrderedTabs.value = list;
}
}
} catch (e) {
debugPrint('$e');
} }
setPeer() { adjustTab();
final index = bind.getLocalFlutterConfig(k: 'peer-tab-index');
if (index != '') {
_tabIndex.value = int.parse(index);
}
final uiType = bind.getLocalFlutterConfig(k: 'peer-card-ui-type'); final uiType = bind.getLocalFlutterConfig(k: 'peer-card-ui-type');
if (uiType != '') { if (uiType != '') {
@ -38,27 +95,13 @@ class _PeerTabPageState extends State<PeerTabPage>
? PeerUiType.list ? PeerUiType.list
: PeerUiType.grid; : PeerUiType.grid;
} }
super.initState();
} }
// hard code for now Future<void> handleTabSelection(String tabName) async {
Future<void> _handleTabSelection(int index) async { currentTab.value = tabName;
_tabIndex.value = index; await bind.setLocalFlutterConfig(k: 'current-peer-tab', v: tabName);
await bind.setLocalFlutterConfig(k: 'peer-tab-index', v: index.toString()); entries.firstWhereOrNull((e) => e.name == tabName)?.load();
switch (index) {
case 0:
bind.mainLoadRecentPeers();
break;
case 1:
bind.mainLoadFavPeers();
break;
case 2:
bind.mainDiscover();
break;
case 3:
/// AddressBook initState will refresh ab state
break;
}
} }
@override @override
@ -80,8 +123,9 @@ class _PeerTabPageState extends State<PeerTabPage>
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Expanded(child: _createSwitchBar(context)), Expanded(
const SizedBox(width: 10), child: visibleContextMenuListener(
_createSwitchBar(context))),
const PeerSearchBar(), const PeerSearchBar(),
Offstage( Offstage(
offstage: !isDesktop, offstage: !isDesktop,
@ -97,16 +141,34 @@ class _PeerTabPageState extends State<PeerTabPage>
Widget _createSwitchBar(BuildContext context) { Widget _createSwitchBar(BuildContext context) {
final textColor = Theme.of(context).textTheme.titleLarge?.color; final textColor = Theme.of(context).textTheme.titleLarge?.color;
return ListView( return Obx(() {
int indexCounter = -1;
return ReorderableListView(
buildDefaultDragHandles: false,
onReorder: (oldIndex, newIndex) {
var list = visibleOrderedTabs.toList();
if (oldIndex < newIndex) {
newIndex -= 1;
}
final String item = list.removeAt(oldIndex);
list.insert(newIndex, item);
bind.setLocalFlutterConfig(
k: 'peer-tab-order', v: jsonEncode(list));
visibleOrderedTabs.value = list;
},
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
shrinkWrap: true, shrinkWrap: true,
controller: ScrollController(), scrollController: ScrollController(),
children: super.widget.tabs.asMap().entries.map((t) { children: visibleOrderedTabs.map((t) {
return Obx(() => InkWell( indexCounter++;
return ReorderableDragStartListener(
key: ValueKey(t),
index: indexCounter,
child: InkWell(
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8), padding: const EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _tabIndex.value == t.key color: currentTab.value == t
? Theme.of(context).backgroundColor ? Theme.of(context).backgroundColor
: null, : null,
borderRadius: BorderRadius.circular(isDesktop ? 2 : 6), borderRadius: BorderRadius.circular(isDesktop ? 2 : 6),
@ -114,27 +176,30 @@ class _PeerTabPageState extends State<PeerTabPage>
child: Align( child: Align(
alignment: Alignment.center, alignment: Alignment.center,
child: Text( child: Text(
t.value, translate(t),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
height: 1, height: 1,
fontSize: 14, fontSize: 14,
color: color: currentTab.value == t ? textColor : textColor
_tabIndex.value == t.key ? textColor : textColor
?..withOpacity(0.5)), ?..withOpacity(0.5)),
), ),
)), )),
onTap: () async => await _handleTabSelection(t.key), onTap: () async => await handleTabSelection(t),
)); ),
);
}).toList()); }).toList());
});
} }
Widget _createPeersView() { Widget _createPeersView() {
final verticalMargin = isDesktop ? 12.0 : 6.0; final verticalMargin = isDesktop ? 12.0 : 6.0;
return Expanded( return Expanded(
child: Obx(() => widget child: Obx(() =>
.children[_tabIndex.value]) //: (to) => _tabIndex.value = to) entries.firstWhereOrNull((e) => e.name == currentTab.value)?.widget ??
.marginSymmetric(vertical: verticalMargin), visibleContextMenuListener(Center(
child: Text(translate('Right click to select tabs')),
))).marginSymmetric(vertical: verticalMargin),
); );
} }
@ -167,6 +232,87 @@ class _PeerTabPageState extends State<PeerTabPage>
.toList(), .toList(),
); );
} }
bool isTabHidden(String name) {
int index = entries.indexWhere((e) => e.name == name);
if (index >= 0) {
return tabHiddenFlag & (1 << index) != 0;
}
assert(false);
return false;
}
adjustTab() {
if (visibleOrderedTabs.isNotEmpty) {
if (!visibleOrderedTabs.contains(currentTab.value)) {
handleTabSelection(visibleOrderedTabs[0]);
}
} else {
currentTab.value = '';
}
}
Widget visibleContextMenuListener(Widget child) {
return Listener(
onPointerDown: (e) {
if (e.kind != ui.PointerDeviceKind.mouse) {
return;
}
if (e.buttons == 2) {
showRightMenu(
(CancelFunc cancelFunc) {
return visibleContextMenu(cancelFunc);
},
target: e.position,
);
}
},
child: child);
}
Widget visibleContextMenu(CancelFunc cancelFunc) {
final List<MenuEntryBase> menu = entries.asMap().entries.map((e) {
int bitMask = 1 << e.key;
return MenuEntrySwitch(
switchType: SwitchType.scheckbox,
text: translate(e.value.name),
getter: () async {
return tabHiddenFlag.value & bitMask == 0;
},
setter: (show) async {
if (show) {
tabHiddenFlag.value &= ~bitMask;
} else {
tabHiddenFlag.value |= bitMask;
}
await bind.setLocalFlutterConfig(
k: 'hidden-peer-card', v: tabHiddenFlag.value.toRadixString(2));
visibleOrderedTabs.removeWhere((e) => isTabHidden(e));
visibleOrderedTabs.addAll(entries
.where((e) =>
!visibleOrderedTabs.contains(e.name) &&
!isTabHidden(e.name))
.map((e) => e.name)
.toList());
await bind.setLocalFlutterConfig(
k: 'peer-tab-order', v: jsonEncode(visibleOrderedTabs));
cancelFunc();
adjustTab();
});
}).toList();
return mod_menu.PopupMenu(
items: menu
.map((entry) => entry.build(
context,
const MenuConfig(
commonColor: MyTheme.accent,
height: 20.0,
dividerHeight: 12.0,
)))
.expand((i) => i)
.toList(),
);
}
} }
class PeerSearchBar extends StatefulWidget { class PeerSearchBar extends StatefulWidget {

View File

@ -113,7 +113,7 @@ class _ConnectionPageState extends State<ConnectionPage>
delegate: SliverChildListDelegate([ delegate: SliverChildListDelegate([
Row( Row(
children: [ children: [
_buildRemoteIDTextField(context), Flexible(child: _buildRemoteIDTextField(context)),
], ],
).marginOnly(top: 22), ).marginOnly(top: 22),
SizedBox(height: 12), SizedBox(height: 12),
@ -121,28 +121,7 @@ class _ConnectionPageState extends State<ConnectionPage>
])), ])),
SliverFillRemaining( SliverFillRemaining(
hasScrollBody: false, hasScrollBody: false,
child: PeerTabPage( child: PeerTabPage().paddingOnly(right: 12.0),
tabs: [
translate('Recent Sessions'),
translate('Favorites'),
translate('Discovered'),
translate('Address Book')
],
children: [
RecentPeersView(
menuPadding: kDesktopMenuPadding,
),
FavoritePeersView(
menuPadding: kDesktopMenuPadding,
),
DiscoveredPeersView(
menuPadding: kDesktopMenuPadding,
),
const AddressBook(
menuPadding: kDesktopMenuPadding,
),
],
).paddingOnly(right: 12.0),
) )
], ],
).paddingOnly(left: 12.0), ).paddingOnly(left: 12.0),
@ -258,9 +237,8 @@ class _ConnectionPageState extends State<ConnectionPage>
), ),
), ),
); );
return Center( return Container(
child: Container( constraints: const BoxConstraints(maxWidth: 600), child: w);
constraints: const BoxConstraints(maxWidth: 600), child: w));
} }
Widget buildStatus() { Widget buildStatus() {

View File

@ -238,7 +238,7 @@ Widget buildConnectionCard(Client client) {
key: ValueKey(client.id), key: ValueKey(client.id),
children: [ children: [
_CmHeader(client: client), _CmHeader(client: client),
client.isFileTransfer || client.disconnected client.type_() != ClientType.remote || client.disconnected
? Offstage() ? Offstage()
: _PrivilegeBoard(client: client), : _PrivilegeBoard(client: client),
Expanded( Expanded(
@ -376,7 +376,7 @@ class _CmHeaderState extends State<_CmHeader>
), ),
), ),
Offstage( Offstage(
offstage: !client.authorized || client.isFileTransfer, offstage: !client.authorized || client.type_() != ClientType.remote,
child: IconButton( child: IconButton(
onPressed: () => checkClickTime( onPressed: () => checkClickTime(
client.id, () => gFFI.chatModel.toggleCMChatPage(client.id)), client.id, () => gFFI.chatModel.toggleCMChatPage(client.id)),
@ -510,7 +510,9 @@ class _CmControlPanel extends StatelessWidget {
buildAuthorized(BuildContext context) { buildAuthorized(BuildContext context) {
final bool canElevate = bind.cmCanElevate(); final bool canElevate = bind.cmCanElevate();
final model = Provider.of<ServerModel>(context); final model = Provider.of<ServerModel>(context);
final showElevation = canElevate && model.showElevation; final showElevation = canElevate &&
model.showElevation &&
client.type_() == ClientType.remote;
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
@ -560,7 +562,9 @@ class _CmControlPanel extends StatelessWidget {
buildUnAuthorized(BuildContext context) { buildUnAuthorized(BuildContext context) {
final bool canElevate = bind.cmCanElevate(); final bool canElevate = bind.cmCanElevate();
final model = Provider.of<ServerModel>(context); final model = Provider.of<ServerModel>(context);
final showElevation = canElevate && model.showElevation; final showElevation = canElevate &&
model.showElevation &&
client.type_() == ClientType.remote;
final showAccept = model.approveMode != 'password'; final showAccept = model.approveMode != 'password';
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,

View File

@ -75,20 +75,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
])), ])),
SliverFillRemaining( SliverFillRemaining(
hasScrollBody: false, hasScrollBody: false,
child: PeerTabPage( child: PeerTabPage(),
tabs: [
translate('Recent Sessions'),
translate('Favorites'),
translate('Discovered'),
translate('Address Book')
],
children: [
RecentPeersView(),
FavoritePeersView(),
DiscoveredPeersView(),
const AddressBook(),
],
),
) )
], ],
).marginOnly(top: 2, left: 10, right: 10); ).marginOnly(top: 2, left: 10, right: 10);

View File

@ -34,15 +34,22 @@ class AbModel {
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") { if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
Map<String, dynamic> json = jsonDecode(resp.body); Map<String, dynamic> json = jsonDecode(resp.body);
if (json.containsKey('error')) { if (json.containsKey('error')) {
abError = json['error']; abError.value = json['error'];
} else if (json.containsKey('data')) { } else if (json.containsKey('data')) {
final data = jsonDecode(json['data']); final data = jsonDecode(json['data']);
tags.value = data['tags']; if (data != null) {
tags.clear();
peers.clear(); peers.clear();
if (data['tags'] is List) {
tags.value = data['tags'];
}
if (data['peers'] is List) {
for (final peer in data['peers']) { for (final peer in data['peers']) {
peers.add(Peer.fromJson(peer)); peers.add(Peer.fromJson(peer));
} }
} }
}
}
return resp.body; return resp.body;
} else { } else {
return ""; return "";

View File

@ -581,10 +581,17 @@ class ServerModel with ChangeNotifier {
} }
} }
enum ClientType {
remote,
file,
portForward,
}
class Client { class Client {
int id = 0; // client connections inner count id int id = 0; // client connections inner count id
bool authorized = false; bool authorized = false;
bool isFileTransfer = false; bool isFileTransfer = false;
String portForward = "";
String name = ""; String name = "";
String peerId = ""; // peer user's id,show at app String peerId = ""; // peer user's id,show at app
bool keyboard = false; bool keyboard = false;
@ -604,6 +611,7 @@ class Client {
id = json['id']; id = json['id'];
authorized = json['authorized']; authorized = json['authorized'];
isFileTransfer = json['is_file_transfer']; isFileTransfer = json['is_file_transfer'];
portForward = json['port_forward'];
name = json['name']; name = json['name'];
peerId = json['peer_id']; peerId = json['peer_id'];
keyboard = json['keyboard']; keyboard = json['keyboard'];
@ -620,6 +628,7 @@ class Client {
data['id'] = id; data['id'] = id;
data['is_start'] = authorized; data['is_start'] = authorized;
data['is_file_transfer'] = isFileTransfer; data['is_file_transfer'] = isFileTransfer;
data['port_forward'] = portForward;
data['name'] = name; data['name'] = name;
data['peer_id'] = peerId; data['peer_id'] = peerId;
data['keyboard'] = keyboard; data['keyboard'] = keyboard;
@ -631,6 +640,16 @@ class Client {
data['disconnected'] = disconnected; data['disconnected'] = disconnected;
return data; return data;
} }
ClientType type_() {
if (isFileTransfer) {
return ClientType.file;
} else if (portForward.isNotEmpty) {
return ClientType.portForward;
} else {
return ClientType.remote;
}
}
} }
String getLoginDialogTag(int id) { String getLoginDialogTag(int id) {

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", "隐藏连接管理窗口"), ("Hide connection management window", "隐藏连接管理窗口"),
("hide_cm_tip", "在只允许密码连接并且只用固定密码的情况下才允许隐藏"), ("hide_cm_tip", "在只允许密码连接并且只用固定密码的情况下才允许隐藏"),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", "右键选择选项卡"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", "Fenster zur Verwaltung der Verbindung verstecken"), ("Hide connection management window", "Fenster zur Verwaltung der Verbindung verstecken"),
("hide_cm_tip", "Dies ist nur möglich, wenn der Zugriff nur über ein permanentes Passwort erfolgt."), // Sehr unklar. Muss noch angepasst werden. Original: Allow hiding only if accepting sessions via password and using pernament passw"), ("hide_cm_tip", "Dies ist nur möglich, wenn der Zugriff nur über ein permanentes Passwort erfolgt."), // Sehr unklar. Muss noch angepasst werden. Original: Allow hiding only if accepting sessions via password and using pernament passw"),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", "Ocultar ventana de gestión de conexión"), ("Hide connection management window", "Ocultar ventana de gestión de conexión"),
("hide_cm_tip", "Permitir ocultar solo si se aceptan sesiones a través de contraseña y usando contraseña permanente"), ("hide_cm_tip", "Permitir ocultar solo si se aceptan sesiones a través de contraseña y usando contraseña permanente"),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", "پنهان کردن پنجره مدیریت اتصال"), ("Hide connection management window", "پنهان کردن پنجره مدیریت اتصال"),
("hide_cm_tip", "فقط در صورت پذیرفتن جلسات از طریق رمز عبور و استفاده از رمز عبور دائمی، مخفی شدن مجاز است"), ("hide_cm_tip", "فقط در صورت پذیرفتن جلسات از طریق رمز عبور و استفاده از رمز عبور دائمی، مخفی شدن مجاز است"),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", "Masquer la fenêtre de gestion des connexions"), ("Hide connection management window", "Masquer la fenêtre de gestion des connexions"),
("hide_cm_tip", "Autoriser le masquage uniquement si vous acceptez des sessions via un mot de passe et utilisez un mot de passe permanent"), ("hide_cm_tip", "Autoriser le masquage uniquement si vous acceptez des sessions via un mot de passe et utilisez un mot de passe permanent"),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -397,6 +397,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Request access to your device", "Αίτημα πρόσβασης στη συσκευή σας"), ("Request access to your device", "Αίτημα πρόσβασης στη συσκευή σας"),
("Hide connection management window", "Απόκρυψη παραθύρου διαχείρισης σύνδεσης"), ("Hide connection management window", "Απόκρυψη παραθύρου διαχείρισης σύνδεσης"),
("hide_cm_tip", "Να επιτρέπεται η απόκρυψη, μόνο εάν αποδέχεστε συνδέσεις μέσω κωδικού πρόσβασης και χρησιμοποιείτε μόνιμο κωδικό πρόσβασης"), ("hide_cm_tip", "Να επιτρέπεται η απόκρυψη, μόνο εάν αποδέχεστε συνδέσεις μέσω κωδικού πρόσβασης και χρησιμοποιείτε μόνιμο κωδικό πρόσβασης"),
("wayland_experiment_tip", ""), ("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", "Nascondi la finestra di gestione delle connessioni"), ("Hide connection management window", "Nascondi la finestra di gestione delle connessioni"),
("hide_cm_tip", "Permetti di nascondere solo se si accettano sessioni con password permanente"), ("hide_cm_tip", "Permetti di nascondere solo se si accettano sessioni con password permanente"),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", "Скрывать окно управления соединениями"), ("Hide connection management window", "Скрывать окно управления соединениями"),
("hide_cm_tip", "Разрешать скрытие случае, если принимаются сеансы по паролю или используется постоянный пароль"), ("hide_cm_tip", "Разрешать скрытие случае, если принимаются сеансы по паролю или используется постоянный пароль"),
("wayland_experiment_tip", "Поддержка Wayland находится на экспериментальной стадии, используйте X11, если вам требуется автоматический доступ."), ("wayland_experiment_tip", "Поддержка Wayland находится на экспериментальной стадии, используйте X11, если вам требуется автоматический доступ."),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", "Fshih dritaren e menaxhimit të lidhjes"), ("Hide connection management window", "Fshih dritaren e menaxhimit të lidhjes"),
("hide_cm_tip", "Kjo është e mundur vetëm nëse aksesi bëhet nëpërmjet një fjalëkalimi të përhershëm"), ("hide_cm_tip", "Kjo është e mundur vetëm nëse aksesi bëhet nëpërmjet një fjalëkalimi të përhershëm"),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", "Göm hanteringsfönster"), ("Hide connection management window", "Göm hanteringsfönster"),
("hide_cm_tip", "Tillåt att gömma endast om accepterande sessioner med lösenord och permanenta lösenord"), ("hide_cm_tip", "Tillåt att gömma endast om accepterande sessioner med lösenord och permanenta lösenord"),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", "Bağlantı yönetimi penceresini gizle"), ("Hide connection management window", "Bağlantı yönetimi penceresini gizle"),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", "隱藏連接管理窗口"), ("Hide connection management window", "隱藏連接管理窗口"),
("hide_cm_tip", "在只允許密碼連接並且只用固定密碼的情況下才允許隱藏"), ("hide_cm_tip", "在只允許密碼連接並且只用固定密碼的情況下才允許隱藏"),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", "右鍵選擇選項卡"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", ""), ("Hide connection management window", ""),
("hide_cm_tip", ""), ("hide_cm_tip", ""),
("wayland_experiment_tip", ""), ("wayland_experiment_tip", ""),
("Right click to select tabs", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -29,7 +29,7 @@ class Body: Reactor.Component
}; };
var right_style = show_chat ? "" : "display: none"; var right_style = show_chat ? "" : "display: none";
var disconnected = c.disconnected; var disconnected = c.disconnected;
var show_elevation_btn = handler.can_elevate() && show_elevation; var show_elevation_btn = handler.can_elevate() && show_elevation && !c.is_file_transfer && c.port_forward.length == 0;
var show_accept_btn = handler.get_option('approve-mode') != 'password'; var show_accept_btn = handler.get_option('approve-mode') != 'password';
// below size:* is work around for Linux, it alreayd set in css, but not work, shit sciter // below size:* is work around for Linux, it alreayd set in css, but not work, shit sciter
return <div .content style="size:*"> return <div .content style="size:*">

View File

@ -200,7 +200,7 @@ impl<T: InvokeUiSession> Session<T> {
h265 = h265 && encoding_265; h265 = h265 && encoding_265;
return (h264, h265); return (h264, h265);
} }
#[allow(dead_code)] #[allow(unreachable_code)]
(false, false) (false, false)
} }
@ -1211,7 +1211,13 @@ impl<T: InvokeUiSession> Interface for Session<T> {
input_os_password(p, true, self.clone()); input_os_password(p, true, self.clone());
} }
let current = &pi.displays[pi.current_display as usize]; let current = &pi.displays[pi.current_display as usize];
self.set_display(current.x, current.y, current.width, current.height, current.cursor_embeded); self.set_display(
current.x,
current.y,
current.width,
current.height,
current.cursor_embeded,
);
} }
self.update_privacy_mode(); self.update_privacy_mode();
// Save recent peers, then push event to flutter. So flutter can refresh peer page. // Save recent peers, then push event to flutter. So flutter can refresh peer page.