mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-15 18:02:58 +08:00
refactor: frame toolbar
This commit is contained in:
@@ -1,8 +1,6 @@
|
|||||||
package app.termora
|
package app.termora
|
||||||
|
|
||||||
import app.termora.actions.ActionManager
|
|
||||||
import app.termora.database.DatabaseManager
|
import app.termora.database.DatabaseManager
|
||||||
import app.termora.keymap.KeymapManager
|
|
||||||
import app.termora.plugin.ExtensionManager
|
import app.termora.plugin.ExtensionManager
|
||||||
import app.termora.plugin.PluginManager
|
import app.termora.plugin.PluginManager
|
||||||
import com.formdev.flatlaf.FlatClientProperties
|
import com.formdev.flatlaf.FlatClientProperties
|
||||||
@@ -54,12 +52,6 @@ class ApplicationRunner {
|
|||||||
// 统计
|
// 统计
|
||||||
enableAnalytics()
|
enableAnalytics()
|
||||||
|
|
||||||
// init ActionManager、KeymapManager、VFS
|
|
||||||
swingCoroutineScope.launch(Dispatchers.IO) {
|
|
||||||
ActionManager.getInstance()
|
|
||||||
KeymapManager.getInstance()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置 LAF
|
// 设置 LAF
|
||||||
setupLaf()
|
setupLaf()
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package app.termora
|
package app.termora
|
||||||
|
|
||||||
import app.termora.Application.ohMyJson
|
|
||||||
import app.termora.actions.MultipleAction
|
|
||||||
import app.termora.database.DatabaseManager
|
|
||||||
import com.jgoodies.forms.builder.FormBuilder
|
import com.jgoodies.forms.builder.FormBuilder
|
||||||
import com.jgoodies.forms.layout.FormLayout
|
import com.jgoodies.forms.layout.FormLayout
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
@@ -18,10 +15,10 @@ import javax.swing.event.ListDataListener
|
|||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class CustomizeToolBarDialog(
|
internal class CustomizeToolBarDialog(
|
||||||
owner: Window,
|
owner: Window,
|
||||||
private val windowScope: WindowScope,
|
private val windowScope: WindowScope,
|
||||||
private val toolbar: TermoraToolBar
|
private val model: TermoraToolbarModel,
|
||||||
) : DialogWrapper(owner) {
|
) : DialogWrapper(owner) {
|
||||||
|
|
||||||
private val moveTopBtn = JButton(Icons.moveUp)
|
private val moveTopBtn = JButton(Icons.moveUp)
|
||||||
@@ -40,6 +37,7 @@ class CustomizeToolBarDialog(
|
|||||||
private val actionManager get() = ActionManager.getInstance()
|
private val actionManager get() = ActionManager.getInstance()
|
||||||
|
|
||||||
private var isOk = false
|
private var isOk = false
|
||||||
|
private val actions = mutableListOf<ToolBarAction>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
size = Dimension(UIManager.getInt("Dialog.width") - 150, UIManager.getInt("Dialog.height") - 100)
|
size = Dimension(UIManager.getInt("Dialog.width") - 150, UIManager.getInt("Dialog.height") - 100)
|
||||||
@@ -147,7 +145,7 @@ class CustomizeToolBarDialog(
|
|||||||
resetBtn.addActionListener {
|
resetBtn.addActionListener {
|
||||||
leftList.model.removeAllElements()
|
leftList.model.removeAllElements()
|
||||||
rightList.model.removeAllElements()
|
rightList.model.removeAllElements()
|
||||||
for (action in toolbar.getAllActions()) {
|
for (action in model.getAllActions()) {
|
||||||
getActionHolder(action.id)?.let { rightList.model.addElement(it) }
|
getActionHolder(action.id)?.let { rightList.model.addElement(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,7 +256,7 @@ class CustomizeToolBarDialog(
|
|||||||
override fun windowOpened(e: WindowEvent) {
|
override fun windowOpened(e: WindowEvent) {
|
||||||
removeWindowListener(this)
|
removeWindowListener(this)
|
||||||
|
|
||||||
for (action in toolbar.getActions()) {
|
for (action in model.getActions()) {
|
||||||
if (action.visible) {
|
if (action.visible) {
|
||||||
getActionHolder(action.id)?.let { rightList.model.addElement(it) }
|
getActionHolder(action.id)?.let { rightList.model.addElement(it) }
|
||||||
} else {
|
} else {
|
||||||
@@ -271,12 +269,7 @@ class CustomizeToolBarDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getActionHolder(actionId: String): ActionHolder? {
|
private fun getActionHolder(actionId: String): ActionHolder? {
|
||||||
var action = actionManager.getAction(actionId)
|
val action = actionManager.getAction(actionId)
|
||||||
if (action == null) {
|
|
||||||
if (actionId == MultipleAction.MULTIPLE) {
|
|
||||||
action = MultipleAction.getInstance(windowScope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (action == null) return null
|
if (action == null) return null
|
||||||
return ActionHolder(actionId, action)
|
return ActionHolder(actionId, action)
|
||||||
}
|
}
|
||||||
@@ -365,12 +358,14 @@ class CustomizeToolBarDialog(
|
|||||||
actions.add(ToolBarAction(leftList.model.getElementAt(i).id, false))
|
actions.add(ToolBarAction(leftList.model.getElementAt(i).id, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseManager.getInstance()
|
this.actions.clear()
|
||||||
.properties.putString("Termora.ToolBar.Actions", ohMyJson.encodeToString(actions))
|
this.actions.addAll(actions)
|
||||||
|
|
||||||
super.doOKAction()
|
super.doOKAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getActions()=actions
|
||||||
|
|
||||||
fun open(): Boolean {
|
fun open(): Boolean {
|
||||||
isModal = true
|
isModal = true
|
||||||
isVisible = true
|
isVisible = true
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class MultipleTerminalListener : TerminalPaintListener {
|
|||||||
) {
|
) {
|
||||||
val windowScope = AnActionEvent(terminalPanel, StringUtils.EMPTY, EventObject(terminalPanel))
|
val windowScope = AnActionEvent(terminalPanel, StringUtils.EMPTY, EventObject(terminalPanel))
|
||||||
.getData(DataProviders.WindowScope) ?: return
|
.getData(DataProviders.WindowScope) ?: return
|
||||||
if (!MultipleAction.getInstance(windowScope).isSelected) return
|
if (MultipleAction.getInstance().isSelected(windowScope).not()) return
|
||||||
|
|
||||||
val oldFont = g.font
|
val oldFont = g.font
|
||||||
val colorPalette = terminal.getTerminalModel().getColorPalette()
|
val colorPalette = terminal.getTerminalModel().getColorPalette()
|
||||||
|
|||||||
153
src/main/kotlin/app/termora/MyTermoraToolbar.kt
Normal file
153
src/main/kotlin/app/termora/MyTermoraToolbar.kt
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
package app.termora
|
||||||
|
|
||||||
|
import app.termora.actions.StateAction
|
||||||
|
import app.termora.plugin.internal.badge.Badge
|
||||||
|
import com.formdev.flatlaf.extras.components.FlatPopupMenu
|
||||||
|
import com.formdev.flatlaf.extras.components.FlatToolBar
|
||||||
|
import java.awt.AWTEvent
|
||||||
|
import java.awt.Rectangle
|
||||||
|
import java.awt.event.AWTEventListener
|
||||||
|
import java.awt.event.ActionEvent
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
|
import java.beans.PropertyChangeEvent
|
||||||
|
import java.beans.PropertyChangeListener
|
||||||
|
import javax.swing.*
|
||||||
|
|
||||||
|
internal class MyTermoraToolbar(private val windowScope: WindowScope) : FlatToolBar() {
|
||||||
|
|
||||||
|
|
||||||
|
private val customizeToolBarAWTEventListener = CustomizeToolBarAWTEventListener()
|
||||||
|
private val model get() = TermoraToolbarModel.getInstance()
|
||||||
|
private val actionManager get() = model.getActionManager()
|
||||||
|
private val toolbar get() = this
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一次性生命周期 每次刷新都会重置
|
||||||
|
*/
|
||||||
|
private var disposable = Disposer.newDisposable()
|
||||||
|
|
||||||
|
init {
|
||||||
|
initView()
|
||||||
|
initEvents()
|
||||||
|
refreshActions()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
isFloatable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initEvents() {
|
||||||
|
Disposer.register(windowScope, object : Disposable {
|
||||||
|
override fun dispose() {
|
||||||
|
Disposer.dispose(disposable)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// 监听全局事件
|
||||||
|
toolkit.addAWTEventListener(customizeToolBarAWTEventListener, AWTEvent.MOUSE_EVENT_MASK)
|
||||||
|
Disposer.register(windowScope, customizeToolBarAWTEventListener)
|
||||||
|
|
||||||
|
// 监听变化
|
||||||
|
model.addTermoraToolbarModelListener(object : TermoraToolbarModel.TermoraToolbarModelListener {
|
||||||
|
override fun onChanged() {
|
||||||
|
refreshActions()
|
||||||
|
}
|
||||||
|
}).let { Disposer.register(windowScope, it) }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshActions() {
|
||||||
|
Disposer.dispose(disposable)
|
||||||
|
disposable = Disposer.newDisposable()
|
||||||
|
|
||||||
|
removeAll()
|
||||||
|
add(Box.createHorizontalGlue())
|
||||||
|
|
||||||
|
for (action in model.getActions()) {
|
||||||
|
if (action.visible.not()) continue
|
||||||
|
val action = actionManager.getAction(action.id) ?: continue
|
||||||
|
add(redirectAction(action, disposable))
|
||||||
|
}
|
||||||
|
|
||||||
|
revalidate()
|
||||||
|
repaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun redirectAction(action: Action, disposable: Disposable): AbstractButton {
|
||||||
|
val button = if (action is StateAction) JToggleButton() else JButton()
|
||||||
|
button.toolTipText = action.getValue(Action.SHORT_DESCRIPTION) as? String
|
||||||
|
button.icon = action.getValue(Action.SMALL_ICON) as? Icon
|
||||||
|
button.addActionListener(object : AbstractAction() {
|
||||||
|
override fun actionPerformed(e: ActionEvent) {
|
||||||
|
action.actionPerformed(e)
|
||||||
|
if (action is StateAction) {
|
||||||
|
button.isSelected = action.isSelected(windowScope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
val listener = object : PropertyChangeListener, Disposable {
|
||||||
|
private val badge get() = Badge.getInstance(windowScope)
|
||||||
|
override fun propertyChange(evt: PropertyChangeEvent) {
|
||||||
|
if (evt.propertyName == "Badge") {
|
||||||
|
if (action.getValue("Badge") == true) {
|
||||||
|
badge.addBadge(button)
|
||||||
|
} else {
|
||||||
|
badge.removeBadge(button)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action is StateAction) {
|
||||||
|
button.isSelected = action.isSelected(windowScope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dispose() {
|
||||||
|
action.removePropertyChangeListener(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
action.addPropertyChangeListener(listener)
|
||||||
|
Disposer.register(disposable, listener)
|
||||||
|
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对着 ToolBar 右键
|
||||||
|
*/
|
||||||
|
private inner class CustomizeToolBarAWTEventListener : AWTEventListener, Disposable {
|
||||||
|
override fun eventDispatched(event: AWTEvent) {
|
||||||
|
if (event !is MouseEvent || event.id != MouseEvent.MOUSE_CLICKED
|
||||||
|
|| SwingUtilities.isRightMouseButton(event).not()
|
||||||
|
) return
|
||||||
|
|
||||||
|
// 如果 ToolBar 没有显示
|
||||||
|
if (toolbar.isShowing.not()) return
|
||||||
|
|
||||||
|
// 如果不是作用于在 ToolBar 上面
|
||||||
|
if (Rectangle(toolbar.locationOnScreen, toolbar.size).contains(event.locationOnScreen).not()) return
|
||||||
|
|
||||||
|
// 显示右键菜单
|
||||||
|
showContextMenu(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showContextMenu(event: MouseEvent) {
|
||||||
|
val popupMenu = FlatPopupMenu()
|
||||||
|
popupMenu.add(I18n.getString("termora.toolbar.customize-toolbar")).addActionListener {
|
||||||
|
val owner = windowScope.window
|
||||||
|
val dialog = CustomizeToolBarDialog(owner, windowScope, model)
|
||||||
|
dialog.setLocationRelativeTo(owner)
|
||||||
|
if (dialog.open()) {
|
||||||
|
model.setActions(dialog.getActions())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popupMenu.show(event.component, event.x, event.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dispose() {
|
||||||
|
toolkit.removeAWTEventListener(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -133,8 +133,8 @@ class TerminalPanelFactory : Disposable {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val multipleAction = MultipleAction.getInstance(windowScope)
|
val multipleAction = MultipleAction.getInstance()
|
||||||
if (!multipleAction.isSelected) {
|
if (multipleAction.isSelected(windowScope).not()) {
|
||||||
ptyConnector.write(request.buffer)
|
ptyConnector.write(request.buffer)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,13 +28,15 @@ import kotlin.math.min
|
|||||||
|
|
||||||
class TerminalTabbed(
|
class TerminalTabbed(
|
||||||
private val windowScope: WindowScope,
|
private val windowScope: WindowScope,
|
||||||
private val termoraToolBar: TermoraToolBar,
|
|
||||||
private val tabbedPane: FlatTabbedPane,
|
private val tabbedPane: FlatTabbedPane,
|
||||||
private val layout: TermoraLayout,
|
private val layout: TermoraLayout,
|
||||||
) : JPanel(BorderLayout()), Disposable, TerminalTabbedManager, DataProvider {
|
) : JPanel(BorderLayout()), Disposable, TerminalTabbedManager, DataProvider {
|
||||||
private val tabs = mutableListOf<TerminalTab>()
|
private val tabs = mutableListOf<TerminalTab>()
|
||||||
private val customizeToolBarAWTEventListener = CustomizeToolBarAWTEventListener()
|
private val customizeToolBarAWTEventListener = object : AWTEventListener, Disposable {
|
||||||
private val toolbar = termoraToolBar.getJToolBar()
|
override fun eventDispatched(event: AWTEvent?) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
private val actionManager = ActionManager.getInstance()
|
private val actionManager = ActionManager.getInstance()
|
||||||
private val dataProviderSupport = DataProviderSupport()
|
private val dataProviderSupport = DataProviderSupport()
|
||||||
private val appearance get() = DatabaseManager.getInstance().appearance
|
private val appearance get() = DatabaseManager.getInstance().appearance
|
||||||
@@ -60,8 +62,6 @@ class TerminalTabbed(
|
|||||||
tabbedPane.isTabsClosable = true
|
tabbedPane.isTabsClosable = true
|
||||||
tabbedPane.tabType = FlatTabbedPane.TabType.card
|
tabbedPane.tabType = FlatTabbedPane.TabType.card
|
||||||
|
|
||||||
tabbedPane.trailingComponent = toolbar
|
|
||||||
|
|
||||||
add(tabbedPane, BorderLayout.CENTER)
|
add(tabbedPane, BorderLayout.CENTER)
|
||||||
|
|
||||||
windowScope.getOrCreate(TerminalTabbedManager::class) { this }
|
windowScope.getOrCreate(TerminalTabbedManager::class) { this }
|
||||||
@@ -388,7 +388,7 @@ class TerminalTabbed(
|
|||||||
/**
|
/**
|
||||||
* 对着 ToolBar 右键
|
* 对着 ToolBar 右键
|
||||||
*/
|
*/
|
||||||
private inner class CustomizeToolBarAWTEventListener : AWTEventListener, Disposable {
|
/*private inner class CustomizeToolBarAWTEventListener : AWTEventListener, Disposable {
|
||||||
override fun eventDispatched(event: AWTEvent) {
|
override fun eventDispatched(event: AWTEvent) {
|
||||||
if (event !is MouseEvent || event.id != MouseEvent.MOUSE_CLICKED || !SwingUtilities.isRightMouseButton(event)) return
|
if (event !is MouseEvent || event.id != MouseEvent.MOUSE_CLICKED || !SwingUtilities.isRightMouseButton(event)) return
|
||||||
// 如果 ToolBar 没有显示
|
// 如果 ToolBar 没有显示
|
||||||
@@ -416,7 +416,7 @@ class TerminalTabbed(
|
|||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
toolkit.removeAWTEventListener(this)
|
toolkit.removeAWTEventListener(this)
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/*private inner class CustomizeToolBarDialog(owner: Window) : DialogWrapper(owner) {
|
/*private inner class CustomizeToolBarDialog(owner: Window) : DialogWrapper(owner) {
|
||||||
init {
|
init {
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ class TermoraFrame : JFrame(), DataProvider {
|
|||||||
private val id = UUID.randomUUID().toString()
|
private val id = UUID.randomUUID().toString()
|
||||||
private val windowScope = ApplicationScope.forWindowScope(this)
|
private val windowScope = ApplicationScope.forWindowScope(this)
|
||||||
private val tabbedPane = MyTabbedPane().apply { tabHeight = titleBarHeight }
|
private val tabbedPane = MyTabbedPane().apply { tabHeight = titleBarHeight }
|
||||||
private val toolbar = TermoraToolBar(windowScope, this)
|
private val toolbar = MyTermoraToolbar(windowScope)
|
||||||
private val terminalTabbed = TerminalTabbed(windowScope, toolbar, tabbedPane, layout)
|
private val terminalTabbed = TerminalTabbed(windowScope, tabbedPane, layout)
|
||||||
private val dataProviderSupport = DataProviderSupport()
|
private val dataProviderSupport = DataProviderSupport()
|
||||||
private var notifyListeners = emptyArray<NotifyListener>()
|
private var notifyListeners = emptyArray<NotifyListener>()
|
||||||
private val moveMouseAdapter = createMoveMouseAdaptor()
|
private val moveMouseAdapter = createMoveMouseAdaptor()
|
||||||
@@ -59,8 +59,8 @@ class TermoraFrame : JFrame(), DataProvider {
|
|||||||
|
|
||||||
private fun initEvents() {
|
private fun initEvents() {
|
||||||
if (SystemInfo.isLinux) {
|
if (SystemInfo.isLinux) {
|
||||||
toolbar.getJToolBar().addMouseListener(moveMouseAdapter)
|
toolbar.addMouseListener(moveMouseAdapter)
|
||||||
toolbar.getJToolBar().addMouseMotionListener(moveMouseAdapter)
|
toolbar.addMouseMotionListener(moveMouseAdapter)
|
||||||
} else if (SystemInfo.isMacOS) {
|
} else if (SystemInfo.isMacOS) {
|
||||||
terminalTabbed.addMouseListener(moveMouseAdapter)
|
terminalTabbed.addMouseListener(moveMouseAdapter)
|
||||||
terminalTabbed.addMouseMotionListener(moveMouseAdapter)
|
terminalTabbed.addMouseMotionListener(moveMouseAdapter)
|
||||||
@@ -68,8 +68,8 @@ class TermoraFrame : JFrame(), DataProvider {
|
|||||||
tabbedPane.addMouseListener(moveMouseAdapter)
|
tabbedPane.addMouseListener(moveMouseAdapter)
|
||||||
tabbedPane.addMouseMotionListener(moveMouseAdapter)
|
tabbedPane.addMouseMotionListener(moveMouseAdapter)
|
||||||
|
|
||||||
toolbar.getJToolBar().addMouseListener(moveMouseAdapter)
|
toolbar.addMouseListener(moveMouseAdapter)
|
||||||
toolbar.getJToolBar().addMouseMotionListener(moveMouseAdapter)
|
toolbar.addMouseMotionListener(moveMouseAdapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 快捷键变动时重新监听
|
// 快捷键变动时重新监听
|
||||||
@@ -168,6 +168,8 @@ class TermoraFrame : JFrame(), DataProvider {
|
|||||||
tabbedPane.tabAreaInsets = Insets(1, 2, 0, 0)
|
tabbedPane.tabAreaInsets = Insets(1, 2, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tabbedPane.trailingComponent = toolbar
|
||||||
|
|
||||||
val height = UIManager.getInt("TabbedPane.tabHeight") + tabbedPane.tabAreaInsets.top
|
val height = UIManager.getInt("TabbedPane.tabHeight") + tabbedPane.tabAreaInsets.top
|
||||||
|
|
||||||
if (SystemInfo.isWindows || SystemInfo.isLinux) {
|
if (SystemInfo.isWindows || SystemInfo.isLinux) {
|
||||||
@@ -228,11 +230,26 @@ class TermoraFrame : JFrame(), DataProvider {
|
|||||||
|
|
||||||
for ((shortcut, actionIds) in keymap.getShortcuts()) {
|
for ((shortcut, actionIds) in keymap.getShortcuts()) {
|
||||||
if (shortcut !is KeyShortcut) continue
|
if (shortcut !is KeyShortcut) continue
|
||||||
val keyShortcutActionId = "KeyShortcutAction_${randomUUID()}"
|
if (actionIds.contains(SwitchTabAction.SWITCH_TAB)) continue
|
||||||
actionMap.put(keyShortcutActionId, redirectAction(actionIds))
|
registerKeyStroke(actionMap, inputMap, shortcut.keyStroke, actionIds)
|
||||||
inputMap.put(shortcut.keyStroke, keyShortcutActionId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (shortcut in keymap.getShortcut(SwitchTabAction.SWITCH_TAB)) {
|
||||||
|
if (shortcut !is KeyShortcut) continue
|
||||||
|
registerKeyStroke(actionMap, inputMap, shortcut.keyStroke, listOf(SwitchTabAction.SWITCH_TAB))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerKeyStroke(
|
||||||
|
actionMap: ActionMap,
|
||||||
|
inputMap: InputMap,
|
||||||
|
keyStroke: KeyStroke,
|
||||||
|
actionIds: List<String>
|
||||||
|
) {
|
||||||
|
val keyShortcutActionId = "KeyShortcutAction_${randomUUID()}"
|
||||||
|
actionMap.put(keyShortcutActionId, redirectAction(actionIds))
|
||||||
|
inputMap.put(keyStroke, keyShortcutActionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun redirectAction(actionIds: List<String>): Action {
|
private fun redirectAction(actionIds: List<String>): Action {
|
||||||
@@ -249,7 +266,7 @@ class TermoraFrame : JFrame(), DataProvider {
|
|||||||
|
|
||||||
for (actionId in actionIds) {
|
for (actionId in actionIds) {
|
||||||
val action = actionManager.getAction(actionId) ?: continue
|
val action = actionManager.getAction(actionId) ?: continue
|
||||||
action.actionPerformed(RedirectAnActionEvent(source, e.actionCommand, e))
|
action.actionPerformed(RedirectAnActionEvent(source, e.actionCommand, EventQueue.getCurrentEvent()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,192 +0,0 @@
|
|||||||
package app.termora
|
|
||||||
|
|
||||||
import app.termora.Application.ohMyJson
|
|
||||||
import app.termora.actions.*
|
|
||||||
import app.termora.database.DatabaseManager
|
|
||||||
import app.termora.findeverywhere.FindEverywhereAction
|
|
||||||
import app.termora.snippet.SnippetAction
|
|
||||||
import com.formdev.flatlaf.FlatClientProperties
|
|
||||||
import com.formdev.flatlaf.util.SystemInfo
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import org.apache.commons.lang3.StringUtils
|
|
||||||
import org.jdesktop.swingx.action.ActionContainerFactory
|
|
||||||
import java.awt.Rectangle
|
|
||||||
import java.awt.event.ComponentAdapter
|
|
||||||
import java.awt.event.ComponentEvent
|
|
||||||
import javax.swing.Box
|
|
||||||
import javax.swing.JToolBar
|
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ToolBarAction(
|
|
||||||
val id: String,
|
|
||||||
val visible: Boolean,
|
|
||||||
)
|
|
||||||
|
|
||||||
class TermoraToolBar(
|
|
||||||
private val windowScope: WindowScope,
|
|
||||||
private val frame: TermoraFrame,
|
|
||||||
) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun rebuild() {
|
|
||||||
for (frame in TermoraFrameManager.getInstance().getWindows()) {
|
|
||||||
val toolbars = SwingUtils.getDescendantsOfClass(MyToolBar::class.java, frame)
|
|
||||||
for (toolbar in toolbars) {
|
|
||||||
toolbar.rebuild()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val properties get() = DatabaseManager.getInstance().properties
|
|
||||||
private val toolbar by lazy { MyToolBar().apply { rebuild() } }
|
|
||||||
|
|
||||||
|
|
||||||
fun getJToolBar(): JToolBar {
|
|
||||||
return toolbar
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取到所有的 Action
|
|
||||||
*/
|
|
||||||
fun getAllActions(): List<ToolBarAction> {
|
|
||||||
return listOf(
|
|
||||||
ToolBarAction(SnippetAction.SNIPPET, true),
|
|
||||||
ToolBarAction(Actions.SFTP, true),
|
|
||||||
ToolBarAction(Actions.TERMINAL_LOGGER, true),
|
|
||||||
ToolBarAction(Actions.MACRO, true),
|
|
||||||
ToolBarAction(Actions.KEYWORD_HIGHLIGHT, true),
|
|
||||||
ToolBarAction(Actions.KEY_MANAGER, true),
|
|
||||||
ToolBarAction(MultipleAction.MULTIPLE, true),
|
|
||||||
ToolBarAction(FindEverywhereAction.FIND_EVERYWHERE, true),
|
|
||||||
ToolBarAction(SettingsAction.SETTING, true),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取到所有 Action,会根据用户个性化排序/显示
|
|
||||||
*/
|
|
||||||
fun getActions(): List<ToolBarAction> {
|
|
||||||
val text = properties.getString(
|
|
||||||
"Termora.ToolBar.Actions",
|
|
||||||
StringUtils.EMPTY
|
|
||||||
)
|
|
||||||
|
|
||||||
val actions = getAllActions()
|
|
||||||
|
|
||||||
if (text.isBlank()) {
|
|
||||||
return actions
|
|
||||||
}
|
|
||||||
|
|
||||||
// 存储的 action
|
|
||||||
val storageActions = (ohMyJson.runCatching {
|
|
||||||
ohMyJson.decodeFromString<List<ToolBarAction>>(text)
|
|
||||||
}.getOrNull() ?: return actions).toMutableList()
|
|
||||||
|
|
||||||
for (action in actions) {
|
|
||||||
// 如果存储的 action 不包含这个,那么这个可能是新增的,新增的默认显示出来
|
|
||||||
if (storageActions.none { it.id == action.id }) {
|
|
||||||
storageActions.addFirst(ToolBarAction(action.id, true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果存储的 Action 在所有 Action 里没有,那么移除
|
|
||||||
storageActions.removeIf { e -> actions.none { e.id == it.id } }
|
|
||||||
|
|
||||||
return storageActions
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class MyToolBar : JToolBar() {
|
|
||||||
init {
|
|
||||||
// 监听窗口大小变动,然后修改边距避开控制按钮
|
|
||||||
addComponentListener(object : ComponentAdapter() {
|
|
||||||
override fun componentResized(e: ComponentEvent) {
|
|
||||||
adjust()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fun adjust() {
|
|
||||||
if (SystemInfo.isWindows || SystemInfo.isLinux) {
|
|
||||||
val rectangle =
|
|
||||||
frame.rootPane.getClientProperty(FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS)
|
|
||||||
as? Rectangle ?: return
|
|
||||||
val right = rectangle.width
|
|
||||||
val toolbar = this@MyToolBar
|
|
||||||
for (i in 0 until toolbar.componentCount) {
|
|
||||||
val c = toolbar.getComponent(i)
|
|
||||||
if (c.name == "spacing") {
|
|
||||||
if (c.width == right) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
toolbar.remove(i)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (right > 0) {
|
|
||||||
val spacing = Box.createHorizontalStrut(right)
|
|
||||||
spacing.name = "spacing"
|
|
||||||
toolbar.add(spacing)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun rebuild() {
|
|
||||||
val toolbar: JToolBar = this
|
|
||||||
val actionManager = ActionManager.getInstance()
|
|
||||||
val actionContainerFactory = ActionContainerFactory(actionManager)
|
|
||||||
|
|
||||||
toolbar.removeAll()
|
|
||||||
|
|
||||||
toolbar.add(actionContainerFactory.createButton(object : AnAction(StringUtils.EMPTY, Icons.add) {
|
|
||||||
override fun actionPerformed(evt: AnActionEvent) {
|
|
||||||
actionManager.getAction(FindEverywhereAction.FIND_EVERYWHERE)?.actionPerformed(evt)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isEnabled(): Boolean {
|
|
||||||
return actionManager.getAction(FindEverywhereAction.FIND_EVERYWHERE)?.isEnabled ?: false
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
toolbar.add(Box.createHorizontalGlue())
|
|
||||||
|
|
||||||
if (SystemInfo.isLinux || SystemInfo.isWindows) {
|
|
||||||
toolbar.add(Box.createHorizontalStrut(16))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// update btn
|
|
||||||
val updateBtn = actionContainerFactory.createButton(actionManager.getAction(Actions.APP_UPDATE))
|
|
||||||
updateBtn.isVisible = updateBtn.isEnabled
|
|
||||||
updateBtn.addChangeListener { updateBtn.isVisible = updateBtn.isEnabled }
|
|
||||||
toolbar.add(updateBtn)
|
|
||||||
|
|
||||||
|
|
||||||
// 获取显示的Action,如果不是 false 那么就是显示出来
|
|
||||||
for (action in getActions()) {
|
|
||||||
if (action.visible) {
|
|
||||||
val ac = actionManager.getAction(action.id)
|
|
||||||
if (ac == null) {
|
|
||||||
if (action.id == MultipleAction.MULTIPLE) {
|
|
||||||
toolbar.add(actionContainerFactory.createButton(MultipleAction.getInstance(windowScope)))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
toolbar.add(actionContainerFactory.createButton(ac))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (toolbar is MyToolBar) {
|
|
||||||
toolbar.adjust()
|
|
||||||
}
|
|
||||||
|
|
||||||
toolbar.revalidate()
|
|
||||||
toolbar.repaint()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
103
src/main/kotlin/app/termora/TermoraToolbarModel.kt
Normal file
103
src/main/kotlin/app/termora/TermoraToolbarModel.kt
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package app.termora
|
||||||
|
|
||||||
|
import app.termora.Application.ohMyJson
|
||||||
|
import app.termora.actions.ActionManager
|
||||||
|
import app.termora.actions.MultipleAction
|
||||||
|
import app.termora.actions.SettingsAction
|
||||||
|
import app.termora.database.DatabaseManager
|
||||||
|
import app.termora.findeverywhere.FindEverywhereAction
|
||||||
|
import app.termora.snippet.SnippetAction
|
||||||
|
import org.apache.commons.lang3.StringUtils
|
||||||
|
import java.util.*
|
||||||
|
import javax.swing.event.EventListenerList
|
||||||
|
|
||||||
|
internal class TermoraToolbarModel private constructor() {
|
||||||
|
companion object {
|
||||||
|
fun getInstance(): TermoraToolbarModel {
|
||||||
|
return ApplicationScope.forApplicationScope()
|
||||||
|
.getOrCreate(TermoraToolbarModel::class) { TermoraToolbarModel() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val properties get() = DatabaseManager.getInstance().properties
|
||||||
|
private val eventListener = EventListenerList()
|
||||||
|
|
||||||
|
|
||||||
|
fun getActionManager() = ActionManager.getInstance()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取到所有的 Action
|
||||||
|
*/
|
||||||
|
fun getAllActions(): List<ToolBarAction> {
|
||||||
|
return listOf(
|
||||||
|
ToolBarAction(SnippetAction.SNIPPET, true),
|
||||||
|
ToolBarAction(Actions.SFTP, true),
|
||||||
|
ToolBarAction(Actions.TERMINAL_LOGGER, true),
|
||||||
|
ToolBarAction(Actions.MACRO, true),
|
||||||
|
ToolBarAction(Actions.KEYWORD_HIGHLIGHT, true),
|
||||||
|
ToolBarAction(Actions.KEY_MANAGER, true),
|
||||||
|
ToolBarAction(MultipleAction.MULTIPLE, true),
|
||||||
|
ToolBarAction(FindEverywhereAction.FIND_EVERYWHERE, true),
|
||||||
|
ToolBarAction(SettingsAction.SETTING, true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取到所有 Action,会根据用户个性化排序/显示
|
||||||
|
*/
|
||||||
|
fun getActions(): List<ToolBarAction> {
|
||||||
|
val text = properties.getString(
|
||||||
|
"Termora.ToolBar.Actions",
|
||||||
|
StringUtils.EMPTY
|
||||||
|
)
|
||||||
|
|
||||||
|
val actions = getAllActions()
|
||||||
|
|
||||||
|
if (text.isBlank()) {
|
||||||
|
return actions
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存储的 action
|
||||||
|
val storageActions = (ohMyJson.runCatching {
|
||||||
|
ohMyJson.decodeFromString<List<ToolBarAction>>(text)
|
||||||
|
}.getOrNull() ?: return actions).toMutableList()
|
||||||
|
|
||||||
|
for (action in actions) {
|
||||||
|
// 如果存储的 action 不包含这个,那么这个可能是新增的,新增的默认显示出来
|
||||||
|
if (storageActions.none { it.id == action.id }) {
|
||||||
|
storageActions.addFirst(ToolBarAction(action.id, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果存储的 Action 在所有 Action 里没有,那么移除
|
||||||
|
storageActions.removeIf { e -> actions.none { e.id == it.id } }
|
||||||
|
|
||||||
|
return storageActions
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setActions(actions: List<ToolBarAction>) {
|
||||||
|
assertEventDispatchThread()
|
||||||
|
properties.putString("Termora.ToolBar.Actions", ohMyJson.encodeToString(actions))
|
||||||
|
for (listener in eventListener.getListeners(TermoraToolbarModelListener::class.java)) {
|
||||||
|
listener.onChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun addTermoraToolbarModelListener(listener: TermoraToolbarModelListener): Disposable {
|
||||||
|
eventListener.add(TermoraToolbarModelListener::class.java, listener)
|
||||||
|
return object : Disposable {
|
||||||
|
override fun dispose() {
|
||||||
|
removeTermoraToolbarModelListener(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeTermoraToolbarModelListener(listener: TermoraToolbarModelListener) {
|
||||||
|
eventListener.remove(TermoraToolbarModelListener::class.java, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TermoraToolbarModelListener : EventListener {
|
||||||
|
fun onChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/main/kotlin/app/termora/ToolBarAction.kt
Normal file
9
src/main/kotlin/app/termora/ToolBarAction.kt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package app.termora
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ToolBarAction(
|
||||||
|
val id: String,
|
||||||
|
val visible: Boolean,
|
||||||
|
)
|
||||||
@@ -34,6 +34,7 @@ class ActionManager : org.jdesktop.swingx.action.ActionManager() {
|
|||||||
addAction(Actions.TERMINAL_LOGGER, TerminalLoggerAction())
|
addAction(Actions.TERMINAL_LOGGER, TerminalLoggerAction())
|
||||||
addAction(Actions.SFTP, TransferAnAction())
|
addAction(Actions.SFTP, TransferAnAction())
|
||||||
addAction(SFTPCommandAction.SFTP_COMMAND, SFTPCommandAction())
|
addAction(SFTPCommandAction.SFTP_COMMAND, SFTPCommandAction())
|
||||||
|
addAction(MultipleAction.MULTIPLE, MultipleAction.getInstance())
|
||||||
addAction(SnippetAction.SNIPPET, SnippetAction.getInstance())
|
addAction(SnippetAction.SNIPPET, SnippetAction.getInstance())
|
||||||
addAction(Actions.MACRO, MacroAction())
|
addAction(Actions.MACRO, MacroAction())
|
||||||
addAction(Actions.KEY_MANAGER, KeyManagerAction())
|
addAction(Actions.KEY_MANAGER, KeyManagerAction())
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
package app.termora.actions
|
package app.termora.actions
|
||||||
|
|
||||||
import app.termora.I18n
|
import app.termora.*
|
||||||
import app.termora.Icons
|
|
||||||
import app.termora.TerminalPanelFactory
|
|
||||||
import app.termora.WindowScope
|
|
||||||
|
|
||||||
class MultipleAction private constructor() : AnAction(
|
class MultipleAction private constructor() : AnAction(
|
||||||
I18n.getString("termora.tools.multiple"),
|
I18n.getString("termora.tools.multiple"),
|
||||||
Icons.vcs
|
Icons.vcs
|
||||||
) {
|
), StateAction {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@@ -17,8 +14,8 @@ class MultipleAction private constructor() : AnAction(
|
|||||||
*/
|
*/
|
||||||
const val MULTIPLE = "MultipleAction"
|
const val MULTIPLE = "MultipleAction"
|
||||||
|
|
||||||
fun getInstance(windowScope: WindowScope): MultipleAction {
|
fun getInstance(): MultipleAction {
|
||||||
return windowScope.getOrCreate(MultipleAction::class) { MultipleAction() }
|
return ApplicationScope.forApplicationScope().getOrCreate(MultipleAction::class) { MultipleAction() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +24,24 @@ class MultipleAction private constructor() : AnAction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun actionPerformed(evt: AnActionEvent) {
|
override fun actionPerformed(evt: AnActionEvent) {
|
||||||
|
super.setSelected(false)
|
||||||
|
val windowScope = evt.getData(DataProviders.WindowScope) ?: return
|
||||||
|
setSelected(windowScope, isSelected(windowScope).not())
|
||||||
TerminalPanelFactory.getInstance().repaintAll()
|
TerminalPanelFactory.getInstance().repaintAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isSelected(): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSelected(windowScope: WindowScope): Boolean {
|
||||||
|
return windowScope.getBoolean("MultipleAction.isSelected", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSelected(windowScope: WindowScope, selected: Boolean) {
|
||||||
|
windowScope.putBoolean("MultipleAction.isSelected", selected)
|
||||||
|
putValue("MultipleAction.isSelected", selected)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
8
src/main/kotlin/app/termora/actions/StateAction.kt
Normal file
8
src/main/kotlin/app/termora/actions/StateAction.kt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package app.termora.actions
|
||||||
|
|
||||||
|
import app.termora.WindowScope
|
||||||
|
|
||||||
|
interface StateAction {
|
||||||
|
fun isSelected(windowScope: WindowScope): Boolean
|
||||||
|
fun setSelected(windowScope: WindowScope, selected: Boolean)
|
||||||
|
}
|
||||||
@@ -22,9 +22,7 @@ class QuickActionsFindEverywhereProvider(private val windowScope: WindowScope) :
|
|||||||
for (action in actions) {
|
for (action in actions) {
|
||||||
val ac = actionManager.getAction(action)
|
val ac = actionManager.getAction(action)
|
||||||
if (ac == null) {
|
if (ac == null) {
|
||||||
if (action == MultipleAction.MULTIPLE) {
|
continue
|
||||||
results.add(ActionFindEverywhereResult(MultipleAction.getInstance(windowScope)))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
results.add(ActionFindEverywhereResult(ac))
|
results.add(ActionFindEverywhereResult(ac))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,30 @@
|
|||||||
package app.termora.plugin.internal.badge
|
package app.termora.plugin.internal.badge
|
||||||
|
|
||||||
import app.termora.WindowScope
|
import app.termora.*
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.swing.JComponent
|
import javax.swing.JComponent
|
||||||
import javax.swing.UIManager
|
import javax.swing.UIManager
|
||||||
|
|
||||||
class Badge private constructor() {
|
class Badge private constructor(scope: Scope) {
|
||||||
companion object {
|
companion object {
|
||||||
fun getInstance(scope: WindowScope): Badge {
|
fun getInstance(scope: WindowScope): Badge {
|
||||||
return scope.getOrCreate(Badge::class) { Badge() }
|
return scope.getOrCreate(Badge::class) { Badge(scope) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getInstance(): Badge {
|
||||||
|
val scope = ApplicationScope.forApplicationScope()
|
||||||
|
return scope.getOrCreate(Badge::class) { Badge(scope) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
Disposer.register(scope, object : Disposable {
|
||||||
|
override fun dispose() {
|
||||||
|
map.clear()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private val map = WeakHashMap<JComponent, BadgePresentation>()
|
private val map = WeakHashMap<JComponent, BadgePresentation>()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user