feat: 支持自定义工具栏

This commit is contained in:
hstyi
2025-01-09 12:59:57 +08:00
committed by hstyi
parent 7d65a88d63
commit f24151f6d8
22 changed files with 675 additions and 98 deletions

View File

@@ -20,7 +20,7 @@ object Actions {
/**
* 关键词高亮
*/
const val KEYWORD_HIGHLIGHT_EVERYWHERE = "KeywordHighlightAction"
const val KEYWORD_HIGHLIGHT = "KeywordHighlightAction"
/**
* Key manager

View File

@@ -0,0 +1,351 @@
package app.termora
import app.termora.Application.ohMyJson
import app.termora.db.Database
import com.jgoodies.forms.builder.FormBuilder
import com.jgoodies.forms.layout.FormLayout
import kotlinx.serialization.encodeToString
import org.apache.commons.lang3.StringUtils
import org.jdesktop.swingx.action.ActionManager
import java.awt.Component
import java.awt.Dimension
import java.awt.Window
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import javax.swing.*
import javax.swing.event.ListDataEvent
import javax.swing.event.ListDataListener
import kotlin.math.max
import kotlin.math.min
class CustomizeToolBarDialog(
owner: Window,
private val toolbar: TermoraToolBar
) : DialogWrapper(owner) {
private val moveTopBtn = JButton(Icons.moveUp)
private val moveBottomBtn = JButton(Icons.moveDown)
private val upBtn = JButton(Icons.up)
private val downBtn = JButton(Icons.down)
private val leftBtn = JButton(Icons.left)
private val rightBtn = JButton(Icons.right)
private val allToLeftBtn = JButton(Icons.applyNotConflictsRight)
private val allToRightBtn = JButton(Icons.applyNotConflictsLeft)
private val leftList = ToolBarActionList()
private val rightList = ToolBarActionList()
private val actionManager get() = ActionManager.getInstance()
private var isOk = false
init {
size = Dimension(UIManager.getInt("Dialog.width") - 150, UIManager.getInt("Dialog.height") - 100)
isModal = true
controlsVisible = false
isResizable = false
title = I18n.getString("termora.toolbar.customize-toolbar")
setLocationRelativeTo(null)
moveTopBtn.isEnabled = false
moveBottomBtn.isEnabled = false
downBtn.isEnabled = false
upBtn.isEnabled = false
leftBtn.isEnabled = false
rightBtn.isEnabled = false
initEvents()
init()
}
override fun createCenterPanel(): JComponent {
allToLeftBtn.isEnabled = !rightList.model.isEmpty
allToRightBtn.isEnabled = !leftList.model.isEmpty
val box = JToolBar(JToolBar.VERTICAL)
box.add(Box.createVerticalStrut(leftList.fixedCellHeight))
box.add(rightBtn)
box.add(leftBtn)
box.add(Box.createVerticalGlue())
box.add(allToRightBtn)
box.add(allToLeftBtn)
box.add(Box.createVerticalStrut(leftList.fixedCellHeight))
val box2 = JToolBar(JToolBar.VERTICAL)
box2.add(Box.createVerticalStrut(leftList.fixedCellHeight))
box2.add(moveTopBtn)
box2.add(upBtn)
box2.add(Box.createVerticalGlue())
box2.add(downBtn)
box2.add(moveBottomBtn)
box2.add(Box.createVerticalStrut(leftList.fixedCellHeight))
return FormBuilder.create().debug(false)
.border(BorderFactory.createMatteBorder(1, 0, 0, 0, DynamicColor.BorderColor))
.layout(FormLayout("default:grow, pref, default:grow, pref", "fill:p:grow"))
.add(JScrollPane(leftList).apply {
border = BorderFactory.createMatteBorder(0, 0, 0, 1, DynamicColor.BorderColor)
}).xy(1, 1)
.add(box).xy(2, 1)
.add(JScrollPane(rightList).apply {
border = BorderFactory.createMatteBorder(0, 1, 0, 1, DynamicColor.BorderColor)
}).xy(3, 1)
.add(box2).xy(4, 1)
.build()
}
private fun initEvents() {
rightList.addListSelectionListener { resetMoveButtons() }
leftList.addListSelectionListener {
val indices = leftList.selectedIndices
rightBtn.isEnabled = indices.isNotEmpty()
}
leftList.model.addListDataListener(object : ListDataListener {
override fun intervalAdded(e: ListDataEvent) {
contentsChanged(e)
}
override fun intervalRemoved(e: ListDataEvent) {
contentsChanged(e)
}
override fun contentsChanged(e: ListDataEvent) {
allToLeftBtn.isEnabled = !rightList.model.isEmpty
allToRightBtn.isEnabled = !leftList.model.isEmpty
resetMoveButtons()
}
})
rightList.model.addListDataListener(object : ListDataListener {
override fun intervalAdded(e: ListDataEvent) {
contentsChanged(e)
}
override fun intervalRemoved(e: ListDataEvent) {
contentsChanged(e)
}
override fun contentsChanged(e: ListDataEvent) {
allToLeftBtn.isEnabled = !rightList.model.isEmpty
allToRightBtn.isEnabled = !leftList.model.isEmpty
resetMoveButtons()
}
})
// move first
moveTopBtn.addActionListener {
val indices = rightList.selectedIndices
rightList.clearSelection()
for (index in indices.indices) {
val ele = rightList.model.getElementAt(indices[index])
rightList.model.removeElementAt(indices[index])
rightList.model.add(index, ele)
rightList.selectionModel.addSelectionInterval(index, max(index - 1, 0))
}
}
// move up
upBtn.addActionListener {
val indices = rightList.selectedIndices
rightList.clearSelection()
for (index in indices) {
val ele = rightList.model.getElementAt(index)
rightList.model.removeElementAt(index)
rightList.model.add(index - 1, ele)
rightList.selectionModel.addSelectionInterval(max(index - 1, 0), max(index - 1, 0))
}
}
// move down
downBtn.addActionListener {
val indices = rightList.selectedIndices
rightList.clearSelection()
for (index in indices) {
val ele = rightList.model.getElementAt(index)
rightList.model.removeElementAt(index)
rightList.model.add(index + 1, ele)
rightList.selectionModel.addSelectionInterval(index + 1, index + 1)
}
}
// move last
moveBottomBtn.addActionListener {
val indices = rightList.selectedIndices
val size = rightList.model.size
rightList.clearSelection()
for (index in indices.indices) {
val ele = rightList.model.getElementAt(indices[index])
rightList.model.removeElementAt(indices[index])
rightList.model.add(size - index - 1, ele)
rightList.selectionModel.addSelectionInterval(size - index - 1, size - index - 1)
}
}
allToLeftBtn.addActionListener {
while (!rightList.model.isEmpty) {
val ele = rightList.model.getElementAt(0)
rightList.model.removeElementAt(0)
leftList.model.addElement(ele)
}
}
allToRightBtn.addActionListener {
while (!leftList.model.isEmpty) {
val ele = leftList.model.getElementAt(0)
leftList.model.removeElementAt(0)
rightList.model.addElement(ele)
}
}
leftBtn.addActionListener {
val indices = rightList.selectedIndices
for (index in indices) {
val ele = rightList.model.getElementAt(index)
rightList.model.removeElementAt(index)
leftList.model.addElement(ele)
}
rightList.clearSelection()
val index = min(indices.max(), rightList.model.size - 1)
if (!rightList.model.isEmpty) {
rightList.addSelectionInterval(index, index)
}
}
rightBtn.addActionListener {
val indices = leftList.selectedIndices
val rightSelectedIndex = if (rightList.selectedIndices.isEmpty()) rightList.model.size else
rightList.selectionModel.maxSelectionIndex + 1
if (indices.isNotEmpty()) {
for (index in indices.indices) {
val ele = leftList.model.getElementAt(indices[index])
leftList.model.removeElementAt(indices[index])
rightList.model.add(rightSelectedIndex + index, ele)
}
leftList.clearSelection()
val index = min(indices.max(), leftList.model.size - 1)
if (!leftList.model.isEmpty) {
leftList.addSelectionInterval(index, index)
}
rightList.clearSelection()
rightList.addSelectionInterval(rightSelectedIndex, rightSelectedIndex)
}
}
addWindowListener(object : WindowAdapter() {
override fun windowOpened(e: WindowEvent) {
removeWindowListener(this)
val allActions = toolbar.getAllActions().toMutableList()
val shownActions = toolbar.getShownActions()
allActions.removeAll(shownActions)
for (action in allActions) {
actionManager.getAction(action)?.let { leftList.model.addElement(ActionHolder(action, it)) }
}
for (action in shownActions) {
actionManager.getAction(action)?.let { rightList.model.addElement(ActionHolder(action, it)) }
}
}
})
}
private fun resetMoveButtons() {
val indices = rightList.selectedIndices
if (indices.isEmpty()) {
moveTopBtn.isEnabled = false
moveBottomBtn.isEnabled = false
downBtn.isEnabled = false
upBtn.isEnabled = false
} else {
moveTopBtn.isEnabled = !indices.contains(0)
upBtn.isEnabled = moveTopBtn.isEnabled
moveBottomBtn.isEnabled = !indices.contains(rightList.model.size - 1)
downBtn.isEnabled = moveBottomBtn.isEnabled
}
leftBtn.isEnabled = indices.isNotEmpty()
}
private class ToolBarActionList : JList<ActionHolder>() {
private val model = DefaultListModel<ActionHolder>()
init {
initView()
initEvents()
setModel(model)
}
private fun initView() {
border = BorderFactory.createEmptyBorder(4, 4, 4, 4)
background = UIManager.getColor("window")
fixedCellHeight = UIManager.getInt("Tree.rowHeight")
cellRenderer = object : DefaultListCellRenderer() {
override fun getListCellRendererComponent(
list: JList<*>?,
value: Any?,
index: Int,
isSelected: Boolean,
cellHasFocus: Boolean
): Component {
var text = value?.toString() ?: StringUtils.EMPTY
if (value is ActionHolder) {
val action = value.action
text = action.getValue(Action.NAME)?.toString() ?: text
}
val c = super.getListCellRendererComponent(list, text, index, isSelected, cellHasFocus)
if (value is ActionHolder) {
val action = value.action
val icon = action.getValue(Action.SMALL_ICON) as Icon?
if (icon != null) {
this.icon = icon
if (icon is DynamicIcon) {
if (isSelected && cellHasFocus) {
this.icon = icon.dark
}
}
}
}
return c
}
}
}
private fun initEvents() {
}
override fun getModel(): DefaultListModel<ActionHolder> {
return model
}
}
override fun doOKAction() {
isOk = true
val actions = mutableListOf<String>()
for (i in 0 until rightList.model.size()) {
actions.add(rightList.model.getElementAt(i).id)
}
Database.instance.properties.putString("Termora.ToolBar.Actions", ohMyJson.encodeToString(actions))
super.doOKAction()
}
fun open(): Boolean {
isModal = true
isVisible = true
return isOk
}
private class ActionHolder(val id: String, val action: Action)
}

View File

@@ -3,7 +3,9 @@ package app.termora
object Icons {
val bulletList by lazy { DynamicIcon("icons/bulletList.svg", "icons/bulletList_dark.svg") }
val up by lazy { DynamicIcon("icons/up.svg", "icons/up_dark.svg") }
val moveUp by lazy { DynamicIcon("icons/moveUp.svg", "icons/moveUp_dark.svg") }
val down by lazy { DynamicIcon("icons/down.svg", "icons/down_dark.svg") }
val moveDown by lazy { DynamicIcon("icons/moveDown.svg", "icons/moveDown_dark.svg") }
val close by lazy { DynamicIcon("icons/close.svg", "icons/close_dark.svg") }
val searchHistory by lazy { DynamicIcon("icons/searchHistory.svg", "icons/searchHistory_dark.svg") }
val matchCase by lazy { DynamicIcon("icons/matchCase.svg", "icons/matchCase_dark.svg") }
@@ -74,9 +76,23 @@ object Icons {
val colorPicker by lazy { DynamicIcon("icons/colorPicker.svg", "icons/colorPicker_dark.svg") }
val folder by lazy { DynamicIcon("icons/folder.svg", "icons/folder_dark.svg") }
val listFiles by lazy { DynamicIcon("icons/listFiles.svg", "icons/listFiles_dark.svg") }
val left by lazy { DynamicIcon("icons/left.svg", "icons/left_dark.svg") }
val right by lazy { DynamicIcon("icons/right.svg", "icons/right_dark.svg") }
val dotListFiles by lazy { DynamicIcon("icons/dotListFiles.svg", "icons/dotListFiles_dark.svg") }
val fileTransfer by lazy { DynamicIcon("icons/fileTransfer.svg", "icons/fileTransfer_dark.svg") }
val help by lazy { DynamicIcon("icons/help.svg", "icons/help_dark.svg") }
val applyNotConflictsLeft by lazy {
DynamicIcon(
"icons/applyNotConflictsLeft.svg",
"icons/applyNotConflictsLeft_dark.svg"
)
}
val applyNotConflictsRight by lazy {
DynamicIcon(
"icons/applyNotConflictsRight.svg",
"icons/applyNotConflictsRight_dark.svg"
)
}
val expand by lazy { DynamicIcon("icons/expand.svg", "icons/expand_dark.svg") }
val collapse by lazy { DynamicIcon("icons/collapse.svg", "icons/collapse_dark.svg") }
val expandAll by lazy { DynamicIcon("icons/expandAll.svg", "icons/expandAll_dark.svg") }

View File

@@ -7,27 +7,21 @@ import app.termora.findeverywhere.FindEverywhereResult
import com.formdev.flatlaf.FlatLaf
import com.formdev.flatlaf.extras.components.FlatPopupMenu
import com.formdev.flatlaf.extras.components.FlatTabbedPane
import org.apache.commons.lang3.StringUtils
import org.jdesktop.swingx.action.ActionContainerFactory
import org.jdesktop.swingx.action.ActionManager
import java.awt.BorderLayout
import java.awt.Component
import java.awt.Dimension
import java.awt.event.ActionEvent
import java.awt.event.KeyEvent
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.beans.PropertyChangeEvent
import java.awt.*
import java.awt.event.*
import java.beans.PropertyChangeListener
import javax.swing.*
import javax.swing.JTabbedPane.SCROLL_TAB_LAYOUT
import kotlin.math.min
class TerminalTabbed(
private val toolbar: JToolBar,
private val termoraToolBar: TermoraToolBar,
private val tabbedPane: FlatTabbedPane,
) : JPanel(BorderLayout()), Disposable, TerminalTabbedManager {
private val tabs = mutableListOf<TerminalTab>()
private val customizeToolBarAWTEventListener = CustomizeToolBarAWTEventListener()
private val toolbar = termoraToolBar.getJToolBar()
private val iconListener = PropertyChangeListener { e ->
val source = e.source
@@ -53,34 +47,6 @@ class TerminalTabbed(
tabbedPane.styleMap = mapOf(
"focusColor" to UIManager.getColor("TabbedPane.selectedBackground")
)
val actionManager = ActionManager.getInstance()
val actionContainerFactory = ActionContainerFactory(actionManager)
val updateBtn = actionContainerFactory.createButton(actionManager.getAction(Actions.APP_UPDATE))
updateBtn.isVisible = updateBtn.isEnabled
updateBtn.addChangeListener { updateBtn.isVisible = updateBtn.isEnabled }
toolbar.add(actionContainerFactory.createButton(object : AnAction(StringUtils.EMPTY, Icons.add) {
override fun actionPerformed(e: ActionEvent?) {
actionManager.getAction(Actions.FIND_EVERYWHERE)?.actionPerformed(e)
}
override fun isEnabled(): Boolean {
return actionManager.getAction(Actions.FIND_EVERYWHERE)?.isEnabled ?: false
}
}))
toolbar.add(Box.createHorizontalStrut(UIManager.getInt("TabbedPane.tabHeight")))
toolbar.add(Box.createHorizontalGlue())
toolbar.add(actionContainerFactory.createButton(actionManager.getAction(Actions.TERMINAL_LOGGER)))
toolbar.add(actionContainerFactory.createButton(actionManager.getAction(Actions.MACRO)))
toolbar.add(actionContainerFactory.createButton(actionManager.getAction(Actions.KEYWORD_HIGHLIGHT_EVERYWHERE)))
toolbar.add(actionContainerFactory.createButton(actionManager.getAction(Actions.KEY_MANAGER)))
toolbar.add(actionContainerFactory.createButton(actionManager.getAction(Actions.MULTIPLE)))
toolbar.add(updateBtn)
toolbar.add(actionContainerFactory.createButton(actionManager.getAction(Actions.FIND_EVERYWHERE)))
toolbar.add(actionContainerFactory.createButton(actionManager.getAction(Actions.SETTING)))
tabbedPane.trailingComponent = toolbar
add(tabbedPane, BorderLayout.CENTER)
@@ -93,8 +59,7 @@ class TerminalTabbed(
tabbedPane.setTabCloseCallback { _, i -> removeTabAt(i, true) }
// 选中变动
tabbedPane.addPropertyChangeListener("selectedIndex", object : PropertyChangeListener {
override fun propertyChange(evt: PropertyChangeEvent) {
tabbedPane.addPropertyChangeListener("selectedIndex") { evt ->
val oldIndex = evt.oldValue as Int
val newIndex = evt.newValue as Int
if (oldIndex >= 0 && tabs.size > newIndex) {
@@ -104,7 +69,6 @@ class TerminalTabbed(
tabs[newIndex].onGrabFocus()
}
}
})
// 选择变动
tabbedPane.addChangeListener {
@@ -209,6 +173,9 @@ class TerminalTabbed(
}
})
// 监听全局事件
toolkit.addAWTEventListener(customizeToolBarAWTEventListener, AWTEvent.MOUSE_EVENT_MASK)
}
private fun removeTabAt(index: Int, disposable: Boolean = true) {
@@ -274,16 +241,12 @@ class TerminalTabbed(
// 克隆
val clone = popupMenu.add(I18n.getString("termora.tabbed.contextmenu.clone"))
clone.addActionListener {
val index = tabbedPane.selectedIndex
if (index > 0) {
val tab = tabs[index]
if (tab is HostTerminalTab) {
ActionManager.getInstance()
.getAction(Actions.OPEN_HOST)
.actionPerformed(OpenHostActionEvent(this, tab.host))
}
}
}
// 在新窗口中打开
val openInNewWindow = popupMenu.add(I18n.getString("termora.tabbed.contextmenu.open-in-new-window"))
@@ -370,6 +333,64 @@ class TerminalTabbed(
Disposer.register(this, tab)
}
/**
* 对着 ToolBar 右键
*/
private inner class CustomizeToolBarAWTEventListener : AWTEventListener, Disposable {
init {
Disposer.register(this@TerminalTabbed, this)
}
override fun eventDispatched(event: AWTEvent) {
if (event !is MouseEvent || event.id != MouseEvent.MOUSE_CLICKED || !SwingUtilities.isRightMouseButton(event)) return
// 如果 ToolBar 没有显示
if (!toolbar.isShowing) return
// 如果不是作用于在 ToolBar 上面
if (!Rectangle(toolbar.locationOnScreen, toolbar.size).contains(event.locationOnScreen)) return
// 显示右键菜单
showContextMenu(event)
}
private fun showContextMenu(event: MouseEvent) {
val popupMenu = FlatPopupMenu()
popupMenu.add(I18n.getString("termora.toolbar.customize-toolbar")).addActionListener {
val dialog = CustomizeToolBarDialog(
SwingUtilities.getWindowAncestor(this@TerminalTabbed),
termoraToolBar
)
if (dialog.open()) {
termoraToolBar.rebuild()
}
}
popupMenu.show(event.component, event.x, event.y)
}
override fun dispose() {
toolkit.removeAWTEventListener(this)
}
}
/*private inner class CustomizeToolBarDialog(owner: Window) : DialogWrapper(owner) {
init {
size = Dimension(UIManager.getInt("Dialog.width"), UIManager.getInt("Dialog.height"))
isModal = true
title = I18n.getString("termora.setting")
setLocationRelativeTo(null)
init()
}
override fun createCenterPanel(): JComponent {
val model = DefaultListModel<String>()
val checkBoxList = CheckBoxList(model)
checkBoxList.fixedCellHeight = UIManager.getInt("Tree.rowHeight")
model.addElement("Test")
return checkBoxList
}
}*/
private inner class SwitchFindEverywhereResult(
private val title: String,
private val icon: Icon?,

View File

@@ -44,12 +44,12 @@ class TermoraFrame : JFrame() {
private val log = LoggerFactory.getLogger(TermoraFrame::class.java)
}
private val toolbar = JToolBar()
private val titleBar = LogicCustomTitleBar.createCustomTitleBar(this)
private val tabbedPane = MyTabbedPane()
private val toolbar = TermoraToolBar(titleBar, tabbedPane)
private lateinit var terminalTabbed: TerminalTabbed
private val disposable = Disposer.newDisposable()
private val isWindowDecorationsSupported by lazy { JBR.isWindowDecorationsSupported() }
private val titleBar = LogicCustomTitleBar.createCustomTitleBar(this)
private val updaterManager get() = UpdaterManager.instance
private val preferencesHandler = object : Runnable {
@@ -81,38 +81,6 @@ class TermoraFrame : JFrame() {
private fun initEvents() {
// 监听窗口大小变动,然后修改边距避开控制按钮
addComponentListener(object : ComponentAdapter() {
override fun componentResized(e: ComponentEvent) {
if (SystemInfo.isMacOS) {
val left = titleBar.leftInset.toInt()
if (tabbedPane.tabAreaInsets.left != left) {
tabbedPane.tabAreaInsets = Insets(0, left, 0, 0)
}
} else if (SystemInfo.isWindows || SystemInfo.isLinux) {
val right = titleBar.rightInset.toInt()
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)
}
}
}
})
forceHitTest()
// macos 需要判断是否全部删除
@@ -210,7 +178,7 @@ class TermoraFrame : JFrame() {
// Keyword Highlight
ActionManager.getInstance().addAction(Actions.KEYWORD_HIGHLIGHT_EVERYWHERE, object : AnAction(
ActionManager.getInstance().addAction(Actions.KEYWORD_HIGHLIGHT, object : AnAction(
I18n.getString("termora.highlight"),
Icons.edit
) {
@@ -444,8 +412,8 @@ class TermoraFrame : JFrame() {
tabbedPane.addMouseListener(mouseAdapter)
tabbedPane.addMouseMotionListener(mouseAdapter)
toolbar.addMouseListener(mouseAdapter)
toolbar.addMouseMotionListener(mouseAdapter)
toolbar.getJToolBar().addMouseListener(mouseAdapter)
toolbar.getJToolBar().addMouseMotionListener(mouseAdapter)
}
private fun initDesktopHandler() {

View File

@@ -0,0 +1,141 @@
package app.termora
import app.termora.Application.ohMyJson
import app.termora.db.Database
import com.formdev.flatlaf.extras.components.FlatTabbedPane
import com.formdev.flatlaf.util.SystemInfo
import com.jetbrains.WindowDecorations
import org.apache.commons.lang3.StringUtils
import org.jdesktop.swingx.action.ActionContainerFactory
import org.jdesktop.swingx.action.ActionManager
import java.awt.Insets
import java.awt.event.ActionEvent
import java.awt.event.ComponentAdapter
import java.awt.event.ComponentEvent
import javax.swing.Box
import javax.swing.JToolBar
class TermoraToolBar(
private val titleBar: WindowDecorations.CustomTitleBar,
private val tabbedPane: FlatTabbedPane
) {
private val properties by lazy { Database.instance.properties }
private val toolbar by lazy { MyToolBar().apply { rebuild(this) } }
private val shownActions = mutableListOf<String>()
fun getJToolBar(): JToolBar {
return toolbar
}
fun getShownActions(): List<String> {
return shownActions
}
fun getAllActions(): List<String> {
return listOf(
Actions.TERMINAL_LOGGER,
Actions.MACRO,
Actions.KEYWORD_HIGHLIGHT,
Actions.KEY_MANAGER,
Actions.MULTIPLE,
Actions.FIND_EVERYWHERE,
Actions.SETTING,
)
}
fun rebuild() {
rebuild(this.toolbar)
}
private fun rebuild(toolbar: JToolBar) {
val actionManager = ActionManager.getInstance()
val actionContainerFactory = ActionContainerFactory(actionManager)
shownActions.clear()
toolbar.removeAll()
toolbar.add(actionContainerFactory.createButton(object : AnAction(StringUtils.EMPTY, Icons.add) {
override fun actionPerformed(e: ActionEvent?) {
actionManager.getAction(Actions.FIND_EVERYWHERE)?.actionPerformed(e)
}
override fun isEnabled(): Boolean {
return actionManager.getAction(Actions.FIND_EVERYWHERE)?.isEnabled ?: false
}
}))
toolbar.add(Box.createHorizontalGlue())
val actions = ohMyJson.runCatching {
ohMyJson.decodeFromString<List<String>>(
properties.getString(
"Termora.ToolBar.Actions",
StringUtils.EMPTY
)
)
}.getOrNull() ?: getAllActions()
// update btn
val updateBtn = actionContainerFactory.createButton(actionManager.getAction(Actions.APP_UPDATE))
updateBtn.isVisible = updateBtn.isEnabled
updateBtn.addChangeListener { updateBtn.isVisible = updateBtn.isEnabled }
toolbar.add(updateBtn)
for (action in actions) {
actionManager.getAction(action)?.let {
toolbar.add(actionContainerFactory.createButton(it))
shownActions.add(action)
}
}
if (toolbar is MyToolBar) {
toolbar.adjust()
}
toolbar.revalidate()
toolbar.repaint()
}
private inner class MyToolBar : JToolBar() {
init {
// 监听窗口大小变动,然后修改边距避开控制按钮
addComponentListener(object : ComponentAdapter() {
override fun componentResized(e: ComponentEvent) {
adjust()
}
})
}
fun adjust() {
if (SystemInfo.isMacOS) {
val left = titleBar.leftInset.toInt()
if (tabbedPane.tabAreaInsets.left != left) {
tabbedPane.tabAreaInsets = Insets(0, left, 0, 0)
}
} else if (SystemInfo.isWindows || SystemInfo.isLinux) {
val right = titleBar.rightInset.toInt()
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)
}
}
}
}
}

View File

@@ -5,7 +5,12 @@ import app.termora.I18n
import org.jdesktop.swingx.action.ActionManager
class QuickActionsFindEverywhereProvider : FindEverywhereProvider {
private val actions = listOf(Actions.KEY_MANAGER, Actions.KEYWORD_HIGHLIGHT_EVERYWHERE, Actions.MULTIPLE)
private val actions = listOf(
Actions.KEY_MANAGER,
Actions.KEYWORD_HIGHLIGHT,
Actions.MULTIPLE,
)
override fun find(pattern: String): List<FindEverywhereResult> {
val actionManager = ActionManager.getInstance()
return actions

View File

@@ -268,7 +268,8 @@ termora.transport.jobs.table.estimated-time=Estimated time
termora.transport.jobs.contextmenu.delete=${termora.remove}
termora.transport.jobs.contextmenu.delete-all=Delete All
# ToolBar
termora.toolbar.customize-toolbar=Customize Toolbar...
# Terminal
termora.terminal.size=Size: {0} x {1}

View File

@@ -260,6 +260,8 @@ termora.transport.jobs.table.speed=速度
termora.transport.jobs.table.estimated-time=剩余时间
termora.transport.jobs.contextmenu.delete-all=删除所有
# ToolBar
termora.toolbar.customize-toolbar=自定义工具栏...
termora.terminal.size=大小: {0} x {1}
termora.terminal.copied=已复制

View File

@@ -240,6 +240,8 @@ termora.transport.jobs.table.speed=速度
termora.transport.jobs.table.estimated-time=剩餘時間
termora.transport.jobs.contextmenu.delete-all=刪除所有
# ToolBar
termora.toolbar.customize-toolbar=自訂工具列...
termora.terminal.size=大小: {0} x {1}
termora.terminal.copied=已複製

View File

@@ -0,0 +1,4 @@
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5 12.5L13 8L8.5 3.5M3.5 12.5L8 8L3.5 3.5" stroke="#6C707E" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 330 B

View File

@@ -0,0 +1,4 @@
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5 12.5L13 8L8.5 3.5M3.5 12.5L8 8L3.5 3.5" stroke="#CED0D6" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 330 B

View File

@@ -0,0 +1,4 @@
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.5 3.5L3 8L7.5 12.5M12.5 3.5L8 8L12.5 12.5" stroke="#6C707E" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 331 B

View File

@@ -0,0 +1,4 @@
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.5 3.5L3 8L7.5 12.5M12.5 3.5L8 8L12.5 12.5" stroke="#CED0D6" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 331 B

View File

@@ -0,0 +1,6 @@
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M6.3464 3.64645C6.54166 3.45118 6.85824 3.45118 7.0535 3.64645C7.24877 3.84171 7.24877 4.15829 7.0535 4.35355L3.90352 7.50354L13.5035 7.50354C13.7797 7.50354 14.0035 7.7274 14.0035 8.00354C14.0035 8.27968 13.7797 8.50354 13.5035 8.50354L3.91059 8.50354L7.05351 11.6464C7.24877 11.8417 7.24877 12.1583 7.05351 12.3536C6.85824 12.5488 6.54166 12.5488 6.3464 12.3536L2.3464 8.35355L1.99284 8L2.3464 7.64645L6.3464 3.64645Z"
fill="#6C707E"/>
</svg>

After

Width:  |  Height:  |  Size: 741 B

View File

@@ -0,0 +1,6 @@
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M6.3464 3.64645C6.54166 3.45118 6.85824 3.45118 7.0535 3.64645C7.24877 3.84171 7.24877 4.15829 7.0535 4.35355L3.90352 7.50354L13.5035 7.50354C13.7797 7.50354 14.0035 7.7274 14.0035 8.00354C14.0035 8.27968 13.7797 8.50354 13.5035 8.50354L3.91059 8.50354L7.05351 11.6464C7.24877 11.8417 7.24877 12.1583 7.05351 12.3536C6.85824 12.5488 6.54166 12.5488 6.3464 12.3536L2.3464 8.35355L1.99284 8L2.3464 7.64645L6.3464 3.64645Z"
fill="#CED0D6"/>
</svg>

After

Width:  |  Height:  |  Size: 741 B

View File

@@ -0,0 +1,8 @@
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11.8536 10.1535C12.0488 9.95828 12.0488 9.6417 11.8536 9.44644C11.6583 9.25117 11.3417 9.25117 11.1464 9.44644L8.5 12.0929L8.5 4.5C8.5 4.22386 8.27615 4 8 4C7.72386 4 7.5 4.22386 7.5 4.5L7.5 12.0929L4.85355 9.44644C4.65829 9.25118 4.34171 9.25118 4.14645 9.44644C3.95118 9.6417 3.95118 9.95828 4.14645 10.1535L7.64645 13.6535L8 14.0071L8.35355 13.6535L11.8536 10.1535Z"
fill="#6C707E"/>
<rect x="2" y="15" width="12" height="1" rx="0.5" fill="#6C707E"/>
</svg>

After

Width:  |  Height:  |  Size: 763 B

View File

@@ -0,0 +1,8 @@
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11.8536 10.1535C12.0488 9.95828 12.0488 9.6417 11.8536 9.44644C11.6583 9.25117 11.3417 9.25117 11.1464 9.44644L8.5 12.0929L8.5 4.5C8.5 4.22386 8.27615 4 8 4C7.72386 4 7.5 4.22386 7.5 4.5L7.5 12.0929L4.85355 9.44644C4.65829 9.25118 4.34171 9.25118 4.14645 9.44644C3.95118 9.6417 3.95118 9.95828 4.14645 10.1535L7.64645 13.6535L8 14.0071L8.35355 13.6535L11.8536 10.1535Z"
fill="#CED0D6"/>
<rect x="2" y="15" width="12" height="1" rx="0.5" fill="#CED0D6"/>
</svg>

After

Width:  |  Height:  |  Size: 763 B

View File

@@ -0,0 +1,7 @@
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="2" y="0" width="12" height="1" rx="0.5" fill="#6C707E"/>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11.8536 5.85354C12.0488 6.0488 12.0488 6.36538 11.8536 6.56064C11.6583 6.75591 11.3417 6.75591 11.1464 6.56064L8.5 3.9142L8.5 11.5071C8.5 11.7832 8.27615 12.0071 8 12.0071C7.72386 12.0071 7.5 11.7832 7.5 11.5071L7.5 3.91419L4.85355 6.56064C4.65829 6.7559 4.34171 6.7559 4.14645 6.56064C3.95118 6.36538 3.95118 6.0488 4.14645 5.85353L7.64645 2.35354L8 1.99998L8.35355 2.35354L11.8536 5.85354Z"
fill="#6C707E"/>
</svg>

After

Width:  |  Height:  |  Size: 783 B

View File

@@ -0,0 +1,7 @@
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="2" y="0" width="12" height="1" rx="0.5" fill="#CED0D6"/>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M11.8536 5.85354C12.0488 6.0488 12.0488 6.36538 11.8536 6.56064C11.6583 6.75591 11.3417 6.75591 11.1464 6.56064L8.5 3.9142L8.5 11.5071C8.5 11.7832 8.27615 12.0071 8 12.0071C7.72386 12.0071 7.5 11.7832 7.5 11.5071L7.5 3.91419L4.85355 6.56064C4.65829 6.7559 4.34171 6.7559 4.14645 6.56064C3.95118 6.36538 3.95118 6.0488 4.14645 5.85353L7.64645 2.35354L8 1.99998L8.35355 2.35354L11.8536 5.85354Z"
fill="#CED0D6"/>
</svg>

After

Width:  |  Height:  |  Size: 783 B

View File

@@ -0,0 +1,6 @@
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M9.65355 12.3536C9.45829 12.5488 9.14171 12.5488 8.94645 12.3536C8.75118 12.1583 8.75118 11.8417 8.94645 11.6464L12.0894 8.50354L2.49646 8.50354C2.22032 8.50354 1.99646 8.27968 1.99646 8.00354C1.99646 7.7274 2.22032 7.50354 2.49646 7.50354L12.0964 7.50354L8.94645 4.35355C8.75118 4.15829 8.75118 3.84171 8.94645 3.64645C9.14171 3.45118 9.45829 3.45118 9.65355 3.64645L13.6536 7.64645L14.0071 8L13.6536 8.35355L9.65355 12.3536Z"
fill="#6C707E"/>
</svg>

After

Width:  |  Height:  |  Size: 748 B

View File

@@ -0,0 +1,6 @@
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M9.65355 12.3536C9.45829 12.5488 9.14171 12.5488 8.94645 12.3536C8.75118 12.1583 8.75118 11.8417 8.94645 11.6464L12.0894 8.50354L2.49646 8.50354C2.22032 8.50354 1.99646 8.27968 1.99646 8.00354C1.99646 7.7274 2.22032 7.50354 2.49646 7.50354L12.0964 7.50354L8.94645 4.35355C8.75118 4.15829 8.75118 3.84171 8.94645 3.64645C9.14171 3.45118 9.45829 3.45118 9.65355 3.64645L13.6536 7.64645L14.0071 8L13.6536 8.35355L9.65355 12.3536Z"
fill="#CED0D6"/>
</svg>

After

Width:  |  Height:  |  Size: 748 B