Merge pull request #3214 from Kingtous/master

refactor: use listview for the file lists
This commit is contained in:
RustDesk 2023-02-15 15:22:18 +08:00 committed by GitHub
commit 38a0b8c960
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 192 additions and 131 deletions

View File

@ -50,6 +50,18 @@ const int kMobileMaxDisplayHeight = 1280;
const int kDesktopMaxDisplayWidth = 1920; const int kDesktopMaxDisplayWidth = 1920;
const int kDesktopMaxDisplayHeight = 1080; const int kDesktopMaxDisplayHeight = 1080;
const double kDesktopFileTransferNameColWidth = 200;
const double kDesktopFileTransferModifiedColWidth = 120;
const double kDesktopFileTransferRowHeight = 25.0;
const double kDesktopFileTransferHeaderHeight = 25.0;
// https://en.wikipedia.org/wiki/Non-breaking_space
const int $nbsp = 0x00A0;
extension StringExtension on String {
String get nonBreaking => replaceAll(' ', String.fromCharCode($nbsp));
}
const Size kConnectionManagerWindowSize = Size(300, 400); const Size kConnectionManagerWindowSize = Size(300, 400);
// Tabbar transition duration, now we remove the duration // Tabbar transition duration, now we remove the duration
const Duration kTabTransitionDuration = Duration.zero; const Duration kTabTransitionDuration = Duration.zero;

View File

@ -236,10 +236,7 @@ class _FileManagerPageState extends State<FileManagerPage>
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
child: SingleChildScrollView( child: _buildFileList(context, isLocal, scrollController),
controller: scrollController,
child: _buildDataTable(context, isLocal, scrollController),
),
) )
], ],
)), )),
@ -248,25 +245,11 @@ class _FileManagerPageState extends State<FileManagerPage>
); );
} }
Widget _buildDataTable( Widget _buildFileList(
BuildContext context, bool isLocal, ScrollController scrollController) { BuildContext context, bool isLocal, ScrollController scrollController) {
const rowHeight = 25.0;
final fd = model.getCurrentDir(isLocal); final fd = model.getCurrentDir(isLocal);
final entries = fd.entries; final entries = fd.entries;
final sortIndex = (SortBy style) { final selectedEntries = getSelectedItems(isLocal);
switch (style) {
case SortBy.name:
return 0;
case SortBy.type:
return 0;
case SortBy.modified:
return 1;
case SortBy.size:
return 2;
}
}(model.getSortStyle(isLocal));
final sortAscending =
isLocal ? model.localSortAscending : model.remoteSortAscending;
return MouseRegion( return MouseRegion(
onEnter: (evt) { onEnter: (evt) {
@ -287,7 +270,6 @@ class _FileManagerPageState extends State<FileManagerPage>
onNext: (buffer) { onNext: (buffer) {
debugPrint("searching next for $buffer"); debugPrint("searching next for $buffer");
assert(buffer.length == 1); assert(buffer.length == 1);
final selectedEntries = getSelectedItems(isLocal);
assert(selectedEntries.length <= 1); assert(selectedEntries.length <= 1);
var skipCount = 0; var skipCount = 0;
if (selectedEntries.items.isNotEmpty) { if (selectedEntries.items.isNotEmpty) {
@ -312,7 +294,8 @@ class _FileManagerPageState extends State<FileManagerPage>
return; return;
} }
_jumpToEntry( _jumpToEntry(
isLocal, searchResult.first, scrollController, rowHeight, buffer); isLocal, searchResult.first, scrollController,
kDesktopFileTransferRowHeight, buffer);
}, },
onSearch: (buffer) { onSearch: (buffer) {
debugPrint("searching for $buffer"); debugPrint("searching for $buffer");
@ -327,7 +310,8 @@ class _FileManagerPageState extends State<FileManagerPage>
return; return;
} }
_jumpToEntry( _jumpToEntry(
isLocal, searchResult.first, scrollController, rowHeight, buffer); isLocal, searchResult.first, scrollController,
kDesktopFileTransferRowHeight, buffer);
}, },
child: ObxValue<RxString>( child: ObxValue<RxString>(
(searchText) { (searchText) {
@ -336,118 +320,120 @@ class _FileManagerPageState extends State<FileManagerPage>
return element.name.contains(searchText.value); return element.name.contains(searchText.value);
}).toList(growable: false) }).toList(growable: false)
: entries; : entries;
return DataTable( final rows = filteredEntries.map((entry) {
key: ValueKey(isLocal ? 0 : 1),
showCheckboxColumn: false,
dataRowHeight: rowHeight,
headingRowHeight: 30,
horizontalMargin: 8,
columnSpacing: 8,
showBottomBorder: true,
sortColumnIndex: sortIndex,
sortAscending: sortAscending,
columns: [
DataColumn(
label: Text(
translate("Name"),
).marginSymmetric(horizontal: 4),
onSort: (columnIndex, ascending) {
model.changeSortStyle(SortBy.name,
isLocal: isLocal, ascending: ascending);
}),
DataColumn(
label: Text(
translate("Modified"),
),
onSort: (columnIndex, ascending) {
model.changeSortStyle(SortBy.modified,
isLocal: isLocal, ascending: ascending);
}),
DataColumn(
label: Text(translate("Size")),
onSort: (columnIndex, ascending) {
model.changeSortStyle(SortBy.size,
isLocal: isLocal, ascending: ascending);
}),
],
rows: filteredEntries.map((entry) {
final sizeStr = final sizeStr =
entry.isFile ? readableFileSize(entry.size.toDouble()) : ""; entry.isFile ? readableFileSize(entry.size.toDouble()) : "";
final lastModifiedStr = entry.isDrive final lastModifiedStr = entry.isDrive
? " " ? " "
: "${entry.lastModified().toString().replaceAll(".000", "")} "; : "${entry.lastModified().toString().replaceAll(".000", "")} ";
return DataRow( final isSelected = selectedEntries.contains(entry);
key: ValueKey(entry.name), return SizedBox(
onSelectChanged: (s) { key: ValueKey(entry.name),
_onSelectedChanged(getSelectedItems(isLocal), height: kDesktopFileTransferRowHeight,
filteredEntries, entry, isLocal); child: Column(
}, mainAxisAlignment: MainAxisAlignment.spaceAround,
selected: getSelectedItems(isLocal).contains(entry), children: [
cells: [ const Divider(
DataCell( height: 1,
Container( ),
width: 200, Expanded(
child: Tooltip( child: Ink(
waitDuration: Duration(milliseconds: 500), decoration: isSelected
message: entry.name, ? BoxDecoration(color: Theme.of(context).hoverColor)
child: Row(children: [ : null,
entry.isDrive child: InkWell(
? Image( child: Row(children: [
image: iconHardDrive, GestureDetector(
fit: BoxFit.scaleDown, child: Container(
color: Theme.of(context) width: kDesktopFileTransferNameColWidth,
.iconTheme child: Tooltip(
.color waitDuration: Duration(milliseconds: 500),
?.withOpacity(0.7)) message: entry.name,
.paddingAll(4) child: Row(children: [
: Icon( entry.isDrive
entry.isFile ? Image(
? Icons.feed_outlined image: iconHardDrive,
: Icons.folder, fit: BoxFit.scaleDown,
size: 20, color: Theme.of(context)
color: Theme.of(context) .iconTheme
.iconTheme .color
.color ?.withOpacity(0.7))
?.withOpacity(0.7), .paddingAll(4)
).marginSymmetric(horizontal: 2), : Icon(
Expanded( entry.isFile
child: Text(entry.name, ? Icons.feed_outlined
overflow: TextOverflow.ellipsis)) : Icons.folder,
]), size: 20,
color: Theme.of(context)
.iconTheme
.color
?.withOpacity(0.7),
).marginSymmetric(horizontal: 2),
Expanded(
child: Text(entry.name.nonBreaking,
overflow: TextOverflow.ellipsis))
]),
)),
onTap: () {
final items = getSelectedItems(isLocal);
// handle double click
if (_checkDoubleClick(entry)) {
openDirectory(entry.path, isLocal: isLocal);
items.clear();
return;
}
_onSelectedChanged(
items, filteredEntries, entry, isLocal);
},
),
GestureDetector(
child: SizedBox(
width: kDesktopFileTransferModifiedColWidth,
child: Tooltip(
waitDuration: Duration(milliseconds: 500),
message: lastModifiedStr,
child: Text(
lastModifiedStr,
style: TextStyle(
fontSize: 12, color: MyTheme.darkGray),
)),
)), )),
onTap: () { GestureDetector(
final items = getSelectedItems(isLocal); child: Tooltip(
waitDuration: Duration(milliseconds: 500),
// handle double click message: sizeStr,
if (_checkDoubleClick(entry)) { child: Text(
openDirectory(entry.path, isLocal: isLocal); sizeStr,
items.clear(); overflow: TextOverflow.ellipsis,
return; style: TextStyle(
} fontSize: 10,
_onSelectedChanged( color: MyTheme.darkGray),
items, filteredEntries, entry, isLocal); ))),
}, ]),
),
), ),
DataCell(FittedBox( ),
child: Tooltip( ],
waitDuration: Duration(milliseconds: 500), ),
message: lastModifiedStr, );
child: Text( }).toList(growable: false);
lastModifiedStr,
style: TextStyle( return Column(
fontSize: 12, color: MyTheme.darkGray), children: [
)))), // Header
DataCell(Tooltip( _buildFileBrowserHeader(context, isLocal),
waitDuration: Duration(milliseconds: 500), // Body
message: sizeStr, Expanded(
child: Text( child: ListView.builder(
sizeStr, controller: scrollController,
overflow: TextOverflow.ellipsis, itemExtent: kDesktopFileTransferRowHeight,
style: TextStyle( itemBuilder: (context, index) {
fontSize: 10, color: MyTheme.darkGray), return rows[index];
))), },
]); itemCount: rows.length,
}).toList(growable: false), ),
),
],
); );
}, },
isLocal ? _searchTextLocal : _searchTextRemote, isLocal ? _searchTextLocal : _searchTextRemote,
@ -1133,4 +1119,60 @@ class _FileManagerPageState extends State<FileManagerPage>
} }
}); });
} }
Widget headerItemFunc(
double? width, SortBy sortBy, String name, bool isLocal) {
final headerTextStyle =
Theme.of(context).dataTableTheme.headingTextStyle ?? TextStyle();
return ObxValue<Rx<bool?>>(
(ascending) => InkWell(
onTap: () {
if (ascending.value == null) {
ascending.value = true;
} else {
ascending.value = !ascending.value!;
}
model.changeSortStyle(sortBy,
isLocal: isLocal, ascending: ascending.value!);
},
child: SizedBox(
width: width,
height: kDesktopFileTransferHeaderHeight,
child: Row(
children: [
Text(
name,
style: headerTextStyle,
).marginSymmetric(
horizontal: sortBy == SortBy.name ? 4 : 0.0),
ascending.value != null
? Icon(ascending.value!
? Icons.arrow_upward
: Icons.arrow_downward)
: const Offstage()
],
),
),
), () {
if (model.getSortStyle(isLocal) == sortBy) {
return model.getSortAscending(isLocal).obs;
} else {
return Rx<bool?>(null);
}
}());
}
Widget _buildFileBrowserHeader(BuildContext context, bool isLocal) {
return Row(
children: [
headerItemFunc(kDesktopFileTransferNameColWidth, SortBy.name,
translate("Name"), isLocal),
headerItemFunc(kDesktopFileTransferModifiedColWidth, SortBy.modified,
translate("Modified"), isLocal),
Expanded(
child:
headerItemFunc(null, SortBy.size, translate("Size"), isLocal))
],
);
}
} }

View File

@ -439,9 +439,12 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
color: Colors.white, color: Colors.white,
border: Border.all(color: MyTheme.border), border: Border.all(color: MyTheme.border),
), ),
child: Row( child: SingleChildScrollView(
mainAxisSize: MainAxisSize.min, scrollDirection: Axis.horizontal,
children: menubarItems, child: Row(
mainAxisSize: MainAxisSize.min,
children: menubarItems,
),
)), )),
_buildDraggableShowHide(context), _buildDraggableShowHide(context),
])); ]));

View File

@ -75,6 +75,10 @@ class FileModel extends ChangeNotifier {
return isLocal ? _localSortStyle : _remoteSortStyle; return isLocal ? _localSortStyle : _remoteSortStyle;
} }
bool getSortAscending(bool isLocal) {
return isLocal ? _localSortAscending : _remoteSortAscending;
}
FileDirectory _currentLocalDir = FileDirectory(); FileDirectory _currentLocalDir = FileDirectory();
FileDirectory get currentLocalDir => _currentLocalDir; FileDirectory get currentLocalDir => _currentLocalDir;