From 257c4bf21bd22ef033e0ddb07d2ed244ffac2c4f Mon Sep 17 00:00:00 2001 From: eliotcougar Date: Sat, 1 Aug 2026 05:16:14 +0300 Subject: [PATCH] Stabilize profile editor IME scrolling (#5980) Profile editors are bounded forms whose focused child must remain stable while the IME changes the available height. Lazy list item boundaries can be remeasured or detached during these viewport changes, causing visible jitter and leaving lower or multiline fields obscured. Compose the form eagerly in one vertically scrollable Column. Preserve the existing spacing, scrollbar, IME padding, and bottom padding, and update only protocol-editor call sites to the ColumnScope API. --- .../v2ray/ang/ui/server/BaseServerActivity.kt | 19 +++++++++--------- .../v2ray/ang/ui/server/ServerHttpActivity.kt | 4 ++-- .../ang/ui/server/ServerHysteria2Activity.kt | 4 ++-- .../ui/server/ServerShadowsocksActivity.kt | 20 +++++++++---------- .../ang/ui/server/ServerSocksActivity.kt | 4 ++-- .../ang/ui/server/ServerTrojanActivity.kt | 20 +++++++++---------- .../ang/ui/server/ServerVlessActivity.kt | 20 +++++++++---------- .../ang/ui/server/ServerVmessActivity.kt | 20 +++++++++---------- .../ang/ui/server/ServerWireguardActivity.kt | 4 ++-- 9 files changed, 53 insertions(+), 62 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/BaseServerActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/BaseServerActivity.kt index d6a62045..6c78582b 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/BaseServerActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/BaseServerActivity.kt @@ -3,14 +3,13 @@ package com.v2ray.ang.ui.server import android.os.Bundle import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -431,10 +430,10 @@ abstract class BaseServerActivity : BaseComponentActivity() { protected fun ServerEditorScaffold( title: String, onSaveClick: () -> Unit, - content: LazyListScope.() -> Unit + content: @Composable ColumnScope.() -> Unit ) { var showDeleteDialog by rememberSaveable { mutableStateOf(false) } - val listState = rememberLazyListState() + val scrollState = rememberScrollState() Scaffold( contentWindowInsets = ScaffoldDefaults.contentWindowInsets, topBar = { @@ -460,15 +459,15 @@ abstract class BaseServerActivity : BaseComponentActivity() { ) } ) { innerPadding -> - LazyColumn( - state = listState, + Column( modifier = Modifier .fillMaxSize() .padding(innerPadding) .consumeWindowInsets(innerPadding) .imePadding() - .verticalScrollbar(listState), - contentPadding = PaddingValues(bottom = 36.dp), + .verticalScroll(scrollState) + .verticalScrollbar(scrollState) + .padding(bottom = 36.dp), verticalArrangement = Arrangement.spacedBy(8.dp), content = content ) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerHttpActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerHttpActivity.kt index e35bab24..33d3a63d 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerHttpActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerHttpActivity.kt @@ -27,8 +27,8 @@ class ServerHttpActivity : BaseServerActivity() { title = serverConfigType.toString(), onSaveClick = { saveServer(uiState) } ) { - item { CommonBasicFields(uiState) } - item { HttpProtocolFields(uiState) } + CommonBasicFields(uiState) + HttpProtocolFields(uiState) } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerHysteria2Activity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerHysteria2Activity.kt index 8d1d4f4f..1e5c8640 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerHysteria2Activity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerHysteria2Activity.kt @@ -31,8 +31,8 @@ class ServerHysteria2Activity : BaseServerActivity() { title = serverConfigType.toString(), onSaveClick = { saveServer(uiState) } ) { - item { CommonBasicFields(uiState, showPort = false) } - item { Hysteria2ProtocolFields(uiState) } + CommonBasicFields(uiState, showPort = false) + Hysteria2ProtocolFields(uiState) } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerShadowsocksActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerShadowsocksActivity.kt index 76610f22..ee812764 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerShadowsocksActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerShadowsocksActivity.kt @@ -32,17 +32,15 @@ class ServerShadowsocksActivity : BaseServerActivity() { title = serverConfigType.toString(), onSaveClick = { saveServer(uiState) } ) { - item { CommonBasicFields(uiState) } - item { ShadowsocksProtocolFields(uiState, securityOptions) } - item { CommonNetworkFields(uiState, options) } - item { - CommonStreamSecurityFields( - state = uiState, - options = options, - scope = scope, - buildProfileItem = { uiState.toProfileItem(initialConfig) } - ) - } + CommonBasicFields(uiState) + ShadowsocksProtocolFields(uiState, securityOptions) + CommonNetworkFields(uiState, options) + CommonStreamSecurityFields( + state = uiState, + options = options, + scope = scope, + buildProfileItem = { uiState.toProfileItem(initialConfig) } + ) } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerSocksActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerSocksActivity.kt index caf1a696..9f3a993b 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerSocksActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerSocksActivity.kt @@ -27,8 +27,8 @@ class ServerSocksActivity : BaseServerActivity() { title = serverConfigType.toString(), onSaveClick = { saveServer(uiState) } ) { - item { CommonBasicFields(uiState) } - item { SocksProtocolFields(uiState) } + CommonBasicFields(uiState) + SocksProtocolFields(uiState) } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerTrojanActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerTrojanActivity.kt index e53db275..9713c3d2 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerTrojanActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerTrojanActivity.kt @@ -30,17 +30,15 @@ class ServerTrojanActivity : BaseServerActivity() { title = serverConfigType.toString(), onSaveClick = { saveServer(uiState) } ) { - item { CommonBasicFields(uiState) } - item { TrojanProtocolFields(uiState) } - item { CommonNetworkFields(uiState, options) } - item { - CommonStreamSecurityFields( - state = uiState, - options = options, - scope = scope, - buildProfileItem = { uiState.toProfileItem(initialConfig) } - ) - } + CommonBasicFields(uiState) + TrojanProtocolFields(uiState) + CommonNetworkFields(uiState, options) + CommonStreamSecurityFields( + state = uiState, + options = options, + scope = scope, + buildProfileItem = { uiState.toProfileItem(initialConfig) } + ) } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerVlessActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerVlessActivity.kt index 653dd1bb..19595c39 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerVlessActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerVlessActivity.kt @@ -33,17 +33,15 @@ class ServerVlessActivity : BaseServerActivity() { title = serverConfigType.toString(), onSaveClick = { saveServer(uiState) } ) { - item { CommonBasicFields(uiState) } - item { VlessProtocolFields(uiState, flowOptions) } - item { CommonNetworkFields(uiState, options) } - item { - CommonStreamSecurityFields( - state = uiState, - options = options, - scope = scope, - buildProfileItem = { uiState.toProfileItem(initialConfig) } - ) - } + CommonBasicFields(uiState) + VlessProtocolFields(uiState, flowOptions) + CommonNetworkFields(uiState, options) + CommonStreamSecurityFields( + state = uiState, + options = options, + scope = scope, + buildProfileItem = { uiState.toProfileItem(initialConfig) } + ) } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerVmessActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerVmessActivity.kt index 47744360..7eec9f46 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerVmessActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerVmessActivity.kt @@ -33,17 +33,15 @@ class ServerVmessActivity : BaseServerActivity() { title = serverConfigType.toString(), onSaveClick = { saveServer(uiState) } ) { - item { CommonBasicFields(uiState) } - item { VmessProtocolFields(uiState, securityOptions) } - item { CommonNetworkFields(uiState, options) } - item { - CommonStreamSecurityFields( - state = uiState, - options = options, - scope = scope, - buildProfileItem = { uiState.toProfileItem(initialConfig) } - ) - } + CommonBasicFields(uiState) + VmessProtocolFields(uiState, securityOptions) + CommonNetworkFields(uiState, options) + CommonStreamSecurityFields( + state = uiState, + options = options, + scope = scope, + buildProfileItem = { uiState.toProfileItem(initialConfig) } + ) } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerWireguardActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerWireguardActivity.kt index 07b3c0a9..44954ada 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerWireguardActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/server/ServerWireguardActivity.kt @@ -28,8 +28,8 @@ class ServerWireguardActivity : BaseServerActivity() { title = serverConfigType.toString(), onSaveClick = { saveServer(uiState) } ) { - item { CommonBasicFields(uiState) } - item { WireguardProtocolFields(uiState) } + CommonBasicFields(uiState) + WireguardProtocolFields(uiState) } }