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 titleLabel = JLabel()
private val toolbar = FlatToolBar() private val toolbar = FlatToolBar()
private val visualWindow get() = this private val visualWindow get() = this
private val resizer = VisualWindowResizer(this) { !isWindow } private val resizer = VisualWindowResizer(visualWindow) { !isWindow && !isStick }
private var isWindow = false private var isWindow = false
set(value) { set(value) {
val oldValue = field val oldValue = field
@@ -39,11 +39,23 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
} }
} }
protected var isStickHover = false
var title: String private set(value) {
set(value) { if (value == field) return
titleLabel.text = value 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 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) if (w > 0 && h > 0) setSize(w, h) else setSize(400, 200)
oldBounds = bounds
alwaysTopBtn.isSelected = isAlwaysTop alwaysTopBtn.isSelected = isAlwaysTop
alwaysTopBtn.isVisible = false alwaysTopBtn.isVisible = false
} }
@@ -90,14 +103,18 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
if (SwingUtilities.isDescendingFrom(c, visualWindow)) { if (SwingUtilities.isDescendingFrom(c, visualWindow)) {
visualWindowManager.moveToFront(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 { Disposer.register(this, object : Disposable {
override fun dispose() { override fun dispose() {
@@ -110,8 +127,7 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
toggleWindowBtn.addActionListener { toggleWindow() } toggleWindowBtn.addActionListener { toggleWindow() }
addPropertyChangeListener("isWindow", object : PropertyChangeListener { addPropertyChangeListener("isWindow") {
override fun propertyChange(evt: PropertyChangeEvent) {
if (isWindow) { if (isWindow) {
border = BorderFactory.createMatteBorder(1, 0, 0, 0, DynamicColor.BorderColor) border = BorderFactory.createMatteBorder(1, 0, 0, 0, DynamicColor.BorderColor)
toggleWindowBtn.icon = Icons.openInToolWindow toggleWindowBtn.icon = Icons.openInToolWindow
@@ -120,6 +136,14 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
toggleWindowBtn.icon = Icons.openInNewWindow 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 { alwaysTopBtn.addActionListener {
@@ -152,11 +176,12 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
override fun dispose() { 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.x", bounds.x.toString())
properties.putString("VisualWindow.${id}.location.y", bounds.y.toString()) properties.putString("VisualWindow.${id}.location.y", bounds.y.toString())
properties.putString("VisualWindow.${id}.location.width", bounds.width.toString()) properties.putString("VisualWindow.${id}.location.width", bounds.width.toString())
properties.putString("VisualWindow.${id}.location.height", bounds.height.toString()) properties.putString("VisualWindow.${id}.location.height", bounds.height.toString())
properties.putString("VisualWindow.${id}.stick", isStick.toString())
resizer.uninstall() resizer.uninstall()
@@ -198,6 +223,13 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
// 变基 // 变基
visualWindowManager.rebaseVisualWindow(this) visualWindowManager.rebaseVisualWindow(this)
// 如果在预览状态,那么设置成 false
if (isStickHover) {
isStickHover = false
} else {
reassemble()
}
val dialog = VisualWindowDialog().apply { dialog = this } val dialog = VisualWindowDialog().apply { dialog = this }
dialog.addWindowListener(closeWindowListener) dialog.addWindowListener(closeWindowListener)
dialog.isVisible = true dialog.isVisible = true
@@ -206,44 +238,78 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
bounds = oldBounds bounds = oldBounds
visualWindowManager.removeVisualWindow(visualWindow) visualWindowManager.removeVisualWindow(visualWindow)
visualWindowManager.addVisualWindow(visualWindow) visualWindowManager.addVisualWindow(visualWindow)
// 重组
reassemble()
} }
} }
private inner class DragListener() : MouseAdapter() { private inner class DragListener : MouseAdapter() {
private var startPoint: Point? = null private var startPoint: Point? = null
private val stickPx = 2
override fun mousePressed(e: MouseEvent) { override fun mousePressed(e: MouseEvent) {
if (isWindow) { if (isWindow) {
startPoint = null startPoint = null
return return
} }
startPoint = SwingUtilities.convertPoint(visualWindow, e.getPoint(), visualWindow.getParent()) startPoint = SwingUtilities.convertPoint(visualWindow, e.getPoint(), visualWindow.parent)
} }
override fun mouseDragged(e: MouseEvent) { override fun mouseDragged(e: MouseEvent) {
val startPoint = this.startPoint ?: return 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 dimension = visualWindowManager.getDimension()
val x = min( val x = min(
visualWindow.getX() + (newPoint.x - startPoint.x), visualWindow.x + (newPoint.x - startPoint.x),
dimension.width - visualWindow.width dimension.width - visualWindow.width
) )
val y = min( val y = min(
visualWindow.getY() + (newPoint.y - startPoint.y), visualWindow.y + (newPoint.y - startPoint.y),
dimension.height - visualWindow.height 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 this.startPoint = newPoint
} }
override fun mouseReleased(e: MouseEvent) { override fun mouseReleased(e: MouseEvent) {
visualWindowManager.moveToFront(visualWindow) 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) }
} }