novel reading settings; improve ui

This commit is contained in:
wgh19
2024-05-20 21:58:58 +08:00
parent c51df1efde
commit f33df47cd6
6 changed files with 292 additions and 38 deletions

View File

@@ -132,7 +132,13 @@
"Replace with '-p${index}' if the work have more than one images, otherwise replace with blank.": "替换为'-p${index}'如果作品有多张图片, 否则替换为空白", "Replace with '-p${index}' if the work have more than one images, otherwise replace with blank.": "替换为'-p${index}'如果作品有多张图片, 否则替换为空白",
"Recommendation": "推荐", "Recommendation": "推荐",
"Novel": "小说", "Novel": "小说",
"Novels": "小说" "Novels": "小说",
"Reading Settings": "阅读设置",
"Font Size": "字体大小",
"Line Height": "行高",
"Paragraph Spacing": "段间距",
"light": "浅色",
"dark": "深色"
}, },
"zh_TW": { "zh_TW": {
"Search": "搜索", "Search": "搜索",
@@ -267,6 +273,12 @@
"Replace with '-p${index}' if the work have more than one images, otherwise replace with blank.": "替換為'-p${index}'如果作品有多張圖片, 否則替換為空白", "Replace with '-p${index}' if the work have more than one images, otherwise replace with blank.": "替換為'-p${index}'如果作品有多張圖片, 否則替換為空白",
"Recommendation": "推薦", "Recommendation": "推薦",
"Novel": "小說", "Novel": "小說",
"Novels": "小說" "Novels": "小說",
"Reading Settings": "閱讀設置",
"Font Size": "字體大小",
"Line Height": "行高",
"Paragraph Spacing": "段間距",
"light": "淺色",
"dark": "深色"
} }
} }

View File

@@ -20,6 +20,9 @@ class _Appdata {
"proxy": "", "proxy": "",
"darkMode": "System", "darkMode": "System",
"language": "System", "language": "System",
"readingFontSize": 16.0,
"readingLineHeight": 1.5,
"readingParagraphSpacing": 8.0,
}; };
bool lock = false; bool lock = false;

View File

@@ -8,7 +8,6 @@ import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:pixes/components/animated_image.dart'; import 'package:pixes/components/animated_image.dart';
import 'package:pixes/components/loading.dart'; import 'package:pixes/components/loading.dart';
import 'package:pixes/components/message.dart'; import 'package:pixes/components/message.dart';
import 'package:pixes/components/page_route.dart';
import 'package:pixes/components/title_bar.dart'; import 'package:pixes/components/title_bar.dart';
import 'package:pixes/foundation/app.dart'; import 'package:pixes/foundation/app.dart';
import 'package:pixes/foundation/image_provider.dart'; import 'package:pixes/foundation/image_provider.dart';

View File

@@ -30,6 +30,32 @@ import "downloading_page.dart";
double get _appBarHeight => App.isDesktop ? 36.0 : 48.0; double get _appBarHeight => App.isDesktop ? 36.0 : 48.0;
class TitleBarAction {
final IconData icon;
final String title;
final void Function() onPressed;
TitleBarAction(this.icon, this.title, this.onPressed);
}
class TitleBarController extends StateController {
TitleBarController();
final List<TitleBarAction> actions = [
if (kDebugMode) TitleBarAction(MdIcons.bug_report, "Debug", debug)
];
void addAction(TitleBarAction action) {
actions.add(action);
update();
}
void removeAction(TitleBarAction action) {
actions.remove(action);
update();
}
}
class MainPage extends StatefulWidget { class MainPage extends StatefulWidget {
const MainPage({super.key}); const MainPage({super.key});
@@ -46,6 +72,7 @@ class _MainPageState extends State<MainPage> with WindowListener {
@override @override
void initState() { void initState() {
StateController.put<TitleBarController>(TitleBarController());
windowManager.addListener(this); windowManager.addListener(this);
listenMouseSideButtonToBack(navigatorKey); listenMouseSideButtonToBack(navigatorKey);
App.mainNavigatorKey = navigatorKey; App.mainNavigatorKey = navigatorKey;
@@ -54,6 +81,7 @@ class _MainPageState extends State<MainPage> with WindowListener {
@override @override
void dispose() { void dispose() {
StateController.remove<TitleBarController>();
windowManager.removeListener(this); windowManager.removeListener(this);
super.dispose(); super.dispose();
} }
@@ -224,33 +252,50 @@ class _MainPageState extends State<MainPage> with WindowListener {
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
height: _appBarHeight, height: _appBarHeight,
title: () { title: () {
if (!App.isDesktop) { return StateBuilder<TitleBarController>(
return const Align( builder: (controller) {
alignment: AlignmentDirectional.centerStart, Widget content = Padding(
child: Text("pixes"), padding: const EdgeInsets.only(bottom: 4),
); child: Align(
} alignment: AlignmentDirectional.centerStart,
return const DragToMoveArea( child: Row(
child: Padding( children: [
padding: EdgeInsets.only(bottom: 4), if (!App.isDesktop)
child: Align( const Text(
alignment: AlignmentDirectional.centerStart, "Pixes",
child: Row( style: TextStyle(fontSize: 13),
children: [ ),
Text( if (!App.isDesktop) const Spacer(),
"Pixes", if (App.isDesktop)
style: TextStyle(fontSize: 13), const Expanded(
), child: DragToMoveArea(
Spacer(), child: Text(
if (kDebugMode) "Pixes",
Padding( style: TextStyle(fontSize: 13),
padding: EdgeInsets.only(right: 138), )),
child: Button(onPressed: debug, child: Text("Debug")), ),
) for (var action in controller.actions)
], Button(
onPressed: action.onPressed,
child: Row(
children: [
Icon(
action.icon,
size: 18,
),
const SizedBox(width: 4),
Text(action.title),
],
),
).paddingTop(4).paddingLeft(4),
if (App.isDesktop) const SizedBox(width: 128),
],
),
), ),
), );
),
return content;
},
); );
}(), }(),
leading: _BackButton(navigatorKey), leading: _BackButton(navigatorKey),

View File

@@ -53,7 +53,10 @@ class _NovelPageState extends State<NovelPage> {
), ),
if (widget.novel.seriesId != null) if (widget.novel.seriesId != null)
NovelSeriesWidget( NovelSeriesWidget(
widget.novel.seriesId!, widget.novel.seriesTitle!) widget.novel.seriesId!, widget.novel.seriesTitle!),
SliverPadding(
padding: EdgeInsets.only(
top: 16 + MediaQuery.of(context).padding.bottom)),
], ],
), ),
).padding(const EdgeInsets.symmetric(horizontal: 16))); ).padding(const EdgeInsets.symmetric(horizontal: 16)));
@@ -240,6 +243,7 @@ class _NovelPageState extends State<NovelPage> {
const SizedBox(width: 12), const SizedBox(width: 12),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text(widget.novel.author.name, Text(widget.novel.author.name,
style: const TextStyle( style: const TextStyle(
@@ -248,9 +252,9 @@ class _NovelPageState extends State<NovelPage> {
)), )),
Text( Text(
widget.novel.createDate.toString().substring(0, 10), widget.novel.createDate.toString().substring(0, 10),
style: const TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
color: Colors.grey, color: ColorScheme.of(context).outline,
), ),
), ),
], ],
@@ -336,6 +340,9 @@ class _NovelPageState extends State<NovelPage> {
Button( Button(
onPressed: favorite, onPressed: favorite,
child: Row( child: Row(
mainAxisAlignment: constrains.maxWidth > 420
? MainAxisAlignment.start
: MainAxisAlignment.center,
children: [ children: [
if (isAddingFavorite) if (isAddingFavorite)
const SizedBox( const SizedBox(
@@ -353,8 +360,9 @@ class _NovelPageState extends State<NovelPage> {
) )
else else
const Icon(MdIcons.favorite_outline, size: 18), const Icon(MdIcons.favorite_outline, size: 18),
const SizedBox(width: 12), if (constrains.maxWidth > 420)
Text("Favorite".tl) const SizedBox(width: 12),
if (constrains.maxWidth > 420) Text("Favorite".tl)
], ],
) )
.fixWidth(shouldFillSpace .fixWidth(shouldFillSpace
@@ -365,10 +373,14 @@ class _NovelPageState extends State<NovelPage> {
const SizedBox(width: 8), const SizedBox(width: 8),
Button( Button(
child: Row( child: Row(
mainAxisAlignment: constrains.maxWidth > 420
? MainAxisAlignment.start
: MainAxisAlignment.center,
children: [ children: [
const Icon(MdIcons.comment, size: 18), const Icon(MdIcons.comment, size: 18),
const SizedBox(width: 12), if (constrains.maxWidth > 420)
Text("Comments".tl) const SizedBox(width: 12),
if (constrains.maxWidth > 420) Text("Comments".tl)
], ],
) )
.fixWidth(shouldFillSpace .fixWidth(shouldFillSpace

View File

@@ -1,10 +1,17 @@
import 'package:fluent_ui/fluent_ui.dart'; import 'package:fluent_ui/fluent_ui.dart';
import 'package:pixes/appdata.dart';
import 'package:pixes/components/animated_image.dart'; import 'package:pixes/components/animated_image.dart';
import 'package:pixes/components/loading.dart'; import 'package:pixes/components/loading.dart';
import 'package:pixes/components/md.dart';
import 'package:pixes/components/page_route.dart';
import 'package:pixes/components/title_bar.dart';
import 'package:pixes/foundation/app.dart';
import 'package:pixes/foundation/image_provider.dart'; import 'package:pixes/foundation/image_provider.dart';
import 'package:pixes/network/network.dart'; import 'package:pixes/network/network.dart';
import 'package:pixes/pages/image_page.dart'; import 'package:pixes/pages/image_page.dart';
import 'package:pixes/pages/main_page.dart';
import 'package:pixes/utils/ext.dart'; import 'package:pixes/utils/ext.dart';
import 'package:pixes/utils/translation.dart';
class NovelReadingPage extends StatefulWidget { class NovelReadingPage extends StatefulWidget {
const NovelReadingPage(this.novel, {super.key}); const NovelReadingPage(this.novel, {super.key});
@@ -16,6 +23,37 @@ class NovelReadingPage extends StatefulWidget {
} }
class _NovelReadingPageState extends LoadingState<NovelReadingPage, String> { class _NovelReadingPageState extends LoadingState<NovelReadingPage, String> {
TitleBarAction? action;
bool isShowingSettings = false;
@override
void initState() {
action = TitleBarAction(MdIcons.tune, "Settings", () {
if (!isShowingSettings) {
_NovelReadingSettings.show(context, () {
setState(() {});
});
isShowingSettings = true;
} else {
Navigator.of(context).pop();
isShowingSettings = false;
}
});
Future.delayed(const Duration(milliseconds: 200), () {
StateController.find<TitleBarController>().addAction(action!);
});
super.initState();
}
@override
void dispose() {
Future.delayed(const Duration(milliseconds: 200), () {
StateController.find<TitleBarController>().removeAction(action!);
});
super.dispose();
}
@override @override
Widget buildContent(BuildContext context, String data) { Widget buildContent(BuildContext context, String data) {
var content = buildList(context).toList(); var content = buildList(context).toList();
@@ -41,8 +79,12 @@ class _NovelReadingPageState extends LoadingState<NovelReadingPage, String> {
} }
Iterable<Widget> buildList(BuildContext context) sync* { Iterable<Widget> buildList(BuildContext context) sync* {
double fontSizeAdd = appdata.settings["readingFontSize"] - 16.0;
double fontHeight = appdata.settings["readingLineHeight"];
yield Text(widget.novel.title, yield Text(widget.novel.title,
style: const TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold)); style: TextStyle(
fontSize: 24.0 + fontSizeAdd, fontWeight: FontWeight.bold));
yield const SizedBox(height: 12.0); yield const SizedBox(height: 12.0);
yield const Divider( yield const Divider(
style: DividerThemeData(horizontalMargin: EdgeInsets.all(0)), style: DividerThemeData(horizontalMargin: EdgeInsets.all(0)),
@@ -71,9 +113,150 @@ class _NovelReadingPageState extends LoadingState<NovelReadingPage, String> {
), ),
), ),
); );
} else if (content.startsWith('[chapter:')) {
var title = content.replaceLast(']', '').split(':')[1];
yield Text(title,
style: TextStyle(
fontSize: 20.0 + fontSizeAdd,
fontWeight: FontWeight.bold,
height: fontHeight))
.paddingBottom(8);
} else { } else {
yield Text(content); yield Text(content,
style:
TextStyle(fontSize: 16.0 + fontSizeAdd, height: fontHeight))
.paddingBottom(appdata.settings["readingParagraphSpacing"]);
} }
} }
} }
} }
class _NovelReadingSettings extends StatefulWidget {
const _NovelReadingSettings(this.callback);
final void Function() callback;
static void show(BuildContext context, void Function() callback) {
Navigator.of(context).push(SideBarRoute(_NovelReadingSettings(callback)));
}
@override
State<_NovelReadingSettings> createState() => __NovelReadingSettingsState();
}
class __NovelReadingSettingsState extends State<_NovelReadingSettings> {
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: [
TitleBar(title: "Reading Settings".tl),
const SizedBox(height: 8),
Card(
padding: EdgeInsets.zero,
child: ListTile(
title: Text("Font Size".tl),
subtitle: Slider(
value: appdata.settings["readingFontSize"],
onChanged: (value) {
setState(() {
appdata.settings["readingFontSize"] = value;
});
appdata.writeSettings();
widget.callback();
},
min: 12.0,
max: 24.0,
divisions: 12,
label: appdata.settings["readingFontSize"].toString(),
),
trailing: Text(appdata.settings["readingFontSize"].toString()),
),
).paddingHorizontal(8).paddingBottom(8),
Card(
padding: EdgeInsets.zero,
child: ListTile(
title: Text("Line Height".tl),
subtitle: Slider(
value: appdata.settings["readingLineHeight"],
onChanged: (value) {
setState(() {
appdata.settings["readingLineHeight"] = value;
});
appdata.writeSettings();
widget.callback();
},
min: 1.0,
max: 2.0,
divisions: 10,
label: appdata.settings["readingLineHeight"].toString(),
),
trailing: Text(appdata.settings["readingLineHeight"].toString()),
),
).paddingHorizontal(8).paddingBottom(8),
Card(
padding: EdgeInsets.zero,
child: ListTile(
title: Text("Paragraph Spacing".tl),
subtitle: Slider(
value: appdata.settings["readingParagraphSpacing"],
onChanged: (value) {
setState(() {
appdata.settings["readingParagraphSpacing"] = value;
});
appdata.writeSettings();
widget.callback();
},
min: 0.0,
max: 16.0,
divisions: 8,
label: appdata.settings["readingParagraphSpacing"].toString(),
),
trailing:
Text(appdata.settings["readingParagraphSpacing"].toString()),
),
).paddingHorizontal(8).paddingBottom(8),
// 深色模式
Card(
margin: const EdgeInsets.symmetric(horizontal: 8),
padding: EdgeInsets.zero,
child: ListTile(
title: Text("Theme".tl),
trailing: DropDownButton(
title: Text(appdata.settings["theme"] ?? "System".tl),
items: [
MenuFlyoutItem(
text: Text("System".tl),
onPressed: () {
setState(() {
appdata.settings["theme"] = "System";
});
appdata.writeData();
StateController.findOrNull(tag: "MyApp")?.update();
}),
MenuFlyoutItem(
text: Text("light".tl),
onPressed: () {
setState(() {
appdata.settings["theme"] = "Light";
});
appdata.writeData();
StateController.findOrNull(tag: "MyApp")?.update();
}),
MenuFlyoutItem(
text: Text("dark".tl),
onPressed: () {
setState(() {
appdata.settings["theme"] = "Dark";
});
appdata.writeData();
StateController.findOrNull(tag: "MyApp")?.update();
}),
]),
),
),
],
),
);
}
}