From 2b10da167ce0a19b193e340b27a57eb3d4e2151e Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 27 Jun 2022 16:44:34 +0800 Subject: [PATCH] add: file transfer dual logic with bridge --- .../lib/desktop/pages/file_manager_page.dart | 265 ++++++++---------- .../desktop/pages/file_manager_tab_page.dart | 125 ++++----- flutter/lib/models/file_model.dart | 73 +++-- flutter/lib/utils/multi_window_manager.dart | 2 +- 4 files changed, 220 insertions(+), 245 deletions(-) diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index e9e69556c..ed4a32b37 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -11,7 +11,6 @@ import 'package:provider/provider.dart'; import 'package:wakelock/wakelock.dart'; import '../../common.dart'; -import '../../mobile/widgets/dialog.dart'; import '../../models/model.dart'; class FileManagerPage extends StatefulWidget { @@ -25,7 +24,8 @@ class FileManagerPage extends StatefulWidget { class _FileManagerPageState extends State with AutomaticKeepAliveClientMixin { final _selectedItems = SelectedItems(); - final _breadCrumbScroller = ScrollController(); + final _breadCrumbLocalScroller = ScrollController(); + final _breadCrumbRemoteScroller = ScrollController(); /// FFI with name file_transfer_id FFI get _ffi => ffi('ft_${widget.id}'); @@ -66,135 +66,11 @@ class _FileManagerPageState extends State onWillPop: () async { if (model.selectMode) { model.toggleSelectMode(); - } else { - goBack(); } return false; }, child: Scaffold( backgroundColor: MyTheme.grayBg, - appBar: AppBar( - leading: Row(children: [ - IconButton(icon: Icon(Icons.close), onPressed: clientClose), - ]), - centerTitle: true, - // title: ToggleSwitch( - // initialLabelIndex: model.isLocal ? 0 : 1, - // activeBgColor: [MyTheme.idColor], - // inactiveBgColor: MyTheme.grayBg, - // inactiveFgColor: Colors.black54, - // totalSwitches: 2, - // minWidth: 100, - // fontSize: 15, - // iconSize: 18, - // labels: [translate("Local"), translate("Remote")], - // icons: [Icons.phone_android_sharp, Icons.screen_share], - // onToggle: (index) { - // final current = model.isLocal ? 0 : 1; - // if (index != current) { - // model.togglePage(); - // } - // }, - // ), - actions: [ - PopupMenuButton( - icon: Icon(Icons.more_vert), - itemBuilder: (context) { - return [ - PopupMenuItem( - child: Row( - children: [ - Icon(Icons.refresh, color: Colors.black), - SizedBox(width: 5), - Text(translate("Refresh File")) - ], - ), - value: "refresh", - ), - PopupMenuItem( - child: Row( - children: [ - Icon(Icons.check, color: Colors.black), - SizedBox(width: 5), - Text(translate("Multi Select")) - ], - ), - value: "select", - ), - PopupMenuItem( - child: Row( - children: [ - Icon(Icons.folder_outlined, - color: Colors.black), - SizedBox(width: 5), - Text(translate("Create Folder")) - ], - ), - value: "folder", - ), - PopupMenuItem( - child: Row( - children: [ - Icon( - model.currentShowHidden - ? Icons.check_box_outlined - : Icons.check_box_outline_blank, - color: Colors.black), - SizedBox(width: 5), - Text(translate("Show Hidden Files")) - ], - ), - value: "hidden", - ) - ]; - }, - onSelected: (v) { - if (v == "refresh") { - model.refresh(); - } else if (v == "select") { - _selectedItems.clear(); - model.toggleSelectMode(); - } else if (v == "folder") { - final name = TextEditingController(); - DialogManager.show((setState, close) => - CustomAlertDialog( - title: Text(translate("Create Folder")), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextFormField( - decoration: InputDecoration( - labelText: translate( - "Please enter the folder name"), - ), - controller: name, - ), - ], - ), - actions: [ - TextButton( - style: flatButtonStyle, - onPressed: () => close(false), - child: Text(translate("Cancel"))), - ElevatedButton( - style: flatButtonStyle, - onPressed: () { - if (name.value.text.isNotEmpty) { - model.createDir(PathUtil.join( - model.currentDir.path, - name.value.text, - model.currentIsWindows)); - close(); - } - }, - child: Text(translate("OK"))) - ])); - } else if (v == "hidden") { - model.toggleShowHidden(); - } - }), - ], - ), body: Row( children: [ Flexible(flex: 1, child: body(isLocal: true)), @@ -213,11 +89,110 @@ class _FileManagerPageState extends State return !_selectedItems.isOtherPage(model.isLocal); } + Widget menu({bool isLocal = false}) { + return PopupMenuButton( + icon: Icon(Icons.more_vert), + itemBuilder: (context) { + return [ + PopupMenuItem( + child: Row( + children: [ + Icon(Icons.refresh, color: Colors.black), + SizedBox(width: 5), + Text(translate("Refresh File")) + ], + ), + value: "refresh", + ), + PopupMenuItem( + child: Row( + children: [ + Icon(Icons.check, color: Colors.black), + SizedBox(width: 5), + Text(translate("Multi Select")) + ], + ), + value: "select", + ), + PopupMenuItem( + child: Row( + children: [ + Icon(Icons.folder_outlined, + color: Colors.black), + SizedBox(width: 5), + Text(translate("Create Folder")) + ], + ), + value: "folder", + ), + PopupMenuItem( + child: Row( + children: [ + Icon( + model.currentShowHidden + ? Icons.check_box_outlined + : Icons.check_box_outline_blank, + color: Colors.black), + SizedBox(width: 5), + Text(translate("Show Hidden Files")) + ], + ), + value: "hidden", + ) + ]; + }, + onSelected: (v) { + if (v == "refresh") { + model.refresh(); + } else if (v == "select") { + _selectedItems.clear(); + model.toggleSelectMode(); + } else if (v == "folder") { + final name = TextEditingController(); + DialogManager.show((setState, close) => + CustomAlertDialog( + title: Text(translate("Create Folder")), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + decoration: InputDecoration( + labelText: translate( + "Please enter the folder name"), + ), + controller: name, + ), + ], + ), + actions: [ + TextButton( + style: flatButtonStyle, + onPressed: () => close(false), + child: Text(translate("Cancel"))), + ElevatedButton( + style: flatButtonStyle, + onPressed: () { + if (name.value.text.isNotEmpty) { + model.createDir(PathUtil.join( + model.currentDir.path, + name.value.text, + model.currentIsWindows)); + close(); + } + }, + child: Text(translate("OK"))) + ])); + } else if (v == "hidden") { + model.toggleShowHidden(local: isLocal); + } + }); + } + Widget body({bool isLocal = false}) { final fd = isLocal ? model.currentLocalDir : model.currentRemoteDir; final entries = fd.entries; return Column(children: [ - headTools(), + headTools(isLocal), Expanded( child: ListView.builder( itemCount: entries.length + 1, @@ -301,8 +276,8 @@ class _FileManagerPageState extends State return; } if (entries[index].isDirectory) { - model.openDirectory(entries[index].path); - breadCrumbScrollToEnd(); + model.openDirectory(entries[index].path, isLocal: isLocal); + breadCrumbScrollToEnd(isLocal); } else { // Perform file-related tasks. } @@ -322,20 +297,21 @@ class _FileManagerPageState extends State ]); } - goBack() { - model.goToParentDirectory(); + goBack({bool? isLocal}) { + model.goToParentDirectory(isLocal: isLocal); } - breadCrumbScrollToEnd() { + breadCrumbScrollToEnd(bool isLocal) { + final controller = isLocal ? _breadCrumbLocalScroller : _breadCrumbRemoteScroller; Future.delayed(Duration(milliseconds: 200), () { - _breadCrumbScroller.animateTo( - _breadCrumbScroller.position.maxScrollExtent, + controller.animateTo( + controller.position.maxScrollExtent, duration: Duration(milliseconds: 200), curve: Curves.fastLinearToSlowEaseIn); }); } - Widget headTools() => Container( + Widget headTools(bool isLocal) => Container( child: Row( children: [ Expanded( @@ -353,16 +329,18 @@ class _FileManagerPageState extends State path = PathUtil.join(path, item, model.currentIsWindows); } } - model.openDirectory(path); - }), + model.openDirectory(path, isLocal: isLocal); + }, isLocal), divider: Icon(Icons.chevron_right), - overflow: ScrollableOverflow(controller: _breadCrumbScroller), + overflow: ScrollableOverflow(controller: isLocal ? _breadCrumbLocalScroller : _breadCrumbRemoteScroller), )), Row( children: [ IconButton( icon: Icon(Icons.arrow_upward), - onPressed: goBack, + onPressed: () { + goBack(isLocal: isLocal); + }, ), PopupMenuButton( icon: Icon(Icons.sort), @@ -375,7 +353,10 @@ class _FileManagerPageState extends State )) .toList(); }, - onSelected: model.changeSortStyle), + onSelected: (sort) { + model.changeSortStyle(sort, isLocal: isLocal); + }), + menu(isLocal: isLocal) ], ) ], @@ -486,8 +467,8 @@ class _FileManagerPageState extends State } List getPathBreadCrumbItems( - void Function() onHome, void Function(List) onPressed) { - final path = model.currentShortPath; + void Function() onHome, void Function(List) onPressed, bool isLocal) { + final path = model.shortPath(isLocal); final list = PathUtil.split(path, model.currentIsWindows); final breadCrumbList = [ BreadCrumbItem( diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart index 6c945aede..af65c86df 100644 --- a/flutter/lib/desktop/pages/file_manager_tab_page.dart +++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hbb/desktop/pages/file_manager_page.dart'; import 'package:flutter_hbb/desktop/widgets/titlebar_widget.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart'; +import 'package:get/get.dart'; /// File Transfer for multi tabs class FileManagerTabPage extends StatefulWidget { @@ -21,7 +22,7 @@ class _FileManagerTabPageState extends State // refactor List when using multi-tab // this singleton is only for test List connectionIds = List.empty(growable: true); - var initialIndex = 0; + var initialIndex = 0.obs; _FileManagerTabPageState(Map params) { if (params['id'] != null) { @@ -37,21 +38,15 @@ class _FileManagerTabPageState extends State "call ${call.method} with args ${call.arguments} from window ${fromWindowId}"); // for simplify, just replace connectionId if (call.method == "new_file_transfer") { - setState(() { - final args = jsonDecode(call.arguments); - final id = args['id']; - final indexOf = connectionIds.indexOf(id); - if (indexOf >= 0) { - setState(() { - initialIndex = indexOf; - }); - } else { - connectionIds.add(id); - setState(() { - initialIndex = connectionIds.length - 1; - }); - } - }); + final args = jsonDecode(call.arguments); + final id = args['id']; + final indexOf = connectionIds.indexOf(id); + if (indexOf >= 0) { + initialIndex.value = indexOf; + } else { + connectionIds.add(id); + initialIndex.value = connectionIds.length - 1; + } } }); } @@ -59,51 +54,53 @@ class _FileManagerTabPageState extends State @override Widget build(BuildContext context) { return Scaffold( - body: DefaultTabController( - initialIndex: initialIndex, - length: connectionIds.length, - animationDuration: Duration.zero, - child: Column( - children: [ - DesktopTitleBar( - child: TabBar( - isScrollable: true, - labelColor: Colors.white, - physics: NeverScrollableScrollPhysics(), - indicatorColor: Colors.white, - tabs: connectionIds - .map((e) => Tab( - child: Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text(e), - SizedBox( - width: 4, - ), - InkWell( - onTap: () { - onRemoveId(e); - }, - child: Icon( - Icons.highlight_remove, - size: 20, - )) - ], - ), - )) - .toList()), - ), - Expanded( - child: TabBarView( - children: connectionIds - .map((e) => Container( - child: FileManagerPage( - key: ValueKey(e), - id: e))) //RemotePage(key: ValueKey(e), id: e)) - .toList()), - ) - ], + body: Obx( + ()=> DefaultTabController( + initialIndex: initialIndex.value, + length: connectionIds.length, + animationDuration: Duration.zero, + child: Column( + children: [ + DesktopTitleBar( + child: TabBar( + isScrollable: true, + labelColor: Colors.white, + physics: NeverScrollableScrollPhysics(), + indicatorColor: Colors.white, + tabs: connectionIds + .map((e) => Tab( + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(e), + SizedBox( + width: 4, + ), + InkWell( + onTap: () { + onRemoveId(e); + }, + child: Icon( + Icons.highlight_remove, + size: 20, + )) + ], + ), + )) + .toList()), + ), + Expanded( + child: TabBarView( + children: connectionIds + .map((e) => Container( + child: FileManagerPage( + key: ValueKey(e), + id: e))) //RemotePage(key: ValueKey(e), id: e)) + .toList()), + ) + ], + ), ), ), ); @@ -114,9 +111,7 @@ class _FileManagerTabPageState extends State if (indexOf == -1) { return; } - setState(() { - connectionIds.removeAt(indexOf); - initialIndex = max(0, initialIndex - 1); - }); + connectionIds.removeAt(indexOf); + initialIndex.value = max(0, initialIndex.value - 1); } } diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index 2c42d3b02..58ddd658a 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -60,6 +60,21 @@ class FileModel extends ChangeNotifier { } } + String shortPath(bool isLocal) { + final dir = isLocal ? currentLocalDir : currentRemoteDir; + if (dir.path.startsWith(currentHome)) { + var path = dir.path.replaceFirst(currentHome, ""); + if (path.length == 0) return ""; + if (path[0] == "/" || path[0] == "\\") { + // remove more '/' or '\' + path = path.replaceFirst(path[0], ""); + } + return path; + } else { + return dir.path.replaceFirst(currentHome, ""); + } + } + bool get currentShowHidden => _isLocal ? _localOption.showHidden : _remoteOption.showHidden; @@ -265,9 +280,9 @@ class FileModel extends ChangeNotifier { openDirectory(currentHome); } - goToParentDirectory() { + goToParentDirectory({bool? isLocal}) { final parent = PathUtil.dirname(currentDir.path, currentIsWindows); - openDirectory(parent); + openDirectory(parent, isLocal: isLocal); } sendFiles(SelectedItems items) { @@ -282,17 +297,10 @@ class FileModel extends ChangeNotifier { items.isLocal! ? _localOption.isWindows : _remoteOption.isWindows; final showHidden = items.isLocal! ? _localOption.showHidden : _remoteOption.showHidden; - items.items.forEach((from) { + items.items.forEach((from) async { _jobId++; - final msg = { - "id": _jobId.toString(), - "path": from.path, - "to": PathUtil.join(toPath, from.name, isWindows), - "file_num": "0", - "show_hidden": showHidden.toString(), - "is_remote": (!(items.isLocal!)).toString() - }; - _ffi.target?.setByName("send_files", jsonEncode(msg)); + await _ffi.target?.bind.sessionSendFiles(id: '${_ffi.target?.getId()}', actId: _jobId, path: from.path, to: PathUtil.join(toPath, from.name, isWindows) + ,fileNum: 0, includeHidden: showHidden, isRemote: !(items.isLocal!)); }); } @@ -485,43 +493,34 @@ class FileModel extends ChangeNotifier { } sendRemoveFile(String path, int fileNum, bool isLocal) { - final msg = { - "id": _jobId.toString(), - "path": path, - "file_num": fileNum.toString(), - "is_remote": (!(isLocal)).toString() - }; - _ffi.target?.setByName("remove_file", jsonEncode(msg)); + _ffi.target?.bind.sessionRemoveFile(id: '${_ffi.target?.getId()}', actId: _jobId, path: path, isRemote: !isLocal, fileNum: fileNum); } sendRemoveEmptyDir(String path, int fileNum, bool isLocal) { - final msg = { - "id": _jobId.toString(), - "path": path, - "is_remote": (!isLocal).toString() - }; - _ffi.target?.setByName("remove_all_empty_dirs", jsonEncode(msg)); + _ffi.target?.bind.sessionRemoveAllEmptyDirs(id: '${_ffi.target?.getId()}', actId: _jobId, path: path, isRemote: !isLocal); } - createDir(String path) { + createDir(String path) async { _jobId++; - final msg = { - "id": _jobId.toString(), - "path": path, - "is_remote": (!isLocal).toString() - }; - _ffi.target?.setByName("create_dir", jsonEncode(msg)); + _ffi.target?.bind.sessionCreateDir(id: '${_ffi.target?.getId()}', actId: _jobId, path: path, isRemote: !isLocal); } - cancelJob(int id) { - _ffi.target?.setByName("cancel_job", id.toString()); + cancelJob(int id) async { + _ffi.target?.bind.sessionCancelJob(id: '${_ffi.target?.getId()}', actId: id); jobReset(); } - changeSortStyle(SortBy sort) { + changeSortStyle(SortBy sort, {bool? isLocal}) { _sortStyle = sort; - _currentLocalDir.changeSortStyle(sort); - _currentRemoteDir.changeSortStyle(sort); + if (isLocal == null) { + // compatible for mobile logic + _currentLocalDir.changeSortStyle(sort); + _currentRemoteDir.changeSortStyle(sort); + } else if (isLocal) { + _currentLocalDir.changeSortStyle(sort); + } else { + _currentRemoteDir.changeSortStyle(sort); + } notifyListeners(); } diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 5c522f3a5..979ebffd7 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -101,7 +101,7 @@ class RustDeskMultiWindowManager { case WindowType.RemoteDesktop: return _remoteDesktopWindowId; case WindowType.FileTransfer: - break; + return _fileTransferWindowId; case WindowType.PortForward: break; case WindowType.Unknown: