mirror of
https://gitlab.com/cc-ru/ocelot/ocelot-desktop.git
synced 2025-12-20 02:59:19 +01:00
166 lines
4.6 KiB
Scala
166 lines
4.6 KiB
Scala
package ocelot.desktop.ui.widget
|
|
|
|
import ocelot.desktop.color.RGBAColorNorm
|
|
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
|
|
import ocelot.desktop.graphics.Graphics
|
|
import ocelot.desktop.ui.event.{MouseEvent, ScrollEvent}
|
|
import ocelot.desktop.ui.{KeyEvents, UiHandler}
|
|
import ocelot.desktop.util.Logging
|
|
|
|
class ScrollView(val inner: Widget) extends Widget with Logging {
|
|
inner.size = Size2D.Zero
|
|
children +:= inner
|
|
|
|
private var xOffset = 0f
|
|
private var yOffset = 0f
|
|
private var xAcceleration = 0f
|
|
private var yAcceleration = 0f
|
|
private var dragging = 0
|
|
private var mouseOldPos = Vector2D(0, 0)
|
|
|
|
def offset: Vector2D = Vector2D(xOffset, yOffset)
|
|
|
|
receiveMouseEvents = true
|
|
|
|
eventHandlers += {
|
|
case ScrollEvent(offset) =>
|
|
if (bounds.contains(UiHandler.mousePosition))
|
|
if (KeyEvents.isDown(42)) // shift
|
|
xAcceleration -= offset * 385f
|
|
else
|
|
yAcceleration -= offset * 385f
|
|
|
|
case event: MouseEvent if event.state == MouseEvent.State.Press =>
|
|
val pos = UiHandler.mousePosition
|
|
mouseOldPos = pos
|
|
|
|
if (vThumbBounds.contains(pos))
|
|
dragging = 1
|
|
else if (hThumbBounds.contains(pos))
|
|
dragging = 2
|
|
else
|
|
dragging = 0
|
|
|
|
case event: MouseEvent if event.state == MouseEvent.State.Release =>
|
|
dragging = 0
|
|
}
|
|
|
|
override def minimumSize: Size2D = Size2D.Zero
|
|
|
|
override def maximumSize: Size2D = inner.maximumSize
|
|
|
|
shouldClip = true
|
|
|
|
override def draw(g: Graphics): Unit = {
|
|
if (xOffset.abs > 0 || yOffset.abs > 0) {
|
|
inner.position = position - Vector2D(xOffset, yOffset)
|
|
}
|
|
|
|
inner.draw(g)
|
|
|
|
g.setScissor()
|
|
|
|
if (vThumbVisible)
|
|
drawVThumb(g)
|
|
|
|
if (hThumbVisible)
|
|
drawHThumb(g)
|
|
}
|
|
|
|
override def size_=(value: Size2D): Unit = {
|
|
super.size_=(value)
|
|
// inner.size = value
|
|
clampOffsets()
|
|
}
|
|
|
|
private var vAnim = 0f
|
|
private var vAnimDir = -1
|
|
private var hAnim = 0f
|
|
private var hAnimDir = -1
|
|
|
|
override def update(): Unit = {
|
|
super.update()
|
|
|
|
xAcceleration *= 1f - 5f * UiHandler.dt
|
|
xAcceleration = xAcceleration.min(1540f).max(-1540f)
|
|
xOffset += xAcceleration * UiHandler.dt
|
|
|
|
yAcceleration *= 1f - 5f * UiHandler.dt
|
|
yAcceleration = yAcceleration.min(1540f).max(-1540f)
|
|
yOffset += yAcceleration * UiHandler.dt
|
|
|
|
clampOffsets()
|
|
|
|
val mousePos = UiHandler.mousePosition
|
|
|
|
vAnimDir = if (vThumbHoverArea.contains(mousePos) || dragging == 1) 1 else -1
|
|
hAnimDir = if (hThumbHoverArea.contains(mousePos) || dragging == 2) 1 else -1
|
|
|
|
vAnim = math.max(0f, math.min(1f, vAnim + UiHandler.dt / 0.2f * vAnimDir))
|
|
hAnim = math.max(0f, math.min(1f, hAnim + UiHandler.dt / 0.2f * hAnimDir))
|
|
|
|
if (dragging == 0) return
|
|
|
|
if (dragging == 1) {
|
|
val dy = (mousePos - mouseOldPos).y
|
|
yOffset += dy / vThumbCoeff
|
|
} else if (dragging == 2) {
|
|
val dx = (mousePos - mouseOldPos).x
|
|
xOffset += dx / hThumbCoeff
|
|
}
|
|
|
|
clampOffsets()
|
|
mouseOldPos = mousePos
|
|
}
|
|
|
|
private def drawVThumb(g: Graphics): Unit = {
|
|
val b = vThumbBounds
|
|
g.rect(position.x + size.width - 11, position.y, 10, size.height, RGBAColorNorm(0.9f, 0.9f, 0.9f, vAnim * 0.15f))
|
|
g.rect(b.x + 3, b.y, b.w - 6, b.h, RGBAColorNorm(0.8f, 0.25f, 0.45f, vAnim * 0.5f + 0.4f))
|
|
}
|
|
|
|
private def drawHThumb(g: Graphics): Unit = {
|
|
val b = hThumbBounds
|
|
g.rect(position.x, position.y + size.height - 11, size.width - 12, 10, RGBAColorNorm(0.9f, 0.9f, 0.9f, hAnim * 0.15f))
|
|
g.rect(b.x, b.y + 3, b.w, b.h - 6, RGBAColorNorm(0.8f, 0.25f, 0.45f, hAnim * 0.5f + 0.4f))
|
|
}
|
|
|
|
private def maxXOffset: Float = inner.size.width - size.width
|
|
private def maxYOffset: Float = inner.size.height - size.height
|
|
|
|
private def clampOffsets(): Unit = {
|
|
xOffset = xOffset.min(maxXOffset).max(0f)
|
|
yOffset = yOffset.min(maxYOffset).max(0f)
|
|
}
|
|
|
|
private def vThumbHoverArea: Rect2D = {
|
|
Rect2D(position.x + size.width - 12, position.y, 12, size.height)
|
|
}
|
|
|
|
private def vThumbBounds: Rect2D = {
|
|
val x = position.x + size.width - 12
|
|
val y = position.y + yOffset * vThumbCoeff
|
|
|
|
Rect2D(x, y + 2, 12, vThumbCoeff * size.height)
|
|
}
|
|
|
|
private def hThumbHoverArea: Rect2D = {
|
|
Rect2D(position.x, position.y + size.height - 12, size.width, 12)
|
|
}
|
|
|
|
private def hThumbBounds: Rect2D = {
|
|
val x = position.x + xOffset * hThumbCoeff
|
|
val y = position.y + size.height - 12
|
|
|
|
Rect2D(x + 2, y, hThumbCoeff * size.width, 12)
|
|
}
|
|
|
|
private def vThumbVisible: Boolean = inner.size.height > size.height
|
|
|
|
private def hThumbVisible: Boolean = inner.size.width > size.width
|
|
|
|
private def vThumbCoeff: Float = (size.height - 4) / inner.size.height
|
|
|
|
private def hThumbCoeff: Float = (size.width - 14) / inner.size.width
|
|
}
|