feat: without jbr

This commit is contained in:
hstyi
2025-03-15 13:15:55 +08:00
committed by hstyi
parent 9884ed19fa
commit 4bb1a411e8
16 changed files with 405 additions and 405 deletions

View File

@@ -118,7 +118,6 @@ dependencies {
application { application {
val args = mutableListOf( val args = mutableListOf(
"--add-exports java.base/sun.nio.ch=ALL-UNNAMED",
"-Xmx2g", "-Xmx2g",
"-XX:+UseZGC", "-XX:+UseZGC",
"-XX:+ZUncommit", "-XX:+ZUncommit",
@@ -127,6 +126,10 @@ application {
) )
if (os.isMacOsX) { if (os.isMacOsX) {
// macOS NSWindow
args.add("--add-opens java.desktop/java.awt=ALL-UNNAMED")
args.add("--add-opens java.desktop/sun.lwawt=ALL-UNNAMED")
args.add("--add-opens java.desktop/sun.lwawt.macosx=ALL-UNNAMED")
args.add("-Dsun.java2d.metal=true") args.add("-Dsun.java2d.metal=true")
args.add("-Dapple.awt.application.appearance=system") args.add("-Dapple.awt.application.appearance=system")
} }
@@ -344,6 +347,10 @@ tasks.register<Exec>("jpackage") {
options.add("-Dsun.java2d.metal=true") options.add("-Dsun.java2d.metal=true")
if (os.isMacOsX) { if (os.isMacOsX) {
// NSWindow
options.add("--add-opens java.desktop/java.awt=ALL-UNNAMED")
options.add("--add-opens java.desktop/sun.lwawt=ALL-UNNAMED")
options.add("--add-opens java.desktop/sun.lwawt.macosx=ALL-UNNAMED")
options.add("-Dapple.awt.application.appearance=system") options.add("-Dapple.awt.application.appearance=system")
options.add("--add-opens java.desktop/sun.lwawt.macosx.concurrent=ALL-UNNAMED") options.add("--add-opens java.desktop/sun.lwawt.macosx.concurrent=ALL-UNNAMED")
} }
@@ -420,15 +427,9 @@ tasks.register<Exec>("jpackage") {
tasks.register("dist") { tasks.register("dist") {
doLast { doLast {
val vendor = Jvm.current().vendor ?: StringUtils.EMPTY
@Suppress("UnstableApiUsage")
if (!JvmVendorSpec.JETBRAINS.matches(vendor)) {
throw GradleException("JVM: $vendor is not supported")
}
val gradlew = File(projectDir, if (os.isWindows) "gradlew.bat" else "gradlew").absolutePath val gradlew = File(projectDir, if (os.isWindows) "gradlew.bat" else "gradlew").absolutePath
// 清空目录 // 清空目录
exec { commandLine(gradlew, "clean") } exec { commandLine(gradlew, "clean") }
@@ -735,8 +736,6 @@ fun stapleMacOSLocalFile(file: File) {
kotlin { kotlin {
jvmToolchain { jvmToolchain {
languageVersion = JavaLanguageVersion.of(21) languageVersion = JavaLanguageVersion.of(21)
@Suppress("UnstableApiUsage")
vendor = JvmVendorSpec.JETBRAINS
} }
} }

View File

@@ -144,7 +144,7 @@ class ApplicationRunner {
private fun setupLaf() { private fun setupLaf() {
System.setProperty(FlatSystemProperties.USE_WINDOW_DECORATIONS, "${SystemInfo.isLinux}") System.setProperty(FlatSystemProperties.USE_WINDOW_DECORATIONS, "${SystemInfo.isLinux || SystemInfo.isWindows}")
System.setProperty(FlatSystemProperties.USE_ROUNDED_POPUP_BORDER, "false") System.setProperty(FlatSystemProperties.USE_ROUNDED_POPUP_BORDER, "false")
if (SystemInfo.isLinux) { if (SystemInfo.isLinux) {
@@ -152,15 +152,6 @@ class ApplicationRunner {
JDialog.setDefaultLookAndFeelDecorated(true) JDialog.setDefaultLookAndFeelDecorated(true)
} }
UIManager.put(
"FileChooser.${if (SystemInfo.isWindows) "win32" else "other"}.newFolder",
I18n.getString("termora.welcome.contextmenu.new.folder.name")
)
UIManager.put(
"FileChooser.${if (SystemInfo.isWindows) "win32" else "other"}.newFolder.subsequent",
"${I18n.getString("termora.welcome.contextmenu.new.folder.name")}.{0}"
)
val themeManager = ThemeManager.getInstance() val themeManager = ThemeManager.getInstance()
val appearance = Database.getDatabase().appearance val appearance = Database.getDatabase().appearance
var theme = appearance.theme var theme = appearance.theme
@@ -176,8 +167,8 @@ class ApplicationRunner {
themeManager.change(theme, true) themeManager.change(theme, true)
if (Application.isUnknownVersion()) // if (Application.isUnknownVersion())
FlatInspector.install("ctrl shift alt X") FlatInspector.install("ctrl X")
UIManager.put(FlatClientProperties.FULL_WINDOW_CONTENT, true) UIManager.put(FlatClientProperties.FULL_WINDOW_CONTENT, true)
UIManager.put(FlatClientProperties.USE_WINDOW_DECORATIONS, false) UIManager.put(FlatClientProperties.USE_WINDOW_DECORATIONS, false)

View File

@@ -2,8 +2,8 @@ package app.termora
import app.termora.actions.AnAction import app.termora.actions.AnAction
import app.termora.actions.AnActionEvent import app.termora.actions.AnActionEvent
import app.termora.native.osx.NativeMacLibrary
import com.formdev.flatlaf.FlatClientProperties import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.FlatLaf
import com.formdev.flatlaf.util.SystemInfo import com.formdev.flatlaf.util.SystemInfo
import com.jetbrains.JBR import com.jetbrains.JBR
import java.awt.* import java.awt.*
@@ -12,29 +12,60 @@ import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent import java.awt.event.WindowEvent
import javax.swing.* import javax.swing.*
abstract class DialogWrapper(owner: Window?) : JDialog(owner) { abstract class DialogWrapper(owner: Window?) : JDialog(owner) {
private val rootPanel = JPanel(BorderLayout())
private val titleLabel = JLabel() private val titleLabel = JLabel()
private val titleBar by lazy { LogicCustomTitleBar.createCustomTitleBar(this) }
val disposable = Disposer.newDisposable() val disposable = Disposer.newDisposable()
private val customTitleBar = if (SystemInfo.isMacOS && JBR.isWindowDecorationsSupported())
JBR.getWindowDecorations().createCustomTitleBar() else null
companion object { companion object {
const val DEFAULT_ACTION = "DEFAULT_ACTION" const val DEFAULT_ACTION = "DEFAULT_ACTION"
private const val PROCESS_GLOBAL_KEYMAP = "PROCESS_GLOBAL_KEYMAP" private const val PROCESS_GLOBAL_KEYMAP = "PROCESS_GLOBAL_KEYMAP"
} }
protected var controlsVisible = true protected var controlsVisible = true
set(value) { set(value) {
field = value field = value
titleBar.putProperty("controls.visible", value) if (SystemInfo.isMacOS) {
if (customTitleBar != null) {
customTitleBar.putProperty("controls.visible", value)
} else {
NativeMacLibrary.setControlsVisible(this, value)
}
} else {
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_ICONIFFY, value)
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_MAXIMIZE, value)
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_CLOSE, value)
}
} }
protected var titleBarHeight = UIManager.getInt("TabbedPane.tabHeight").toFloat() protected var fullWindowContent = false
set(value) { set(value) {
titleBar.height = value
field = value field = value
rootPane.putClientProperty(FlatClientProperties.FULL_WINDOW_CONTENT, value)
}
protected var titleVisible = true
set(value) {
field = value
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_TITLE, value)
}
protected var titleIconVisible = false
set(value) {
field = value
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_ICON, value)
}
protected var titleBarHeight = UIManager.getInt("TabbedPane.tabHeight")
set(value) {
field = value
if (SystemInfo.isMacOS) {
customTitleBar?.height = height.toFloat()
} else {
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_HEIGHT, value)
}
} }
protected var lostFocusDispose = false protected var lostFocusDispose = false
@@ -51,24 +82,42 @@ abstract class DialogWrapper(owner: Window?) : JDialog(owner) {
super.rootPane.putClientProperty(PROCESS_GLOBAL_KEYMAP, value) super.rootPane.putClientProperty(PROCESS_GLOBAL_KEYMAP, value)
} }
init {
super.setDefaultCloseOperation(DISPOSE_ON_CLOSE)
// 使用 FlatLaf 的 TitlePane
if (SystemInfo.isWindows || SystemInfo.isLinux) {
rootPane.windowDecorationStyle = JRootPane.PLAIN_DIALOG
}
}
protected fun init() { protected fun init() {
defaultCloseOperation = DISPOSE_ON_CLOSE
initTitleBar()
initEvents() initEvents()
if (JBR.isWindowDecorationsSupported()) { val rootPanel = JPanel(BorderLayout())
if (rootPane.getClientProperty(FlatClientProperties.TITLE_BAR_SHOW_TITLE) != false) { rootPanel.add(createCenterPanel(), BorderLayout.CENTER)
if (SystemInfo.isMacOS) {
rootPane.putClientProperty("apple.awt.windowTitleVisible", false)
rootPane.putClientProperty("apple.awt.fullWindowContent", true)
rootPane.putClientProperty("apple.awt.transparentTitleBar", true)
rootPane.putClientProperty(
FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING,
FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_MEDIUM
)
val titlePanel = createTitlePanel() val titlePanel = createTitlePanel()
if (titlePanel != null) { if (titlePanel != null) {
rootPanel.add(titlePanel, BorderLayout.NORTH) rootPanel.add(titlePanel, BorderLayout.NORTH)
} }
}
}
rootPanel.add(createCenterPanel(), BorderLayout.CENTER) val customTitleBar = this.customTitleBar
if (customTitleBar != null) {
customTitleBar.putProperty("controls.visible", controlsVisible)
customTitleBar.height = titleBarHeight.toFloat()
JBR.getWindowDecorations().setCustomTitleBar(this, customTitleBar)
}
}
val southPanel = createSouthPanel() val southPanel = createSouthPanel()
if (southPanel != null) { if (southPanel != null) {
@@ -122,7 +171,7 @@ abstract class DialogWrapper(owner: Window?) : JDialog(owner) {
val panel = JPanel(BorderLayout()) val panel = JPanel(BorderLayout())
panel.add(titleLabel, BorderLayout.CENTER) panel.add(titleLabel, BorderLayout.CENTER)
panel.preferredSize = Dimension(-1, titleBar.height.toInt()) panel.preferredSize = Dimension(-1, titleBarHeight)
return panel return panel
@@ -191,30 +240,20 @@ abstract class DialogWrapper(owner: Window?) : JDialog(owner) {
} }
}) })
if (SystemInfo.isWindows) {
addWindowListener(object : WindowAdapter(), ThemeChangeListener {
override fun windowClosed(e: WindowEvent) {
ThemeManager.getInstance().removeThemeChangeListener(this)
} }
override fun windowOpened(e: WindowEvent) { override fun addNotify() {
onChanged() super.addNotify()
ThemeManager.getInstance().addThemeChangeListener(this)
// 显示后触发一次重绘制
if (SystemInfo.isWindows || SystemInfo.isLinux) {
this.controlsVisible = controlsVisible
this.titleBarHeight = titleBarHeight
this.titleIconVisible = titleIconVisible
this.titleVisible = titleVisible
this.fullWindowContent = fullWindowContent
} }
override fun onChanged() {
titleBar.putProperty("controls.dark", FlatLaf.isLafDark())
}
})
}
}
private fun initTitleBar() {
titleBar.height = titleBarHeight
titleBar.putProperty("controls.visible", controlsVisible)
if (JBR.isWindowDecorationsSupported()) {
JBR.getWindowDecorations().setCustomTitleBar(this, titleBar)
}
} }
protected open fun doOKAction() { protected open fun doOKAction() {

View File

@@ -1,74 +0,0 @@
package app.termora
import com.formdev.flatlaf.extras.components.FlatTextField
import org.apache.commons.lang3.StringUtils
import java.awt.Window
import java.awt.event.KeyAdapter
import java.awt.event.KeyEvent
import javax.swing.BorderFactory
import javax.swing.JComponent
import javax.swing.UIManager
class InputDialog(
owner: Window,
title: String,
text: String = StringUtils.EMPTY,
placeholderText: String = StringUtils.EMPTY
) : DialogWrapper(owner) {
private val textField = FlatTextField()
private var text: String? = null
init {
setSize(340, 60)
setLocationRelativeTo(owner)
super.setTitle(title)
isResizable = false
isModal = true
controlsVisible = false
titleBarHeight = UIManager.getInt("TabbedPane.tabHeight") * 0.8f
textField.placeholderText = placeholderText
textField.text = text
textField.addKeyListener(object : KeyAdapter() {
override fun keyPressed(e: KeyEvent) {
if (e.keyCode == KeyEvent.VK_ENTER) {
if (textField.text.isBlank()) {
return
}
doOKAction()
}
}
})
init()
}
override fun createCenterPanel(): JComponent {
textField.background = UIManager.getColor("window")
textField.border = BorderFactory.createEmptyBorder(0, 13, 0, 13)
return textField
}
fun getText(): String? {
isVisible = true
return text
}
override fun doCancelAction() {
text = null
super.doCancelAction()
}
override fun doOKAction() {
text = textField.text
super.doOKAction()
}
override fun createSouthPanel(): JComponent? {
return null
}
}

View File

@@ -1,109 +0,0 @@
package app.termora
import com.formdev.flatlaf.FlatClientProperties
import com.jetbrains.JBR
import com.jetbrains.WindowDecorations.CustomTitleBar
import java.awt.Rectangle
import java.awt.Window
import javax.swing.RootPaneContainer
class LogicCustomTitleBar(private val titleBar: CustomTitleBar) : CustomTitleBar {
companion object {
fun createCustomTitleBar(rootPaneContainer: RootPaneContainer): CustomTitleBar {
if (!JBR.isWindowDecorationsSupported()) {
return LogicCustomTitleBar(object : CustomTitleBar {
override fun getHeight(): Float {
val bounds = rootPaneContainer.rootPane
.getClientProperty(FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS)
if (bounds is Rectangle) {
return bounds.height.toFloat()
}
return 0f
}
override fun setHeight(height: Float) {
rootPaneContainer.rootPane.putClientProperty(
FlatClientProperties.TITLE_BAR_HEIGHT,
height.toInt()
)
}
override fun getProperties(): MutableMap<String, Any> {
return mutableMapOf()
}
override fun putProperties(m: MutableMap<String, *>?) {
}
override fun putProperty(key: String?, value: Any?) {
if (key == "controls.visible" && value is Boolean) {
rootPaneContainer.rootPane.putClientProperty(
FlatClientProperties.TITLE_BAR_SHOW_CLOSE,
value
)
}
}
override fun getLeftInset(): Float {
return 0f
}
override fun getRightInset(): Float {
val bounds = rootPaneContainer.rootPane
.getClientProperty(FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS)
if (bounds is Rectangle) {
return bounds.width.toFloat()
}
return 0f
}
override fun forceHitTest(client: Boolean) {
}
override fun getContainingWindow(): Window {
return rootPaneContainer as Window
}
})
}
return JBR.getWindowDecorations().createCustomTitleBar()
}
}
override fun getHeight(): Float {
return titleBar.height
}
override fun setHeight(height: Float) {
titleBar.height = height
}
override fun getProperties(): MutableMap<String, Any> {
return titleBar.properties
}
override fun putProperties(m: MutableMap<String, *>?) {
titleBar.putProperties(m)
}
override fun putProperty(key: String?, value: Any?) {
titleBar.putProperty(key, value)
}
override fun getLeftInset(): Float {
return titleBar.leftInset
}
override fun getRightInset(): Float {
return titleBar.rightInset
}
override fun forceHitTest(client: Boolean) {
titleBar.forceHitTest(client)
}
override fun getContainingWindow(): Window {
return titleBar.containingWindow
}
}

View File

@@ -0,0 +1,11 @@
package app.termora
import com.formdev.flatlaf.ui.FlatRootPaneUI
import com.formdev.flatlaf.ui.FlatTitlePane
class MyFlatRootPaneUI : FlatRootPaneUI() {
fun getTitlePane(): FlatTitlePane? {
return super.titlePane
}
}

View File

@@ -1,11 +1,13 @@
package app.termora package app.termora
import app.termora.native.osx.NativeMacLibrary
import com.formdev.flatlaf.FlatClientProperties import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.extras.components.FlatTextPane import com.formdev.flatlaf.extras.components.FlatTextPane
import com.formdev.flatlaf.util.SystemInfo import com.formdev.flatlaf.util.SystemInfo
import com.jetbrains.JBR import com.jetbrains.JBR
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.swing.Swing
import org.apache.commons.lang3.StringUtils
import java.awt.BorderLayout import java.awt.BorderLayout
import java.awt.Component import java.awt.Component
import java.awt.Desktop import java.awt.Desktop
@@ -113,6 +115,36 @@ object OptionPane {
dialog.dispose() dialog.dispose()
} }
fun showInputDialog(
parentComponent: Component?,
title: String = UIManager.getString("OptionPane.messageDialogTitle"),
value: String = StringUtils.EMPTY,
placeholder: String = StringUtils.EMPTY,
): String? {
val pane = JOptionPane(StringUtils.EMPTY, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION)
val dialog = initDialog(pane.createDialog(parentComponent, title))
pane.wantsInput = true
pane.initialSelectionValue = value
val textField = SwingUtils.getDescendantsOfType(JTextField::class.java, pane, true).firstOrNull()
if (textField?.name == "OptionPane.textField") {
textField.border = BorderFactory.createCompoundBorder(
BorderFactory.createMatteBorder(0, 0, 1, 0, DynamicColor.BorderColor),
BorderFactory.createEmptyBorder(0, 0, 2, 0)
)
textField.background = UIManager.getColor("window")
textField.putClientProperty(FlatClientProperties.PLACEHOLDER_TEXT, placeholder)
}
dialog.isVisible = true
dialog.dispose()
val inputValue = pane.inputValue
if (inputValue == JOptionPane.UNINITIALIZED_VALUE) return null
return inputValue as? String
}
fun openFileInFolder( fun openFileInFolder(
parentComponent: Component, parentComponent: Component,
file: File, file: File,
@@ -140,14 +172,31 @@ object OptionPane {
} }
private fun initDialog(dialog: JDialog): JDialog { private fun initDialog(dialog: JDialog): JDialog {
if (SystemInfo.isWindows || SystemInfo.isLinux) {
dialog.rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_CLOSE, false)
dialog.rootPane.putClientProperty(
FlatClientProperties.TITLE_BAR_HEIGHT,
UIManager.getInt("TabbedPane.tabHeight")
)
} else if (SystemInfo.isMacOS) {
dialog.rootPane.putClientProperty("apple.awt.windowTitleVisible", false)
dialog.rootPane.putClientProperty("apple.awt.fullWindowContent", true)
dialog.rootPane.putClientProperty("apple.awt.transparentTitleBar", true)
dialog.rootPane.putClientProperty(
FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING,
FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_MEDIUM
)
val height = UIManager.getInt("TabbedPane.tabHeight") - 10
if (JBR.isWindowDecorationsSupported()) { if (JBR.isWindowDecorationsSupported()) {
val customTitleBar = JBR.getWindowDecorations().createCustomTitleBar()
val windowDecorations = JBR.getWindowDecorations() customTitleBar.putProperty("controls.visible", false)
val titleBar = windowDecorations.createCustomTitleBar() customTitleBar.height = height.toFloat()
titleBar.putProperty("controls.visible", false) JBR.getWindowDecorations().setCustomTitleBar(dialog, customTitleBar)
titleBar.height = UIManager.getInt("TabbedPane.tabHeight") - if (SystemInfo.isMacOS) 10f else 6f } else {
windowDecorations.setCustomTitleBar(dialog, titleBar) NativeMacLibrary.setControlsVisible(dialog, false)
}
val label = JLabel(dialog.title) val label = JLabel(dialog.title)
label.putClientProperty(FlatClientProperties.STYLE, "font: bold") label.putClientProperty(FlatClientProperties.STYLE, "font: bold")
@@ -155,11 +204,9 @@ object OptionPane {
box.add(Box.createHorizontalGlue()) box.add(Box.createHorizontalGlue())
box.add(label) box.add(label)
box.add(Box.createHorizontalGlue()) box.add(Box.createHorizontalGlue())
box.preferredSize = Dimension(-1, titleBar.height.toInt()) box.preferredSize = Dimension(-1, height)
dialog.contentPane.add(box, BorderLayout.NORTH) dialog.contentPane.add(box, BorderLayout.NORTH)
} }
return dialog return dialog
} }
} }

View File

@@ -190,12 +190,11 @@ class TerminalTabbed(
val rename = popupMenu.add(I18n.getString("termora.tabbed.contextmenu.rename")) val rename = popupMenu.add(I18n.getString("termora.tabbed.contextmenu.rename"))
rename.addActionListener { rename.addActionListener {
if (tabIndex > 0) { if (tabIndex > 0) {
val dialog = InputDialog( val text = OptionPane.showInputDialog(
SwingUtilities.getWindowAncestor(this), SwingUtilities.getWindowAncestor(this),
title = rename.text, title = rename.text,
text = tabbedPane.getTitleAt(tabIndex), value = tabbedPane.getTitleAt(tabIndex)
) )
val text = dialog.getText()
if (!text.isNullOrBlank()) { if (!text.isNullOrBlank()) {
tabbedPane.setTitleAt(tabIndex, text) tabbedPane.setTitleAt(tabIndex, text)
c.putClientProperty(titleProperty, text) c.putClientProperty(titleProperty, text)

View File

@@ -7,21 +7,19 @@ import app.termora.actions.DataProviders
import app.termora.sftp.SFTPTab import app.termora.sftp.SFTPTab
import app.termora.terminal.DataKey import app.termora.terminal.DataKey
import com.formdev.flatlaf.FlatClientProperties import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.FlatLaf
import com.formdev.flatlaf.util.SystemInfo import com.formdev.flatlaf.util.SystemInfo
import com.jetbrains.JBR import com.jetbrains.JBR
import java.awt.BorderLayout
import java.awt.Dimension import java.awt.Dimension
import java.awt.Insets import java.awt.Insets
import java.awt.event.MouseAdapter import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent import java.awt.event.MouseEvent
import java.awt.event.MouseListener
import java.awt.event.MouseMotionListener
import java.util.* import java.util.*
import javax.imageio.ImageIO import javax.imageio.ImageIO
import javax.swing.Box import javax.swing.*
import javax.swing.JFrame
import javax.swing.SwingUtilities
import javax.swing.SwingUtilities.isEventDispatchThread import javax.swing.SwingUtilities.isEventDispatchThread
import javax.swing.UIManager
import kotlin.math.max
fun assertEventDispatchThread() { fun assertEventDispatchThread() {
if (!isEventDispatchThread()) throw WrongThreadException("AWT EventQueue") if (!isEventDispatchThread()) throw WrongThreadException("AWT EventQueue")
@@ -33,14 +31,13 @@ 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 titleBar = LogicCustomTitleBar.createCustomTitleBar(this)
private val tabbedPane = MyTabbedPane() private val tabbedPane = MyTabbedPane()
private val toolbar = TermoraToolBar(windowScope, titleBar, tabbedPane) private val toolbar = TermoraToolBar(windowScope, this, tabbedPane)
private val terminalTabbed = TerminalTabbed(windowScope, toolbar, tabbedPane) private val terminalTabbed = TerminalTabbed(windowScope, toolbar, tabbedPane)
private val isWindowDecorationsSupported by lazy { JBR.isWindowDecorationsSupported() }
private val dataProviderSupport = DataProviderSupport() private val dataProviderSupport = DataProviderSupport()
private val welcomePanel = WelcomePanel(windowScope) private val welcomePanel = WelcomePanel(windowScope)
private val sftp get() = Database.getDatabase().sftp private val sftp get() = Database.getDatabase().sftp
private val myUI = MyFlatRootPaneUI()
init { init {
@@ -49,44 +46,146 @@ class TermoraFrame : JFrame(), DataProvider {
} }
private fun initEvents() { private fun initEvents() {
if (SystemInfo.isLinux) {
forceHitTest() val mouseAdapter = object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// macos 需要判断是否全部删除 getMouseHandler()?.mouseClicked(e)
// 当 Tab 为 0 的时候,需要加一个边距,避开控制栏
if (SystemInfo.isMacOS && isWindowDecorationsSupported) {
tabbedPane.addChangeListener {
tabbedPane.leadingComponent = if (tabbedPane.tabCount == 0) {
Box.createHorizontalStrut(titleBar.leftInset.toInt())
} else {
null
} }
override fun mousePressed(e: MouseEvent) {
getMouseHandler()?.mousePressed(e)
}
override fun mouseDragged(e: MouseEvent) {
val mouseLayer = getMouseLayer() ?: return
getMouseMotionListener()?.mouseDragged(
MouseEvent(
mouseLayer,
e.id,
e.`when`,
e.modifiersEx,
e.x,
e.y,
e.clickCount,
e.isPopupTrigger,
e.button
)
)
}
private fun getMouseHandler(): MouseListener? {
return getHandler() as? MouseListener
}
private fun getMouseMotionListener(): MouseMotionListener? {
return getHandler() as? MouseMotionListener
}
private fun getMouseLayer(): JComponent? {
val titlePane = myUI.getTitlePane() ?: return null
val handlerField = titlePane.javaClass.getDeclaredField("mouseLayer") ?: return null
handlerField.isAccessible = true
return handlerField.get(titlePane) as? JComponent
}
private fun getHandler(): Any? {
val titlePane = myUI.getTitlePane() ?: return null
val handlerField = titlePane.javaClass.getDeclaredField("handler") ?: return null
handlerField.isAccessible = true
return handlerField.get(titlePane)
}
}
toolbar.getJToolBar().addMouseListener(mouseAdapter)
toolbar.getJToolBar().addMouseMotionListener(mouseAdapter)
}
/// force hit
if (SystemInfo.isMacOS) {
if (JBR.isWindowDecorationsSupported()) {
val height = UIManager.getInt("TabbedPane.tabHeight") + tabbedPane.tabAreaInsets.top
val customTitleBar = JBR.getWindowDecorations().createCustomTitleBar()
customTitleBar.height = height.toFloat()
val mouseAdapter = object : MouseAdapter() {
private fun hit(e: MouseEvent) {
if (e.source == tabbedPane) {
val index = tabbedPane.indexAtLocation(e.x, e.y)
if (index >= 0) {
return
}
}
customTitleBar.forceHitTest(false)
}
override fun mouseClicked(e: MouseEvent) {
hit(e)
}
override fun mousePressed(e: MouseEvent) {
hit(e)
}
override fun mouseReleased(e: MouseEvent) {
hit(e)
}
override fun mouseEntered(e: MouseEvent) {
hit(e)
}
override fun mouseDragged(e: MouseEvent) {
hit(e)
}
override fun mouseMoved(e: MouseEvent) {
hit(e)
} }
} }
terminalTabbed.addMouseListener(mouseAdapter)
terminalTabbed.addMouseMotionListener(mouseAdapter)
// 监听主题变化 需要动态修改控制栏颜色 tabbedPane.addMouseListener(mouseAdapter)
if (SystemInfo.isWindows && isWindowDecorationsSupported) { tabbedPane.addMouseMotionListener(mouseAdapter)
ThemeManager.getInstance().addThemeChangeListener(object : ThemeChangeListener {
override fun onChanged() {
titleBar.putProperty("controls.dark", FlatLaf.isLafDark())
}
})
}
toolbar.getJToolBar().addMouseListener(mouseAdapter)
toolbar.getJToolBar().addMouseMotionListener(mouseAdapter)
JBR.getWindowDecorations().setCustomTitleBar(this, customTitleBar)
}
}
} }
private fun initView() { private fun initView() {
if (isWindowDecorationsSupported) {
titleBar.height = UIManager.getInt("TabbedPane.tabHeight").toFloat() // macOS 要避开左边的控制栏
titleBar.putProperty("controls.dark", FlatLaf.isLafDark()) if (SystemInfo.isMacOS) {
JBR.getWindowDecorations().setCustomTitleBar(this, titleBar) tabbedPane.tabAreaInsets = Insets(0, 76, 0, 0)
} else if (SystemInfo.isWindows) {
// Windows 10 会有1像素误差
tabbedPane.tabAreaInsets = Insets(if (SystemInfo.isWindows_11_orLater) 1 else 2, 2, 0, 0)
} else if (SystemInfo.isLinux) {
rootPane.setUI(myUI)
tabbedPane.tabAreaInsets = Insets(1, 2, 0, 0)
} }
if (SystemInfo.isLinux) { val height = UIManager.getInt("TabbedPane.tabHeight") + tabbedPane.tabAreaInsets.top
if (SystemInfo.isWindows || SystemInfo.isLinux) {
rootPane.putClientProperty(FlatClientProperties.FULL_WINDOW_CONTENT, true) rootPane.putClientProperty(FlatClientProperties.FULL_WINDOW_CONTENT, true)
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_HEIGHT, UIManager.getInt("TabbedPane.tabHeight")) rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_ICON, false)
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_TITLE, false)
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_HEIGHT, height)
} else if (SystemInfo.isMacOS) {
rootPane.putClientProperty("apple.awt.windowTitleVisible", false)
rootPane.putClientProperty("apple.awt.fullWindowContent", true)
rootPane.putClientProperty("apple.awt.transparentTitleBar", true)
rootPane.putClientProperty(
FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING,
FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_MEDIUM
)
} }
if (SystemInfo.isWindows || SystemInfo.isLinux) { if (SystemInfo.isWindows || SystemInfo.isLinux) {
@@ -102,88 +201,21 @@ class TermoraFrame : JFrame(), DataProvider {
terminalTabbed.addTerminalTab(welcomePanel) terminalTabbed.addTerminalTab(welcomePanel)
// 下一次事件循环检测是否固定 SFTP // 下一次事件循环检测是否固定 SFTP
SwingUtilities.invokeLater {
if (sftp.pinTab) { if (sftp.pinTab) {
SwingUtilities.invokeLater {
terminalTabbed.addTerminalTab(SFTPTab(), false) terminalTabbed.addTerminalTab(SFTPTab(), false)
} }
} }
// macOS 要避开左边的控制栏
if (SystemInfo.isMacOS) {
val left = max(titleBar.leftInset.toInt(), 76)
if (tabbedPane.tabCount == 0) {
tabbedPane.leadingComponent = Box.createHorizontalStrut(left)
} else {
tabbedPane.tabAreaInsets = Insets(0, left, 0, 0)
}
}
Disposer.register(windowScope, terminalTabbed) Disposer.register(windowScope, terminalTabbed)
add(terminalTabbed) add(terminalTabbed, BorderLayout.CENTER)
dataProviderSupport.addData(DataProviders.TabbedPane, tabbedPane) dataProviderSupport.addData(DataProviders.TabbedPane, tabbedPane)
dataProviderSupport.addData(DataProviders.TermoraFrame, this) dataProviderSupport.addData(DataProviders.TermoraFrame, this)
dataProviderSupport.addData(DataProviders.WindowScope, windowScope) dataProviderSupport.addData(DataProviders.WindowScope, windowScope)
} }
private fun forceHitTest() {
val mouseAdapter = object : MouseAdapter() {
private fun hit(e: MouseEvent) {
if (e.source == tabbedPane) {
val index = tabbedPane.indexAtLocation(e.x, e.y)
if (index >= 0) {
return
}
}
titleBar.forceHitTest(false)
}
override fun mouseClicked(e: MouseEvent) {
hit(e)
}
override fun mousePressed(e: MouseEvent) {
if (e.source == toolbar.getJToolBar()) {
if (!isWindowDecorationsSupported && SwingUtilities.isLeftMouseButton(e)) {
if (JBR.isWindowMoveSupported()) {
JBR.getWindowMove().startMovingTogetherWithMouse(this@TermoraFrame, e.button)
}
}
}
hit(e)
}
override fun mouseReleased(e: MouseEvent) {
hit(e)
}
override fun mouseEntered(e: MouseEvent) {
hit(e)
}
override fun mouseDragged(e: MouseEvent) {
hit(e)
}
override fun mouseMoved(e: MouseEvent) {
hit(e)
}
}
terminalTabbed.addMouseListener(mouseAdapter)
terminalTabbed.addMouseMotionListener(mouseAdapter)
tabbedPane.addMouseListener(mouseAdapter)
tabbedPane.addMouseMotionListener(mouseAdapter)
toolbar.getJToolBar().addMouseListener(mouseAdapter)
toolbar.getJToolBar().addMouseMotionListener(mouseAdapter)
}
override fun <T : Any> getData(dataKey: DataKey<T>): T? { override fun <T : Any> getData(dataKey: DataKey<T>): T? {
return dataProviderSupport.getData(dataKey) return dataProviderSupport.getData(dataKey)
?: terminalTabbed.getData(dataKey) ?: terminalTabbed.getData(dataKey)
@@ -204,5 +236,22 @@ class TermoraFrame : JFrame(), DataProvider {
return id.hashCode() return id.hashCode()
} }
override fun addNotify() {
super.addNotify()
val dialog = object : DialogWrapper(this@TermoraFrame) {
init {
init()
controlsVisible = false
}
override fun createCenterPanel(): JComponent {
return JPanel()
}
}
dialog.title = "Hello"
dialog.size = Dimension(800, 600)
dialog.setLocationRelativeTo(this)
// dialog.isVisible = true
}
} }

View File

@@ -4,13 +4,13 @@ import app.termora.Application.ohMyJson
import app.termora.actions.* import app.termora.actions.*
import app.termora.findeverywhere.FindEverywhereAction import app.termora.findeverywhere.FindEverywhereAction
import app.termora.snippet.SnippetAction import app.termora.snippet.SnippetAction
import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.extras.components.FlatTabbedPane import com.formdev.flatlaf.extras.components.FlatTabbedPane
import com.formdev.flatlaf.util.SystemInfo import com.formdev.flatlaf.util.SystemInfo
import com.jetbrains.WindowDecorations
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
import org.jdesktop.swingx.action.ActionContainerFactory import org.jdesktop.swingx.action.ActionContainerFactory
import java.awt.Insets import java.awt.Rectangle
import java.awt.event.ComponentAdapter import java.awt.event.ComponentAdapter
import java.awt.event.ComponentEvent import java.awt.event.ComponentEvent
import javax.swing.Box import javax.swing.Box
@@ -25,7 +25,7 @@ data class ToolBarAction(
class TermoraToolBar( class TermoraToolBar(
private val windowScope: WindowScope, private val windowScope: WindowScope,
private val titleBar: WindowDecorations.CustomTitleBar, private val frame: TermoraFrame,
private val tabbedPane: FlatTabbedPane private val tabbedPane: FlatTabbedPane
) { ) {
private val properties by lazy { Database.getDatabase().properties } private val properties by lazy { Database.getDatabase().properties }
@@ -155,14 +155,11 @@ class TermoraToolBar(
} }
fun adjust() { fun adjust() {
if (SystemInfo.isMacOS) { if (SystemInfo.isWindows || SystemInfo.isLinux) {
val left = titleBar.leftInset.toInt() val rectangle =
if (tabbedPane.tabAreaInsets.left != left) { frame.rootPane.getClientProperty(FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS)
tabbedPane.tabAreaInsets = Insets(0, left, 0, 0) as? Rectangle ?: return
} val right = rectangle.width
} else if (SystemInfo.isWindows || SystemInfo.isLinux) {
val right = titleBar.rightInset.toInt()
val toolbar = this@MyToolBar val toolbar = this@MyToolBar
for (i in 0 until toolbar.componentCount) { for (i in 0 until toolbar.componentCount) {
val c = toolbar.getComponent(i) val c = toolbar.getComponent(i)

View File

@@ -7,9 +7,7 @@ import app.termora.WindowScope
import app.termora.actions.AnAction import app.termora.actions.AnAction
import app.termora.actions.AnActionEvent import app.termora.actions.AnActionEvent
import app.termora.macro.MacroFindEverywhereProvider import app.termora.macro.MacroFindEverywhereProvider
import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.extras.components.FlatTextField import com.formdev.flatlaf.extras.components.FlatTextField
import com.jetbrains.JBR
import java.awt.BorderLayout import java.awt.BorderLayout
import java.awt.Dimension import java.awt.Dimension
import java.awt.Insets import java.awt.Insets
@@ -45,17 +43,10 @@ class FindEverywhere(owner: Window, windowScope: WindowScope) : DialogWrapper(ow
minimumSize = Dimension(size.width / 2, size.height / 2) minimumSize = Dimension(size.width / 2, size.height / 2)
isModal = false isModal = false
lostFocusDispose = true lostFocusDispose = true
controlsVisible = false
defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
setLocationRelativeTo(null) setLocationRelativeTo(null)
// 不支持装饰,铺满
if (!JBR.isWindowDecorationsSupported()) {
rootPane.putClientProperty(FlatClientProperties.FULL_WINDOW_CONTENT, true)
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_CLOSE, false)
}
rootPane.background = DynamicColor("desktop") val desktopBackground = DynamicColor("desktop")
centerPanel.background = DynamicColor("desktop") centerPanel.background = DynamicColor("desktop")
centerPanel.border = BorderFactory.createEmptyBorder(12, 12, 12, 12) centerPanel.border = BorderFactory.createEmptyBorder(12, 12, 12, 12)
@@ -70,7 +61,7 @@ class FindEverywhere(owner: Window, windowScope: WindowScope) : DialogWrapper(ow
resultList.isRolloverEnabled = false resultList.isRolloverEnabled = false
resultList.selectionMode = ListSelectionModel.SINGLE_SELECTION resultList.selectionMode = ListSelectionModel.SINGLE_SELECTION
resultList.border = BorderFactory.createEmptyBorder(5, 0, 0, 0) resultList.border = BorderFactory.createEmptyBorder(5, 0, 0, 0)
resultList.background = rootPane.background resultList.background = desktopBackground
val scrollPane = JScrollPane(resultList) val scrollPane = JScrollPane(resultList)
@@ -226,5 +217,11 @@ class FindEverywhere(owner: Window, windowScope: WindowScope) : DialogWrapper(ow
super.setVisible(visible) super.setVisible(visible)
} }
override fun addNotify() {
super.addNotify()
controlsVisible = false
fullWindowContent = true
}
} }

View File

@@ -119,10 +119,11 @@ class KeymapPanel : JPanel(BorderLayout()) {
val keymap = getCurrentKeymap() val keymap = getCurrentKeymap()
val index = keymapComboBox.selectedIndex val index = keymapComboBox.selectedIndex
if (keymap != null && !keymap.isReadonly && index >= 0) { if (keymap != null && !keymap.isReadonly && index >= 0) {
val text = InputDialog( val text = OptionPane.showInputDialog(
SwingUtilities.getWindowAncestor(this@KeymapPanel), SwingUtilities.getWindowAncestor(this@KeymapPanel),
title = renameBtn.toolTipText, text = keymap.name title = renameBtn.toolTipText,
).getText() value = keymap.name
)
if (!text.isNullOrBlank()) { if (!text.isNullOrBlank()) {
if (text != keymap.name) { if (text != keymap.name) {
keymapManager.removeKeymap(keymap.name) keymapManager.removeKeymap(keymap.name)

View File

@@ -103,7 +103,9 @@ class KeyManagerPanel : JPanel(BorderLayout()) {
private fun initEvents() { private fun initEvents() {
generateBtn.addActionListener { generateBtn.addActionListener {
val dialog = GenerateKeyDialog(SwingUtilities.getWindowAncestor(this)) val owner = SwingUtilities.getWindowAncestor(this)
val dialog = GenerateKeyDialog(owner)
dialog.setLocationRelativeTo(owner)
dialog.isVisible = true dialog.isVisible = true
if (dialog.ohKeyPair != OhKeyPair.empty) { if (dialog.ohKeyPair != OhKeyPair.empty) {
val keyPair = dialog.ohKeyPair val keyPair = dialog.ohKeyPair
@@ -142,12 +144,14 @@ class KeyManagerPanel : JPanel(BorderLayout()) {
editBtn.addActionListener { editBtn.addActionListener {
val row = keyPairTable.selectedRow val row = keyPairTable.selectedRow
if (row >= 0) { if (row >= 0) {
val owner = SwingUtilities.getWindowAncestor(this)
var ohKeyPair = keyPairTableModel.getOhKeyPair(row) var ohKeyPair = keyPairTableModel.getOhKeyPair(row)
val dialog = GenerateKeyDialog( val dialog = GenerateKeyDialog(
SwingUtilities.getWindowAncestor(this), owner,
ohKeyPair, ohKeyPair,
true true
) )
dialog.setLocationRelativeTo(owner)
dialog.title = ohKeyPair.name dialog.title = ohKeyPair.name
dialog.isVisible = true dialog.isVisible = true
ohKeyPair = dialog.ohKeyPair ohKeyPair = dialog.ohKeyPair
@@ -342,7 +346,6 @@ class KeyManagerPanel : JPanel(BorderLayout()) {
pack() pack()
size = Dimension(UIManager.getInt("Dialog.width") - 300, size.height) size = Dimension(UIManager.getInt("Dialog.width") - 300, size.height)
setLocationRelativeTo(null)
} }
@@ -354,7 +357,7 @@ class KeyManagerPanel : JPanel(BorderLayout()) {
var rows = 1 var rows = 1
val step = 2 val step = 2
return FormBuilder.create().layout(layout).padding("0dlu, $formMargin, $formMargin, $formMargin") return FormBuilder.create().layout(layout).padding("2dlu, $formMargin, $formMargin, $formMargin")
.add("${I18n.getString("termora.keymgr.table.type")}:").xy(1, rows) .add("${I18n.getString("termora.keymgr.table.type")}:").xy(1, rows)
.add(typeComboBox).xy(3, rows).apply { rows += step } .add(typeComboBox).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.keymgr.table.length")}:").xy(1, rows) .add("${I18n.getString("termora.keymgr.table.length")}:").xy(1, rows)
@@ -512,7 +515,7 @@ class KeyManagerPanel : JPanel(BorderLayout()) {
var rows = 1 var rows = 1
val step = 2 val step = 2
return FormBuilder.create().layout(layout).padding("0dlu, $formMargin, $formMargin, $formMargin") return FormBuilder.create().layout(layout).padding("2dlu, $formMargin, $formMargin, $formMargin")
.add("File:").xy(1, rows) .add("File:").xy(1, rows)
.add(fileTextField).xy(3, rows).apply { rows += step } .add(fileTextField).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.keymgr.table.type")}:").xy(1, rows) .add("${I18n.getString("termora.keymgr.table.type")}:").xy(1, rows)
@@ -587,8 +590,10 @@ class KeyManagerPanel : JPanel(BorderLayout()) {
try { try {
val provider = FileKeyPairProvider(file.toPath()) val provider = FileKeyPairProvider(file.toPath())
provider.passwordFinder = FilePasswordProvider { _, _, _ -> provider.passwordFinder = FilePasswordProvider { _, _, _ ->
val dialog = InputDialog(owner = this@ImportKeyDialog, title = "Password") OptionPane.showInputDialog(
dialog.getText() ?: String() SwingUtilities.getWindowAncestor(this),
title = I18n.getString("termora.new-host.general.password"),
) ?: String()
} }
val keyPair = provider.loadKeys(null).firstOrNull() val keyPair = provider.loadKeys(null).firstOrNull()
?: throw IllegalStateException("Failed to load the key file") ?: throw IllegalStateException("Failed to load the key file")

View File

@@ -83,8 +83,11 @@ class MacroDialog(owner: Window) : DialogWrapper(owner) {
val index = list.selectedIndex val index = list.selectedIndex
if (index >= 0) { if (index >= 0) {
val macro = model.getElementAt(index) val macro = model.getElementAt(index)
val dialog = InputDialog(owner = this, title = macro.name, text = macro.name) val text = OptionPane.showInputDialog(
val text = dialog.getText() ?: String() this,
title = macro.name,
value = macro.name
) ?: String()
if (text.isNotBlank()) { if (text.isNotBlank()) {
val newMacro = macro.copy(name = text) val newMacro = macro.copy(name = text)
macroManager.addMacro(newMacro) macroManager.addMacro(newMacro)

View File

@@ -0,0 +1,51 @@
package app.termora.native.osx
import de.jangassen.jfa.foundation.Foundation
import de.jangassen.jfa.foundation.ID
import org.slf4j.LoggerFactory
import java.awt.Component
import java.awt.Window
object NativeMacLibrary {
private val log = LoggerFactory.getLogger(NativeMacLibrary::class.java)
fun getNSWindow(window: Window): Long? {
try {
val peerField = Component::class.java.getDeclaredField("peer") ?: return null
peerField.isAccessible = true
val peer = peerField.get(window) ?: return null
val platformWindowField = peer.javaClass.getDeclaredField("platformWindow") ?: return null
platformWindowField.isAccessible = true
val platformWindow = platformWindowField.get(peer)
val ptrField = Class.forName("sun.lwawt.macosx.CFRetainedResource")
.getDeclaredField("ptr") ?: return null
ptrField.isAccessible = true
return ptrField.get(platformWindow) as Long
} catch (e: Exception) {
if (log.isErrorEnabled) {
log.error(e.message, e)
}
return null
}
}
fun setControlsVisible(window: Window, visible: Boolean) {
val nsWindow = ID(getNSWindow(window) ?: return)
try {
Foundation.executeOnMainThread(true, true) {
for (i in 0..2) {
val button = Foundation.invoke(nsWindow, "standardWindowButton:", i)
Foundation.invoke(button, "setHidden:", !visible)
}
}
} catch (e: Exception) {
if (log.isErrorEnabled) {
log.error(e.message, e)
}
}
}
}

View File

@@ -414,12 +414,11 @@ class FileSystemViewTable(
val index = selectedRow val index = selectedRow
if (index < 0) return if (index < 0) return
val attr = model.getAttr(index) val attr = model.getAttr(index)
val dialog = InputDialog( val text = OptionPane.showInputDialog(
owner, owner,
title = attr.name, value = attr.name,
text = attr.name, title = I18n.getString("termora.transport.table.contextmenu.rename")
) ) ?: return
val text = dialog.getText() ?: return
if (text.isBlank() || text == attr.name) return if (text.isBlank() || text == attr.name) return
if (model.getPathNames().contains(text)) { if (model.getPathNames().contains(text)) {
OptionPane.showMessageDialog( OptionPane.showMessageDialog(
@@ -544,12 +543,7 @@ class FileSystemViewTable(
private fun newFolderOrFile(isFile: Boolean) { private fun newFolderOrFile(isFile: Boolean) {
val name = if (isFile) I18n.getString("termora.transport.table.contextmenu.new.file") val name = if (isFile) I18n.getString("termora.transport.table.contextmenu.new.file")
else I18n.getString("termora.welcome.contextmenu.new.folder.name") else I18n.getString("termora.welcome.contextmenu.new.folder.name")
val dialog = InputDialog( val text = OptionPane.showInputDialog(owner, title = name, value = name) ?: return
owner,
title = name,
text = name,
)
val text = dialog.getText() ?: return
if (text.isBlank()) return if (text.isBlank()) return
if (model.getPathNames().contains(text)) { if (model.getPathNames().contains(text)) {
OptionPane.showMessageDialog( OptionPane.showMessageDialog(