allow hide peer tab
Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
parent
348ed268c3
commit
09d380ba8f
@ -1,3 +1,6 @@
|
||||
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/dialog.dart';
|
||||
@ -6,6 +9,9 @@ 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/material_mod_popup_menu.dart'
|
||||
as mod_menu;
|
||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||
import 'package:flutter_hbb/models/ab_model.dart';
|
||||
|
||||
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
||||
@ -61,6 +67,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
({dynamic hint}) => gFFI.groupModel.pull(force: hint == null),
|
||||
),
|
||||
];
|
||||
RelativeRect? mobileTabContextMenuPos;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -100,7 +107,9 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
child: selectionWrap(Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(child: _createSwitchBar(context)),
|
||||
Expanded(
|
||||
child:
|
||||
visibleContextMenuListener(_createSwitchBar(context))),
|
||||
const PeerSearchBar().marginOnly(right: isMobile ? 0 : 13),
|
||||
_createRefresh(),
|
||||
_createMultiSelection(),
|
||||
@ -145,7 +154,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
return ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
children: model.indexs.map((t) {
|
||||
children: model.visibleIndexs.map((t) {
|
||||
final selected = model.currentTab == t;
|
||||
final color = selected
|
||||
? MyTheme.tabbar(context).selectedTextColor
|
||||
@ -164,8 +173,10 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
decoration:
|
||||
selected ? decoBorder : (hover.value ? deco : null),
|
||||
child: Tooltip(
|
||||
preferBelow: false,
|
||||
message:
|
||||
model.tabTooltip(t, gFFI.groupModel.groupName.value),
|
||||
onTriggered: isMobile ? mobileShowTabVisibilityMenu : null,
|
||||
child: Icon(model.tabIcon(t), color: color),
|
||||
).paddingSymmetric(horizontal: 4),
|
||||
).paddingSymmetric(horizontal: 4),
|
||||
@ -182,14 +193,15 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
Widget _createPeersView() {
|
||||
final model = Provider.of<PeerTabModel>(context);
|
||||
Widget child;
|
||||
if (model.indexs.isEmpty) {
|
||||
child = Center(
|
||||
child: Text(translate('Right click to select tabs')),
|
||||
);
|
||||
if (model.visibleIndexs.isEmpty) {
|
||||
child = visibleContextMenuListener(Row(
|
||||
children: [Expanded(child: InkWell())],
|
||||
));
|
||||
} else {
|
||||
if (model.indexs.contains(model.currentTab)) {
|
||||
if (model.visibleIndexs.contains(model.currentTab)) {
|
||||
child = entries[model.currentTab].widget;
|
||||
} else {
|
||||
debugPrint("should not happen! currentTab not in visibleIndexs");
|
||||
Future.delayed(Duration.zero, () {
|
||||
model.setCurrentTab(model.indexs[0]);
|
||||
});
|
||||
@ -268,6 +280,96 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
);
|
||||
}
|
||||
|
||||
void mobileShowTabVisibilityMenu() {
|
||||
final model = gFFI.peerTabModel;
|
||||
final items = List<PopupMenuItem>.empty(growable: true);
|
||||
for (int i = 0; i < model.tabNames.length; i++) {
|
||||
items.add(PopupMenuItem(
|
||||
height: kMinInteractiveDimension * 0.8,
|
||||
onTap: () => model.setTabVisible(i, !model.isVisible[i]),
|
||||
child: Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: model.isVisible[i],
|
||||
onChanged: (_) {
|
||||
model.setTabVisible(i, !model.isVisible[i]);
|
||||
if (Navigator.canPop(context)) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}),
|
||||
Expanded(
|
||||
child:
|
||||
Text(model.tabTooltip(i, gFFI.groupModel.groupName.value))),
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
if (mobileTabContextMenuPos != null) {
|
||||
showMenu(
|
||||
context: context, position: mobileTabContextMenuPos!, items: items);
|
||||
}
|
||||
}
|
||||
|
||||
Widget visibleContextMenuListener(Widget child) {
|
||||
if (isMobile) {
|
||||
return GestureDetector(
|
||||
onLongPressDown: (e) {
|
||||
final x = e.globalPosition.dx;
|
||||
final y = e.globalPosition.dy;
|
||||
mobileTabContextMenuPos = RelativeRect.fromLTRB(x, y, x, y);
|
||||
},
|
||||
onLongPressUp: () {
|
||||
mobileShowTabVisibilityMenu();
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
} else {
|
||||
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 model = Provider.of<PeerTabModel>(context);
|
||||
final menu = List<MenuEntrySwitch>.empty(growable: true);
|
||||
for (int i = 0; i < model.tabNames.length; i++) {
|
||||
menu.add(MenuEntrySwitch(
|
||||
switchType: SwitchType.scheckbox,
|
||||
text: model.tabTooltip(i, gFFI.groupModel.groupName.value),
|
||||
getter: () async {
|
||||
return model.isVisible[i];
|
||||
},
|
||||
setter: (show) async {
|
||||
model.setTabVisible(i, show);
|
||||
cancelFunc();
|
||||
}));
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
Widget createMultiSelectionBar() {
|
||||
final model = Provider.of<PeerTabModel>(context);
|
||||
return Row(
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@ -36,7 +37,10 @@ class PeerTabModel with ChangeNotifier {
|
||||
IconFont.addressBook,
|
||||
Icons.group,
|
||||
];
|
||||
final List<bool> _isVisible = List.filled(4, true, growable: false);
|
||||
List<bool> get isVisible => _isVisible;
|
||||
List<int> get indexs => List.generate(tabNames.length, (index) => index);
|
||||
List<int> get visibleIndexs => indexs.where((e) => _isVisible[e]).toList();
|
||||
List<Peer> _selectedPeers = List.empty(growable: true);
|
||||
List<Peer> get selectedPeers => _selectedPeers;
|
||||
bool _multiSelectionMode = false;
|
||||
@ -49,12 +53,29 @@ class PeerTabModel with ChangeNotifier {
|
||||
String get lastId => _lastId;
|
||||
|
||||
PeerTabModel(this.parent) {
|
||||
// visible
|
||||
try {
|
||||
final option = bind.getLocalFlutterOption(k: 'peer-tab-visible');
|
||||
if (option.isNotEmpty) {
|
||||
List<dynamic> decodeList = jsonDecode(option);
|
||||
if (decodeList.length == _isVisible.length) {
|
||||
for (int i = 0; i < _isVisible.length; i++) {
|
||||
if (decodeList[i] is bool) {
|
||||
_isVisible[i] = decodeList[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("failed to get peer tab visible list:$e");
|
||||
}
|
||||
// init currentTab
|
||||
_currentTab =
|
||||
int.tryParse(bind.getLocalFlutterOption(k: 'peer-tab-index')) ?? 0;
|
||||
if (_currentTab < 0 || _currentTab >= tabNames.length) {
|
||||
_currentTab = 0;
|
||||
}
|
||||
_trySetCurrentTabToFirstVisible();
|
||||
}
|
||||
|
||||
setCurrentTab(int index) {
|
||||
@ -158,4 +179,31 @@ class PeerTabModel with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setTabVisible(int index, bool visible) {
|
||||
if (index >= 0 && index < _isVisible.length) {
|
||||
if (_isVisible[index] != visible) {
|
||||
_isVisible[index] = visible;
|
||||
if (index == _currentTab && !visible) {
|
||||
_trySetCurrentTabToFirstVisible();
|
||||
} else if (visible && visibleIndexs.length == 1) {
|
||||
_currentTab = index;
|
||||
}
|
||||
try {
|
||||
bind.setLocalFlutterOption(
|
||||
k: 'peer-tab-visible', v: jsonEncode(_isVisible));
|
||||
} catch (_) {}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_trySetCurrentTabToFirstVisible() {
|
||||
if (!_isVisible[_currentTab]) {
|
||||
int firstVisible = _isVisible.indexWhere((e) => e);
|
||||
if (firstVisible >= 0) {
|
||||
_currentTab = firstVisible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("ID Server", "ID serveris"),
|
||||
("Relay Server", "Releja serveris"),
|
||||
("API Server", "API serveris"),
|
||||
("Key", "Atslēga"),
|
||||
("invalid_http", "jāsākas ar http:// vai https://"),
|
||||
("Invalid IP", "Nederīga IP"),
|
||||
("Invalid format", "Nederīgs formāts"),
|
||||
@ -297,7 +296,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("This file exists, skip or overwrite this file?", "Šis fails pastāv, izlaist vai pārrakstīt šo failu?"),
|
||||
("Quit", "Iziet"),
|
||||
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
|
||||
("doc_fix_wayland", "https://rustdesk.com/docs/en/manual/linux/#x11-required"),
|
||||
("Help", "Palīdzība"),
|
||||
("Failed", "Neizdevās"),
|
||||
("Succeeded", "Izdevās"),
|
||||
@ -481,7 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Me", "Es"),
|
||||
("identical_file_tip", "Šis fails ir identisks sesijas failam."),
|
||||
("show_monitors_tip", "Rādīt monitorus rīkjoslā"),
|
||||
("enter_rustdesk_passwd_tip", "Ievadiet RustDesk paroli"),
|
||||
("View Mode", "Skatīšanas režīms"),
|
||||
("login_linux_tip", "Jums ir jāpiesakās attālajā Linux kontā, lai iespējotu X darbvirsmas sesiju"),
|
||||
("verify_rustdesk_password_tip", "Pārbaudīt RustDesk paroli"),
|
||||
|
Loading…
Reference in New Issue
Block a user