Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add more events #41

Merged
merged 8 commits into from
Sep 23, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import org.maplibre.android.geometry.LatLng
import org.maplibre.android.geometry.LatLngBounds
import org.maplibre.android.geometry.LatLngQuad
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.MapLibreMap.OnCameraMoveStartedListener.REASON_API_ANIMATION
import org.maplibre.android.maps.MapLibreMap.OnCameraMoveStartedListener.REASON_API_GESTURE
import org.maplibre.android.maps.MapLibreMap.OnCameraMoveStartedListener.REASON_DEVELOPER_ANIMATION
import org.maplibre.android.maps.MapLibreMapOptions
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.OnMapReadyCallback
Expand Down Expand Up @@ -124,7 +127,18 @@ class MapLibreMapController(
val target = mapLibreMap.cameraPosition.target!!
val center = LngLat(target.longitude, target.latitude)
val camera = MapCamera(center, position.zoom, position.tilt, position.bearing)
flutterApi.onCameraMoved(camera) {}
flutterApi.onMoveCamera(camera) {}
}
this.mapLibreMap.addOnCameraIdleListener { flutterApi.onCameraIdle { } }
this.mapView.addOnDidBecomeIdleListener { flutterApi.onIdle { } }
this.mapLibreMap.addOnCameraMoveStartedListener { reason ->
val changeReason = when (reason) {
REASON_API_ANIMATION -> CameraChangeReason.API_ANIMATION
REASON_API_GESTURE -> CameraChangeReason.API_GESTURE
REASON_DEVELOPER_ANIMATION -> CameraChangeReason.DEVELOPER_ANIMATION
else -> null
}
if (changeReason != null) flutterApi.onStartMoveCamera(changeReason) { }
}
val style = Style.Builder().fromUri(mapOptions.style)
mapLibreMap.setStyle(style) { loadedStyle ->
Expand Down Expand Up @@ -560,17 +574,22 @@ class MapLibreMapController(
// remove map bounds
mapLibreMap.setLatLngBoundsForCameraTarget(null)
} else if (oldBounds == null && newBounds != null) {
val bounds = LatLngBounds.from(newBounds.latitudeNorth, newBounds.longitudeEast,
newBounds.latitudeSouth, newBounds.longitudeWest)
val bounds = LatLngBounds.from(
newBounds.latitudeNorth, newBounds.longitudeEast,
newBounds.latitudeSouth, newBounds.longitudeWest
)
mapLibreMap.setLatLngBoundsForCameraTarget(bounds)
} else if (newBounds != null &&
// can get improved when https://github.com/flutter/flutter/issues/118087 is implemented
(oldBounds?.latitudeNorth != newBounds.latitudeNorth
|| oldBounds.latitudeSouth != newBounds.latitudeSouth
|| oldBounds.longitudeEast != newBounds.longitudeEast
|| oldBounds.longitudeWest != newBounds.longitudeWest)) {
val bounds = LatLngBounds.from(newBounds.latitudeNorth, newBounds.longitudeEast,
newBounds.latitudeSouth, newBounds.longitudeWest)
|| oldBounds.latitudeSouth != newBounds.latitudeSouth
|| oldBounds.longitudeEast != newBounds.longitudeEast
|| oldBounds.longitudeWest != newBounds.longitudeWest)
) {
val bounds = LatLngBounds.from(
newBounds.latitudeNorth, newBounds.longitudeEast,
newBounds.latitudeSouth, newBounds.longitudeWest
)
mapLibreMap.setLatLngBoundsForCameraTarget(bounds)
}
}
Expand Down
101 changes: 90 additions & 11 deletions android/src/main/kotlin/com/github/josxha/maplibre/Pigeon.g.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,22 @@ enum class RasterDemEncoding(val raw: Int) {
}
}

/** The reason the camera is changing. */
enum class CameraChangeReason(val raw: Int) {
/** Developer animation. */
DEVELOPER_ANIMATION(0),
/** API animation. */
API_ANIMATION(1),
/** API gesture */
API_GESTURE(2);

companion object {
fun ofRaw(raw: Int): CameraChangeReason? {
return values().firstOrNull { it.raw == raw }
}
}
}

/**
* The map options define initial values for the MapLibre map.
*
Expand Down Expand Up @@ -275,26 +291,31 @@ private open class PigeonPigeonCodec : StandardMessageCodec() {
}
}
131.toByte() -> {
return (readValue(buffer) as Long?)?.let {
CameraChangeReason.ofRaw(it.toInt())
}
}
132.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MapOptions.fromList(it)
}
}
132.toByte() -> {
133.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
LngLat.fromList(it)
}
}
133.toByte() -> {
134.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ScreenLocation.fromList(it)
}
}
134.toByte() -> {
135.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MapCamera.fromList(it)
}
}
135.toByte() -> {
136.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
LngLatBounds.fromList(it)
}
Expand All @@ -312,24 +333,28 @@ private open class PigeonPigeonCodec : StandardMessageCodec() {
stream.write(130)
writeValue(stream, value.raw)
}
is MapOptions -> {
is CameraChangeReason -> {
stream.write(131)
writeValue(stream, value.raw)
}
is MapOptions -> {
stream.write(132)
writeValue(stream, value.toList())
}
is LngLat -> {
stream.write(132)
stream.write(133)
writeValue(stream, value.toList())
}
is ScreenLocation -> {
stream.write(133)
stream.write(134)
writeValue(stream, value.toList())
}
is MapCamera -> {
stream.write(134)
stream.write(135)
writeValue(stream, value.toList())
}
is LngLatBounds -> {
stream.write(135)
stream.write(136)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
Expand Down Expand Up @@ -1094,6 +1119,42 @@ class MapLibreFlutterApi(private val binaryMessenger: BinaryMessenger, private v
}
}
}
/** Callback when the map idles. */
fun onIdle(callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onIdle$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(null) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
}
}
}
/** Callback when the map camera idles. */
fun onCameraIdle(callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onCameraIdle$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(null) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
}
}
}
/**
* Callback when the user performs a secondary click on the map
* (e.g. by default a click with the right mouse button).
Expand Down Expand Up @@ -1152,10 +1213,10 @@ class MapLibreFlutterApi(private val binaryMessenger: BinaryMessenger, private v
}
}
/** Callback when the map camera changes. */
fun onCameraMoved(cameraArg: MapCamera, callback: (Result<Unit>) -> Unit)
fun onMoveCamera(cameraArg: MapCamera, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onCameraMoved$separatedMessageChannelSuffix"
val channelName = "dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onMoveCamera$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(cameraArg)) {
if (it is List<*>) {
Expand All @@ -1169,4 +1230,22 @@ class MapLibreFlutterApi(private val binaryMessenger: BinaryMessenger, private v
}
}
}
/** Callback when the map camera starts changing. */
fun onStartMoveCamera(reasonArg: CameraChangeReason, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.maplibre.MapLibreFlutterApi.onStartMoveCamera$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(reasonArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
}
}
}
}
36 changes: 6 additions & 30 deletions docs/docs/map-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,36 +59,12 @@ void _onEvent(event) {
|--------------------------|-------------|-----------------------------------|-----|---------|-------|-------|
| MapEventMapCreated | | | | | | |
| MapEventStyleLoaded | load | (OnDidFinishLoadingStyleListener) | | | | |
| MapEventClicked | click | | | | | |
| MapEventClicked | click | OnMapClickListener | | | | |
| MapEventDoubleClicked | dblclick | | | | | |
| MapEventSecondaryClicked | contextmenu | | | | | |
| MapEventLongClicked | | | | | | |
| | drag | | | | | |
| | dragstart | | | | | |
| | dragend | | | | | |
| | error | | | | | |
| | idle | OnDidBecomeIdleListener | | | | |
| MapEventLongClicked | | OnMapLongClickListener | | | | |
| MapEventIdle | idle | OnDidBecomeIdleListener | | | | |
| | mousedown | | | | | |
| | mousemove | | | | | |
| | mouseout | | | | | |
| | mouseover | | | | | |
| | mouseup | | | | | |
| | move | OnCameraIsChangingListener | | | | |
| | movestart | OnCameraWillChangeListener | | | | |
| MapEventMovementStopped | moveend | OnCameraDidChangeListener | | | | |
| | pitch | | | | | |
| | pitchstart | | | | | |
| | pitchend | | | | | |
| | pitchend | | | | | |
| | rotate | | | | | |
| | rotatestart | | | | | |
| | rotateend | | | | | |
| | rotateend | | | | | |
| | touchcancel | | | | | |
| | touchend | | | | | |
| | touchmove | | | | | |
| | touchstart | | | | | |
| | wheel | | | | | |
| | zoom | | | | | |
| | zoomstart | | | | | |
| | zoomend | | | | | |
| MapEventStartMoveCamera | | OnCameraMoveStartedListener | | | | |
| MapEventMoveCamera | move | OnCameraMoveListener | | | | |
| MapEventCameraIdle | moveend | OnCameraMoveCanceledListener | | | | |
40 changes: 23 additions & 17 deletions example/lib/events_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,23 @@ class EventsPage extends StatefulWidget {
}

class _EventsPageState extends State<EventsPage> {
String _lastEventMessage = '';
final _eventMessages = <String>[];

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Events')),
body: Column(
body: Stack(
children: [
Padding(
padding: const EdgeInsets.all(8),
child: Text(_lastEventMessage, textAlign: TextAlign.center),
MapLibreMap(
options: MapOptions(center: Position(9.17, 47.68)),
onEvent: _onEvent,
),
Expanded(
child: MapLibreMap(
options: MapOptions(center: Position(9.17, 47.68)),
onEvent: _onEvent,
IgnorePointer(
child: Container(
padding: const EdgeInsets.all(8),
alignment: Alignment.bottomLeft,
child: Text(_eventMessages.join('\n')),
),
),
],
Expand All @@ -38,28 +39,33 @@ class _EventsPageState extends State<EventsPage> {
void _onEvent(MapEvent event) => switch (event) {
MapEventMapCreated() => _print('map created'),
MapEventStyleLoaded() => _print('style loaded'),
MapEventCameraMoved() => _print(
'camera moved: center ${_formatPosition(event.camera.center)}, '
MapEventMoveCamera() => _print(
'move camera: center ${_formatPosition(event.camera.center)}, '
'zoom ${event.camera.zoom.toStringAsFixed(2)}, '
'tilt ${event.camera.tilt.toStringAsFixed(2)}, '
'bearing ${event.camera.bearing.toStringAsFixed(2)}',
),
MapEventClicked() => _print('clicked: ${_formatPosition(event.point)}'),
MapEventDoubleClicked() =>
MapEventStartMoveCamera() =>
_print('start move camera, reason: ${event.reason.name}'),
MapEventClick() => _print('clicked: ${_formatPosition(event.point)}'),
MapEventDoubleClick() =>
_print('double clicked: ${_formatPosition(event.point)}'),
MapEventLongClicked() =>
MapEventLongClick() =>
_print('long clicked: ${_formatPosition(event.point)}'),
MapEventSecondaryClicked() =>
MapEventSecondaryClick() =>
_print('secondary clicked: ${_formatPosition(event.point)}'),
MapEventIdle() => _print('idle'),
MapEventCameraIdle() => _print('camera idle'),
};

void _print(String message) {
debugPrint('[MapLibreMap] $message');
setState(() {
_lastEventMessage = message;
_eventMessages.add(message);
if (_eventMessages.length > 10) _eventMessages.removeAt(0);
});
}

String _formatPosition(Position point) =>
'${point.lng.toStringAsFixed(6)}, ${point.lat.toStringAsFixed(6)}';
'${point.lng.toStringAsFixed(3)}, ${point.lat.toStringAsFixed(3)}';
}
Loading