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/material.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_home_page.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/peer_widget.dart';
|
||||||
|
import 'package:flutter_hbb/desktop/widgets/peercard_widget.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -976,6 +977,9 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage>
|
|||||||
// hard code for now
|
// hard code for now
|
||||||
void _handleTabSelection() {
|
void _handleTabSelection() {
|
||||||
if (_tabController.indexIsChanging) {
|
if (_tabController.indexIsChanging) {
|
||||||
|
// reset search text
|
||||||
|
peerSearchText.value = "";
|
||||||
|
peerSearchTextController.clear();
|
||||||
_tabIndex.value = _tabController.index;
|
_tabIndex.value = _tabController.index;
|
||||||
switch (_tabController.index) {
|
switch (_tabController.index) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -1014,7 +1018,13 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage>
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_createTabBar(context),
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: _createTabBar(context)),
|
||||||
|
_createSearchBar(context),
|
||||||
|
_createPeerViewTypeSwitch(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
_createTabBarView(),
|
_createTabBarView(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -1054,4 +1064,72 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage>
|
|||||||
controller: _tabController, children: super.widget.children)
|
controller: _tabController, children: super.widget.children)
|
||||||
.paddingSymmetric(horizontal: 12.0, vertical: 4.0));
|
.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 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:visibility_detector/visibility_detector.dart';
|
import 'package:visibility_detector/visibility_detector.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
|
import '../../common.dart';
|
||||||
import '../../models/peer_model.dart';
|
import '../../models/peer_model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../../common.dart';
|
|
||||||
import 'peercard_widget.dart';
|
import 'peercard_widget.dart';
|
||||||
|
|
||||||
typedef OffstageFunc = bool Function(Peer peer);
|
typedef OffstageFunc = bool Function(Peer peer);
|
||||||
typedef PeerCardWidgetFunc = Widget 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 {
|
class _PeerWidget extends StatefulWidget {
|
||||||
late final _peers;
|
late final _peers;
|
||||||
late final OffstageFunc _offstageFunc;
|
late final OffstageFunc _offstageFunc;
|
||||||
late final PeerCardWidgetFunc _peerCardWidgetFunc;
|
late final PeerCardWidgetFunc _peerCardWidgetFunc;
|
||||||
|
|
||||||
_PeerWidget(Peers peers, OffstageFunc offstageFunc,
|
_PeerWidget(Peers peers, OffstageFunc offstageFunc,
|
||||||
PeerCardWidgetFunc peerCardWidgetFunc,
|
PeerCardWidgetFunc peerCardWidgetFunc,
|
||||||
{Key? key})
|
{Key? key})
|
||||||
@ -71,20 +78,32 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final space = 8.0;
|
final space = 12.0;
|
||||||
return ChangeNotifierProvider<Peers>(
|
return ChangeNotifierProvider<Peers>(
|
||||||
create: (context) => super.widget._peers,
|
create: (context) => super.widget._peers,
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Consumer<Peers>(
|
child: Consumer<Peers>(
|
||||||
builder: (context, peers, child) => Wrap(
|
builder: (context, peers, child) => peers.peers.isEmpty
|
||||||
children: () {
|
? Center(
|
||||||
|
child: Text(translate("Empty")),
|
||||||
|
)
|
||||||
|
: SingleChildScrollView(
|
||||||
|
child: ObxValue<RxString>((searchText) {
|
||||||
final cards = <Widget>[];
|
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(
|
cards.add(Offstage(
|
||||||
offstage: super.widget._offstageFunc(peer),
|
offstage: super.widget._offstageFunc(peer),
|
||||||
child: Container(
|
child: Obx(
|
||||||
|
() => Container(
|
||||||
width: 225,
|
width: 225,
|
||||||
height: 150,
|
height: peerCardUiType.value == PeerUiType.grid
|
||||||
|
? 150
|
||||||
|
: 50,
|
||||||
child: VisibilityDetector(
|
child: VisibilityDetector(
|
||||||
key: Key('${peer.id}'),
|
key: Key('${peer.id}'),
|
||||||
onVisibilityChanged: (info) {
|
onVisibilityChanged: (info) {
|
||||||
@ -98,12 +117,13 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
|
|||||||
},
|
},
|
||||||
child: super.widget._peerCardWidgetFunc(peer),
|
child: super.widget._peerCardWidgetFunc(peer),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
)));
|
)));
|
||||||
});
|
});
|
||||||
return cards;
|
return Wrap(
|
||||||
}(),
|
children: cards, spacing: space, runSpacing: space);
|
||||||
spacing: space,
|
}, peerSearchText),
|
||||||
runSpacing: space))),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +182,9 @@ class RecentPeerWidget extends BasePeerWidget {
|
|||||||
super._name = "recent peer";
|
super._name = "recent peer";
|
||||||
super._loadEvent = "load_recent_peers";
|
super._loadEvent = "load_recent_peers";
|
||||||
super._offstageFunc = (Peer _peer) => false;
|
super._offstageFunc = (Peer _peer) => false;
|
||||||
super._peerCardWidgetFunc = (Peer peer) => RecentPeerCard(peer: peer);
|
super._peerCardWidgetFunc = (Peer peer) => RecentPeerCard(
|
||||||
|
peer: peer,
|
||||||
|
);
|
||||||
super._initPeers = [];
|
super._initPeers = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,13 +5,17 @@ import 'package:get/get.dart';
|
|||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/platform_model.dart';
|
|
||||||
import '../../models/peer_model.dart';
|
import '../../models/peer_model.dart';
|
||||||
|
import '../../models/platform_model.dart';
|
||||||
|
|
||||||
typedef PopupMenuItemsFunc = Future<List<PopupMenuItem<String>>> Function();
|
typedef PopupMenuItemsFunc = Future<List<PopupMenuItem<String>>> Function();
|
||||||
|
|
||||||
enum PeerType { recent, fav, discovered, ab }
|
enum PeerType { recent, fav, discovered, ab }
|
||||||
|
|
||||||
|
enum PeerUiType { grid, list }
|
||||||
|
|
||||||
|
final peerCardUiType = PeerUiType.grid.obs;
|
||||||
|
|
||||||
class _PeerCard extends StatefulWidget {
|
class _PeerCard extends StatefulWidget {
|
||||||
final Peer peer;
|
final Peer peer;
|
||||||
final PopupMenuItemsFunc popupMenuItemsFunc;
|
final PopupMenuItemsFunc popupMenuItemsFunc;
|
||||||
@ -39,29 +43,134 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
final peer = super.widget.peer;
|
final peer = super.widget.peer;
|
||||||
var deco = Rx<BoxDecoration?>(BoxDecoration(
|
var deco = Rx<BoxDecoration?>(BoxDecoration(
|
||||||
border: Border.all(color: Colors.transparent, width: 1.0),
|
border: Border.all(color: Colors.transparent, width: 1.0),
|
||||||
borderRadius: BorderRadius.circular(20)));
|
borderRadius: peerCardUiType.value == PeerUiType.grid
|
||||||
return Card(
|
? BorderRadius.circular(20)
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
: null));
|
||||||
child: MouseRegion(
|
return MouseRegion(
|
||||||
onEnter: (evt) {
|
onEnter: (evt) {
|
||||||
deco.value = BoxDecoration(
|
deco.value = BoxDecoration(
|
||||||
border: Border.all(color: Colors.blue, width: 1.0),
|
border: Border.all(color: Colors.blue, width: 1.0),
|
||||||
borderRadius: BorderRadius.circular(20));
|
borderRadius: peerCardUiType.value == PeerUiType.grid
|
||||||
|
? BorderRadius.circular(20)
|
||||||
|
: null);
|
||||||
},
|
},
|
||||||
onExit: (evt) {
|
onExit: (evt) {
|
||||||
deco.value = BoxDecoration(
|
deco.value = BoxDecoration(
|
||||||
border: Border.all(color: Colors.transparent, width: 1.0),
|
border: Border.all(color: Colors.transparent, width: 1.0),
|
||||||
borderRadius: BorderRadius.circular(20));
|
borderRadius: peerCardUiType.value == PeerUiType.grid
|
||||||
|
? BorderRadius.circular(20)
|
||||||
|
: null);
|
||||||
},
|
},
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onDoubleTap: () => _connect(peer.id),
|
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(
|
Widget _buildPeerTile(
|
||||||
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||||
|
final greyStyle = TextStyle(fontSize: 12, color: Colors.grey);
|
||||||
return Obx(
|
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(
|
() => Container(
|
||||||
decoration: deco.value,
|
decoration: deco.value,
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -141,8 +250,9 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
padding: EdgeInsets.fromLTRB(0, 4, 8, 4),
|
padding: EdgeInsets.fromLTRB(0, 4, 8, 4),
|
||||||
child: CircleAvatar(
|
child: CircleAvatar(
|
||||||
radius: 5,
|
radius: 5,
|
||||||
backgroundColor:
|
backgroundColor: peer.online
|
||||||
peer.online ? Colors.green : Colors.yellow)),
|
? Colors.green
|
||||||
|
: Colors.yellow)),
|
||||||
Text('${peer.id}')
|
Text('${peer.id}')
|
||||||
]),
|
]),
|
||||||
InkWell(
|
InkWell(
|
||||||
@ -160,6 +270,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1230,8 +1230,8 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: f1d69e5d0531af947373ec26ae22808f08b1aac6
|
ref: "799ef079e87938c3f4340591b4330c2598f38bb9"
|
||||||
resolved-ref: f1d69e5d0531af947373ec26ae22808f08b1aac6
|
resolved-ref: "799ef079e87938c3f4340591b4330c2598f38bb9"
|
||||||
url: "https://github.com/Kingtous/rustdesk_window_manager"
|
url: "https://github.com/Kingtous/rustdesk_window_manager"
|
||||||
source: git
|
source: git
|
||||||
version: "0.2.5"
|
version: "0.2.5"
|
||||||
|
@ -61,7 +61,7 @@ dependencies:
|
|||||||
window_manager:
|
window_manager:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Kingtous/rustdesk_window_manager
|
url: https://github.com/Kingtous/rustdesk_window_manager
|
||||||
ref: f1d69e5d0531af947373ec26ae22808f08b1aac6
|
ref: 799ef079e87938c3f4340591b4330c2598f38bb9
|
||||||
desktop_multi_window:
|
desktop_multi_window:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Kingtous/rustdesk_desktop_multi_window
|
url: https://github.com/Kingtous/rustdesk_desktop_multi_window
|
||||||
|
Loading…
x
Reference in New Issue
Block a user