Inceptum/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/InstanceListEntryFactory.kt

152 lines
6.5 KiB
Kotlin
Raw Normal View History

package io.gitlab.jfronny.inceptum.gtk.control
import io.gitlab.jfronny.commons.io.JFiles
import io.gitlab.jfronny.commons.ref.R
import io.gitlab.jfronny.inceptum.common.MetaHolder
import io.gitlab.jfronny.inceptum.common.Utils
import io.gitlab.jfronny.inceptum.gtk.GtkMenubar
import io.gitlab.jfronny.inceptum.gtk.menu.MenuBuilder
import io.gitlab.jfronny.inceptum.gtk.util.I18n
import io.gitlab.jfronny.inceptum.gtk.util.fixSubtitle
import io.gitlab.jfronny.inceptum.gtk.util.get
import io.gitlab.jfronny.inceptum.gtk.util.margin
import io.gitlab.jfronny.inceptum.gtk.window.settings.instance.InstanceSettingsWindow
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance
import io.gitlab.jfronny.inceptum.launcher.system.instance.InstanceNameTool
import io.gitlab.jfronny.inceptum.launcher.system.launch.LaunchType
import org.gnome.adw.ActionRow
import org.gnome.gdk.Gdk
import org.gnome.gio.Menu
import org.gnome.gtk.*
import java.io.IOException
class InstanceListEntryFactory(
private val app: Application?,
private val instanceList: List<Instance>
) : KSignalListItemFactory<InstanceListEntryFactory.Decomposed, ActionRow>() {
override fun setup(): ActionRow {
val thumbnail = InstanceThumbnail()
thumbnail.name = "inceptum-thumbnail"
val launch = Button.newFromIconName("media-playback-start-symbolic")
launch.addCssClass("flat")
launch.name = "inceptum-launch"
launch.tooltipText = I18n["instance.launch"]
val menu = MenuButton()
menu.addCssClass("flat")
menu.iconName = "view-more-symbolic"
menu.setPopover(PopoverMenu.newFromModel(Menu()))
val row = ActionRow()
row.margin = 8
row.name = "inceptum-row"
row.removeCssClass("activatable") //TODO remove this workaround if a better way to support opening the menu is found
row.addPrefix(thumbnail)
row.addSuffix(launch)
row.addSuffix(menu)
row.fixSubtitle()
val rightClicked = GestureClick()
rightClicked.button = Gdk.BUTTON_SECONDARY
rightClicked.onPressed { nPress, _, _ -> if (nPress == 1) menu.emitActivate() }
row.addController(rightClicked)
return row
}
override fun BindContext.bind(widget: ActionRow, data: Decomposed) {
if (data.instance?.isLocked ?: true) {
data.item.activatable = false
widget.subtitle = if (data.instance?.isRunningLocked ?: false) I18n["instance.launch.locked.running"]
else I18n["instance.launch.locked.setup"]
}
widget.title = data.instance.toString()
data.thumbnail.bind(data.instance!!)
val menuBuilder = MenuBuilder(data.popoverMenu, data.instanceId)
val launchSection = menuBuilder.literalSection("launch", null)
val kill = launchSection.literalButton("kill", I18n["instance.kill"]) {
//TODO test
LauncherEnv.showOkCancel(I18n["instance.kill.prompt"], I18n["instance.kill.details"]) {
if (!data.instance.kill()) LauncherEnv.showError(I18n["instance.kill.fail"], I18n["failed"])
}
}
kill.enabled = data.instance.isRunningLocked
launchSection.literalButton(
"launch.client", I18n["instance.launch.client"]
) { GtkMenubar.launch(data.instance, LaunchType.Client) }.iconName = "media-playback-start-symbolic"
launchSection.literalButton(
"launch.server", I18n["instance.launch.server"]
) { GtkMenubar.launch(data.instance, LaunchType.Server) }.iconName = "network-server-symbolic"
val settingsSection = menuBuilder.literalSection("settings", null)
settingsSection.literalButton("settings", I18n["instance.settings"]) {
//TODO keep track of properties windows and don't allow opening two
InstanceSettingsWindow(app, data.instance).visible = true
}.iconName = "document-edit-symbolic"
settingsSection.literalButton(
"directory", I18n["instance.directory"]
) { Utils.openFile(data.instance.path.toFile()) }.iconName = "folder-symbolic"
settingsSection.literalButton("copy", I18n["instance.copy"]) {
LauncherEnv.getInput(
I18n["instance.copy.prompt"],
I18n["instance.copy.details"],
InstanceNameTool.getNextValid(data.instance.name),
{ s: String? ->
try {
JFiles.copyRecursive(
data.instance.path,
MetaHolder.INSTANCE_DIR.resolve(InstanceNameTool.getNextValid(s))
)
} catch (e: IOException) {
LauncherEnv.showError(I18n["instance.copy.fail"], e)
}
}) { R.nop() }
}.iconName = "edit-copy-symbolic"
settingsSection.literalButton("delete", I18n["instance.delete"]) {
LauncherEnv.showOkCancel(
I18n["instance.delete.confirm"],
I18n["instance.delete.confirm.title"]
) {
try {
JFiles.deleteRecursive(data.instance.path)
} catch (e: IOException) {
LauncherEnv.showError(I18n["instance.delete.fail"], e)
}
}
}.iconName = "edit-delete-symbolic"
registerForUnbind(data.launch.onClicked { GtkMenubar.launch(data.instance, LaunchType.Client) })
}
override fun UnbindContext.unbind(widget: ActionRow, data: Decomposed) {
data.popoverMenu.insertActionGroup(data.instanceId, null)
}
override fun ActionContext.castWidget(widget: Widget): ActionRow = widget as ActionRow
override fun ActionContext.lookup(id: String, widget: ActionRow): Decomposed {
val instance = instanceList[id]
val prefixes = widget.firstChild!!.firstChild as Box
val suffixes = widget.firstChild!!.lastChild as Box
val thumbnail = InstanceThumbnail.castFrom(prefixes.firstChild as Stack)
val launch = suffixes.firstChild as Button
val menuButton = launch.nextSibling as MenuButton
val popoverMenu = menuButton.popover as PopoverMenu
return Decomposed(listItem, id, instance, thumbnail, launch, popoverMenu)
}
class Decomposed(
val item: ListItem,
val instanceId: String,
val instance: Instance?,
val thumbnail: InstanceThumbnail,
val launch: Button,
val popoverMenu: PopoverMenu
)
}