IJPL-158075 MacScrollBarAnimationBehavior - use flow for hideThumb

GitOrigin-RevId: 630f4fd27a19efdd973bcf8c73111a06643425ae
This commit is contained in:
Vladimir Krivosheev
2024-07-11 14:47:57 +02:00
committed by intellij-monorepo-bot
parent 7a927bdadf
commit 8d8130566f
4 changed files with 74 additions and 30 deletions

View File

@@ -8,6 +8,7 @@ import com.intellij.ui.scale.JBUIScale;
import com.intellij.util.MathUtil;
import com.intellij.util.ui.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
@@ -38,7 +39,7 @@ class DefaultScrollBarUI extends ScrollBarUI {
private int myCachedValue;
private int myOldValue;
protected final ScrollBarAnimationBehavior myAnimationBehavior = createWrapAnimationBehaviour();
protected @Nullable ScrollBarAnimationBehavior myAnimationBehavior = null;
DefaultScrollBarUI() {
this(ScrollSettings.isThumbSmallIfOpaque() ? 13 : 10, 14, 10);
@@ -73,7 +74,9 @@ class DefaultScrollBarUI extends ScrollBarUI {
}
void toggle(boolean isOn) {
myAnimationBehavior.onToggle(isOn);
if (myAnimationBehavior != null) {
myAnimationBehavior.onToggle(isOn);
}
}
static boolean isOpaque(Component c) {
@@ -88,7 +91,7 @@ class DefaultScrollBarUI extends ScrollBarUI {
}
boolean isTrackClickable() {
return isOpaque(myScrollBar) || myAnimationBehavior.getTrackFrame() > 0;
return isOpaque(myScrollBar) || (myAnimationBehavior != null && myAnimationBehavior.getTrackFrame() > 0);
}
boolean isTrackExpandable() {
@@ -148,7 +151,7 @@ class DefaultScrollBarUI extends ScrollBarUI {
private int getTrackOffset(int offset) {
if (!isTrackExpandable()) return offset;
float value = myAnimationBehavior.getTrackFrame();
float value = myAnimationBehavior == null ? 0 :myAnimationBehavior.getTrackFrame();
if (value <= 0) return offset;
if (value >= 1) return 0;
return (int)(.5f + offset * (1 - value));
@@ -174,6 +177,8 @@ class DefaultScrollBarUI extends ScrollBarUI {
@Override
public void installUI(JComponent c) {
myAnimationBehavior = createWrapAnimationBehaviour();
myScrollBar = (JScrollBar)c;
ScrollBarPainter.setBackground(c);
myScrollBar.setOpaque(false);
@@ -188,7 +193,10 @@ class DefaultScrollBarUI extends ScrollBarUI {
@Override
public void uninstallUI(JComponent c) {
assert myAnimationBehavior != null;
myAnimationBehavior.onUninstall();
myAnimationBehavior = null;
myScrollTimer.stop();
myScrollBar.removeFocusListener(myListener);
myScrollBar.removePropertyChangeListener(myListener);
@@ -344,7 +352,9 @@ class DefaultScrollBarUI extends ScrollBarUI {
}
}
myOldValue = value;
if (animate) myAnimationBehavior.onThumbMove();
if (animate && myAnimationBehavior != null) {
myAnimationBehavior.onThumbMove();
}
}
private int getValue() {
@@ -373,9 +383,13 @@ class DefaultScrollBarUI extends ScrollBarUI {
private void updateMouse(int x, int y) {
if (isTrackContains(x, y)) {
if (!isOverTrack) myAnimationBehavior.onTrackHover(isOverTrack = true);
if (!isOverTrack && myAnimationBehavior != null) {
myAnimationBehavior.onTrackHover(isOverTrack = true);
}
boolean hover = isThumbContains(x, y);
if (isOverThumb != hover) myAnimationBehavior.onThumbHover(isOverThumb = hover);
if (isOverThumb != hover && myAnimationBehavior != null) {
myAnimationBehavior.onThumbHover(isOverThumb = hover);
}
}
else {
updateMouseExit();
@@ -383,8 +397,12 @@ class DefaultScrollBarUI extends ScrollBarUI {
}
private void updateMouseExit() {
if (isOverThumb) myAnimationBehavior.onThumbHover(isOverThumb = false);
if (isOverTrack) myAnimationBehavior.onTrackHover(isOverTrack = false);
if (isOverThumb && myAnimationBehavior != null) {
myAnimationBehavior.onThumbHover(isOverThumb = false);
}
if (isOverTrack && myAnimationBehavior != null) {
myAnimationBehavior.onTrackHover(isOverTrack = false);
}
}
private boolean redispatchIfTrackNotClickable(MouseEvent event) {
@@ -546,7 +564,9 @@ class DefaultScrollBarUI extends ScrollBarUI {
repaint();
}
if ("opaque".equals(name) || "visible".equals(name)) {
myAnimationBehavior.onReset();
if (myAnimationBehavior != null) {
myAnimationBehavior.onReset();
}
myTrack.bounds.setBounds(0, 0, 0, 0);
myThumb.bounds.setBounds(0, 0, 0, 0);
}
@@ -565,7 +585,9 @@ class DefaultScrollBarUI extends ScrollBarUI {
int minY = Math.min(myThumb.bounds.y, thumbPos);
int maxY = Math.max(myThumb.bounds.y, thumbPos) + myThumb.bounds.height;
myThumb.bounds.y = thumbPos;
myAnimationBehavior.onThumbMove();
if (myAnimationBehavior != null) {
myAnimationBehavior.onThumbMove();
}
repaint(myThumb.bounds.x, minY, myThumb.bounds.width, maxY - minY);
}
}
@@ -577,7 +599,9 @@ class DefaultScrollBarUI extends ScrollBarUI {
int minX = Math.min(myThumb.bounds.x, thumbPos);
int maxX = Math.max(myThumb.bounds.x, thumbPos) + myThumb.bounds.width;
myThumb.bounds.x = thumbPos;
myAnimationBehavior.onThumbMove();
if (myAnimationBehavior != null) {
myAnimationBehavior.onThumbMove();
}
repaint(minX, myThumb.bounds.y, maxX - minX, myThumb.bounds.height);
}
}

View File

@@ -1,7 +1,14 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:OptIn(FlowPreview::class)
package com.intellij.ui.components
import com.intellij.util.SingleAlarm
import com.intellij.openapi.application.EDT
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import javax.swing.JScrollBar
internal abstract class ScrollBarAnimationBehavior(@JvmField protected val trackAnimator: TwoWayAnimator,
@@ -52,7 +59,23 @@ internal class MacScrollBarAnimationBehavior(
thumbAnimator: TwoWayAnimator,
) : DefaultScrollBarAnimationBehavior(trackAnimator, thumbAnimator) {
private var isTrackHovered: Boolean = false
private val hideThumbAlarm = SingleAlarm(task = { thumbAnimator.start(forward = false) }, delay = 700)
@Suppress("SSBasedInspection")
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + CoroutineName("IdeRootPane"))
private val hideThumbRequests = MutableSharedFlow<Boolean>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
init {
coroutineScope.launch {
hideThumbRequests
.debounce(700)
.collectLatest { start ->
if (start) {
withContext(Dispatchers.EDT) {
thumbAnimator.start(forward = false)
}
}
}
}
}
override fun onTrackHover(hovered: Boolean) {
isTrackHovered = hovered
@@ -74,19 +97,13 @@ internal class MacScrollBarAnimationBehavior(
override fun onThumbMove() {
val scrollBar = scrollBarComputable()
if (scrollBar != null && scrollBar.isShowing() && !DefaultScrollBarUI.isOpaque(scrollBar)) {
if (!isTrackHovered && thumbAnimator.value == 0f) trackAnimator.rewind(false)
thumbAnimator.rewind(true)
hideThumbAlarm.cancelAllRequests()
if (!isTrackHovered) {
hideThumbAlarm.request()
if (!isTrackHovered && thumbAnimator.value == 0f) {
trackAnimator.rewind(false)
}
thumbAnimator.rewind(true)
check(hideThumbRequests.tryEmit(!isTrackHovered))
}
}
override fun onUninstall() {
hideThumbAlarm.cancelAllRequests()
super.onUninstall()
}
}
internal open class ToggleableScrollBarAnimationBehaviorDecorator(

View File

@@ -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.components
import com.intellij.ui.MixedColorProducer
@@ -8,8 +8,11 @@ import java.awt.Graphics2D
import java.awt.Insets
import javax.swing.JComponent
internal class TabMacScrollBarUI(thickness: Int, thicknessMax: Int, thicknessMin: Int) : ThinMacScrollBarUI(thickness, thicknessMax,
thicknessMin) {
internal class TabMacScrollBarUI(
thickness: Int,
thicknessMax: Int,
thicknessMin: Int,
) : ThinMacScrollBarUI(thickness, thicknessMax, thicknessMin) {
private var isHovered: Boolean = false
private val defaultColorProducer: MixedColorProducer = MixedColorProducer(
ScrollBarPainter.getColor({ myScrollBar }, ScrollBarPainter.TABS_TRANSPARENT_THUMB_BACKGROUND),
@@ -39,7 +42,7 @@ internal class TabMacScrollBarUI(thickness: Int, thicknessMax: Int, thicknessMin
override fun paintThumb(g: Graphics2D?, c: JComponent?) {
if (myAnimationBehavior.thumbFrame > 0) {
if (myAnimationBehavior != null && myAnimationBehavior!!.thumbFrame > 0) {
paint(myThumb, g, c, !isHovered)
}
}

View File

@@ -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.components
import com.intellij.ui.MixedColorProducer
@@ -39,7 +39,7 @@ internal class TabScrollBarUI(thickness: Int, thicknessMax: Int, thicknessMin: I
override fun paintThumb(g: Graphics2D?, c: JComponent?) {
if (myAnimationBehavior.thumbFrame > 0) {
if (myAnimationBehavior != null && myAnimationBehavior!!.thumbFrame > 0) {
paint(myThumb, g, c, !isHovered)
}
}