Merge pull request #1336 from Kingtous/flutter_desktop
feat: find id and grid/tile view for peers
This commit is contained in:
commit
75b04b9af1
@ -5,6 +5,7 @@ import 'package:contextmenu/contextmenu.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/peer_widget.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/peercard_widget.dart';
|
||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@ -976,6 +977,9 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage>
|
||||
// hard code for now
|
||||
void _handleTabSelection() {
|
||||
if (_tabController.indexIsChanging) {
|
||||
// reset search text
|
||||
peerSearchText.value = "";
|
||||
peerSearchTextController.clear();
|
||||
_tabIndex.value = _tabController.index;
|
||||
switch (_tabController.index) {
|
||||
case 0:
|
||||
@ -1014,7 +1018,13 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage>
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_createTabBar(context),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _createTabBar(context)),
|
||||
_createSearchBar(context),
|
||||
_createPeerViewTypeSwitch(context),
|
||||
],
|
||||
),
|
||||
_createTabBarView(),
|
||||
],
|
||||
);
|
||||
@ -1054,4 +1064,72 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage>
|
||||
controller: _tabController, children: super.widget.children)
|
||||
.paddingSymmetric(horizontal: 12.0, vertical: 4.0));
|
||||
}
|
||||
|
||||
_createSearchBar(BuildContext context) {
|
||||
return Container(
|
||||
width: 175,
|
||||
height: 30,
|
||||
margin: EdgeInsets.only(right: 16),
|
||||
decoration: BoxDecoration(color: Colors.white),
|
||||
child: Obx(
|
||||
() => TextField(
|
||||
controller: peerSearchTextController,
|
||||
onChanged: (searchText) {
|
||||
peerSearchText.value = searchText;
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Icon(
|
||||
Icons.search,
|
||||
size: 20,
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hintText: translate("Search ID"),
|
||||
hintStyle: TextStyle(fontSize: 14),
|
||||
border: OutlineInputBorder(),
|
||||
isDense: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_createPeerViewTypeSwitch(BuildContext context) {
|
||||
final activeDeco = BoxDecoration(color: Colors.white);
|
||||
return Row(
|
||||
children: [
|
||||
Obx(
|
||||
() => Container(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
decoration:
|
||||
peerCardUiType.value == PeerUiType.grid ? activeDeco : null,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
peerCardUiType.value = PeerUiType.grid;
|
||||
},
|
||||
child: Icon(
|
||||
Icons.grid_view_rounded,
|
||||
size: 20,
|
||||
color: Colors.black54,
|
||||
)),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => Container(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
decoration:
|
||||
peerCardUiType.value == PeerUiType.list ? activeDeco : null,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
peerCardUiType.value = PeerUiType.list;
|
||||
},
|
||||
child: Icon(
|
||||
Icons.list,
|
||||
size: 24,
|
||||
color: Colors.black54,
|
||||
)),
|
||||
),
|
||||
),
|
||||
],
|
||||
).paddingOnly(right: 16.0);
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,30 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:visibility_detector/visibility_detector.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import '../../common.dart';
|
||||
import '../../models/peer_model.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
import '../../common.dart';
|
||||
import 'peercard_widget.dart';
|
||||
|
||||
typedef OffstageFunc = bool Function(Peer peer);
|
||||
typedef PeerCardWidgetFunc = Widget Function(Peer peer);
|
||||
|
||||
/// for peer search text, global obs value
|
||||
final peerSearchText = "".obs;
|
||||
final peerSearchTextController =
|
||||
TextEditingController(text: peerSearchText.value);
|
||||
|
||||
class _PeerWidget extends StatefulWidget {
|
||||
late final _peers;
|
||||
late final OffstageFunc _offstageFunc;
|
||||
late final PeerCardWidgetFunc _peerCardWidgetFunc;
|
||||
|
||||
_PeerWidget(Peers peers, OffstageFunc offstageFunc,
|
||||
PeerCardWidgetFunc peerCardWidgetFunc,
|
||||
{Key? key})
|
||||
@ -71,20 +78,32 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final space = 8.0;
|
||||
final space = 12.0;
|
||||
return ChangeNotifierProvider<Peers>(
|
||||
create: (context) => super.widget._peers,
|
||||
child: SingleChildScrollView(
|
||||
child: Consumer<Peers>(
|
||||
builder: (context, peers, child) => Wrap(
|
||||
children: () {
|
||||
builder: (context, peers, child) => peers.peers.isEmpty
|
||||
? Center(
|
||||
child: Text(translate("Empty")),
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
child: ObxValue<RxString>((searchText) {
|
||||
final cards = <Widget>[];
|
||||
peers.peers.forEach((peer) {
|
||||
peers.peers.where((peer) {
|
||||
if (searchText.isEmpty) {
|
||||
return true;
|
||||
} else {
|
||||
return peer.id.contains(peerSearchText.value);
|
||||
}
|
||||
}).forEach((peer) {
|
||||
cards.add(Offstage(
|
||||
offstage: super.widget._offstageFunc(peer),
|
||||
child: Container(
|
||||
child: Obx(
|
||||
() => Container(
|
||||
width: 225,
|
||||
height: 150,
|
||||
height: peerCardUiType.value == PeerUiType.grid
|
||||
? 150
|
||||
: 50,
|
||||
child: VisibilityDetector(
|
||||
key: Key('${peer.id}'),
|
||||
onVisibilityChanged: (info) {
|
||||
@ -98,12 +117,13 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
|
||||
},
|
||||
child: super.widget._peerCardWidgetFunc(peer),
|
||||
),
|
||||
),
|
||||
)));
|
||||
});
|
||||
return cards;
|
||||
}(),
|
||||
spacing: space,
|
||||
runSpacing: space))),
|
||||
return Wrap(
|
||||
children: cards, spacing: space, runSpacing: space);
|
||||
}, peerSearchText),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -162,7 +182,9 @@ class RecentPeerWidget extends BasePeerWidget {
|
||||
super._name = "recent peer";
|
||||
super._loadEvent = "load_recent_peers";
|
||||
super._offstageFunc = (Peer _peer) => false;
|
||||
super._peerCardWidgetFunc = (Peer peer) => RecentPeerCard(peer: peer);
|
||||
super._peerCardWidgetFunc = (Peer peer) => RecentPeerCard(
|
||||
peer: peer,
|
||||
);
|
||||
super._initPeers = [];
|
||||
}
|
||||
|
||||
|
@ -5,13 +5,17 @@ import 'package:get/get.dart';
|
||||
|
||||
import '../../common.dart';
|
||||
import '../../models/model.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
import '../../models/peer_model.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
|
||||
typedef PopupMenuItemsFunc = Future<List<PopupMenuItem<String>>> Function();
|
||||
|
||||
enum PeerType { recent, fav, discovered, ab }
|
||||
|
||||
enum PeerUiType { grid, list }
|
||||
|
||||
final peerCardUiType = PeerUiType.grid.obs;
|
||||
|
||||
class _PeerCard extends StatefulWidget {
|
||||
final Peer peer;
|
||||
final PopupMenuItemsFunc popupMenuItemsFunc;
|
||||
@ -39,29 +43,134 @@ class _PeerCardState extends State<_PeerCard>
|
||||
final peer = super.widget.peer;
|
||||
var deco = Rx<BoxDecoration?>(BoxDecoration(
|
||||
border: Border.all(color: Colors.transparent, width: 1.0),
|
||||
borderRadius: BorderRadius.circular(20)));
|
||||
return Card(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
child: MouseRegion(
|
||||
borderRadius: peerCardUiType.value == PeerUiType.grid
|
||||
? BorderRadius.circular(20)
|
||||
: null));
|
||||
return MouseRegion(
|
||||
onEnter: (evt) {
|
||||
deco.value = BoxDecoration(
|
||||
border: Border.all(color: Colors.blue, width: 1.0),
|
||||
borderRadius: BorderRadius.circular(20));
|
||||
borderRadius: peerCardUiType.value == PeerUiType.grid
|
||||
? BorderRadius.circular(20)
|
||||
: null);
|
||||
},
|
||||
onExit: (evt) {
|
||||
deco.value = BoxDecoration(
|
||||
border: Border.all(color: Colors.transparent, width: 1.0),
|
||||
borderRadius: BorderRadius.circular(20));
|
||||
borderRadius: peerCardUiType.value == PeerUiType.grid
|
||||
? BorderRadius.circular(20)
|
||||
: null);
|
||||
},
|
||||
child: GestureDetector(
|
||||
onDoubleTap: () => _connect(peer.id),
|
||||
child: _buildPeerTile(context, peer, deco)),
|
||||
));
|
||||
child: Obx(() => peerCardUiType.value == PeerUiType.grid
|
||||
? _buildPeerCard(context, peer, deco)
|
||||
: _buildPeerTile(context, peer, deco))),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPeerTile(
|
||||
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||
final greyStyle = TextStyle(fontSize: 12, color: Colors.grey);
|
||||
return Obx(
|
||||
() => Container(
|
||||
decoration: deco.value,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: str2color('${peer.id}${peer.platform}', 0x7f),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: _getPlatformImage('${peer.platform}').paddingAll(8.0),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: Colors.white),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Row(children: [
|
||||
Text(
|
||||
'${peer.id}',
|
||||
style: TextStyle(fontWeight: FontWeight.w400),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(4, 4, 8, 4),
|
||||
child: CircleAvatar(
|
||||
radius: 5,
|
||||
backgroundColor: peer.online
|
||||
? Colors.green
|
||||
: Colors.yellow)),
|
||||
]),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: FutureBuilder<String>(
|
||||
future: bind.mainGetPeerOption(
|
||||
id: peer.id, key: 'alias'),
|
||||
builder: (_, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final name = snapshot.data!.isEmpty
|
||||
? '${peer.username}@${peer.hostname}'
|
||||
: snapshot.data!;
|
||||
return Tooltip(
|
||||
message: name,
|
||||
child: Text(
|
||||
name,
|
||||
style: greyStyle,
|
||||
textAlign: TextAlign.start,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// alias has not arrived
|
||||
return Text(
|
||||
'${peer.username}@${peer.hostname}',
|
||||
style: greyStyle,
|
||||
textAlign: TextAlign.start,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
child: Icon(Icons.more_vert),
|
||||
onTapDown: (e) {
|
||||
final x = e.globalPosition.dx;
|
||||
final y = e.globalPosition.dy;
|
||||
_menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
||||
},
|
||||
onTap: () {
|
||||
_showPeerMenu(context, peer.id);
|
||||
}),
|
||||
],
|
||||
).paddingSymmetric(horizontal: 8.0),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPeerCard(
|
||||
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||
return Card(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
child: GestureDetector(
|
||||
onDoubleTap: () => _connect(peer.id),
|
||||
child: Obx(
|
||||
() => Container(
|
||||
decoration: deco.value,
|
||||
child: Column(
|
||||
@ -141,8 +250,9 @@ class _PeerCardState extends State<_PeerCard>
|
||||
padding: EdgeInsets.fromLTRB(0, 4, 8, 4),
|
||||
child: CircleAvatar(
|
||||
radius: 5,
|
||||
backgroundColor:
|
||||
peer.online ? Colors.green : Colors.yellow)),
|
||||
backgroundColor: peer.online
|
||||
? Colors.green
|
||||
: Colors.yellow)),
|
||||
Text('${peer.id}')
|
||||
]),
|
||||
InkWell(
|
||||
@ -160,6 +270,7 @@ class _PeerCardState extends State<_PeerCard>
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1230,8 +1230,8 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: f1d69e5d0531af947373ec26ae22808f08b1aac6
|
||||
resolved-ref: f1d69e5d0531af947373ec26ae22808f08b1aac6
|
||||
ref: "799ef079e87938c3f4340591b4330c2598f38bb9"
|
||||
resolved-ref: "799ef079e87938c3f4340591b4330c2598f38bb9"
|
||||
url: "https://github.com/Kingtous/rustdesk_window_manager"
|
||||
source: git
|
||||
version: "0.2.5"
|
||||
|
@ -61,7 +61,7 @@ dependencies:
|
||||
window_manager:
|
||||
git:
|
||||
url: https://github.com/Kingtous/rustdesk_window_manager
|
||||
ref: f1d69e5d0531af947373ec26ae22808f08b1aac6
|
||||
ref: 799ef079e87938c3f4340591b4330c2598f38bb9
|
||||
desktop_multi_window:
|
||||
git:
|
||||
url: https://github.com/Kingtous/rustdesk_desktop_multi_window
|
||||
|
Loading…
x
Reference in New Issue
Block a user