IJPL-158075 updateHeadersRunnable - use flow instead of alarm

GitOrigin-RevId: 6984b38a83886070e434b522846716de4cfd2432
This commit is contained in:
Vladimir Krivosheev
2024-07-11 14:23:08 +02:00
committed by intellij-monorepo-bot
parent 184812fb0d
commit 7a927bdadf

View File

@@ -1,6 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplaceGetOrSet", "ReplacePutWithAssignment", "OverridingDeprecatedMember", "ReplaceNegatedIsEmptyWithIsNotEmpty",
"PrivatePropertyName")
@file:OptIn(FlowPreview::class)
package com.intellij.openapi.wm.impl
@@ -27,6 +28,7 @@ import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.application.asContextElement
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.components.serviceAsync
import com.intellij.openapi.components.serviceIfCreated
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.debug
@@ -58,7 +60,6 @@ import com.intellij.openapi.wm.ex.ToolWindowEx
import com.intellij.openapi.wm.ex.ToolWindowManagerEx
import com.intellij.openapi.wm.ex.ToolWindowManagerListener
import com.intellij.openapi.wm.ex.ToolWindowManagerListener.ToolWindowManagerEventType
import com.intellij.openapi.wm.ex.ToolWindowManagerListener.ToolWindowManagerEventType.*
import com.intellij.platform.ide.progress.runWithModalProgressBlocking
import com.intellij.serviceContainer.NonInjectable
import com.intellij.toolWindow.*
@@ -70,6 +71,10 @@ import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.messages.SimpleMessageBusConnection
import com.intellij.util.ui.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import org.intellij.lang.annotations.MagicConstant
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.TestOnly
@@ -203,7 +208,7 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
}
@Service(Service.Level.APP)
internal class ToolWindowManagerAppLevelHelper {
internal class ToolWindowManagerAppLevelHelper(coroutineScope: CoroutineScope) {
companion object {
private fun handleFocusEvent(event: FocusEvent) {
if (event.id == FocusEvent.FOCUS_LOST) {
@@ -284,16 +289,23 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
val awtFocusListener = MyListener()
Toolkit.getDefaultToolkit().addAWTEventListener(awtFocusListener, AWTEvent.FOCUS_EVENT_MASK or AWTEvent.WINDOW_FOCUS_EVENT_MASK)
val updateHeadersRunnable = {
for (project in getOpenedProjects()) {
(getInstance(project) as ToolWindowManagerImpl).updateToolWindowHeaders()
}
val updateHeadersRequests = MutableSharedFlow<Unit>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
coroutineScope.launch {
updateHeadersRequests
.debounce(50)
.collectLatest {
for (project in getOpenedProjects()) {
val toolWindowManager = project.serviceAsync<ToolWindowManager>() as ToolWindowManagerImpl
withContext(Dispatchers.EDT) {
toolWindowManager.updateToolWindowHeaders()
}
}
}
}
val updateHeadersAlarm = SingleAlarm(updateHeadersRunnable, 50, ApplicationManager.getApplication())
val focusListener = PropertyChangeListener { updateHeadersAlarm.cancelAndRequest() }
val focusListener = PropertyChangeListener { check(updateHeadersRequests.tryEmit(Unit)) }
FocusUtil.addFocusOwnerListener(ApplicationManager.getApplication(), focusListener)
val connection = ApplicationManager.getApplication().messageBus.connect()
val connection = ApplicationManager.getApplication().messageBus.connect(coroutineScope)
connection.subscribe(ProjectCloseListener.TOPIC, object : ProjectCloseListener {
override fun projectClosingBeforeSave(project: Project) {
val manager = (project.serviceIfCreated<ToolWindowManager>() as ToolWindowManagerImpl?) ?: return
@@ -349,7 +361,7 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
}
false
}, ApplicationManager.getApplication())
}, coroutineScope)
}
}
@@ -406,7 +418,7 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
}
private fun updateToolWindowHeaders() {
focusManager.doWhenFocusSettlesDown(ExpirableRunnable.forProject(project) {
IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(ExpirableRunnable.forProject(project) {
for (entry in idToEntry.values) {
if (entry.readOnlyWindowInfo.isVisible) {
val decorator = entry.toolWindow.decorator ?: continue
@@ -487,9 +499,11 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
}
}
suspend fun init(frameHelperDeferred: Deferred<ProjectFrameHelper>,
reopeningEditorJob: Job,
taskListDeferred: Deferred<List<RegisterToolWindowTask>>) {
suspend fun init(
frameHelperDeferred: Deferred<ProjectFrameHelper>,
reopeningEditorJob: Job,
taskListDeferred: Deferred<List<RegisterToolWindowTask>>,
) {
doInit(frameHelperDeferred = frameHelperDeferred,
connection = project.messageBus.connect(coroutineScope),
reopeningEditorJob = reopeningEditorJob,
@@ -497,10 +511,12 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
}
@VisibleForTesting
suspend fun doInit(frameHelperDeferred: Deferred<ProjectFrameHelper>,
connection: SimpleMessageBusConnection,
reopeningEditorJob: Job,
taskListDeferred: Deferred<List<RegisterToolWindowTask>>?) {
suspend fun doInit(
frameHelperDeferred: Deferred<ProjectFrameHelper>,
connection: SimpleMessageBusConnection,
reopeningEditorJob: Job,
taskListDeferred: Deferred<List<RegisterToolWindowTask>>?,
) {
withContext(ModalityState.any().asContextElement()) {
val frameHelper = frameHelperDeferred.await()
launch(Dispatchers.EDT) {
@@ -636,10 +652,12 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
}
}
internal fun activateToolWindow(entry: ToolWindowEntry,
info: WindowInfoImpl,
autoFocusContents: Boolean = true,
source: ToolWindowEventSource? = null) {
internal fun activateToolWindow(
entry: ToolWindowEntry,
info: WindowInfoImpl,
autoFocusContents: Boolean = true,
source: ToolWindowEventSource? = null,
) {
LOG.debug { "activateToolWindow($entry)" }
if (isUnifiedToolWindowSizesEnabled()) {
@@ -727,11 +745,13 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
return info
}
private fun deactivateToolWindow(info: WindowInfoImpl,
entry: ToolWindowEntry,
dirtyMode: Boolean = false,
mutation: Mutation? = null,
source: ToolWindowEventSource? = null) {
private fun deactivateToolWindow(
info: WindowInfoImpl,
entry: ToolWindowEntry,
dirtyMode: Boolean = false,
mutation: Mutation? = null,
source: ToolWindowEventSource? = null,
) {
LOG.debug { "deactivateToolWindow(${info.id})" }
setHiddenState(info, entry, source)
@@ -857,11 +877,13 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
hideToolWindow(id = id, hideSide = hideSide, source = null)
}
open fun hideToolWindow(id: String,
hideSide: Boolean = false,
moveFocus: Boolean = true,
removeFromStripe: Boolean = false,
source: ToolWindowEventSource? = null) {
open fun hideToolWindow(
id: String,
hideSide: Boolean = false,
moveFocus: Boolean = true,
removeFromStripe: Boolean = false,
source: ToolWindowEventSource? = null,
) {
EDT.assertIsEdt()
val entry = idToEntry.get(id)!!
@@ -892,12 +914,14 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
revalidateStripeButtons()
}
private fun executeHide(entry: ToolWindowEntry,
info: WindowInfoImpl,
dirtyMode: Boolean,
hideSide: Boolean = false,
mutation: Mutation? = null,
source: ToolWindowEventSource? = null) {
private fun executeHide(
entry: ToolWindowEntry,
info: WindowInfoImpl,
dirtyMode: Boolean,
hideSide: Boolean = false,
mutation: Mutation? = null,
source: ToolWindowEventSource? = null,
) {
// hide and deactivate
deactivateToolWindow(info, entry, dirtyMode = dirtyMode, mutation = mutation, source = source)
@@ -950,10 +974,12 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
/**
* @param dirtyMode if `true` then all UI operations are performed in dirty mode.
*/
private fun showToolWindowImpl(entry: ToolWindowEntry,
toBeShownInfo: WindowInfoImpl,
dirtyMode: Boolean,
source: ToolWindowEventSource? = null): Boolean {
private fun showToolWindowImpl(
entry: ToolWindowEntry,
toBeShownInfo: WindowInfoImpl,
dirtyMode: Boolean,
source: ToolWindowEventSource? = null,
): Boolean {
if (!entry.toolWindow.isAvailable) {
return false
}
@@ -1069,10 +1095,12 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
return result.entry
}
internal fun registerToolWindow(preparedTask: PreparedRegisterToolWindowTask,
buttonManager: ToolWindowButtonManager,
layout: DesktopLayout,
ensureToolWindowActionRegistered: Boolean): RegisterToolWindowResult {
internal fun registerToolWindow(
preparedTask: PreparedRegisterToolWindowTask,
buttonManager: ToolWindowButtonManager,
layout: DesktopLayout,
ensureToolWindowActionRegistered: Boolean,
): RegisterToolWindowResult {
val task = preparedTask.task
LOG.debug { "registerToolWindow($task)" }
@@ -1525,7 +1553,7 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
}
}
fireStateChanged(MoreButtonUpdated)
fireStateChanged(ToolWindowManagerEventType.MoreButtonUpdated)
}
override fun setShowNames(value: Boolean) {
@@ -1538,7 +1566,7 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
}
}
fireStateChanged(ShowNames)
fireStateChanged(ToolWindowManagerEventType.ShowNames)
}
override fun setSideCustomWidth(toolbar: ToolWindowToolbar, width: Int) {
@@ -1551,7 +1579,7 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
}
}
fireStateChanged(SideCustomWidth)
fireStateChanged(ToolWindowManagerEventType.SideCustomWidth)
}
override fun invokeLater(runnable: Runnable) {
@@ -1563,7 +1591,7 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
}
override val focusManager: IdeFocusManager
get() = IdeFocusManager.getInstance(project)!!
get() = IdeFocusManager.getGlobalInstance()
override fun canShowNotification(toolWindowId: String): Boolean {
val readOnlyWindowInfo = idToEntry.get(toolWindowId)?.readOnlyWindowInfo
@@ -1862,13 +1890,15 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
}
}
private fun doSetAnchor(entry: ToolWindowEntry,
info: WindowInfoImpl,
paneId: String,
anchor: ToolWindowAnchor,
order: Int,
currentInfo: WindowInfo? = null,
layoutState: DesktopLayout?) {
private fun doSetAnchor(
entry: ToolWindowEntry,
info: WindowInfoImpl,
paneId: String,
anchor: ToolWindowAnchor,
order: Int,
currentInfo: WindowInfo? = null,
layoutState: DesktopLayout?,
) {
if (isNewUi && currentInfo != null) {
entry.removeStripeButton(currentInfo.anchor, currentInfo.isSplit)
}
@@ -1952,10 +1982,12 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
fireStateChanged(ToolWindowManagerEventType.SetSideToolAndAnchor, entry.toolWindow)
}
private fun hideIfNeededAndShowAfterTask(entry: ToolWindowEntry,
info: WindowInfoImpl,
source: ToolWindowEventSource? = null,
task: () -> Unit) {
private fun hideIfNeededAndShowAfterTask(
entry: ToolWindowEntry,
info: WindowInfoImpl,
source: ToolWindowEventSource? = null,
task: () -> Unit,
) {
val wasVisible = entry.readOnlyWindowInfo.isVisible
val wasFocused = entry.toolWindow.isActive
if (wasVisible) {
@@ -2359,7 +2391,7 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
layoutState.setUnifiedAnchorWeight(anchor, dockingAreaWeight)
LOG.debug { "Moved/resized tool window ${info.id}, updated weight=${toolWindowWeight}, docking area weight=${dockingAreaWeight}" }
}
fireStateChanged(MovedOrResized, toolWindow)
fireStateChanged(ToolWindowManagerEventType.MovedOrResized, toolWindow)
}
private fun dockingAreaComponentSizeCanBeTrusted(dockingAreaComponent: Component): Boolean {
@@ -2384,7 +2416,7 @@ open class ToolWindowManagerImpl @NonInjectable @TestOnly internal constructor(
private fun getAdjustedWeight(
toolWindowPane: ToolWindowPane,
anchor: ToolWindowAnchor,
component: Component
component: Component,
): Float {
val wholeSize = toolWindowPane.rootPane.size
return getAdjustedRatio(
@@ -2503,8 +2535,10 @@ private enum class KeyState {
WAITING, PRESSED, RELEASED, HOLD
}
private fun areAllModifiersPressed(@MagicConstant(flagsFromClass = InputEvent::class) modifiers: Int,
@MagicConstant(flagsFromClass = InputEvent::class) mask: Int): Boolean {
private fun areAllModifiersPressed(
@MagicConstant(flagsFromClass = InputEvent::class) modifiers: Int,
@MagicConstant(flagsFromClass = InputEvent::class) mask: Int,
): Boolean {
return (modifiers xor mask) == 0
}
@@ -2549,7 +2583,7 @@ private fun getActivateToolWindowVKsMask(): Int {
@Suppress("DEPRECATION")
baseModifiers = baseModifiers and (InputEvent.SHIFT_MASK or InputEvent.CTRL_MASK or InputEvent.META_MASK or InputEvent.ALT_MASK)
// If the keymap either doesn't define an ActivateProjectToolWindow shortcut,
// If the keymap either doesn't define an ActivateProjectToolWindow shortcut
// or defines it with multiple modifiers, fall back to the default behavior.
if (baseModifiers.countOneBits() == 1) {
return baseModifiers