Skip to content

Commit b109ad5

Browse files
committed
UI feature: Grid view mode
1 parent d6e6328 commit b109ad5

File tree

3 files changed

+273
-252
lines changed

3 files changed

+273
-252
lines changed

lib/screens/homepage/homepage.dart

Lines changed: 183 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,111 @@ class HomePage extends StatefulWidget {
114114

115115
class _HomePageState extends State<HomePage> {
116116
late ModulesManager mm;
117+
bool isListView = true;
117118
final Future<SharedPreferences> _pref = SharedPreferences.getInstance();
118119

120+
/// Called from the project list control
121+
/// Switches between list and grid view
122+
void onToggleView() async {
123+
(await _pref).setBool("isListView", isListView);
124+
setState(() {
125+
isListView = !isListView;
126+
});
127+
}
128+
129+
List<PopupMenuEntry<String>> getPopupMenu(HistoryItem item) {
130+
return <PopupMenuEntry<String>>[
131+
const PopupMenuItem<String>(
132+
value: "delete",
133+
child: Text('Delete Project'),
134+
),
135+
const PopupMenuItem<String>(
136+
//TODO: Implement this menu
137+
value: "rename",
138+
child: Text('Rename Project'),
139+
),
140+
const PopupMenuItem<String>(
141+
//TODO: Implement this menu
142+
value: "export",
143+
child: Text('Export Project'),
144+
),
145+
const PopupMenuItem<String>(
146+
//TODO: Implement this menu
147+
value: "remove",
148+
child: Text('Remove from list')),
149+
];
150+
}
151+
152+
void onMenuItemSelected(String itemTitle, HistoryItem item) {
153+
switch (itemTitle) {
154+
case "delete":
155+
if (item.type == HistoryItemType.solution) {
156+
showDialog(
157+
context: context,
158+
builder: (BuildContext context) {
159+
return AlertDialog(
160+
title: Text("Delete ${item.name}?"),
161+
content: Text(
162+
"This action cannot be undone!\n folders will be deleted: ${() {
163+
String result = "";
164+
for (var folder in item.solution!.folders.keys) {
165+
result +=
166+
(item.solution!.folders[folder] as String) + ", \n";
167+
}
168+
return result;
169+
}()}"),
170+
actions: [
171+
TextButton(
172+
onPressed: () {
173+
Navigator.pop(context);
174+
},
175+
child: const Text("No")),
176+
TextButton(
177+
onPressed: () {
178+
var folders = <String>[];
179+
for (var folder in item.solution!.folders.keys) {
180+
folders.add(item.solution!.slnFolderPath +
181+
Platform.pathSeparator +
182+
item.solution!.folders[folder]!);
183+
}
184+
deleteFolderWithIndicator(context, folders);
185+
// Delete the solution file too
186+
File(item.solution!.slnPath).deleteSync();
187+
188+
// Quit and refresh
189+
Navigator.pop(context);
190+
refreshRecentProjects();
191+
},
192+
child: const Text(
193+
"Delete",
194+
style: TextStyle(color: Colors.redAccent),
195+
)),
196+
],
197+
);
198+
});
199+
} else {
200+
//TODO: Handle single file delete
201+
}
202+
break;
203+
case "remove":
204+
setState(() {
205+
/// Remove item from the list without deleting the actual file
206+
RecentProjectsManager.instance.projects.remove(item);
207+
RecentProjectsManager.staticCommit();
208+
});
209+
break;
210+
}
211+
}
212+
213+
void onHistoryItemTap(HistoryItem p){
214+
if (p.type == HistoryItemType.solution) {
215+
touchFile(File(p.solution!.slnPath), p.solution!);
216+
refreshRecentProjects();
217+
loadSolution(p.solution!, context);
218+
}
219+
//TODO: Handle single file
220+
}
221+
119222
List<Widget> get projectsWidgetList {
120223
var result = <Widget>[];
121224
for (HistoryItem p in RecentProjectsManager.instance.projects) {
@@ -125,134 +228,76 @@ class _HomePageState extends State<HomePage> {
125228
//debugPrint(p.name);
126229
result.add(Card(
127230
//TODO: refactor this as a widget elsewhere, then reference that widget from here
128-
child: ListTile(
129-
130-
shape: RoundedRectangleBorder(
131-
borderRadius: BorderRadius.circular(4),
132-
side: BorderSide(color: Theme.of(context).dividerColor),
133-
),
134-
onTap: () {
135-
if (p.type == HistoryItemType.solution) {
136-
touchFile(File(p.solution!.slnPath), p.solution!);
137-
refreshRecentProjects();
138-
loadSolution(p.solution!, context);
139-
}
140-
//TODO: Handle single file
141-
},
142-
leading: p.type == HistoryItemType.solution
143-
? p.solution!.image ??
144-
const Icon(
145-
Icons.insert_drive_file,
146-
size: 48,
147-
)
148-
: const Icon(
149-
Icons.insert_drive_file,
150-
size: 48,
151-
),
152-
title: Text(
153-
p.name,
154-
style: const TextStyle(fontWeight: FontWeight.bold),
155-
),
156-
subtitle: Text("Last Modified: " +
157-
Utils.getFormattedDateTime(dateTime: p.dateModified)),
158-
trailing: PopupMenuButton<String>(
159-
onSelected: (String result) {
160-
switch (result) {
161-
case "delete":
162-
if (p.type == HistoryItemType.solution) {
163-
showDialog(
164-
context: context,
165-
builder: (BuildContext context) {
166-
return AlertDialog(
167-
title: Text("Delete ${p.name}?"),
168-
content: Text(
169-
"This action cannot be undone!\n folders will be deleted: ${() {
170-
String result = "";
171-
for (var folder in p.solution!.folders.keys) {
172-
result += (p.solution!.folders[folder]
173-
as String) +
174-
", \n";
175-
}
176-
return result;
177-
}()}"),
178-
actions: [
179-
TextButton(
180-
onPressed: () {
181-
Navigator.pop(context);
182-
},
183-
child: const Text("No")),
184-
TextButton(
185-
onPressed: () {
186-
var folders = <String>[];
187-
for (var folder
188-
in p.solution!.folders.keys) {
189-
folders.add(
190-
p.solution!.slnFolderPath +
191-
Platform.pathSeparator +
192-
p.solution!.folders[folder]!);
193-
}
194-
deleteFolderWithIndicator(
195-
context, folders);
196-
// Delete the solution file too
197-
File(p.solution!.slnPath).deleteSync();
198-
199-
// Quit and refresh
200-
Navigator.pop(context);
201-
refreshRecentProjects();
202-
},
203-
child: const Text(
204-
"Delete",
205-
style:
206-
TextStyle(color: Colors.redAccent),
207-
)),
208-
],
209-
);
210-
});
211-
} else {
212-
//TODO: Handle single file delete
213-
}
214-
break;
215-
case "remove":
216-
setState(() {
217-
/// Remove item from the list without deleting the actual file
218-
RecentProjectsManager.instance.projects.remove(p);
219-
RecentProjectsManager.staticCommit();
220-
});
221-
break;
222-
}
223-
},
224-
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
225-
const PopupMenuItem<String>(
226-
value: "delete",
227-
child: Text('Delete Project'),
228-
),
229-
const PopupMenuItem<String>(
230-
//TODO: Implement this menu
231-
value: "rename",
232-
child: Text('Rename Project'),
231+
child: isListView
232+
? ListTile(
233+
shape: RoundedRectangleBorder(
234+
borderRadius: BorderRadius.circular(4),
235+
side: BorderSide(color: Theme.of(context).dividerColor),
233236
),
234-
const PopupMenuItem<String>(
235-
//TODO: Implement this menu
236-
value: "export",
237-
child: Text('Export Project'),
237+
onTap: () => onHistoryItemTap(p),
238+
leading: p.type == HistoryItemType.solution
239+
? p.solution!.image ??
240+
const Icon(
241+
Icons.insert_drive_file,
242+
size: 48,
243+
)
244+
: const Icon(
245+
Icons.insert_drive_file,
246+
size: 48,
247+
),
248+
title: Text(
249+
p.name,
250+
style: const TextStyle(fontWeight: FontWeight.bold),
238251
),
239-
const PopupMenuItem<String>(
240-
//TODO: Implement this menu
241-
value: "remove",
242-
child: Text('Remove from list')),
243-
],
244-
))));
245-
// IconButton(
246-
// onPressed: () {
247-
// showMenu(context: context, position: RelativeRect.fromLTRB(
248-
// details.globalPosition.dx,
249-
// details.globalPosition.dy,
250-
// details.globalPosition.dx,
251-
// details.globalPosition.dy,
252-
// ), items: <PopupMenuEntry<dynamic>>[]);
253-
// },
254-
// icon: const Icon(FontAwesomeIcons.ellipsisV),
255-
// )));
252+
subtitle: Text("Last Modified: " +
253+
Utils.getFormattedDateTime(dateTime: p.dateModified)),
254+
trailing: PopupMenuButton<String>(
255+
onSelected: (String result) =>
256+
onMenuItemSelected(result, p),
257+
itemBuilder: (BuildContext context) => getPopupMenu(p)))
258+
: SizedBox(
259+
width: 128,
260+
height: 128,
261+
child: OutlinedButton(
262+
onPressed: () => onHistoryItemTap(p),
263+
child: Stack(children: [
264+
Column(
265+
mainAxisAlignment: MainAxisAlignment.center,
266+
children: [
267+
p.type == HistoryItemType.solution
268+
? p.solution!.image ??
269+
const Icon(
270+
Icons.insert_drive_file,
271+
size: 48,
272+
)
273+
: const Icon(
274+
Icons.insert_drive_file,
275+
size: 48,
276+
),
277+
Text(
278+
p.name,
279+
style: TextStyle(
280+
color: Theme.of(context)
281+
.textTheme
282+
.bodyText1!
283+
.color!,
284+
fontSize: 12.0),
285+
),
286+
Text(
287+
Utils.getFormattedDateTime(
288+
dateTime: p.dateModified),
289+
style: TextStyle(
290+
color: Theme.of(context).focusColor,
291+
fontSize: 12.0),
292+
)
293+
],
294+
),
295+
Positioned(
296+
top: 0,right: -16,
297+
child: PopupMenuButton<String>(
298+
onSelected: (String result) => onMenuItemSelected(result, p),
299+
itemBuilder: (BuildContext context) => getPopupMenu(p)))
300+
])))));
256301
}
257302
return result;
258303
}
@@ -308,6 +353,7 @@ class _HomePageState extends State<HomePage> {
308353
/// Check the last opened projects
309354
SharedPreferences.getInstance().then((inst) async {
310355
var isAutoOpen = inst.getBool("openLastProjectOnStartup");
356+
isListView = inst.getBool("isListView") ?? true;
311357
if (isAutoOpen != null && isAutoOpen) {
312358
var val = inst.getString("lastOpenedPath");
313359
debugPrint("Loading last opened $val");
@@ -384,7 +430,7 @@ class _HomePageState extends State<HomePage> {
384430
content: Text("Can't fetch update from url $url"),
385431
);
386432
ScaffoldMessenger.of(context).showSnackBar(snackBar);
387-
} on SocketException catch (err){
433+
} on SocketException catch (err) {
388434
debugPrint("[Update Checker] Can't connect to github");
389435
}
390436
}
@@ -406,8 +452,7 @@ class _HomePageState extends State<HomePage> {
406452
);
407453
});
408454
for (var path in paths) {
409-
if(path.endsWith("."))
410-
path = path.substring(0,path.length - 2);
455+
if (path.endsWith(".")) path = path.substring(0, path.length - 2);
411456
debugPrint(path);
412457
Directory target = Directory(path);
413458
text = path;
@@ -478,6 +523,8 @@ class _HomePageState extends State<HomePage> {
478523
onAddProject: onAddProject,
479524
onRefresh: refreshRecentProjects,
480525
children: projectsWidgetList,
526+
isListView: isListView,
527+
onToggleView: onToggleView,
481528
),
482529
PluginsBrowser(modulesManager: mm),
483530
SettingsPage(mm),
@@ -491,6 +538,8 @@ class _HomePageState extends State<HomePage> {
491538
onAddProject: onAddProject,
492539
onRefresh: refreshRecentProjects,
493540
children: projectsWidgetList,
541+
isListView: isListView,
542+
onToggleView: onToggleView,
494543
),
495544
));
496545
return Scaffold(
@@ -508,12 +557,13 @@ class _HomePageState extends State<HomePage> {
508557
],
509558
),
510559
body: Container(
511-
512560
decoration: BoxDecoration(
513561
image: DecorationImage(
514-
image: ThemeManager.getImage("mainBG")!.image, fit: BoxFit.cover)),
515-
child:SingleChildScrollView(child: page,)
516-
),
562+
image: ThemeManager.getImage("mainBG")!.image,
563+
fit: BoxFit.cover)),
564+
child: SingleChildScrollView(
565+
child: page,
566+
)),
517567
floatingActionButton: FloatingActionButton(
518568
onPressed: () => showCreateProjectDialog(),
519569
child: const Icon(Icons.create_new_folder),

0 commit comments

Comments
 (0)