IDEA-206346 Implement a hack to reduce variables tree flickering: freeze repainting for some time after update started

It is under registry debugger.anti.flickering.delay. The solution is applied only if that key is not set to zero. Currently, this solution is breaking autoscroll to the selected element in the variable tree.

GitOrigin-RevId: fb5d505abfef53c977f50779a14d098a605ddd33
This commit is contained in:
Alexey Merkulov
2023-12-04 13:34:43 +00:00
committed by intellij-monorepo-bot
parent 6a59332dff
commit 0d67de8403
4 changed files with 93 additions and 1 deletions
@@ -0,0 +1,70 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.ui
import com.intellij.ide.ui.UISettings.Companion.setupAntialiasing
import com.intellij.ui.DirtyUI
import com.intellij.util.SingleAlarm
import com.intellij.util.ui.ImageUtil
import com.intellij.util.ui.UIUtil
import org.jetbrains.annotations.ApiStatus
import java.awt.Component
import java.awt.Dimension
import java.awt.Graphics
import java.awt.LayoutManager
import java.awt.image.BufferedImage
import javax.swing.JPanel
/** A hacky way to reduce flickering. */
@ApiStatus.Internal
class AntiFlickeringPanel(layout: LayoutManager?) : JPanel(layout) {
private var savedSelfieImage: BufferedImage? = null
private var savedSize: Dimension? = null
private var savedPreferredSize: Dimension? = null
fun freezePainting(delay: Int) {
isOpaque = true
savedSelfieImage = takeSelfie(this)
savedSize = size
savedPreferredSize = preferredSize
val alarm = SingleAlarm({
savedSelfieImage = null
savedSize = null
savedPreferredSize = null
isOpaque = false
revalidate()
repaint()
}, delay, null)
alarm.request()
}
override fun getSize(): Dimension {
return savedSize ?: super.getSize()
}
override fun getPreferredSize(): Dimension {
return savedPreferredSize ?: super.getPreferredSize()
}
@DirtyUI
override fun paint(g: Graphics) {
val image = savedSelfieImage
if (image != null) {
System.err.println("image")
UIUtil.drawImage(g, image, 0, 0, null)
return
}
super.paint(g)
}
companion object {
@JvmStatic
private fun takeSelfie(component: Component): BufferedImage {
val graphicsConfiguration = component.graphicsConfiguration
val image = ImageUtil.createImage(graphicsConfiguration, component.width, component.height, BufferedImage.TYPE_INT_ARGB)
setupAntialiasing(image.graphics)
component.paint(image.graphics)
return image
}
}
}
@@ -626,6 +626,9 @@ debugger.show.breakpoints.inline.even.trivial=false
debugger.show.breakpoints.inline.even.trivial.description=Show inline inlay for line breakpoint even if it's the only breakpoint
debugger.inlayRunToCursor.hover.area=4
debugger.inlayRunToCursor.hover.area.description=The size of hover area in the editor to show inlay Run-To-Cursor popup (counting in characters)
debugger.anti.flickering.delay=0
debugger.anti.flickering.delay.description=Use anti-flickering panel atop of debugger variable tree and freeze repainting on specified ms \
after stepping. Zero value means do not use anti-flickering panel. Need to restart debug after setting it to zero and back.
execution.java.always.debug=false
execution.java.always.debug.description=Always run java processes with the debug agent
@@ -2,6 +2,7 @@
package com.intellij.xdebugger.impl.frame;
import com.intellij.ide.dnd.DnDManager;
import com.intellij.ide.ui.AntiFlickeringPanel;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.event.SelectionEvent;
@@ -79,6 +80,13 @@ public abstract class XVariablesViewBase extends XDebugView {
protected void buildTreeAndRestoreState(@NotNull final XStackFrame stackFrame) {
XSourcePosition position = stackFrame.getSourcePosition();
XDebuggerTree tree = getTree();
Container treeParent = tree.getParent();
if (treeParent instanceof AntiFlickeringPanel antiFlickeringPanel) {
int delay = Registry.intValue("debugger.anti.flickering.delay", 0);
if (delay > 0) {
antiFlickeringPanel.freezePainting(delay);
}
}
tree.setSourcePosition(position);
createNewRootNode(stackFrame);
XVariablesView.InlineVariablesInfo.set(getSession(tree), new XVariablesView.InlineVariablesInfo());
@@ -5,10 +5,12 @@ import com.intellij.ide.dnd.DnDAction;
import com.intellij.ide.dnd.DnDDragStartBean;
import com.intellij.ide.dnd.DnDSource;
import com.intellij.ide.dnd.aware.DnDAwareTree;
import com.intellij.ide.ui.AntiFlickeringPanel;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.xdebugger.XDebuggerBundle;
import com.intellij.xdebugger.XSourcePosition;
@@ -31,7 +33,16 @@ public final class XDebuggerTreePanel implements DnDSource {
@NotNull @NonNls final String popupActionGroupId, @Nullable XValueMarkers<?, ?> markers) {
myTree = new XDebuggerTree(project, editorsProvider, sourcePosition, popupActionGroupId, markers);
myMainPanel = new JPanel(new BorderLayout());
myMainPanel.add(ScrollPaneFactory.createScrollPane(myTree), BorderLayout.CENTER);
Component content;
if (Registry.intValue("debugger.anti.flickering.delay", 0) > 0) {
AntiFlickeringPanel antiFlickeringPanel = new AntiFlickeringPanel(new BorderLayout());
antiFlickeringPanel.add(myTree);
content = antiFlickeringPanel;
}
else {
content = myTree;
}
myMainPanel.add(ScrollPaneFactory.createScrollPane(content), BorderLayout.CENTER);
Disposer.register(parentDisposable, myTree);
Disposer.register(parentDisposable, new Disposable() {
@Override