[RDCT] PY-84272: Show UI feature buttons only for available backend features

GitOrigin-RevId: b833d1aa4e9e24d9f477b90b534a58e3153a14cf
This commit is contained in:
Vyacheslav Moklev
2025-10-02 11:44:10 +00:00
committed by intellij-monorepo-bot
parent 71250e3095
commit f4af8086ed
5 changed files with 57 additions and 6 deletions
@@ -12,6 +12,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
internal class BackendWelcomeScreenFeatureApi : WelcomeScreenFeatureApi {
override suspend fun getAvailableFeatureIds(): List<String> {
return WelcomeScreenFeatureBackend.getFeatureIds()
}
override suspend fun onClick(projectId: ProjectId, featureKey: String) {
val project = projectId.findProject()
val feature = WelcomeScreenFeatureBackend.getForFeatureKey(featureKey) ?: run {
@@ -23,6 +23,10 @@ abstract class WelcomeScreenFeatureBackend {
private val EP_NAME: ExtensionPointName<WelcomeScreenFeatureBackend> =
ExtensionPointName.create("com.intellij.platform.ide.welcomeScreenFeatureBackend")
fun getFeatureIds(): List<String> {
return EP_NAME.extensionList.map { it.featureKey }
}
fun getForFeatureKey(featureKey: String): WelcomeScreenFeatureBackend? {
return EP_NAME.lazySequence().firstOrNull { it.featureKey == featureKey }
}
@@ -7,6 +7,9 @@ import androidx.compose.ui.graphics.vector.ImageVector
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import com.intellij.platform.project.projectId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
import org.jetbrains.jewel.ui.icon.IconKey
@@ -15,6 +18,8 @@ import javax.swing.Icon
@ApiStatus.Internal
interface WelcomeRightTabContentProvider {
val coroutineScope: CoroutineScope
// Use Valkyrie IDEA plugin to generate the ImageVector
val backgroundImageVectorLight: ImageVector
val backgroundImageVectorDark: ImageVector
@@ -28,13 +33,34 @@ interface WelcomeRightTabContentProvider {
@Composable
fun getFeatureButtonModels(project: Project): List<FeatureButtonModel>
data class FeatureButtonModel(
/**
* Base feature button model. Use for frontend-only features.
* For backend-based features, use [FeatureButtonModelWithBackend] and
* register a `WelcomeScreenFeatureBackend` implementation.
*/
open class FeatureButtonModel(
val text: String,
val icon: IconKey,
val tint: Color = Color.Unspecified,
val onClick: () -> Unit,
val onClick: (Project, CoroutineScope) -> Unit,
)
/**
* Feature button backed by a `WelcomeScreenFeatureBackend` implementation.
*/
class FeatureButtonModelWithBackend(
val featureKey: String,
text: String,
icon: IconKey,
tint: Color = Color.Unspecified,
val beforeOnClick: suspend (Project) -> Unit = {}
) : FeatureButtonModel(text, icon, tint, { project, coroutineScope ->
coroutineScope.launch {
beforeOnClick(project)
WelcomeScreenFeatureApi.getInstance().onClick(project.projectId(), featureKey)
}
})
companion object {
private val EP_NAME: ExtensionPointName<WelcomeRightTabContentProvider> = ExtensionPointName("com.intellij.platform.ide.welcomeScreenContentProvider")
@@ -11,6 +11,8 @@ import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
@Rpc
interface WelcomeScreenFeatureApi : RemoteApi<Unit> {
suspend fun getAvailableFeatureIds(): List<String>
suspend fun onClick(projectId: ProjectId, featureKey: String)
companion object {
@@ -32,11 +32,14 @@ import com.intellij.platform.ide.nonModalWelcomeScreen.GoFileDragAndDropHandler
import com.intellij.platform.ide.nonModalWelcomeScreen.NON_MODAL_WELCOME_SCREEN_SETTING_ID
import com.intellij.platform.ide.nonModalWelcomeScreen.NonModalWelcomeScreenBundle
import com.intellij.platform.ide.nonModalWelcomeScreen.WelcomeScreenTabUsageCollector
import com.intellij.platform.ide.nonModalWelcomeScreen.rightTab.WelcomeRightTabContentProvider.FeatureButtonModelWithBackend
import com.intellij.platform.ide.nonModalWelcomeScreen.rightTab.WelcomeScreenRightTabComboBoxModel.KeymapModel
import com.intellij.platform.ide.nonModalWelcomeScreen.rightTab.WelcomeScreenRightTabComboBoxModel.ThemeModel
import com.intellij.platform.ide.nonModalWelcomeScreen.rightTab.components.WelcomeScreenCustomButton
import com.intellij.platform.ide.nonModalWelcomeScreen.rightTab.components.WelcomeScreenCustomListComboBox
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.jewel.bridge.JewelComposePanel
@@ -138,12 +141,24 @@ class WelcomeScreenRightTab(
@Composable
fun FeatureGrid(modifier: Modifier = Modifier) {
val coroutineScope = contentProvider.coroutineScope
var backendFeatureIds by remember { mutableStateOf(emptySet<String>()) }
LaunchedEffect(true) {
coroutineScope.launch {
backendFeatureIds = WelcomeScreenFeatureApi.getInstance().getAvailableFeatureIds().toSet()
}
}
Column(modifier = modifier.wrapContentSize(Alignment.Center), verticalArrangement = Arrangement.spacedBy(16.dp)) {
val featureModels = contentProvider.getFeatureButtonModels(project)
// Show only available backend features (and all non-backend features)
val featureModels = contentProvider.getFeatureButtonModels(project).filter {
it !is FeatureButtonModelWithBackend || it.featureKey in backendFeatureIds
}
for (row in featureModels.chunked(3)) {
Row(modifier = Modifier.wrapContentSize(Alignment.Center), horizontalArrangement = Arrangement.spacedBy(16.dp)) {
for (model in row) {
FeatureButton(model)
FeatureButton(model, coroutineScope)
}
}
}
@@ -151,10 +166,10 @@ class WelcomeScreenRightTab(
}
@Composable
private fun FeatureButton(model: WelcomeRightTabContentProvider.FeatureButtonModel) {
private fun FeatureButton(model: WelcomeRightTabContentProvider.FeatureButtonModel, scope: CoroutineScope) {
WelcomeScreenCustomButton(
onClick = {
model.onClick()
model.onClick(project, scope)
},
style = CustomButtonStyle(),
modifier = Modifier.size(112.dp, 87.dp),