integrate file search bar into path location

This commit is contained in:
csf 2022-10-13 10:17:20 +09:00
parent 5b1a12c6a7
commit baf437e6f0

View File

@ -14,7 +14,17 @@ import '../../common.dart';
import '../../models/model.dart'; import '../../models/model.dart';
import '../../models/platform_model.dart'; import '../../models/platform_model.dart';
enum LocationStatus { bread, textField } /// status of location bar
enum LocationStatus {
/// normal bread crumb bar
bread,
/// show path text field
pathLocation,
/// show file search bar text field
fileSearchBar
}
class FileManagerPage extends StatefulWidget { class FileManagerPage extends StatefulWidget {
const FileManagerPage({Key? key, required this.id}) : super(key: key); const FileManagerPage({Key? key, required this.id}) : super(key: key);
@ -40,7 +50,7 @@ class _FileManagerPageState extends State<FileManagerPage>
final _breadCrumbScrollerLocal = ScrollController(); final _breadCrumbScrollerLocal = ScrollController();
final _breadCrumbScrollerRemote = ScrollController(); final _breadCrumbScrollerRemote = ScrollController();
final _dropMaskVisible = false.obs; final _dropMaskVisible = false.obs; // TODO impl drop mask
ScrollController getBreadCrumbScrollController(bool isLocal) { ScrollController getBreadCrumbScrollController(bool isLocal) {
return isLocal ? _breadCrumbScrollerLocal : _breadCrumbScrollerRemote; return isLocal ? _breadCrumbScrollerLocal : _breadCrumbScrollerRemote;
@ -84,6 +94,8 @@ class _FileManagerPageState extends State<FileManagerPage>
Get.delete<FFI>(tag: 'ft_${widget.id}'); Get.delete<FFI>(tag: 'ft_${widget.id}');
_locationNodeLocal.removeListener(onLocalLocationFocusChanged); _locationNodeLocal.removeListener(onLocalLocationFocusChanged);
_locationNodeRemote.removeListener(onRemoteLocationFocusChanged); _locationNodeRemote.removeListener(onRemoteLocationFocusChanged);
_locationNodeLocal.dispose();
_locationNodeRemote.dispose();
super.dispose(); super.dispose();
} }
@ -96,14 +108,7 @@ class _FileManagerPageState extends State<FileManagerPage>
return ChangeNotifierProvider.value( return ChangeNotifierProvider.value(
value: _ffi.fileModel, value: _ffi.fileModel,
child: Consumer<FileModel>(builder: (context, model, child) { child: Consumer<FileModel>(builder: (context, model, child) {
return WillPopScope( return Scaffold(
onWillPop: () async {
if (model.selectMode) {
model.toggleSelectMode();
}
return false;
},
child: Scaffold(
backgroundColor: Theme.of(context).backgroundColor, backgroundColor: Theme.of(context).backgroundColor,
body: Row( body: Row(
children: [ children: [
@ -112,7 +117,7 @@ class _FileManagerPageState extends State<FileManagerPage>
Flexible(flex: 2, child: statusList()) Flexible(flex: 2, child: statusList())
], ],
), ),
)); );
})); }));
}) })
]); ]);
@ -406,8 +411,6 @@ class _FileManagerPageState extends State<FileManagerPage>
final locationStatus = final locationStatus =
isLocal ? _locationStatusLocal : _locationStatusRemote; isLocal ? _locationStatusLocal : _locationStatusRemote;
final locationFocus = isLocal ? _locationNodeLocal : _locationNodeRemote; final locationFocus = isLocal ? _locationNodeLocal : _locationNodeRemote;
final searchTextObs = isLocal ? _searchTextLocal : _searchTextRemote;
final searchController = TextEditingController(text: searchTextObs.value);
return Container( return Container(
child: Column( child: Column(
children: [ children: [
@ -473,73 +476,62 @@ class _FileManagerPageState extends State<FileManagerPage>
onTap: () { onTap: () {
locationStatus.value = locationStatus.value =
locationStatus.value == LocationStatus.bread locationStatus.value == LocationStatus.bread
? LocationStatus.textField ? LocationStatus.pathLocation
: LocationStatus.bread; : LocationStatus.bread;
Future.delayed(Duration.zero, () { Future.delayed(Duration.zero, () {
if (locationStatus.value == LocationStatus.textField) { if (locationStatus.value == LocationStatus.pathLocation) {
locationFocus.requestFocus(); locationFocus.requestFocus();
} }
}); });
}, },
child: Container( child: Obx(() => Container(
decoration: decoration: BoxDecoration(
BoxDecoration(border: Border.all(color: Colors.black12)), border: Border.all(
color: locationStatus.value == LocationStatus.bread
? Colors.black12
: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.5))),
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
child: Obx(() => child: locationStatus.value == LocationStatus.bread
locationStatus.value == LocationStatus.bread
? buildBread(isLocal) ? buildBread(isLocal)
: buildPathLocation(isLocal))), : buildPathLocation(isLocal)),
DropdownButton<String>(
isDense: true,
underline: Offstage(),
items: [
// TODO: favourite
DropdownMenuItem(
child: Text('/'),
value: '/',
)
],
onChanged: (path) {
if (path is String && path.isNotEmpty) {
openDirectory(path, isLocal: isLocal);
}
})
], ],
))),
)), )),
)), Obx(() {
PopupMenuButton( switch (locationStatus.value) {
tooltip: "", case LocationStatus.bread:
itemBuilder: (context) => [ return IconButton(
PopupMenuItem(
enabled: false,
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: 200),
child: TextField(
textAlignVertical: TextAlignVertical.center,
controller: searchController,
autofocus: true,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
suffix: IconButton(
icon: Icon(Icons.clear),
splashRadius: 20,
onPressed: () { onPressed: () {
searchController.clear(); locationStatus.value = LocationStatus.fileSearchBar;
searchTextObs.value = ""; final focusNode =
Get.back(); isLocal ? _locationNodeLocal : _locationNodeRemote;
Future.delayed(
Duration.zero, () => focusNode.requestFocus());
}, },
),
),
onChanged: (searchText) =>
onSearchText(searchText, isLocal),
),
))
],
splashRadius: 20, splashRadius: 20,
icon: const Icon(Icons.search), icon: Icon(Icons.search));
), case LocationStatus.pathLocation:
return IconButton(
color: Theme.of(context).disabledColor,
onPressed: null,
splashRadius: 20,
icon: Icon(Icons.close));
case LocationStatus.fileSearchBar:
return IconButton(
color: Theme.of(context).disabledColor,
onPressed: () {
onSearchText("", isLocal);
locationStatus.value = LocationStatus.bread;
},
splashRadius: 1,
icon: Icon(Icons.close));
}
}),
IconButton( IconButton(
onPressed: () { onPressed: () {
model.refresh(isLocal: isLocal); model.refresh(isLocal: isLocal);
@ -649,9 +641,11 @@ class _FileManagerPageState extends State<FileManagerPage>
// ignore // ignore
} else { } else {
// lost focus, change to bread // lost focus, change to bread
if (_locationStatusLocal.value != LocationStatus.fileSearchBar) {
_locationStatusLocal.value = LocationStatus.bread; _locationStatusLocal.value = LocationStatus.bread;
} }
} }
}
void onRemoteLocationFocusChanged() { void onRemoteLocationFocusChanged() {
debugPrint("focus changed on remote"); debugPrint("focus changed on remote");
@ -659,9 +653,11 @@ class _FileManagerPageState extends State<FileManagerPage>
// ignore // ignore
} else { } else {
// lost focus, change to bread // lost focus, change to bread
if (_locationStatusRemote.value != LocationStatus.fileSearchBar) {
_locationStatusRemote.value = LocationStatus.bread; _locationStatusRemote.value = LocationStatus.bread;
} }
} }
}
Widget buildBread(bool isLocal) { Widget buildBread(bool isLocal) {
final items = getPathBreadCrumbItems(isLocal, (list) { final items = getPathBreadCrumbItems(isLocal, (list) {
@ -671,14 +667,33 @@ class _FileManagerPageState extends State<FileManagerPage>
} }
openDirectory(path, isLocal: isLocal); openDirectory(path, isLocal: isLocal);
}); });
breadCrumbScrollToEnd(isLocal);
return items.isEmpty return items.isEmpty
? Offstage() ? Offstage()
: BreadCrumb( : Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Expanded(
child: BreadCrumb(
items: items, items: items,
divider: Text("/").paddingSymmetric(horizontal: 4.0), divider: Text("/").paddingSymmetric(horizontal: 4.0),
overflow: ScrollableOverflow( overflow: ScrollableOverflow(
controller: getBreadCrumbScrollController(isLocal)), controller: getBreadCrumbScrollController(isLocal)),
); )),
DropdownButton<String>(
isDense: true,
underline: Offstage(),
items: [
// TODO: favourite
DropdownMenuItem(
child: Text('/'),
value: '/',
)
],
onChanged: (path) {
if (path is String && path.isNotEmpty) {
openDirectory(path, isLocal: isLocal);
}
})
]);
} }
List<BreadCrumbItem> getPathBreadCrumbItems( List<BreadCrumbItem> getPathBreadCrumbItems(
@ -705,19 +720,37 @@ class _FileManagerPageState extends State<FileManagerPage>
} }
Widget buildPathLocation(bool isLocal) { Widget buildPathLocation(bool isLocal) {
return TextField( final searchTextObs = isLocal ? _searchTextLocal : _searchTextRemote;
focusNode: isLocal ? _locationNodeLocal : _locationNodeRemote, final locationStatus =
isLocal ? _locationStatusLocal : _locationStatusRemote;
final focusNode = isLocal ? _locationNodeLocal : _locationNodeRemote;
return Row(children: [
Icon(
locationStatus.value == LocationStatus.pathLocation
? Icons.folder
: Icons.search,
color: Theme.of(context).hintColor,
).paddingSymmetric(horizontal: 2),
Expanded(
child: TextField(
focusNode: focusNode,
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
isDense: true, isDense: true,
prefix: Padding(padding: EdgeInsets.only(left: 4.0)), prefix: Padding(padding: EdgeInsets.only(left: 4.0))),
), controller: locationStatus.value == LocationStatus.pathLocation
controller: ? TextEditingController(text: model.getCurrentDir(isLocal).path)
TextEditingController(text: model.getCurrentDir(isLocal).path), : TextEditingController(text: searchTextObs.value)
..selection =
TextSelection.collapsed(offset: searchTextObs.value.length),
onSubmitted: (path) { onSubmitted: (path) {
openDirectory(path, isLocal: isLocal); openDirectory(path, isLocal: isLocal);
}, },
); onChanged: locationStatus.value == LocationStatus.fileSearchBar
? (searchText) => onSearchText(searchText, isLocal)
: null,
))
]);
} }
onSearchText(String searchText, bool isLocal) { onSearchText(String searchText, bool isLocal) {