Analog scroll bar, slim scroll bar, canvas scroll panel

This commit is contained in:
DBotThePony 2022-09-29 00:04:18 +07:00
parent 93b3a01ee4
commit bb689a39eb
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 412 additions and 29 deletions

View File

@ -256,6 +256,11 @@ minecraft.runs.all {
}
repositories {
// If you have mod jar dependencies in ./libs, you can declare them as a repository like so:
flatDir {
dir("libs")
}
maven {
url = uri("https://maven.dbotthepony.ru")
}
@ -285,11 +290,6 @@ repositories {
name = "Kotlin for Forge"
url = uri("https://thedarkcolour.github.io/KotlinForForge/")
}
// If you have mod jar dependencies in ./libs, you can declare them as a repository like so:
flatDir {
dir("libs")
}
}
fun org.gradle.jvm.tasks.Jar.attachManifest() {

View File

@ -171,15 +171,24 @@ class ExoSuitInventoryScreen(menu: ExoSuitInventoryMenu) : MatteryScreen<ExoSuit
var x = -4f
if (menu.curiosSlots.isNotEmpty()) {
val curiosRect = BackgroundPanel.padded(this, frame, x,
width = if (menu.curiosSlots.stream().anyMatch { it.second != null }) AbstractSlotPanel.SIZE * 2f else AbstractSlotPanel.SIZE,
height = menu.curiosSlots.size * AbstractSlotPanel.SIZE)
val curiosWidth = if (menu.curiosSlots.stream().anyMatch { it.second != null }) AbstractSlotPanel.SIZE * 2f else AbstractSlotPanel.SIZE
val curiosHeight = menu.curiosSlots.size.coerceAtMost(4) * AbstractSlotPanel.SIZE
val curiosRect =
if (menu.curiosSlots.size > 4)
ScrollbarBackgroundPanel.padded(this, frame, x,
width = curiosWidth,
height = curiosHeight, alwaysShowScrollbar = true)
else
BackgroundPanel.padded(this, frame, x,
width = curiosWidth,
height = curiosHeight)
x -= curiosRect.width
curiosRect.x = x
for ((slot, cosmetic) in menu.curiosSlots) {
val row = EditablePanel(this, curiosRect, height = AbstractSlotPanel.SIZE)
val row = EditablePanel(this, if (curiosRect is ScrollbarBackgroundPanel) curiosRect.canvas else curiosRect, height = AbstractSlotPanel.SIZE)
row.dock = Dock.TOP
SlotPanel(this, row, slot).dock = Dock.RIGHT

View File

@ -0,0 +1,179 @@
package ru.dbotthepony.mc.otm.client.screen.panels
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.core.linearInterpolation
import kotlin.math.roundToInt
open class AnalogScrollBarPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
val maxScroll: (panel: AnalogScrollBarPanel<*>) -> Float,
val scrollCallback: (panel: AnalogScrollBarPanel<*>, oldScroll: Float, newScroll: Float) -> Unit = { _, _, _ -> },
val smoothScrollCallback: (panel: AnalogScrollBarPanel<*>, oldScroll: Float, newScroll: Float) -> Unit = { _, _, _ -> },
x: Float = 0f,
y: Float = 0f,
height: Float = 20f,
open val allowSmoothScroll: Boolean = true,
open var scrollStep: Float = AbstractSlotPanel.SIZE,
isSlim: Boolean = false
) : EditablePanel<S>(screen, parent, x, y, width = if (isSlim) ScrollBarConstants.SLIM_WIDTH else ScrollBarConstants.WIDTH, height = height) {
inner class Button : EditablePanel<S>(screen, this@AnalogScrollBarPanel, 1f, 1f, this@AnalogScrollBarPanel.width - 2f, 15f) {
var isScrolling = false
private set
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
if (this@AnalogScrollBarPanel.width == ScrollBarConstants.SLIM_WIDTH) {
if (isScrolling) {
ScrollBarConstants.scrollSlimBarButtonPress.render(stack, width = width, height = height)
} else if (maxScroll.invoke(this@AnalogScrollBarPanel) <= 0) {
ScrollBarConstants.scrollSlimBarButtonDisabled.render(stack, width = width, height = height)
} else if (isHovered) {
ScrollBarConstants.scrollSlimBarButtonHover.render(stack, width = width, height = height)
} else {
ScrollBarConstants.scrollSlimBarButton.render(stack, width = width, height = height)
}
} else {
if (isScrolling) {
ScrollBarConstants.scrollBarButtonPress.render(stack, width = width, height = height)
} else if (maxScroll.invoke(this@AnalogScrollBarPanel) <= 0) {
ScrollBarConstants.scrollBarButtonDisabled.render(stack, width = width, height = height)
} else if (isHovered) {
ScrollBarConstants.scrollBarButtonHover.render(stack, width = width, height = height)
} else {
ScrollBarConstants.scrollBarButton.render(stack, width = width, height = height)
}
}
}
private var rememberScroll = 0f
private var rememberY = 0.0
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
if (maxScroll.invoke(this@AnalogScrollBarPanel) <= 0f) {
return true
}
if (button == InputConstants.MOUSE_BUTTON_LEFT && tryToGrabMouseInput()) {
isScrolling = true
rememberScroll = scroll
rememberY = y
}
return true
}
override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean {
if (isScrolling && button == InputConstants.MOUSE_BUTTON_LEFT) {
isScrolling = false
grabMouseInput = false
return true
}
return false
}
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
return this@AnalogScrollBarPanel.mouseScrolledInner(x, y, scroll)
}
override fun mouseDraggedInner(x: Double, y: Double, button: Int, xDelta: Double, yDelta: Double): Boolean {
if (isScrolling) {
val pixelsPerRow = (this@AnalogScrollBarPanel.height - height) / maxScroll.invoke(this@AnalogScrollBarPanel)
val diff = y - rememberY
this@AnalogScrollBarPanel.scroll = rememberScroll + (diff / pixelsPerRow).roundToInt()
return true
}
return false
}
}
val scrollButton = Button()
private var lastRender = System.nanoTime()
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
if (width == ScrollBarConstants.SLIM_WIDTH) {
ScrollBarConstants.scrollSlimBarBody.render(stack, y = 2f, height = height - 4f)
ScrollBarConstants.scrollSlimBarTop.render(stack)
ScrollBarConstants.scrollSlimBarBottom.render(stack, y = height - 2f)
} else {
ScrollBarConstants.scrollBarBody.render(stack, y = 2f, height = height - 4f)
ScrollBarConstants.scrollBarTop.render(stack)
ScrollBarConstants.scrollBarBottom.render(stack, y = height - 2f)
}
val time = System.nanoTime()
val diff = time - lastRender
lastRender = time
if (scroll != smoothScroll) {
smoothScroll = linearInterpolation(diff / 50_000_000.0, smoothScroll.toDouble(), scroll.toDouble()).toFloat()
}
}
public override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
this.scroll -= scroll.toFloat() * scrollStep
return true
}
var scroll = 0f
set(value) {
val newValue = value.coerceAtLeast(0f).coerceAtMost(maxScroll.invoke(this))
if (newValue != field) {
val old = field
field = newValue
scrollChanged(old, newValue)
if (!allowSmoothScroll) {
smoothScroll = newValue
}
}
}
var smoothScroll = 0f
set(value) {
val newValue = value.coerceAtLeast(0f).coerceAtMost(maxScroll.invoke(this))
if (newValue != field) {
val old = field
field = newValue
smoothScrollChanged(old, newValue)
}
}
fun hardSetScroll(scroll: Float) {
this.scroll = scroll
this.smoothScroll = scroll
}
open fun updateScrollButtonPosition() {
val maxScroll = this.maxScroll.invoke(this)
if (maxScroll <= 0f) {
scrollButton.y = 1f
return
}
val availableHeight = height - scrollButton.height - 2f
scrollButton.y = 1f + availableHeight * (scroll / maxScroll)
}
open fun scrollChanged(oldScroll: Float, newScroll: Float) {
scrollCallback.invoke(this, oldScroll, newScroll)
updateScrollButtonPosition()
}
open fun smoothScrollChanged(oldScroll: Float, newScroll: Float) {
smoothScrollCallback.invoke(this, oldScroll, newScroll)
}
override fun performLayout() {
super.performLayout()
updateScrollButtonPosition()
}
}

View File

@ -5,20 +5,32 @@ import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.screens.Screen
import kotlin.math.roundToInt
open class DiscreteScrollBarPanel<S : Screen> @JvmOverloads constructor(
open class DiscreteScrollBarPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
val maxScroll: (panel: DiscreteScrollBarPanel<S>) -> Int,
val scrollCallback: (panel: DiscreteScrollBarPanel<S>, oldScroll: Int, newScroll: Int) -> Unit,
val maxScroll: (panel: DiscreteScrollBarPanel<*>) -> Int,
val scrollCallback: (panel: DiscreteScrollBarPanel<*>, oldScroll: Int, newScroll: Int) -> Unit,
x: Float = 0f,
y: Float = 0f,
height: Float = 20f
) : EditablePanel<S>(screen, parent, x, y, width = ScrollBarConstants.WIDTH, height = height) {
open inner class Button : EditablePanel<S>(screen, this@DiscreteScrollBarPanel, 1f, 1f, 12f, 15f) {
height: Float = 20f,
isSlim: Boolean = false
) : EditablePanel<S>(screen, parent, x, y, width = if (isSlim) ScrollBarConstants.SLIM_WIDTH else ScrollBarConstants.WIDTH, height = height) {
inner class Button : EditablePanel<S>(screen, this@DiscreteScrollBarPanel, 1f, 1f, this@DiscreteScrollBarPanel.width - 12f, 15f) {
var isScrolling = false
protected set
private set
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
if (this@DiscreteScrollBarPanel.width == ScrollBarConstants.SLIM_WIDTH) {
if (isScrolling) {
ScrollBarConstants.scrollSlimBarButtonPress.render(stack, width = width, height = height)
} else if (maxScroll.invoke(this@DiscreteScrollBarPanel) <= 0) {
ScrollBarConstants.scrollSlimBarButtonDisabled.render(stack, width = width, height = height)
} else if (isHovered) {
ScrollBarConstants.scrollSlimBarButtonHover.render(stack, width = width, height = height)
} else {
ScrollBarConstants.scrollSlimBarButton.render(stack, width = width, height = height)
}
} else {
if (isScrolling) {
ScrollBarConstants.scrollBarButtonPress.render(stack, width = width, height = height)
} else if (maxScroll.invoke(this@DiscreteScrollBarPanel) <= 0) {
@ -29,8 +41,9 @@ open class DiscreteScrollBarPanel<S : Screen> @JvmOverloads constructor(
ScrollBarConstants.scrollBarButton.render(stack, width = width, height = height)
}
}
}
protected var rememberScroll = 0
private var rememberScroll = 0
private var rememberY = 0.0
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
@ -77,9 +90,15 @@ open class DiscreteScrollBarPanel<S : Screen> @JvmOverloads constructor(
val scrollButton = Button()
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
ScrollBarConstants.scrollBarBody.render(stack, y = 2f, height = height - 4)
if (width == ScrollBarConstants.SLIM_WIDTH) {
ScrollBarConstants.scrollSlimBarBody.render(stack, y = 2f, height = height - 4f)
ScrollBarConstants.scrollSlimBarTop.render(stack)
ScrollBarConstants.scrollSlimBarBottom.render(stack, y = height - 2f)
} else {
ScrollBarConstants.scrollBarBody.render(stack, y = 2f, height = height - 4f)
ScrollBarConstants.scrollBarTop.render(stack)
ScrollBarConstants.scrollBarBottom.render(stack, y = height - 2)
ScrollBarConstants.scrollBarBottom.render(stack, y = height - 2f)
}
}
public override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {

View File

@ -5,7 +5,8 @@ import ru.dbotthepony.mc.otm.client.render.element
object ScrollBarConstants {
const val WIDTH = 14f
const val TEXTURE_WIDTH = 14f
const val SLIM_WIDTH = 8f
const val TEXTURE_WIDTH = 22f
const val TEXTURE_HEIGHT = 68f
val scrollBarTop = WidgetLocation.SCROLL.element(0f, 45f, 14f, 2f, TEXTURE_WIDTH, TEXTURE_HEIGHT)
@ -16,4 +17,13 @@ object ScrollBarConstants {
val scrollBarButtonHover = WidgetLocation.SCROLL.element(0f, 15f, 12f, 15f, TEXTURE_WIDTH, TEXTURE_HEIGHT)
val scrollBarButtonPress = WidgetLocation.SCROLL.element(0f, 30f, 12f, 15f, TEXTURE_WIDTH, TEXTURE_HEIGHT)
val scrollBarButtonDisabled = WidgetLocation.SCROLL.element(0f, 53f, 12f, 15f, TEXTURE_WIDTH, TEXTURE_HEIGHT)
val scrollSlimBarTop = WidgetLocation.SCROLL.element(14f, 45f, 8f, 2f, TEXTURE_WIDTH, TEXTURE_HEIGHT)
val scrollSlimBarBody = WidgetLocation.SCROLL.element(14f, 46f, 8f, 6f, TEXTURE_WIDTH, TEXTURE_HEIGHT)
val scrollSlimBarBottom = WidgetLocation.SCROLL.element(14f, 51f, 8f, 2f, TEXTURE_WIDTH, TEXTURE_HEIGHT)
val scrollSlimBarButton = WidgetLocation.SCROLL.element(14f, 0f, 6f, 15f, TEXTURE_WIDTH, TEXTURE_HEIGHT)
val scrollSlimBarButtonHover = WidgetLocation.SCROLL.element(14f, 15f, 6f, 15f, TEXTURE_WIDTH, TEXTURE_HEIGHT)
val scrollSlimBarButtonPress = WidgetLocation.SCROLL.element(14f, 30f, 6f, 15f, TEXTURE_WIDTH, TEXTURE_HEIGHT)
val scrollSlimBarButtonDisabled = WidgetLocation.SCROLL.element(14f, 53f, 6f, 15f, TEXTURE_WIDTH, TEXTURE_HEIGHT)
}

View File

@ -0,0 +1,166 @@
package ru.dbotthepony.mc.otm.client.screen.panels
import net.minecraft.client.gui.screens.Screen
open class ScrollbarBackgroundPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = 30f,
height: Float = 30f,
alwaysShowScrollbar: Boolean = false,
autoResizeForScrollbar: Boolean = true,
autoMoveForScrollbar: Boolean = true,
scrollStep: Float = AbstractSlotPanel.SIZE,
) : BackgroundPanel<S>(screen, parent, x, y, width, height) {
/**
* Whenever is to hide scrollbar when there is nothing to scroll
*/
var alwaysShowScrollbar = alwaysShowScrollbar
set(value) {
if (field != value) {
field = value
determineScrollbarVisible()
}
}
private var lastResizedForScrollbar: Boolean = false
private var lastMovedForScrollbar: Boolean = false
/**
* Whenever is to auto resize width when scrollbar has to be shown
*
* Has no effect if [alwaysShowScrollbar] is true
*/
var autoResizeForScrollbar = autoResizeForScrollbar
set(value) {
if (field != value) {
field = value
determineScrollbarVisible()
}
}
/**
* Whenever is to auto move panel along X axis when scrollbar has to be shown (given no [dock] is [Dock.NONE])
*
* Has no effect if [alwaysShowScrollbar] is true
*/
var autoMoveForScrollbar = autoMoveForScrollbar
set(value) {
if (field != value) {
field = value
determineScrollbarVisible()
}
}
private fun getMaxScroll(it: AnalogScrollBarPanel<*>): Float {
return (canvas.childrenRectHeight - canvas.height).coerceAtLeast(0f)
}
private fun onScrollUpdate(it: AnalogScrollBarPanel<*>, old: Float, new: Float) {
this.canvas.yOffset = -new
}
val scrollbar = AnalogScrollBarPanel(screen, this, this::getMaxScroll, smoothScrollCallback = this::onScrollUpdate, isSlim = true, scrollStep = scrollStep)
fun determineScrollbarVisible() {
if (alwaysShowScrollbar) {
scrollbar.visible = true
if (lastResizedForScrollbar) {
width -= scrollbar.width
lastResizedForScrollbar = false
}
if (lastMovedForScrollbar) {
x += scrollbar.width
lastMovedForScrollbar = false
}
} else {
if (canvas.childrenRectHeight > (height - dockPadding.top - dockPadding.bottom)) {
if (!scrollbar.visible) {
scrollbar.visible = true
if (autoResizeForScrollbar) {
width += scrollbar.width
lastResizedForScrollbar = true
}
if (autoMoveForScrollbar && dock == Dock.NONE) {
x -= scrollbar.width
lastMovedForScrollbar = true
}
}
} else {
if (scrollbar.visible) {
scrollbar.visible = false
if (lastResizedForScrollbar) {
width -= scrollbar.width
lastResizedForScrollbar = false
}
if (lastMovedForScrollbar) {
x += scrollbar.width
lastMovedForScrollbar = false
}
}
}
}
}
val canvas = object : EditablePanel<S>(screen, this@ScrollbarBackgroundPanel) {
init {
scissor = true
}
override fun performLayout() {
super.performLayout()
determineScrollbarVisible()
}
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
scrollbar.mouseScrolledInner(x, y, scroll)
return true
}
}
fun addPanel(panel: EditablePanel<*>) {
panel.parent = canvas
}
init {
scrollbar.dock = Dock.RIGHT
canvas.dock = Dock.FILL
scrollbar.visible = alwaysShowScrollbar
}
companion object {
fun <S : Screen> padded(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = 30f,
height: Float = 30f,
alwaysShowScrollbar: Boolean = false,
autoResizeForScrollbar: Boolean = true,
autoMoveForScrollbar: Boolean = true,
scrollStep: Float = AbstractSlotPanel.SIZE,
) = ScrollbarBackgroundPanel(screen, parent, x, y, width + 6f + (if (alwaysShowScrollbar) ScrollBarConstants.SLIM_WIDTH else 0f), height + 6f, alwaysShowScrollbar, autoResizeForScrollbar, autoMoveForScrollbar, scrollStep)
fun <S : Screen> paddedCenter(
screen: S,
parent: EditablePanel<*>?,
x: Float = 0f,
y: Float = 0f,
width: Float = 30f,
height: Float = 30f,
alwaysShowScrollbar: Boolean = false,
autoResizeForScrollbar: Boolean = true,
autoMoveForScrollbar: Boolean = true,
scrollStep: Float = AbstractSlotPanel.SIZE,
) = ScrollbarBackgroundPanel(screen, parent, x - 3f, y - 3f - (if (alwaysShowScrollbar) ScrollBarConstants.SLIM_WIDTH / 2f else 0f), width + 6f + (if (alwaysShowScrollbar) ScrollBarConstants.SLIM_WIDTH else 0f), height + 6f, alwaysShowScrollbar, autoResizeForScrollbar, autoMoveForScrollbar, scrollStep)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 714 B

After

Width:  |  Height:  |  Size: 779 B