refactor: adopt M3 Expressive components from material3 1.11.0-alpha06 (#5063)

This commit is contained in:
James Rich 2026-04-10 21:10:03 -05:00 committed by GitHub
parent a6423d0a0f
commit 3794c79dae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 128 additions and 69 deletions

View file

@ -77,8 +77,6 @@ import org.meshtastic.app.map.cluster.RadiusMarkerClusterer
import org.meshtastic.app.map.component.CacheLayout
import org.meshtastic.app.map.component.DownloadButton
import org.meshtastic.app.map.component.EditWaypointDialog
import org.meshtastic.app.map.component.MapButton
import org.meshtastic.app.map.component.MapControlsOverlay
import org.meshtastic.app.map.model.CustomTileSource
import org.meshtastic.app.map.model.MarkerWithLabel
import org.meshtastic.core.common.gpsDisabled
@ -130,6 +128,8 @@ import org.meshtastic.core.ui.util.formatAgo
import org.meshtastic.core.ui.util.showToast
import org.meshtastic.feature.map.BaseMapViewModel.MapFilterState
import org.meshtastic.feature.map.LastHeardFilter
import org.meshtastic.feature.map.component.MapButton
import org.meshtastic.feature.map.component.MapControlsOverlay
import org.meshtastic.proto.Config.DisplayConfig.DisplayUnits
import org.meshtastic.proto.Waypoint
import org.osmdroid.bonuspack.utils.BonusPackHelper.getBitmapFromVectorDrawable

View file

@ -42,7 +42,6 @@ import org.meshtastic.app.map.addCopyright
import org.meshtastic.app.map.addPolyline
import org.meshtastic.app.map.addPositionMarkers
import org.meshtastic.app.map.addScaleBarOverlay
import org.meshtastic.app.map.component.MapControlsOverlay
import org.meshtastic.app.map.model.CustomTileSource
import org.meshtastic.app.map.rememberMapViewWithLifecycle
import org.meshtastic.core.common.util.nowSeconds
@ -50,6 +49,7 @@ import org.meshtastic.core.model.util.GeoConstants.DEG_D
import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.last_heard_filter_label
import org.meshtastic.feature.map.LastHeardFilter
import org.meshtastic.feature.map.component.MapControlsOverlay
import org.meshtastic.proto.Position
import org.osmdroid.util.BoundingBox
import org.osmdroid.util.GeoPoint
@ -61,8 +61,8 @@ import kotlin.math.roundToInt
*
* Applies the [lastHeardTrackFilter][org.meshtastic.feature.map.BaseMapViewModel.MapFilterState.lastHeardTrackFilter]
* from [MapViewModel] to filter positions by time, matching the behavior of the Google Maps implementation. Includes a
* minimal [MapControlsOverlay][org.meshtastic.app.map.component.MapControlsOverlay] with a track time filter slider so
* users can adjust the time range directly from the map.
* minimal [MapControlsOverlay][org.meshtastic.feature.map.component.MapControlsOverlay] with a track time filter slider
* so users can adjust the time range directly from the map.
*
* Supports optional synchronized selection via [selectedPositionTime] and [onPositionSelected].
*

View file

@ -97,8 +97,6 @@ import org.meshtastic.app.map.component.ClusterItemsListDialog
import org.meshtastic.app.map.component.CustomMapLayersSheet
import org.meshtastic.app.map.component.CustomTileProviderManagerSheet
import org.meshtastic.app.map.component.EditWaypointDialog
import org.meshtastic.app.map.component.MapButton
import org.meshtastic.app.map.component.MapControlsOverlay
import org.meshtastic.app.map.component.MapFilterDropdown
import org.meshtastic.app.map.component.MapTypeDropdown
import org.meshtastic.app.map.component.NodeClusterMarkers
@ -137,6 +135,8 @@ import org.meshtastic.core.ui.util.formatAgo
import org.meshtastic.core.ui.util.formatPositionTime
import org.meshtastic.feature.map.BaseMapViewModel.MapFilterState
import org.meshtastic.feature.map.LastHeardFilter
import org.meshtastic.feature.map.component.MapButton
import org.meshtastic.feature.map.component.MapControlsOverlay
import org.meshtastic.feature.map.tracerouteNodeSelection
import org.meshtastic.proto.Config.DisplayConfig.DisplayUnits
import org.meshtastic.proto.Position

View file

@ -31,6 +31,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconToggleButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
@ -125,7 +126,10 @@ fun CustomMapLayersSheet(
}
}
}
IconButton(onClick = { onToggleVisibility(layer.id) }) {
IconToggleButton(
checked = layer.isVisible,
onCheckedChange = { onToggleVisibility(layer.id) },
) {
Icon(
imageVector =
if (layer.isVisible) {

View file

@ -19,7 +19,7 @@ package org.meshtastic.core.ui.component
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconToggleButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -36,6 +36,7 @@ import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.hide_password
import org.meshtastic.core.resources.show_password
import org.meshtastic.core.ui.icon.MeshtasticIcons
import org.meshtastic.core.ui.icon.Visibility
import org.meshtastic.core.ui.icon.VisibilityOff
@Composable
@ -63,10 +64,9 @@ fun EditPasswordPreference(
onFocusChanged = {},
visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
IconButton(onClick = { isPasswordVisible = !isPasswordVisible }) {
IconToggleButton(checked = isPasswordVisible, onCheckedChange = { isPasswordVisible = it }) {
Icon(
imageVector =
if (isPasswordVisible) MeshtasticIcons.VisibilityOff else MeshtasticIcons.VisibilityOff,
imageVector = if (isPasswordVisible) MeshtasticIcons.VisibilityOff else MeshtasticIcons.Visibility,
contentDescription =
if (isPasswordVisible) {
stringResource(Res.string.hide_password)

View file

@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -43,22 +44,28 @@ fun PreferenceFooter(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
val mediumHeight = ButtonDefaults.MediumContainerHeight
if (negativeText != null) {
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
ElevatedButton(
modifier = Modifier.height(48.dp).weight(1f),
shapes = ButtonDefaults.shapesFor(mediumHeight),
modifier = Modifier.height(mediumHeight).weight(1f),
colors = ButtonDefaults.filledTonalButtonColors(),
onClick = onNegativeClicked,
) {
Text(text = negativeText)
Text(text = negativeText, style = ButtonDefaults.textStyleFor(mediumHeight))
}
}
if (positiveText != null) {
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
ElevatedButton(
modifier = Modifier.height(48.dp).weight(1f),
shapes = ButtonDefaults.shapesFor(mediumHeight),
modifier = Modifier.height(mediumHeight).weight(1f),
colors = ButtonDefaults.buttonColors(),
onClick = { if (enabled) onPositiveClicked() },
) {
Text(text = positiveText)
Text(text = positiveText, style = ButtonDefaults.textStyleFor(mediumHeight))
}
}
}

View file

@ -29,6 +29,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
@ -240,21 +241,33 @@ fun ScannedQrCodeDialog(
val unselectedColors =
ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.onSurface)
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
val mediumHeight = ButtonDefaults.MediumContainerHeight
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
OutlinedButton(
onClick = { shouldReplace = false },
modifier = Modifier.height(48.dp).weight(1f),
shapes = ButtonDefaults.shapesFor(mediumHeight),
modifier = Modifier.height(mediumHeight).weight(1f),
colors = if (!shouldReplace) selectedColors else unselectedColors,
) {
Text(text = stringResource(Res.string.add))
Text(
text = stringResource(Res.string.add),
style = ButtonDefaults.textStyleFor(mediumHeight),
)
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
OutlinedButton(
onClick = { shouldReplace = true },
modifier = Modifier.height(48.dp).weight(1f),
shapes = ButtonDefaults.shapesFor(mediumHeight),
modifier = Modifier.height(mediumHeight).weight(1f),
enabled = incoming.lora_config != null,
colors = if (shouldReplace) selectedColors else unselectedColors,
) {
Text(text = stringResource(Res.string.replace))
Text(
text = stringResource(Res.string.replace),
style = ButtonDefaults.textStyleFor(mediumHeight),
)
}
}
}

View file

@ -26,6 +26,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -74,17 +75,20 @@ fun ConnectingDeviceInfo(
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
val largeHeight = ButtonDefaults.LargeContainerHeight
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
Button(
modifier = Modifier.fillMaxWidth().height(56.dp),
shape = MaterialTheme.shapes.medium,
onClick = onClickDisconnect,
shapes = ButtonDefaults.shapesFor(largeHeight),
modifier = Modifier.fillMaxWidth().height(largeHeight),
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.StatusRed,
contentColor = Color.White,
),
onClick = onClickDisconnect,
) {
Text(stringResource(Res.string.disconnect), style = MaterialTheme.typography.titleMedium)
Text(stringResource(Res.string.disconnect), style = ButtonDefaults.textStyleFor(largeHeight))
}
}
}

View file

@ -35,15 +35,18 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.CircularWavyProgressIndicator
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.LinearWavyProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
@ -381,24 +384,35 @@ private fun ReadyState(
Spacer(Modifier.height(16.dp))
if (selectedReleaseType == FirmwareReleaseType.LOCAL) {
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
val largeHeight = ButtonDefaults.LargeContainerHeight
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
Button(
onClick = {
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
showDisclaimer = true
},
modifier = Modifier.fillMaxWidth().height(56.dp),
shapes = ButtonDefaults.shapesFor(largeHeight),
modifier = Modifier.fillMaxWidth().height(largeHeight),
) {
Icon(MeshtasticIcons.Folder, contentDescription = null)
Spacer(Modifier.width(8.dp))
Text(stringResource(Res.string.firmware_update_select_file))
Text(
stringResource(Res.string.firmware_update_select_file),
style = ButtonDefaults.textStyleFor(largeHeight),
)
}
} else if (state.release != null) {
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
val largeHeight = ButtonDefaults.LargeContainerHeight
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
Button(
onClick = {
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
showDisclaimer = true
},
modifier = Modifier.fillMaxWidth().height(56.dp),
shapes = ButtonDefaults.shapesFor(largeHeight),
modifier = Modifier.fillMaxWidth().height(largeHeight),
) {
Icon(
imageVector =
@ -416,6 +430,7 @@ private fun ReadyState(
resource = Res.string.firmware_update_method_detail,
stringResource(state.updateMethod.description),
),
style = ButtonDefaults.textStyleFor(largeHeight),
)
}
Spacer(Modifier.height(24.dp))
@ -680,7 +695,8 @@ private fun ProgressContent(
tint = MaterialTheme.colorScheme.primary,
)
} else {
CircularProgressIndicator(
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
CircularWavyProgressIndicator(
progress = { if (isUpdating) progressState.progress else 1f },
modifier = Modifier.size(64.dp),
)
@ -708,7 +724,8 @@ private fun ProgressContent(
Spacer(Modifier.height(12.dp))
if (isDownloading || isUpdating) {
LinearProgressIndicator(
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
LinearWavyProgressIndicator(
progress = { progressState.progress },
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp),
)
@ -850,8 +867,15 @@ private fun SuccessState(onDone: () -> Unit) {
textAlign = TextAlign.Center,
)
Spacer(Modifier.height(32.dp))
Button(onClick = onDone, modifier = Modifier.fillMaxWidth().height(56.dp)) {
Text(stringResource(Res.string.firmware_update_done))
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
val largeHeight = ButtonDefaults.LargeContainerHeight
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
Button(
onClick = onDone,
shapes = ButtonDefaults.shapesFor(largeHeight),
modifier = Modifier.fillMaxWidth().height(largeHeight),
) {
Text(stringResource(Res.string.firmware_update_done), style = ButtonDefaults.textStyleFor(largeHeight))
}
}
}

View file

@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.app.map.component
package org.meshtastic.feature.map.component
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.Icon

View file

@ -14,13 +14,15 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.app.map.component
package org.meshtastic.feature.map.component
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FloatingToolbarDefaults
import androidx.compose.material3.HorizontalFloatingToolbar
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@ -41,8 +43,9 @@ import org.meshtastic.core.ui.icon.Tune
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
/**
* Shared map controls overlay used by both Google and F-Droid map views. Provides compass, filter button, location
* tracking button, and optional slots for flavor-specific content (map type selector, layers, refresh).
* Shared map controls overlay using [HorizontalFloatingToolbar] for Material 3 Expressive styling. Provides compass,
* filter button, location tracking button, and optional slots for flavor-specific content (map type selector, layers,
* refresh).
*
* @param onToggleFilterMenu Callback to open/close the filter dropdown.
* @param filterDropdownContent Composable rendered inside a [Box] alongside the filter button typically a
@ -54,6 +57,7 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusRed
* @param isRefreshing Whether a refresh is currently in progress.
* @param onRefresh Callback when the refresh button is clicked.
*/
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Suppress("LongParameterList")
@Composable
fun MapControlsOverlay(
@ -71,7 +75,11 @@ fun MapControlsOverlay(
isRefreshing: Boolean = false,
onRefresh: () -> Unit = {},
) {
Row(modifier = modifier) {
HorizontalFloatingToolbar(
expanded = true,
modifier = modifier,
colors = FloatingToolbarDefaults.standardFloatingToolbarColors(),
) {
// Compass
CompassButton(onClick = onCompassClick, bearing = bearing, isFollowing = followPhoneBearing)

View file

@ -31,7 +31,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@ -96,7 +95,6 @@ private const val ACTIVE_ALPHA = 0.5f
private const val INACTIVE_ALPHA = 0.2f
private const val GRID_COLUMNS = 3
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
@Suppress("LongMethod")
fun NodeItem(
@ -391,7 +389,6 @@ private fun MetricsGrid(items: List<@Composable () -> Unit>) {
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun NodeItemHeader(
thatNode: Node,

View file

@ -31,6 +31,7 @@ import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconToggleButton
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@ -273,7 +274,7 @@ fun <T> BaseMetricScreen(
)
}
}
IconButton(onClick = { isChartExpanded = !isChartExpanded }) {
IconToggleButton(checked = isChartExpanded, onCheckedChange = { isChartExpanded = it }) {
Icon(
imageVector =
if (isChartExpanded) {

View file

@ -15,7 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@file:Suppress("MagicNumber")
@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
package org.meshtastic.feature.node.metrics
@ -31,7 +30,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text

View file

@ -15,7 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@file:Suppress("TooManyFunctions")
@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
package org.meshtastic.feature.node.metrics
@ -30,7 +29,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text

View file

@ -36,7 +36,6 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults
@ -155,7 +154,6 @@ private fun HostMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick:
}
/** Card body showing timestamp, load averages with progress bars, memory, disk, and uptime. */
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun HostMetricsCardContent(time: String, hostMetrics: org.meshtastic.proto.HostMetrics?) {
Column(modifier = Modifier.padding(12.dp)) {

View file

@ -14,8 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
package org.meshtastic.feature.node.metrics
import androidx.compose.foundation.BorderStroke
@ -35,7 +33,6 @@ import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text

View file

@ -14,8 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
package org.meshtastic.feature.node.metrics
import androidx.compose.foundation.layout.Column
@ -28,7 +26,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable

View file

@ -15,7 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@file:Suppress("MagicNumber")
@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
package org.meshtastic.feature.node.metrics
@ -31,7 +30,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilterChip
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text

View file

@ -14,8 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
package org.meshtastic.feature.node.metrics
import androidx.compose.foundation.layout.Arrangement
@ -31,7 +29,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable

View file

@ -36,7 +36,6 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@ -242,7 +241,6 @@ private fun TracerouteCard(
/** Card body showing timestamp, route summary text/icon, and metric indicators. */
@Composable
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
private fun TracerouteCardContent(time: String, summaryText: String, icon: ImageVector, point: TraceroutePoint) {
Column(modifier = Modifier.padding(12.dp)) {
Row(

View file

@ -35,6 +35,7 @@ import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconToggleButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
@ -138,7 +139,7 @@ fun DebugScreen(onNavigateUp: () -> Unit, viewModel: DebugViewModel) {
canNavigateUp = true,
onNavigateUp = onNavigateUp,
actions = {
IconButton(onClick = { showSettings = !showSettings }) {
IconToggleButton(checked = showSettings, onCheckedChange = { showSettings = it }) {
Icon(imageVector = MeshtasticIcons.Settings, contentDescription = null)
}
DebugMenuActions(deleteLogs = { viewModel.requestDeleteAllLogs() })

View file

@ -22,6 +22,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -220,12 +222,19 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit, onO
onValueChanged = { formState.value = formState.value.copy(wifi_psk = it) },
)
HorizontalDivider()
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
val mediumHeight = ButtonDefaults.MediumContainerHeight
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
Button(
onClick = { barcodeScanner.startScan() },
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp).height(48.dp),
shapes = ButtonDefaults.shapesFor(mediumHeight),
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp).height(mediumHeight),
enabled = state.connected,
) {
Text(text = stringResource(Res.string.wifi_qr_code_scan))
Text(
text = stringResource(Res.string.wifi_qr_code_scan),
style = ButtonDefaults.textStyleFor(mediumHeight),
)
}
}
}

View file

@ -24,9 +24,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -37,14 +38,22 @@ import androidx.compose.ui.unit.dp
@Composable
fun NodeActionButton(
modifier: Modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp).height(48.dp),
modifier: Modifier = Modifier,
title: String,
enabled: Boolean,
icon: ImageVector? = null,
iconTint: Color? = null,
onClick: () -> Unit,
) {
Button(onClick = { onClick() }, enabled = enabled, modifier = modifier) {
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
val mediumHeight = ButtonDefaults.MediumContainerHeight
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
Button(
onClick = { onClick() },
shapes = ButtonDefaults.shapesFor(mediumHeight),
enabled = enabled,
modifier = modifier.then(Modifier.fillMaxWidth().padding(vertical = 4.dp).height(mediumHeight)),
) {
Row(verticalAlignment = Alignment.CenterVertically) {
if (icon != null) {
Icon(
@ -55,7 +64,7 @@ fun NodeActionButton(
)
Spacer(modifier = Modifier.width(8.dp))
}
Text(text = title, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.weight(1f))
Text(text = title, style = ButtonDefaults.textStyleFor(mediumHeight), modifier = Modifier.weight(1f))
}
}
}

View file

@ -52,6 +52,7 @@ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconToggleButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
@ -414,7 +415,7 @@ internal fun ConnectedContent(
singleLine = true,
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
IconButton(onClick = { passwordVisible = !passwordVisible }) {
IconToggleButton(checked = passwordVisible, onCheckedChange = { passwordVisible = it }) {
Icon(
imageVector =
if (passwordVisible) MeshtasticIcons.VisibilityOff else MeshtasticIcons.Visibility,