diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 0000000..d4d23fc Binary files /dev/null and b/assets/images/logo.png differ diff --git a/lib/main.dart b/lib/main.dart index 7a77d2e..0d0b3e1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,7 +4,7 @@ import 'package:window_size/window_size.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - setWindowTitle('QuickUI : a flutter frontend for Quickget and Quickemu'); + setWindowTitle('Quickgui : a flutter frontend for Quickget and Quickemu'); setWindowMinSize(const Size(692, 580)); setWindowMaxSize(const Size(692, 580)); runApp(const App()); diff --git a/lib/src/app.dart b/lib/src/app.dart index 795b584..bc99648 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -7,11 +7,10 @@ class App extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.pink, ), - home: const MainPage(title: 'QuickUI - A Flutter frontend for Quickget and Quickemu'), + home: const MainPage(title: 'Quickgui - A Flutter frontend for Quickget and Quickemu'), ); } } diff --git a/lib/src/model/operating_system.dart b/lib/src/model/operating_system.dart new file mode 100644 index 0000000..18a9a0c --- /dev/null +++ b/lib/src/model/operating_system.dart @@ -0,0 +1,7 @@ +class OperatingSystem { + OperatingSystem({required this.name, this.code, this.hasMore = false}); + + final String name; + final String? code; + final bool hasMore; +} diff --git a/lib/src/model/version.dart b/lib/src/model/version.dart new file mode 100644 index 0000000..32de562 --- /dev/null +++ b/lib/src/model/version.dart @@ -0,0 +1,6 @@ +class Version { + Version({required this.name, this.code}); + + final String name; + final String? code; +} diff --git a/lib/src/pages/main_page.dart b/lib/src/pages/main_page.dart index b8fa9e7..d8f4a2e 100644 --- a/lib/src/pages/main_page.dart +++ b/lib/src/pages/main_page.dart @@ -1,5 +1,11 @@ +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:quickgui/src/pages/operating_system_page.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'; class MainPage extends StatefulWidget { @@ -12,6 +18,9 @@ class MainPage extends StatefulWidget { } class _MainPageState extends State { + OperatingSystem? _selectedOperatingSystem; + Version? _selectedVersion; + @override Widget build(BuildContext context) { return Scaffold( @@ -21,10 +30,13 @@ class _MainPageState extends State { height: 250, child: Flex( direction: Axis.vertical, - children: const [ + children: [ Expanded( child: Center( - child: Text("QuickGui"), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Image.asset('assets/images/logo.png'), + ), ), ), ], @@ -36,23 +48,60 @@ class _MainPageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ - Row( - children: [ - HomePageButton( - text: "Operating system", - onPressed: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) => const OperatingSystemPpage())); - }, - ), - const HomePageButton( - text: "Version", - onPressed: null, - ), - const HomePageButton( - text: "Download", - onPressed: null, - ), - ], + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + HomePageButton( + label: "Operating system", + text: _selectedOperatingSystem?.name ?? 'Select...', + onPressed: () { + Navigator.of(context) + .push(MaterialPageRoute(fullscreenDialog: true, builder: (context) => const OperatingSystemSelection())) + .then((selection) { + if (selection != null) { + setState(() { + _selectedOperatingSystem = selection; + _selectedVersion = null; + }); + } + }); + }, + ), + HomePageButton( + label: "Version", + text: _selectedVersion?.name ?? 'Select...', + onPressed: (_selectedOperatingSystem != null) + ? () { + Navigator.of(context) + .push(MaterialPageRoute( + fullscreenDialog: true, + builder: (context) => VersionSelection(operatingSystem: _selectedOperatingSystem!), + )) + .then((selection) { + if (selection != null) { + setState(() { + _selectedVersion = selection; + }); + } + }); + } + : null, + ), + HomePageButton( + text: 'Download', + onPressed: (_selectedVersion == null) + ? null + : () async { + showLoadingIndicator(text: 'Downloading'); + await Process.run('quickget', [_selectedOperatingSystem!.code!, _selectedVersion!.code!]); + print('Finished !'); + hideOpenDialog(); + Navigator.of(context).pop(); + }, + ), + ], + ), ) ], ), @@ -62,4 +111,44 @@ class _MainPageState extends State { ), ); } + + void showLoadingIndicator({String text = ''}) { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => WillPopScope( + onWillPop: () async => false, + child: AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8.0)), + ), + backgroundColor: Colors.black87, + content: SizedBox( + height: 200, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 32), + 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), + ), + ), + ], + ), + ), + ), + ), + ); + } + + void hideOpenDialog() { + Navigator.of(context).pop(); + } } diff --git a/lib/src/pages/operating_system_selection.dart b/lib/src/pages/operating_system_selection.dart new file mode 100644 index 0000000..05420d7 --- /dev/null +++ b/lib/src/pages/operating_system_selection.dart @@ -0,0 +1,94 @@ +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; + + @override + State createState() => _OperatingSystemSelectionState(); +} + +class _OperatingSystemSelectionState extends State { + late Future> _future; + + @override + void initState() { + _future = loadOperatingSystems(widget.showUbuntus); + super.initState(); + } + + @override + Widget build(BuildContext context) { + 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); + } + }); + } + }, + ), + ); + }, + ), + ], + ), + ); + } else { + return const Center( + child: CircularProgressIndicator(), + ); + } + }, + ), + ); + } + + Future> loadOperatingSystems(bool showUbuntus) async { + return 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)); + + return items; + }); + } +} diff --git a/lib/src/pages/version_selection.dart b/lib/src/pages/version_selection.dart new file mode 100644 index 0000000..2c4116c --- /dev/null +++ b/lib/src/pages/version_selection.dart @@ -0,0 +1,79 @@ +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'; + +class VersionSelection extends StatefulWidget { + const VersionSelection({Key? key, required this.operatingSystem}) : super(key: key); + + final OperatingSystem operatingSystem; + + @override + _VersionSelectionState createState() => _VersionSelectionState(); +} + +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); + }, + ), + ); + }, + ), + ], + ), + ); + } 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_button.dart b/lib/src/widgets/home_page_button.dart index 54762dd..9df309d 100644 --- a/lib/src/widgets/home_page_button.dart +++ b/lib/src/widgets/home_page_button.dart @@ -1,33 +1,47 @@ import 'package:flutter/material.dart'; class HomePageButton extends StatelessWidget { - final String text; - final VoidCallback? onPressed; - const HomePageButton({ Key? key, + this.label, required this.text, this.onPressed, }) : super(key: key); + final String? label; + final String text; + final VoidCallback? onPressed; + @override Widget build(BuildContext context) { return Expanded( flex: 1, child: Padding( - padding: const EdgeInsets.all(24.0), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - primary: Colors.white, - onPrimary: Colors.pink, - ), - onPressed: onPressed, - child: Padding( - padding: const EdgeInsets.fromLTRB(0, 16, 0, 16), - child: Text( - text.toUpperCase(), + padding: const EdgeInsets.fromLTRB(12, 24, 12, 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 24), + child: Center( + child: Text( + label?.toUpperCase() ?? '', + style: Theme.of(context).textTheme.subtitle2?.copyWith(color: Colors.white), + ), + ), ), - ), + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.white, + onPrimary: Colors.pink, + ), + onPressed: onPressed, + child: Padding( + padding: const EdgeInsets.fromLTRB(0, 16, 0, 16), + child: Text(text), + ), + ), + ], ), ), ); diff --git a/packaging/quickgui_1.0.0-1.deb b/packaging/quickgui_1.0.0-1.deb new file mode 100644 index 0000000..a4ebca1 Binary files /dev/null and b/packaging/quickgui_1.0.0-1.deb differ diff --git a/packaging/quickgui_1.0.0-1/DEBIAN/control b/packaging/quickgui_1.0.0-1/DEBIAN/control new file mode 100644 index 0000000..69d1d5b --- /dev/null +++ b/packaging/quickgui_1.0.0-1/DEBIAN/control @@ -0,0 +1,8 @@ +Package: quickgui +Version: 1.0.0-1 +Section: base +Priority: optional +Architecture: amd64 +Maintainer: Yannick Mauray +Description: Flutter frontend for Quickget + diff --git a/packaging/quickgui_1.0.0-1/usr/local/bin/quickgui b/packaging/quickgui_1.0.0-1/usr/local/bin/quickgui new file mode 100755 index 0000000..c2e8e9e Binary files /dev/null and b/packaging/quickgui_1.0.0-1/usr/local/bin/quickgui differ diff --git a/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/AssetManifest.json b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/AssetManifest.json new file mode 100644 index 0000000..43a045e --- /dev/null +++ b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/AssetManifest.json @@ -0,0 +1 @@ +{"assets/images/logo.png":["assets/images/logo.png"],"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"]} \ No newline at end of file diff --git a/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/FontManifest.json b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/FontManifest.json new file mode 100644 index 0000000..464ab58 --- /dev/null +++ b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/FontManifest.json @@ -0,0 +1 @@ +[{"family":"MaterialIcons","fonts":[{"asset":"fonts/MaterialIcons-Regular.otf"}]},{"family":"packages/cupertino_icons/CupertinoIcons","fonts":[{"asset":"packages/cupertino_icons/assets/CupertinoIcons.ttf"}]}] \ No newline at end of file diff --git a/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/NOTICES.Z b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/NOTICES.Z new file mode 100644 index 0000000..dd1026c Binary files /dev/null and b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/NOTICES.Z differ diff --git a/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/assets/images/logo.png b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/assets/images/logo.png new file mode 100644 index 0000000..d4d23fc Binary files /dev/null and b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/assets/images/logo.png differ diff --git a/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/fonts/MaterialIcons-Regular.otf b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/fonts/MaterialIcons-Regular.otf new file mode 100644 index 0000000..3246ad5 Binary files /dev/null and b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/fonts/MaterialIcons-Regular.otf differ diff --git a/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf new file mode 100644 index 0000000..79ba7ea Binary files /dev/null and b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf differ diff --git a/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/version.json b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/version.json new file mode 100644 index 0000000..d5680c3 --- /dev/null +++ b/packaging/quickgui_1.0.0-1/usr/local/data/flutter_assets/version.json @@ -0,0 +1 @@ +{"app_name":"quickgui","version":"1.0.0","build_number":"1"} \ No newline at end of file diff --git a/packaging/quickgui_1.0.0-1/usr/local/data/icudtl.dat b/packaging/quickgui_1.0.0-1/usr/local/data/icudtl.dat new file mode 100644 index 0000000..01ce223 Binary files /dev/null and b/packaging/quickgui_1.0.0-1/usr/local/data/icudtl.dat differ diff --git a/packaging/quickgui_1.0.0-1/usr/local/lib/libapp.so b/packaging/quickgui_1.0.0-1/usr/local/lib/libapp.so new file mode 100644 index 0000000..084716f Binary files /dev/null and b/packaging/quickgui_1.0.0-1/usr/local/lib/libapp.so differ diff --git a/packaging/quickgui_1.0.0-1/usr/local/lib/libflutter_linux_gtk.so b/packaging/quickgui_1.0.0-1/usr/local/lib/libflutter_linux_gtk.so new file mode 100644 index 0000000..a0c4b16 Binary files /dev/null and b/packaging/quickgui_1.0.0-1/usr/local/lib/libflutter_linux_gtk.so differ diff --git a/packaging/quickgui_1.0.0-1/usr/local/lib/libwindow_size_plugin.so b/packaging/quickgui_1.0.0-1/usr/local/lib/libwindow_size_plugin.so new file mode 100644 index 0000000..9abe9ec Binary files /dev/null and b/packaging/quickgui_1.0.0-1/usr/local/lib/libwindow_size_plugin.so differ diff --git a/pubspec.yaml b/pubspec.yaml index 9c224c2..92b2391 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,6 +38,7 @@ dependencies: git: url: git://github.com/google/flutter-desktop-embedding.git path: plugins/window_size + quiver: ^3.0.1+1 dev_dependencies: flutter_test: @@ -65,6 +66,8 @@ flutter: # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg + assets: + - assets/images/logo.png # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware.