refactor: use listview for file lists

This commit is contained in:
Kingtous 2023-02-15 15:03:19 +08:00
parent 20be9e10b1
commit 8df357c941
3 changed files with 186 additions and 128 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,57 +320,32 @@ 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);
return SizedBox(
key: ValueKey(entry.name), key: ValueKey(entry.name),
onSelectChanged: (s) { height: kDesktopFileTransferRowHeight,
_onSelectedChanged(getSelectedItems(isLocal), child: Column(
filteredEntries, entry, isLocal); mainAxisAlignment: MainAxisAlignment.spaceAround,
}, children: [
selected: getSelectedItems(isLocal).contains(entry), const Divider(
cells: [ height: 1,
DataCell( ),
Container( Expanded(
width: 200, child: Ink(
decoration: isSelected
? BoxDecoration(color: Theme.of(context).hoverColor)
: null,
child: InkWell(
child: Row(children: [
GestureDetector(
child: Container(
width: kDesktopFileTransferNameColWidth,
child: Tooltip( child: Tooltip(
waitDuration: Duration(milliseconds: 500), waitDuration: Duration(milliseconds: 500),
message: entry.name, message: entry.name,
@ -411,13 +370,12 @@ class _FileManagerPageState extends State<FileManagerPage>
?.withOpacity(0.7), ?.withOpacity(0.7),
).marginSymmetric(horizontal: 2), ).marginSymmetric(horizontal: 2),
Expanded( Expanded(
child: Text(entry.name, child: Text(entry.name.nonBreaking,
overflow: TextOverflow.ellipsis)) overflow: TextOverflow.ellipsis))
]), ]),
)), )),
onTap: () { onTap: () {
final items = getSelectedItems(isLocal); final items = getSelectedItems(isLocal);
// handle double click // handle double click
if (_checkDoubleClick(entry)) { if (_checkDoubleClick(entry)) {
openDirectory(entry.path, isLocal: isLocal); openDirectory(entry.path, isLocal: isLocal);
@ -428,7 +386,9 @@ class _FileManagerPageState extends State<FileManagerPage>
items, filteredEntries, entry, isLocal); items, filteredEntries, entry, isLocal);
}, },
), ),
DataCell(FittedBox( GestureDetector(
child: SizedBox(
width: kDesktopFileTransferModifiedColWidth,
child: Tooltip( child: Tooltip(
waitDuration: Duration(milliseconds: 500), waitDuration: Duration(milliseconds: 500),
message: lastModifiedStr, message: lastModifiedStr,
@ -436,18 +396,44 @@ class _FileManagerPageState extends State<FileManagerPage>
lastModifiedStr, lastModifiedStr,
style: TextStyle( style: TextStyle(
fontSize: 12, color: MyTheme.darkGray), fontSize: 12, color: MyTheme.darkGray),
)))), )),
DataCell(Tooltip( )),
GestureDetector(
child: Tooltip(
waitDuration: Duration(milliseconds: 500), waitDuration: Duration(milliseconds: 500),
message: sizeStr, message: sizeStr,
child: Text( child: Text(
sizeStr, sizeStr,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(
fontSize: 10, color: MyTheme.darkGray), fontSize: 10,
color: MyTheme.darkGray),
))), ))),
]); ]),
}).toList(growable: false), ),
),
),
],
),
);
}).toList(growable: false);
return Column(
children: [
// Header
_buildFileBrowserHeader(context, isLocal),
// Body
Expanded(
child: ListView.builder(
controller: scrollController,
itemExtent: kDesktopFileTransferRowHeight,
itemBuilder: (context, index) {
return rows[index];
},
itemCount: rows.length,
),
),
],
); );
}, },
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

@ -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;