From 07f91d23e618e0fcf3df7972a4ecc1b906095430 Mon Sep 17 00:00:00 2001 From: H2Sxxa Date: Mon, 22 Jul 2024 10:56:50 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20complete=20the=20install=20prepare?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/i18n/en_US/fields.yaml | 4 + assets/i18n/languages.yaml | 4 +- assets/i18n/zh_CN/fields.yaml | 6 +- lib/main.dart | 4 + lib/views/pages/install.dart | 279 +++++++++++++++++---------------- lib/views/pages/settings.dart | 44 +++++- lib/views/widgets/basic.dart | 12 +- lib/views/widgets/process.dart | 24 +++ lib/windows/sh.dart | 15 +- test/widget_test.dart | 13 -- 10 files changed, 242 insertions(+), 163 deletions(-) create mode 100644 lib/views/widgets/process.dart diff --git a/assets/i18n/en_US/fields.yaml b/assets/i18n/en_US/fields.yaml index 4e7093d..1808bd2 100644 --- a/assets/i18n/en_US/fields.yaml +++ b/assets/i18n/en_US/fields.yaml @@ -9,3 +9,7 @@ optional_features: Optional Features language: Language font: Font need_admin_permission: Need Admin Permission (Exit and right click exe and "Run as administrator") +success: Susscess +error: Error +output: Output +doctor: Doctor \ No newline at end of file diff --git a/assets/i18n/languages.yaml b/assets/i18n/languages.yaml index 4fea8f4..9808d6c 100644 --- a/assets/i18n/languages.yaml +++ b/assets/i18n/languages.yaml @@ -1,2 +1,2 @@ -English: en_US -简体中文: zh_CN \ No newline at end of file +en_US: English +zh_CN: 简体中文 \ No newline at end of file diff --git a/assets/i18n/zh_CN/fields.yaml b/assets/i18n/zh_CN/fields.yaml index fb76c84..dd30347 100644 --- a/assets/i18n/zh_CN/fields.yaml +++ b/assets/i18n/zh_CN/fields.yaml @@ -8,4 +8,8 @@ optional_features: 可选功能 settings: 设置 language: 语言 font: 字体 -need_admin_permission: 需要管理员权限 (退出并右键程序 "以管理员身份运行") \ No newline at end of file +need_admin_permission: 需要管理员权限 (退出并右键程序 "以管理员身份运行") +success: 成功 +error: 错误 +output: 输出 +doctor: 诊断 diff --git a/lib/main.dart b/lib/main.dart index 3db9009..d63a96b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:arche/arche.dart'; import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:superuser/superuser.dart'; import 'package:system_fonts/system_fonts.dart'; import 'package:wslconfigurer/i18n/i18n.dart'; @@ -158,6 +159,9 @@ class HomePageState extends State with RefreshMountedStateMixin { title: context.i18n.getOrKey("install"), child: const InstallPage(), ).toItem(icon: const Icon(Icons.install_desktop)), + PageContainer( + title: context.i18n.getOrKey("doctor"), + ).toItem(icon: const Icon(FontAwesomeIcons.userDoctor)), PageContainer( title: context.i18n.getOrKey("settings"), child: const SettingsPage(), diff --git a/lib/views/pages/install.dart b/lib/views/pages/install.dart index 466dccd..066d660 100644 --- a/lib/views/pages/install.dart +++ b/lib/views/pages/install.dart @@ -2,12 +2,14 @@ import 'package:arche/arche.dart'; import 'package:arche/extensions/dialogs.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:superuser/superuser.dart'; +import 'package:rinf/rinf.dart'; +import 'package:wslconfigurer/views/widgets/process.dart'; import 'package:wslconfigurer/windows/ms_open.dart'; import 'package:wslconfigurer/i18n/i18n.dart'; import 'package:wslconfigurer/messages/windows.pb.dart'; import 'package:wslconfigurer/views/widgets/basic.dart'; import 'package:wslconfigurer/views/widgets/divider.dart'; +import 'package:wslconfigurer/windows/sh.dart'; class InstallPage extends StatefulWidget { const InstallPage({super.key}); @@ -24,149 +26,158 @@ class _InstallPageState extends State { QueryOptionalFeature().sendSignalToRust(); } - @override - Widget build(BuildContext context) { - return StreamBuilder( - stream: OptionFeatures.rustSignalStream, - builder: (context, snapshot) { - var message = snapshot.data; - if (message == null) { - return const Center( - child: CircularProgressIndicator(), - ); - } + Widget buildWidget(BuildContext context, + AsyncSnapshot> snapshot) { + var message = snapshot.data; + if (message == null) { + return const Center( + child: CircularProgressIndicator(), + ); + } - var features = message.message.features; + var features = message.message.features; - if (features.fold(0, (state, feat) => state + feat.installState) == 2) { - var children = [ - ListTile( - leading: IconButton( - onPressed: () { - ComplexDialog.instance.text( - context: context, - content: Wrap( - spacing: 8, - direction: Axis.vertical, - children: [ - context.i18nMarkdown("optional_features.md", true), - FilledButton( - onPressed: () { - openMSSetting("optionalfeatures"); - }, - child: context.i18nText("optional_features"), - ) - ], - ), - ); - }, - icon: const Icon(Icons.help), - ), - title: Row( - children: [ - Text( - "${context.i18n.getOrKey("configure")} ${context.i18n.getOrKey("optional_features")}"), - ], - ), - trailing: IconButton( - onPressed: () => setState(() { - QueryOptionalFeature().sendSignalToRust(); - }), - icon: const Icon(Icons.refresh), - ), - ) - ]; - for (var feat in features) { - children.add( - ListTile( - title: Text(feat.caption), - subtitle: Text(feat.name), - trailing: feat.installState == 1 - ? const Icon( - Icons.check, - color: Colors.green, + if (features.fold(0, (state, feat) => state + feat.installState) != 2) { + return ScrollableContainer( + key: const ValueKey("prepare"), + children: [ + ListTile( + leading: IconButton( + onPressed: () { + ComplexDialog.instance.text( + context: context, + content: Wrap( + spacing: 8, + direction: Axis.vertical, + children: [ + context.i18nMarkdown("optional_features.md", true), + FilledButton( + onPressed: () { + openMSSetting("optionalfeatures"); + }, + child: context.i18nText("optional_features"), ) - : const Icon( - Icons.warning, - color: Colors.amber, - ), - ), - ); - } - - children.add( - Padding( - padding: const EdgeInsets.all(8), - child: Align( - alignment: Alignment.centerRight, - child: FilledButton( - onPressed: () { - ComplexDialog.instance.text( - context: context, - content: Text(Superuser.isSuperuser.toString())); - }, - child: context.i18nText("install.auto_install")), - ), + ], + ), + ); + }, + icon: const Icon(Icons.help), ), - ); - - return ScrollableContainer( - children: children, - ); - } - - return ScrollableContainer( - children: [ - ListTile( - leading: const Icon(FontAwesomeIcons.section), - title: context.i18nText("Install Linux Distribution"), + title: Row( + children: [ + Text( + "${context.i18n.getOrKey("configure")} ${context.i18n.getOrKey("optional_features")}"), + ], ), - Padding( - padding: const EdgeInsets.all(8), - child: Card.filled( - child: Column( - children: [ - ListTile( - title: const Text("Ubuntu"), - trailing: IconButton( - onPressed: () {}, - icon: const Icon(Icons.open_in_new)), - ), - ListTile( - title: const Text("ArchLinux"), - trailing: IconButton( - onPressed: () {}, - icon: const Icon(Icons.open_in_new)), + trailing: IconButton( + onPressed: () => setState(() { + QueryOptionalFeature().sendSignalToRust(); + }), + icon: const Icon(Icons.refresh), + ), + ), + ...features.map( + (feat) => ListTile( + title: Text(feat.caption), + subtitle: Text(feat.name), + trailing: feat.installState == 1 + ? const Icon( + Icons.check, + color: Colors.green, ) - ], - ), - ), + : IconButton( + icon: const Icon(Icons.download), + onPressed: () { + su( + context, + () => enableFeature(feat.name).then( + (process) => ComplexDialog.instance + .text( + context: context, + title: context.i18nText("output"), + content: SingleChildScrollView( + child: ProcessText( + process: process, + ), + ), + ) + .then( + (_) => setState(() { + QueryOptionalFeature().sendSignalToRust(); + }), + ), + ), + ); + }, + ), ), - divider8, - ListTile( - leading: const Icon(FontAwesomeIcons.section), - title: context.i18nText("Install Essential Packages(Optional)"), + ) + ], + ); + } + + return ScrollableContainer( + key: const ValueKey("install"), + children: [ + ListTile( + leading: const Icon(FontAwesomeIcons.section), + title: context.i18nText("Install Linux Distribution"), + ), + Padding( + padding: const EdgeInsets.all(8), + child: Card.filled( + child: Column( + children: [ + ListTile( + title: const Text("Ubuntu"), + trailing: IconButton( + onPressed: () {}, icon: const Icon(Icons.open_in_new)), + ), + ListTile( + title: const Text("ArchLinux"), + trailing: IconButton( + onPressed: () {}, icon: const Icon(Icons.open_in_new)), + ) + ], ), - Padding( - padding: const EdgeInsets.all(8), - child: Card.filled( - child: Column( - children: [ - ListTile( - title: const Text("C (GCC)"), - trailing: IconButton( - onPressed: () {}, icon: const Icon(Icons.copy)), - ), - ListTile( - title: const Text("C (Clang)"), - trailing: IconButton( - onPressed: () {}, icon: const Icon(Icons.copy)), - ), - ], + ), + ), + divider8, + ListTile( + leading: const Icon(FontAwesomeIcons.section), + title: context.i18nText("Install Essential Packages(Optional)"), + ), + Padding( + padding: const EdgeInsets.all(8), + child: Card.filled( + child: Column( + children: [ + ListTile( + title: const Text("C (GCC)"), + trailing: IconButton( + onPressed: () {}, icon: const Icon(Icons.copy)), ), - ), + ListTile( + title: const Text("C (Clang)"), + trailing: IconButton( + onPressed: () {}, icon: const Icon(Icons.copy)), + ), + ], ), - ], + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return StreamBuilder( + stream: OptionFeatures.rustSignalStream, + builder: (context, snapshot) { + return AnimatedSwitcher( + duration: Durations.medium4, + child: buildWidget(context, snapshot), ); }, ); diff --git a/lib/views/pages/settings.dart b/lib/views/pages/settings.dart index 4a5a513..b2e0b15 100644 --- a/lib/views/pages/settings.dart +++ b/lib/views/pages/settings.dart @@ -22,6 +22,8 @@ class _SettingsPageState extends State { children: [ ListTile( title: context.i18nText("language"), + subtitle: Text( + context.i18n.avaiableLanguages[context.i18n.locale] ?? "Error"), trailing: PopupMenuButton( initialValue: configs.locale.getOr("en_US"), onSelected: (value) async { @@ -33,23 +35,51 @@ class _SettingsPageState extends State { setState(() {}); }, itemBuilder: (context) => context.i18n.avaiableLanguages.entries - .map((entry) => PopupMenuItem( - value: entry.value, - child: Text(entry.key), - )) + .map( + (entry) => PopupMenuItem( + value: entry.key, + child: Text(entry.value), + ), + ) .toList(), ), ), divider8, ListTile( title: context.i18nText("font"), - trailing: SystemFontSelector( - initial: configs.font.tryGet(), - onFontSelected: (value) { + subtitle: Text(configs.font.tryGet() ?? "Default"), + trailing: PopupMenuButton( + initialValue: configs.font.tryGet(), + onSelected: (value) async { + await SystemFonts().loadFont(value); configs.font.write(value); setState(() {}); appKey.currentState?.refreshMounted(); }, + itemBuilder: (BuildContext context) { + var fonts = SystemFonts().getFontList(); + fonts.sort(); + return fonts + .map( + (fontName) => PopupMenuItem( + value: fontName, + child: FutureBuilder( + future: SystemFonts().loadFont(fontName), + builder: (context, snapshot) { + var data = snapshot.data; + + if (data == null) { + return Text(fontName); + } + return Text( + data, + style: TextStyle(fontFamily: fontName), + ); + }, + )), + ) + .toList(); + }, ), ) ], diff --git a/lib/views/widgets/basic.dart b/lib/views/widgets/basic.dart index e91df7f..72619ec 100644 --- a/lib/views/widgets/basic.dart +++ b/lib/views/widgets/basic.dart @@ -75,11 +75,13 @@ class ScrollableContainer extends StatelessWidget { @override Widget build(BuildContext context) { - return SingleChildScrollView( - child: Padding( - padding: padding ?? EdgeInsets.zero, - child: Column( - children: children, + return SizedBox.expand( + child: SingleChildScrollView( + child: Padding( + padding: padding ?? EdgeInsets.zero, + child: Column( + children: children, + ), ), ), ); diff --git a/lib/views/widgets/process.dart b/lib/views/widgets/process.dart new file mode 100644 index 0000000..a77b738 --- /dev/null +++ b/lib/views/widgets/process.dart @@ -0,0 +1,24 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; + +class ProcessText extends StatelessWidget { + final Process process; + + const ProcessText({super.key, required this.process}); + + @override + Widget build(BuildContext context) { + return StreamBuilder( + stream: process.stdout, + builder: (context, snapshot) { + var data = snapshot.data; + if (data == null) { + return const Text(""); + } + + return SelectableText(const SystemEncoding().decode(data)); + }, + ); + } +} diff --git a/lib/windows/sh.dart b/lib/windows/sh.dart index c6749cf..4c9fbef 100644 --- a/lib/windows/sh.dart +++ b/lib/windows/sh.dart @@ -14,6 +14,19 @@ void su( run(); } else { ComplexDialog.instance.text( - context: context, content: context.i18nText("need_admin_permission")); + context: context, + title: context.i18nText("error"), + content: context.i18nText("need_admin_permission"), + ); } } + +Future enableFeature(String featurename) async { + return await Process.start("dism", [ + "/online", + "/enable-feature", + "/featurename:$featurename", + "/all", + "/norestart" + ]); +} diff --git a/test/widget_test.dart b/test/widget_test.dart index 6679c6a..de1c04b 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -5,7 +5,6 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:wslconfigurer/main.dart'; @@ -14,17 +13,5 @@ void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); }); }