feat: font size updates + fixes

This commit is contained in:
2024-04-09 17:45:16 +03:00
parent 1d2b6e7516
commit a4ade4a7ed
10 changed files with 288 additions and 180 deletions

View File

@@ -42,34 +42,41 @@ class Settings {
class GlobalSettings {
bool keepAwake;
double gameTextScale;
double uiTextScale;
GlobalSettings({
required this.keepAwake,
required this.gameTextScale,
required this.uiTextScale,
});
factory GlobalSettings.empty() => GlobalSettings(
keepAwake: true,
gameTextScale: 1.0,
uiTextScale: 1.0,
);
factory GlobalSettings.fromJson(Map<String, dynamic> json) => GlobalSettings(
keepAwake: json['keepAwake'] as bool? ?? true,
gameTextScale: json['gameTextScale'] as double? ?? 1.0,
uiTextScale: json['uiTextScale'] as double? ?? 1.0,
);
Map<String, dynamic> toJson() => {
'keepAwake': keepAwake,
'gameTextScale': gameTextScale,
'uiTextScale': uiTextScale,
};
GlobalSettings copyWith({
bool? keepAwake,
double? gameTextScale,
double? uiTextScale,
}) {
return GlobalSettings(
keepAwake: keepAwake ?? this.keepAwake,
gameTextScale: gameTextScale ?? this.gameTextScale,
uiTextScale: uiTextScale ?? this.uiTextScale,
);
}
}

View File

@@ -66,8 +66,8 @@ class GameStore extends ChangeNotifier {
Future<GameStore> init() async {
debugPrint('GameStore.init');
await storage.init();
await loadGlobalSettings();
debugPrint('storage.init $storage');
loadGlobalSettings();
loadAllProfiles();
return this;
}
@@ -494,7 +494,7 @@ class GameStore extends ChangeNotifier {
notifyListeners();
}
void loadGlobalSettings() async {
Future<void> loadGlobalSettings() async {
final settings = await storage.readFile('settings');
if (settings != null) {
globalSettings = GlobalSettings.fromJson(settings);
@@ -556,13 +556,21 @@ class GameStore extends ChangeNotifier {
// TODO move to [GlobalSettings]
void echoGlobalSettingsChanged(GlobalSettings old, GlobalSettings updated) {
echoSystem('Settings updated:');
echoSystem('Global Settings updated:');
var updateCount = 0;
if (updated.keepAwake != old.keepAwake) {
updateCount++;
echoSystem(
'Keep Screen Awake is now ${updated.keepAwake ? 'enabled' : 'disabled'}');
}
if (updated.uiTextScale != old.uiTextScale) {
updateCount++;
echoSystem('UI Text Scale is now ${updated.gameTextScale}');
}
if (updated.gameTextScale != old.gameTextScale) {
updateCount++;
echoSystem('Game Output Text Scale is now ${updated.gameTextScale}');
}
if (updateCount == 0) {
echoSystem('<no changes>');
}
@@ -654,6 +662,7 @@ class GameStore extends ChangeNotifier {
Future<void> saveGlobalSettings(GlobalSettings settings) async {
globalSettings = settings;
notifyListeners();
storage.writeFile('settings', settings.toJson());
}
}

View File

@@ -52,23 +52,26 @@ class MudblockApp extends StatelessWidget {
builder: (context, child) {
return GameStore.provider(
builder: (context, child) {
final store = GameStore.of(context);
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.linear(store.globalSettings.gameTextScale),
return GameStore.consumer(
builder: (context, store, child) => MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler:
TextScaler.linear(store.globalSettings.uiTextScale),
),
child: child!,
),
child: Container(
color: darkTheme.colorScheme.background,
child: Padding(
padding: PlatformUtils.isDesktop
? EdgeInsets.only(top: Platform.isMacOS ? 28.0 : 32.0)
: EdgeInsets.zero,
child: child,
),
),
child: child!,
);
},
child: Container(
color: darkTheme.colorScheme.background,
child: Padding(
padding: PlatformUtils.isDesktop
? EdgeInsets.only(top: Platform.isMacOS ? 28.0 : 32.0)
: EdgeInsets.zero,
child: child!,
),
),
child: child,
);
},
initialRoute: Paths.home,

View File

@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:mudblock/core/store.dart';
class HomeAppBar extends StatelessWidget implements PreferredSizeWidget {
const HomeAppBar({
super.key,
});
@override
Widget build(BuildContext context) {
return AppBar(
title: GameStore.consumer(
builder: (context, store, child) => Text(store.connected
? '${store.currentProfile.name} - Mudblock'
: 'Mudblock'),
),
);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}

119
lib/pages/home_drawer.dart Normal file
View File

@@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:mudblock/core/features/alias.dart';
import 'package:mudblock/core/features/game_button_set.dart';
import 'package:mudblock/core/features/profile.dart';
import 'package:mudblock/core/features/trigger.dart';
import 'package:mudblock/core/features/variable.dart';
import 'package:mudblock/core/platform_utils.dart';
import 'package:mudblock/core/routes.dart';
import 'package:mudblock/core/store.dart';
import 'package:package_info_plus/package_info_plus.dart';
class HomeDrawer extends StatelessWidget {
const HomeDrawer({super.key});
@override
Widget build(BuildContext context) {
final interaction = PlatformUtils.isDesktop ? 'Click' : 'Tap';
return Drawer(
child: GameStore.consumer(
builder: (context, store, child) => ListView(
children: [
ListTile(
leading: const Image(
image: AssetImage('assets/images/logo/logo.png'),
width: 32,
height: 32,
),
title: const Text('Mudblock'),
subtitle: FutureBuilder<String>(
future: PackageInfo.fromPlatform().then((pkg) => pkg.version),
builder: (context, data) {
final version = data.data ?? '...';
return Text('v$version');
},
),
onTap: () => Navigator.pushNamed(context, Paths.about),
),
const Divider(),
ListTile(
title: store.connected
? Text(store.currentProfile.name)
: const Text('Not connected'),
subtitle: store.connected
? Text(
'${store.currentProfile.host}:${store.currentProfile.port}',
)
: Text('$interaction to connect'),
leading: const CircleAvatar(child: Icon(Icons.cable)),
onTap: store.connected
? () async {
final updated = await Navigator.pushNamed(
context,
Paths.profile,
arguments: store.currentProfile,
);
if (updated != null) {
await (updated as MUDProfile).save();
store.loadSavedProfiles();
}
}
: () {
store.selectProfileAndConnect(context);
},
),
if (store.connected) ...[
_tile(context, GameButtonSetData.iconData, 'Button Sets',
route: Paths.buttons),
_tile(context, Alias.iconData, 'Aliases', route: Paths.aliases),
_tile(context, Trigger.iconData, 'Triggers',
route: Paths.triggers),
_tile(context, Variable.iconData, 'Variables',
route: Paths.variables),
if (PlatformUtils.isDesktop)
_tile(context, Icons.keyboard, 'Keyboard Shortcuts',
route: Paths.shortcuts),
const Divider(),
_tile(context, Icons.cable, 'Profiles', route: Paths.profiles),
_tile(context, Icons.settings, 'Settings', route: Paths.settings),
_tile(
context,
Icons.exit_to_app,
'Disconnect',
onTap: () async {
Navigator.of(context).pop();
await gameStore.disconnect();
if (context.mounted) {
gameStore.selectProfileAndConnect(context);
}
},
),
],
if (!store.connected)
ListTile(
title: const Text('Settings'),
leading: const Icon(Icons.settings),
onTap: () => Navigator.pushNamed(context, Paths.settings),
),
],
),
),
);
}
Widget _tile(
BuildContext context,
IconData icon,
String title, {
String? route,
void Function()? onTap,
}) {
assert(route != null || onTap != null);
return ListTile(
title: Text(title),
leading: Icon(icon),
onTap: onTap ?? () => Navigator.pushNamed(context, route!),
);
}
}

View File

@@ -76,41 +76,50 @@ class HomePageState extends State<HomePage>
Expanded(
child: Material(
color: Colors.black,
child: Consumer<GameStore>(
child: GameStore.consumer(
builder: (context, store, child) {
final lines = store.lines;
return Theme(
data: Theme.of(context).copyWith(
textSelectionTheme: TextSelectionThemeData(
cursorColor: Colors.white,
selectionColor: Colors.white.withOpacity(0.3),
selectionHandleColor: Colors.white,
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.linear(
store.globalSettings.gameTextScale,
),
),
child: Stack(
children: [
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: store.selectInput,
child: SingleChildScrollView(
controller: store.scrollController,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Focus(
child: _buildGameOutput(lines),
child: Theme(
data: Theme.of(context).copyWith(
textSelectionTheme: TextSelectionThemeData(
cursorColor: Colors.white,
selectionColor: Colors.white.withOpacity(0.3),
selectionHandleColor: Colors.white,
),
),
child: Stack(
children: [
Positioned.fill(
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: store.selectInput,
child: SingleChildScrollView(
controller: store.scrollController,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Focus(
child: _buildGameOutput(lines),
),
),
),
),
),
),
if (store.connected)
for (final buttonSet in store
.currentProfile.buttonSets
.where((b) => b.enabled))
Padding(
padding: const EdgeInsets.all(8.0),
child: GameButtonSet(data: buttonSet),
)
],
if (store.connected)
for (final buttonSet in store
.currentProfile.buttonSets
.where((b) => b.enabled))
Padding(
padding: const EdgeInsets.all(8.0),
child: GameButtonSet(data: buttonSet),
)
],
),
),
);
},
@@ -125,7 +134,8 @@ class HomePageState extends State<HomePage>
controller: store.input,
onSubmitted: store.submitAsInput,
onTap: store.selectInput,
style: consoleStyle.copyWith(color: Theme.of(context).colorScheme.onSurface),
style: consoleStyle.copyWith(
color: Theme.of(context).colorScheme.onSurface),
decoration: InputDecoration(
hintText: 'Enter command',
border: const OutlineInputBorder(),
@@ -140,7 +150,7 @@ class HomePageState extends State<HomePage>
);
}
SelectableText _buildGameOutput(List<String> lines) {
Widget _buildGameOutput(List<String> lines) {
return SelectableText.rich(
TextSpan(
children: [

View File

@@ -1,15 +1,9 @@
import 'package:flutter/material.dart';
import 'package:mudblock/core/features/game_button_set.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart';
import '../core/features/alias.dart';
import '../core/features/profile.dart';
import '../core/features/trigger.dart';
import '../core/features/variable.dart';
import '../core/platform_utils.dart';
import '../core/routes.dart';
import '../core/store.dart';
import 'home_app_bar.dart';
import 'home_drawer.dart';
class HomeScaffold extends StatelessWidget with GameStoreMixin {
final Widget Function(BuildContext, Widget?) builder;
@@ -18,127 +12,16 @@ class HomeScaffold extends StatelessWidget with GameStoreMixin {
@override
Widget build(BuildContext context) {
final interaction = PlatformUtils.isDesktop ? 'Click' : 'Tap';
return Scaffold(
appBar: AppBar(
title: GameStore.consumer(
builder: (context, store, child) => Text(store.connected
? '${store.currentProfile.name} - Mudblock'
: 'Mudblock'),
),
),
appBar: const HomeAppBar(),
body: ChangeNotifierProvider.value(
value: gameStore,
builder: builder,
),
endDrawer: Drawer(
child: GameStore.consumer(
builder: (context, store, child) => ListView(
children: [
ListTile(
leading: const Image(
image: AssetImage('assets/images/logo/logo.png'),
width: 32,
height: 32,
),
title: const Text('Mudblock'),
subtitle: FutureBuilder<String>(
future: PackageInfo.fromPlatform().then((pkg) => pkg.version),
builder: (context, data) {
final version = data.data ?? '...';
return Text('v$version');
},
),
onTap: () => Navigator.pushNamed(context, Paths.about),
),
const Divider(),
ListTile(
title: store.connected
? Text(store.currentProfile.name)
: const Text('Not connected'),
subtitle: store.connected
? Text(
'${store.currentProfile.host}:${store.currentProfile.port}',
)
: Text('$interaction to connect'),
leading: const CircleAvatar(child: Icon(Icons.cable)),
onTap: store.connected
? () async {
final updated = await Navigator.pushNamed(
context,
Paths.profile,
arguments: store.currentProfile,
);
if (updated != null) {
await (updated as MUDProfile).save();
store.loadSavedProfiles();
}
}
: () {
store.selectProfileAndConnect(context);
},
),
if (store.connected) ...[
ListTile(
title: const Text('Button Sets'),
leading: const Icon(GameButtonSetData.iconData),
onTap: () => Navigator.pushNamed(context, Paths.buttons),
),
ListTile(
title: const Text('Aliases'),
leading: const Icon(Alias.iconData),
onTap: () => Navigator.pushNamed(context, Paths.aliases),
),
ListTile(
title: const Text('Triggers'),
leading: const Icon(Trigger.iconData),
onTap: () => Navigator.pushNamed(context, Paths.triggers),
),
ListTile(
title: const Text('Variables'),
leading: const Icon(Variable.iconData),
onTap: () => Navigator.pushNamed(context, Paths.variables),
),
if (PlatformUtils.isDesktop)
ListTile(
title: const Text('Keyboard Shortcuts'),
leading: const Icon(Icons.keyboard),
onTap: () => Navigator.pushNamed(context, Paths.shortcuts),
),
const Divider(),
ListTile(
title: const Text('Profiles'),
leading: const Icon(Icons.cable),
onTap: () => Navigator.pushNamed(context, Paths.profiles),
),
ListTile(
title: const Text('Settings'),
leading: const Icon(Icons.settings),
onTap: () => Navigator.pushNamed(context, Paths.settings),
),
ListTile(
title: const Text('Disconnect'),
leading: const Icon(Icons.exit_to_app),
onTap: () async {
Navigator.of(context).pop();
await gameStore.disconnect();
if (context.mounted) {
gameStore.selectProfileAndConnect(context);
}
},
),
],
if (!store.connected)
ListTile(
title: const Text('Settings'),
leading: const Icon(Icons.settings),
onTap: () => Navigator.pushNamed(context, Paths.settings),
),
],
),
),
),
endDrawer: const HomeDrawer(),
);
}
}

View File

@@ -32,6 +32,7 @@ class _SettingsPageState extends State<SettingsPage> with GameStoreStateMixin {
@override
Widget build(BuildContext context) {
final baseFontSize = Theme.of(context).textTheme.bodyText1!.fontSize!;
return Scaffold(
appBar: AppBar(
title: const Text('Settings'),
@@ -89,6 +90,56 @@ class _SettingsPageState extends State<SettingsPage> with GameStoreStateMixin {
'Enabling this will make sure the screen doesn\'t turn off while a session is running.',
),
),
ListTile(
title: const Text('UI Font Size'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Change the font size of the entire UI.'),
Row(
children: [
Expanded(
child: Slider(
value: globalSettings.uiTextScale,
onChanged: (value) => setState(
() => globalSettings.uiTextScale = value),
min: 0.5,
max: 2.0,
divisions: 15,
),
),
Text(
'${(globalSettings.uiTextScale * baseFontSize).toStringAsFixed(0)}pt (${(globalSettings.uiTextScale * 100).toStringAsFixed(0)}%)'),
],
),
],
),
),
ListTile(
title: const Text('Game Output Font Size'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Change the font size of the game output text.'),
Row(
children: [
Expanded(
child: Slider(
value: globalSettings.gameTextScale,
onChanged: (value) => setState(
() => globalSettings.gameTextScale = value),
min: 0.5,
max: 2.0,
divisions: 15,
),
),
Text(
'${(globalSettings.gameTextScale * baseFontSize).toStringAsFixed(0)}pt (${(globalSettings.gameTextScale*100).toStringAsFixed(0)}%)'),
],
),
],
),
),
],
),
),

View File

@@ -116,10 +116,11 @@ packages:
ctelnet:
dependency: "direct main"
description:
path: "../ctelnet"
relative: true
source: path
version: "0.2.0"
name: ctelnet
sha256: "4432d54e336dc3674c20e09de9f834a2c103674d74c389ea2efefe4a3e6e77a8"
url: "https://pub.dev"
source: hosted
version: "0.3.0"
cupertino_icons:
dependency: "direct main"
description:
@@ -638,9 +639,10 @@ packages:
terminal_color_parser:
dependency: "direct main"
description:
path: "../terminal_color_parser"
relative: true
source: path
name: terminal_color_parser
sha256: "2766aee8a2c1bba3cf3c87bdf37b24408517ae6aec9956bb29e320f6c9425937"
url: "https://pub.dev"
source: hosted
version: "0.5.0"
test_api:
dependency: transitive

View File

@@ -28,11 +28,12 @@ environment:
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
ctelnet: ^0.3.0
ctelnet: any
# ctelnet:
# path: ../ctelnet
terminal_color_parser:
path: ../terminal_color_parser
terminal_color_parser: any
# terminal_color_parser:
# path: ../terminal_color_parser
flutter:
sdk: flutter