Skip to content

Commit d8dc378

Browse files
committed
files: Cleanup workspace and everything related
1 parent fc91b40 commit d8dc378

File tree

11 files changed

+765
-671
lines changed

11 files changed

+765
-671
lines changed

lib/backend/fetch.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class CancelableFsFetch {
2020
final ValueChanged<double?>? onProgressChange;
2121
final ValueChanged<OSError?>? onFileSystemException;
2222
final bool ascending;
23-
final int columnIndex;
23+
final SortType sortType;
2424
final bool showHidden;
2525

2626
CancelableFsFetch({
@@ -30,7 +30,7 @@ class CancelableFsFetch {
3030
this.onProgressChange,
3131
this.onFileSystemException,
3232
this.ascending = false,
33-
this.columnIndex = 0,
33+
this.sortType = SortType.name,
3434
this.showHidden = false,
3535
});
3636

@@ -112,7 +112,7 @@ class CancelableFsFetch {
112112
item1 = b;
113113
}
114114

115-
switch (columnIndex) {
115+
switch (sortType.index) {
116116
case 0:
117117
return Utils.getEntityName(item1.path.toLowerCase())
118118
.compareTo(Utils.getEntityName(item2.path.toLowerCase()));

lib/backend/workspace.dart

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
import 'dart:async';
2+
import 'dart:io';
3+
4+
import 'package:files/backend/entity_info.dart';
5+
import 'package:files/backend/fetch.dart';
6+
import 'package:flutter/widgets.dart';
7+
import 'package:provider/provider.dart';
8+
9+
enum WorkspaceView { table, grid }
10+
11+
class WorkspaceController with ChangeNotifier {
12+
WorkspaceController({required String initialDir}) {
13+
_resetStates();
14+
changeCurrentDir(initialDir);
15+
}
16+
17+
// Scroll cache
18+
double lastHorizontalScrollOffset = 0.0;
19+
double lastVerticalScrollOffset = 0.0;
20+
21+
// Private utilities
22+
CancelableFsFetch? _fetcher;
23+
StreamSubscription<FileSystemEvent>? _directoryStream;
24+
25+
// Loading related
26+
late String _currentDir;
27+
List<EntityInfo>? _currentInfo;
28+
double? _loadingProgress;
29+
OSError? _lastError;
30+
31+
// Sort, view and selection
32+
bool _ascending = true;
33+
bool _showHidden = false;
34+
SortType _sortType = SortType.name;
35+
WorkspaceView _view = WorkspaceView.table; // save on SharedPreferences?
36+
final List<EntityInfo> _selectedItems = [];
37+
38+
// States
39+
late TableViewState _tableState;
40+
late GridViewState _gridState;
41+
42+
// Navigation history
43+
final List<String> _history = [];
44+
int _historyOffset = 0;
45+
46+
void _resetStates() {
47+
_tableState = const TableViewState(
48+
firstWidth: 480,
49+
secondWidth: 180,
50+
thirdWidth: 120,
51+
fourthWidth: 120,
52+
);
53+
_gridState = const GridViewState(size: 96);
54+
}
55+
56+
Future<void> getInfoForDir(Directory dir) async {
57+
await _fetcher?.cancel();
58+
_lastError = null;
59+
_fetcher = CancelableFsFetch(
60+
directory: dir,
61+
onFetched: (data) {
62+
_currentInfo = data;
63+
notifyListeners();
64+
},
65+
onProgressChange: (value) {
66+
_loadingProgress = value;
67+
notifyListeners();
68+
},
69+
showHidden: _showHidden,
70+
ascending: _ascending,
71+
sortType: _sortType,
72+
onFileSystemException: (value) {
73+
_lastError = value;
74+
notifyListeners();
75+
},
76+
);
77+
await _fetcher!.startFetch();
78+
}
79+
80+
List<EntityInfo>? get currentInfo =>
81+
_currentInfo != null ? List.unmodifiable(_currentInfo!) : null;
82+
double? get loadingProgress => _loadingProgress;
83+
84+
void clearCurrentInfo() {
85+
_currentInfo = null;
86+
notifyListeners();
87+
}
88+
89+
String get currentDir => _currentDir;
90+
91+
bool get ascending => _ascending;
92+
set ascending(bool value) {
93+
_ascending = value;
94+
notifyListeners();
95+
}
96+
97+
bool get showHidden => _showHidden;
98+
set showHidden(bool value) {
99+
_showHidden = value;
100+
notifyListeners();
101+
}
102+
103+
SortType get sortType => _sortType;
104+
set sortType(SortType value) {
105+
_sortType = value;
106+
notifyListeners();
107+
}
108+
109+
WorkspaceView get view => _view;
110+
set view(WorkspaceView value) {
111+
_view = value;
112+
notifyListeners();
113+
}
114+
115+
TableViewState get tableState => _tableState;
116+
void setTableState(TableViewState state) {
117+
_tableState = state;
118+
notifyListeners();
119+
}
120+
121+
GridViewState get gridState => _gridState;
122+
void setGridState(GridViewState state) {
123+
_gridState = state;
124+
notifyListeners();
125+
}
126+
127+
List<EntityInfo> get selectedItems => List.unmodifiable(_selectedItems);
128+
void addSelectedItem(EntityInfo info) {
129+
_selectedItems.add(info);
130+
notifyListeners();
131+
}
132+
133+
void removeSelectedItem(EntityInfo info) {
134+
_selectedItems.remove(info);
135+
notifyListeners();
136+
}
137+
138+
void clearSelectedItems() {
139+
_selectedItems.clear();
140+
notifyListeners();
141+
}
142+
143+
List<String> get history => List.from(_history);
144+
int get historyOffset => _historyOffset;
145+
146+
OSError? get lastError => _lastError;
147+
148+
Future<void> changeCurrentDir(
149+
String newDir, {
150+
bool updateHistory = true,
151+
}) async {
152+
_currentDir = newDir;
153+
154+
if (updateHistory) {
155+
if (_historyOffset != 0) {
156+
final List<String> validHistory =
157+
_history.reversed.toList().sublist(_historyOffset);
158+
_history.clear();
159+
_history.addAll(validHistory.reversed);
160+
_historyOffset = 0;
161+
}
162+
_history.add(_currentDir);
163+
}
164+
165+
clearCurrentInfo();
166+
clearSelectedItems();
167+
await _directoryStream?.cancel();
168+
await getInfoForDir(Directory(newDir));
169+
_directoryStream =
170+
Directory(newDir).watch().listen(_directoryStreamListener);
171+
}
172+
173+
void setHistoryOffset(int offset) {
174+
_historyOffset = 0; // hack
175+
changeCurrentDir(_history.reversed.toList()[offset], updateHistory: false);
176+
_historyOffset = offset;
177+
178+
notifyListeners();
179+
}
180+
181+
void goToPreviousHistoryEntry() => setHistoryOffset(_historyOffset + 1);
182+
void goToNextHistoryEntry() => setHistoryOffset(_historyOffset - 1);
183+
184+
bool get canGoToPreviousHistoryEntry =>
185+
_history.length > 1 && _historyOffset < _history.length - 1;
186+
bool get canGoToNextHistoryEntry => _historyOffset != 0;
187+
188+
Future<void> _directoryStreamListener(FileSystemEvent event) async {
189+
await getInfoForDir(Directory(currentDir));
190+
}
191+
192+
static WorkspaceController of(BuildContext context, {bool listen = true}) {
193+
return Provider.of<WorkspaceController>(context, listen: listen);
194+
}
195+
}
196+
197+
class TableViewState {
198+
final double firstWidth;
199+
final double secondWidth;
200+
final double thirdWidth;
201+
final double fourthWidth;
202+
203+
const TableViewState({
204+
required this.firstWidth,
205+
required this.secondWidth,
206+
required this.thirdWidth,
207+
required this.fourthWidth,
208+
});
209+
210+
List<double> get widths => [
211+
firstWidth,
212+
secondWidth,
213+
thirdWidth,
214+
fourthWidth,
215+
];
216+
217+
TableViewState copyWith({
218+
double? firstWidth,
219+
double? secondWidth,
220+
double? thirdWidth,
221+
double? fourthWidth,
222+
}) {
223+
return TableViewState(
224+
firstWidth: firstWidth ?? this.firstWidth,
225+
secondWidth: secondWidth ?? this.secondWidth,
226+
thirdWidth: thirdWidth ?? this.thirdWidth,
227+
fourthWidth: fourthWidth ?? this.fourthWidth,
228+
);
229+
}
230+
231+
TableViewState applyDeltaToWidth(int index, double delta) {
232+
switch (index) {
233+
case 0:
234+
return copyWith(firstWidth: firstWidth + delta);
235+
case 1:
236+
return copyWith(secondWidth: secondWidth + delta);
237+
case 2:
238+
return copyWith(thirdWidth: thirdWidth + delta);
239+
case 3:
240+
return copyWith(fourthWidth: fourthWidth + delta);
241+
}
242+
243+
throw RangeError.index(index, widths);
244+
}
245+
}
246+
247+
class GridViewState {
248+
final double size;
249+
250+
const GridViewState({
251+
required this.size,
252+
});
253+
}

lib/main.dart

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import 'package:animations/animations.dart';
1817
import 'package:files/backend/folder_provider.dart';
1918
import 'package:files/backend/providers.dart';
2019
import 'package:files/backend/utils.dart';
20+
import 'package:files/backend/workspace.dart';
2121
import 'package:files/widgets/context_menu/context_menu_theme.dart';
2222
import 'package:files/widgets/side_pane.dart';
2323
import 'package:files/widgets/tab_strip.dart';
@@ -148,21 +148,9 @@ class _FilesHomeState extends State<FilesHome> {
148148
),
149149
),
150150
Expanded(
151-
child: PageTransitionSwitcher(
152-
transitionBuilder:
153-
(child, primaryAnimation, secondaryAnimation) {
154-
return SharedAxisTransition(
155-
animation: primaryAnimation,
156-
secondaryAnimation: secondaryAnimation,
157-
transitionType: SharedAxisTransitionType.scaled,
158-
fillColor: Colors.transparent,
159-
child: child,
160-
);
161-
},
162-
child: FilesWorkspace(
163-
key: ValueKey(currentWorkspace),
164-
controller: workspaces[currentWorkspace],
165-
),
151+
child: FilesWorkspace(
152+
key: ValueKey(currentWorkspace),
153+
controller: workspaces[currentWorkspace],
166154
),
167155
),
168156
],

lib/widgets/folder_dialog.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import 'package:flutter/material.dart';
2+
3+
class FolderDialog extends StatefulWidget {
4+
const FolderDialog({super.key});
5+
6+
@override
7+
State<FolderDialog> createState() => _FolderDialogState();
8+
}
9+
10+
class _FolderDialogState extends State<FolderDialog> {
11+
final TextEditingController controller = TextEditingController();
12+
final RegExp folderValidator = RegExp(
13+
r'^[^\s^\x00-\x1f\\?*:"";<>|\/][^\x00-\x1f\\?*:"";<>|\/]*[^\s^\x00-\x1f\\?*:"";<>|\/.]+$',
14+
);
15+
16+
@override
17+
Widget build(BuildContext context) {
18+
return AlertDialog(
19+
title: const Text("New Folder"),
20+
content: TextField(
21+
autofocus: true,
22+
decoration: const InputDecoration(
23+
hintText: "Folder name",
24+
),
25+
controller: controller,
26+
onSubmitted: (value) {
27+
Navigator.pop(context, value);
28+
},
29+
),
30+
actions: [
31+
TextButton(
32+
child: const Text("Cancel"),
33+
onPressed: () {
34+
Navigator.pop(context);
35+
},
36+
),
37+
ValueListenableBuilder<TextEditingValue>(
38+
valueListenable: controller,
39+
builder: (context, value, __) => TextButton(
40+
onPressed:
41+
value.text.isNotEmpty && folderValidator.hasMatch(value.text)
42+
? () => Navigator.of(context).pop(controller.text)
43+
: null,
44+
child: const Text("Create"),
45+
),
46+
),
47+
],
48+
);
49+
}
50+
}

0 commit comments

Comments
 (0)