mirror of
https://github.com/chylex/IntelliJ-Pin-Undocked-Tool-Windows.git
synced 2025-06-01 11:34:06 +02:00
Stop IntelliJ from hiding Undocked tool windows when they lose focus
This commit is contained in:
parent
96c63d6b06
commit
7ec831c0c3
src/main/kotlin/com/chylex/intellij/pinundockedtoolwindows
@ -0,0 +1,21 @@
|
|||||||
|
package com.chylex.intellij.pinundockedtoolwindows
|
||||||
|
|
||||||
|
import com.chylex.intellij.pinundockedtoolwindows.patch.ToolWindowPatcher
|
||||||
|
import com.intellij.ide.plugins.DynamicPluginListener
|
||||||
|
import com.intellij.ide.plugins.IdeaPluginDescriptor
|
||||||
|
|
||||||
|
const val PLUGIN_ID = "com.chylex.intellij.pinundockedtoolwindows"
|
||||||
|
|
||||||
|
class DynamicPluginListener : DynamicPluginListener {
|
||||||
|
override fun pluginLoaded(pluginDescriptor: IdeaPluginDescriptor) {
|
||||||
|
if (pluginDescriptor.pluginId.idString == PLUGIN_ID) {
|
||||||
|
ToolWindowPatcher.apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun beforePluginUnload(pluginDescriptor: IdeaPluginDescriptor, isUpdate: Boolean) {
|
||||||
|
if (pluginDescriptor.pluginId.idString == PLUGIN_ID) {
|
||||||
|
ToolWindowPatcher.revert()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.chylex.intellij.pinundockedtoolwindows
|
||||||
|
|
||||||
|
import com.chylex.intellij.pinundockedtoolwindows.patch.ToolWindowPatcher
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.startup.ProjectActivity
|
||||||
|
|
||||||
|
class ProjectActivityListener : ProjectActivity {
|
||||||
|
override suspend fun execute(project: Project) {
|
||||||
|
ApplicationManager.getApplication().invokeLater(ToolWindowPatcher::apply)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.chylex.intellij.pinundockedtoolwindows.patch
|
||||||
|
|
||||||
|
import com.intellij.openapi.wm.IdeFocusManager
|
||||||
|
import com.intellij.openapi.wm.ToolWindowManager
|
||||||
|
import com.intellij.openapi.wm.ToolWindowType
|
||||||
|
import com.intellij.openapi.wm.impl.ToolWindowManagerImpl
|
||||||
|
import com.intellij.toolWindow.InternalDecoratorImpl
|
||||||
|
import com.intellij.ui.ClientProperty
|
||||||
|
import java.awt.AWTEvent
|
||||||
|
import java.awt.Component
|
||||||
|
import java.awt.event.AWTEventListener
|
||||||
|
import java.awt.event.FocusEvent
|
||||||
|
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
|
class PatchedAwtFocusListener(internal val original: AWTEventListener) : AWTEventListener {
|
||||||
|
override fun eventDispatched(event: AWTEvent?) {
|
||||||
|
if (event is FocusEvent && event.id == FocusEvent.FOCUS_LOST && shouldIgnoreFocusLostEvent(event.component)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
original.eventDispatched(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shouldIgnoreFocusLostEvent(currentlyFocused: Component?): Boolean {
|
||||||
|
val project = IdeFocusManager.getGlobalInstance().lastFocusedFrame?.project ?: return false
|
||||||
|
if (project.isDisposed || project.isDefault) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val toolWindowManager = ToolWindowManager.getInstance(project)
|
||||||
|
val toolWindowId = getToolWindowIdForComponent(currentlyFocused) ?: return false
|
||||||
|
val toolWindow = toolWindowManager.getToolWindow(toolWindowId) ?: return false
|
||||||
|
|
||||||
|
return toolWindow.type == ToolWindowType.SLIDING
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getToolWindowIdForComponent(component: Component?): String? {
|
||||||
|
var c = component
|
||||||
|
while (c != null) {
|
||||||
|
if (c is InternalDecoratorImpl) {
|
||||||
|
return c.toolWindowId
|
||||||
|
}
|
||||||
|
c = ClientProperty.get(c, ToolWindowManagerImpl.PARENT_COMPONENT) ?: c.parent
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.chylex.intellij.pinundockedtoolwindows.patch
|
||||||
|
|
||||||
|
import java.awt.AWTEvent
|
||||||
|
import java.awt.Toolkit
|
||||||
|
import java.awt.event.AWTEventListenerProxy
|
||||||
|
|
||||||
|
object ToolWindowPatcher {
|
||||||
|
private const val EVENT_MASK = AWTEvent.FOCUS_EVENT_MASK or AWTEvent.WINDOW_FOCUS_EVENT_MASK
|
||||||
|
|
||||||
|
private var activePatch: ActivePatch? = null
|
||||||
|
|
||||||
|
private data class ActivePatch(val listener: PatchedAwtFocusListener, val eventMask: Long)
|
||||||
|
|
||||||
|
fun apply() {
|
||||||
|
if (activePatch != null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val toolkit = Toolkit.getDefaultToolkit()
|
||||||
|
for (listenerProxy in toolkit.getAWTEventListeners(EVENT_MASK)) {
|
||||||
|
if (listenerProxy is AWTEventListenerProxy) {
|
||||||
|
val listener = listenerProxy.listener
|
||||||
|
if (listener.javaClass.name == "com.intellij.openapi.wm.impl.ToolWindowManagerImpl\$ToolWindowManagerAppLevelHelper\$MyListener") {
|
||||||
|
val patch = ActivePatch(PatchedAwtFocusListener(listener), listenerProxy.eventMask)
|
||||||
|
toolkit.removeAWTEventListener(listener)
|
||||||
|
toolkit.addAWTEventListener(patch.listener, patch.eventMask)
|
||||||
|
activePatch = patch
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun revert() {
|
||||||
|
val patch = activePatch
|
||||||
|
if (patch != null) {
|
||||||
|
val toolkit = Toolkit.getDefaultToolkit()
|
||||||
|
toolkit.removeAWTEventListener(patch.listener)
|
||||||
|
toolkit.addAWTEventListener(patch.listener.original, patch.eventMask)
|
||||||
|
activePatch = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user