| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  | import 'dart:ui' as ui; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import 'package:bot_toast/bot_toast.dart'; | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  | import 'package:flutter/gestures.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  | import 'package:flutter_hbb/common/widgets/address_book.dart'; | 
					
						
							| 
									
										
										
										
											2022-12-11 21:40:35 +08:00
										 |  |  | import 'package:flutter_hbb/common/widgets/my_group.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-22 15:35:46 +08:00
										 |  |  | import 'package:flutter_hbb/common/widgets/peers_view.dart'; | 
					
						
							|  |  |  | import 'package:flutter_hbb/common/widgets/peer_card.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | import 'package:flutter_hbb/consts.dart'; | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  | 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; | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  | import 'package:flutter_hbb/models/peer_tab_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | import 'package:get/get.dart'; | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  | import 'package:get/get_rx/src/rx_workers/utils/debouncer.dart'; | 
					
						
							|  |  |  | import 'package:provider/provider.dart'; | 
					
						
							|  |  |  | import 'package:visibility_detector/visibility_detector.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import '../../common.dart'; | 
					
						
							|  |  |  | import '../../models/platform_model.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PeerTabPage extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  |   const PeerTabPage({Key? key}) : super(key: key); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   State<PeerTabPage> createState() => _PeerTabPageState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  | class _TabEntry { | 
					
						
							|  |  |  |   final Widget widget; | 
					
						
							|  |  |  |   final Function() load; | 
					
						
							| 
									
										
										
										
											2022-12-11 21:40:35 +08:00
										 |  |  |   _TabEntry(this.widget, this.load); | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-05 09:33:01 +08:00
										 |  |  | EdgeInsets? _menuPadding() { | 
					
						
							|  |  |  |   return isDesktop ? kDesktopMenuPadding : null; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | class _PeerTabPageState extends State<PeerTabPage> | 
					
						
							|  |  |  |     with SingleTickerProviderStateMixin { | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  |   final List<_TabEntry> entries = [ | 
					
						
							|  |  |  |     _TabEntry( | 
					
						
							|  |  |  |         RecentPeersView( | 
					
						
							| 
									
										
										
										
											2022-12-05 09:33:01 +08:00
										 |  |  |           menuPadding: _menuPadding(), | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  |         ), | 
					
						
							|  |  |  |         bind.mainLoadRecentPeers), | 
					
						
							|  |  |  |     _TabEntry( | 
					
						
							|  |  |  |         FavoritePeersView( | 
					
						
							| 
									
										
										
										
											2022-12-05 09:33:01 +08:00
										 |  |  |           menuPadding: _menuPadding(), | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  |         ), | 
					
						
							|  |  |  |         bind.mainLoadFavPeers), | 
					
						
							|  |  |  |     _TabEntry( | 
					
						
							|  |  |  |         DiscoveredPeersView( | 
					
						
							| 
									
										
										
										
											2022-12-05 09:33:01 +08:00
										 |  |  |           menuPadding: _menuPadding(), | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  |         ), | 
					
						
							|  |  |  |         bind.mainDiscover), | 
					
						
							|  |  |  |     _TabEntry( | 
					
						
							| 
									
										
										
										
											2022-12-05 09:33:01 +08:00
										 |  |  |         AddressBook( | 
					
						
							|  |  |  |           menuPadding: _menuPadding(), | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  |         ), | 
					
						
							|  |  |  |         () => {}), | 
					
						
							| 
									
										
										
										
											2022-12-11 21:40:35 +08:00
										 |  |  |     _TabEntry( | 
					
						
							|  |  |  |         MyGroup( | 
					
						
							|  |  |  |           menuPadding: _menuPadding(), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         () => {}), | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  |   ]; | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |   final _scrollDebounce = Debouncer(delay: Duration(milliseconds: 50)); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |     final uiType = bind.getLocalFlutterConfig(k: 'peer-card-ui-type'); | 
					
						
							| 
									
										
										
										
											2022-11-18 10:19:55 +08:00
										 |  |  |     if (uiType != '') { | 
					
						
							|  |  |  |       peerCardUiType.value = int.parse(uiType) == PeerUiType.list.index | 
					
						
							|  |  |  |           ? PeerUiType.list | 
					
						
							|  |  |  |           : PeerUiType.grid; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  |     super.initState(); | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-11 21:40:35 +08:00
										 |  |  |   Future<void> handleTabSelection(int tabIndex) async { | 
					
						
							|  |  |  |     if (tabIndex < entries.length) { | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |       gFFI.peerTabModel.setCurrentTab(tabIndex); | 
					
						
							| 
									
										
										
										
											2022-12-11 21:40:35 +08:00
										 |  |  |       entries[tabIndex].load(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return Column( | 
					
						
							|  |  |  |       textBaseline: TextBaseline.ideographic, | 
					
						
							|  |  |  |       crossAxisAlignment: CrossAxisAlignment.start, | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         SizedBox( | 
					
						
							|  |  |  |           height: 28, | 
					
						
							|  |  |  |           child: Container( | 
					
						
							| 
									
										
										
										
											2022-09-21 17:16:09 +08:00
										 |  |  |               padding: isDesktop ? null : EdgeInsets.symmetric(horizontal: 2), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |               constraints: isDesktop ? null : kMobilePageConstraints, | 
					
						
							|  |  |  |               child: Row( | 
					
						
							|  |  |  |                 crossAxisAlignment: CrossAxisAlignment.center, | 
					
						
							|  |  |  |                 children: [ | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  |                   Expanded( | 
					
						
							|  |  |  |                       child: visibleContextMenuListener( | 
					
						
							|  |  |  |                           _createSwitchBar(context))), | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |                   buildScrollJumper(), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                   const PeerSearchBar(), | 
					
						
							|  |  |  |                   Offstage( | 
					
						
							|  |  |  |                       offstage: !isDesktop, | 
					
						
							|  |  |  |                       child: _createPeerViewTypeSwitch(context) | 
					
						
							|  |  |  |                           .marginOnly(left: 13)), | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |               )), | 
					
						
							|  |  |  |         ), | 
					
						
							| 
									
										
										
										
											2022-09-28 11:20:57 +08:00
										 |  |  |         _createPeersView(), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |       ], | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-28 11:20:57 +08:00
										 |  |  |   Widget _createSwitchBar(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |     final model = Provider.of<PeerTabModel>(context); | 
					
						
							|  |  |  |     int indexCounter = -1; | 
					
						
							|  |  |  |     return ReorderableListView( | 
					
						
							|  |  |  |         buildDefaultDragHandles: false, | 
					
						
							|  |  |  |         onReorder: (oldIndex, newIndex) { | 
					
						
							|  |  |  |           model.onReorder(oldIndex, newIndex); | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         scrollDirection: Axis.horizontal, | 
					
						
							|  |  |  |         physics: NeverScrollableScrollPhysics(), | 
					
						
							|  |  |  |         scrollController: model.sc, | 
					
						
							|  |  |  |         children: model.visibleOrderedTabs.map((t) { | 
					
						
							|  |  |  |           indexCounter++; | 
					
						
							|  |  |  |           return ReorderableDragStartListener( | 
					
						
							|  |  |  |             key: ValueKey(t), | 
					
						
							|  |  |  |             index: indexCounter, | 
					
						
							|  |  |  |             child: VisibilityDetector( | 
					
						
							| 
									
										
										
										
											2023-02-02 16:45:29 +08:00
										 |  |  |               key: ValueKey(t), | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |               onVisibilityChanged: (info) { | 
					
						
							|  |  |  |                 final id = (info.key as ValueKey).value; | 
					
						
							|  |  |  |                 model.setTabFullyVisible(id, info.visibleFraction > 0.99); | 
					
						
							|  |  |  |               }, | 
					
						
							|  |  |  |               child: Listener( | 
					
						
							|  |  |  |                 // handle mouse wheel
 | 
					
						
							|  |  |  |                 onPointerSignal: (e) { | 
					
						
							|  |  |  |                   if (e is PointerScrollEvent) { | 
					
						
							|  |  |  |                     if (!model.sc.canScroll) return; | 
					
						
							|  |  |  |                     _scrollDebounce.call(() { | 
					
						
							|  |  |  |                       model.sc.animateTo(model.sc.offset + e.scrollDelta.dy, | 
					
						
							|  |  |  |                           duration: Duration(milliseconds: 200), | 
					
						
							|  |  |  |                           curve: Curves.ease); | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                   } | 
					
						
							| 
									
										
										
										
											2023-02-02 16:45:29 +08:00
										 |  |  |                 }, | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |                 child: InkWell( | 
					
						
							|  |  |  |                   child: Container( | 
					
						
							|  |  |  |                       padding: const EdgeInsets.symmetric(horizontal: 8), | 
					
						
							|  |  |  |                       decoration: BoxDecoration( | 
					
						
							|  |  |  |                         color: model.currentTab == t | 
					
						
							|  |  |  |                             ? Theme.of(context).backgroundColor | 
					
						
							|  |  |  |                             : null, | 
					
						
							|  |  |  |                         borderRadius: BorderRadius.circular(isDesktop ? 2 : 6), | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                       child: Align( | 
					
						
							|  |  |  |                         alignment: Alignment.center, | 
					
						
							|  |  |  |                         child: Text( | 
					
						
							|  |  |  |                           model.translatedTabname(t), | 
					
						
							|  |  |  |                           textAlign: TextAlign.center, | 
					
						
							|  |  |  |                           style: TextStyle( | 
					
						
							|  |  |  |                               height: 1, | 
					
						
							|  |  |  |                               fontSize: 14, | 
					
						
							|  |  |  |                               color: model.currentTab == t | 
					
						
							|  |  |  |                                   ? MyTheme.tabbar(context).selectedTextColor | 
					
						
							|  |  |  |                                   : MyTheme.tabbar(context).unSelectedTextColor | 
					
						
							|  |  |  |                                 ?..withOpacity(0.5)), | 
					
						
							|  |  |  |                         ), | 
					
						
							|  |  |  |                       )), | 
					
						
							|  |  |  |                   onTap: () async { | 
					
						
							|  |  |  |                     await handleTabSelection(t); | 
					
						
							|  |  |  |                     await bind.setLocalFlutterConfig( | 
					
						
							|  |  |  |                         k: 'peer-tab-index', v: t.toString()); | 
					
						
							|  |  |  |                   }, | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2023-02-02 16:45:29 +08:00
										 |  |  |               ), | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |             ), | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         }).toList()); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |   Widget buildScrollJumper() { | 
					
						
							|  |  |  |     final model = Provider.of<PeerTabModel>(context); | 
					
						
							|  |  |  |     return Offstage( | 
					
						
							|  |  |  |         offstage: !model.showScrollBtn, | 
					
						
							|  |  |  |         child: Row( | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             GestureDetector( | 
					
						
							|  |  |  |                 child: Icon(Icons.arrow_left, | 
					
						
							|  |  |  |                     size: 22, | 
					
						
							|  |  |  |                     color: model.leftFullyVisible | 
					
						
							|  |  |  |                         ? Theme.of(context).disabledColor | 
					
						
							|  |  |  |                         : null), | 
					
						
							|  |  |  |                 onTap: model.sc.backward), | 
					
						
							|  |  |  |             GestureDetector( | 
					
						
							|  |  |  |                 child: Icon(Icons.arrow_right, | 
					
						
							|  |  |  |                     size: 22, | 
					
						
							|  |  |  |                     color: model.rightFullyVisible | 
					
						
							|  |  |  |                         ? Theme.of(context).disabledColor | 
					
						
							|  |  |  |                         : null), | 
					
						
							|  |  |  |                 onTap: model.sc.forward) | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         )); | 
					
						
							| 
									
										
										
										
											2022-12-17 12:28:11 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-28 11:20:57 +08:00
										 |  |  |   Widget _createPeersView() { | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |     final model = Provider.of<PeerTabModel>(context); | 
					
						
							|  |  |  |     Widget child; | 
					
						
							|  |  |  |     if (model.visibleOrderedTabs.isEmpty) { | 
					
						
							|  |  |  |       child = visibleContextMenuListener(Center( | 
					
						
							|  |  |  |         child: Text(translate('Right click to select tabs')), | 
					
						
							|  |  |  |       )); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       if (model.visibleOrderedTabs.contains(model.currentTab)) { | 
					
						
							|  |  |  |         child = entries[model.currentTab].widget; | 
					
						
							| 
									
										
										
										
											2022-12-11 21:40:35 +08:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |         model.setCurrentTab(model.visibleOrderedTabs[0]); | 
					
						
							|  |  |  |         child = entries[0].widget; | 
					
						
							| 
									
										
										
										
											2022-12-11 21:40:35 +08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |     return Expanded( | 
					
						
							|  |  |  |         child: child.marginSymmetric(vertical: isDesktop ? 12.0 : 6.0)); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _createPeerViewTypeSwitch(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |     final textColor = Theme.of(context).textTheme.titleLarge?.color; | 
					
						
							|  |  |  |     final activeDeco = BoxDecoration(color: Theme.of(context).backgroundColor); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |     return Row( | 
					
						
							| 
									
										
										
										
											2022-09-21 14:56:01 +08:00
										 |  |  |       children: [PeerUiType.grid, PeerUiType.list] | 
					
						
							|  |  |  |           .map((type) => Obx( | 
					
						
							|  |  |  |                 () => Container( | 
					
						
							|  |  |  |                   padding: EdgeInsets.all(4.0), | 
					
						
							|  |  |  |                   decoration: peerCardUiType.value == type ? activeDeco : null, | 
					
						
							|  |  |  |                   child: InkWell( | 
					
						
							|  |  |  |                       onTap: () async { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |                         await bind.setLocalFlutterConfig( | 
					
						
							|  |  |  |                             k: 'peer-card-ui-type', v: type.index.toString()); | 
					
						
							| 
									
										
										
										
											2022-09-21 14:56:01 +08:00
										 |  |  |                         peerCardUiType.value = type; | 
					
						
							|  |  |  |                       }, | 
					
						
							|  |  |  |                       child: Icon( | 
					
						
							|  |  |  |                         type == PeerUiType.grid | 
					
						
							|  |  |  |                             ? Icons.grid_view_rounded | 
					
						
							|  |  |  |                             : Icons.list, | 
					
						
							|  |  |  |                         size: 18, | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |                         color: | 
					
						
							|  |  |  |                             peerCardUiType.value == type ? textColor : textColor | 
					
						
							|  |  |  |                               ?..withOpacity(0.5), | 
					
						
							| 
									
										
										
										
											2022-09-21 14:56:01 +08:00
										 |  |  |                       )), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               )) | 
					
						
							|  |  |  |           .toList(), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   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) { | 
					
						
							| 
									
										
										
										
											2023-02-03 15:07:45 +08:00
										 |  |  |     final model = Provider.of<PeerTabModel>(context); | 
					
						
							|  |  |  |     final List<MenuEntryBase> menu = List.empty(growable: true); | 
					
						
							|  |  |  |     final List<int> menuIndex = List.empty(growable: true); | 
					
						
							|  |  |  |     var list = model.orderedNotFilteredTabs(); | 
					
						
							|  |  |  |     for (int i = 0; i < list.length; i++) { | 
					
						
							|  |  |  |       int tabIndex = list[i]; | 
					
						
							|  |  |  |       int bitMask = 1 << tabIndex; | 
					
						
							|  |  |  |       menuIndex.add(tabIndex); | 
					
						
							|  |  |  |       menu.add(MenuEntrySwitch( | 
					
						
							|  |  |  |           switchType: SwitchType.scheckbox, | 
					
						
							|  |  |  |           text: model.translatedTabname(tabIndex), | 
					
						
							|  |  |  |           getter: () async { | 
					
						
							|  |  |  |             return model.tabHiddenFlag & bitMask == 0; | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           setter: (show) async { | 
					
						
							|  |  |  |             model.onHideShow(tabIndex, 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()); | 
					
						
							| 
									
										
										
										
											2022-12-04 15:15:48 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PeerSearchBar extends StatefulWidget { | 
					
						
							|  |  |  |   const PeerSearchBar({Key? key}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<StatefulWidget> createState() => _PeerSearchBarState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _PeerSearchBarState extends State<PeerSearchBar> { | 
					
						
							|  |  |  |   var drawer = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return drawer | 
					
						
							|  |  |  |         ? _buildSearchBar() | 
					
						
							|  |  |  |         : IconButton( | 
					
						
							|  |  |  |             alignment: Alignment.centerRight, | 
					
						
							|  |  |  |             padding: const EdgeInsets.only(right: 2), | 
					
						
							|  |  |  |             onPressed: () { | 
					
						
							|  |  |  |               setState(() { | 
					
						
							|  |  |  |                 drawer = true; | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  |             }, | 
					
						
							| 
									
										
										
										
											2022-09-23 17:16:25 +08:00
										 |  |  |             icon: Icon( | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |               Icons.search_rounded, | 
					
						
							| 
									
										
										
										
											2022-09-23 17:16:25 +08:00
										 |  |  |               color: Theme.of(context).hintColor, | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |             )); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildSearchBar() { | 
					
						
							|  |  |  |     RxBool focused = false.obs; | 
					
						
							|  |  |  |     FocusNode focusNode = FocusNode(); | 
					
						
							| 
									
										
										
										
											2023-01-30 18:30:38 +08:00
										 |  |  |     focusNode.addListener(() { | 
					
						
							|  |  |  |       focused.value = focusNode.hasFocus; | 
					
						
							| 
									
										
										
										
											2023-02-02 16:45:29 +08:00
										 |  |  |       peerSearchTextController.selection = TextSelection( | 
					
						
							|  |  |  |           baseOffset: 0, | 
					
						
							|  |  |  |           extentOffset: peerSearchTextController.value.text.length); | 
					
						
							| 
									
										
										
										
											2023-01-30 18:30:38 +08:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |     return Container( | 
					
						
							|  |  |  |       width: 120, | 
					
						
							|  |  |  |       decoration: BoxDecoration( | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |         color: Theme.of(context).backgroundColor, | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |         borderRadius: BorderRadius.circular(6), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       child: Obx(() => Row( | 
					
						
							|  |  |  |             children: [ | 
					
						
							|  |  |  |               Expanded( | 
					
						
							|  |  |  |                 child: Row( | 
					
						
							|  |  |  |                   children: [ | 
					
						
							|  |  |  |                     Icon( | 
					
						
							|  |  |  |                       Icons.search_rounded, | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |                       color: Theme.of(context).hintColor, | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                     ).marginSymmetric(horizontal: 4), | 
					
						
							|  |  |  |                     Expanded( | 
					
						
							|  |  |  |                       child: TextField( | 
					
						
							|  |  |  |                         autofocus: true, | 
					
						
							|  |  |  |                         controller: peerSearchTextController, | 
					
						
							|  |  |  |                         onChanged: (searchText) { | 
					
						
							|  |  |  |                           peerSearchText.value = searchText; | 
					
						
							|  |  |  |                         }, | 
					
						
							|  |  |  |                         focusNode: focusNode, | 
					
						
							|  |  |  |                         textAlign: TextAlign.start, | 
					
						
							|  |  |  |                         maxLines: 1, | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |                         cursorColor: Theme.of(context) | 
					
						
							|  |  |  |                             .textTheme | 
					
						
							|  |  |  |                             .titleLarge | 
					
						
							|  |  |  |                             ?.color | 
					
						
							|  |  |  |                             ?.withOpacity(0.5), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                         cursorHeight: 18, | 
					
						
							|  |  |  |                         cursorWidth: 1, | 
					
						
							|  |  |  |                         style: const TextStyle(fontSize: 14), | 
					
						
							|  |  |  |                         decoration: InputDecoration( | 
					
						
							|  |  |  |                           contentPadding: | 
					
						
							|  |  |  |                               const EdgeInsets.symmetric(vertical: 6), | 
					
						
							|  |  |  |                           hintText: | 
					
						
							|  |  |  |                               focused.value ? null : translate("Search ID"), | 
					
						
							|  |  |  |                           hintStyle: TextStyle( | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |                               fontSize: 14, color: Theme.of(context).hintColor), | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                           border: InputBorder.none, | 
					
						
							|  |  |  |                           isDense: true, | 
					
						
							|  |  |  |                         ), | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                     // Icon(Icons.close),
 | 
					
						
							|  |  |  |                     IconButton( | 
					
						
							|  |  |  |                         alignment: Alignment.centerRight, | 
					
						
							|  |  |  |                         padding: const EdgeInsets.only(right: 2), | 
					
						
							|  |  |  |                         onPressed: () { | 
					
						
							|  |  |  |                           setState(() { | 
					
						
							|  |  |  |                             peerSearchTextController.clear(); | 
					
						
							|  |  |  |                             peerSearchText.value = ""; | 
					
						
							|  |  |  |                             drawer = false; | 
					
						
							|  |  |  |                           }); | 
					
						
							|  |  |  |                         }, | 
					
						
							| 
									
										
										
										
											2022-09-23 17:16:25 +08:00
										 |  |  |                         icon: Icon( | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                           Icons.close, | 
					
						
							| 
									
										
										
										
											2022-09-23 17:16:25 +08:00
										 |  |  |                           color: Theme.of(context).hintColor, | 
					
						
							| 
									
										
										
										
											2022-09-19 20:26:39 +08:00
										 |  |  |                         )), | 
					
						
							|  |  |  |                   ], | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ) | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |           )), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |