From b194e7f2d6b9c7992de391dc96cdb7e4b7a721b7 Mon Sep 17 00:00:00 2001 From: Caoimhe Date: Mon, 22 Jul 2024 09:46:36 +0100 Subject: [PATCH] ScrollComponent: Add a minimum scrollbar grip size Making it easier to grab when there is a lot of content within the ScrollComponent. GitHub: #146 --- api/Elementa.api | 1 + .../gg/essential/elementa/ElementaVersion.kt | 8 +++ .../elementa/components/ScrollComponent.kt | 59 +++++++++++++++++-- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/api/Elementa.api b/api/Elementa.api index 4d2010cc..5d4bc35f 100644 --- a/api/Elementa.api +++ b/api/Elementa.api @@ -6,6 +6,7 @@ public final class gg/essential/elementa/ElementaVersion : java/lang/Enum { public static final field V3 Lgg/essential/elementa/ElementaVersion; public static final field V4 Lgg/essential/elementa/ElementaVersion; public static final field V5 Lgg/essential/elementa/ElementaVersion; + public static final field V6 Lgg/essential/elementa/ElementaVersion; public final fun enableFor (Lkotlin/jvm/functions/Function0;)Ljava/lang/Object; public static fun valueOf (Ljava/lang/String;)Lgg/essential/elementa/ElementaVersion; public static fun values ()[Lgg/essential/elementa/ElementaVersion; diff --git a/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt b/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt index 2a0f72a7..3077ccef 100644 --- a/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt +++ b/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt @@ -84,8 +84,14 @@ enum class ElementaVersion { /** * Change the behavior of scroll components to no longer require holding down shift when horizontal is the only possible scrolling direction. */ + @Deprecated(DEPRECATION_MESSAGE) V5, + /** + * [gg.essential.elementa.components.ScrollComponent] now has a minimum size for scrollbar grips. + */ + V6, + ; /** @@ -126,7 +132,9 @@ Be sure to read through all the changes between your current version and your ne internal val v3 = V3 @Suppress("DEPRECATION") internal val v4 = V4 + @Suppress("DEPRECATION") internal val v5 = V5 + internal val v6 = V6 @PublishedApi diff --git a/src/main/kotlin/gg/essential/elementa/components/ScrollComponent.kt b/src/main/kotlin/gg/essential/elementa/components/ScrollComponent.kt index de81528e..d67c829c 100644 --- a/src/main/kotlin/gg/essential/elementa/components/ScrollComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/components/ScrollComponent.kt @@ -32,7 +32,7 @@ class ScrollComponent constructor( private val pixelsPerScroll: Float = 15f, private val scrollAcceleration: Float = 1.0f, customScissorBoundingBox: UIComponent? = null, - private val passthroughScroll: Boolean = true + private val passthroughScroll: Boolean = true, ) : UIContainer() { @JvmOverloads constructor( emptyString: String = "", @@ -44,7 +44,7 @@ class ScrollComponent constructor( verticalScrollOpposite: Boolean = false, pixelsPerScroll: Float = 15f, scrollAcceleration: Float = 1.0f, - customScissorBoundingBox: UIComponent? = null + customScissorBoundingBox: UIComponent? = null, ) : this ( emptyString, innerPadding, @@ -59,7 +59,7 @@ class ScrollComponent constructor( verticalScrollOpposite, pixelsPerScroll, scrollAcceleration, - customScissorBoundingBox + customScissorBoundingBox, ) private val primaryScrollDirection @@ -466,10 +466,17 @@ class ScrollComponent constructor( } } + val relativeConstraint = RelativeConstraint(clampedPercentage) + val desiredSizeConstraint = if (Window.of(this).version >= ElementaVersion.v6) { + ScrollBarGripMinSizeConstraint(relativeConstraint) + } else { + relativeConstraint + } + if (isHorizontal) { - component.setWidth(RelativeConstraint(clampedPercentage)) + component.setWidth(desiredSizeConstraint) } else { - component.setHeight(RelativeConstraint(clampedPercentage)) + component.setHeight(desiredSizeConstraint) } component.animate { @@ -800,6 +807,48 @@ class ScrollComponent constructor( } + /** + * Constraints the scrollbar grip's size to be a certain minimum size, or the [desiredSize]. + * This is the default constraint for horizontal scrollbar grips if [ElementaVersion.V6] is used. + * + * @param desiredSize The intended size for the scrollbar grip. + */ + private class ScrollBarGripMinSizeConstraint( + private val desiredSize: SizeConstraint + ) : SizeConstraint { + override var cachedValue: Float = 0f + override var recalculate: Boolean = true + override var constrainTo: UIComponent? = null + + override fun animationFrame() { + super.animationFrame() + desiredSize.animationFrame() + } + + override fun getWidthImpl(component: UIComponent): Float { + val parent = component.parent + val minimumWidthPercentage = if (parent.getWidth() < 200) { 0.15f } else { 0.10f } + val minimumWidth = parent.getWidth() * minimumWidthPercentage + + return desiredSize.getWidth(component).coerceAtLeast(minimumWidth) + } + + override fun getHeightImpl(component: UIComponent): Float { + val parent = component.parent + val minimumHeightPercentage = if (parent.getHeight() < 200) { 0.15f } else { 0.10f } + val minimumHeight = parent.getHeight() * minimumHeightPercentage + + return desiredSize.getHeight(component).coerceAtLeast(minimumHeight) + } + + override fun visitImpl(visitor: ConstraintVisitor, type: ConstraintType) { + } + + override fun getRadiusImpl(component: UIComponent): Float { + throw IllegalStateException("`ScrollBarGripMinSizeConstraint` does not support `getRadiusImpl`.") + } + } + enum class Direction { Vertical, Horizontal,