mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 08:20:36 +00:00
Add message font scale setting
This commit is contained in:
@@ -90,6 +90,8 @@ class AppProvider with ChangeNotifier {
|
||||
bool get isVoiceCompressorEnabled => _isVoiceCompressorEnabled;
|
||||
bool _isVoiceLimiterEnabled = true;
|
||||
bool get isVoiceLimiterEnabled => _isVoiceLimiterEnabled;
|
||||
double _messageFontScale = 1.0;
|
||||
double get messageFontScale => _messageFontScale;
|
||||
bool _autoAddDiscoveredContacts = false;
|
||||
bool get autoAddDiscoveredContacts => _autoAddDiscoveredContacts;
|
||||
bool _autoRouteRotationEnabled =
|
||||
@@ -144,6 +146,7 @@ class AppProvider with ChangeNotifier {
|
||||
_loadVoiceBandPassFilterEnabled();
|
||||
_loadVoiceCompressorEnabled();
|
||||
_loadVoiceLimiterEnabled();
|
||||
_loadMessageFontScale();
|
||||
_loadAutoAddDiscoveredContacts();
|
||||
_loadMessagingRouteSettings();
|
||||
unawaited(_pathHistoryService.initialize());
|
||||
@@ -431,6 +434,30 @@ class AppProvider with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadMessageFontScale() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
_messageFontScale = (prefs.getDouble('message_font_scale') ?? 1.0).clamp(
|
||||
0.85,
|
||||
1.4,
|
||||
);
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Error loading message font scale setting: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setMessageFontScale(double scale) async {
|
||||
try {
|
||||
_messageFontScale = scale.clamp(0.85, 1.4);
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setDouble('message_font_scale', _messageFontScale);
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Error saving message font scale setting: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Load auto-add discovered contacts setting from shared preferences.
|
||||
Future<void> _loadAutoAddDiscoveredContacts() async {
|
||||
try {
|
||||
|
||||
@@ -1023,6 +1023,28 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
subtitle: const Text('Delete all stored message history'),
|
||||
onTap: _clearMessages,
|
||||
),
|
||||
Consumer<AppProvider>(
|
||||
builder: (context, appProvider, child) => ListTile(
|
||||
leading: const Icon(Icons.format_size),
|
||||
title: const Text('Message font size'),
|
||||
subtitle: Text(
|
||||
'${(appProvider.messageFontScale * 100).round()}% of default',
|
||||
),
|
||||
trailing: SizedBox(
|
||||
width: 150,
|
||||
child: Slider(
|
||||
value: appProvider.messageFontScale,
|
||||
min: 0.85,
|
||||
max: 1.4,
|
||||
divisions: 11,
|
||||
label: '${(appProvider.messageFontScale * 100).round()}%',
|
||||
onChanged: (value) {
|
||||
appProvider.setMessageFontScale(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
|
||||
_buildSectionHeader('Voice'),
|
||||
|
||||
@@ -13,6 +13,7 @@ import '../../providers/connection_provider.dart';
|
||||
import '../../providers/drawing_provider.dart';
|
||||
import '../../providers/voice_provider.dart';
|
||||
import '../../providers/image_provider.dart' as ip;
|
||||
import '../../providers/app_provider.dart';
|
||||
import '../drawing_minimap_preview.dart';
|
||||
import '../../models/ble_packet_log.dart';
|
||||
import '../../services/sar_template_service.dart';
|
||||
@@ -1649,6 +1650,36 @@ class _MessageBubbleState extends State<MessageBubble> {
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget _buildDirectMessageStatusIndicator(
|
||||
BuildContext context,
|
||||
Message message,
|
||||
) {
|
||||
switch (message.deliveryStatus) {
|
||||
case MessageDeliveryStatus.delivered:
|
||||
return Icon(
|
||||
Icons.done_all,
|
||||
size: 16,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
);
|
||||
case MessageDeliveryStatus.sent:
|
||||
return Icon(
|
||||
Icons.done,
|
||||
size: 16,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
);
|
||||
case MessageDeliveryStatus.sending:
|
||||
return Icon(Icons.schedule, size: 15, color: Colors.orange);
|
||||
case MessageDeliveryStatus.failed:
|
||||
return const Icon(Icons.error_outline, size: 15, color: Colors.red);
|
||||
case MessageDeliveryStatus.received:
|
||||
return Icon(
|
||||
Icons.done_all,
|
||||
size: 16,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Display system messages with minimal styling
|
||||
@@ -1666,6 +1697,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
||||
}
|
||||
final isSarMarker = message.isSarMarker;
|
||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
final messageFontScale = context.watch<AppProvider>().messageFontScale;
|
||||
|
||||
// Determine if this is own message
|
||||
final connectionProvider = context.read<ConnectionProvider>();
|
||||
@@ -1779,6 +1811,9 @@ class _MessageBubbleState extends State<MessageBubble> {
|
||||
: null;
|
||||
|
||||
final shouldFloatBubble = widget.isCompact;
|
||||
final baseBodyStyle = Theme.of(
|
||||
context,
|
||||
).textTheme.bodyMedium?.copyWith(fontSize: 14 * messageFontScale);
|
||||
final bubble = ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: shouldFloatBubble
|
||||
@@ -2197,10 +2232,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
||||
);
|
||||
|
||||
if (drawing == null) {
|
||||
return Text(
|
||||
message.text,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
);
|
||||
return Text(message.text, style: baseBodyStyle);
|
||||
}
|
||||
|
||||
final String drawingTypeLabel;
|
||||
@@ -2286,10 +2318,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
||||
)
|
||||
// Regular message content
|
||||
else if (!message.isDrawing || widget.isCompact)
|
||||
Text(
|
||||
message.text,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
Text(message.text, style: baseBodyStyle),
|
||||
|
||||
if (!widget.isCompact &&
|
||||
!isSarMarker &&
|
||||
@@ -2509,33 +2538,42 @@ class _MessageBubbleState extends State<MessageBubble> {
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Icon(
|
||||
getDeliveryStatusIcon(message.deliveryStatus),
|
||||
size: 12,
|
||||
color: getDeliveryStatusColor(
|
||||
message.deliveryStatus,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 3),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
message.getLocalizedDeliveryStatus(context),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelSmall
|
||||
?.copyWith(
|
||||
color: getDeliveryStatusColor(
|
||||
message.deliveryStatus,
|
||||
),
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
if (message.isContactMessage)
|
||||
_buildDirectMessageStatusIndicator(
|
||||
context,
|
||||
message,
|
||||
)
|
||||
else ...[
|
||||
Icon(
|
||||
getDeliveryStatusIcon(message.deliveryStatus),
|
||||
size: 12,
|
||||
color: getDeliveryStatusColor(
|
||||
message.deliveryStatus,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 3),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
message.getLocalizedDeliveryStatus(
|
||||
context,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelSmall
|
||||
?.copyWith(
|
||||
color: getDeliveryStatusColor(
|
||||
message.deliveryStatus,
|
||||
),
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
// Show retry button for failed messages
|
||||
if (message.deliveryStatus ==
|
||||
MessageDeliveryStatus.failed) ...[
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../l10n/app_localizations.dart';
|
||||
import '../../providers/app_provider.dart';
|
||||
|
||||
class MessagesComposer extends StatelessWidget {
|
||||
final TextEditingController textController;
|
||||
@@ -253,6 +255,10 @@ class _MessageInput extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final messageFontScale = context.watch<AppProvider>().messageFontScale;
|
||||
const baseFontSize = 15.0;
|
||||
final resolvedFontSize = baseFontSize * messageFontScale;
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 180),
|
||||
constraints: const BoxConstraints(minHeight: 46, maxHeight: 132),
|
||||
@@ -286,12 +292,12 @@ class _MessageInput extends StatelessWidget {
|
||||
maxLines: 4,
|
||||
keyboardType: TextInputType.multiline,
|
||||
inputFormatters: [messageByteLimiter],
|
||||
style: const TextStyle(fontSize: 15),
|
||||
style: TextStyle(fontSize: resolvedFontSize),
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
decoration: InputDecoration(
|
||||
hintText: AppLocalizations.of(context)!.typeYourMessage,
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 15,
|
||||
fontSize: resolvedFontSize,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurfaceVariant.withValues(alpha: 0.9),
|
||||
|
||||
Reference in New Issue
Block a user