Skip to content

Commit

Permalink
Templates: handle non-installed case with instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
seclerp committed Oct 6, 2024
1 parent 4f496cd commit 3221c0d
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 31 deletions.
12 changes: 12 additions & 0 deletions src/rider/main/kotlin/me/seclerp/rider/extensions/ClipboardUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package me.seclerp.rider.extensions

import java.awt.Toolkit
import java.awt.datatransfer.StringSelection

internal object ClipboardUtil {
fun copyToClipboard(text: String) {
val selection = StringSelection(text)
val clipboard = Toolkit.getDefaultToolkit().systemClipboard
clipboard.setContents(selection, null)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package me.seclerp.rider.plugins.monogame.templates

import com.jetbrains.rider.projectView.projectTemplates.providers.ProjectTemplateCustomizer

class MonoGameProjectTemplateCustomizer : ProjectTemplateCustomizer {
internal class MonoGameProjectTemplateCustomizer : ProjectTemplateCustomizer {
private val monoGameTemplateTypes = setOf(MonoGameTemplateType())
override fun getCustomProjectTemplateTypes() = monoGameTemplateTypes
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package me.seclerp.rider.plugins.monogame.templates

import com.intellij.icons.AllIcons
import com.intellij.openapi.editor.colors.EditorColorsManager
import com.intellij.openapi.editor.colors.EditorFontType
import com.intellij.openapi.ui.DialogPanel
import com.intellij.ui.AnimatedIcon
import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.components.fields.ExtendableTextComponent
import com.intellij.ui.components.fields.ExtendableTextField
import com.intellij.ui.components.panels.NonOpaquePanel
import com.intellij.ui.components.panels.VerticalLayout
import com.intellij.ui.dsl.builder.Panel
import com.intellij.ui.dsl.builder.Row
import com.intellij.ui.dsl.builder.TopGap
import com.intellij.ui.dsl.builder.panel
import com.intellij.util.ui.JBUI
import com.jetbrains.rd.util.lifetime.Lifetime
import com.jetbrains.rd.util.reactive.IOptProperty
import com.jetbrains.rider.model.RdProjectTemplate
import com.jetbrains.rider.projectView.projectTemplates.NewProjectDialogContext
import com.jetbrains.rider.projectView.projectTemplates.ProjectTemplatesSharedModel
import com.jetbrains.rider.projectView.projectTemplates.generators.TypeListBasedProjectTemplateGenerator
import me.seclerp.rider.extensions.ClipboardUtil
import me.seclerp.rider.plugins.monogame.MonoGameIcons
import me.seclerp.rider.plugins.monogame.MonoGameUiBundle
import me.seclerp.rider.plugins.monogame.templates.MonoGameTemplateMetadata.Names
import java.awt.BorderLayout
import java.awt.Component
import javax.swing.JLabel
import javax.swing.SwingConstants

internal class MonoGameProjectTemplateGenerator(
lifetime: Lifetime,
context: NewProjectDialogContext,
sharedModel: ProjectTemplatesSharedModel,
projectTemplates: IOptProperty<Set<RdProjectTemplate>>
) : TypeListBasedProjectTemplateGenerator(lifetime, context, sharedModel, projectTemplates) {
init {
context.isReady.afterChange {
val templates = tryGetAcceptableTemplates()
when {
it && templates.isNullOrEmpty() -> setTemplatesMissingState()
it && !templates.isNullOrEmpty() -> setReadyState()
else -> setLoadingState()
}
}
}

override val defaultName = "MonoGameProject1"

override fun getPredefinedTypes() = listOf(
TemplateTypeWithIcon(Names.CROSS_PLATFORM_APP, MonoGameIcons.MgcbFile),
TemplateTypeWithIcon(Names.WINDOWS_DESKTOP_APP, MonoGameIcons.MgcbFile),
TemplateTypeWithIcon(Names.ANDROID_APP, MonoGameIcons.MgcbFile),
TemplateTypeWithIcon(Names.IOS_APP, MonoGameIcons.MgcbFile),
TemplateTypeWithIcon(Names.GAME_LIB, MonoGameIcons.MgcbFile),
TemplateTypeWithIcon(Names.CONTENT_PIPELINE_EXTENSION, MonoGameIcons.MgcbFile),
TemplateTypeWithIcon(Names.SHARED_LIB, MonoGameIcons.MgcbFile),
)

override fun getType(template: RdProjectTemplate) = template.name
.removePrefix("MonoGame ")
.replace("Application", "App")

private var myLoadingRow: Row? = null
private var myTemplatesMissingRow: Row? = null
private var myTemplatesRow: Row? = null

override fun createTemplateSpecificPanelAfterLanguage(): DialogPanel {
return panel {
myLoadingRow = createLoadingRow().apply { visible(false)}
myTemplatesMissingRow = createMissingTemplatesRow().apply { visible(false)}
myTemplatesRow = createReadyRow().apply { visible(false)}
setLoadingState()
}
}

private fun setLoadingState() {
myTemplatesRow?.visible(false)
myTemplatesMissingRow?.visible(false)
myLoadingRow?.visible(true)
}

private fun setReadyState() {
myLoadingRow?.visible(false)
myTemplatesMissingRow?.visible(false)
myTemplatesRow?.visible(true)
}

private fun setTemplatesMissingState() {
myLoadingRow?.visible(false)
myTemplatesRow?.visible(false)
myTemplatesMissingRow?.visible(true)
}

private fun Panel.createLoadingRow() = row(" ") {
cell(JLabel(MonoGameUiBundle.message("templates.loading.templates"), AnimatedIcon.Default(), SwingConstants.LEFT))
}

private fun Panel.createReadyRow() = row {
cell(super.createTemplateSpecificPanelAfterLanguage())
}

private fun Panel.createMissingTemplatesRow() = row(" ") {
val installCommand = "dotnet new install MonoGame.Templates.CSharp"
cell(CustomNotificationPanel(EditorNotificationPanel.Status.Warning))
.applyToComponent {
text(MonoGameUiBundle.message("templates.no.templates.found"))
addBottomItem(JLabel(MonoGameUiBundle.message("templates.no.templates.install.hint")).apply {
border = JBUI.Borders.emptyTop(8)
})
addBottomItem(CodeSnippetTextField(installCommand, 50))
}
.resizableColumn()
topGap(TopGap.SMALL)
}

private class CustomNotificationPanel(status: Status) : EditorNotificationPanel(status) {
private val myBottomPanel = NonOpaquePanel(VerticalLayout(8, VerticalLayout.FILL))

init {
add(myBottomPanel, BorderLayout.SOUTH)
}

fun addBottomItem(comp: Component) {
myBottomPanel.add(comp)
}
}

private class CodeSnippetTextField(text: String, columns: Int = 20) : ExtendableTextField(text, columns) {
init {
isEditable = false
font = EditorColorsManager.getInstance().globalScheme.getFont(EditorFontType.PLAIN)
addExtension(CopyToClipboardExtension())
}

private inner class CopyToClipboardExtension : ExtendableTextComponent.Extension {
override fun getIcon(hovered: Boolean) = AllIcons.General.Copy

override fun getActionOnClick() = Runnable {
ClipboardUtil.copyToClipboard(this@CodeSnippetTextField.text)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package me.seclerp.rider.plugins.monogame.templates

internal object MonoGameTemplateMetadata {
internal object Names {
const val CROSS_PLATFORM_APP = "Cross-Platform Desktop App"
const val WINDOWS_DESKTOP_APP = "Windows Desktop App"
const val ANDROID_APP = "Android App"
const val IOS_APP = "iOS App"
const val GAME_LIB = "Game Library"
const val CONTENT_PIPELINE_EXTENSION = "Content Pipeline Extension"
const val SHARED_LIB = "Shared Library Project"
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import com.jetbrains.rd.util.lifetime.Lifetime
import com.jetbrains.rider.model.RdProjectTemplate
import com.jetbrains.rider.projectView.projectTemplates.NewProjectDialogContext
import com.jetbrains.rider.projectView.projectTemplates.ProjectTemplatesSharedModel
import com.jetbrains.rider.projectView.projectTemplates.generators.TypeListBasedProjectTemplateGenerator
import com.jetbrains.rider.projectView.projectTemplates.templateTypes.PredefinedProjectTemplateType
import com.jetbrains.rider.projectView.projectTemplates.utils.hasClassification
import me.seclerp.rider.plugins.monogame.MonoGameIcons

class MonoGameTemplateType : PredefinedProjectTemplateType() {
internal class MonoGameTemplateType : PredefinedProjectTemplateType() {
override val icon = MonoGameIcons.MgcbFile
override val name = "MonoGame"
override val order = 50
Expand All @@ -19,21 +18,5 @@ class MonoGameTemplateType : PredefinedProjectTemplateType() {
}

override fun createGenerator(lifetime: Lifetime, context: NewProjectDialogContext, sharedModel: ProjectTemplatesSharedModel) =
object : TypeListBasedProjectTemplateGenerator(lifetime, context, sharedModel, projectTemplates) {
override val defaultName = "MonoGameProject1"

override fun getPredefinedTypes() = listOf(
TemplateTypeWithIcon(MonoGameTemplateNames.CROSS_PLATFORM_APP, MonoGameIcons.MgcbFile),
TemplateTypeWithIcon(MonoGameTemplateNames.WINDOWS_DESKTOP_APP, MonoGameIcons.MgcbFile),
TemplateTypeWithIcon(MonoGameTemplateNames.ANDROID_APP, MonoGameIcons.MgcbFile),
TemplateTypeWithIcon(MonoGameTemplateNames.IOS_APP, MonoGameIcons.MgcbFile),
TemplateTypeWithIcon(MonoGameTemplateNames.GAME_LIB, MonoGameIcons.MgcbFile),
TemplateTypeWithIcon(MonoGameTemplateNames.CONTENT_PIPELINE_EXTENSION, MonoGameIcons.MgcbFile),
TemplateTypeWithIcon(MonoGameTemplateNames.SHARED_LIB, MonoGameIcons.MgcbFile),
)

override fun getType(template: RdProjectTemplate) = template.name
.removePrefix("MonoGame ")
.replace("Application", "App")
}
MonoGameProjectTemplateGenerator(lifetime, context, sharedModel, projectTemplates)
}
4 changes: 4 additions & 0 deletions src/rider/main/resources/messages/MonoGameUiBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ command.execution.error.message.code=Status code doesn't indicate success: {0}.\
command.mgcb.open.title=Open in external MGCB editor
command.mgcb.open.missing.editor.title=mgcb-editor tool is missing
templates.loading.templates=Loading templates...
templates.no.templates.found=No MonoGame templates were found.
templates.no.templates.install.hint=Please install them using the following command:

0 comments on commit 3221c0d

Please sign in to comment.