Skip to content

Commit 2f401d1

Browse files
committed
feat: Add show dialog logic in CodeXPUIManager
- Add dialog area for show multiple dialogs - Add listeners for notify when level up and challenge completed
1 parent 11bb64e commit 2f401d1

File tree

1 file changed

+157
-27
lines changed

1 file changed

+157
-27
lines changed

src/main/kotlin/com/github/ilovegamecoding/intellijcodexp/managers/CodeXPUIManager.kt

Lines changed: 157 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
11
package com.github.ilovegamecoding.intellijcodexp.managers
22

33
import com.github.ilovegamecoding.intellijcodexp.enums.Event
4+
import com.github.ilovegamecoding.intellijcodexp.listeners.CodeXPEventListener
45
import com.github.ilovegamecoding.intellijcodexp.listeners.CodeXPListener
6+
import com.github.ilovegamecoding.intellijcodexp.models.CodeXPChallenge
7+
import com.github.ilovegamecoding.intellijcodexp.models.CodeXPConfiguration
8+
import com.github.ilovegamecoding.intellijcodexp.models.CodeXPDialog
9+
import com.github.ilovegamecoding.intellijcodexp.models.CodeXPLevel
510
import com.github.ilovegamecoding.intellijcodexp.services.CodeXPService
611
import com.intellij.openapi.actionSystem.CommonDataKeys
712
import com.intellij.openapi.actionSystem.DataContext
813
import com.intellij.openapi.application.ApplicationManager
14+
import com.intellij.openapi.diagnostic.thisLogger
15+
import com.intellij.openapi.project.ProjectManager
16+
import com.intellij.openapi.wm.WindowManager
917
import com.intellij.ui.JBColor
1018
import java.awt.Color
19+
import java.awt.Dimension
1120
import java.awt.Font
12-
import javax.swing.JComponent
13-
import javax.swing.JLabel
21+
import java.awt.Point
22+
import java.awt.event.ActionListener
23+
import java.awt.event.MouseAdapter
24+
import java.awt.event.MouseMotionAdapter
25+
import javax.swing.*
1426
import kotlin.math.max
1527

28+
1629
/**
1730
* CodeXPUIManager class
1831
*
1932
* This class manages the UI of the CodeXP plugin.
2033
*/
21-
object CodeXPUIManager : CodeXPListener {
34+
object CodeXPUIManager : CodeXPEventListener, CodeXPListener {
2235
/**
2336
* Fading labels in each swing component.
2437
*/
@@ -39,22 +52,55 @@ object CodeXPUIManager : CodeXPListener {
3952
*/
4053
private val connection = messageBus.connect()
4154

55+
private lateinit var ide: JLayeredPane
56+
private lateinit var dialogArea: JPanel
57+
private val dialogTimers: MutableMap<CodeXPDialog, Timer> = mutableMapOf()
58+
4259
init {
43-
connection.subscribe(CodeXPListener.CODEXP_EVENT, this)
60+
connection.subscribe(CodeXPEventListener.CODEXP_EVENT, this)
61+
connection.subscribe(CodeXPListener.CODEXP, this)
4462
}
4563

4664
override fun eventOccurred(event: Event, dataContext: DataContext?) {
4765
displayXPLabel(event, dataContext)
4866
}
4967

68+
override fun xpUpdated(levelInfo: CodeXPLevel) {
69+
70+
}
71+
72+
override fun levelUp(levelInfo: CodeXPLevel) {
73+
showDialog(
74+
CodeXPDialog.createDialog(
75+
"Level Up!",
76+
"Congratulations! You have reached level ${levelInfo.level}!",
77+
"XP to next level: ${levelInfo.totalXPForNextLevel}xp"
78+
)
79+
)
80+
}
81+
82+
override fun challengeUpdated(event: Event, challenge: CodeXPChallenge, newChallenge: CodeXPChallenge?) {
83+
84+
}
85+
86+
override fun challengeCompleted(event: Event, challenge: CodeXPChallenge) {
87+
showDialog(
88+
CodeXPDialog.createDialog(
89+
"Challenge Completed!",
90+
"Congratulations! You have completed '${challenge.name}'!",
91+
"XP gained: ${challenge.rewardXP}xp"
92+
)
93+
)
94+
}
95+
5096
/**
5197
* Display XP label at the caret position.
5298
*
5399
* @param event The event to fire.
54100
* @param dataContext The data context of the event.
55101
*/
56102
private fun displayXPLabel(event: Event, dataContext: DataContext?) {
57-
if (dataContext == null) return
103+
dataContext ?: return
58104

59105
val codeXPConfiguration =
60106
ApplicationManager.getApplication().getService(CodeXPService::class.java).state.codeXPConfiguration
@@ -72,9 +118,11 @@ object CodeXPUIManager : CodeXPListener {
72118
fadingLabels[component]?.let { fadingLabel ->
73119
fadingLabel.cancelFadeOut()
74120
currentXPGainValue = fadingLabel.value
75-
component.remove(fadingLabel)
76-
component.revalidate()
77-
component.repaint()
121+
with(component) {
122+
remove(fadingLabel)
123+
revalidate()
124+
repaint()
125+
}
78126
}
79127

80128
currentXPGainValue += event.xpValue.toInt()
@@ -83,48 +131,69 @@ object CodeXPUIManager : CodeXPListener {
83131
font = Font(font.fontName, Font.BOLD, editor.colorsScheme.editorFontSize)
84132
size = preferredSize
85133
val caretHeight = editor.lineHeight
86-
fadingLabelPosition.let { point ->
87-
val position = codeXPConfiguration.positionToDisplayGainedXP
88-
val xOffset = when {
89-
position.name.contains("LEFT") -> -width
90-
position.name.contains("RIGHT") -> 0
91-
else -> -width / 2
92-
}
93-
point.translate(
94-
(position.x * 4) + xOffset,
95-
position.y * (caretHeight / 2)
96-
)
97-
location = point
98-
}
134+
location =
135+
calculateLabelLocation(codeXPConfiguration, fadingLabelPosition, caretHeight, preferredSize.width)
99136
startFadeOut()
100137
}
101138

102139
fadingLabels[component] = newFadingLabel
103140

104-
component.add(newFadingLabel)
105-
component.revalidate()
106-
component.repaint()
141+
with(component) {
142+
add(newFadingLabel)
143+
revalidate()
144+
repaint()
145+
}
146+
}
147+
148+
private fun calculateLabelLocation(
149+
config: CodeXPConfiguration,
150+
point: Point,
151+
caretHeight: Int,
152+
labelWidth: Int
153+
): Point {
154+
with(config.positionToDisplayGainedXP) {
155+
val xOffset = when {
156+
name.contains("LEFT") -> -labelWidth
157+
name.contains("RIGHT") -> 0
158+
else -> -labelWidth / 2
159+
}
160+
point.translate((x * 4) + xOffset, y * (caretHeight / 2))
161+
}
162+
return point
107163
}
108164

109165
/**
110166
* Fading label class for displaying XP gain.
111167
*/
112168
internal class FadingLabel(initialValue: Int) : JLabel(if (initialValue == 0) "0 xp" else "+$initialValue XP") {
113-
private lateinit var timer: javax.swing.Timer
169+
private lateinit var timer: Timer
114170

115171
/**
116172
* Start fade out animation.
117173
*/
118174
fun startFadeOut() {
119-
timer = javax.swing.Timer(100, null)
175+
timer = Timer(100, null)
120176
timer.addActionListener {
121177
val newAlpha = max(foreground.alpha - 255 / 10, 0)
122178
if (newAlpha <= 0) {
123179
timer.stop()
124180
value = 0
125181
} else {
126182
foreground =
127-
Color(JBColor.foreground().red, JBColor.foreground().green, JBColor.foreground().blue, newAlpha)
183+
JBColor(
184+
Color(
185+
JBColor.foreground().red,
186+
JBColor.foreground().green,
187+
JBColor.foreground().blue,
188+
newAlpha
189+
),
190+
Color(
191+
JBColor.foreground().red,
192+
JBColor.foreground().green,
193+
JBColor.foreground().blue,
194+
newAlpha
195+
)
196+
)
128197
}
129198
}
130199
timer.start()
@@ -149,4 +218,65 @@ object CodeXPUIManager : CodeXPListener {
149218
}
150219
}
151220
}
221+
222+
/**
223+
* Create a dialog.
224+
*/
225+
fun createDialogArea() {
226+
dialogArea = JPanel()
227+
with(dialogArea) {
228+
layout = BoxLayout(this, BoxLayout.Y_AXIS)
229+
isVisible = false
230+
background = JBColor(Color(255, 255, 255, 0), Color(255, 255, 255, 0))
231+
isOpaque = false
232+
233+
addMouseListener(object : MouseAdapter() {})
234+
addMouseMotionListener(object : MouseMotionAdapter() {})
235+
}
236+
237+
WindowManager.getInstance()
238+
.getIdeFrame(ProjectManager.getInstance().openProjects.firstOrNull())?.component?.rootPane?.layeredPane?.let {
239+
ide = it
240+
ide.add(dialogArea, JLayeredPane.POPUP_LAYER, 0)
241+
} ?: run {
242+
thisLogger().error("Could not find IDE frame.")
243+
}
244+
}
245+
246+
/**
247+
* Show the progress window for 3 seconds.
248+
*/
249+
private fun showDialog(dialog: CodeXPDialog) {
250+
dialogTimers[dialog] = Timer(3000, ActionListener { hideDialog(dialog) })
251+
dialogTimers[dialog]?.start()
252+
dialog.show()
253+
254+
with(dialogArea) {
255+
add(dialog.frame, 0)
256+
size = Dimension(480, preferredSize.height)
257+
location = Point(ide.width / 2 - width / 2, (ide.height * 0.05).toInt())
258+
revalidate()
259+
repaint()
260+
isVisible = true
261+
}
262+
}
263+
264+
/**
265+
* Hide the progress window immediately.
266+
*/
267+
private fun hideDialog(dialog: CodeXPDialog) {
268+
SwingUtilities.invokeLater {
269+
dialogTimers[dialog]?.stop()
270+
dialogTimers.remove(dialog)
271+
272+
with(dialogArea) {
273+
remove(dialog.frame)
274+
size = Dimension(480, preferredSize.height)
275+
}
276+
277+
if (dialogArea.components.isEmpty()) {
278+
dialogArea.isVisible = false
279+
}
280+
}
281+
}
152282
}

0 commit comments

Comments
 (0)