diff --git a/lib/model/settings/settings.dart b/lib/model/settings/settings.dart index 0b041da31..1c59522ba 100644 --- a/lib/model/settings/settings.dart +++ b/lib/model/settings/settings.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:math'; import 'package:aves/l10n/l10n.dart'; @@ -15,6 +16,7 @@ import 'package:aves_map/aves_map.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:latlong2/latlong.dart'; final Settings settings = Settings._private(); @@ -121,11 +123,14 @@ class Settings extends ChangeNotifier { static const subtitleBackgroundColorKey = 'subtitle_background_color'; // info - static const infoMapStyleKey = 'info_map_style'; static const infoMapZoomKey = 'info_map_zoom'; static const coordinateFormatKey = 'coordinates_format'; static const unitSystemKey = 'unit_system'; + // map + static const mapStyleKey = 'info_map_style'; + static const mapDefaultCenterKey = 'map_default_center'; + // search static const saveSearchHistoryKey = 'save_search_history'; static const searchHistoryKey = 'search_history'; @@ -198,10 +203,10 @@ class Settings extends ChangeNotifier { // availability final defaultMapStyle = mobileServices.defaultMapStyle; if (mobileServices.mapStyles.contains(defaultMapStyle)) { - infoMapStyle = defaultMapStyle; + mapStyle = defaultMapStyle; } else { final styles = EntryMapStyle.values.whereNot((v) => v.needMobileService).toList(); - infoMapStyle = styles[Random().nextInt(styles.length)]; + mapStyle = styles[Random().nextInt(styles.length)]; } } @@ -556,14 +561,6 @@ class Settings extends ChangeNotifier { // info - EntryMapStyle get infoMapStyle { - final preferred = getEnumOrDefault(infoMapStyleKey, SettingsDefaults.infoMapStyle, EntryMapStyle.values); - final available = availability.mapStyles; - return available.contains(preferred) ? preferred : available.first; - } - - set infoMapStyle(EntryMapStyle newValue) => setAndNotify(infoMapStyleKey, newValue.toString()); - double get infoMapZoom => getDouble(infoMapZoomKey) ?? SettingsDefaults.infoMapZoom; set infoMapZoom(double newValue) => setAndNotify(infoMapZoomKey, newValue); @@ -576,6 +573,23 @@ class Settings extends ChangeNotifier { set unitSystem(UnitSystem newValue) => setAndNotify(unitSystemKey, newValue.toString()); + // map + + EntryMapStyle get mapStyle { + final preferred = getEnumOrDefault(mapStyleKey, SettingsDefaults.infoMapStyle, EntryMapStyle.values); + final available = availability.mapStyles; + return available.contains(preferred) ? preferred : available.first; + } + + set mapStyle(EntryMapStyle newValue) => setAndNotify(mapStyleKey, newValue.toString()); + + LatLng? get mapDefaultCenter { + final json = getString(mapDefaultCenterKey); + return json != null ? LatLng.fromJson(jsonDecode(json)) : null; + } + + set mapDefaultCenter(LatLng? newValue) => setAndNotify(mapDefaultCenterKey, newValue != null ? jsonEncode(newValue.toJson()) : null); + // search bool get saveSearchHistory => getBoolOrDefault(saveSearchHistoryKey, SettingsDefaults.saveSearchHistory); @@ -863,7 +877,8 @@ class Settings extends ChangeNotifier { case videoLoopModeKey: case videoControlsKey: case subtitleTextAlignmentKey: - case infoMapStyleKey: + case mapStyleKey: + case mapDefaultCenterKey: case coordinateFormatKey: case unitSystemKey: case accessibilityAnimationsKey: diff --git a/lib/widgets/common/map/buttons/panel.dart b/lib/widgets/common/map/buttons/panel.dart index 8914f6e8f..63c974513 100644 --- a/lib/widgets/common/map/buttons/panel.dart +++ b/lib/widgets/common/map/buttons/panel.dart @@ -129,11 +129,11 @@ class MapButtonPanel extends StatelessWidget { onPressed: () => showSelectionDialog( context: context, builder: (context) => AvesSelectionDialog( - initialValue: settings.infoMapStyle, + initialValue: settings.mapStyle, options: Map.fromEntries(availability.mapStyles.map((v) => MapEntry(v, v.getName(context)))), title: context.l10n.mapStyleDialogTitle, ), - onSelection: (v) => settings.infoMapStyle = v, + onSelection: (v) => settings.mapStyle = v, ), tooltip: context.l10n.mapStyleTooltip, ), diff --git a/lib/widgets/common/map/geo_map.dart b/lib/widgets/common/map/geo_map.dart index 44de80547..3518e7095 100644 --- a/lib/widgets/common/map/geo_map.dart +++ b/lib/widgets/common/map/geo_map.dart @@ -149,7 +149,7 @@ class _GeoMapState extends State { } return Selector( - selector: (context, s) => s.infoMapStyle, + selector: (context, s) => s.mapStyle, builder: (context, mapStyle, child) { final isHeavy = mapStyle.isHeavy; Widget _buildMarkerWidget(MarkerKey key) => ImageMarker( @@ -281,6 +281,7 @@ class _GeoMapState extends State { final overlayEntry = widget.overlayEntry; if (overlayEntry != null) { + // fit map to overlaid item final corner1 = overlayEntry.topLeft; final corner2 = overlayEntry.bottomRight; if (corner1 != null && corner2 != null) { @@ -290,10 +291,26 @@ class _GeoMapState extends State { } } if (bounds == null) { + // fit map to located items final initialCenter = widget.initialCenter; final points = initialCenter != null ? {initialCenter} : entries.map((v) => v.latLng!).toSet(); + if (points.isNotEmpty) { + bounds = ZoomedBounds.fromPoints( + points: points, + collocationZoom: settings.infoMapZoom, + ); + final center = bounds.projectedCenter; + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + settings.mapDefaultCenter = center; + }); + } + } + if (bounds == null) { + // fallback to default center + final center = settings.mapDefaultCenter ??= Constants.wonders[Random().nextInt(Constants.wonders.length)]; bounds = ZoomedBounds.fromPoints( - points: points.isNotEmpty ? points : {Constants.wonders[Random().nextInt(Constants.wonders.length)]}, + points: {center}, collocationZoom: settings.infoMapZoom, ); } diff --git a/lib/widgets/dialogs/location_pick_dialog.dart b/lib/widgets/dialogs/location_pick_dialog.dart index b9b7d1b14..6879d8603 100644 --- a/lib/widgets/dialogs/location_pick_dialog.dart +++ b/lib/widgets/dialogs/location_pick_dialog.dart @@ -78,7 +78,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin void initState() { super.initState(); - if (settings.infoMapStyle.isHeavy) { + if (settings.mapStyle.isHeavy) { _isPageAnimatingNotifier = ValueNotifier(true); Future.delayed(Durations.pageTransitionAnimation * timeDilation).then((_) { if (!mounted) return; diff --git a/lib/widgets/map/map_page.dart b/lib/widgets/map/map_page.dart index e8ba5b67f..464c7b74e 100644 --- a/lib/widgets/map/map_page.dart +++ b/lib/widgets/map/map_page.dart @@ -107,7 +107,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin void initState() { super.initState(); - if (settings.infoMapStyle.isHeavy) { + if (settings.mapStyle.isHeavy) { _isPageAnimatingNotifier.value = true; Future.delayed(Durations.pageTransitionAnimation * timeDilation).then((_) { if (!mounted) return; @@ -170,7 +170,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin return true; }, child: Selector( - selector: (context, s) => s.infoMapStyle, + selector: (context, s) => s.mapStyle, builder: (context, mapStyle, child) { late Widget scroller; if (mapStyle.isHeavy) { diff --git a/test_driver/driver_shaders.dart b/test_driver/driver_shaders.dart index 4901d9df8..055ea6545 100644 --- a/test_driver/driver_shaders.dart +++ b/test_driver/driver_shaders.dart @@ -38,6 +38,6 @@ Future configureAndLaunch() async { ..showOverlayThumbnailPreview = true ..imageBackground = EntryBackground.checkered // info - ..infoMapStyle = EntryMapStyle.googleNormal; + ..mapStyle = EntryMapStyle.googleNormal; app.main(); }