Skip to content

Commit 3c8b603

Browse files
committed
game configuration window fixes
1 parent a8c5d31 commit 3c8b603

File tree

3 files changed

+224
-10
lines changed

3 files changed

+224
-10
lines changed

src/main/kotlin/com/styx/ui/GameConfigDialog.kt

Lines changed: 216 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@ package com.styx.ui
22

33
import com.styx.api.SteamApiHelper
44
import com.styx.models.Game
5+
import com.styx.models.GameType
6+
import com.styx.utils.formatTimePlayed
7+
import java.awt.BorderLayout
58
import java.awt.Color
9+
import java.awt.Desktop
610
import java.awt.Dimension
711
import java.awt.FlowLayout
812
import java.awt.Font
13+
import java.io.File
14+
import java.net.URI
915
import javax.swing.BorderFactory
1016
import javax.swing.Box
1117
import javax.swing.BoxLayout
@@ -22,12 +28,13 @@ import javax.swing.border.EmptyBorder
2228

2329
class GameConfigDialog(
2430
private val game: Game,
25-
parent: JFrame,
31+
private val launcher: GameLauncher,
2632
private val onLaunchOptions: (Game) -> Unit,
2733
private val onPrefixManager: (Game) -> Unit,
2834
private val onProtonManager: (Game) -> Unit,
29-
private val onChangePrefix: (Game) -> Unit
30-
) : JDialog(parent, "Configure - ${game.name}", true) {
35+
private val onChangePrefix: (Game) -> Unit,
36+
private val onRename: (Game) -> Unit
37+
) : JDialog(launcher, "Configure - ${game.name}", true) {
3138

3239
private val verboseCheckbox = JCheckBox("Enable Verbose Logging (show all Wine debug)", game.verboseLogging)
3340
private val steamAppIdInput = JTextField(20)
@@ -145,6 +152,72 @@ class GameConfigDialog(
145152

146153
actionsPanel.add(changePrefixBtn)
147154
mainPanel.add(actionsPanel)
155+
mainPanel.add(Box.createVerticalStrut(15))
156+
157+
val additionalActionsPanel = JPanel()
158+
additionalActionsPanel.layout = BoxLayout(additionalActionsPanel, BoxLayout.Y_AXIS)
159+
additionalActionsPanel.border = BorderFactory.createTitledBorder("Quick Actions")
160+
additionalActionsPanel.alignmentX = LEFT_ALIGNMENT
161+
162+
val statsBtn = JButton("Show Statistics").apply {
163+
maximumSize = Dimension(Short.MAX_VALUE.toInt(), 32)
164+
alignmentX = LEFT_ALIGNMENT
165+
toolTipText = "View game statistics"
166+
addActionListener {
167+
showStatistics()
168+
}
169+
}
170+
additionalActionsPanel.add(statsBtn)
171+
additionalActionsPanel.add(Box.createVerticalStrut(8))
172+
173+
val renameBtn = JButton("Rename Game").apply {
174+
maximumSize = Dimension(Short.MAX_VALUE.toInt(), 32)
175+
alignmentX = LEFT_ALIGNMENT
176+
toolTipText = "Rename this game"
177+
addActionListener {
178+
renameGame()
179+
}
180+
}
181+
additionalActionsPanel.add(renameBtn)
182+
additionalActionsPanel.add(Box.createVerticalStrut(8))
183+
184+
val openGameLocationBtn = JButton("Open Game Location").apply {
185+
maximumSize = Dimension(Short.MAX_VALUE.toInt(), 32)
186+
alignmentX = LEFT_ALIGNMENT
187+
toolTipText = "Open the game's directory in file manager"
188+
addActionListener {
189+
openGameLocation()
190+
}
191+
}
192+
additionalActionsPanel.add(openGameLocationBtn)
193+
194+
if (game.getGameType() == GameType.WINDOWS) {
195+
additionalActionsPanel.add(Box.createVerticalStrut(8))
196+
val openPrefixBtn = JButton("Open Wine Prefix Location").apply {
197+
maximumSize = Dimension(Short.MAX_VALUE.toInt(), 32)
198+
alignmentX = LEFT_ALIGNMENT
199+
toolTipText = "Open the wine prefix directory"
200+
addActionListener {
201+
openPrefixLocation()
202+
}
203+
}
204+
additionalActionsPanel.add(openPrefixBtn)
205+
206+
if (!game.steamAppId.isNullOrEmpty()) {
207+
additionalActionsPanel.add(Box.createVerticalStrut(8))
208+
val protonDbBtn = JButton("Open ProtonDB Page").apply {
209+
maximumSize = Dimension(Short.MAX_VALUE.toInt(), 32)
210+
alignmentX = LEFT_ALIGNMENT
211+
toolTipText = "Open this game's ProtonDB page"
212+
addActionListener {
213+
openProtonDB()
214+
}
215+
}
216+
additionalActionsPanel.add(protonDbBtn)
217+
}
218+
}
219+
220+
mainPanel.add(additionalActionsPanel)
148221
mainPanel.add(Box.createVerticalGlue())
149222

150223
val buttonPanel = JPanel(FlowLayout(FlowLayout.RIGHT, 8, 0))
@@ -223,4 +296,144 @@ class GameConfigDialog(
223296

224297
progressDialog.isVisible = true
225298
}
299+
300+
private fun showStatistics() {
301+
val statsWindow = JDialog(this, "Stats - ${game.name}", true)
302+
statsWindow.defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE
303+
statsWindow.setSize(400, 400)
304+
statsWindow.minimumSize = Dimension(400, 400)
305+
statsWindow.setLocationRelativeTo(this)
306+
307+
val mainPanel = JPanel(BorderLayout())
308+
mainPanel.border = EmptyBorder(20, 20, 20, 20)
309+
mainPanel.background = Color(34, 35, 36)
310+
311+
val titleLabel = JLabel("Statistics for ${game.name}")
312+
titleLabel.font = titleLabel.font.deriveFont(Font.BOLD, 16f)
313+
titleLabel.border = EmptyBorder(0, 0, 20, 0)
314+
titleLabel.foreground = launcher.globalSettings.theme.getGameTitleColorObject()
315+
mainPanel.add(titleLabel, BorderLayout.NORTH)
316+
317+
val tableData = arrayOf(
318+
arrayOf("Time Played", formatTimePlayed(game.timePlayed)),
319+
arrayOf("Times Opened", game.timesOpened.toString()),
320+
arrayOf("Times Crashed", game.timesCrashed.toString()),
321+
arrayOf("Wineprefix Path", game.prefix),
322+
arrayOf("Compatibility Layer", game.protonVersion ?: "Wine (default)")
323+
)
324+
325+
val columnNames = arrayOf("Statistic", "Value")
326+
val tableModel = javax.swing.table.DefaultTableModel(tableData, columnNames)
327+
val table = javax.swing.JTable(tableModel)
328+
table.font = table.font.deriveFont(14f)
329+
table.rowHeight = 30
330+
table.setEnabled(false)
331+
332+
val scrollPane = javax.swing.JScrollPane(table)
333+
mainPanel.add(scrollPane, BorderLayout.CENTER)
334+
335+
statsWindow.contentPane = mainPanel
336+
statsWindow.isVisible = true
337+
}
338+
339+
private fun renameGame() {
340+
val newName = JOptionPane.showInputDialog(
341+
this,
342+
"Enter new name for '${game.name}':",
343+
"Rename Game",
344+
JOptionPane.QUESTION_MESSAGE,
345+
null,
346+
null,
347+
game.name
348+
) as? String
349+
350+
if (newName != null && newName.isNotBlank() && newName != game.name) {
351+
game.name = newName
352+
onRename(game)
353+
title = "Configure - ${game.name}"
354+
}
355+
}
356+
357+
private fun openGameLocation() {
358+
if (game.getGameType() == GameType.STEAM) {
359+
JOptionPane.showMessageDialog(
360+
this,
361+
"Steam App ID: ${game.executable}\nThis game is launched via Steam.",
362+
"Steam Game Info",
363+
JOptionPane.INFORMATION_MESSAGE
364+
)
365+
return
366+
}
367+
368+
val gameFile = File(game.executable)
369+
val gameDirectory = gameFile.parentFile
370+
371+
if (gameDirectory != null && gameDirectory.exists()) {
372+
try {
373+
if (Desktop.isDesktopSupported()) {
374+
Desktop.getDesktop().open(gameDirectory)
375+
} else {
376+
ProcessBuilder("xdg-open", gameDirectory.absolutePath).start()
377+
}
378+
} catch (e: Exception) {
379+
JOptionPane.showMessageDialog(
380+
this,
381+
"Failed to open game location: ${e.message}",
382+
"Error",
383+
JOptionPane.ERROR_MESSAGE
384+
)
385+
}
386+
} else {
387+
JOptionPane.showMessageDialog(
388+
this,
389+
"Game location not found: ${gameDirectory?.absolutePath ?: game.executable}",
390+
"Error",
391+
JOptionPane.ERROR_MESSAGE
392+
)
393+
}
394+
}
395+
396+
private fun openPrefixLocation() {
397+
val prefixDirectory = File(game.prefix)
398+
399+
if (prefixDirectory.exists()) {
400+
try {
401+
if (Desktop.isDesktopSupported()) {
402+
Desktop.getDesktop().open(prefixDirectory)
403+
} else {
404+
ProcessBuilder("xdg-open", prefixDirectory.absolutePath).start()
405+
}
406+
} catch (e: Exception) {
407+
JOptionPane.showMessageDialog(
408+
this,
409+
"Failed to open wine prefix location: ${e.message}",
410+
"Error",
411+
JOptionPane.ERROR_MESSAGE
412+
)
413+
}
414+
} else {
415+
JOptionPane.showMessageDialog(
416+
this,
417+
"Wine prefix not found: ${game.prefix}",
418+
"Error",
419+
JOptionPane.ERROR_MESSAGE
420+
)
421+
}
422+
}
423+
424+
private fun openProtonDB() {
425+
if (!game.steamAppId.isNullOrEmpty()) {
426+
try {
427+
val uri = URI("https://www.protondb.com/app/${game.steamAppId}")
428+
Desktop.getDesktop().browse(uri)
429+
} catch (e: Exception) {
430+
JOptionPane.showMessageDialog(
431+
this,
432+
"Failed to open ProtonDB: ${e.message}",
433+
"Error",
434+
JOptionPane.ERROR_MESSAGE
435+
)
436+
}
437+
}
438+
}
226439
}

src/main/kotlin/com/styx/ui/GameLauncher.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -973,7 +973,8 @@ class GameLauncher : JFrame("Styx") {
973973
::openLaunchOptions,
974974
::openPrefixManager,
975975
::openProtonManager,
976-
::changeGamePrefix
976+
::changeGamePrefix,
977+
::renameGame
977978
)
978979
dialog.isVisible = true
979980
saveGames()

src/main/kotlin/com/styx/ui/WineprefixManagerWindow.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ class WineprefixManagerWindow(private val parent: GameLauncher) : JFrame("Winepr
237237
if (name.isEmpty()) {
238238
JOptionPane.showMessageDialog(
239239
this,
240-
"Please enter a prefix name.",
240+
"Enter a prefix name.",
241241
"Invalid Input",
242242
JOptionPane.WARNING_MESSAGE
243243
)
@@ -259,7 +259,7 @@ class WineprefixManagerWindow(private val parent: GameLauncher) : JFrame("Winepr
259259
}
260260

261261
val progressDialog = JDialog(this, "Creating Prefix...", true)
262-
val progressLabel = JLabel("Initializing wineprefix, please wait...")
262+
val progressLabel = JLabel("Initializing wineprefix, wait...")
263263
progressLabel.border = EmptyBorder(20, 20, 20, 20)
264264
progressDialog.add(progressLabel)
265265
progressDialog.pack()
@@ -322,7 +322,7 @@ class WineprefixManagerWindow(private val parent: GameLauncher) : JFrame("Winepr
322322
if (selectedRow < 0) {
323323
JOptionPane.showMessageDialog(
324324
this,
325-
"Please select a prefix to rename.",
325+
"Select a prefix to rename.",
326326
"No Selection",
327327
JOptionPane.WARNING_MESSAGE
328328
)
@@ -432,7 +432,7 @@ class WineprefixManagerWindow(private val parent: GameLauncher) : JFrame("Winepr
432432
if (selectedRow < 0) {
433433
JOptionPane.showMessageDialog(
434434
this,
435-
"Please select a prefix to delete.",
435+
"Select a prefix to delete.",
436436
"No Selection",
437437
JOptionPane.WARNING_MESSAGE
438438
)
@@ -445,7 +445,7 @@ class WineprefixManagerWindow(private val parent: GameLauncher) : JFrame("Winepr
445445
if (name == "Default (~/.wine)") {
446446
JOptionPane.showMessageDialog(
447447
this,
448-
"Cannot delete the default wineprefix.\nPlease delete it manually if needed.",
448+
"Cannot delete the default wineprefix.\nDelete it manually if needed.",
449449
"Invalid Operation",
450450
JOptionPane.WARNING_MESSAGE
451451
)
@@ -512,7 +512,7 @@ class WineprefixManagerWindow(private val parent: GameLauncher) : JFrame("Winepr
512512
if (selectedRow < 0) {
513513
JOptionPane.showMessageDialog(
514514
this,
515-
"Please select a prefix to open.",
515+
"Select a prefix to open.",
516516
"No Selection",
517517
JOptionPane.WARNING_MESSAGE
518518
)

0 commit comments

Comments
 (0)