diff --git a/platform/platform-impl/src/com/intellij/ide/ui/AntiFlickeringPanel.kt b/platform/platform-impl/src/com/intellij/ide/ui/AntiFlickeringPanel.kt new file mode 100644 index 000000000000..05209481cc72 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/ui/AntiFlickeringPanel.kt @@ -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 + } + } +} diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 25fba149d38c..df34128fd7c6 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -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 diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java index ad546839d39b..ae1251c384ca 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java @@ -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()); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java index bf2667f85fbd..7969d8d9327c 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java @@ -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