diff --git a/platform/platform-api/src/com/intellij/openapi/ui/DialogEarthquakeShaker.java b/platform/platform-api/src/com/intellij/openapi/ui/DialogEarthquakeShaker.java index b30fefa7ac3b..0b05a11bd5ca 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/DialogEarthquakeShaker.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/DialogEarthquakeShaker.java @@ -1,7 +1,6 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.openapi.ui; -import com.intellij.openapi.util.Disposer; import com.intellij.util.ui.Animator; import java.awt.*; @@ -10,31 +9,31 @@ import java.awt.*; * @author Konstantin Bulenkov */ public final class DialogEarthquakeShaker { - private final Window myWindow; - private Point myNaturalLocation; - private long myStartTime; + private final Window window; + private Point naturalLocation; + private long startTime; private DialogEarthquakeShaker(Window window) { - myWindow = window; + this.window = window; } public void startShake() { - myNaturalLocation = myWindow.getLocation(); - myStartTime = System.currentTimeMillis(); + naturalLocation = window.getLocation(); + startTime = System.currentTimeMillis(); new Animator("EarthQuake", 10, 70, true) { @Override public void paintNow(int frame, int totalFrames, int cycle) { - final long elapsed = System.currentTimeMillis() - myStartTime; + final long elapsed = System.currentTimeMillis() - startTime; final double waveOffset = (elapsed % 70) / 70d; final double angle = waveOffset * 2d * Math.PI; - final int shakenX = (int)((Math.sin(angle) * 10) + myNaturalLocation.x); - myWindow.setLocation(shakenX, myNaturalLocation.y); - myWindow.repaint(); + final int shakenX = (int)((Math.sin(angle) * 10) + naturalLocation.x); + window.setLocation(shakenX, naturalLocation.y); + window.repaint(); if (elapsed > 150) { suspend(); - myWindow.setLocation(myNaturalLocation); - myWindow.repaint(); - Disposer.dispose(this); + window.setLocation(naturalLocation); + window.repaint(); + dispose(); } } }.resume(); diff --git a/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.kt b/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.kt index b1af7f4d9375..f0ca18ba469f 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.kt +++ b/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.kt @@ -74,9 +74,9 @@ open class LoadingDecorator @JvmOverloads constructor( pane.repaint() }, ) - Disposer.register(parent, fadeOutAnimator) pane.add(content, JLayeredPane.DEFAULT_LAYER, 0) Disposer.register(parent) { + fadeOutAnimator.dispose() loadingLayer.progress.dispose() startRequestJob?.cancel() } diff --git a/platform/platform-api/src/com/intellij/ui/components/DefaultScrollBarUI.kt b/platform/platform-api/src/com/intellij/ui/components/DefaultScrollBarUI.kt index c9b659d927c6..f08cc7bd27d8 100644 --- a/platform/platform-api/src/com/intellij/ui/components/DefaultScrollBarUI.kt +++ b/platform/platform-api/src/com/intellij/ui/components/DefaultScrollBarUI.kt @@ -1,12 +1,19 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.ui.components +import com.intellij.codeWithMe.ClientId +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.application.asContextElement import com.intellij.openapi.util.Key import com.intellij.ui.ClientProperty import com.intellij.ui.scale.JBUIScale import com.intellij.util.MathUtil import com.intellij.util.ui.* import com.intellij.util.ui.UIUtil.ComponentStyle +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import org.jetbrains.annotations.ApiStatus import java.awt.* import java.awt.event.* @@ -28,22 +35,13 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( private val listener = Listener() private val scrollTimer = TimerUtil.createNamedTimer("ScrollBarThumbScrollTimer", 60, listener) - @JvmField - protected var scrollBar: JScrollBar? = null - - @JvmField - protected val myTrack: ScrollBarPainter.Track = ScrollBarPainter.Track({ scrollBar }) - @Suppress("LeakingThis") - @JvmField - protected val thumb: ScrollBarPainter.Thumb = createThumbPainter() + internal var installedState: DefaultScrollbarUiInstalledState? = null + private set private var isValueCached: Boolean = false private var cachedValue: Int = 0 private var oldValue: Int = 0 - @JvmField - protected var animationBehavior: ScrollBarAnimationBehavior? = null - companion object { @ApiStatus.Internal @JvmField @@ -64,53 +62,60 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( } } - protected open fun createWrapAnimationBehaviour(): ScrollBarAnimationBehavior { + internal open fun createWrapAnimationBehaviour(state: DefaultScrollbarUiInstalledState): ScrollBarAnimationBehavior { return ToggleableScrollBarAnimationBehaviorDecorator( - decoratedBehavior = createBaseAnimationBehavior(), - trackAnimator = myTrack.animator, - thumbAnimator = thumb.animator, + decoratedBehavior = createBaseAnimationBehavior(state), + trackAnimator = state.track.animator, + thumbAnimator = state.thumb.animator, ) } - protected open fun createThumbPainter(): ScrollBarPainter.Thumb { - return ScrollBarPainter.Thumb({ scrollBar }, false) + internal open fun createThumbPainter(state: DefaultScrollbarUiInstalledState): ScrollBarPainter.Thumb { + return ScrollBarPainter.Thumb({ state.scrollBar }, false, state.coroutineScope) } - protected open fun createBaseAnimationBehavior(): ScrollBarAnimationBehavior { - return DefaultScrollBarAnimationBehavior(trackAnimator = myTrack.animator, thumbAnimator = thumb.animator) + internal open fun createBaseAnimationBehavior(state: DefaultScrollbarUiInstalledState): ScrollBarAnimationBehavior { + return DefaultScrollBarAnimationBehavior(trackAnimator = state.track.animator, thumbAnimator = state.thumb.animator) } - private fun getEffectiveThickness(): Int = scale(if (scrollBar == null || isOpaque(scrollBar!!)) thickness else thicknessMax) + private fun getEffectiveThickness(): Int { + val scrollBar = installedState?.scrollBar + return scale(if (scrollBar == null || isOpaque(scrollBar)) thickness else thicknessMax) + } - private fun getMinimalThickness(): Int = scale(if (scrollBar == null || isOpaque(scrollBar!!)) thickness else thicknessMin) + private fun getMinimalThickness(): Int { + val scrollBar = installedState?.scrollBar + return scale(if (scrollBar == null || isOpaque(scrollBar)) thickness else thicknessMin) + } fun toggle(isOn: Boolean) { - if (animationBehavior != null) { - animationBehavior!!.onToggle(isOn) - } + installedState?.animationBehavior?.onToggle(isOn) } open fun isAbsolutePositioning(event: MouseEvent): Boolean = SwingUtilities.isMiddleMouseButton(event) - open fun isTrackClickable(): Boolean = isOpaque(scrollBar!!) || (animationBehavior != null && animationBehavior!!.trackFrame > 0) + open fun isTrackClickable(): Boolean { + val state = installedState ?: return false + return isOpaque(state.scrollBar) || state.animationBehavior.trackFrame > 0 + } open val isTrackExpandable: Boolean get() = false fun isTrackContains(x: Int, y: Int): Boolean { - return myTrack.bounds.contains(x, y) + return installedState!!.track.bounds.contains(x, y) } fun isThumbContains(x: Int, y: Int): Boolean { - return thumb.bounds.contains(x, y) + return installedState!!.thumb.bounds.contains(x, y) } protected open fun paintTrack(g: Graphics2D, c: JComponent) { - paint(myTrack, g, c, false) + paint(p = installedState!!.track, g = g, c = c, small = false) } - protected open fun paintThumb(g: Graphics2D, c: JComponent) { - paint(thumb, g, c, ScrollSettings.isThumbSmallIfOpaque.invoke() && isOpaque(c)) + internal open fun paintThumb(g: Graphics2D, c: JComponent, state: DefaultScrollbarUiInstalledState) { + paint(p = installedState!!.thumb, g = g, c = c, small = ScrollSettings.isThumbSmallIfOpaque.invoke() && isOpaque(c)) } fun paint(p: ScrollBarPainter, g: Graphics2D, c: JComponent?, small: Boolean) { @@ -149,24 +154,29 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( } private fun getTrackOffset(offset: Int): Int { - if (!isTrackExpandable) return offset - val value: Float = if (animationBehavior == null) 0f else animationBehavior!!.trackFrame - if (value <= 0) return offset - if (value >= 1) return 0 - return (.5f + offset * (1 - value)).toInt() + if (!isTrackExpandable) { + return offset + } + + val value = installedState?.animationBehavior?.trackFrame ?: 0f + return when { + value <= 0 -> offset + value >= 1 -> 0 + else -> (.5f + offset * (1 - value)).toInt() + } } fun repaint() { - if (scrollBar != null) scrollBar!!.repaint() + installedState?.scrollBar?.repaint() } fun repaint(x: Int, y: Int, width: Int, height: Int) { - if (scrollBar != null) scrollBar!!.repaint(x, y, width, height) + installedState?.scrollBar?.repaint(x, y, width, height) } private fun scale(value: Int): Int { val scaledValue = JBUIScale.scale(value) - return when (UIUtil.getComponentStyle(scrollBar)) { + return when (UIUtil.getComponentStyle(installedState!!.scrollBar)) { ComponentStyle.LARGE -> (scaledValue * 1.15).toInt() ComponentStyle.SMALL -> (scaledValue * 0.857).toInt() ComponentStyle.MINI -> (scaledValue * 0.714).toInt() @@ -175,39 +185,42 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( } override fun installUI(c: JComponent) { - animationBehavior = createWrapAnimationBehaviour() + val scrollBar = c as JScrollBar + installedState = DefaultScrollbarUiInstalledState(ui = this, scrollBar = scrollBar) - scrollBar = c as JScrollBar ScrollBarPainter.setBackground(c) - scrollBar!!.isOpaque = false - scrollBar!!.isFocusable = false - scrollBar!!.addMouseListener(listener) - scrollBar!!.addMouseMotionListener(listener) - scrollBar!!.model.addChangeListener(listener) - scrollBar!!.addPropertyChangeListener(listener) - scrollBar!!.addFocusListener(listener) + scrollBar.isOpaque = false + scrollBar.isFocusable = false + scrollBar.addMouseListener(listener) + scrollBar.addMouseMotionListener(listener) + scrollBar.model.addChangeListener(listener) + scrollBar.addPropertyChangeListener(listener) + scrollBar.addFocusListener(listener) scrollTimer.initialDelay = 300 } override fun uninstallUI(c: JComponent) { - checkNotNull(animationBehavior) - animationBehavior!!.onUninstall() - animationBehavior = null + val state = installedState ?: return + state.coroutineScope.cancel() + installedState = null + + state.animationBehavior.onUninstall() scrollTimer.stop() - scrollBar!!.removeFocusListener(listener) - scrollBar!!.removePropertyChangeListener(listener) - scrollBar!!.model.removeChangeListener(listener) - scrollBar!!.removeMouseMotionListener(listener) - scrollBar!!.removeMouseListener(listener) - scrollBar!!.foreground = null - scrollBar!!.background = null - scrollBar = null + val scrollBar = state.scrollBar + scrollBar.removeFocusListener(listener) + scrollBar.removePropertyChangeListener(listener) + scrollBar.model.removeChangeListener(listener) + scrollBar.removeMouseMotionListener(listener) + scrollBar.removeMouseListener(listener) + scrollBar.foreground = null + scrollBar.background = null } override fun getPreferredSize(c: JComponent): Dimension { - val thickness: Int = getEffectiveThickness() - val alignment: JBScrollPane.Alignment = JBScrollPane.Alignment.get(c) + val scrollBar = installedState!!.scrollBar + val thickness = getEffectiveThickness() + val alignment = JBScrollPane.Alignment.get(c) val preferred = Dimension(thickness, thickness) if (alignment == JBScrollPane.Alignment.LEFT || alignment == JBScrollPane.Alignment.RIGHT) { preferred.height += preferred.height @@ -223,100 +236,115 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( } override fun paint(g: Graphics, c: JComponent) { - val alignment: JBScrollPane.Alignment? = JBScrollPane.Alignment.get(c) - if (alignment != null && g is Graphics2D) { - val background: Color? = if (!isOpaque(c)) null else c.background - if (background != null) { - g.setColor(background) - g.fillRect(0, 0, c.width, c.height) - } - val bounds = Rectangle(c.width, c.height) - JBInsets.removeFrom(bounds, c.insets) - // process an area before the track - val leading: Component? = ClientProperty.get(c, LEADING) - if (leading != null) { - if (alignment == JBScrollPane.Alignment.LEFT || alignment == JBScrollPane.Alignment.RIGHT) { - val size: Int = leading.preferredSize.height - leading.setBounds(bounds.x, bounds.y, bounds.width, size) - bounds.height -= size - bounds.y += size - } - else { - val size: Int = leading.preferredSize.width - leading.setBounds(bounds.x, bounds.y, size, bounds.height) - bounds.width -= size - bounds.x += size - } - } - // process an area after the track - val trailing: Component? = ClientProperty.get(c, TRAILING) - if (trailing != null) { - if (alignment == JBScrollPane.Alignment.LEFT || alignment == JBScrollPane.Alignment.RIGHT) { - val size: Int = trailing.preferredSize.height - bounds.height -= size - trailing.setBounds(bounds.x, bounds.y + bounds.height, bounds.width, size) - } - else { - val size: Int = trailing.preferredSize.width - bounds.width -= size - trailing.setBounds(bounds.x + bounds.width, bounds.y, size, bounds.height) - } - } - // do not set track size bigger that expected thickness + val alignment = JBScrollPane.Alignment.get(c) + if (alignment == null || g !is Graphics2D) { + return + } + + val background: Color? = if (isOpaque(c)) c.background else null + if (background != null) { + g.setColor(background) + g.fillRect(0, 0, c.width, c.height) + } + + val bounds = Rectangle(c.width, c.height) + JBInsets.removeFrom(bounds, c.insets) + // process an area before the track + val leading: Component? = ClientProperty.get(c, LEADING) + if (leading != null) { if (alignment == JBScrollPane.Alignment.LEFT || alignment == JBScrollPane.Alignment.RIGHT) { - val offset: Int = bounds.width - getEffectiveThickness() - if (offset > 0) { - bounds.width -= offset - if (alignment == JBScrollPane.Alignment.RIGHT) bounds.x += offset - } + val size = leading.preferredSize.height + leading.setBounds(bounds.x, bounds.y, bounds.width, size) + bounds.height -= size + bounds.y += size } else { - val offset: Int = bounds.height - getEffectiveThickness() - if (offset > 0) { - bounds.height -= offset - if (alignment == JBScrollPane.Alignment.BOTTOM) bounds.y += offset - } + val size: Int = leading.preferredSize.width + leading.setBounds(bounds.x, bounds.y, size, bounds.height) + bounds.width -= size + bounds.x += size } - val animate: Boolean = myTrack.bounds != bounds // animate thumb on resize - if (animate) myTrack.bounds.bounds = bounds - updateThumbBounds(animate) - paintTrack(g, c) - // process additional drawing on the track - val track: RegionPainter? = ClientProperty.get(c, JBScrollBar.TRACK) - if (track != null && myTrack.bounds.width > 0 && myTrack.bounds.height > 0) { - track.paint(g, myTrack.bounds.x, myTrack.bounds.y, myTrack.bounds.width, myTrack.bounds.height, null) + } + + // process an area after the track + val trailing: Component? = ClientProperty.get(c, TRAILING) + if (trailing != null) { + if (alignment == JBScrollPane.Alignment.LEFT || alignment == JBScrollPane.Alignment.RIGHT) { + val size: Int = trailing.preferredSize.height + bounds.height -= size + trailing.setBounds(bounds.x, bounds.y + bounds.height, bounds.width, size) } - // process drawing the thumb - if (thumb.bounds.width > 0 && thumb.bounds.height > 0) { - paintThumb(g, c) + else { + val size: Int = trailing.preferredSize.width + bounds.width -= size + trailing.setBounds(bounds.x + bounds.width, bounds.y, size, bounds.height) } } + + // do not set track size bigger that expected thickness + if (alignment == JBScrollPane.Alignment.LEFT || alignment == JBScrollPane.Alignment.RIGHT) { + val offset: Int = bounds.width - getEffectiveThickness() + if (offset > 0) { + bounds.width -= offset + if (alignment == JBScrollPane.Alignment.RIGHT) bounds.x += offset + } + } + else { + val offset: Int = bounds.height - getEffectiveThickness() + if (offset > 0) { + bounds.height -= offset + if (alignment == JBScrollPane.Alignment.BOTTOM) bounds.y += offset + } + } + + val state = installedState!! + val track = state.track + // animate thumb on resize + val animate = track.bounds != bounds + if (animate) { + track.bounds.bounds = bounds + } + updateThumbBounds(animate) + paintTrack(g, c) + // process additional drawing on the track + val trackPainter: RegionPainter? = ClientProperty.get(c, JBScrollBar.TRACK) + if (trackPainter != null && track.bounds.width > 0 && track.bounds.height > 0) { + trackPainter.paint(g, track.bounds.x, track.bounds.y, track.bounds.width, track.bounds.height, null) + } + + // process drawing the thumb + if (state.thumb.bounds.width > 0 && state.thumb.bounds.height > 0) { + paintThumb(g, c, state) + } } private fun updateThumbBounds(animate: Boolean) { var animate = animate var value = 0 - val min: Int = scrollBar!!.minimum - val max: Int = scrollBar!!.maximum + val state = installedState!! + val scrollBar = state.scrollBar + val min: Int = scrollBar.minimum + val max: Int = scrollBar.maximum val range: Int = max - min if (range <= 0) { - thumb.bounds.setBounds(0, 0, 0, 0) + state.thumb.bounds.setBounds(0, 0, 0, 0) } - else if (Adjustable.VERTICAL == scrollBar!!.orientation) { - val extent = scrollBar!!.visibleAmount + else if (Adjustable.VERTICAL == scrollBar.orientation) { + val extent = scrollBar.visibleAmount + val track = state.track val height = max( - convert(newRange = myTrack.bounds.height.toDouble(), oldValue = extent.toDouble(), oldRange = range.toDouble()).toDouble(), + convert(newRange = track.bounds.height.toDouble(), oldValue = extent.toDouble(), oldRange = range.toDouble()).toDouble(), (2 * getEffectiveThickness()).toDouble(), ).toInt() - if (myTrack.bounds.height <= height) { - thumb.bounds.setBounds(0, 0, 0, 0) + if (track.bounds.height <= height) { + state.thumb.bounds.setBounds(0, 0, 0, 0) } else { value = this.value - val maxY = myTrack.bounds.y + myTrack.bounds.height - height + val maxY = track.bounds.y + track.bounds.height - height val y = if ((value < max - extent)) { convert( - newRange = (myTrack.bounds.height - height).toDouble(), + newRange = (track.bounds.height - height).toDouble(), oldValue = (value - min).toDouble(), oldRange = (range - extent).toDouble(), ) @@ -324,45 +352,46 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( else { maxY } - thumb.bounds.setBounds(myTrack.bounds.x, adjust(y, myTrack.bounds.y, maxY), myTrack.bounds.width, height) + state.thumb.bounds.setBounds(track.bounds.x, adjust(y, track.bounds.y, maxY), track.bounds.width, height) // animate thumb on move animate = animate or (oldValue != value) } } else { - val extent: Int = scrollBar!!.visibleAmount + val track = state.track + val extent = scrollBar.visibleAmount val width: Int = max( convert( - newRange = myTrack.bounds.width.toDouble(), + newRange = track.bounds.width.toDouble(), oldValue = extent.toDouble(), oldRange = range.toDouble(), ).toDouble(), (2 * getEffectiveThickness()).toDouble(), ).toInt() - if (myTrack.bounds.width <= width) { - thumb.bounds.setBounds(0, 0, 0, 0) + if (track.bounds.width <= width) { + state.thumb.bounds.setBounds(0, 0, 0, 0) } else { value = this.value - val maxX: Int = myTrack.bounds.x + myTrack.bounds.width - width - var x: Int = if ((value < max - extent)) convert((myTrack.bounds.width - width).toDouble(), (value - min).toDouble(), + val maxX: Int = track.bounds.x + track.bounds.width - width + var x: Int = if ((value < max - extent)) convert((track.bounds.width - width).toDouble(), (value - min).toDouble(), (range - extent).toDouble()) else maxX - if (!scrollBar!!.componentOrientation.isLeftToRight) { - x = myTrack.bounds.x - x + maxX + if (!scrollBar.componentOrientation.isLeftToRight) { + x = track.bounds.x - x + maxX } - thumb.bounds.setBounds(adjust(x, myTrack.bounds.x, maxX), myTrack.bounds.y, width, myTrack.bounds.height) + state.thumb.bounds.setBounds(adjust(x, track.bounds.x, maxX), track.bounds.y, width, track.bounds.height) animate = animate or (oldValue != value) // animate thumb on move } } oldValue = value - if (animate && animationBehavior != null) { - animationBehavior!!.onThumbMove() + if (animate) { + state.animationBehavior.onThumbMove() } } private val value: Int - get() = if (isValueCached) cachedValue else scrollBar!!.value + get() = if (isValueCached) cachedValue else installedState!!.scrollBar.value private inner class Listener : MouseAdapter(), ActionListener, FocusListener, ChangeListener, PropertyChangeListener { private var myOffset: Int = 0 @@ -375,12 +404,13 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( fun updateMouse(x: Int, y: Int) { if (isTrackContains(x, y)) { - if (!isOverTrack && animationBehavior != null) { - animationBehavior!!.onTrackHover(true.also { isOverTrack = it }) + val animationBehavior = installedState?.animationBehavior ?: return + if (!isOverTrack) { + animationBehavior.onTrackHover(true.also { isOverTrack = true }) } val hover: Boolean = isThumbContains(x, y) - if (isOverThumb != hover && animationBehavior != null) { - animationBehavior!!.onThumbHover(hover.also { isOverThumb = it }) + if (isOverThumb != hover) { + animationBehavior.onThumbHover(hover.also { isOverThumb = it }) } } else { @@ -389,11 +419,12 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( } fun updateMouseExit() { - if (isOverThumb && animationBehavior != null) { - animationBehavior!!.onThumbHover(false.also { isOverThumb = it }) + val animationBehavior = installedState?.animationBehavior ?: return + if (isOverThumb) { + animationBehavior.onThumbHover(false.also { isOverThumb = false }) } - if (isOverTrack && animationBehavior != null) { - animationBehavior!!.onTrackHover(false.also { isOverTrack = it }) + if (isOverTrack) { + animationBehavior.onTrackHover(false.also { isOverTrack = false }) } } @@ -403,36 +434,39 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( } // redispatch current event to the view - val parent: Container = scrollBar!!.parent - if (parent is JScrollPane) { - val view: Component? = parent.viewport.view - if (view != null) { - val point: Point = event.locationOnScreen - SwingUtilities.convertPointFromScreen(point, view) - val target: Component = SwingUtilities.getDeepestComponentAt(view, point.x, point.y) - MouseEventAdapter.redispatch(event, target) - } + val parent = installedState?.scrollBar?.parent + val view = (parent as? JScrollPane)?.viewport?.view + if (view != null) { + val point = event.locationOnScreen + SwingUtilities.convertPointFromScreen(point, view) + MouseEventAdapter.redispatch(event, SwingUtilities.getDeepestComponentAt(view, point.x, point.y)) } return true } override fun mouseClicked(e: MouseEvent) { - if (scrollBar != null && scrollBar!!.isEnabled) redispatchIfTrackNotClickable(e) + val scrollBar = installedState?.scrollBar ?: return + if (scrollBar.isEnabled) { + redispatchIfTrackNotClickable(e) + } } override fun mousePressed(event: MouseEvent) { - if (scrollBar == null || !scrollBar!!.isEnabled) return - if (redispatchIfTrackNotClickable(event)) return - if (SwingUtilities.isRightMouseButton(event)) return + val scrollBar = installedState?.scrollBar ?: return + if (!scrollBar.isEnabled || redispatchIfTrackNotClickable(event) || SwingUtilities.isRightMouseButton(event)) { + return + } isValueCached = true - cachedValue = scrollBar!!.value - scrollBar!!.valueIsAdjusting = true + cachedValue = scrollBar.value + scrollBar.valueIsAdjusting = true myMouseX = event.x myMouseY = event.y - val vertical: Boolean = Adjustable.VERTICAL == scrollBar!!.orientation + val thumb = installedState!!.thumb + + val vertical = Adjustable.VERTICAL == scrollBar.orientation if (isThumbContains(myMouseX, myMouseY)) { // pressed on the thumb myOffset = if (vertical) (myMouseY - thumb.bounds.y) else (myMouseX - thumb.bounds.x) @@ -448,14 +482,14 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( else { scrollTimer.stop() isDragging = false - if (Adjustable.VERTICAL == scrollBar!!.orientation) { - val y: Int = if (thumb.bounds.isEmpty) scrollBar!!.height / 2 else thumb.bounds.y + if (Adjustable.VERTICAL == scrollBar.orientation) { + val y = if (thumb.bounds.isEmpty) scrollBar.height / 2 else thumb.bounds.y isReversed = myMouseY < y } else { - val x: Int = if (thumb.bounds.isEmpty) scrollBar!!.width / 2 else thumb.bounds.x + val x = if (thumb.bounds.isEmpty) scrollBar.width / 2 else thumb.bounds.x isReversed = myMouseX < x - if (!scrollBar!!.componentOrientation.isLeftToRight) { + if (!scrollBar.componentOrientation.isLeftToRight) { isReversed = !isReversed } } @@ -466,22 +500,41 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( } override fun mouseReleased(event: MouseEvent) { - if (isDragging) updateMouse(event.x, event.y) - if (scrollBar == null || !scrollBar!!.isEnabled) return - scrollBar!!.valueIsAdjusting = false - if (redispatchIfTrackNotClickable(event)) return - if (SwingUtilities.isRightMouseButton(event)) return + if (isDragging) { + updateMouse(event.x, event.y) + } + + val scrollBar = installedState?.scrollBar ?: return + if (!scrollBar.isEnabled) { + return + } + + scrollBar.valueIsAdjusting = false + if (redispatchIfTrackNotClickable(event)) { + return + } + if (SwingUtilities.isRightMouseButton(event)) { + return + } + isDragging = false myOffset = 0 scrollTimer.stop() isValueCached = true - cachedValue = scrollBar!!.value + cachedValue = scrollBar.value repaint() } override fun mouseDragged(event: MouseEvent) { - if (scrollBar == null || !scrollBar!!.isEnabled) return - if (thumb.bounds.isEmpty || SwingUtilities.isRightMouseButton(event)) return + val scrollBar = installedState?.scrollBar ?: return + if (!scrollBar.isEnabled) { + return + } + + if (installedState!!.thumb.bounds.isEmpty || SwingUtilities.isRightMouseButton(event)) { + return + } + if (isDragging) { setValueFrom(event) } @@ -494,28 +547,39 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( } override fun mouseMoved(event: MouseEvent) { - if (scrollBar == null || !scrollBar!!.isEnabled) return - if (!isDragging) updateMouse(event.x, event.y) + val scrollBar = installedState?.scrollBar ?: return + if (!scrollBar.isEnabled) { + return + } + if (!isDragging) { + updateMouse(event.x, event.y) + } } override fun mouseExited(event: MouseEvent) { - if (scrollBar == null || !scrollBar!!.isEnabled) return - if (!isDragging) updateMouseExit() + val scrollBar = installedState?.scrollBar ?: return + if (!scrollBar.isEnabled) { + return + } + if (!isDragging) { + updateMouseExit() + } } override fun actionPerformed(event: ActionEvent) { + val scrollBar = installedState?.scrollBar if (scrollBar == null) { scrollTimer.stop() } else { scroll(isReversed) - if (!thumb.bounds.isEmpty) { + if (!installedState!!.thumb.bounds.isEmpty) { if (if (isReversed) !isMouseBeforeThumb() else !isMouseAfterThumb()) { scrollTimer.stop() } } - val value: Int = scrollBar!!.value - if (if (isReversed) value <= scrollBar!!.minimum else value >= scrollBar!!.maximum - scrollBar!!.visibleAmount) { + val value: Int = scrollBar.value + if (if (isReversed) value <= scrollBar.minimum else value >= scrollBar.maximum - scrollBar.visibleAmount) { scrollTimer.stop() } } @@ -548,11 +612,12 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( repaint() } if ("opaque" == name || "visible" == name) { - if (animationBehavior != null) { - animationBehavior!!.onReset() + val state = installedState + if (state != null) { + state.animationBehavior.onReset() + state.track.bounds.setBounds(0, 0, 0, 0) + state.thumb.bounds.setBounds(0, 0, 0, 0) } - myTrack.bounds.setBounds(0, 0, 0, 0) - thumb.bounds.setBounds(0, 0, 0, 0) } } @@ -563,41 +628,41 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( val thumbMin: Int val thumbMax: Int val thumbPos: Int - if (Adjustable.VERTICAL == scrollBar!!.orientation) { - thumbMin = myTrack.bounds.y - thumbMax = myTrack.bounds.y + myTrack.bounds.height - thumb.bounds.height + val state = installedState ?: return + val track = state.track + val thumb = state.thumb + val scrollBar = state.scrollBar + if (Adjustable.VERTICAL == scrollBar.orientation) { + thumbMin = track.bounds.y + thumbMax = track.bounds.y + track.bounds.height - thumb.bounds.height thumbPos = MathUtil.clamp(y - myOffset, thumbMin, thumbMax) if (thumb.bounds.y != thumbPos) { val minY: Int = min(thumb.bounds.y.toDouble(), thumbPos.toDouble()).toInt() val maxY: Int = (max(thumb.bounds.y.toDouble(), thumbPos.toDouble()) + thumb.bounds.height).toInt() thumb.bounds.y = thumbPos - if (animationBehavior != null) { - animationBehavior!!.onThumbMove() - } + state.animationBehavior.onThumbMove() repaint(thumb.bounds.x, minY, thumb.bounds.width, maxY - minY) } } else { - thumbMin = myTrack.bounds.x - thumbMax = myTrack.bounds.x + myTrack.bounds.width - thumb.bounds.width + thumbMin = track.bounds.x + thumbMax = track.bounds.x + track.bounds.width - thumb.bounds.width thumbPos = MathUtil.clamp(x - myOffset, thumbMin, thumbMax) if (thumb.bounds.x != thumbPos) { val minX: Int = min(thumb.bounds.x.toDouble(), thumbPos.toDouble()).toInt() val maxX: Int = (max(thumb.bounds.x.toDouble(), thumbPos.toDouble()) + thumb.bounds.width).toInt() thumb.bounds.x = thumbPos - if (animationBehavior != null) { - animationBehavior!!.onThumbMove() - } + state.animationBehavior.onThumbMove() repaint(minX, thumb.bounds.y, maxX - minX, thumb.bounds.height) } } - val valueMin: Int = scrollBar!!.minimum - val valueMax: Int = scrollBar!!.maximum - scrollBar!!.visibleAmount + val valueMin = scrollBar.minimum + val valueMax = scrollBar.maximum - scrollBar.visibleAmount // If the thumb has reached the end of the scrollbar, then set the value to its maximum. // Otherwise, compute the value as accurately as possible. - val isDefaultOrientation: Boolean = Adjustable.VERTICAL == scrollBar!!.orientation || scrollBar!!.componentOrientation.isLeftToRight + val isDefaultOrientation: Boolean = Adjustable.VERTICAL == scrollBar.orientation || scrollBar.componentOrientation.isLeftToRight if (thumbPos == thumbMax) { - scrollBar!!.value = if (isDefaultOrientation) valueMax else valueMin + scrollBar.value = if (isDefaultOrientation) valueMax else valueMin } else { val valueRange = valueMax - valueMin @@ -605,9 +670,11 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( val thumbValue = if (isDefaultOrientation) thumbPos - thumbMin else thumbMax - thumbPos isValueCached = true cachedValue = valueMin + convert(valueRange.toDouble(), thumbValue.toDouble(), thumbRange.toDouble()) - scrollBar!!.value = cachedValue + scrollBar.value = cachedValue + } + if (!isDragging) { + updateMouse(x, y) } - if (!isDragging) updateMouse(x, y) } fun startScrollTimerIfNecessary() { @@ -620,56 +687,75 @@ open class DefaultScrollBarUI @JvmOverloads internal constructor( fun isMouseBeforeThumb(): Boolean { return when { - Adjustable.VERTICAL == scrollBar!!.orientation -> isMouseOnTop() - scrollBar!!.componentOrientation.isLeftToRight -> isMouseOnLeft() + Adjustable.VERTICAL == installedState!!.scrollBar.orientation -> isMouseOnTop() + installedState!!.scrollBar.componentOrientation.isLeftToRight -> isMouseOnLeft() else -> isMouseOnRight() } } fun isMouseAfterThumb(): Boolean { return when { - Adjustable.VERTICAL == scrollBar!!.orientation -> isMouseOnBottom() - scrollBar!!.componentOrientation.isLeftToRight -> isMouseOnRight() + Adjustable.VERTICAL == installedState!!.scrollBar.orientation -> isMouseOnBottom() + installedState!!.scrollBar.componentOrientation.isLeftToRight -> isMouseOnRight() else -> isMouseOnLeft() } } - fun isMouseOnTop(): Boolean { - return myMouseY < thumb.bounds.y - } + fun isMouseOnTop(): Boolean = myMouseY < installedState!!.thumb.bounds.y - fun isMouseOnLeft(): Boolean { - return myMouseX < thumb.bounds.x - } + fun isMouseOnLeft(): Boolean = myMouseX < installedState!!.thumb.bounds.x fun isMouseOnRight(): Boolean { + val thumb = installedState!!.thumb return myMouseX > thumb.bounds.x + thumb.bounds.width } fun isMouseOnBottom(): Boolean { + val thumb = installedState!!.thumb return myMouseY > thumb.bounds.y + thumb.bounds.height } fun scroll(reversed: Boolean) { - var delta: Int = scrollBar!!.getBlockIncrement(if (reversed) -1 else 1) - if (reversed) delta = -delta + val scrollBar = installedState!!.scrollBar + var delta: Int = scrollBar.getBlockIncrement(if (reversed) -1 else 1) + if (reversed) { + delta = -delta + } - val oldValue: Int = scrollBar!!.value - var newValue: Int = oldValue + delta + val oldValue = scrollBar.value + var newValue = oldValue + delta if (delta > 0 && newValue < oldValue) { - newValue = scrollBar!!.maximum + newValue = scrollBar.maximum } else if (delta < 0 && newValue > oldValue) { - newValue = scrollBar!!.minimum + newValue = scrollBar.minimum } + if (oldValue != newValue) { - scrollBar!!.value = newValue + scrollBar.value = newValue } } } } +internal class DefaultScrollbarUiInstalledState(ui: DefaultScrollBarUI, @JvmField val scrollBar: JScrollBar) { + @JvmField + val animationBehavior: ScrollBarAnimationBehavior = ui.createWrapAnimationBehaviour(this) + + @JvmField + val coroutineScope = CoroutineScope(SupervisorJob() + + Dispatchers.Default + + ModalityState.defaultModalityState().asContextElement() + + ClientId.coroutineContext()) + + @JvmField + val track: ScrollBarPainter.Track = ScrollBarPainter.Track({ scrollBar }, coroutineScope) + + @JvmField + val thumb: ScrollBarPainter.Thumb = ui.createThumbPainter(this) +} + private fun addPreferredWidth(preferred: Dimension, component: Component?) { if (component != null) { val size: Dimension = component.preferredSize diff --git a/platform/platform-api/src/com/intellij/ui/components/MacScrollBarUI.kt b/platform/platform-api/src/com/intellij/ui/components/MacScrollBarUI.kt index 5b0bb7ce10b5..209c050454c5 100644 --- a/platform/platform-api/src/com/intellij/ui/components/MacScrollBarUI.kt +++ b/platform/platform-api/src/com/intellij/ui/components/MacScrollBarUI.kt @@ -80,8 +80,7 @@ internal open class MacScrollBarUI : DefaultScrollBarUI { if (event != null && MouseEvent.MOUSE_MOVED == event.id) { val source = event.source if (source is Component) { - val pane = ComponentUtil.getParentOfType( - JScrollPane::class.java as Class, source) + val pane = ComponentUtil.getParentOfType(JScrollPane::class.java, source) if (pane != null) { pauseThumbAnimation(pane.horizontalScrollBar) pauseThumbAnimation(pane.verticalScrollBar) @@ -97,42 +96,47 @@ internal open class MacScrollBarUI : DefaultScrollBarUI { */ private fun pauseThumbAnimation(bar: JScrollBar?) { val ui = bar?.ui - if (ui is MacScrollBarUI && 0 < ui.animationBehavior!!.thumbFrame) { - ui.animationBehavior!!.onThumbMove() + if (ui is MacScrollBarUI) { + val animationBehavior = ui.installedState!!.animationBehavior + if (0 < animationBehavior.thumbFrame) { + animationBehavior.onThumbMove() + } } } }) } - override fun createBaseAnimationBehavior(): ScrollBarAnimationBehavior { + override fun createBaseAnimationBehavior(state: DefaultScrollbarUiInstalledState): ScrollBarAnimationBehavior { return MacScrollBarAnimationBehavior( - scrollBarComputable = { scrollBar }, - trackAnimator = myTrack.animator, - thumbAnimator = thumb.animator, + scrollBarComputable = { state.scrollBar }, + trackAnimator = state.track.animator, + thumbAnimator = state.thumb.animator, ) } override fun isAbsolutePositioning(event: MouseEvent): Boolean = Behavior.JumpToSpot == Behavior.CURRENT_BEHAVIOR() override fun isTrackClickable(): Boolean { - return isOpaque(scrollBar!!) || (animationBehavior!!.trackFrame > 0 && animationBehavior!!.thumbFrame > 0) + val state = installedState ?: return false + return isOpaque(state.scrollBar) || (state.animationBehavior.trackFrame > 0 && state.animationBehavior.thumbFrame > 0) } override val isTrackExpandable: Boolean - get() = !isOpaque(scrollBar!!) + get() = !isOpaque(installedState!!.scrollBar) override fun paintTrack(g: Graphics2D, c: JComponent) { - if (animationBehavior!!.trackFrame > 0 && animationBehavior!!.thumbFrame > 0 || isOpaque(c)) { + val animationBehavior = installedState!!.animationBehavior + if (animationBehavior.trackFrame > 0 && animationBehavior.thumbFrame > 0 || isOpaque(c)) { super.paintTrack(g, c) } } - public override fun paintThumb(g: Graphics2D, c: JComponent) { + public override fun paintThumb(g: Graphics2D, c: JComponent, state: DefaultScrollbarUiInstalledState) { if (isOpaque(c)) { - paint(p = thumb, g = g, c = c, small = true) + paint(p = state.thumb, g = g, c = c, small = true) } - else if (animationBehavior!!.thumbFrame > 0) { - paint(p = thumb, g = g, c = c, small = false) + else if (state.animationBehavior.thumbFrame > 0) { + paint(p = state.thumb, g = g, c = c, small = false) } } @@ -152,13 +156,12 @@ internal open class MacScrollBarUI : DefaultScrollBarUI { } protected open fun updateStyle(style: MacScrollbarStyle?) { - val scrollBar = scrollBar - if (scrollBar != null) { - scrollBar.isOpaque = style != MacScrollbarStyle.Overlay - scrollBar.revalidate() - scrollBar.repaint() - animationBehavior?.onThumbMove() - } + val state = installedState + val scrollBar = state?.scrollBar ?: return + scrollBar.isOpaque = style != MacScrollbarStyle.Overlay + scrollBar.revalidate() + scrollBar.repaint() + state.animationBehavior.onThumbMove() } } diff --git a/platform/platform-api/src/com/intellij/ui/components/ScrollBarPainter.java b/platform/platform-api/src/com/intellij/ui/components/ScrollBarPainter.java index a39aa3f21062..0dfa58d471ac 100644 --- a/platform/platform-api/src/com/intellij/ui/components/ScrollBarPainter.java +++ b/platform/platform-api/src/com/intellij/ui/components/ScrollBarPainter.java @@ -12,6 +12,7 @@ import com.intellij.ui.JBColor; import com.intellij.ui.MixedColorProducer; import com.intellij.ui.paint.RectanglePainter; import com.intellij.util.ui.RegionPainter; +import kotlinx.coroutines.CoroutineScope; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -130,8 +131,8 @@ public abstract class ScrollBarPainter implements RegionPainter { private static final int LIGHT_ALPHA = SystemInfoRt.isMac ? 120 : 160; private static final int DARK_ALPHA = SystemInfoRt.isMac ? 255 : 180; - ScrollBarPainter(@NotNull Supplier supplier) { - animator = new TwoWayAnimator(getClass().getName(), 11, 150, 125, 300, 125) { + ScrollBarPainter(@NotNull Supplier supplier, @NotNull CoroutineScope coroutineScope) { + animator = new TwoWayAnimator(getClass().getName(), 11, 150, 125, 300, 125, coroutineScope) { @Override public void onValueUpdate() { Component component = supplier.get(); @@ -191,8 +192,8 @@ public abstract class ScrollBarPainter implements RegionPainter { public static final class Track extends ScrollBarPainter { private final MixedColorProducer fillProducer; - public Track(@NotNull Supplier supplier) { - super(supplier); + public Track(@NotNull Supplier supplier, @NotNull CoroutineScope coroutineScope) { + super(supplier, coroutineScope); fillProducer = new MixedColorProducer( getColor(supplier, TRACK_BACKGROUND, TRACK_OPAQUE_BACKGROUND), getColor(supplier, TRACK_HOVERED_BACKGROUND, TRACK_OPAQUE_HOVERED_BACKGROUND)); @@ -217,8 +218,8 @@ public abstract class ScrollBarPainter implements RegionPainter { private final MixedColorProducer fillProducer; private final MixedColorProducer drawProducer; - Thumb(@NotNull Supplier supplier, boolean opaque) { - super(supplier); + Thumb(@NotNull Supplier supplier, boolean opaque, @NotNull CoroutineScope coroutineScope) { + super(supplier, coroutineScope); fillProducer = new MixedColorProducer( opaque ? getColor(supplier, THUMB_OPAQUE_BACKGROUND) : getColor(supplier, THUMB_BACKGROUND, THUMB_OPAQUE_BACKGROUND), @@ -266,8 +267,8 @@ public abstract class ScrollBarPainter implements RegionPainter { @ApiStatus.Internal public static class ThinScrollBarThumb extends Thumb { - ThinScrollBarThumb(@NotNull Supplier supplier, boolean opaque) { - super(supplier, opaque); + ThinScrollBarThumb(@NotNull Supplier supplier, boolean opaque, @NotNull CoroutineScope coroutineScope) { + super(supplier, opaque, coroutineScope); } @Override diff --git a/platform/platform-api/src/com/intellij/ui/components/TabMacScrollBarUI.kt b/platform/platform-api/src/com/intellij/ui/components/TabMacScrollBarUI.kt index f785c1559eb4..7b4364a0eb06 100644 --- a/platform/platform-api/src/com/intellij/ui/components/TabMacScrollBarUI.kt +++ b/platform/platform-api/src/com/intellij/ui/components/TabMacScrollBarUI.kt @@ -14,40 +14,45 @@ internal class TabMacScrollBarUI( thicknessMin: Int, ) : ThinMacScrollBarUI(thickness, thicknessMax, thicknessMin) { private var isHovered: Boolean = false - private val defaultColorProducer: MixedColorProducer = MixedColorProducer( - ScrollBarPainter.getColor({ scrollBar }, ScrollBarPainter.TABS_TRANSPARENT_THUMB_BACKGROUND), - ScrollBarPainter.getColor({ scrollBar }, ScrollBarPainter.TABS_THUMB_BACKGROUND)) - private val hoveredColorProducer: MixedColorProducer = MixedColorProducer( - ScrollBarPainter.getColor({ scrollBar }, ScrollBarPainter.TABS_THUMB_BACKGROUND), - ScrollBarPainter.getColor({ scrollBar }, ScrollBarPainter.TABS_THUMB_HOVERED_BACKGROUND)) - override fun createThumbPainter(): ScrollBarPainter.Thumb { - return object : ThinScrollBarThumb({ scrollBar }, false) { + override fun createThumbPainter(state: DefaultScrollbarUiInstalledState): ScrollBarPainter.Thumb { + val defaultColorProducer = MixedColorProducer( + ScrollBarPainter.getColor({ state.scrollBar }, ScrollBarPainter.TABS_TRANSPARENT_THUMB_BACKGROUND), + ScrollBarPainter.getColor({ state.scrollBar }, ScrollBarPainter.TABS_THUMB_BACKGROUND), + ) + val hoveredColorProducer = MixedColorProducer( + ScrollBarPainter.getColor({ state.scrollBar }, ScrollBarPainter.TABS_THUMB_BACKGROUND), + ScrollBarPainter.getColor({ state.scrollBar }, ScrollBarPainter.TABS_THUMB_HOVERED_BACKGROUND), + ) + + return object : ThinScrollBarThumb({ state.scrollBar }, false, state.coroutineScope) { override fun getFillProducer() = if (isHovered) hoveredColorProducer else defaultColorProducer } } - override fun createWrapAnimationBehaviour(): ScrollBarAnimationBehavior { - return object : ToggleableScrollBarAnimationBehaviorDecorator(createBaseAnimationBehavior(), myTrack.animator, thumb.animator) { + override fun createWrapAnimationBehaviour(state: DefaultScrollbarUiInstalledState): ScrollBarAnimationBehavior { + return object : ToggleableScrollBarAnimationBehaviorDecorator( + decoratedBehavior = createBaseAnimationBehavior(state), + trackAnimator = state.track.animator, + thumbAnimator = state.thumb.animator, + ) { override fun onThumbHover(hovered: Boolean) { super.onThumbHover(hovered) if (isHovered != hovered) { isHovered = hovered - scrollBar!!.revalidate() - scrollBar!!.repaint() + state.scrollBar.revalidate() + state.scrollBar.repaint() } } } } - override fun paintThumb(g: Graphics2D, c: JComponent) { - if (animationBehavior != null && animationBehavior!!.thumbFrame > 0) { - paint(thumb, g, c, !isHovered) + override fun paintThumb(g: Graphics2D, c: JComponent, state: DefaultScrollbarUiInstalledState) { + if (state.animationBehavior.thumbFrame > 0) { + paint(p = state.thumb, g = g, c = c, small = !isHovered) } } - override fun getInsets(small: Boolean): Insets { - return if (small) JBUI.insetsBottom(2) else JBUI.emptyInsets() - } + override fun getInsets(small: Boolean): Insets = if (small) JBUI.insetsBottom(2) else JBUI.emptyInsets() } \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/ui/components/TabScrollBarUI.kt b/platform/platform-api/src/com/intellij/ui/components/TabScrollBarUI.kt index 847f0ec68de7..b032de2df333 100644 --- a/platform/platform-api/src/com/intellij/ui/components/TabScrollBarUI.kt +++ b/platform/platform-api/src/com/intellij/ui/components/TabScrollBarUI.kt @@ -28,7 +28,7 @@ internal class TabScrollBarUI( } } - override fun createWrapAnimationBehaviour(): ScrollBarAnimationBehavior { + override fun createWrapAnimationBehaviour(defaultScrollbarUiInstalledState: DefaultScrollbarUiInstalledState): ScrollBarAnimationBehavior { return object : ToggleableScrollBarAnimationBehaviorDecorator(createBaseAnimationBehavior(), myTrack.animator, thumb.animator) { override fun onThumbHover(hovered: Boolean) { super.onThumbHover(hovered) @@ -42,7 +42,7 @@ internal class TabScrollBarUI( } - override fun paintThumb(g: Graphics2D, c: JComponent) { + override fun paintThumb(g: Graphics2D, c: JComponent, state: DefaultScrollbarUiInstalledState) { if (animationBehavior != null && animationBehavior!!.thumbFrame > 0) { paint(thumb, g, c, !isHovered) } diff --git a/platform/platform-api/src/com/intellij/ui/components/ThinMacScrollBarUI.java b/platform/platform-api/src/com/intellij/ui/components/ThinMacScrollBarUI.java index c37a992ec251..d374298a4d5c 100644 --- a/platform/platform-api/src/com/intellij/ui/components/ThinMacScrollBarUI.java +++ b/platform/platform-api/src/com/intellij/ui/components/ThinMacScrollBarUI.java @@ -21,12 +21,12 @@ public class ThinMacScrollBarUI extends MacScrollBarUI { } @Override - protected ScrollBarPainter.Thumb createThumbPainter() { - return new ScrollBarPainter.ThinScrollBarThumb(() -> scrollBar, false); + public @NotNull ScrollBarPainter.Thumb createThumbPainter$intellij_platform_ide(@NotNull DefaultScrollbarUiInstalledState state) { + return new ScrollBarPainter.ThinScrollBarThumb(() -> state.scrollBar, false, state.coroutineScope); } @Override - public void paintTrack(Graphics2D g, JComponent c) { + public void paintTrack(@NotNull Graphics2D g, @NotNull JComponent c) { // Track is not needed } diff --git a/platform/platform-api/src/com/intellij/ui/components/ThinScrollBarUI.kt b/platform/platform-api/src/com/intellij/ui/components/ThinScrollBarUI.kt index 3c924dc5b6a0..a16860786bc0 100644 --- a/platform/platform-api/src/com/intellij/ui/components/ThinScrollBarUI.kt +++ b/platform/platform-api/src/com/intellij/ui/components/ThinScrollBarUI.kt @@ -18,18 +18,20 @@ internal open class ThinScrollBarUI : DefaultScrollBarUI { thicknessMin = thicknessMin, ) - override fun createThumbPainter(): ScrollBarPainter.Thumb = ThinScrollBarThumb({ scrollBar }, false) + override fun createThumbPainter(state: DefaultScrollbarUiInstalledState): ScrollBarPainter.Thumb { + return ThinScrollBarThumb({ state.scrollBar }, false, state.coroutineScope) + } override fun paintTrack(g: Graphics2D, c: JComponent) { // track is not needed } - override fun paintThumb(g: Graphics2D, c: JComponent) { + override fun paintThumb(g: Graphics2D, c: JComponent, state: DefaultScrollbarUiInstalledState) { if (isOpaque(c)) { - paint(p = thumb, g = g, c = c, small = ScrollSettings.isThumbSmallIfOpaque.invoke()) + paint(p = state.thumb, g = g, c = c, small = ScrollSettings.isThumbSmallIfOpaque.invoke()) } - else if (animationBehavior != null && animationBehavior!!.thumbFrame > 0) { - paint(p = thumb, g = g, c = c, small = false) + else if (state.animationBehavior.thumbFrame > 0) { + paint(p = state.thumb, g = g, c = c, small = false) } } diff --git a/platform/platform-api/src/com/intellij/ui/components/TwoWayAnimator.kt b/platform/platform-api/src/com/intellij/ui/components/TwoWayAnimator.kt index 354cdc779bf5..fded0fdcab6b 100644 --- a/platform/platform-api/src/com/intellij/ui/components/TwoWayAnimator.kt +++ b/platform/platform-api/src/com/intellij/ui/components/TwoWayAnimator.kt @@ -19,6 +19,7 @@ abstract class TwoWayAnimator( durationForward: Int, pauseBackward: Int, durationBackward: Int, + private val coroutineScope: CoroutineScope, ) { private val animateRequests = MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) @@ -29,6 +30,7 @@ abstract class TwoWayAnimator( cycleDuration = durationForward, pauseInMs = pauseForward, forward = true, + coroutineScope = coroutineScope, ) } @@ -39,6 +41,7 @@ abstract class TwoWayAnimator( cycleDuration = durationBackward, pauseInMs = pauseBackward, forward = false, + coroutineScope = coroutineScope, ) } @@ -48,16 +51,14 @@ abstract class TwoWayAnimator( @JvmField var value: Float = 0f - private var coroutineScope: CoroutineScope? = null + private var job: Job? = null abstract fun onValueUpdate() fun start(forward: Boolean) { - @Suppress("SSBasedInspection") - if (coroutineScope == null) { + if (job == null) { val context = Dispatchers.EDT + ModalityState.defaultModalityState().asContextElement() - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + CoroutineName("TwoWayAnimator")) - coroutineScope!!.launch { + job = coroutineScope.launch { animateRequests.collectLatest { animator -> delay(animator.pauseInMs.toLong()) withContext(context) { @@ -96,8 +97,8 @@ abstract class TwoWayAnimator( } fun stop() { - coroutineScope?.let { - coroutineScope = null + job?.let { + job = null it.cancel() } suspendAnimation() @@ -124,12 +125,14 @@ abstract class TwoWayAnimator( cycleDuration: Int, @JvmField val pauseInMs: Int, forward: Boolean, + coroutineScope: CoroutineScope, ) : Animator( name = name, totalFrames = totalFrames, cycleDuration = cycleDuration, isRepeatable = false, isForward = forward, + coroutineScope = coroutineScope, ) { override fun paintNow(frame: Int, totalFrames: Int, cycle: Int) { if (if (isForward) frame > this@TwoWayAnimator.frame else frame < this@TwoWayAnimator.frame) { diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.kt b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.kt index e347ad6ed05e..94b32d94307f 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.kt +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.kt @@ -286,12 +286,17 @@ open class JBTabsImpl internal constructor( internal val attractions: MutableSet = HashSet() private val animator = lazy { - val result = object : Animator("JBTabs Attractions", 2, 500, true) { + val result = object : Animator( + name = "JBTabs Attractions", + totalFrames = 2, + cycleDuration = 500, + isRepeatable = true, + coroutineScope = coroutineScope, + ) { override fun paintNow(frame: Int, totalFrames: Int, cycle: Int) { repaintAttractions() } } - Disposer.register(parentDisposable, result) result } @@ -3029,19 +3034,19 @@ open class JBTabsImpl internal constructor( for (each in visibleInfos) { each.changeSupport.firePropertyChange(TabInfo.ACTION_GROUP, "new1", "new2") } - relayout(true, false) + relayout(forced = true, layoutNow = false) return this } final override fun setSideComponentOnTabs(onTabs: Boolean): JBTabsPresentation { isSideComponentOnTabs = onTabs - relayout(true, false) + relayout(forced = true, layoutNow = false) return this } final override fun setSideComponentBefore(before: Boolean): JBTabsPresentation { isSideComponentBefore = before - relayout(true, false) + relayout(forced = true, layoutNow = false) return this } @@ -3137,10 +3142,12 @@ open class JBTabsImpl internal constructor( private class DefaultDecorator : UiDecorator { override fun getDecoration(): UiDecoration { - return UiDecoration(labelFont = null, - labelInsets = JBUI.insets(5, 8), - contentInsetsSupplier = java.util.function.Function { JBUI.insets(0, 4) }, - iconTextGap = JBUI.scale(4)) + return UiDecoration( + labelFont = null, + labelInsets = JBUI.insets(5, 8), + contentInsetsSupplier = { JBUI.insets(0, 4) }, + iconTextGap = JBUI.scale(4), + ) } } diff --git a/platform/platform-api/src/com/intellij/util/ui/Animator.kt b/platform/platform-api/src/com/intellij/util/ui/Animator.kt index 56836848b90c..21bf10fda577 100644 --- a/platform/platform-api/src/com/intellij/util/ui/Animator.kt +++ b/platform/platform-api/src/com/intellij/util/ui/Animator.kt @@ -2,7 +2,6 @@ package com.intellij.util.ui import com.intellij.codeWithMe.ClientId -import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.asContextElement @@ -14,18 +13,23 @@ import java.awt.GraphicsEnvironment import javax.swing.SwingUtilities import kotlin.time.Duration.Companion.microseconds -abstract class Animator @JvmOverloads constructor(private val name: @NonNls String?, - private val totalFrames: Int, - private val cycleDuration: Int, - private val isRepeatable: Boolean, - @JvmField protected val isForward: Boolean = true, - coroutineScope: CoroutineScope? = null) : Disposable { +abstract class Animator @JvmOverloads constructor( + private val name: @NonNls String?, + private val totalFrames: Int, + private val cycleDuration: Int, + private val isRepeatable: Boolean, + @JvmField protected val isForward: Boolean = true, + coroutineScope: CoroutineScope? = null, +) { private var ticker: Job? = null private var currentFrame = 0 private var startTime: Long = 0 private var startDeltaTime: Long = 0 private var initialStep = false - private val coroutineScope = coroutineScope ?: CoroutineScope(SupervisorJob() + Dispatchers.Default + ClientId.coroutineContext()) + private val coroutineScope = coroutineScope ?: CoroutineScope(SupervisorJob() + + Dispatchers.Default + + ModalityState.defaultModalityState().asContextElement() + + ClientId.coroutineContext()) @Obsolete fun isForward(): Boolean = isForward @@ -133,7 +137,7 @@ abstract class Animator @JvmOverloads constructor(private val name: @NonNls Stri abstract fun paintNow(frame: Int, totalFrames: Int, cycle: Int) - override fun dispose() { + open fun dispose() { stopTicker() coroutineScope.cancel() isDisposed = true diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java index 334eb973c0a0..2d1512bd7d04 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java @@ -1,4 +1,4 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.ui; import com.intellij.application.Topics; @@ -95,7 +95,7 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons private int mySmartFadeoutDelay; private MyComponent component; - private JLayeredPane myLayeredPane; + private JLayeredPane layeredPane; private AbstractPosition myPosition; private Point myTargetPoint; private final boolean myHideOnFrameResize; @@ -242,8 +242,8 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons public void setPointerColor(Color pointerColor) { myPointerColor = pointerColor; - myLayeredPane.revalidate(); - myLayeredPane.repaint(); + layeredPane.revalidate(); + layeredPane.repaint(); } private final long myFadeoutTime; @@ -317,7 +317,7 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons } } }; - private Animator myAnimator; + private Animator animator; private boolean myShowPointer; private boolean isDisposed; @@ -472,11 +472,11 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons myVisible = true; - myLayeredPane = root.getLayeredPane(); + layeredPane = root.getLayeredPane(); myPosition = position; UIUtil.setFutureRootPane(myContent, root); - myFocusManager = IdeFocusManager.findInstanceByComponent(myLayeredPane); + myFocusManager = IdeFocusManager.findInstanceByComponent(layeredPane); final Ref originalFocusOwner = new Ref<>(); final Ref proxyFocusRequest = new Ref<>(ActionCallback.DONE); @@ -497,9 +497,9 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons } }); } - myLayeredPane.addComponentListener(myComponentListener); + layeredPane.addComponentListener(myComponentListener); - myTargetPoint = myPosition.getShiftedPoint(myTracker.recalculateLocation(this).getPoint(myLayeredPane), myCalloutShift); + myTargetPoint = myPosition.getShiftedPoint(myTracker.recalculateLocation(this).getPoint(layeredPane), myCalloutShift); if (isDisposed) return; //tracker may dispose the balloon int positionChangeFix = 0; @@ -510,11 +510,11 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons if (!myPosition.isOkToHavePointer(myTargetPoint, rec, getPointerLength(myPosition), getPointerWidth(myPosition), getArc())) { rec = getRecForPosition(myPosition, false); - Rectangle lp = new Rectangle(new Point(myContainerInsets.left, myContainerInsets.top), myLayeredPane.getSize()); + Rectangle lp = new Rectangle(new Point(myContainerInsets.left, myContainerInsets.top), layeredPane.getSize()); lp.width -= myContainerInsets.right; lp.height -= myContainerInsets.bottom; - if (!lp.contains(rec) || !PopupLocationTracker.canRectangleBeUsed(myLayeredPane, rec, this)) { + if (!lp.contains(rec) || !PopupLocationTracker.canRectangleBeUsed(layeredPane, rec, this)) { Rectangle2D currentSquare = lp.createIntersection(rec); double maxSquare = currentSquare.getWidth() * currentSquare.getHeight(); @@ -536,7 +536,7 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons } if (myPosition != position) { - myTargetPoint = myPosition.getShiftedPoint(myTracker.recalculateLocation(this).getPoint(myLayeredPane), + myTargetPoint = myPosition.getShiftedPoint(myTracker.recalculateLocation(this).getPoint(layeredPane), myCalloutShift > 0 ? myCalloutShift + positionChangeFix : positionChangeFix); position = myPosition; } @@ -544,18 +544,18 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons createComponent(); Rectangle r = getRecForPosition(myPosition, false); Point location = r.getLocation(); - SwingUtilities.convertPointToScreen(location, myLayeredPane); + SwingUtilities.convertPointToScreen(location, layeredPane); r.setLocation(location); - if (!PopupLocationTracker.canRectangleBeUsed(myLayeredPane, r, this)) { + if (!PopupLocationTracker.canRectangleBeUsed(layeredPane, r, this)) { for (AbstractPosition eachPosition : myPosition.getOtherPositions()) { r = getRecForPosition(eachPosition, false); location = r.getLocation(); - SwingUtilities.convertPointToScreen(location, myLayeredPane); + SwingUtilities.convertPointToScreen(location, layeredPane); r.setLocation(location); - if (PopupLocationTracker.canRectangleBeUsed(myLayeredPane, r, this)) { + if (PopupLocationTracker.canRectangleBeUsed(layeredPane, r, this)) { myPosition = eachPosition; positionChangeFix = myPosition.getChangeShift(position, myPositionChangeXShift, myPositionChangeYShift); - myTargetPoint = myPosition.getShiftedPoint(myTracker.recalculateLocation(this).getPoint(myLayeredPane), + myTargetPoint = myPosition.getShiftedPoint(myTracker.recalculateLocation(this).getPoint(layeredPane), myCalloutShift > 0 ? myCalloutShift + positionChangeFix : positionChangeFix); myPosition.updateBounds(this); break; @@ -571,10 +571,10 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons !myPosition.isOkToHavePointer(myTargetPoint, rec, getPointerLength(myPosition), getPointerWidth(myPosition), getArc())) { myShowPointer = false; component.removeAll(); - myLayeredPane.remove(component); + layeredPane.remove(component); createComponent(); - Dimension availSpace = myLayeredPane.getSize(); + Dimension availSpace = layeredPane.getSize(); Dimension reqSpace = component.getSize(); if (!new Rectangle(availSpace).contains(new Rectangle(reqSpace))) { // Balloon is bigger than window, don't show it at all. @@ -582,8 +582,8 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons "required [" + reqSpace.width + " x " + reqSpace.height + "], " + "available [" + availSpace.width + " x " + availSpace.height + "]"); component.removeAll(); - myLayeredPane.remove(component); - myLayeredPane = null; + layeredPane.remove(component); + layeredPane = null; hide(); return; } @@ -594,11 +594,11 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons } if (isAnimationEnabled()) { - runAnimation(true, myLayeredPane, null); + runAnimation(true, layeredPane, null); } - myLayeredPane.revalidate(); - myLayeredPane.repaint(); + layeredPane.revalidate(); + layeredPane.repaint(); if (myRequestFocus) { myFocusManager.doWhenFocusSettlesDown(new ExpirableRunnable() { @@ -783,9 +783,9 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons createComponentBorder(); - myLayeredPane.add(component); + layeredPane.add(component); if (myZeroPositionInLayer) { - myLayeredPane.setLayer(component, getLayer(), 0); // the second balloon must be over the first one + layeredPane.setLayer(component, getLayer(), 0); // the second balloon must be over the first one } myPosition.updateBounds(this); @@ -828,14 +828,14 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons public @NotNull Rectangle getConsumedScreenBounds() { Rectangle bounds = component.getBounds(); Point location = bounds.getLocation(); - SwingUtilities.convertPointToScreen(location, myLayeredPane); + SwingUtilities.convertPointToScreen(location, layeredPane); bounds.setLocation(location); return bounds; } @Override public Window getUnderlyingWindow() { - return ComponentUtil.getWindow(myLayeredPane); + return ComponentUtil.getWindow(layeredPane); } private @NotNull EmptyBorder getPointlessBorder() { @@ -857,7 +857,7 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons RelativePoint newPosition = tracker.recalculateLocation(this); if (newPosition != null) { - Point newPoint = myPosition.getShiftedPoint(newPosition.getPoint(myLayeredPane), myCalloutShift); + Point newPoint = myPosition.getShiftedPoint(newPosition.getPoint(layeredPane), myCalloutShift); invalidateShadow = !Objects.equals(myTargetPoint, newPoint); myTargetPoint = newPoint; myPosition.updateBounds(this); @@ -918,11 +918,11 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons private void runAnimation(boolean forward, final JLayeredPane layeredPane, final @Nullable Runnable onDone) { - if (myAnimator != null) { - Disposer.dispose(myAnimator); + if (animator != null) { + animator.dispose(); } - myAnimator = new Animator("Balloon", 8, isAnimationEnabled() ? myAnimationCycle : 0, false, forward) { + animator = new Animator("Balloon", 8, isAnimationEnabled() ? myAnimationCycle : 0, false, forward) { @Override public void paintNow(final int frame, final int totalFrames, final int cycle) { if (component == null || component.getParent() == null || !isAnimationEnabled()) return; @@ -948,20 +948,20 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons layeredPane.revalidate(); layeredPane.repaint(); } - Disposer.dispose(this); + dispose(); } @Override public void dispose() { super.dispose(); - myAnimator = null; + animator = null; if (onDone != null) { onDone.run(); } } }; - myAnimator.resume(); + animator.resume(); } void runWithSmartFadeoutPause(@NotNull Runnable handler) { @@ -1112,14 +1112,14 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons }; Toolkit.getDefaultToolkit().removeAWTEventListener(myAwtActivityListener); - if (myLayeredPane == null) { + if (layeredPane == null) { disposeRunnable.run(); } else { - myLayeredPane.removeComponentListener(myComponentListener); + layeredPane.removeComponentListener(myComponentListener); if (isAnimationEnabled()) { - runAnimation(false, myLayeredPane, disposeRunnable); + runAnimation(false, layeredPane, disposeRunnable); } else { disposeAnimationAndRemoveComponent(); @@ -1132,13 +1132,13 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons } private void disposeAnimationAndRemoveComponent() { - if (myAnimator != null) { - Disposer.dispose(myAnimator); + if (animator != null) { + animator.dispose(); } if (component != null) { - myLayeredPane.remove(component); - myLayeredPane.revalidate(); - myLayeredPane.repaint(); + layeredPane.remove(component); + layeredPane.revalidate(); + layeredPane.repaint(); component = null; } } @@ -1218,7 +1218,7 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons abstract int getChangeShift(AbstractPosition original, int xShift, int yShift); public void updateBounds(final @NotNull BalloonImpl balloon) { - if (balloon.myLayeredPane == null || balloon.component == null) return; + if (balloon.layeredPane == null || balloon.component == null) return; Insets shadow = balloon.component.getInsets(); Dimension prefSize = balloon.component.getPreferredSize(); @@ -1237,7 +1237,7 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons */ @NotNull Rectangle getUpdatedBounds(BalloonImpl balloon, Dimension contentSize, Insets shadowInsets) { - Dimension layeredPaneSize = balloon.myLayeredPane.getSize(); + Dimension layeredPaneSize = balloon.layeredPane.getSize(); Point point = balloon.myTargetPoint; Rectangle bounds = balloon.myForcedBounds; @@ -1955,7 +1955,7 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons final Graphics2D g2d = (Graphics2D)g; - Point pointTarget = SwingUtilities.convertPoint(myLayeredPane, myBalloon.myTargetPoint, this); + Point pointTarget = SwingUtilities.convertPoint(layeredPane, myBalloon.myTargetPoint, this); Rectangle shapeBounds = myContent.getBounds(); if (!DrawUtil.isSimplifiedUI()) { @@ -2008,7 +2008,7 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons @Override public boolean contains(int x, int y) { - Point pointTarget = SwingUtilities.convertPoint(myLayeredPane, myBalloon.myTargetPoint, this); + Point pointTarget = SwingUtilities.convertPoint(layeredPane, myBalloon.myTargetPoint, this); Rectangle bounds = myContent.getBounds(); Shape shape; if (myShowPointer) { @@ -2072,14 +2072,14 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons for (ActionButton button : myActionButtons) { if (button.getParent() == null) { - myLayeredPane.add(button); - myLayeredPane.setLayer(button, JLayeredPane.DRAG_LAYER); + layeredPane.add(button); + layeredPane.setLayer(button, JLayeredPane.DRAG_LAYER); } } } if (isVisible()) { - Rectangle lpBounds = SwingUtilities.convertRectangle(getParent(), bounds, myLayeredPane); + Rectangle lpBounds = SwingUtilities.convertRectangle(getParent(), bounds, layeredPane); lpBounds = myPosition .getPointlessContentRec(lpBounds, myBalloon.myShadowBorderProvider == null ? myBalloon.getPointerLength(myPosition) : 0); myActionProvider.layout(lpBounds); @@ -2237,7 +2237,7 @@ public final class BalloonImpl implements Balloon, IdeTooltip.Ui, ScreenAreaCons @Override public RelativePoint getShowingPoint() { Point p = myPosition.getShiftedPoint(myTargetPoint, myCalloutShift * -1); - return new RelativePoint(myLayeredPane, p); + return new RelativePoint(layeredPane, p); } @Override diff --git a/plugins/settings-sync/src/com/intellij/settingsSync/CloudConfigServerCommunicator.kt b/plugins/settings-sync/src/com/intellij/settingsSync/CloudConfigServerCommunicator.kt index d1613552fbf7..972db7b18276 100644 --- a/plugins/settings-sync/src/com/intellij/settingsSync/CloudConfigServerCommunicator.kt +++ b/plugins/settings-sync/src/com/intellij/settingsSync/CloudConfigServerCommunicator.kt @@ -6,15 +6,14 @@ import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.util.JDOMUtil import com.intellij.openapi.util.io.FileUtil import com.intellij.settingsSync.auth.SettingsSyncAuthService +import com.intellij.util.concurrency.SynchronizedClearableLazy import com.intellij.util.io.HttpRequests import com.intellij.util.io.delete -import com.intellij.util.resettableLazy import com.jetbrains.cloudconfig.* import com.jetbrains.cloudconfig.auth.JbaJwtTokenAuthProvider import com.jetbrains.cloudconfig.exception.InvalidVersionIdException import com.jetbrains.cloudconfig.exception.UnauthorizedException import org.jdom.JDOMException -import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.VisibleForTesting import java.io.FileNotFoundException import java.io.IOException @@ -38,8 +37,7 @@ internal open class CloudConfigServerCommunicator(serverUrl: String? = null) : S @Volatile internal var _currentIdTokenVar: String? = null - internal var _client = resettableLazy { createCloudConfigClient(serverUrl ?: defaultUrl, clientVersionContext) } - @TestOnly set + private var _client = SynchronizedClearableLazy { createCloudConfigClient(serverUrl ?: defaultUrl, clientVersionContext) } internal open val client get() = _client.value private val lastRemoteErrorRef = AtomicReference() @@ -48,7 +46,7 @@ internal open class CloudConfigServerCommunicator(serverUrl: String? = null) : S SettingsSyncEvents.getInstance().addListener( object : SettingsSyncEventListener { override fun loginStateChanged() { - _client.reset() + _client.drop() } } ) @@ -357,7 +355,7 @@ internal open class CloudConfigServerCommunicator(serverUrl: String? = null) : S private fun getProductionUrl(): String { val configUrl = HttpRequests.request(URL_PROVIDER) .productNameAsUserAgent() - .connect(HttpRequests.RequestProcessor { request: HttpRequests.Request -> + .connect({ request: HttpRequests.Request -> try { val documentElement = JDOMUtil.load(request.inputStream) documentElement.getAttributeValue("baseUrl")