Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Controls/Maps/src/HandlerImpl/Map.Impl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ bool IMap.ClusterClicked(IReadOnlyList<IMapPin> pins, Location location)
return args.Handled;
}

void IMap.LongClicked(Location location) => MapLongClicked?.Invoke(this, new MapClickedEventArgs(location));

MapSpan? IMap.VisibleRegion
{
get
Expand Down
5 changes: 5 additions & 0 deletions src/Controls/Maps/src/Map.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@ public MapSpan? Region
/// </remarks>
public event EventHandler<ClusterClickedEventArgs>? ClusterClicked;

/// <summary>
/// Occurs when the user long-presses/holds on the map control.
/// </summary>
public event EventHandler<MapClickedEventArgs>? MapLongClicked;

/// <summary>
/// Gets the currently visible region of the map.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs.Pins.get -> System.Collecti
Microsoft.Maui.Controls.Maps.Map.ClusterClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.MapElement.IsVisible.get -> bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs.Pins.get -> System.Collecti
Microsoft.Maui.Controls.Maps.Map.ClusterClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.MapElement.IsVisible.get -> bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs.Pins.get -> System.Collecti
Microsoft.Maui.Controls.Maps.Map.ClusterClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.MapElement.IsVisible.get -> bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs.Pins.get -> System.Collecti
Microsoft.Maui.Controls.Maps.Map.ClusterClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.MapElement.IsVisible.get -> bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs.Pins.get -> System.Collecti
Microsoft.Maui.Controls.Maps.Map.ClusterClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.MapElement.IsVisible.get -> bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs.Pins.get -> System.Collecti
Microsoft.Maui.Controls.Maps.Map.ClusterClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.MapElement.IsVisible.get -> bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs.Pins.get -> System.Collecti
Microsoft.Maui.Controls.Maps.Map.ClusterClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.ClusterClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.get -> bool
Microsoft.Maui.Controls.Maps.Map.IsClusteringEnabled.set -> void
Microsoft.Maui.Controls.Maps.Map.MapLongClicked -> System.EventHandler<Microsoft.Maui.Controls.Maps.MapClickedEventArgs!>?
Microsoft.Maui.Controls.Maps.Map.Region.get -> Microsoft.Maui.Maps.MapSpan?
Microsoft.Maui.Controls.Maps.Map.Region.set -> void
Microsoft.Maui.Controls.Maps.MapElement.IsVisible.get -> bool
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:maps="http://schemas.microsoft.com/dotnet/2021/maui/maps"
x:Class="Maui.Controls.Sample.Pages.MapsGalleries.MapLongClickGallery"
Title="Map Long Click">
<Grid RowDefinitions="Auto, Auto, *">
<Label x:Name="InstructionLabel"
Grid.Row="0"
Text="Long press on the map to add a pin at that location"
HorizontalOptions="Center"
Margin="10"/>
<Label x:Name="StatusLabel"
Grid.Row="1"
Text="No long press detected yet"
HorizontalOptions="Center"
Margin="10"
FontAttributes="Bold"/>
<maps:Map x:Name="map"
Grid.Row="2"
MapLongClicked="OnMapLongClicked"/>
</Grid>
</ContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Maps;
using Microsoft.Maui.Devices.Sensors;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Maps;
using Position = Microsoft.Maui.Devices.Sensors.Location;

namespace Maui.Controls.Sample.Pages.MapsGalleries
{
public partial class MapLongClickGallery : ContentPage
{
int _pinCount = 0;

public MapLongClickGallery()
{
InitializeComponent();

// Center map on San Francisco
map.MoveToRegion(MapSpan.FromCenterAndRadius(
new Position(37.7749, -122.4194),
Distance.FromMiles(5)));
}

void OnMapLongClicked(object? sender, MapClickedEventArgs e)
{
_pinCount++;

// Update status label
StatusLabel.Text = $"Long press #{_pinCount} at ({e.Location.Latitude:F4}, {e.Location.Longitude:F4})";
StatusLabel.TextColor = Colors.Green;

// Add a pin at the long-pressed location
var pin = new Pin
{
Label = $"Pin #{_pinCount}",
Address = $"Added via long press",
Location = e.Location,
Type = PinType.Generic
};

map.Pins.Add(pin);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public MapsGallery()
GalleryBuilder.NavButton("Polygon", () => new PolygonsGallery(), Navigation),
GalleryBuilder.NavButton("Element Visibility & ZIndex", () => new MapElementVisibilityGallery(), Navigation),
GalleryBuilder.NavButton("MapElement Click Events", () => new MapElementClickGallery(), Navigation),
GalleryBuilder.NavButton("Map Long Click", () => new MapLongClickGallery(), Navigation),
}
}
};
Expand Down
121 changes: 121 additions & 0 deletions src/Controls/tests/Core.UnitTests/MapTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,57 @@ public void PolylineClickedEventSenderIsPolyline()
Assert.Same(polyline, sender);
}

[Fact]
public void MapLongClickedEventFires()
{
var map = new Map();
var expectedLocation = new Location(37.79752, -122.40183);

bool eventFired = false;
Location receivedLocation = null;
map.MapLongClicked += (s, e) =>
{
eventFired = true;
receivedLocation = e.Location;
};

((IMap)map).LongClicked(expectedLocation);

Assert.True(eventFired);
Assert.Equal(expectedLocation.Latitude, receivedLocation.Latitude);
Assert.Equal(expectedLocation.Longitude, receivedLocation.Longitude);
}

[Fact]
public void MapLongClickedEventSenderIsMap()
{
var map = new Map();

object sender = null;
map.MapLongClicked += (s, e) => sender = s;

((IMap)map).LongClicked(new Location(37.79752, -122.40183));

Assert.Same(map, sender);
}

[Fact]
public void MapLongClickedEventArgsContainsLocation()
{
var map = new Map();
var expectedLocation = new Location(47.6062, -122.3321);

MapClickedEventArgs eventArgs = null;
map.MapLongClicked += (s, e) => eventArgs = e;

((IMap)map).LongClicked(expectedLocation);

Assert.NotNull(eventArgs);
Assert.NotNull(eventArgs.Location);
Assert.Equal(expectedLocation.Latitude, eventArgs.Location.Latitude);
Assert.Equal(expectedLocation.Longitude, eventArgs.Location.Longitude);
}

[Fact]
public void IsClusteringEnabledDefaultValue()
{
Expand Down Expand Up @@ -730,5 +781,75 @@ public void PinClusteringIdentifierImplementsIMapPin()
IMapPin mapPin = pin;
Assert.Equal("custom_cluster", mapPin.ClusteringIdentifier);
}

[Fact]
public void MapClickedAndLongClickedCanCoexist()
{
var map = new Map();
var clickLocation = new Location(37.7749, -122.4194);
var longClickLocation = new Location(47.6062, -122.3321);

bool clickFired = false;
bool longClickFired = false;
map.MapClicked += (s, e) => clickFired = true;
map.MapLongClicked += (s, e) => longClickFired = true;

// Fire click - only click handler should respond
((IMap)map).Clicked(clickLocation);
Assert.True(clickFired);
Assert.False(longClickFired);

// Reset and fire long click - only long click handler should respond
clickFired = false;
((IMap)map).LongClicked(longClickLocation);
Assert.False(clickFired);
Assert.True(longClickFired);
}

[Fact]
public void MapLongClickedDoesNotFireWithoutHandler()
{
var map = new Map();

// Should not throw when no handler is attached
var exception = Record.Exception(() => ((IMap)map).LongClicked(new Location(37.7749, -122.4194)));

Assert.Null(exception);
}

[Fact]
public void MapLongClickedMultipleHandlersAllFire()
{
var map = new Map();
var location = new Location(37.7749, -122.4194);

int handler1Count = 0;
int handler2Count = 0;
map.MapLongClicked += (s, e) => handler1Count++;
map.MapLongClicked += (s, e) => handler2Count++;

((IMap)map).LongClicked(location);

Assert.Equal(1, handler1Count);
Assert.Equal(1, handler2Count);
}

[Fact]
public void MapLongClickedHandlerCanBeRemoved()
{
var map = new Map();
var location = new Location(37.7749, -122.4194);

int fireCount = 0;
EventHandler<MapClickedEventArgs> handler = (s, e) => fireCount++;

map.MapLongClicked += handler;
((IMap)map).LongClicked(location);
Assert.Equal(1, fireCount);

map.MapLongClicked -= handler;
((IMap)map).LongClicked(location);
Assert.Equal(1, fireCount); // Should still be 1, not 2
}
}
}
5 changes: 5 additions & 0 deletions src/Core/maps/src/Core/IMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ public interface IMap : IView
/// <returns><see langword="true"/> if the click was handled and default behavior should be suppressed; otherwise <see langword="false"/>.</returns>
bool ClusterClicked(IReadOnlyList<IMapPin> pins, Location location);

/// <summary>
/// Method called by the handler when user long-presses on the Map.
/// </summary>
void LongClicked(Location position);

/// <summary>
/// Moves the map so that it displays the specified <see cref="MapSpan"/> region.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Core/maps/src/Handlers/Map/MapHandler.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ protected override void DisconnectHandler(MapView platformView)
Map.MarkerClick -= OnMarkerClick;
Map.InfoWindowClick -= OnInfoWindowClick;
Map.MapClick -= OnMapClick;
Map.MapLongClick -= OnMapLongClick;
}

_clusters?.Clear();
Expand Down Expand Up @@ -413,6 +414,7 @@ internal void OnMapReady(GoogleMap map)
map.MarkerClick += OnMarkerClick;
map.InfoWindowClick += OnInfoWindowClick;
map.MapClick += OnMapClick;
map.MapLongClick += OnMapLongClick;

if (VirtualView != null)
{
Expand Down Expand Up @@ -591,6 +593,9 @@ void OnInfoWindowClick(object? sender, GoogleMap.InfoWindowClickEventArgs e)
void OnMapClick(object? sender, GoogleMap.MapClickEventArgs e) =>
VirtualView.Clicked(new Devices.Sensors.Location(e.Point.Latitude, e.Point.Longitude));

void OnMapLongClick(object? sender, GoogleMap.MapLongClickEventArgs e) =>
VirtualView.LongClicked(new Devices.Sensors.Location(e.Point.Latitude, e.Point.Longitude));

void AddPins(IList pins)
{
//Mapper could be called before we have a Map ready
Expand Down
29 changes: 29 additions & 0 deletions src/Core/maps/src/Platform/iOS/MauiMKMapView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public class MauiMKMapView : MKMapView
UITapGestureRecognizer? _mapClickedGestureRecognizer;
bool _isClusteringEnabled;

UILongPressGestureRecognizer? _mapLongClickedGestureRecognizer;

public MauiMKMapView(IMapHandler handler)
{
_handlerRef = new WeakReference<IMapHandler>(handler);
Expand Down Expand Up @@ -306,6 +308,11 @@ void Startup()
{
ShouldReceiveTouch = OnShouldReceiveMapTouch
});

AddGestureRecognizer(_mapLongClickedGestureRecognizer = new UILongPressGestureRecognizer(OnMapLongClicked)
{
ShouldReceiveTouch = OnShouldReceiveMapTouch
});
}

void Cleanup()
Expand All @@ -316,6 +323,12 @@ void Cleanup()
_mapClickedGestureRecognizer.Dispose();
_mapClickedGestureRecognizer = null;
}
if (_mapLongClickedGestureRecognizer != null)
{
RemoveGestureRecognizer(_mapLongClickedGestureRecognizer);
_mapLongClickedGestureRecognizer.Dispose();
_mapLongClickedGestureRecognizer = null;
}
RegionChanged -= MkMapViewOnRegionChanged;
DidSelectAnnotationView -= MkMapViewOnAnnotationViewSelected;
}
Expand Down Expand Up @@ -517,5 +530,21 @@ void SendClickEvent(IMKOverlay overlay)
}
}
}

static void OnMapLongClicked(UILongPressGestureRecognizer recognizer)
{
// Only trigger on the began state to avoid multiple callbacks
if (recognizer.State != UIGestureRecognizerState.Began)
return;

if (recognizer.View is not MauiMKMapView mauiMkMapView)
return;

var tapPoint = recognizer.LocationInView(mauiMkMapView);
var tapGPS = mauiMkMapView.ConvertPoint(tapPoint, mauiMkMapView);

if (mauiMkMapView._handlerRef.TryGetTarget(out IMapHandler? handler))
handler?.VirtualView.LongClicked(new Devices.Sensors.Location(tapGPS.Latitude, tapGPS.Longitude));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#nullable enable
Microsoft.Maui.Maps.IMap.ClusterClicked(System.Collections.Generic.IReadOnlyList<Microsoft.Maui.Maps.IMapPin!>! pins, Microsoft.Maui.Devices.Sensors.Location! location) -> bool
Microsoft.Maui.Maps.IMap.IsClusteringEnabled.get -> bool
Microsoft.Maui.Maps.IMap.LongClicked(Microsoft.Maui.Devices.Sensors.Location! position) -> void
Microsoft.Maui.Maps.IMapElement.Clicked() -> void
Microsoft.Maui.Maps.IMapElement.IsVisible.get -> bool
Microsoft.Maui.Maps.IMapElement.ZIndex.get -> int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#nullable enable
Microsoft.Maui.Maps.IMap.ClusterClicked(System.Collections.Generic.IReadOnlyList<Microsoft.Maui.Maps.IMapPin!>! pins, Microsoft.Maui.Devices.Sensors.Location! location) -> bool
Microsoft.Maui.Maps.IMap.IsClusteringEnabled.get -> bool
Microsoft.Maui.Maps.IMap.LongClicked(Microsoft.Maui.Devices.Sensors.Location! position) -> void
Microsoft.Maui.Maps.IMapElement.Clicked() -> void
Microsoft.Maui.Maps.IMapElement.IsVisible.get -> bool
Microsoft.Maui.Maps.IMapElement.ZIndex.get -> int
Expand Down
Loading
Loading