feat: floating window supports stick (#374)

This commit is contained in:
hstyi
2025-03-16 08:42:25 +08:00
committed by GitHub
parent e72c6b77b5
commit e0ea42faee

View File

@@ -18,7 +18,7 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
private val titleLabel = JLabel()
private val toolbar = FlatToolBar()
private val visualWindow get() = this
private val resizer = VisualWindowResizer(this) { !isWindow }
private val resizer = VisualWindowResizer(visualWindow) { !isWindow && !isStick }
private var isWindow = false
set(value) {
val oldValue = field
@@ -39,11 +39,23 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
}
}
var title: String
set(value) {
titleLabel.text = value
protected var isStickHover = false
private set(value) {
if (value == field) return
field = value
reassemble()
}
protected var isStick: Boolean = false
private set(value) {
if (field == value) return
if (!value) isStickHover = false
field = value
reassemble()
}
protected val expand get() = isWindow || isStickHover || !isStick
protected var title: String
set(value) = titleLabel.setText(value)
get() = titleLabel.text
@@ -69,6 +81,7 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
if (w > 0 && h > 0) setSize(w, h) else setSize(400, 200)
oldBounds = bounds
alwaysTopBtn.isSelected = isAlwaysTop
alwaysTopBtn.isVisible = false
}
@@ -90,14 +103,18 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
if (SwingUtilities.isDescendingFrom(c, visualWindow)) {
visualWindowManager.moveToFront(visualWindow)
}
} else if (event.id == MouseEvent.MOUSE_MOVED) {
if (isStick && !isWindow) {
val c = event.component ?: return
isStickHover = SwingUtilities.isDescendingFrom(c, visualWindow)
}
}
}
}
}
// 监听全局事件
toolkit.addAWTEventListener(awtEventListener, MouseEvent.MOUSE_EVENT_MASK)
toolkit.addAWTEventListener(awtEventListener, MouseEvent.MOUSE_EVENT_MASK or MouseEvent.MOUSE_MOTION_EVENT_MASK)
Disposer.register(this, object : Disposable {
override fun dispose() {
@@ -110,8 +127,7 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
toggleWindowBtn.addActionListener { toggleWindow() }
addPropertyChangeListener("isWindow", object : PropertyChangeListener {
override fun propertyChange(evt: PropertyChangeEvent) {
addPropertyChangeListener("isWindow") {
if (isWindow) {
border = BorderFactory.createMatteBorder(1, 0, 0, 0, DynamicColor.BorderColor)
toggleWindowBtn.icon = Icons.openInToolWindow
@@ -120,6 +136,14 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
toggleWindowBtn.icon = Icons.openInNewWindow
}
}
// 被添加到组件后
addPropertyChangeListener("ancestor", object : PropertyChangeListener {
override fun propertyChange(evt: PropertyChangeEvent) {
removePropertyChangeListener("ancestor", this)
// 是否吸附
isStick = properties.getString("VisualWindow.${id}.stick", "false").toBoolean()
}
})
alwaysTopBtn.addActionListener {
@@ -152,11 +176,12 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
override fun dispose() {
val bounds = if (isWindow) oldBounds else bounds
val bounds = if (isWindow || isStick) oldBounds else bounds
properties.putString("VisualWindow.${id}.location.x", bounds.x.toString())
properties.putString("VisualWindow.${id}.location.y", bounds.y.toString())
properties.putString("VisualWindow.${id}.location.width", bounds.width.toString())
properties.putString("VisualWindow.${id}.location.height", bounds.height.toString())
properties.putString("VisualWindow.${id}.stick", isStick.toString())
resizer.uninstall()
@@ -198,6 +223,13 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
// 变基
visualWindowManager.rebaseVisualWindow(this)
// 如果在预览状态,那么设置成 false
if (isStickHover) {
isStickHover = false
} else {
reassemble()
}
val dialog = VisualWindowDialog().apply { dialog = this }
dialog.addWindowListener(closeWindowListener)
dialog.isVisible = true
@@ -206,44 +238,78 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
bounds = oldBounds
visualWindowManager.removeVisualWindow(visualWindow)
visualWindowManager.addVisualWindow(visualWindow)
// 重组
reassemble()
}
}
private inner class DragListener() : MouseAdapter() {
private inner class DragListener : MouseAdapter() {
private var startPoint: Point? = null
private val stickPx = 2
override fun mousePressed(e: MouseEvent) {
if (isWindow) {
startPoint = null
return
}
startPoint = SwingUtilities.convertPoint(visualWindow, e.getPoint(), visualWindow.getParent())
startPoint = SwingUtilities.convertPoint(visualWindow, e.getPoint(), visualWindow.parent)
}
override fun mouseDragged(e: MouseEvent) {
val startPoint = this.startPoint ?: return
val newPoint = SwingUtilities.convertPoint(visualWindow, e.getPoint(), visualWindow.getParent())
val newPoint = SwingUtilities.convertPoint(visualWindow, e.getPoint(), visualWindow.parent)
val dimension = visualWindowManager.getDimension()
val x = min(
visualWindow.getX() + (newPoint.x - startPoint.x),
visualWindow.x + (newPoint.x - startPoint.x),
dimension.width - visualWindow.width
)
val y = min(
visualWindow.getY() + (newPoint.y - startPoint.y),
visualWindow.y + (newPoint.y - startPoint.y),
dimension.height - visualWindow.height
)
visualWindow.setBounds(max(x, 0), max(y, 0), visualWindow.getWidth(), visualWindow.getHeight())
if (isStick) {
if (y > stickPx) {
isStick = false
return
}
} else {
oldBounds = visualWindow.bounds
}
// 如果太靠近边缘,那么动态显示/隐藏边框
border = BorderFactory.createMatteBorder(if (y <= 0) 0 else 1, 1, 1, 1, DynamicColor.BorderColor)
visualWindow.setBounds(max(x, 0), max(y, 0), visualWindow.width, visualWindow.height)
this.startPoint = newPoint
}
override fun mouseReleased(e: MouseEvent) {
visualWindowManager.moveToFront(visualWindow)
isStick = visualWindow.bounds.y <= stickPx
}
}
/**
* 重新组装
*/
protected open fun reassemble() {
if (expand) {
border = BorderFactory.createMatteBorder(if (isStickHover) 0 else 1, 1, 1, 1, DynamicColor.BorderColor)
visualWindow.setBounds(bounds.x, bounds.y, oldBounds.width, oldBounds.height)
} else {
val bounds = visualWindow.bounds
visualWindow.setBounds(bounds.x, bounds.y, bounds.width, max(toolbar.height, toolbar.preferredSize.height))
border = BorderFactory.createMatteBorder(0, 1, 1, 1, DynamicColor.BorderColor)
}
// 重新渲染
SwingUtilities.invokeLater { SwingUtilities.updateComponentTreeUI(this) }
}