diff --git a/.gitignore b/.gitignore index 0b6607d..3f1b889 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ list.csv *.iso *.conf *.mo +RELEASING.md +BUILDING.md diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8c3bb03 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "assets/quickemu-icons"] + path = assets/quickemu-icons + url = https://github.com/quickemu-project/quickemu-icons.git + branch = quickgui diff --git a/BUILDING.md b/BUILDING.md deleted file mode 100644 index 7867318..0000000 --- a/BUILDING.md +++ /dev/null @@ -1 +0,0 @@ -flutter pub add window_size --git-url git://github.com/google/flutter-desktop-embedding.git --git-path plugins/window_size diff --git a/Makefile b/Makefile index f9b6926..fe3419c 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ BASE_NAME := quickgui-$(VERSION) BUILD_DIR := $(BUILD_ROOT)/$(BASE_NAME) BIN_TAR := $(BUILD_ROOT)/$(BASE_NAME).tar SRC_TAR := $(BUILD_ROOT)/$(BASE_NAME)-src.tar -FLUTTER := /usr/local/bin/flutter +FLUTTER := /opt/flutter/bin/flutter all: version bin @@ -25,7 +25,7 @@ bin: quickgui mkdir -p $(BUILD_DIR) cp -a build/linux/x64/release/bundle/* $(BUILD_DIR) cp -a assets/resources $(BUILD_DIR) - tar -C $(BUILD_ROOT) -c -v -f $(BIN_TAR) $(BASE_NAME) + tar -C $(BUILD_ROOT) -c -v --exclude "quickemu-icons/*.png" -f $(BIN_TAR) $(BASE_NAME) xz -z $(BIN_TAR) src: diff --git a/RELEASING.md b/RELEASING.md deleted file mode 100644 index ff2ba4c..0000000 --- a/RELEASING.md +++ /dev/null @@ -1,23 +0,0 @@ -* bump the version number in pubspec.yaml (ex: version: 1.0.5) -* push to github -* create and push version tag (ex: v1.0.5) -* create a release on GitHub -* build the tarball (`make`) -* upload the tarball to github - -* `cd /mnt/data/dev/debianpackages/quickgui.deb/quickgui` -* `head debian/changelog` to check the latest changelog -* `dch -v a.b.c "New changelog message"` -* `nano debian/changelog` to change the release (ex: 1.0.5-1~focal1.0 focal) -* `uscan --noconf --force-download --rename --download-current-version --destdir=.. --verbose` to download the tarball properly -* `dpkg-buildpackage -d -S -sa` to build the changes file (the name of the key to use is in ~/.config/dpkg/buildpackage.conf) -* `nano debian/changelog` to change the release (ex: 1.0.5-1~hirsute1.0 hirsute) -* `dpkg-buildpackage -d -S -sa` -* `nano debian/changelog` to change the release (ex: 1.0.5-1~impish1.0 impish) -* `dpkg-buildpackage -d -S -sa` -* `nano debian/changelog` to change the release (ex: 1.0.5-1~jammy.0 jammy) -* `dpkg-buildpackage -d -S -sa` -* `dput ppa:yannick-mauray/quickgui ../quickgui_1.0.5-1~focal1.0_source.changes` -* `dput ppa:yannick-mauray/quickgui ../quickgui_1.0.5-1~hirsute1.0_source.changes` -* `dput ppa:yannick-mauray/quickgui ../quickgui_1.0.5-1~impish1.0_source.changes` -* `dput ppa:yannick-mauray/quickgui ../quickgui_1.0.5-1~jammy1.0_source.changes` diff --git a/assets/i18n/en.po b/assets/i18n/en.po index bb2e860..99f0b4c 100644 --- a/assets/i18n/en.po +++ b/assets/i18n/en.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: quickgui\n" "POT-Creation-Date: 2021-09-29 09:59+0200\n" -"PO-Revision-Date: 2021-11-11 16:42+0100\n" +"PO-Revision-Date: 2022-02-24 23:38+0100\n" "Last-Translator: Yannick Mauray\n" "Language-Team: \n" "Language: en\n" @@ -69,6 +69,9 @@ msgstr "Downloading...{0}%" msgid "Downloading {0}" msgstr "Downloading {0}" +msgid "Download cancelled" +msgstr "Download cancelled" + #, fuzzy msgid "Download complete" msgstr "Download complete" @@ -77,6 +80,10 @@ msgstr "Download complete" msgid "Download of {0} has completed." msgstr "Download of {0} has completed." +#, fuzzy +msgid "Download of {0} has been cancelled." +msgstr "Download of {0} has been cancelled." + #, fuzzy msgid "Manage existing machines" msgstr "Manage existing machines" diff --git a/assets/i18n/es.po b/assets/i18n/es.po new file mode 100644 index 0000000..302ab77 --- /dev/null +++ b/assets/i18n/es.po @@ -0,0 +1,166 @@ +msgid "" +msgstr "" +"Project-Id-Version: quickgui\n" +"POT-Creation-Date: 2021-09-29 09:59+0200\n" +"PO-Revision-Date: 2021-12-19 19:23+0100\n" +"Last-Translator: José Miguel Manzano\n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.0\n" +"X-Poedit-Basepath: .\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, fuzzy +msgid "Quickgui : a Flutter frontend for Quickget and Quickemu" +msgstr "Quickgui : una aplicación Flutter para Quickget y Quickemu" + +#, fuzzy +msgid "Main menu" +msgstr "Menu principal" + +#, fuzzy +msgid "Use dark mode" +msgstr "Utilizar el modo oscuro" + +#, fuzzy +msgid "Cancel" +msgstr "Cancelar" + +#, fuzzy +msgid "Click to use SPICE display" +msgstr "Pulsa para utilizar SPICE display" + +#, fuzzy +msgid "Create new machines" +msgstr "Crear nuevas máquinas" + +#, fuzzy +msgid "Dismiss" +msgstr "Descartar" + +#, fuzzy +msgid "Done !" +msgstr "Hecho !" + +#, fuzzy +msgid "Download" +msgstr "Descargar" + +#, fuzzy +msgid "Download finished." +msgstr "Descarga completa." + +#, fuzzy +msgid "Downloader" +msgstr "Descargas" + +#, fuzzy +msgid "Downloading (no progress available)..." +msgstr "Descargando (progreso no disponible)..." + +#, fuzzy +msgid "Downloading...{0}%" +msgstr "Descargando...{0}%" + +#, fuzzy +msgid "Downloading {0}" +msgstr "Descargando {0}" + +#, fuzzy +msgid "Download complete" +msgstr "Descarga completa" + +#, fuzzy +msgid "Download of {0} has completed." +msgstr "La descarga de {0} se ha completado." + +#, fuzzy +msgid "Manage existing machines" +msgstr "Gestionar máquinas existentes" + +#, fuzzy +msgid "Manager" +msgstr "Gestionar" + +#, fuzzy +msgid "{0} Mbs downloaded" +msgstr "{0} Mbs descargados" + +#, fuzzy +msgid "Now run {0} to start the VM" +msgstr "Ahora ejecuta {0} para iniciar la VM" + +#, fuzzy +msgid "OK" +msgstr "OK" + +#, fuzzy +msgid "Operating system" +msgstr "Sistema operativo" + +#, fuzzy +msgid "Search operating system" +msgstr "Buscar sistema operativo" + +#, fuzzy +msgid "Search option" +msgstr "Opción de busqueda" + +#, fuzzy +msgid "Select operating system" +msgstr "Selecciona sistema operativo" + +#, fuzzy +msgid "Select option" +msgstr "Selecciona opción" + +#, fuzzy +msgid "Select version for {0}" +msgstr "Selecciona versión para {0}" + +#, fuzzy +msgid "Select..." +msgstr "Selecciona..." + +#, fuzzy +msgid "SPICE port" +msgstr "Puerto SPICE" + +#, fuzzy +msgid "SSH port" +msgstr "Puerto SSH" + +#, fuzzy +msgid "Stop The Virtual Machine?" +msgstr "¿Detener la Máquina Virtual?" + +#, fuzzy +msgid "Target folder : {0}" +msgstr "Directorio de destino : {0}" + +#, fuzzy +msgid "Use SPICE display" +msgstr "Usar SPICE display" + +#, fuzzy +msgid "Using SPICE display" +msgstr "Usando SPICE display" + +#, fuzzy +msgid "Version" +msgstr "Versión" + +#, fuzzy +msgid "Waiting for download to start" +msgstr "Esperando que comience la descarga" + +#, fuzzy +msgid "You are about to terminate the virtual machine {0}" +msgstr "Estás a punto de cerrar la máquina virtual {0}" + +#, fuzzy +msgid "Language" +msgstr "Lenguaje" diff --git a/assets/i18n/fr.po b/assets/i18n/fr.po index 608e992..62d16d8 100644 --- a/assets/i18n/fr.po +++ b/assets/i18n/fr.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: quickgui\n" "POT-Creation-Date: 2021-09-29 09:59+0200\n" -"PO-Revision-Date: 2021-12-02 20:23+0000\n" +"PO-Revision-Date: 2022-02-24 23:39+0100\n" "Last-Translator: Yannick Mauray\n" "Language-Team: Yannick Mauray\n" "Language: fr\n" @@ -80,6 +80,9 @@ msgstr "Téléchargement terminé" msgid "Download of {0} has completed." msgstr "Le téléchargement de {0} est terminé." +msgid "Download of {0} has been cancelled." +msgstr "Le téléchargement de {0} a été annulé." + #, fuzzy msgid "Manage existing machines" msgstr "Gérer les machines existantes" diff --git a/assets/i18n/quickgui.pot b/assets/i18n/quickgui.pot index a515fc1..de851c6 100644 --- a/assets/i18n/quickgui.pot +++ b/assets/i18n/quickgui.pot @@ -64,6 +64,9 @@ msgstr "" msgid "Download of {0} has completed." msgstr "" +msgid "Download of {0} has been cancelled." +msgstr "" + msgid "Manage existing machines" msgstr "" diff --git a/assets/quickemu-icons b/assets/quickemu-icons new file mode 160000 index 0000000..1fab2a7 --- /dev/null +++ b/assets/quickemu-icons @@ -0,0 +1 @@ +Subproject commit 1fab2a77f7465dd163fcb0087c2927d663d151bb diff --git a/lib/main.dart b/lib/main.dart index a03ffc7..8c5e0f4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; -import 'package:quickgui/src/globals.dart'; import 'package:tuple/tuple.dart'; import 'package:window_size/window_size.dart'; diff --git a/lib/src/app.dart b/lib/src/app.dart index acfd85b..0b284c0 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -1,4 +1,3 @@ -import 'dart:ffi'; import 'dart:io'; import 'package:flutter/material.dart'; @@ -32,8 +31,10 @@ class _AppState extends State with PreferencesMixin { appSettings.setActiveLocaleSilently( snapshot.data?.getString(prefCurrentLocale) ?? Platform.localeName); - appSettings.useDarkModeSilently = - snapshot.data!.getBool(prefThemeMode) as bool; + var pref = snapshot.data!.getBool(prefThemeMode); + if (pref != null) { + appSettings.useDarkModeSilently = pref; + } return Consumer( builder: (context, appSettings, _) => MaterialApp( theme: ThemeData(primarySwatch: Colors.pink), diff --git a/lib/src/pages/downloader.dart b/lib/src/pages/downloader.dart index ac0d381..c76e21e 100644 --- a/lib/src/pages/downloader.dart +++ b/lib/src/pages/downloader.dart @@ -33,6 +33,7 @@ class _DownloaderState extends State { final notificationsClient = NotificationsClient(); final wgetPattern = RegExp("( [0-9.]+%)"); final macRecoveryPattern = RegExp("([0-9]+\\.[0-9])"); + final ariaPattern = RegExp("([0-9.]+%)"); late final Stream _progressStream; bool _downloadFinished = false; var controller = StreamController(); @@ -55,6 +56,17 @@ class _DownloaderState extends State { } } + void parseAriaProgress(String line) { + var matches = ariaPattern.allMatches(line).toList(); + if (matches.isNotEmpty) { + var percent = matches[0].group(1); + if (percent != null) { + var value = double.parse(percent.replaceAll('%', '')) / 100.0; + controller.add(value); + } + } + } + void parseMacRecoveryProgress(String line) { var matches = macRecoveryPattern.allMatches(line).toList(); if (matches.isNotEmpty) { @@ -77,7 +89,11 @@ class _DownloaderState extends State { } else if (widget.option!.downloader == 'zsync') { controller.add(-1); } else if (widget.option!.downloader == 'macrecovery') { - process.stdout.transform(utf8.decoder).forEach(parseMacRecoveryProgress); + process.stdout + .transform(utf8.decoder) + .forEach(parseMacRecoveryProgress); + } else if (widget.option!.downloader == 'aria2c') { + process.stderr.transform(utf8.decoder).forEach(parseAriaProgress); } process.exitCode.then((value) { @@ -86,13 +102,18 @@ class _DownloaderState extends State { setState(() { _downloadFinished = true; notificationsClient.notify( - _cancelled ? context.t('Download cancelled') : context.t('Download complete'), + _cancelled + ? context.t('Download cancelled') + : context.t('Download complete'), body: _cancelled ? context.t( - 'Download of {0} has completed.', + 'Download of {0} has been canceled.', args: [widget.operatingSystem.name], ) - : context.t('Download cancelled.'), + : context.t( + 'Download of {0} has completed.', + args: [widget.operatingSystem.name], + ), appName: 'Quickgui', expireTimeoutMs: 10000, /* 10 seconds */ ); @@ -111,8 +132,12 @@ class _DownloaderState extends State { return Scaffold( appBar: AppBar( title: Text( - context.t('Downloading {0}', - args: ['${widget.operatingSystem.name} ${widget.version.version}' + (widget.option!.option.isNotEmpty ? ' (${widget.option!.option})' : '')]), + context.t('Downloading {0}', args: [ + '${widget.operatingSystem.name} ${widget.version.version}' + + (widget.option!.option.isNotEmpty + ? ' (${widget.option!.option})' + : '') + ]), ), automaticallyImplyLeading: false, ), @@ -122,7 +147,11 @@ class _DownloaderState extends State { child: StreamBuilder( stream: _progressStream, builder: (context, AsyncSnapshot snapshot) { - var data = !snapshot.hasData || widget.option!.downloader != 'wget' ? null : snapshot.data; + var data = !snapshot.hasData || + widget.option!.downloader != 'wget' || + widget.option!.downloader != 'aria2c' + ? null + : snapshot.data; return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -137,7 +166,8 @@ class _DownloaderState extends State { ), Padding( padding: const EdgeInsets.only(top: 32), - child: Text(context.t('Target folder : {0}', args: [Directory.current.path])), + child: Text(context.t('Target folder : {0}', + args: [Directory.current.path])), ), ], ); diff --git a/lib/src/pages/main_page.dart b/lib/src/pages/main_page.dart index 61c66ed..ca0c469 100644 --- a/lib/src/pages/main_page.dart +++ b/lib/src/pages/main_page.dart @@ -17,7 +17,8 @@ class _MainPageState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - setWindowTitle(context.t('Quickgui : a Flutter frontend for Quickget and Quickemu')); + setWindowTitle( + context.t('Quickgui : a Flutter frontend for Quickget and Quickemu')); } @override diff --git a/lib/src/pages/manager.dart b/lib/src/pages/manager.dart index 082702e..1028694 100644 --- a/lib/src/pages/manager.dart +++ b/lib/src/pages/manager.dart @@ -61,7 +61,8 @@ class _ManagerState extends State with PreferencesMixin { } Directory.current = pref; }); - Future.delayed(Duration.zero, () => _getVms(context)); // Reload VM list when we enter the page. + Future.delayed(Duration.zero, + () => _getVms(context)); // Reload VM list when we enter the page. }); refreshTimer = Timer.periodic(const Duration(seconds: 5), (Timer t) { _getVms(context); @@ -78,7 +79,8 @@ class _ManagerState extends State with PreferencesMixin { // Find out which terminal emulator we have set as the default. ProcessResult result = await Process.run('which', ['x-terminal-emulator']); if (result.exitCode == 0) { - String terminalEmulator = await File(result.stdout.toString().trim()).resolveSymbolicLinks(); + String terminalEmulator = + await File(result.stdout.toString().trim()).resolveSymbolicLinks(); terminalEmulator = path.basenameWithoutExtension(terminalEmulator); if (_supportedTerminalEmulators.contains(terminalEmulator)) { setState(() { @@ -130,7 +132,8 @@ class _ManagerState extends State with PreferencesMixin { List currentVms = []; Map activeVms = {}; - await for (var entity in Directory.current.list(recursive: false, followLinks: true)) { + await for (var entity + in Directory.current.list(recursive: false, followLinks: true)) { if ((entity.path.endsWith('.conf')) && (_isValidConf(entity.path))) { String name = path.basenameWithoutExtension(entity.path); currentVms.add(name); @@ -169,7 +172,9 @@ class _ManagerState extends State with PreferencesMixin { Widget _buildVmList() { List _widgetList = []; - final Color buttonColor = Theme.of(context).brightness == Brightness.dark ? Colors.white70 : Theme.of(context).colorScheme.primary; + final Color buttonColor = Theme.of(context).brightness == Brightness.dark + ? Colors.white70 + : Theme.of(context).colorScheme.primary; _widgetList.add( Row( mainAxisAlignment: MainAxisAlignment.center, @@ -181,7 +186,8 @@ class _ManagerState extends State with PreferencesMixin { width: 8, ), ElevatedButton( - style: ElevatedButton.styleFrom(primary: Theme.of(context).canvasColor, onPrimary: buttonColor), + style: ElevatedButton.styleFrom( + primary: Theme.of(context).canvasColor, onPrimary: buttonColor), onPressed: () async { String? result = await FilePicker.platform.getDirectoryPath(); if (result != null) { @@ -276,7 +282,9 @@ class _ManagerState extends State with PreferencesMixin { context: context, builder: (BuildContext context) => AlertDialog( title: Text(context.t('Stop The Virtual Machine?')), - content: Text(context.t('You are about to terminate the virtual machine', args: [currentVm])), + content: Text(context.t( + 'You are about to terminate the virtual machine', + args: [currentVm])), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), @@ -300,44 +308,49 @@ class _ManagerState extends State with PreferencesMixin { }, ), IconButton( - icon: Icon( - Icons.delete, - color: active ? null : buttonColor, - semanticLabel: 'Delete' - ), - onPressed: active ? null : () { - showDialog( - context: context, - builder: (BuildContext context) => AlertDialog( - title: Text('Delete ' + currentVm), - content: Text( - 'You are about to delete ' + currentVm + '. This cannot be undone. ' + - 'Would you like to delete the disk image but keep the ' + - 'configuration, or delete the whole VM?' - ), - actions: [ - TextButton( - child: Text('Cancel'), - onPressed: () => Navigator.pop(context, 'cancel'), - ), - TextButton( - child: Text('Delete disk image'), - onPressed: () => Navigator.pop(context, 'disk'), - ), - TextButton( - child: Text('Delete whole VM'), - onPressed: () => Navigator.pop(context, 'vm'), - ) // set up the AlertDialog - ], - ), - ).then((result) async { - result = result ?? 'cancel'; - if (result != 'cancel') { - List args = ['--vm', currentVm + '.conf', '--delete-' + result]; - await Process.start('quickemu', args); - } - }); - }, + icon: Icon(Icons.delete, + color: active ? null : buttonColor, + semanticLabel: 'Delete'), + onPressed: active + ? null + : () { + showDialog( + context: context, + builder: (BuildContext context) => AlertDialog( + title: Text('Delete ' + currentVm), + content: Text('You are about to delete ' + + currentVm + + '. This cannot be undone. ' + + 'Would you like to delete the disk image but keep the ' + + 'configuration, or delete the whole VM?'), + actions: [ + TextButton( + child: const Text('Cancel'), + onPressed: () => + Navigator.pop(context, 'cancel'), + ), + TextButton( + child: const Text('Delete disk image'), + onPressed: () => Navigator.pop(context, 'disk'), + ), + TextButton( + child: const Text('Delete whole VM'), + onPressed: () => Navigator.pop(context, 'vm'), + ) // set up the AlertDialog + ], + ), + ).then((result) async { + result = result ?? 'cancel'; + if (result != 'cancel') { + List args = [ + '--vm', + currentVm + '.conf', + '--delete-' + result + ]; + await Process.start('quickemu', args); + } + }); + }, ), ], )), @@ -351,7 +364,9 @@ class _ManagerState extends State with PreferencesMixin { color: _spicy ? buttonColor : null, semanticLabel: 'Connect display with SPICE', ), - tooltip: _spicy ? 'Connect display with SPICE' : 'SPICE client not found', + tooltip: _spicy + ? 'Connect display with SPICE' + : 'SPICE client not found', onPressed: !_spicy ? null : () { @@ -359,19 +374,25 @@ class _ManagerState extends State with PreferencesMixin { }, ), IconButton( - icon: SvgPicture.asset('assets/images/console.svg', semanticsLabel: 'Connect with SSH', color: sshy ? buttonColor : Colors.grey), - tooltip: sshy ? 'Connect with SSH' : 'SSH server not detected on guest', + icon: SvgPicture.asset('assets/images/console.svg', + semanticsLabel: 'Connect with SSH', + color: sshy ? buttonColor : Colors.grey), + tooltip: sshy + ? 'Connect with SSH' + : 'SSH server not detected on guest', onPressed: !sshy ? null : () { - TextEditingController _usernameController = TextEditingController(); + TextEditingController _usernameController = + TextEditingController(); showDialog( context: context, builder: (BuildContext context) => AlertDialog( title: Text('Launch SSH connection to $currentVm'), content: TextField( controller: _usernameController, - decoration: const InputDecoration(hintText: "SSH username"), + decoration: const InputDecoration( + hintText: "SSH username"), ), actions: [ TextButton( @@ -387,10 +408,16 @@ class _ManagerState extends State with PreferencesMixin { ).then((result) { result = result ?? false; if (result) { - List sshArgs = ['ssh', '-p', vmInfo.sshPort!, _usernameController.text + '@localhost']; + List sshArgs = [ + 'ssh', + '-p', + vmInfo.sshPort!, + _usernameController.text + '@localhost' + ]; // Set the arguments to execute the ssh command in the default terminal. // Strip the extension as x-terminal-emulator may point to a .wrapper - switch (path.basenameWithoutExtension(_terminalEmulator!)) { + switch (path + .basenameWithoutExtension(_terminalEmulator!)) { case 'gnome-terminal': case 'mate-terminal': sshArgs.insert(0, '--'); diff --git a/lib/src/pages/operating_system_selection.dart b/lib/src/pages/operating_system_selection.dart index 9aa9709..c563a41 100644 --- a/lib/src/pages/operating_system_selection.dart +++ b/lib/src/pages/operating_system_selection.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:gettext_i18n/gettext_i18n.dart'; import '../model/operating_system.dart'; @@ -7,7 +8,8 @@ class OperatingSystemSelection extends StatefulWidget { const OperatingSystemSelection({Key? key}) : super(key: key); @override - State createState() => _OperatingSystemSelectionState(); + State createState() => + _OperatingSystemSelectionState(); } class _OperatingSystemSelectionState extends State { @@ -22,7 +24,9 @@ class _OperatingSystemSelectionState extends State { @override Widget build(BuildContext context) { - var list = gOperatingSystems.where((os) => os.name.toLowerCase().contains(term.toLowerCase())).toList(); + var list = gOperatingSystems + .where((os) => os.name.toLowerCase().contains(term.toLowerCase())) + .toList(); return Scaffold( appBar: AppBar( title: Text(context.t('Select operating system')), @@ -44,7 +48,8 @@ class _OperatingSystemSelectionState extends State { Expanded( child: TextField( focusNode: focusNode, - decoration: InputDecoration.collapsed(hintText: context.t('Search operating system')), + decoration: InputDecoration.collapsed( + hintText: context.t('Search operating system')), onChanged: (value) { setState(() { term = value; @@ -69,9 +74,15 @@ class _OperatingSystemSelectionState extends State { itemCount: list.length, itemBuilder: (context, index) { var item = list[index]; + var icon = SvgPicture.asset( + "assets/quickemu-icons/${item.code}-quickemu-white-pinkbg.svg", + width: 32, + height: 32, + ); return Card( child: ListTile( title: Text(item.name), + leading: icon, onTap: () { Navigator.of(context).pop(item); }, diff --git a/lib/src/pages/option_selection.dart b/lib/src/pages/option_selection.dart index 6431650..67128d2 100644 --- a/lib/src/pages/option_selection.dart +++ b/lib/src/pages/option_selection.dart @@ -24,7 +24,9 @@ class _OptionSelectionState extends State { @override Widget build(BuildContext context) { - var list = widget.version.options.where((e) => e.option.toLowerCase().contains(term.toLowerCase())).toList(); + var list = widget.version.options + .where((e) => e.option.toLowerCase().contains(term.toLowerCase())) + .toList(); return Scaffold( appBar: AppBar( @@ -49,7 +51,8 @@ class _OptionSelectionState extends State { Expanded( child: TextField( focusNode: focusNode, - decoration: InputDecoration.collapsed(hintText: context.t('Search option')), + decoration: InputDecoration.collapsed( + hintText: context.t('Search option')), onChanged: (value) { setState(() { term = value; diff --git a/lib/src/pages/version_selection.dart b/lib/src/pages/version_selection.dart index 940f046..cba1b8b 100644 --- a/lib/src/pages/version_selection.dart +++ b/lib/src/pages/version_selection.dart @@ -8,7 +8,8 @@ import '../model/version.dart'; import 'option_selection.dart'; class VersionSelection extends StatefulWidget { - const VersionSelection({Key? key, required this.operatingSystem}) : super(key: key); + const VersionSelection({Key? key, required this.operatingSystem}) + : super(key: key); final OperatingSystem operatingSystem; @@ -21,12 +22,14 @@ class _VersionSelectionState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(context.t('Select version for {0}', args: [widget.operatingSystem.name])), + title: Text(context + .t('Select version for {0}', args: [widget.operatingSystem.name])), ), body: SingleChildScrollView( child: Column( children: [ ListView.builder( + padding: const EdgeInsets.only(top: 4), shrinkWrap: true, itemCount: widget.operatingSystem.versions.length, itemBuilder: (context, index) { @@ -35,17 +38,23 @@ class _VersionSelectionState extends State { child: ListTile( title: Text(item.version), onTap: () { - if (widget.operatingSystem.versions[index].options.length > 1) { + if (widget + .operatingSystem.versions[index].options.length > + 1) { Navigator.of(context) - .push