Skip to content

Commit 7abe4c0

Browse files
committed
Add control toolbar on the left
1 parent b299379 commit 7abe4c0

5 files changed

Lines changed: 142 additions & 79 deletions

File tree

examples/Example01Pan/src/main/kotlin/de/afarber/openmapview/example01pan/ArrowToolbar.kt

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import androidx.compose.material3.MaterialTheme
2222
import androidx.compose.material3.Surface
2323
import androidx.compose.runtime.Composable
2424
import androidx.compose.ui.Modifier
25+
import androidx.compose.ui.graphics.Color
2526
import androidx.compose.ui.graphics.RectangleShape
2627
import androidx.compose.ui.unit.dp
2728

@@ -35,17 +36,18 @@ fun ArrowToolbar(
3536
) {
3637
Surface(
3738
modifier = modifier,
38-
shape = RoundedCornerShape(8.dp),
39+
shape = RoundedCornerShape(ToolbarCornerRadius),
3940
shadowElevation = 6.dp,
4041
color = MaterialTheme.colorScheme.surface,
4142
) {
4243
Row {
4344
FilledIconButton(
4445
onClick = onLeftClick,
4546
modifier = Modifier.size(56.dp),
46-
shape = RoundedCornerShape(topStart = 12.dp, bottomStart = 12.dp),
47+
shape = RoundedCornerShape(topStart = ToolbarCornerRadius, bottomStart = ToolbarCornerRadius),
4748
colors = IconButtonDefaults.filledIconButtonColors(
48-
containerColor = MaterialTheme.colorScheme.surfaceVariant,
49+
containerColor = OsmParkGreen,
50+
contentColor = Color.Black,
4951
),
5052
) {
5153
Icon(Icons.Default.KeyboardDoubleArrowLeft, contentDescription = "Left")
@@ -55,7 +57,8 @@ fun ArrowToolbar(
5557
modifier = Modifier.size(56.dp),
5658
shape = RectangleShape,
5759
colors = IconButtonDefaults.filledIconButtonColors(
58-
containerColor = MaterialTheme.colorScheme.surfaceVariant,
60+
containerColor = OsmParkGreen,
61+
contentColor = Color.Black,
5962
),
6063
) {
6164
Icon(Icons.Default.KeyboardDoubleArrowUp, contentDescription = "Up")
@@ -65,17 +68,19 @@ fun ArrowToolbar(
6568
modifier = Modifier.size(56.dp),
6669
shape = RectangleShape,
6770
colors = IconButtonDefaults.filledIconButtonColors(
68-
containerColor = MaterialTheme.colorScheme.surfaceVariant,
71+
containerColor = OsmParkGreen,
72+
contentColor = Color.Black,
6973
),
7074
) {
7175
Icon(Icons.Default.KeyboardDoubleArrowDown, contentDescription = "Down")
7276
}
7377
FilledIconButton(
7478
onClick = onRightClick,
7579
modifier = Modifier.size(56.dp),
76-
shape = RoundedCornerShape(topEnd = 12.dp, bottomEnd = 12.dp),
80+
shape = RoundedCornerShape(topEnd = ToolbarCornerRadius, bottomEnd = ToolbarCornerRadius),
7781
colors = IconButtonDefaults.filledIconButtonColors(
78-
containerColor = MaterialTheme.colorScheme.surfaceVariant,
82+
containerColor = OsmParkGreen,
83+
contentColor = Color.Black,
7984
),
8085
) {
8186
Icon(Icons.Default.KeyboardDoubleArrowRight, contentDescription = "Right")
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright (c) 2025 Alexander Farber
3+
* SPDX-License-Identifier: MIT
4+
*
5+
* This file is part of the OpenMapView project (https://github.com/afarber/OpenMapView)
6+
*/
7+
8+
package de.afarber.openmapview.example01pan
9+
10+
import androidx.compose.ui.graphics.Color
11+
import androidx.compose.ui.unit.dp
12+
13+
// OSM-inspired colors
14+
val OsmParkGreen = Color(0xFFAAD3A2)
15+
val OsmHighwayPink = Color(0xFFE892A2)
16+
val OsmWaterBlue = Color(0xFFAAD3DF)
17+
18+
val ToolbarCornerRadius = 8.dp
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (c) 2025 Alexander Farber
3+
* SPDX-License-Identifier: MIT
4+
*
5+
* This file is part of the OpenMapView project (https://github.com/afarber/OpenMapView)
6+
*/
7+
8+
package de.afarber.openmapview.example01pan
9+
10+
import androidx.compose.foundation.layout.Column
11+
import androidx.compose.foundation.layout.PaddingValues
12+
import androidx.compose.foundation.layout.size
13+
import androidx.compose.foundation.shape.RoundedCornerShape
14+
import androidx.compose.material3.ButtonDefaults
15+
import androidx.compose.material3.FilledTonalButton
16+
import androidx.compose.material3.MaterialTheme
17+
import androidx.compose.material3.Surface
18+
import androidx.compose.material3.Text
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.ui.Modifier
21+
import androidx.compose.ui.graphics.Color
22+
import androidx.compose.ui.unit.dp
23+
24+
@Composable
25+
fun ControlToolbar(
26+
boundsEnabled: Boolean,
27+
onBoundsClick: () -> Unit,
28+
onResetClick: () -> Unit,
29+
modifier: Modifier = Modifier,
30+
) {
31+
Surface(
32+
modifier = modifier,
33+
shape = RoundedCornerShape(ToolbarCornerRadius),
34+
shadowElevation = 6.dp,
35+
color = MaterialTheme.colorScheme.surface,
36+
) {
37+
Column {
38+
FilledTonalButton(
39+
onClick = onBoundsClick,
40+
modifier = Modifier.size(width = 80.dp, height = 48.dp),
41+
shape = RoundedCornerShape(topStart = ToolbarCornerRadius, topEnd = ToolbarCornerRadius),
42+
contentPadding = PaddingValues(4.dp),
43+
colors = ButtonDefaults.filledTonalButtonColors(
44+
containerColor = OsmWaterBlue,
45+
contentColor = Color.Black,
46+
),
47+
) {
48+
Text(
49+
text = if (boundsEnabled) "Bounds On" else "Bounds Off",
50+
style = MaterialTheme.typography.bodySmall,
51+
)
52+
}
53+
FilledTonalButton(
54+
onClick = onResetClick,
55+
modifier = Modifier.size(width = 80.dp, height = 48.dp),
56+
shape = RoundedCornerShape(bottomStart = ToolbarCornerRadius, bottomEnd = ToolbarCornerRadius),
57+
contentPadding = PaddingValues(4.dp),
58+
colors = ButtonDefaults.filledTonalButtonColors(
59+
containerColor = OsmWaterBlue,
60+
contentColor = Color.Black,
61+
),
62+
) {
63+
Text("Reset", style = MaterialTheme.typography.bodySmall)
64+
}
65+
}
66+
}
67+
}

examples/Example01Pan/src/main/kotlin/de/afarber/openmapview/example01pan/LocationToolbar.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import androidx.compose.foundation.layout.Column
1111
import androidx.compose.foundation.layout.PaddingValues
1212
import androidx.compose.foundation.layout.size
1313
import androidx.compose.foundation.shape.RoundedCornerShape
14+
import androidx.compose.material3.ButtonDefaults
1415
import androidx.compose.material3.FilledTonalButton
1516
import androidx.compose.material3.MaterialTheme
1617
import androidx.compose.material3.Surface
1718
import androidx.compose.material3.Text
1819
import androidx.compose.runtime.Composable
1920
import androidx.compose.ui.Modifier
21+
import androidx.compose.ui.graphics.Color
2022
import androidx.compose.ui.graphics.RectangleShape
2123
import androidx.compose.ui.unit.dp
2224
import de.afarber.openmapview.LatLng
@@ -29,23 +31,27 @@ fun LocationToolbar(
2931
) {
3032
Surface(
3133
modifier = modifier,
32-
shape = RoundedCornerShape(8.dp),
34+
shape = RoundedCornerShape(ToolbarCornerRadius),
3335
shadowElevation = 6.dp,
3436
color = MaterialTheme.colorScheme.surface,
3537
) {
3638
Column {
3739
locations.forEachIndexed { index, location ->
3840
val shape = when {
39-
locations.size == 1 -> RoundedCornerShape(12.dp)
40-
index == 0 -> RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp)
41-
index == locations.lastIndex -> RoundedCornerShape(bottomStart = 12.dp, bottomEnd = 12.dp)
41+
locations.size == 1 -> RoundedCornerShape(ToolbarCornerRadius)
42+
index == 0 -> RoundedCornerShape(topStart = ToolbarCornerRadius, topEnd = ToolbarCornerRadius)
43+
index == locations.lastIndex -> RoundedCornerShape(bottomStart = ToolbarCornerRadius, bottomEnd = ToolbarCornerRadius)
4244
else -> RectangleShape
4345
}
4446
FilledTonalButton(
4547
onClick = { onLocationClick(location) },
4648
modifier = Modifier.size(width = 72.dp, height = 48.dp),
4749
shape = shape,
4850
contentPadding = PaddingValues(0.dp),
51+
colors = ButtonDefaults.filledTonalButtonColors(
52+
containerColor = OsmHighwayPink,
53+
contentColor = Color.Black,
54+
),
4955
) {
5056
Text("Loc ${index + 1}", style = MaterialTheme.typography.bodySmall)
5157
}

examples/Example01Pan/src/main/kotlin/de/afarber/openmapview/example01pan/MainActivity.kt

Lines changed: 35 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,11 @@ import android.os.Bundle
1111
import androidx.activity.ComponentActivity
1212
import androidx.activity.compose.setContent
1313
import androidx.compose.foundation.background
14-
import androidx.compose.foundation.layout.Arrangement
1514
import androidx.compose.foundation.layout.Box
1615
import androidx.compose.foundation.layout.Column
17-
import androidx.compose.foundation.layout.Row
1816
import androidx.compose.foundation.layout.fillMaxSize
19-
import androidx.compose.foundation.layout.fillMaxWidth
2017
import androidx.compose.foundation.layout.padding
2118
import androidx.compose.foundation.shape.RoundedCornerShape
22-
import androidx.compose.material3.Button
23-
import androidx.compose.material3.ButtonDefaults
2419
import androidx.compose.material3.MaterialTheme
2520
import androidx.compose.material3.Surface
2621
import androidx.compose.material3.Text
@@ -32,6 +27,7 @@ import androidx.compose.runtime.setValue
3227
import androidx.compose.ui.Alignment
3328
import androidx.compose.ui.Modifier
3429
import androidx.compose.ui.draw.clip
30+
import androidx.compose.ui.graphics.Color
3531
import androidx.compose.ui.unit.dp
3632
import androidx.compose.ui.viewinterop.AndroidView
3733
import de.afarber.openmapview.CameraUpdateFactory
@@ -119,101 +115,72 @@ fun MapViewScreen() {
119115
Column(
120116
modifier = Modifier
121117
.align(Alignment.TopCenter)
122-
.padding(8.dp)
123-
.clip(RoundedCornerShape(8.dp))
124-
.background(MaterialTheme.colorScheme.surface.copy(alpha = 0.8f))
125-
.padding(8.dp),
118+
.padding(16.dp)
119+
.clip(RoundedCornerShape(ToolbarCornerRadius))
120+
.background(Color.White)
121+
.padding(16.dp),
126122
horizontalAlignment = Alignment.CenterHorizontally,
127123
) {
128124
Text(
129125
text = "Camera: $cameraState",
130-
style = MaterialTheme.typography.bodyMedium,
131-
color = MaterialTheme.colorScheme.onSurface,
126+
color = Color.Black,
132127
)
133128
Text(
134129
text = "Center: $centerPosition",
135-
style = MaterialTheme.typography.bodySmall,
136-
color = MaterialTheme.colorScheme.onSurfaceVariant,
130+
color = Color.Black,
137131
)
138132
Text(
139133
text = "Bounds: ${if (boundsEnabled) "On" else "Off"}",
140-
style = MaterialTheme.typography.bodySmall,
141134
color = if (boundsEnabled) {
142-
MaterialTheme.colorScheme.primary
135+
Color.Black
143136
} else {
144-
MaterialTheme.colorScheme.onSurfaceVariant
137+
Color.Red
145138
},
146139
)
147140
}
148141

149-
// Arrow toolbar at bottom left
142+
// Arrow toolbar at bottom
150143
ArrowToolbar(
151144
onLeftClick = { mapView?.moveCamera(CameraUpdateFactory.scrollBy(-100f, 0f)) },
152145
onUpClick = { mapView?.moveCamera(CameraUpdateFactory.scrollBy(0f, -100f)) },
153146
onDownClick = { mapView?.moveCamera(CameraUpdateFactory.scrollBy(0f, 100f)) },
154147
onRightClick = { mapView?.moveCamera(CameraUpdateFactory.scrollBy(100f, 0f)) },
155148
modifier = Modifier
156-
.align(Alignment.BottomStart)
157-
.padding(start = 16.dp, bottom = 16.dp),
149+
.align(Alignment.BottomCenter)
150+
.padding(bottom = 16.dp),
158151
)
159152

160-
// Location toolbar at top right
153+
// Location toolbar at right
161154
LocationToolbar(
162155
locations = listOf(location1, location2, location3),
163156
onLocationClick = { location ->
164157
mapView?.animateCamera(CameraUpdateFactory.newLatLng(location), 500)
165158
},
166159
modifier = Modifier
167-
.align(Alignment.TopEnd)
168-
.padding(top = 8.dp, end = 8.dp),
160+
.align(Alignment.CenterEnd)
161+
.padding(end = 36.dp),
169162
)
170163

171-
// Control panel at bottom
172-
Column(
173-
modifier = Modifier
174-
.align(Alignment.BottomCenter)
175-
.padding(16.dp)
176-
.fillMaxWidth(),
177-
horizontalAlignment = Alignment.CenterHorizontally,
178-
) {
179-
// Row 3: Bounds toggle and Reset
180-
Row(
181-
modifier = Modifier.fillMaxWidth(),
182-
horizontalArrangement = Arrangement.SpaceEvenly,
183-
) {
184-
Button(
185-
onClick = {
186-
if (boundsEnabled) {
187-
mapView?.setLatLngBoundsForCameraTarget(null)
188-
boundsEnabled = false
189-
} else {
190-
mapView?.setLatLngBoundsForCameraTarget(bochumBounds)
191-
boundsEnabled = true
192-
}
193-
},
194-
colors = if (boundsEnabled) {
195-
ButtonDefaults.outlinedButtonColors(
196-
containerColor = MaterialTheme.colorScheme.primaryContainer,
197-
)
198-
} else {
199-
ButtonDefaults.outlinedButtonColors()
200-
},
201-
) {
202-
Text(
203-
text = if (boundsEnabled) "Bounds On" else "Bounds Off",
204-
style = MaterialTheme.typography.bodySmall,
205-
)
206-
}
207-
Button(
208-
onClick = {
209-
mapView?.moveCamera(
210-
CameraUpdateFactory.newLatLngZoom(initialLocation, 14.0f),
211-
)
212-
},
213-
) {
214-
Text("Reset", style = MaterialTheme.typography.bodySmall)
164+
// Control toolbar at left
165+
ControlToolbar(
166+
boundsEnabled = boundsEnabled,
167+
onBoundsClick = {
168+
if (boundsEnabled) {
169+
mapView?.setLatLngBoundsForCameraTarget(null)
170+
boundsEnabled = false
171+
} else {
172+
mapView?.setLatLngBoundsForCameraTarget(bochumBounds)
173+
boundsEnabled = true
215174
}
216-
}
217-
}
175+
},
176+
onResetClick = {
177+
mapView?.moveCamera(
178+
CameraUpdateFactory.newLatLngZoom(initialLocation, 14.0f),
179+
)
180+
},
181+
modifier = Modifier
182+
.align(Alignment.CenterStart)
183+
.padding(start = 36.dp),
184+
)
218185
}
219186
}

0 commit comments

Comments
 (0)