diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/UIComponent.kt b/src/main/kotlin/net/prismclient/aether/ui/component/UIComponent.kt index f4065f2..bac50dd 100644 --- a/src/main/kotlin/net/prismclient/aether/ui/component/UIComponent.kt +++ b/src/main/kotlin/net/prismclient/aether/ui/component/UIComponent.kt @@ -8,7 +8,6 @@ import net.prismclient.aether.ui.component.util.interfaces.UIFocusable import net.prismclient.aether.ui.style.UIProvider import net.prismclient.aether.ui.style.UIStyleSheet import net.prismclient.aether.ui.unit.UIUnit -import net.prismclient.aether.ui.unit.util.* import net.prismclient.aether.ui.util.UIKey import net.prismclient.aether.ui.util.extensions.calculateX import net.prismclient.aether.ui.util.extensions.calculateY @@ -135,7 +134,7 @@ abstract class UIComponent(style: String) { calculateUnitX(this, width, ignore) fun calculateUnitX(unit: UIUnit?, width: Float, ignore: Boolean): Float = if (unit == null) 0f else { - calculateX(unit, this, ignore, width) + calculateX(unit, this, width, ignore) } @JvmOverloads @@ -146,7 +145,7 @@ abstract class UIComponent(style: String) { calculateUnitY(this, height, ignore) fun calculateUnitY(unit: UIUnit?, height: Float, ignore: Boolean): Float = if (unit == null) 0f else { - calculateY(unit, this, ignore, height) + calculateY(unit, this, height, ignore) } fun getParentX() = if (parent != null) @@ -184,10 +183,10 @@ abstract class UIComponent(style: String) { fun isFocused(): Boolean = UICore.instance.focusedComponent == this - protected fun getMouseX(): Float = + fun getMouseX(): Float = UICore.mouseX - getParentXOffset() - protected fun getMouseY(): Float = + fun getMouseY(): Float = UICore.mouseY - getParentYOffset() protected fun getParentXOffset(): Float = @@ -195,4 +194,7 @@ abstract class UIComponent(style: String) { protected fun getParentYOffset(): Float = if (parent != null && parent is UIFrame && (parent!!.style as UIFrameSheet).clipContent) parent!!.relY else 0f + + protected fun isWithinBounds(x: Float, y: Float, width: Float, height: Float) = + (x <= getMouseX() && y <= getMouseY() && x + width >= getMouseX() && y + height >= getMouseY()) } \ No newline at end of file diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/slider/UISlider.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/slider/UISlider.kt index e0def47..9f08bcd 100644 --- a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/slider/UISlider.kt +++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/slider/UISlider.kt @@ -1,7 +1,7 @@ package net.prismclient.aether.ui.component.type.input.slider import net.prismclient.aether.ui.component.UIComponent -import net.prismclient.aether.ui.util.extensions.renderer +import kotlin.math.roundToInt /** * [UISlider] @@ -9,29 +9,50 @@ import net.prismclient.aether.ui.util.extensions.renderer * @author sen * @since 5/13/2022 */ -open class UISlider( - var value: T, - var min: T, - var max: T, - var step: T, - style: String -) : UIComponent(style) { - protected var controlX = 0f - protected var controlY = 0f - protected var controlWidth = 0f - protected var controlHeight = 0f +open class UISlider(var value: Float, var min: Float, var max: Float, var step: Float, style: String) : UIComponent(style) { + protected var normalizedValue = value / (max - min) + + protected var offsetX = 0f + protected var selected = false override fun update() { super.update() - controlX = style.controlX.getX(relWidth) - controlY = style.controlY.getY(relHeight) - controlWidth = style.controlWidth.getX(relWidth) - controlHeight = style.controlHeight.getY(relHeight) + style.sliderControl.update(this) } override fun renderComponent() { - renderer { - + style.sliderControl.offsetX = normalizedValue * (relWidth - style.sliderControl.cachedWidth) + style.sliderControl.render() + } + + override fun mouseMoved(mouseX: Float, mouseY: Float) { + super.mouseMoved(mouseX, mouseY) + if (selected) { // I got lazy + normalizedValue = (((mouseX - offsetX - style.sliderControl.cachedX)) / (relWidth - style.sliderControl.cachedWidth)) + .coerceAtLeast(0f) + .coerceAtMost(1f) + value = (max - min) * normalizedValue + min + value = (value / step).roundToInt() * step + normalizedValue = value / (max - min) } } + + override fun mouseClicked(mouseX: Float, mouseY: Float) { + super.mouseClicked(mouseX, mouseY) + + if (isWithinBounds( + style.sliderControl.cachedX + style.sliderControl.offsetX, + style.sliderControl.cachedY + style.sliderControl.offsetY, + style.sliderControl.cachedWidth, + style.sliderControl.cachedHeight + )) { + offsetX = mouseX - (style.sliderControl.cachedX + style.sliderControl.offsetX) + selected = true + } + } + + override fun mouseReleased(mouseX: Float, mouseY: Float) { + super.mouseReleased(mouseX, mouseY) + selected = false + } } \ No newline at end of file diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/slider/UISliderSheet.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/slider/UISliderSheet.kt index 615853b..f5cc608 100644 --- a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/slider/UISliderSheet.kt +++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/slider/UISliderSheet.kt @@ -1,19 +1,24 @@ package net.prismclient.aether.ui.component.type.input.slider +import net.prismclient.aether.ui.shape.UIShape +import net.prismclient.aether.ui.shape.type.UIRectangle import net.prismclient.aether.ui.style.UIStyleSheet -import net.prismclient.aether.ui.unit.UIUnit import net.prismclient.aether.ui.util.extensions.px +import net.prismclient.aether.ui.util.extensions.radius import net.prismclient.aether.ui.util.extensions.rel class UISliderSheet : UIStyleSheet() { - var controlX: UIUnit = rel(0.5) - var controlY: UIUnit = rel(0.5) - var controlWidth: UIUnit = px(40) - var controlHeight: UIUnit = rel(1) + var sliderControl: UIShape = UIRectangle().also { + it.color = -1 + it.width = px(40) + it.height = rel(1) + it.radius = radius(2.5f) + } override fun copy(): UIStyleSheet { val it = UISliderSheet() it.apply(this) + it.sliderControl = sliderControl.copy() return it } } \ No newline at end of file diff --git a/src/main/kotlin/net/prismclient/aether/ui/renderer/impl/property/UIRadius.kt b/src/main/kotlin/net/prismclient/aether/ui/renderer/impl/property/UIRadius.kt index dfbe7c8..7645365 100644 --- a/src/main/kotlin/net/prismclient/aether/ui/renderer/impl/property/UIRadius.kt +++ b/src/main/kotlin/net/prismclient/aether/ui/renderer/impl/property/UIRadius.kt @@ -13,7 +13,7 @@ class UIRadius( var topRight: Float = 0f, var bottomRight: Float = 0f, var bottomLeft: Float = 0f -) : UICopy { // TODO: Convert to UIUnit +) : UICopy { constructor(radius: Float) : this(radius, radius, radius, radius) fun set(radius: Float) { diff --git a/src/main/kotlin/net/prismclient/aether/ui/renderer/other/UIRenderable.kt b/src/main/kotlin/net/prismclient/aether/ui/renderer/other/UIRenderable.kt new file mode 100644 index 0000000..44fa7d6 --- /dev/null +++ b/src/main/kotlin/net/prismclient/aether/ui/renderer/other/UIRenderable.kt @@ -0,0 +1,15 @@ +package net.prismclient.aether.ui.renderer.other + +import net.prismclient.aether.ui.component.UIComponent + +/** + * [UIRenderable] + * + * @author sen + * @since 5/13/2022 + */ +interface UIRenderable { + fun update(component: UIComponent<*>) + + fun render() +} \ No newline at end of file diff --git a/src/main/kotlin/net/prismclient/aether/ui/screen/UIScreen.kt b/src/main/kotlin/net/prismclient/aether/ui/screen/UIScreen.kt index 31179bc..57aef2f 100644 --- a/src/main/kotlin/net/prismclient/aether/ui/screen/UIScreen.kt +++ b/src/main/kotlin/net/prismclient/aether/ui/screen/UIScreen.kt @@ -17,7 +17,7 @@ abstract class UIScreen { // components.forEach(UIComponent::initialize) } - // protected inline fun style(block: UIStyle.() -> Unit) = UIStyle.block() + // protected inline fun style(block: UIStyle.() -> Unit) = UIStyle.block() // protected inline fun build(block: UIBuilder.() -> Unit) = UIBuilder.block() @@ -29,25 +29,23 @@ abstract class UIScreen { } open fun renderContent() { - for (i in 0 until frames.size) - frames[i].renderContent() + for (i in 0 until frames.size) frames[i].renderContent() } open fun render() { - for (i in 0 until components.size) - frames[i].render() + for (i in 0 until components.size) components[i].render() } open fun mouseMoved(mouseX: Float, mouseY: Float) { - components.forEach { it.mouseMoved(mouseX, mouseY) } + components.forEach { it.mouseMoved(it.getMouseX(), it.getMouseY()) } } open fun mousePressed(mouseX: Float, mouseY: Float) { - components.forEach { it.mouseClicked(mouseX, mouseY) } + components.forEach { it.mouseClicked(it.getMouseX(), it.getMouseY()) } } open fun mouseReleased(mouseX: Float, mouseY: Float) { - components.forEach { it.mouseReleased(mouseX, mouseY) } + components.forEach { it.mouseReleased(it.getMouseX(), it.getMouseY()) } } open fun keyPressed(key: UIKey, character: Char) { @@ -55,7 +53,7 @@ abstract class UIScreen { } open fun mouseScrolled(mouseX: Float, mouseY: Float, scrollAmount: Float) { - components.forEach { it.mouseScrolled(mouseX, mouseY, scrollAmount) } + components.forEach { it.mouseScrolled(it.getMouseX(), it.getMouseY(), scrollAmount) } } } \ No newline at end of file diff --git a/src/main/kotlin/net/prismclient/aether/ui/shape/UIShape.kt b/src/main/kotlin/net/prismclient/aether/ui/shape/UIShape.kt new file mode 100644 index 0000000..4f2df23 --- /dev/null +++ b/src/main/kotlin/net/prismclient/aether/ui/shape/UIShape.kt @@ -0,0 +1,45 @@ +package net.prismclient.aether.ui.shape + +import net.prismclient.aether.ui.component.UIComponent +import net.prismclient.aether.ui.renderer.other.UIRenderable +import net.prismclient.aether.ui.unit.UIUnit +import net.prismclient.aether.ui.util.UICopy +import net.prismclient.aether.ui.util.extensions.calculateX +import net.prismclient.aether.ui.util.extensions.calculateY + +abstract class UIShape : UIRenderable, UICopy { + var color = 0 + var x: UIUnit? = null + var y: UIUnit? = null + var width: UIUnit? = null + var height: UIUnit? = null + + var offsetX = 0f + var offsetY = 0f + + var cachedX = 0f + protected set + var cachedY = 0f + protected set + var cachedWidth = 0f + protected set + var cachedHeight = 0f + protected set + + + override fun update(component: UIComponent<*>) { + cachedX = calculateX(x, component, component.relWidth) + component.relX + cachedY = calculateY(y, component, component.relHeight) + component.relY + cachedWidth = calculateX(width, component, component.relWidth) + cachedHeight = calculateY(height, component, component.relHeight) + } + + open fun apply(shape: UIShape): UIShape { + x = shape.x?.copy() + y = shape.y?.copy() + width = shape.width?.copy() + height = shape.height?.copy() + color = shape.color + return this + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/prismclient/aether/ui/shape/type/UIRectangle.kt b/src/main/kotlin/net/prismclient/aether/ui/shape/type/UIRectangle.kt new file mode 100644 index 0000000..873f92b --- /dev/null +++ b/src/main/kotlin/net/prismclient/aether/ui/shape/type/UIRectangle.kt @@ -0,0 +1,27 @@ +package net.prismclient.aether.ui.shape.type + +import net.prismclient.aether.ui.renderer.impl.property.UIRadius +import net.prismclient.aether.ui.shape.UIShape +import net.prismclient.aether.ui.util.extensions.renderer + +/** + * [UIRectangle] + * + * @author sen + * @since 5/13/2022 + */ +class UIRectangle : UIShape() { + var radius: UIRadius? = null + + override fun render() { + renderer { + color(color) + rect(cachedX + offsetX, cachedY + offsetY, cachedWidth, cachedHeight, radius) + } + } + + override fun copy(): UIShape = + UIRectangle().also { + it.radius = radius?.copy() + }.apply(this) +} \ No newline at end of file diff --git a/src/main/kotlin/net/prismclient/aether/ui/style/UIStyleSheet.kt b/src/main/kotlin/net/prismclient/aether/ui/style/UIStyleSheet.kt index 748c7b0..2ab16e5 100644 --- a/src/main/kotlin/net/prismclient/aether/ui/style/UIStyleSheet.kt +++ b/src/main/kotlin/net/prismclient/aether/ui/style/UIStyleSheet.kt @@ -47,6 +47,16 @@ open class UIStyleSheet : UICopy { return this } + fun position(x: UIUnit, y: UIUnit) { + this.x = x + this.y = y + } + + fun size(width: UIUnit, height: UIUnit) { + this.width = width + this.height = height + } + inline fun background(block: UIBackground.() -> Unit) { background = UIBackground() background!!.block() diff --git a/src/main/kotlin/net/prismclient/aether/ui/util/UICopy.kt b/src/main/kotlin/net/prismclient/aether/ui/util/UICopy.kt index d91a362..7173532 100644 --- a/src/main/kotlin/net/prismclient/aether/ui/util/UICopy.kt +++ b/src/main/kotlin/net/prismclient/aether/ui/util/UICopy.kt @@ -1,7 +1,7 @@ package net.prismclient.aether.ui.util /** - * Returns a new instance of it's self + * Returns a new instance of [Self] * * @author sen * @since 4/26/2022 diff --git a/src/main/kotlin/net/prismclient/aether/ui/util/extensions/Component.kt b/src/main/kotlin/net/prismclient/aether/ui/util/extensions/Component.kt deleted file mode 100644 index 4389005..0000000 --- a/src/main/kotlin/net/prismclient/aether/ui/util/extensions/Component.kt +++ /dev/null @@ -1,35 +0,0 @@ -//package net.prismclient.util.extensions -// -//import net.prismclient.ui.component__.UIComponent -//import net.prismclient.aether.UIAlignment -//import net.prismclient.ui.component__.style.UIStyleSheet -//import net.prismclient.aether.UITheme -// -//inline infix fun T.style(block: UIStyleSheet.() -> Unit) = apply { -// style.block() -// update() -//} -// -//infix fun T.style(alignment: UIAlignment) = apply { -// style.alignment = alignment -// update() -//} -// -//inline fun T.style(alignment: UIAlignment, block: UIStyleSheet.() -> Unit) = apply { -// style.alignment = alignment -// style.block() -// update() -//} -//// -////inline infix fun T.animate(block: UIAnimation.() -> Unit) = apply { -//// animation.block() -//// update() -////} -// -//infix fun T.apply(component: UIComponent) = apply { -// style.applyStyle(component.style) -//} -// -//inline infix fun T.theme(block: UITheme.() -> Unit) = apply { -// theme.block() -//} \ No newline at end of file diff --git a/src/main/kotlin/net/prismclient/aether/ui/util/extensions/Units.kt b/src/main/kotlin/net/prismclient/aether/ui/util/extensions/Units.kt index fe684ac..7044619 100644 --- a/src/main/kotlin/net/prismclient/aether/ui/util/extensions/Units.kt +++ b/src/main/kotlin/net/prismclient/aether/ui/util/extensions/Units.kt @@ -44,8 +44,8 @@ operator fun UIUnit.div(unit: UIUnit) = UIOperationUnit(this, unit, DIVIDE) * @param ignoreOperation When true, the calculation will to care if the [UIUnit] is an instance of [UIOperationUnit] */ @JvmOverloads -fun calculateX(unit: UIUnit, component: UIComponent<*>, ignoreOperation: Boolean = false, width: Float = component.getParentWidth()): Float { - return if (!ignoreOperation && unit is UIOperationUnit) calculateX(unit, component, width) +fun calculateX(unit: UIUnit?, component: UIComponent<*>, width: Float = component.getParentWidth(), ignoreOperation: Boolean = false): Float { + return if (unit == null) 0f else if (!ignoreOperation && unit is UIOperationUnit) calculateX(unit, component, width) else when (unit.type) { PIXELS, PXANIMRELATIVE -> unit.value RELATIVE, RELANIMRELATIVE -> unit.value * width @@ -60,10 +60,10 @@ fun calculateX(unit: UIUnit, component: UIComponent<*>, ignoreOperation: Boolea * Returns the value of a [UIOperationUnit] on the y-axis */ fun calculateX(operationUnit: UIOperationUnit, component: UIComponent<*>, width: Float): Float = when (operationUnit.operation) { - ADD -> calculateX(operationUnit.unit1, component, false, width) + calculateX(operationUnit.unit2, component, false, width) - SUBTRACT -> calculateX(operationUnit.unit1, component, false, width) - calculateX(operationUnit.unit2, component, false, width) - MULTIPLY -> calculateX(operationUnit.unit1, component, false, width) * calculateX(operationUnit.unit2, component, false, width) - DIVIDE -> calculateX(operationUnit.unit1, component, false, width) / calculateX(operationUnit.unit2, component, false, width) + ADD -> calculateX(operationUnit.unit1, component, width, false) + calculateX(operationUnit.unit2, component, width, false) + SUBTRACT -> calculateX(operationUnit.unit1, component, width, false) - calculateX(operationUnit.unit2, component, width, false) + MULTIPLY -> calculateX(operationUnit.unit1, component, width, false) * calculateX(operationUnit.unit2, component, width, false) + DIVIDE -> calculateX(operationUnit.unit1, component, width, false) / calculateX(operationUnit.unit2, component, width, false) } /** @@ -72,8 +72,8 @@ fun calculateX(operationUnit: UIOperationUnit, component: UIComponent<*>, width: * @param ignoreOperation When true, the calculation will to care if the [UIUnit] is an instance of [UIOperationUnit] */ @JvmOverloads -fun calculateY(unit: UIUnit, component: UIComponent<*>, ignoreOperation: Boolean = false, height: Float): Float { - return if (!ignoreOperation && unit is UIOperationUnit) calculateY(unit, component, height) +fun calculateY(unit: UIUnit?, component: UIComponent<*>, height: Float, ignoreOperation: Boolean = false): Float { + return if (unit == null) 0f else if (!ignoreOperation && unit is UIOperationUnit) calculateY(unit, component, height) else when (unit.type) { PIXELS, PXANIMRELATIVE -> unit.value RELATIVE, RELANIMRELATIVE -> unit.value * height @@ -88,10 +88,10 @@ fun calculateY(unit: UIUnit, component: UIComponent<*>, ignoreOperation: Boolean * Returns the value of a [UIOperationUnit] on the y-axis */ fun calculateY(operationUnit: UIOperationUnit, component: UIComponent<*>, height: Float): Float = when (operationUnit.operation) { - ADD -> calculateY(operationUnit.unit1, component, false, height) + calculateY(operationUnit.unit2, component, false, height) - SUBTRACT -> calculateY(operationUnit.unit1, component, false, height) - calculateY(operationUnit.unit2, component, false, height) - MULTIPLY -> calculateY(operationUnit.unit1, component, false, height) * calculateY(operationUnit.unit2, component, false, height) - DIVIDE -> calculateY(operationUnit.unit1, component, false, height) / calculateY(operationUnit.unit2, component, false, height) + ADD -> calculateY(operationUnit.unit1, component, height, false) + calculateY(operationUnit.unit2, component, height, false) + SUBTRACT -> calculateY(operationUnit.unit1, component, height, false) - calculateY(operationUnit.unit2, component, height, false) + MULTIPLY -> calculateY(operationUnit.unit1, component, height, false) * calculateY(operationUnit.unit2, component, height, false) + DIVIDE -> calculateY(operationUnit.unit1, component, height, false) / calculateY(operationUnit.unit2, component, height, false) } /** Other **/ diff --git a/src/test/java/net/prismclient/aether/Example.java b/src/test/java/net/prismclient/aether/Example.java index a663a33..294ebdd 100644 --- a/src/test/java/net/prismclient/aether/Example.java +++ b/src/test/java/net/prismclient/aether/Example.java @@ -6,6 +6,7 @@ import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GL20; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.Platform; @@ -117,6 +118,7 @@ public static void main(String[] args) { core.update(); }); + glfwSetWindowContentScaleCallback(window, (handle, xscale, yscale) -> { contentScaleX = xscale; contentScaleY = yscale; diff --git a/src/test/kotlin/net/prismclient/aether/ExampleScreen.kt b/src/test/kotlin/net/prismclient/aether/ExampleScreen.kt index cb68f69..f53177c 100644 --- a/src/test/kotlin/net/prismclient/aether/ExampleScreen.kt +++ b/src/test/kotlin/net/prismclient/aether/ExampleScreen.kt @@ -1,8 +1,7 @@ package net.prismclient.aether -import net.prismclient.aether.ui.component.type.color.UIColorSwatch -import net.prismclient.aether.ui.component.type.color.UIColorSwatchSheet -import net.prismclient.aether.ui.component.type.layout.container.UIContainer +import net.prismclient.aether.ui.component.type.input.slider.UISlider +import net.prismclient.aether.ui.component.type.input.slider.UISliderSheet import net.prismclient.aether.ui.component.type.layout.styles.UIContainerSheet import net.prismclient.aether.ui.component.util.enums.UIAlignment import net.prismclient.aether.ui.renderer.UIRenderer.Properties.ALIGNLEFT @@ -100,29 +99,33 @@ class ExampleScreen : UIScreen() { } } - style(UIColorSwatchSheet(), "swatch") { - swatchColor = asRGBA(0, 71, 255) + style(UISliderSheet(), "slider") { + position(px(50), px(50)) + size(px(400), px(5)) - x = px(50) - y = px(50) - width = px(250) - height = px(250) + sliderControl.color = -1 background { + radius = radius(2.5f) + color = asRGBA(255, 255, 255, 0.3f) border { - borderColor = asRGBA(255, 255, 255, 0.5f) borderWidth = 1f + borderColor = asRGBA(255, 255, 255, 0.75f) } } } - val frame = UIContainer("frame") - val swatch = UIColorSwatch("swatch") +// val frame = UIContainer("frame") +// val swatch = UIColorSwatch("swatch") +// +// frame.addComponent(swatch) +// +// frames.add(frame) +// components.add(frame) - frame.addComponent(swatch) + val slider = UISlider(5f, 0f, 10f, 1f, "slider") - frames.add(frame) - components.add(frame) + components.add(slider) update() }