mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +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 get isVoiceCompressorEnabled => _isVoiceCompressorEnabled;
|
||||||
bool _isVoiceLimiterEnabled = true;
|
bool _isVoiceLimiterEnabled = true;
|
||||||
bool get isVoiceLimiterEnabled => _isVoiceLimiterEnabled;
|
bool get isVoiceLimiterEnabled => _isVoiceLimiterEnabled;
|
||||||
|
double _messageFontScale = 1.0;
|
||||||
|
double get messageFontScale => _messageFontScale;
|
||||||
bool _autoAddDiscoveredContacts = false;
|
bool _autoAddDiscoveredContacts = false;
|
||||||
bool get autoAddDiscoveredContacts => _autoAddDiscoveredContacts;
|
bool get autoAddDiscoveredContacts => _autoAddDiscoveredContacts;
|
||||||
bool _autoRouteRotationEnabled =
|
bool _autoRouteRotationEnabled =
|
||||||
@@ -144,6 +146,7 @@ class AppProvider with ChangeNotifier {
|
|||||||
_loadVoiceBandPassFilterEnabled();
|
_loadVoiceBandPassFilterEnabled();
|
||||||
_loadVoiceCompressorEnabled();
|
_loadVoiceCompressorEnabled();
|
||||||
_loadVoiceLimiterEnabled();
|
_loadVoiceLimiterEnabled();
|
||||||
|
_loadMessageFontScale();
|
||||||
_loadAutoAddDiscoveredContacts();
|
_loadAutoAddDiscoveredContacts();
|
||||||
_loadMessagingRouteSettings();
|
_loadMessagingRouteSettings();
|
||||||
unawaited(_pathHistoryService.initialize());
|
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.
|
/// Load auto-add discovered contacts setting from shared preferences.
|
||||||
Future<void> _loadAutoAddDiscoveredContacts() async {
|
Future<void> _loadAutoAddDiscoveredContacts() async {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1023,6 +1023,28 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
subtitle: const Text('Delete all stored message history'),
|
subtitle: const Text('Delete all stored message history'),
|
||||||
onTap: _clearMessages,
|
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'),
|
_buildSectionHeader('Voice'),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import '../../providers/connection_provider.dart';
|
|||||||
import '../../providers/drawing_provider.dart';
|
import '../../providers/drawing_provider.dart';
|
||||||
import '../../providers/voice_provider.dart';
|
import '../../providers/voice_provider.dart';
|
||||||
import '../../providers/image_provider.dart' as ip;
|
import '../../providers/image_provider.dart' as ip;
|
||||||
|
import '../../providers/app_provider.dart';
|
||||||
import '../drawing_minimap_preview.dart';
|
import '../drawing_minimap_preview.dart';
|
||||||
import '../../models/ble_packet_log.dart';
|
import '../../models/ble_packet_log.dart';
|
||||||
import '../../services/sar_template_service.dart';
|
import '../../services/sar_template_service.dart';
|
||||||
@@ -1649,6 +1650,36 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
return null;
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Display system messages with minimal styling
|
// Display system messages with minimal styling
|
||||||
@@ -1666,6 +1697,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
}
|
}
|
||||||
final isSarMarker = message.isSarMarker;
|
final isSarMarker = message.isSarMarker;
|
||||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||||
|
final messageFontScale = context.watch<AppProvider>().messageFontScale;
|
||||||
|
|
||||||
// Determine if this is own message
|
// Determine if this is own message
|
||||||
final connectionProvider = context.read<ConnectionProvider>();
|
final connectionProvider = context.read<ConnectionProvider>();
|
||||||
@@ -1779,6 +1811,9 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
final shouldFloatBubble = widget.isCompact;
|
final shouldFloatBubble = widget.isCompact;
|
||||||
|
final baseBodyStyle = Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodyMedium?.copyWith(fontSize: 14 * messageFontScale);
|
||||||
final bubble = ConstrainedBox(
|
final bubble = ConstrainedBox(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth: shouldFloatBubble
|
maxWidth: shouldFloatBubble
|
||||||
@@ -2197,10 +2232,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (drawing == null) {
|
if (drawing == null) {
|
||||||
return Text(
|
return Text(message.text, style: baseBodyStyle);
|
||||||
message.text,
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final String drawingTypeLabel;
|
final String drawingTypeLabel;
|
||||||
@@ -2286,10 +2318,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
)
|
)
|
||||||
// Regular message content
|
// Regular message content
|
||||||
else if (!message.isDrawing || widget.isCompact)
|
else if (!message.isDrawing || widget.isCompact)
|
||||||
Text(
|
Text(message.text, style: baseBodyStyle),
|
||||||
message.text,
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
|
|
||||||
if (!widget.isCompact &&
|
if (!widget.isCompact &&
|
||||||
!isSarMarker &&
|
!isSarMarker &&
|
||||||
@@ -2509,6 +2538,12 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
children: [
|
||||||
|
if (message.isContactMessage)
|
||||||
|
_buildDirectMessageStatusIndicator(
|
||||||
|
context,
|
||||||
|
message,
|
||||||
|
)
|
||||||
|
else ...[
|
||||||
Icon(
|
Icon(
|
||||||
getDeliveryStatusIcon(message.deliveryStatus),
|
getDeliveryStatusIcon(message.deliveryStatus),
|
||||||
size: 12,
|
size: 12,
|
||||||
@@ -2521,7 +2556,9 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
message.getLocalizedDeliveryStatus(context),
|
message.getLocalizedDeliveryStatus(
|
||||||
|
context,
|
||||||
|
),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
@@ -2536,6 +2573,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
// Show retry button for failed messages
|
// Show retry button for failed messages
|
||||||
if (message.deliveryStatus ==
|
if (message.deliveryStatus ==
|
||||||
MessageDeliveryStatus.failed) ...[
|
MessageDeliveryStatus.failed) ...[
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../l10n/app_localizations.dart';
|
import '../../l10n/app_localizations.dart';
|
||||||
|
import '../../providers/app_provider.dart';
|
||||||
|
|
||||||
class MessagesComposer extends StatelessWidget {
|
class MessagesComposer extends StatelessWidget {
|
||||||
final TextEditingController textController;
|
final TextEditingController textController;
|
||||||
@@ -253,6 +255,10 @@ class _MessageInput extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final messageFontScale = context.watch<AppProvider>().messageFontScale;
|
||||||
|
const baseFontSize = 15.0;
|
||||||
|
final resolvedFontSize = baseFontSize * messageFontScale;
|
||||||
|
|
||||||
return AnimatedContainer(
|
return AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 180),
|
duration: const Duration(milliseconds: 180),
|
||||||
constraints: const BoxConstraints(minHeight: 46, maxHeight: 132),
|
constraints: const BoxConstraints(minHeight: 46, maxHeight: 132),
|
||||||
@@ -286,12 +292,12 @@ class _MessageInput extends StatelessWidget {
|
|||||||
maxLines: 4,
|
maxLines: 4,
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
inputFormatters: [messageByteLimiter],
|
inputFormatters: [messageByteLimiter],
|
||||||
style: const TextStyle(fontSize: 15),
|
style: TextStyle(fontSize: resolvedFontSize),
|
||||||
textAlignVertical: TextAlignVertical.center,
|
textAlignVertical: TextAlignVertical.center,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: AppLocalizations.of(context)!.typeYourMessage,
|
hintText: AppLocalizations.of(context)!.typeYourMessage,
|
||||||
hintStyle: TextStyle(
|
hintStyle: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: resolvedFontSize,
|
||||||
color: Theme.of(
|
color: Theme.of(
|
||||||
context,
|
context,
|
||||||
).colorScheme.onSurfaceVariant.withValues(alpha: 0.9),
|
).colorScheme.onSurfaceVariant.withValues(alpha: 0.9),
|
||||||
|
|||||||
Reference in New Issue
Block a user