171 lines
5.6 KiB
Kotlin
171 lines
5.6 KiB
Kotlin
package io.gitlab.jfronny.inceptum.gtk.menu
|
|
|
|
import io.github.jwharm.javagi.glib.types.VariantTypes
|
|
import io.gitlab.jfronny.commons.throwable.ThrowingRunnable
|
|
import io.gitlab.jfronny.inceptum.gtk.util.I18n
|
|
import io.gitlab.jfronny.inceptum.gtk.util.Log
|
|
import org.gnome.gio.*
|
|
import org.gnome.glib.Variant
|
|
import org.gnome.gtk.Application
|
|
import org.gnome.gtk.MenuButton
|
|
import org.gnome.gtk.PopoverMenu
|
|
import java.util.function.BiFunction
|
|
import java.util.function.Consumer
|
|
|
|
class MenuBuilder private constructor(map: ActionMap, menu: Menu, prefix: String, groupName: String) {
|
|
private val map: ActionMap
|
|
private val refs: MutableMap<String, Action> = LinkedHashMap()
|
|
val menu: Menu
|
|
private val prefix: String
|
|
private val groupName: String
|
|
|
|
constructor(menu: PopoverMenu, groupName: String) : this(
|
|
insertMap(menu, groupName),
|
|
menu.menuModel as Menu,
|
|
"",
|
|
groupName
|
|
)
|
|
|
|
@JvmOverloads
|
|
constructor(app: Application, menu: Menu = getRootMenu(app), prefix: String = "") : this(app, menu, prefix, "app.")
|
|
|
|
init {
|
|
fun String.suffix() = if (isNotEmpty() && !endsWith(".")) "$this." else this
|
|
|
|
this.map = map
|
|
this.menu = menu
|
|
this.prefix = prefix.suffix()
|
|
this.groupName = groupName.suffix()
|
|
}
|
|
|
|
fun button(name: String, onClick: ThrowingRunnable<*>): BuiltButtonItem {
|
|
return literalButton(name, I18n["menu.$prefix$name"], onClick)
|
|
}
|
|
|
|
fun literalButton(internalName: String, label: String?, onClick: ThrowingRunnable<*>): BuiltButtonItem {
|
|
var internalName = internalName
|
|
internalName = prefix + internalName
|
|
val action = SimpleAction(internalName, null)
|
|
addAction(internalName, action)
|
|
action.onActivate { _ ->
|
|
try {
|
|
onClick.run()
|
|
} catch (e: Throwable) {
|
|
Log.error("Could not execute action", e)
|
|
}
|
|
}
|
|
val menuItem = MenuItem(label, groupName + internalName)
|
|
menu.appendItem(menuItem)
|
|
action.enabled = true
|
|
return BuiltButtonItem(action, menuItem)
|
|
}
|
|
|
|
fun toggle(name: String, initial: Boolean, onToggle: Consumer<Boolean?>): BuiltToggleItem {
|
|
var name = name
|
|
name = prefix + name
|
|
val action = SimpleAction.newStateful(name, null, Variant.newBoolean(initial))
|
|
addAction(name, action)
|
|
action.onActivate { _ ->
|
|
val state = !action.getState()!!.boolean
|
|
action.state = Variant.newBoolean(state)
|
|
onToggle.accept(state)
|
|
}
|
|
val menuItem = MenuItem(I18n["menu.$name"], groupName + name)
|
|
menu.appendItem(menuItem)
|
|
return BuiltToggleItem(action, menuItem)
|
|
}
|
|
|
|
fun <T> radio(name: String, initial: T, options: List<T>, onCheck: Consumer<T>): BuiltRadioItem<T> {
|
|
return literalRadio(name, initial, options, { i, _ -> I18n["menu.$prefix$name", i] }, onCheck)
|
|
}
|
|
|
|
fun <T> literalRadio(
|
|
name: String,
|
|
initial: T,
|
|
options: List<T>,
|
|
stringifier: BiFunction<Int, T, String?>,
|
|
onCheck: Consumer<T>
|
|
): BuiltRadioItem<T> {
|
|
var name = name
|
|
name = prefix + name
|
|
val action = SimpleAction.newStateful(name, VariantTypes.INT32, Variant.newInt32(options.indexOf(initial)))
|
|
addAction(name, action)
|
|
action.onActivate { variant: Variant? ->
|
|
action.state = variant
|
|
onCheck.accept(options[variant!!.int32])
|
|
}
|
|
for ((i, option) in options.withIndex()) {
|
|
menu.appendItem(MenuItem(stringifier.apply(i, option), "$groupName$name($i)"))
|
|
}
|
|
return BuiltRadioItem(action, options)
|
|
}
|
|
|
|
fun submenu(name: String): MenuBuilder {
|
|
return literalSubmenu(name, I18n["menu.$prefix$name"])
|
|
}
|
|
|
|
fun literalSubmenu(name: String, label: String?): MenuBuilder {
|
|
var name = name
|
|
name = prefix + name
|
|
val submenu = Menu()
|
|
menu.appendSubmenu(label, submenu)
|
|
return MenuBuilder(map, submenu, name, groupName)
|
|
}
|
|
|
|
fun section(name: String): MenuBuilder {
|
|
return literalSection(name, I18n["section.$prefix$name"])
|
|
}
|
|
|
|
fun literalSection(name: String, label: String?): MenuBuilder {
|
|
var name = name
|
|
name = prefix + name
|
|
val section = Menu()
|
|
menu.appendSection(label, section)
|
|
return MenuBuilder(map, section, name, groupName)
|
|
}
|
|
|
|
fun clear() {
|
|
menu.removeAll()
|
|
refs.forEach { (name, _) -> map.removeAction(name) }
|
|
refs.clear()
|
|
}
|
|
|
|
private fun addAction(name: String, action: SimpleAction) {
|
|
map.addAction(action)
|
|
refs[name] = action
|
|
}
|
|
|
|
fun asPopover(): PopoverMenu {
|
|
return PopoverMenu.newFromModel(menu)
|
|
}
|
|
|
|
companion object {
|
|
private val LOCK = Any()
|
|
private fun getRootMenu(app: Application): Menu {
|
|
synchronized(LOCK) {
|
|
val currentMenu = app.menubar
|
|
return if (currentMenu == null) {
|
|
val menu = Menu()
|
|
app.menubar = menu
|
|
menu
|
|
} else {
|
|
currentMenu as Menu
|
|
}
|
|
}
|
|
}
|
|
|
|
fun create(target: MenuButton, groupName: String): MenuBuilder {
|
|
val menu = Menu()
|
|
val pm = PopoverMenu.newFromModel(menu)
|
|
target.setPopover(pm)
|
|
return MenuBuilder(pm, groupName)
|
|
}
|
|
|
|
private fun insertMap(menu: PopoverMenu, groupName: String): ActionMap {
|
|
val ag = SimpleActionGroup()
|
|
menu.insertActionGroup(groupName, ag)
|
|
return ag
|
|
}
|
|
}
|
|
}
|