feat: add settings UI and allow configuring PDF open mode

Closes #1
This commit is contained in:
Johannes Frohnmeyer 2024-05-29 14:42:24 +02:00
parent cd86b91b10
commit 91b85ed70c
Signed by: Johannes
GPG Key ID: E76429612C2929F4
12 changed files with 138 additions and 27 deletions

View File

@ -13,7 +13,6 @@ You can download the plugin from the [JetBrains Marketplace](https://plugins.jet
<!-- Note to writers: A copy of the feature list exists in plugin.xml, please also add your changes there --> <!-- Note to writers: A copy of the feature list exists in plugin.xml, please also add your changes there -->
## Potential future features ## Potential future features
- Settings UI (login, PDF mode, ...)
- Display how much time is left in a contest or whether it's already over - Display how much time is left in a contest or whether it's already over
- Implement reading detailed results - Implement reading detailed results
- Show scoreboards - Show scoreboards

View File

@ -11,6 +11,7 @@ import io.gitlab.jfronny.sdom.actions.SDGetContestsAction
import io.gitlab.jfronny.sdom.actions.SDGetProblemsAction import io.gitlab.jfronny.sdom.actions.SDGetProblemsAction
import io.gitlab.jfronny.sdom.model.* import io.gitlab.jfronny.sdom.model.*
import io.gitlab.jfronny.sdom.model.scoreboard.Scoreboard import io.gitlab.jfronny.sdom.model.scoreboard.Scoreboard
import io.gitlab.jfronny.sdom.settings.SDCredentials
import io.gitlab.jfronny.sdom.util.notify import io.gitlab.jfronny.sdom.util.notify
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.call.* import io.ktor.client.call.*

View File

@ -2,8 +2,7 @@ package io.gitlab.jfronny.sdom.actions
import com.intellij.notification.Notification import com.intellij.notification.Notification
import com.intellij.notification.NotificationAction import com.intellij.notification.NotificationAction
import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.*
import com.intellij.openapi.actionSystem.AnActionEvent
import io.gitlab.jfronny.sdom.SDom import io.gitlab.jfronny.sdom.SDom
import io.gitlab.jfronny.sdom.ui.SDLoginDialogWrapper import io.gitlab.jfronny.sdom.ui.SDLoginDialogWrapper
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -26,4 +25,19 @@ class SDLoginAction(text: String) : NotificationAction(text) {
} }
} }
} }
companion object {
fun perform() {
SDLoginAction().actionPerformed(
AnActionEvent(
null,
DataContext.EMPTY_CONTEXT,
ActionPlaces.UNKNOWN,
Presentation(),
ActionManager.getInstance(),
0
)
)
}
}
} }

View File

@ -6,10 +6,14 @@ import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.DumbAwareAction import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.ui.jcef.JBCefApp import com.intellij.ui.jcef.JBCefApp
import io.gitlab.jfronny.sdom.SDom import io.gitlab.jfronny.sdom.SDom
import io.gitlab.jfronny.sdom.SDom.currentContest import io.gitlab.jfronny.sdom.SDom.currentContest
import io.gitlab.jfronny.sdom.SDom.currentProblem import io.gitlab.jfronny.sdom.SDom.currentProblem
import io.gitlab.jfronny.sdom.settings.PdfMode
import io.gitlab.jfronny.sdom.settings.SDSettings
import io.gitlab.jfronny.sdom.ui.ByteVirtualFile import io.gitlab.jfronny.sdom.ui.ByteVirtualFile
import io.gitlab.jfronny.sdom.util.OSUtils import io.gitlab.jfronny.sdom.util.OSUtils
import io.gitlab.jfronny.sdom.util.notify import io.gitlab.jfronny.sdom.util.notify
@ -18,6 +22,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardOpenOption
class SDStatementAction(name: String) : DumbAwareAction(name) { class SDStatementAction(name: String) : DumbAwareAction(name) {
constructor() : this("") constructor() : this("")
@ -52,16 +58,24 @@ class SDStatementAction(name: String) : DumbAwareAction(name) {
} }
CoroutineScope(Job() + Dispatchers.IO).launch { CoroutineScope(Job() + Dispatchers.IO).launch {
val data = SDom.downloadProblemStatement(contest, problem) val data = SDom.downloadProblemStatement(contest, problem)
if (JBCefApp.isSupported()) { fun createTmpFile(): Path {
val virtualFile = ByteVirtualFile("${problem.name}_${contest.name}.pdf", data) val path = Files.createTempFile("${problem.name}_${contest.name}", ".pdf")
ApplicationManager.getApplication().invokeLater { Files.write(path, data, StandardOpenOption.WRITE)
FileEditorManager.getInstance(e.project!!).openFile(virtualFile) return path
}
fun open(virtualFile: VirtualFile) = ApplicationManager.getApplication().invokeLater {
FileEditorManager.getInstance(e.project!!).openFile(virtualFile)
}
when (SDSettings.getInstance().state.pdfMode) {
PdfMode.INTERNAL_MEMORY -> {
if (JBCefApp.isSupported()) open(ByteVirtualFile("${problem.name}_${contest.name}.pdf", data))
else OSUtils.openFile(createTmpFile().toFile())
} }
} else { PdfMode.INTERNAL_FILE -> {
val path = Files.createTempFile("sdom", ".pdf") if (JBCefApp.isSupported()) open(VirtualFileManager.getInstance().refreshAndFindFileByNioPath(createTmpFile())!!)
Files.newOutputStream(path).use { it.write(data) } else OSUtils.openFile(createTmpFile().toFile())
}
OSUtils.openFile(path.toFile()) PdfMode.EXTERNAL_FILE -> OSUtils.openFile(createTmpFile().toFile())
} }
} }
} }

View File

@ -0,0 +1,11 @@
package io.gitlab.jfronny.sdom.settings
enum class PdfMode(private val visual: String) {
INTERNAL_MEMORY("Internal viewer, in-memory"),
INTERNAL_FILE("Internal viewer, as file"),
EXTERNAL_FILE("System-default");
override fun toString(): String {
return visual
}
}

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.sdom package io.gitlab.jfronny.sdom.settings
import com.intellij.credentialStore.CredentialAttributes import com.intellij.credentialStore.CredentialAttributes
import com.intellij.credentialStore.Credentials import com.intellij.credentialStore.Credentials
@ -26,9 +26,9 @@ object SDCredentials {
} }
var url: String var url: String
get() = ApplicationManager.getApplication().getService(SDSettings::class.java).state.url ?: "https://domjudge.iti.kit.edu/main/api/v4" get() = SDSettings.getInstance().state.url ?: "https://domjudge.iti.kit.edu/main/api/v4"
set(value) { set(value) {
ApplicationManager.getApplication().getService(SDSettings::class.java).state.url = value SDSettings.getInstance().state.url = value
} }
var teamId: String? var teamId: String?

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.sdom package io.gitlab.jfronny.sdom.settings
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.* import com.intellij.openapi.components.*
@Service @Service
@ -7,5 +8,10 @@ import com.intellij.openapi.components.*
class SDSettings : SimplePersistentStateComponent<SDSettings.SDState>(SDState()) { class SDSettings : SimplePersistentStateComponent<SDSettings.SDState>(SDState()) {
class SDState : BaseState() { class SDState : BaseState() {
var url by string("https://domjudge.iti.kit.edu/main/api/v4") var url by string("https://domjudge.iti.kit.edu/main/api/v4")
var pdfMode by enum<PdfMode>(PdfMode.INTERNAL_FILE)
}
companion object {
fun getInstance(): SDSettings = ApplicationManager.getApplication().getService(SDSettings::class.java)
} }
} }

View File

@ -0,0 +1,34 @@
package io.gitlab.jfronny.sdom.settings
import com.intellij.openapi.ui.ComboBox
import com.intellij.ui.dsl.builder.panel
import io.gitlab.jfronny.sdom.actions.SDLoginAction
import javax.swing.JPanel
class SDSettingsComponent {
val mainPanel: JPanel
val pdfModeBox: ComboBox<PdfMode> = ComboBox(PdfMode.entries.toTypedArray())
init {
// mainPanel = FormBuilder.createFormBuilder()
// .addLabeledComponent("PDF Mode", pdfModeBox)
// .addComponentFillVertically(JPanel(), 0)
// .addComponentFillVertically(ActionManager.getInstance().createActionToolbar(
// "SettingsUI",
// DefaultActionGroup(SDLoginAction("Change Login Credentials")),
// true
// ).component, 0)
// .panel
mainPanel = panel {
row {
label("PDF Mode")
cell(pdfModeBox)
}
row {
button("Change Login Credentials") {
SDLoginAction.perform()
}
}
}
}
}

View File

@ -0,0 +1,37 @@
package io.gitlab.jfronny.sdom.settings
import com.intellij.openapi.options.Configurable
import javax.swing.JComponent
class SDSettingsConfigurable : Configurable {
private var settingsComponent: SDSettingsComponent? = null
override fun getDisplayName(): String = "S-dom"
override fun getPreferredFocusedComponent(): JComponent? {
return settingsComponent!!.pdfModeBox
}
override fun createComponent(): JComponent? {
val component = SDSettingsComponent()
settingsComponent = component
return component.mainPanel
}
override fun isModified(): Boolean {
val state = SDSettings.getInstance().state
return settingsComponent!!.pdfModeBox.selectedItem != state.pdfMode
}
override fun apply() {
val state = SDSettings.getInstance().state
state.pdfMode = settingsComponent!!.pdfModeBox.selectedItem as PdfMode
}
override fun reset() {
val state = SDSettings.getInstance().state
settingsComponent!!.pdfModeBox.selectedItem = state.pdfMode
}
override fun disposeUIResources() {
settingsComponent = null
}
}

View File

@ -19,16 +19,7 @@ fun loggedOutDialogPanel(): DialogPanel = panel {
} }
row { row {
button("Log In") { button("Log In") {
SDLoginAction().actionPerformed( SDLoginAction.perform()
AnActionEvent(
null,
DataContext.EMPTY_CONTEXT,
ActionPlaces.UNKNOWN,
Presentation(),
ActionManager.getInstance(),
0
)
)
}.align(Align.CENTER) }.align(Align.CENTER)
} }
}.resizableColumn().align(AlignY.CENTER) }.resizableColumn().align(AlignY.CENTER)

View File

@ -4,7 +4,7 @@ import com.intellij.openapi.ui.DialogWrapper
import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.bindText
import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.panel
import io.gitlab.jfronny.sdom.SDCredentials import io.gitlab.jfronny.sdom.settings.SDCredentials
import javax.swing.JComponent import javax.swing.JComponent
class SDLoginDialogWrapper : DialogWrapper(true) { class SDLoginDialogWrapper : DialogWrapper(true) {

View File

@ -38,6 +38,10 @@
<toolWindow factoryClass="io.gitlab.jfronny.sdom.toolwindow.SDToolWindowFactory" <toolWindow factoryClass="io.gitlab.jfronny.sdom.toolwindow.SDToolWindowFactory"
id="S-DOM" anchor="bottom" canCloseContents="true" icon="io.gitlab.jfronny.sdom.icons.SDIcons.ToolWindow" /> id="S-DOM" anchor="bottom" canCloseContents="true" icon="io.gitlab.jfronny.sdom.icons.SDIcons.ToolWindow" />
<notificationGroup id="sdom.notifications" displayType="BALLOON" /> <notificationGroup id="sdom.notifications" displayType="BALLOON" />
<applicationConfigurable parentId="tools" instance="io.gitlab.jfronny.sdom.settings.SDSettingsConfigurable"
id="io.gitlab.jfronny.sdom.settings.SDSettingsConfigurable"
displayName="S-dom"/>
<applicationService serviceImplementation="io.gitlab.jfronny.sdom.settings.SDSettings"/>
</extensions> </extensions>
<actions> <actions>
<group id="io.gitlab.jfronny.sdom.actions.SDToolbarActions" text="S-dom" popup="true"/> <group id="io.gitlab.jfronny.sdom.actions.SDToolbarActions" text="S-dom" popup="true"/>