mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
IJPL-158348 install ui - use separate state to avoid NPEs
GitOrigin-RevId: 0a15ee52a17a5ee6da8f096eaf9a2e0a260af030
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2751a94633
commit
e89669ffbf
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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<Any>? = 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<Any>? = 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
|
||||
|
||||
@@ -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<out JScrollPane?>, 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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Float> {
|
||||
private static final int LIGHT_ALPHA = SystemInfoRt.isMac ? 120 : 160;
|
||||
private static final int DARK_ALPHA = SystemInfoRt.isMac ? 255 : 180;
|
||||
|
||||
ScrollBarPainter(@NotNull Supplier<? extends Component> supplier) {
|
||||
animator = new TwoWayAnimator(getClass().getName(), 11, 150, 125, 300, 125) {
|
||||
ScrollBarPainter(@NotNull Supplier<? extends Component> 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<Float> {
|
||||
public static final class Track extends ScrollBarPainter {
|
||||
private final MixedColorProducer fillProducer;
|
||||
|
||||
public Track(@NotNull Supplier<? extends Component> supplier) {
|
||||
super(supplier);
|
||||
public Track(@NotNull Supplier<? extends Component> 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<Float> {
|
||||
private final MixedColorProducer fillProducer;
|
||||
private final MixedColorProducer drawProducer;
|
||||
|
||||
Thumb(@NotNull Supplier<? extends Component> supplier, boolean opaque) {
|
||||
super(supplier);
|
||||
Thumb(@NotNull Supplier<? extends Component> 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<Float> {
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static class ThinScrollBarThumb extends Thumb {
|
||||
ThinScrollBarThumb(@NotNull Supplier<? extends Component> supplier, boolean opaque) {
|
||||
super(supplier, opaque);
|
||||
ThinScrollBarThumb(@NotNull Supplier<? extends Component> supplier, boolean opaque, @NotNull CoroutineScope coroutineScope) {
|
||||
super(supplier, opaque, coroutineScope);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ abstract class TwoWayAnimator(
|
||||
durationForward: Int,
|
||||
pauseBackward: Int,
|
||||
durationBackward: Int,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
) {
|
||||
private val animateRequests = MutableSharedFlow<MyAnimator>(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) {
|
||||
|
||||
@@ -286,12 +286,17 @@ open class JBTabsImpl internal constructor(
|
||||
internal val attractions: MutableSet<TabInfo> = 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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Component> originalFocusOwner = new Ref<>();
|
||||
final Ref<ActionCallback> 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
|
||||
|
||||
@@ -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<Throwable>()
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user