From 5788e1fa9e8ec505d75f47aff5fed00262b7418b Mon Sep 17 00:00:00 2001 From: Yannick Mauray Date: Wed, 20 Oct 2021 01:30:11 +0200 Subject: [PATCH] Support for `quickget list`, including options. --- lib/main.dart | 61 ++++------ lib/src/model/operating_system.dart | 10 +- lib/src/model/version.dart | 6 +- lib/src/pages/operating_system_selection.dart | 113 +++++++++--------- lib/src/pages/option_selection.dart | 89 ++++++++++++++ lib/src/pages/version_selection.dart | 84 +++++-------- lib/src/widgets/home_page_buttons.dart | 68 ++++------- pubspec.yaml | 1 + 8 files changed, 237 insertions(+), 195 deletions(-) create mode 100644 lib/src/pages/option_selection.dart diff --git a/lib/main.dart b/lib/main.dart index 9527970..c9e2564 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,10 +1,10 @@ -import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:quickgui/src/app.dart'; import 'package:quickgui/src/model/operating_system.dart'; -import 'package:quiver/iterables.dart'; +import 'package:quickgui/src/model/version.dart'; +import 'package:tuple/tuple.dart'; import 'package:window_size/window_size.dart'; void main() async { @@ -12,45 +12,32 @@ void main() async { setWindowTitle('Quickgui : a flutter frontend for Quickget and Quickemu'); setWindowMinSize(const Size(692, 580)); setWindowMaxSize(const Size(692, 580)); - var config = await loadOperatingSystems(false); + gOperatingSystems = await loadOperatingSystems(false); runApp(const App()); } Future> loadOperatingSystems(bool showUbuntus) async { - var file = File('list.csv'); - var fileExists = file.existsSync(); - if (fileExists) { - Stream lines = file - .openRead() - .transform(utf8.decoder) - .transform(const LineSplitter()) - .skip(1); - await for (var line in lines) { - print(line); - } - return []; - } else { - return await Process.run('quickget', []) - .then>((process) { - var stdout = process.stdout as String; - var codes = stdout.split('\n')[1].split(' ').where((element) => - showUbuntus ? element.contains('buntu') : !element.contains('buntu')); - var names = codes.map((code) => code - .toLowerCase() - .split('-') - .map((e) => e[0].toUpperCase() + e.substring(1)) - .join(' ')); - List items = []; - if (!showUbuntus) { - items.add(OperatingSystem(name: 'Ubuntu', hasMore: true)); - } - items.addAll(zip([codes, names]) - .map((item) => OperatingSystem(code: item[0], name: item[1])) - .toList()); - items.sort((a, b) => a.name.compareTo(b.name)); + var process = await Process.run('quickget', ['list']); + var stdout = process.stdout as String; + var output = []; - return items; - }); - } + OperatingSystem? currentOperatingSystem; + Version? currentVersion; + + stdout.split('\n').skip(1).where((element) => element.isNotEmpty).map((e) => e.trim()).forEach((element) { + var supportedVersion = Tuple5.fromList(element.split(",")); + if (currentOperatingSystem?.code != supportedVersion.item2) { + currentOperatingSystem = OperatingSystem(supportedVersion.item1, supportedVersion.item2); + output.add(currentOperatingSystem!); + currentVersion = null; + } + if (currentVersion?.version != supportedVersion.item3) { + currentVersion = Version(supportedVersion.item3); + currentOperatingSystem!.versions.add(currentVersion!); + } + currentVersion!.options.add(supportedVersion.item4); + }); + + return output; } diff --git a/lib/src/model/operating_system.dart b/lib/src/model/operating_system.dart index 18a9a0c..695e8f1 100644 --- a/lib/src/model/operating_system.dart +++ b/lib/src/model/operating_system.dart @@ -1,7 +1,11 @@ +import 'package:quickgui/src/model/version.dart'; + class OperatingSystem { - OperatingSystem({required this.name, this.code, this.hasMore = false}); + OperatingSystem(this.name, this.code) : versions = []; final String name; - final String? code; - final bool hasMore; + final String code; + List versions; } + +var gOperatingSystems = []; diff --git a/lib/src/model/version.dart b/lib/src/model/version.dart index 32de562..1d99c01 100644 --- a/lib/src/model/version.dart +++ b/lib/src/model/version.dart @@ -1,6 +1,6 @@ class Version { - Version({required this.name, this.code}); + Version(this.version) : options = []; - final String name; - final String? code; + final String version; + final List options; } diff --git a/lib/src/pages/operating_system_selection.dart b/lib/src/pages/operating_system_selection.dart index 5811a39..e225ae0 100644 --- a/lib/src/pages/operating_system_selection.dart +++ b/lib/src/pages/operating_system_selection.dart @@ -1,83 +1,84 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:quickgui/src/model/operating_system.dart'; -import 'package:quiver/iterables.dart'; class OperatingSystemSelection extends StatefulWidget { - const OperatingSystemSelection({Key? key, this.showUbuntus = false}) - : super(key: key); - - final bool showUbuntus; + const OperatingSystemSelection({Key? key}) : super(key: key); @override - State createState() => - _OperatingSystemSelectionState(); + State createState() => _OperatingSystemSelectionState(); } class _OperatingSystemSelectionState extends State { - late Future> _future; + var term = ""; + final focusNode = FocusNode(); @override void initState() { + focusNode.requestFocus(); super.initState(); } @override Widget build(BuildContext context) { + var list = gOperatingSystems.where((os) => os.name.toLowerCase().contains(term.toLowerCase())).toList(); return Scaffold( appBar: AppBar( title: const Text('Select operating system'), - ), - body: FutureBuilder>( - future: _future, - builder: (context, snapshot) { - if (snapshot.hasData) { - return SingleChildScrollView( - child: Column( - children: [ - ListView.builder( - shrinkWrap: true, - itemCount: snapshot.data!.length, - itemBuilder: (context, index) { - var item = snapshot.data![index]; - return Card( - child: ListTile( - title: Text(item.name), - trailing: item.hasMore - ? const Icon(Icons.chevron_right) - : null, - onTap: () { - if (!item.hasMore) { - Navigator.of(context).pop(item); - } else { - Navigator.of(context) - .push(MaterialPageRoute( - fullscreenDialog: true, - builder: (context) => - const OperatingSystemSelection( - showUbuntus: true, - ))) - .then((selection) { - if (selection != null) { - Navigator.of(context).pop(selection); - } - }); - } + bottom: PreferredSize( + preferredSize: const Size.fromHeight(kToolbarHeight), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).canvasColor, + ), + child: Padding( + padding: const EdgeInsets.all(8), + child: Material( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.search), + Expanded( + child: TextField( + focusNode: focusNode, + decoration: const InputDecoration.collapsed(hintText: 'Search operating system'), + onChanged: (value) { + setState(() { + term = value; + }); }, ), - ); + ), + ], + ), + ), + ), + ), + ), + ), + ), + body: SingleChildScrollView( + child: Column( + children: [ + ListView.builder( + padding: const EdgeInsets.only(top: 4), + shrinkWrap: true, + itemCount: list.length, + itemBuilder: (context, index) { + var item = list[index]; + return Card( + child: ListTile( + title: Text(item.name), + onTap: () { + Navigator.of(context).pop(item); }, ), - ], - ), - ); - } else { - return const Center( - child: CircularProgressIndicator(), - ); - } - }, + ); + }, + ), + ], + ), ), ); } diff --git a/lib/src/pages/option_selection.dart b/lib/src/pages/option_selection.dart new file mode 100644 index 0000000..a3bdd7b --- /dev/null +++ b/lib/src/pages/option_selection.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:quickgui/src/model/version.dart'; + +class OptionSelection extends StatefulWidget { + const OptionSelection(this.version, {Key? key}) : super(key: key); + + final Version version; + + @override + State createState() => _OptionSelectionState(); +} + +class _OptionSelectionState extends State { + var term = ""; + final focusNode = FocusNode(); + + @override + void initState() { + focusNode.requestFocus(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + var list = widget.version.options.where((e) => e.toLowerCase().contains(term.toLowerCase())).toList(); + + return Scaffold( + appBar: AppBar( + title: const Text('Select option'), + bottom: widget.version.options.length <= 6 + ? null + : PreferredSize( + preferredSize: const Size.fromHeight(kToolbarHeight), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).canvasColor, + ), + child: Padding( + padding: const EdgeInsets.all(8), + child: Material( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.search), + Expanded( + child: TextField( + focusNode: focusNode, + decoration: const InputDecoration.collapsed(hintText: 'Search option'), + onChanged: (value) { + setState(() { + term = value; + }); + }, + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + body: SingleChildScrollView( + child: Column( + children: [ + ListView.builder( + shrinkWrap: true, + itemCount: list.length, + itemBuilder: (context, index) { + var item = list[index]; + return Card( + child: ListTile( + title: Text(item), + onTap: () { + Navigator.of(context).pop(item); + }, + ), + ); + }, + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/pages/version_selection.dart b/lib/src/pages/version_selection.dart index 2c4116c..57cbd74 100644 --- a/lib/src/pages/version_selection.dart +++ b/lib/src/pages/version_selection.dart @@ -1,9 +1,8 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:quickgui/src/model/operating_system.dart'; import 'package:quickgui/src/model/version.dart'; -import 'package:quiver/iterables.dart'; +import 'package:quickgui/src/pages/option_selection.dart'; +import 'package:tuple/tuple.dart'; class VersionSelection extends StatefulWidget { const VersionSelection({Key? key, required this.operatingSystem}) : super(key: key); @@ -15,65 +14,44 @@ class VersionSelection extends StatefulWidget { } class _VersionSelectionState extends State { - late Future> _future; - - @override - void initState() { - _future = loadOperatingSystemVersions(); - super.initState(); - } - @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Select version for ${widget.operatingSystem.name}'), ), - body: FutureBuilder>( - future: _future, - builder: (context, snapshot) { - if (snapshot.hasData) { - return SingleChildScrollView( - child: Column( - children: [ - ListView.builder( - shrinkWrap: true, - itemCount: snapshot.data!.length, - itemBuilder: (context, index) { - var item = snapshot.data![index]; - return Card( - child: ListTile( - title: Text(item.name), - onTap: () { - Navigator.of(context).pop(item); - }, - ), - ); + body: SingleChildScrollView( + child: Column( + children: [ + ListView.builder( + shrinkWrap: true, + itemCount: widget.operatingSystem.versions.length, + itemBuilder: (context, index) { + var item = widget.operatingSystem.versions[index]; + return Card( + child: ListTile( + title: Text(item.version), + onTap: () { + if (widget.operatingSystem.versions[index].options.length > 1) { + Navigator.of(context) + .push( + MaterialPageRoute(fullscreenDialog: true, builder: (context) => OptionSelection(widget.operatingSystem.versions[index]))) + .then((selection) { + if (selection != null) { + Navigator.of(context).pop(Tuple2(item, selection)); + } + }); + } else { + Navigator.of(context).pop(Tuple2(item, null)); + } }, ), - ], - ), - ); - } else { - return const Center( - child: CircularProgressIndicator(), - ); - } - }, + ); + }, + ), + ], + ), ), ); } - - Future> loadOperatingSystemVersions() async { - return Process.run('quickget', [widget.operatingSystem.code!, 'dummy']).then>((process) { - var stdout = process.stdout as String; - var versions = stdout.split('\n')[1].split(' '); - var names = - versions.map((version) => version.toLowerCase().split('-').map((e) => e[0].toUpperCase() + e.substring(1)).join(' ').split('_').join('.')).toList(); - List items = []; - items.addAll(zip([versions, names]).map((e) => Version(code: e[0], name: e[1]))); - - return items; - }); - } } diff --git a/lib/src/widgets/home_page_buttons.dart b/lib/src/widgets/home_page_buttons.dart index 803e457..54eebfc 100644 --- a/lib/src/widgets/home_page_buttons.dart +++ b/lib/src/widgets/home_page_buttons.dart @@ -7,6 +7,7 @@ import 'package:quickgui/src/model/version.dart'; import 'package:quickgui/src/pages/operating_system_selection.dart'; import 'package:quickgui/src/pages/version_selection.dart'; import 'package:quickgui/src/widgets/home_page_button.dart'; +import 'package:tuple/tuple.dart'; class HomePageButtons extends StatefulWidget { const HomePageButtons({Key? key}) : super(key: key); @@ -16,10 +17,16 @@ class HomePageButtons extends StatefulWidget { } class _HomePageButtonsState extends State { + OperatingSystem? _selectedOperatingSystem; + Version? _selectedVersion; + String? _selectedOption; + @override Widget build(BuildContext context) { - OperatingSystem? _selectedOperatingSystem; - Version? _selectedVersion; + var _versionButtonLabel = _selectedVersion?.version ?? 'Select...'; + if (_selectedOption != null) { + _versionButtonLabel = "$_versionButtonLabel ($_selectedOption)"; + } return Row( children: [ HomePageButton( @@ -27,14 +34,13 @@ class _HomePageButtonsState extends State { text: _selectedOperatingSystem?.name ?? 'Select...', onPressed: () { Navigator.of(context) - .push(MaterialPageRoute( - fullscreenDialog: true, - builder: (context) => const OperatingSystemSelection())) + .push(MaterialPageRoute(fullscreenDialog: true, builder: (context) => const OperatingSystemSelection())) .then((selection) { if (selection != null) { setState(() { _selectedOperatingSystem = selection; _selectedVersion = null; + _selectedOption = null; }); } }); @@ -42,19 +48,19 @@ class _HomePageButtonsState extends State { ), HomePageButton( label: "Version", - text: _selectedVersion?.name ?? 'Select...', + text: _versionButtonLabel, //_selectedVersion?.version ?? 'Select...', onPressed: (_selectedOperatingSystem != null) ? () { Navigator.of(context) - .push(MaterialPageRoute( + .push>(MaterialPageRoute( fullscreenDialog: true, - builder: (context) => VersionSelection( - operatingSystem: _selectedOperatingSystem!), + builder: (context) => VersionSelection(operatingSystem: _selectedOperatingSystem!), )) .then((selection) { if (selection != null) { setState(() { - _selectedVersion = selection; + _selectedVersion = selection.item1; + _selectedOption = selection.item2; }); } }); @@ -68,14 +74,9 @@ class _HomePageButtonsState extends State { ? null : () async { showLoadingIndicator(text: 'Downloading'); - await Process.run('quickget', [ - _selectedOperatingSystem!.code!, - _selectedVersion!.code! - ]); + await Process.run('quickget', [_selectedOperatingSystem!.code, _selectedVersion!.version]); hideLoadingIndicator(); - showDoneDialog( - operatingSystem: _selectedOperatingSystem!.code!, - version: _selectedVersion!.code!); + showDoneDialog(operatingSystem: _selectedOperatingSystem!.code, version: _selectedVersion!.version); }, ), ], @@ -101,21 +102,14 @@ class _HomePageButtonsState extends State { children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 32), - child: Text('Downloading...', - style: Theme.of(context) - .textTheme - .bodyText1 - ?.copyWith(color: Colors.white)), + child: Text('Downloading...', style: Theme.of(context).textTheme.bodyText1?.copyWith(color: Colors.white)), ), const CircularProgressIndicator(), Padding( padding: const EdgeInsets.symmetric(vertical: 32), child: Text( 'Target : ${Directory.current.absolute.path}', - style: Theme.of(context) - .textTheme - .bodyText1 - ?.copyWith(color: Colors.white), + style: Theme.of(context).textTheme.bodyText1?.copyWith(color: Colors.white), ), ), ], @@ -130,8 +124,7 @@ class _HomePageButtonsState extends State { Navigator.of(context).pop(); } - void showDoneDialog( - {required String operatingSystem, required String version}) { + void showDoneDialog({required String operatingSystem, required String version}) { showDialog( context: context, barrierDismissible: false, @@ -149,18 +142,10 @@ class _HomePageButtonsState extends State { children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 32), - child: Text('Done !', - style: Theme.of(context) - .textTheme - .bodyText1 - ?.copyWith(color: Colors.white)), + child: Text('Done !', style: Theme.of(context).textTheme.bodyText1?.copyWith(color: Colors.white)), ), - Text( - 'Now run "quickemu --vm $operatingSystem-$version" to start the VM', - style: Theme.of(context) - .textTheme - .bodyText1 - ?.copyWith(color: Colors.white)), + Text('Now run "quickemu --vm $operatingSystem-$version" to start the VM', + style: Theme.of(context).textTheme.bodyText1?.copyWith(color: Colors.white)), Padding( padding: const EdgeInsets.symmetric(vertical: 32), child: ElevatedButton( @@ -169,10 +154,7 @@ class _HomePageButtonsState extends State { }, child: Text( 'Dismiss', - style: Theme.of(context) - .textTheme - .bodyText1 - ?.copyWith(color: Colors.white), + style: Theme.of(context).textTheme.bodyText1?.copyWith(color: Colors.white), ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index ceb6113..795493e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: url: git://github.com/google/flutter-desktop-embedding.git path: plugins/window_size quiver: ^3.0.1+1 + tuple: ^2.0.0 dev_dependencies: flutter_test: