mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
feat: 改进事件系统与全局快捷键 (#62)
This commit is contained in:
262
src/main/kotlin/app/termora/keymap/KeymapPanel.kt
Normal file
262
src/main/kotlin/app/termora/keymap/KeymapPanel.kt
Normal file
@@ -0,0 +1,262 @@
|
||||
package app.termora.keymap
|
||||
|
||||
import app.termora.*
|
||||
import app.termora.actions.ActionManager
|
||||
import app.termora.actions.SwitchTabAction
|
||||
import com.formdev.flatlaf.FlatClientProperties
|
||||
import com.formdev.flatlaf.extras.components.FlatToolBar
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.event.InputEvent
|
||||
import java.awt.event.ItemEvent
|
||||
import java.awt.event.KeyAdapter
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.*
|
||||
import javax.swing.table.DefaultTableCellRenderer
|
||||
|
||||
|
||||
class KeymapPanel : JPanel(BorderLayout()) {
|
||||
|
||||
private val model = KeymapTableModel()
|
||||
private val table = JTable(model)
|
||||
private val keymapManager get() = KeymapManager.getInstance()
|
||||
private val keymapModel = DefaultComboBoxModel<String>()
|
||||
private val keymapComboBox = JComboBox(keymapModel)
|
||||
private val copyBtn = JButton(Icons.copy)
|
||||
private val renameBtn = JButton(Icons.edit)
|
||||
private val deleteBtn = JButton(Icons.delete)
|
||||
private val database get() = Database.getDatabase()
|
||||
private val allowKeyCodes = mutableSetOf<Int>()
|
||||
|
||||
init {
|
||||
initView()
|
||||
initEvents()
|
||||
|
||||
// select active
|
||||
keymapComboBox.selectedItem = null
|
||||
keymapComboBox.selectedItem = keymapManager.getActiveKeymap().name
|
||||
}
|
||||
|
||||
|
||||
private fun initView() {
|
||||
|
||||
for (i in KeyEvent.VK_0..KeyEvent.VK_Z) {
|
||||
allowKeyCodes.add(i)
|
||||
}
|
||||
|
||||
allowKeyCodes.add(KeyEvent.VK_EQUALS)
|
||||
allowKeyCodes.add(KeyEvent.VK_MINUS)
|
||||
|
||||
|
||||
copyBtn.toolTipText = I18n.getString("termora.welcome.contextmenu.copy")
|
||||
renameBtn.toolTipText = I18n.getString("termora.welcome.contextmenu.rename")
|
||||
deleteBtn.toolTipText = I18n.getString("termora.remove")
|
||||
|
||||
table.selectionModel.selectionMode = ListSelectionModel.SINGLE_SELECTION
|
||||
|
||||
table.setDefaultRenderer(
|
||||
Any::class.java,
|
||||
DefaultTableCellRenderer().apply {
|
||||
horizontalAlignment = SwingConstants.CENTER
|
||||
}
|
||||
)
|
||||
|
||||
table.putClientProperty(
|
||||
FlatClientProperties.STYLE, mapOf(
|
||||
"showHorizontalLines" to true,
|
||||
"showVerticalLines" to true,
|
||||
)
|
||||
)
|
||||
|
||||
val scrollPane = JScrollPane(table)
|
||||
scrollPane.border = BorderFactory.createMatteBorder(1, 1, 1, 1, DynamicColor.BorderColor)
|
||||
|
||||
table.background = UIManager.getColor("window")
|
||||
|
||||
for (keymap in keymapManager.getKeymaps()) {
|
||||
keymapModel.addElement(keymap.name)
|
||||
}
|
||||
|
||||
val box = FlatToolBar()
|
||||
box.add(keymapComboBox)
|
||||
box.add(Box.createHorizontalStrut(2))
|
||||
box.add(copyBtn)
|
||||
box.add(renameBtn)
|
||||
box.add(deleteBtn)
|
||||
box.add(Box.createHorizontalGlue())
|
||||
box.border = BorderFactory.createEmptyBorder(0, 0, 6, 0)
|
||||
|
||||
add(box, BorderLayout.NORTH)
|
||||
add(scrollPane, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
private fun initEvents() {
|
||||
table.addKeyListener(object : KeyAdapter() {
|
||||
override fun keyPressed(e: KeyEvent) {
|
||||
val row = table.selectedRow
|
||||
if (row < 0) return
|
||||
recordKeyShortcut(row, e)
|
||||
}
|
||||
})
|
||||
|
||||
copyBtn.addActionListener {
|
||||
val keymap = getCurrentKeymap()
|
||||
if (keymap != null) {
|
||||
copyKeymap(keymap)
|
||||
}
|
||||
}
|
||||
|
||||
keymapComboBox.addItemListener {
|
||||
if (it.stateChange == ItemEvent.SELECTED && keymapComboBox.selectedItem != null) {
|
||||
deleteBtn.isEnabled = !(getCurrentKeymap()?.isReadonly ?: true)
|
||||
renameBtn.isEnabled = deleteBtn.isEnabled
|
||||
database.properties.putString("Keymap.Active", keymapComboBox.selectedItem as String)
|
||||
model.fireTableDataChanged()
|
||||
}
|
||||
}
|
||||
|
||||
renameBtn.addActionListener {
|
||||
val keymap = getCurrentKeymap()
|
||||
val index = keymapComboBox.selectedIndex
|
||||
if (keymap != null && !keymap.isReadonly && index >= 0) {
|
||||
val text = InputDialog(
|
||||
SwingUtilities.getWindowAncestor(this@KeymapPanel),
|
||||
title = renameBtn.toolTipText, text = keymap.name
|
||||
).getText()
|
||||
if (!text.isNullOrBlank()) {
|
||||
if (text != keymap.name) {
|
||||
keymapManager.removeKeymap(keymap.name)
|
||||
val newKeymap = cloneKeymap(text, keymap)
|
||||
keymapManager.addKeymap(newKeymap)
|
||||
keymapModel.removeElementAt(index)
|
||||
keymapModel.insertElementAt(text, index)
|
||||
keymapModel.selectedItem = newKeymap.name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
deleteBtn.addActionListener {
|
||||
val keymap = getCurrentKeymap()
|
||||
val index = keymapComboBox.selectedIndex
|
||||
if (keymap != null && !keymap.isReadonly && index >= 0) {
|
||||
if (OptionPane.showConfirmDialog(
|
||||
SwingUtilities.getWindowAncestor(this),
|
||||
I18n.getString("termora.keymgr.delete-warning"),
|
||||
messageType = JOptionPane.WARNING_MESSAGE
|
||||
) == JOptionPane.YES_OPTION
|
||||
) {
|
||||
keymapManager.removeKeymap(keymap.name)
|
||||
keymapModel.removeElementAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun copyKeymap(keymap: Keymap) {
|
||||
var name = keymap.name + " Copy"
|
||||
for (i in 0 until Int.MAX_VALUE) {
|
||||
if (keymapManager.getKeymap(name) == null) {
|
||||
break
|
||||
}
|
||||
name = keymap.name + " Copy(${i + 1})"
|
||||
}
|
||||
|
||||
keymapManager.addKeymap(cloneKeymap(name, keymap))
|
||||
|
||||
keymapModel.insertElementAt(name, 0)
|
||||
keymapComboBox.selectedItem = name
|
||||
}
|
||||
|
||||
private fun cloneKeymap(name: String, keymap: Keymap): Keymap {
|
||||
val newKeymap = Keymap(name, null, false)
|
||||
for (e in keymap.getShortcuts()) {
|
||||
for (actionId in e.value) {
|
||||
newKeymap.addShortcut(actionId, e.key)
|
||||
}
|
||||
}
|
||||
return newKeymap
|
||||
}
|
||||
|
||||
private fun getCurrentKeymap(): Keymap? {
|
||||
return keymapManager.getKeymap(keymapComboBox.selectedItem as String)
|
||||
}
|
||||
|
||||
private fun recordKeyShortcut(row: Int, e: KeyEvent) {
|
||||
val action = model.getAction(row) ?: return
|
||||
val actionId = (action.getValue(Action.ACTION_COMMAND_KEY) ?: return).toString()
|
||||
val keyStroke = KeyStroke.getKeyStrokeForEvent(e)
|
||||
|
||||
// 如果是选择Tab
|
||||
if (actionId == SwitchTabAction.SWITCH_TAB && keyStroke.keyCode != KeyEvent.VK_BACK_SPACE) {
|
||||
// 如果是 Tab ,那么 keyCode 必须是功能键
|
||||
if (keyStroke.keyCode != KeyEvent.VK_META
|
||||
&& keyStroke.keyCode != KeyEvent.VK_SHIFT
|
||||
&& keyStroke.keyCode != KeyEvent.VK_CONTROL
|
||||
&& keyStroke.keyCode != KeyEvent.VK_ALT
|
||||
) {
|
||||
return
|
||||
}
|
||||
} else if (!isCombinationKey(keyStroke) && keyStroke.keyCode == KeyEvent.VK_BACK_SPACE) {
|
||||
// ignore
|
||||
} else if (!isCombinationKey(keyStroke) || (!allowKeyCodes.contains(keyStroke.keyCode))) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
var keymap = getCurrentKeymap() ?: return
|
||||
if (keymap.isReadonly) {
|
||||
copyKeymap(keymap)
|
||||
keymap = getCurrentKeymap() ?: return
|
||||
}
|
||||
|
||||
e.consume()
|
||||
|
||||
val keyShortcut = KeyShortcut(keyStroke)
|
||||
if (e.keyCode == KeyEvent.VK_BACK_SPACE) {
|
||||
keymap.removeAllActionShortcuts(actionId)
|
||||
} else {
|
||||
val actionIds = keymap.getActionIds(keyShortcut).toMutableList()
|
||||
actionIds.removeIf { it == actionId }
|
||||
if (actionIds.isNotEmpty()) {
|
||||
for (id in actionIds) {
|
||||
val duplicateAction = ActionManager.getInstance().getAction(id) ?: continue
|
||||
val text = duplicateAction.getValue(Action.SHORT_DESCRIPTION) ?: continue
|
||||
OptionPane.showMessageDialog(
|
||||
SwingUtilities.getWindowAncestor(this@KeymapPanel),
|
||||
I18n.getString("termora.settings.keymap.already-exists", model.toHumanText(keyStroke), text),
|
||||
messageType = JOptionPane.ERROR_MESSAGE,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
keymap.removeAllActionShortcuts(actionId)
|
||||
|
||||
// SwitchTab 比较特殊
|
||||
if (actionId == SwitchTabAction.SWITCH_TAB) {
|
||||
for (i in KeyEvent.VK_1..KeyEvent.VK_9) {
|
||||
keymap.addShortcut(actionId, KeyShortcut(KeyStroke.getKeyStroke(i, keyStroke.modifiers)))
|
||||
}
|
||||
} else {
|
||||
// 添加到快捷键
|
||||
keymap.addShortcut(actionId, keyShortcut)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
model.fireTableRowsUpdated(row, row)
|
||||
keymapManager.addKeymap(keymap)
|
||||
}
|
||||
|
||||
private fun isCombinationKey(keyStroke: KeyStroke): Boolean {
|
||||
val modifiers = keyStroke.modifiers
|
||||
return (modifiers and InputEvent.CTRL_DOWN_MASK) != 0
|
||||
|| (modifiers and InputEvent.SHIFT_DOWN_MASK) != 0
|
||||
|| (modifiers and InputEvent.ALT_DOWN_MASK) != 0
|
||||
|| (modifiers and InputEvent.META_DOWN_MASK) != 0
|
||||
|| (modifiers and InputEvent.ALT_GRAPH_DOWN_MASK) != 0
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user