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 }