diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index eac7fbf9b..4c298d917 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -215,18 +215,15 @@ class MyTheme { } static void changeDarkMode(ThemeMode mode) { - final preference = getThemeModePreference(); - if (preference != mode) { + Get.changeThemeMode(mode); + if (desktopType == DesktopType.main) { if (mode == ThemeMode.system) { bind.mainSetLocalOption(key: kCommConfKeyTheme, value: ''); } else { bind.mainSetLocalOption( key: kCommConfKeyTheme, value: mode.toShortString()); } - Get.changeThemeMode(mode); - if (desktopType == DesktopType.main) { - bind.mainChangeTheme(dark: currentThemeMode().toShortString()); - } + bind.mainChangeTheme(dark: currentThemeMode().toShortString()); } } diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index c96dc115a..5b3527fa0 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -90,7 +90,7 @@ class _AddressBookState extends State<AddressBook> { Text(translate(error)), TextButton( onPressed: () { - setState(() {}); + gFFI.abModel.pullAb(); }, child: Text(translate("Retry"))) ], diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index 523230810..01e3939ee 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -1,64 +1,107 @@ +import 'dart:convert'; +import 'dart:ui' as ui; + +import 'package:bot_toast/bot_toast.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/peer_card.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 '../../common.dart'; import '../../models/platform_model.dart'; class PeerTabPage extends StatefulWidget { - final List<String> tabs; - final List<Widget> children; - const PeerTabPage({required this.tabs, required this.children, Key? key}) - : super(key: key); + const PeerTabPage({Key? key}) : super(key: key); @override 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> 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 void initState() { - setPeer(); - super.initState(); - } - - setPeer() { - final index = bind.getLocalFlutterConfig(k: 'peer-tab-index'); - if (index != '') { - _tabIndex.value = int.parse(index); + tabHiddenFlag = (int.tryParse( + 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'); } + adjustTab(); + final uiType = bind.getLocalFlutterConfig(k: 'peer-card-ui-type'); if (uiType != '') { peerCardUiType.value = int.parse(uiType) == PeerUiType.list.index ? PeerUiType.list : PeerUiType.grid; } + super.initState(); } - // hard code for now - Future<void> _handleTabSelection(int index) async { - _tabIndex.value = index; - await bind.setLocalFlutterConfig(k: 'peer-tab-index', v: index.toString()); - 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; - } + Future<void> handleTabSelection(String tabName) async { + currentTab.value = tabName; + await bind.setLocalFlutterConfig(k: 'current-peer-tab', v: tabName); + entries.firstWhereOrNull((e) => e.name == tabName)?.load(); } @override @@ -80,8 +123,9 @@ class _PeerTabPageState extends State<PeerTabPage> child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Expanded(child: _createSwitchBar(context)), - const SizedBox(width: 10), + Expanded( + child: visibleContextMenuListener( + _createSwitchBar(context))), const PeerSearchBar(), Offstage( offstage: !isDesktop, @@ -97,16 +141,34 @@ class _PeerTabPageState extends State<PeerTabPage> Widget _createSwitchBar(BuildContext context) { final textColor = Theme.of(context).textTheme.titleLarge?.color; - return ListView( - scrollDirection: Axis.horizontal, - shrinkWrap: true, - controller: ScrollController(), - children: super.widget.tabs.asMap().entries.map((t) { - return Obx(() => InkWell( + 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, + shrinkWrap: true, + scrollController: ScrollController(), + children: visibleOrderedTabs.map((t) { + indexCounter++; + return ReorderableDragStartListener( + key: ValueKey(t), + index: indexCounter, + child: InkWell( child: Container( padding: const EdgeInsets.symmetric(horizontal: 8), decoration: BoxDecoration( - color: _tabIndex.value == t.key + color: currentTab.value == t ? Theme.of(context).backgroundColor : null, borderRadius: BorderRadius.circular(isDesktop ? 2 : 6), @@ -114,27 +176,30 @@ class _PeerTabPageState extends State<PeerTabPage> child: Align( alignment: Alignment.center, child: Text( - t.value, + translate(t), textAlign: TextAlign.center, style: TextStyle( height: 1, fontSize: 14, - color: - _tabIndex.value == t.key ? textColor : textColor - ?..withOpacity(0.5)), + color: currentTab.value == t ? textColor : textColor + ?..withOpacity(0.5)), ), )), - onTap: () async => await _handleTabSelection(t.key), - )); - }).toList()); + onTap: () async => await handleTabSelection(t), + ), + ); + }).toList()); + }); } Widget _createPeersView() { final verticalMargin = isDesktop ? 12.0 : 6.0; return Expanded( - child: Obx(() => widget - .children[_tabIndex.value]) //: (to) => _tabIndex.value = to) - .marginSymmetric(vertical: verticalMargin), + child: Obx(() => + entries.firstWhereOrNull((e) => e.name == currentTab.value)?.widget ?? + visibleContextMenuListener(Center( + child: Text(translate('Right click to select tabs')), + ))).marginSymmetric(vertical: verticalMargin), ); } @@ -167,6 +232,87 @@ class _PeerTabPageState extends State<PeerTabPage> .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 { diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index a830d6399..7500fe99e 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -113,7 +113,7 @@ class _ConnectionPageState extends State<ConnectionPage> delegate: SliverChildListDelegate([ Row( children: [ - _buildRemoteIDTextField(context), + Flexible(child: _buildRemoteIDTextField(context)), ], ).marginOnly(top: 22), SizedBox(height: 12), @@ -121,28 +121,7 @@ class _ConnectionPageState extends State<ConnectionPage> ])), SliverFillRemaining( hasScrollBody: false, - child: PeerTabPage( - 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), + child: PeerTabPage().paddingOnly(right: 12.0), ) ], ).paddingOnly(left: 12.0), @@ -258,9 +237,8 @@ class _ConnectionPageState extends State<ConnectionPage> ), ), ); - return Center( - child: Container( - constraints: const BoxConstraints(maxWidth: 600), child: w)); + return Container( + constraints: const BoxConstraints(maxWidth: 600), child: w); } Widget buildStatus() { diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index 6c586994b..fa367f488 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -238,7 +238,7 @@ Widget buildConnectionCard(Client client) { key: ValueKey(client.id), children: [ _CmHeader(client: client), - client.isFileTransfer || client.disconnected + client.type_() != ClientType.remote || client.disconnected ? Offstage() : _PrivilegeBoard(client: client), Expanded( @@ -376,7 +376,7 @@ class _CmHeaderState extends State<_CmHeader> ), ), Offstage( - offstage: !client.authorized || client.isFileTransfer, + offstage: !client.authorized || client.type_() != ClientType.remote, child: IconButton( onPressed: () => checkClickTime( client.id, () => gFFI.chatModel.toggleCMChatPage(client.id)), @@ -510,7 +510,9 @@ class _CmControlPanel extends StatelessWidget { buildAuthorized(BuildContext context) { final bool canElevate = bind.cmCanElevate(); final model = Provider.of<ServerModel>(context); - final showElevation = canElevate && model.showElevation; + final showElevation = canElevate && + model.showElevation && + client.type_() == ClientType.remote; return Column( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -560,7 +562,9 @@ class _CmControlPanel extends StatelessWidget { buildUnAuthorized(BuildContext context) { final bool canElevate = bind.cmCanElevate(); 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'; return Column( mainAxisAlignment: MainAxisAlignment.end, diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index e99226c4d..957910324 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -75,20 +75,7 @@ class _ConnectionPageState extends State<ConnectionPage> { ])), SliverFillRemaining( hasScrollBody: false, - child: PeerTabPage( - tabs: [ - translate('Recent Sessions'), - translate('Favorites'), - translate('Discovered'), - translate('Address Book') - ], - children: [ - RecentPeersView(), - FavoritePeersView(), - DiscoveredPeersView(), - const AddressBook(), - ], - ), + child: PeerTabPage(), ) ], ).marginOnly(top: 2, left: 10, right: 10); diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index 5a055fd14..afee97e75 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -34,13 +34,20 @@ class AbModel { if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") { Map<String, dynamic> json = jsonDecode(resp.body); if (json.containsKey('error')) { - abError = json['error']; + abError.value = json['error']; } else if (json.containsKey('data')) { final data = jsonDecode(json['data']); - tags.value = data['tags']; - peers.clear(); - for (final peer in data['peers']) { - peers.add(Peer.fromJson(peer)); + if (data != null) { + tags.clear(); + peers.clear(); + if (data['tags'] is List) { + tags.value = data['tags']; + } + if (data['peers'] is List) { + for (final peer in data['peers']) { + peers.add(Peer.fromJson(peer)); + } + } } } return resp.body; diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index 344733324..338da4ee3 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -581,10 +581,17 @@ class ServerModel with ChangeNotifier { } } +enum ClientType { + remote, + file, + portForward, +} + class Client { int id = 0; // client connections inner count id bool authorized = false; bool isFileTransfer = false; + String portForward = ""; String name = ""; String peerId = ""; // peer user's id,show at app bool keyboard = false; @@ -604,6 +611,7 @@ class Client { id = json['id']; authorized = json['authorized']; isFileTransfer = json['is_file_transfer']; + portForward = json['port_forward']; name = json['name']; peerId = json['peer_id']; keyboard = json['keyboard']; @@ -620,6 +628,7 @@ class Client { data['id'] = id; data['is_start'] = authorized; data['is_file_transfer'] = isFileTransfer; + data['port_forward'] = portForward; data['name'] = name; data['peer_id'] = peerId; data['keyboard'] = keyboard; @@ -631,6 +640,16 @@ class Client { data['disconnected'] = disconnected; return data; } + + ClientType type_() { + if (isFileTransfer) { + return ClientType.file; + } else if (portForward.isNotEmpty) { + return ClientType.portForward; + } else { + return ClientType.remote; + } + } } String getLoginDialogTag(int id) { diff --git a/src/lang/ca.rs b/src/lang/ca.rs index e7794b989..6fc919b8d 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 8af5229f2..1b49f6c4a 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", "隐藏连接管理窗口"), ("hide_cm_tip", "在只允许密码连接并且只用固定密码的情况下才允许隐藏"), ("wayland_experiment_tip", ""), + ("Right click to select tabs", "右键选择选项卡"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 547b233f4..6455023de 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 5d815a90b..afd6476ee 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index e4306301b..273a607ed 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -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_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", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index f5a2f7e55..3a38b6601 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index d8362ff05..39d031c9d 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -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_cm_tip", "Permitir ocultar solo si se aceptan sesiones a través de contraseña y usando contraseña permanente"), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index ee3706e34..7513f84e8 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", "پنهان کردن پنجره مدیریت اتصال"), ("hide_cm_tip", "فقط در صورت پذیرفتن جلسات از طریق رمز عبور و استفاده از رمز عبور دائمی، مخفی شدن مجاز است"), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 860f59518..a4eedfa2a 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -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_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", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/gr.rs b/src/lang/gr.rs index 20bb98200..986919a8b 100644 --- a/src/lang/gr.rs +++ b/src/lang/gr.rs @@ -397,6 +397,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Request access to your device", "Αίτημα πρόσβασης στη συσκευή σας"), ("Hide connection management window", "Απόκρυψη παραθύρου διαχείρισης σύνδεσης"), ("hide_cm_tip", "Να επιτρέπεται η απόκρυψη, μόνο εάν αποδέχεστε συνδέσεις μέσω κωδικού πρόσβασης και χρησιμοποιείτε μόνιμο κωδικό πρόσβασης"), - ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 51f072e16..941caeac1 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index b2492553d..7f4a50523 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index ec993ad04..98ea9366e 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -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_cm_tip", "Permetti di nascondere solo se si accettano sessioni con password permanente"), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 5cd6d8d6d..4121dd55c 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 31d69841c..a1cd730e9 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index c32fa778d..1e623c0b6 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index edd9a4e45..ed14e3ca2 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 1c174af8d..65620e3e8 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 86bd1b77f..cf388a88c 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index bcb539498..76a8cca87 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", "Скрывать окно управления соединениями"), ("hide_cm_tip", "Разрешать скрытие случае, если принимаются сеансы по паролю или используется постоянный пароль"), ("wayland_experiment_tip", "Поддержка Wayland находится на экспериментальной стадии, используйте X11, если вам требуется автоматический доступ."), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 194b3dc83..e7a9b12b5 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index a7cb45443..1926c849b 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -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_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", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 79c9bbcc1..cfef903a7 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("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"), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index acc92be20..ed7189ce6 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index bd2e9cd62..6c518da81 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -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_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 35b2cb2cf..e7c024420 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", "隱藏連接管理窗口"), ("hide_cm_tip", "在只允許密碼連接並且只用固定密碼的情況下才允許隱藏"), ("wayland_experiment_tip", ""), + ("Right click to select tabs", "右鍵選擇選項卡"), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index b1fdca17a..713d15c69 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index acac782d1..c59de33fc 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -398,5 +398,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Hide connection management window", ""), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), + ("Right click to select tabs", ""), ].iter().cloned().collect(); } diff --git a/src/ui/cm.tis b/src/ui/cm.tis index 2cfc14bf1..74eb6c6d2 100644 --- a/src/ui/cm.tis +++ b/src/ui/cm.tis @@ -29,7 +29,7 @@ class Body: Reactor.Component }; var right_style = show_chat ? "" : "display: none"; 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'; // below size:* is work around for Linux, it alreayd set in css, but not work, shit sciter return <div .content style="size:*"> diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 6f8820e87..6b635436d 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -200,7 +200,7 @@ impl<T: InvokeUiSession> Session<T> { h265 = h265 && encoding_265; return (h264, h265); } - #[allow(dead_code)] + #[allow(unreachable_code)] (false, false) } @@ -1211,7 +1211,13 @@ impl<T: InvokeUiSession> Interface for Session<T> { input_os_password(p, true, self.clone()); } 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(); // Save recent peers, then push event to flutter. So flutter can refresh peer page.