diff --git a/src/main/kotlin/net/prismclient/aether/ui/Aether.kt b/src/main/kotlin/net/prismclient/aether/ui/Aether.kt
index bc7ee23..a9a0fba 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/Aether.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/Aether.kt
@@ -4,8 +4,8 @@ import net.prismclient.aether.ui.Aether.Properties
import net.prismclient.aether.ui.component.UIComponent
import net.prismclient.aether.ui.component.controller.UIController
import net.prismclient.aether.ui.component.type.layout.UIFrame
-import net.prismclient.aether.ui.component.type.layout.container.UIContainer
-import net.prismclient.aether.ui.component.type.layout.styles.UIContainerSheet
+import net.prismclient.aether.ui.component.type.layout.UIContainer
+import net.prismclient.aether.ui.component.type.layout.UIContainerSheet
import net.prismclient.aether.ui.event.input.UIMouseEvent
import net.prismclient.aether.ui.renderer.UIProvider
import net.prismclient.aether.ui.renderer.UIRenderer
@@ -33,9 +33,10 @@ import java.util.function.Consumer
* functions [Properties.updateSize] and [Properties.updateMouse] to update the values without
* invoking the [update], and [mouseMoved] functions.
*
+ * [Aether Documentation](https://aether.prismclient.net/getting-started)
+ *
* @author sen
* @since 1.0
- * @see UICore documentation
* @see UIProvider
*/
open class Aether(renderer: UIRenderer) {
@@ -108,12 +109,14 @@ open class Aether(renderer: UIRenderer) {
open fun render() {
renderer {
if (activeScreen != null) {
+ timings.onFrameRenderStart()
beginFrame(width, height, devicePxRatio)
for (i in 0 until components!!.size) {
val component = components!![i]
if (component.visible) component.render()
}
endFrame()
+ timings.onFrameRenderEnd()
}
}
}
@@ -123,7 +126,7 @@ open class Aether(renderer: UIRenderer) {
* eligibility to be focused or bubbled. The [Properties.mouseX] and [Properties.mouseY]
* properties can be found in [Aether.Properties].
*/
- fun mouseMoved(mouseX: Float, mouseY: Float) {
+ open fun mouseMoved(mouseX: Float, mouseY: Float) {
updateMouse(mouseX, mouseY)
mouseMoveListeners?.forEach { it.value.run() }
if (activeScreen != null) for (i in 0 until components!!.size) components!![i].mouseMoved(mouseX, mouseY)
@@ -139,10 +142,10 @@ open class Aether(renderer: UIRenderer) {
*
* @see mouseScrolled
*/
- fun mouseChanged(mouseButton: Int, isRelease: Boolean) {
+ open fun mouseChanged(mouseButton: Int, isRelease: Boolean) {
if (isRelease) {
mouseReleasedListeners?.forEach { it.value.run() }
- components?.forEach { it.mouseReleased(mouseX, mouseY) }
+ components?.forEach { it.mouseReleased(it.getMouseX(), it.getMouseY()) }
return
}
@@ -182,7 +185,7 @@ open class Aether(renderer: UIRenderer) {
return if (component != null) {
component.focus()
- component.mousePressed(UIMouseEvent(mouseX, mouseY, mouseButton, clickCount))
+ component.mousePressed(UIMouseEvent(component.getMouseX(), component.getMouseY(), mouseButton, clickCount))
true
} else false
}
@@ -211,7 +214,7 @@ open class Aether(renderer: UIRenderer) {
i++
}
c?.focus()
- c?.mousePressed(UIMouseEvent(mouseX, mouseY, mouseButton, clickCount))
+ c?.mousePressed(UIMouseEvent(c.getMouseX(), c.getMouseY(), mouseButton, clickCount))
}
/**
@@ -220,7 +223,7 @@ open class Aether(renderer: UIRenderer) {
* @param character The key which was pressed or '\u0000'
* @see updateModifierKey To update keys such as Shift, Alt, Tab etc...
*/
- fun keyPressed(character: Char) {
+ open fun keyPressed(character: Char) {
keyPressListeners?.forEach { it.value.accept(character) }
(focusedComponent as? UIComponent<*>)?.keyPressed(character)
}
@@ -230,6 +233,7 @@ open class Aether(renderer: UIRenderer) {
* of their eligibility to be focused or bubbled.
*/
open fun mouseScrolled(scrollAmount: Float) {
+ if (scrollAmount == 0f) return
tryFocus()
mouseScrollListeners?.forEach { it.value.accept(scrollAmount) }
components?.forEach { it.mouseScrolled(mouseX, mouseY, scrollAmount) }
@@ -241,20 +245,20 @@ open class Aether(renderer: UIRenderer) {
* as de-focusing the focused component and adding listeners to input.
*/
companion object Properties {
+
+ val timings: Timings = Timings()
+
@JvmStatic
var debug: Boolean = true
@JvmStatic
lateinit var instance: Aether
- protected set
@JvmStatic
lateinit var renderer: UIRenderer
- protected set
@JvmStatic
var activeScreen: UIScreen? = null
- protected set
/**
* The focused component (if applicable).
@@ -265,42 +269,36 @@ open class Aether(renderer: UIRenderer) {
*/
@JvmStatic
var focusedComponent: UIFocusable? = null
- protected set
/**
* The width of the screen. It can be set via [update]
*/
@JvmStatic
var width: Float = 0f
- protected set
/**
* The width of the screen. It can be set via [update]
*/
@JvmStatic
var height: Float = 0f
- protected set
/**
* The device pixel ratio. It can be set via [update]. It is the equivalent of content scale.
*/
@JvmStatic
var devicePxRatio: Float = 1f
- protected set
/**
* The x position of the mouse relative to the screen
*/
@JvmStatic
var mouseX: Float = 0f
- protected set
/**
* The y position of the mouse relative to the screen
*/
@JvmStatic
var mouseY: Float = 0f
- protected set
/**
* Invoked whenever the layout needs to be updated. This can be when the screen
@@ -308,49 +306,42 @@ open class Aether(renderer: UIRenderer) {
*/
@JvmStatic
var updateListeners: HashMap? = null
- protected set
/**
* The listeners for then the mouse is moved. Invoked prior to components.
*/
@JvmStatic
var mouseMoveListeners: HashMap? = null
- protected set
/**
* Invoked when the mouse is pressed. Invoked prior to components.
*/
@JvmStatic
var mousePressedListeners: HashMap? = null
- protected set
/**
* Invoked when the mouse is released. Invoked prior to components.
*/
@JvmStatic
var mouseReleasedListeners: HashMap? = null
- protected set
/**
* Invoked when a key is pressed. Invoked prior to components.
*/
@JvmStatic
var keyPressListeners: HashMap>? = null
- protected set
/**
* Invoked when the mouse is scrolled. Invoked prior to components.
*/
@JvmStatic
var mouseScrollListeners: HashMap>? = null
- protected set
/**
* Invoked when the screen is deleted. This is used to deallocate listeners added to UICore.
*/
@JvmStatic
var deallocationListeners: HashMap? = null
- protected set
/**
* The list of modifier keys. The value is if the key is pressed
@@ -502,13 +493,7 @@ open class Aether(renderer: UIRenderer) {
* Focuses the component. Please use [UIComponent.focus] instead.
*/
@JvmStatic
- fun focus(component: UIFocusable) {
- // Check if the given value is a valid instance of UIComponent
- try {
- component as UIComponent<*>
- } catch (castException: ClassCastException) {
- throw RuntimeException("When trying to focus, the provided value is not an instance of UIComponent. Make sure you are only using the UIFocus interface to focus UIComponents.")
- }
+ fun focus(component: T) where T : UIComponent<*>, T : UIFocusable {
focusedComponent = component
}
@@ -548,7 +533,7 @@ open class Aether(renderer: UIRenderer) {
for (i in 0 until instance.frames!!.size) {
// UIContainers are what control scrolling, so
// if it is not an instance of it, skip and continue
- val container = instance.frames!![i] as? UIContainer<*> ?: continue
+ val container = instance.frames!![i] as? UIContainer ?: continue
if (container.isMouseInsideBounds() && container.expandedHeight > 0f && container.style.overflowY != UIContainerSheet.Overflow.None) {
// Iterate through the frame to see if there are more
// containers with it. If there are, it will pass true
diff --git a/src/main/kotlin/net/prismclient/aether/ui/Timings.kt b/src/main/kotlin/net/prismclient/aether/ui/Timings.kt
new file mode 100644
index 0000000..9218a08
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/Timings.kt
@@ -0,0 +1,83 @@
+package net.prismclient.aether.ui
+
+class Timings {
+
+ /**
+ * Time when the current frame render started in milliseconds
+ */
+ var frameRenderStartTime = 0L
+ private set
+
+ /**
+ * Time when the last frame render started in milliseconds
+ */
+ var lastFrameRenderStartTime = 0L
+ private set
+
+ /**
+ * Time when the current frame render ended in milliseconds
+ */
+ var frameRenderEndTime = 0L
+ private set
+
+ /**
+ * Time when the last frame render ended in milliseconds
+ */
+ var lastFrameRenderEndTime = 0L
+ private set
+
+ /**
+ * The delta time (frame render start - frame render end) of the current frame in milliseconds
+ */
+ val frameRenderDeltaTime
+ get() = frameRenderEndTime - frameRenderStartTime
+
+ /**
+ * The delta time (frame render start - frame render end) of the current frame in seconds
+ */
+ val deltaFrameRenderTimeSeconds
+ get() = frameRenderDeltaTime / 1000.0
+
+ /**
+ * The delta time (frame render start - frame render end) of the last frame in milliseconds
+ */
+ val lastFrameRenderDeltaTime
+ get() = lastFrameRenderEndTime - lastFrameRenderStartTime
+
+ /**
+ * The delta time (frame render start - frame render end) of the last frame in seconds
+ */
+ val lastFrameRenderDeltaTimeSeconds
+ get() = lastFrameRenderDeltaTime / 1000.0
+
+ /**
+ * The approximate amount of renders the last frame would have made in a second
+ */
+ val lastFrameRate
+ get() = 1000.0 / lastFrameRenderDeltaTime
+
+ /**
+ * The approximate amount of renders the current frame would have made in a second
+ */
+ val frameRate
+ get() = (1000 / (frameRenderDeltaTime + 1)).toInt()
+
+ /**
+ * Set current & last render start times
+ */
+ fun onFrameRenderStart() {
+ lastFrameRenderStartTime = frameRenderStartTime
+
+ frameRenderStartTime = System.currentTimeMillis()
+ }
+
+ /**
+ * Set current & last render end times
+ */
+ fun onFrameRenderEnd() {
+ lastFrameRenderEndTime = frameRenderEndTime
+
+ frameRenderEndTime = System.currentTimeMillis()
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/callback/UICoreCallback.kt b/src/main/kotlin/net/prismclient/aether/ui/callback/UICoreCallback.kt
deleted file mode 100644
index 0751f94..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/callback/UICoreCallback.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package net.prismclient.aether.ui.callback
-
-/**
- * [UICoreCallback] is an interface for getting data that
- * is not available to aether by default.
- *
- * @author sen
- * @since 5/13/2022
- */
-interface UICoreCallback {
- /**
- * Returns the color of the pixel at the given position
- *
- * @return RGB(A) formatted int
- */
- fun getPixelColor(x: Float, y: Float): Int
-}
\ No newline at end of file
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 c721c96..c6b0a79 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/UIComponent.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/UIComponent.kt
@@ -2,10 +2,10 @@ package net.prismclient.aether.ui.component
import net.prismclient.aether.ui.Aether
import net.prismclient.aether.ui.animation.UIAnimation
+import net.prismclient.aether.ui.component.type.layout.UIContainer
+import net.prismclient.aether.ui.component.type.layout.UIContainerSheet
import net.prismclient.aether.ui.component.type.layout.UIFrame
-import net.prismclient.aether.ui.component.type.layout.container.UIContainer
-import net.prismclient.aether.ui.component.type.layout.styles.UIContainerSheet
-import net.prismclient.aether.ui.component.type.layout.styles.UIFrameSheet
+import net.prismclient.aether.ui.component.type.layout.UIFrameSheet
import net.prismclient.aether.ui.event.input.UIMouseEvent
import net.prismclient.aether.ui.renderer.UIProvider
import net.prismclient.aether.ui.renderer.impl.background.UIBackground
@@ -37,7 +37,7 @@ import java.util.function.Consumer
* @since 1.0
*/
@Suppress("UNCHECKED_CAST", "MemberVisibilityCanBePrivate", "LeakingThis")
-abstract class UIComponent(style: String?) {
+abstract class UIComponent {
/**
* The style of the component.
*/
@@ -193,10 +193,6 @@ abstract class UIComponent(style: String?) {
var mouseScrollListeners: HashMap, Float>>? = null
protected set
- init {
- applyStyle(style)
- }
-
/**
* Attempts to apply the style to the component. If the style is
* empty, or null, a NullPointerException will be thrown when the
@@ -208,8 +204,7 @@ abstract class UIComponent(style: String?) {
* @throws InvalidStyleSheetException If the style is not a valid style sheet of the given component
*/
open fun applyStyle(style: String?) {
- if (style.isNullOrEmpty())
- return
+ if (style.isNullOrEmpty()) return
// Attempt to apply the style provided to the component.
// Throw a InvalidStyleException if the style is not valid.
@@ -246,8 +241,10 @@ abstract class UIComponent(style: String?) {
* might request for this method to be invoked.
*/
open fun update() {
- if (!this::style.isInitialized)
- throw UninitializedStyleSheetException(this)
+ if (!this::style.isInitialized) {
+ println("creating style...")
+ style = createsStyle()
+ }
calculateBounds()
// Update the size, then the anchor, and then the position
@@ -336,8 +333,7 @@ abstract class UIComponent(style: String?) {
if (animations != null) {
animations!!.forEach { it.value.update() }
animations!!.entries.removeIf { it.value.isCompleted }
- if (animations!!.isEmpty())
- animations = null
+ if (animations!!.isEmpty()) animations = null
}
}
@@ -365,7 +361,17 @@ abstract class UIComponent(style: String?) {
*/
abstract fun renderComponent()
- /** Input **/
+ /**
+ * Used to create a new instance of the style sheet provided, [T].
+ */
+ abstract fun createsStyle(): T
+
+ /**
+ * Returns true if [style] is intialized.
+ */
+ open fun hasStyle(): Boolean = this::style.isInitialized
+
+ // -- Input -- //
/**
* Invoked when the mouse moves
@@ -431,7 +437,7 @@ abstract class UIComponent(style: String?) {
mouseScrollListeners?.forEach { it.value.accept(this, scrollAmount) }
}
- /** Event **/
+ // -- Event -- //
/**
* Invoked once on the initialization of the component.
@@ -601,36 +607,28 @@ abstract class UIComponent(style: String?) {
open fun getMouseY(): Float = Aether.mouseY - getParentYOffset()
/**
- * Returns the offset of the parent on the x-axis. It incorporates for [UIFrame] and [UIContainer] scroll offsets.
+ * Returns the actual x position of this component rendered on screen. FBOs change the point of origin
+ * back to 0, so the values that the component has might not reflect it's actual position on screen.
*/
- open fun getParentXOffset(): Float {
- if (parent == null) return 0f
-
- return if (parent is UIFrame) {
- val clipContent = ((parent as UIFrame).style as UIFrameSheet).clipContent
- return (if (clipContent) {
- parent!!.relX
- } else 0f) + parent!!.getParentXOffset() - if (parent is UIContainer) {
- (parent!!.style as UIContainerSheet).horizontalScrollbar.value * (parent as UIContainer).expandedWidth
- } else 0f
+ open fun getParentXOffset(): Float = if (parent is UIFrame) {
+ ((if ((parent!!.style as UIFrameSheet).useFBO) {
+ parent!!.relX
+ } else 0f) + parent!!.getParentXOffset()) - if (parent is UIContainer) {
+ (parent!!.style as UIContainerSheet).horizontalScrollbar.value * (parent as UIContainer).expandedWidth
} else 0f
- }
+ } else 0f
/**
- * Returns the offset of the parent on the y-axis. It incorporates for [UIFrame] and [UIContainer] scrolling offsets
+ * Returns the actual y position of this component rendered on screen. FBOs change the point of origin
+ * back to 0, so the values that the component has might not reflect it's actual position on screen.
*/
- open fun getParentYOffset(): Float {
- if (parent == null) return 0f
-
- return if (parent is UIFrame) {
- val clipContent = ((parent as UIFrame).style as UIFrameSheet).clipContent
- return (if (clipContent) {
- parent!!.relY
- } else 0f) + parent!!.getParentYOffset() - if (parent is UIContainer) {
- (parent!!.style as UIContainerSheet).verticalScrollbar.value * (parent as UIContainer).expandedHeight
- } else 0f
+ open fun getParentYOffset(): Float = if (parent is UIFrame) {
+ ((if ((parent!!.style as UIFrameSheet).useFBO) {
+ parent!!.relY
+ } else 0f) + parent!!.getParentYOffset()) - if (parent is UIContainer) {
+ (parent!!.style as UIContainerSheet).verticalScrollbar.value * (parent as UIContainer).expandedHeight
} else 0f
- }
+ } else 0f
/**
* Shorthand for calculating the x or width of this component
@@ -658,7 +656,7 @@ abstract class UIComponent(style: String?) {
open fun focus() {
if (this is UIFocusable) {
Aether.focus(this)
- focusListeners?.forEach { it.value.accept(this as UIComponent, true) }
+ focusListeners?.forEach { it.value.accept(this, true) }
requestUpdate()
}
}
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/controller/impl/selection/UISelectableController.kt b/src/main/kotlin/net/prismclient/aether/ui/component/controller/impl/selection/UISelectableController.kt
index 0d224f0..b0dd7de 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/controller/impl/selection/UISelectableController.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/controller/impl/selection/UISelectableController.kt
@@ -2,6 +2,7 @@ package net.prismclient.aether.ui.component.controller.impl.selection
import net.prismclient.aether.ui.component.UIComponent
import net.prismclient.aether.ui.component.controller.UIController
+import net.prismclient.aether.ui.style.UIStyleSheet
import java.util.function.Consumer
import kotlin.reflect.KClass
@@ -57,8 +58,20 @@ class UISelectableController>(filter: KClass) : UIControll
}
it.update()
}
+
+ selectedComponent = component
}
+ /**
+ * Returns true if the selected component is at index [index]
+ */
+ fun isSelected(index: Int) = selectedComponent == components[index]
+
+ /**
+ * Returns true if the selected component is [component]
+ */
+ fun > isSelected(component: O) = selectedComponent == component
+
/**
* The action that is preformed when a component is selected
*/
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/UILabel.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/UILabel.kt
index dfa4489..fac02bd 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/UILabel.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/UILabel.kt
@@ -7,10 +7,12 @@ import net.prismclient.aether.ui.style.UIStyleSheet
* [UILabel] is a component which draws a label, or string on screen.
*
* @author sen
- * @since 5/15/2022
+ * @since 1.0
*/
-class UILabel(var text: String, style: String?) : UIComponent(style) {
+class UILabel(var text: String) : UIComponent() {
override fun renderComponent() {
style.font?.render(text)
}
+
+ override fun createsStyle(): UIStyleSheet = UIStyleSheet()
}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/color/UIColorCursor.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/color/UIColorCursor.kt
deleted file mode 100644
index 699c807..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/color/UIColorCursor.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package net.prismclient.aether.ui.component.type.color
-
-class UIColorCursor
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/color/UIColorPicker.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/color/UIColorPicker.kt
deleted file mode 100644
index 5c056bb..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/color/UIColorPicker.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package net.prismclient.aether.ui.component.type.color
-
-import net.prismclient.aether.ui.component.UIComponent
-import net.prismclient.aether.ui.style.UIStyleSheet
-
-class UIColorPicker(style: String?) : UIComponent(style) {
- override fun renderComponent() {
-
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/color/UIColorSwatchSheet.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/color/UIColorSwatchSheet.kt
deleted file mode 100644
index 7b82376..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/color/UIColorSwatchSheet.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package net.prismclient.aether.ui.component.type.color
-
-import net.prismclient.aether.ui.style.UIStyleSheet
-import net.prismclient.aether.ui.util.extensions.setAlpha
-
-class UIColorSwatchSheet(name: String) : UIStyleSheet(name) {
- var swatchColor = -1
- set(value) {
- field = value.setAlpha(255)
- }
-
- override fun copy(): UIColorSwatchSheet = UIColorSwatchSheet(name).also {
- it.apply(this)
- it.swatchColor = swatchColor
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/image/UIImage.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/image/UIImage.kt
index 85131de..3421eda 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/image/UIImage.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/image/UIImage.kt
@@ -4,16 +4,21 @@ import net.prismclient.aether.ui.component.UIComponent
import net.prismclient.aether.ui.dsl.UIAssetDSL
import net.prismclient.aether.ui.renderer.UIProvider
import net.prismclient.aether.ui.renderer.image.UIImageData
+import net.prismclient.aether.ui.renderer.impl.property.UIRadius
+import net.prismclient.aether.ui.style.UIStyleSheet
+import net.prismclient.aether.ui.util.UIColor
import net.prismclient.aether.ui.util.extensions.renderer
+import net.prismclient.aether.ui.util.name
/**
- * [UIImage] is the default implementation for rendering images on screen. It accepts`
- * an image
+ * [UIImage] is the default component for rendering images to a screen. It accepts
+ * the [name] of the image that is to be rendered onto the screen. Alternatively, the
+ * image can also be loaded with the alternative constructor from a resource file.
*
* @author sen
- * @since 5/20/2022
+ * @since 1.0
*/
-class UIImage(name: String, style: String?) : UIComponent(style) {
+class UIImage(name: String) : UIComponent() {
var image: String = name
set(value) {
field = value
@@ -24,12 +29,9 @@ class UIImage(name: String, style: String?) : UIComponent(style) {
/**
* Loads am image or svg from the specified location with a given name
*/
- constructor(name: String, location: String, style: String?) : this(
- name,
- style
- ) {
- UIAssetDSL.image(name, location)
- }
+ constructor(name: String, location: String) : this(
+ name
+ ) { UIAssetDSL.image(name, location) }
init {
activeImage = UIProvider.getImage(image)
@@ -37,7 +39,7 @@ class UIImage(name: String, style: String?) : UIComponent(style) {
override fun renderComponent() {
renderer {
- color(style.imageColor)
+ color(style.imageColor?.rgba ?: -1)
renderImage(
image, x, y, width, height,
style.imageRadius?.topLeft ?: 0f,
@@ -47,4 +49,24 @@ class UIImage(name: String, style: String?) : UIComponent(style) {
)
}
}
+
+ override fun createsStyle(): UIImageSheet = UIImageSheet()
+}
+
+class UIImageSheet : UIStyleSheet() {
+ /**
+ * The color of the image. The default value is RGBA(255, 255, 255)
+ */
+ var imageColor: UIColor? = null
+
+ /**
+ * The radius of the image.
+ */
+ var imageRadius: UIRadius? = null
+
+ override fun copy() = UIImageSheet().name(name).also {
+ it.apply(this)
+ it.imageColor = imageColor
+ it.imageRadius = imageRadius?.copy()
+ }
}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/image/UIImageSheet.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/image/UIImageSheet.kt
deleted file mode 100644
index e63bada..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/image/UIImageSheet.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package net.prismclient.aether.ui.component.type.image
-
-import net.prismclient.aether.ui.renderer.impl.property.UIRadius
-import net.prismclient.aether.ui.style.UIStyleSheet
-
-/**
- * [UIImageSheet] is the sheet implementation for [UIImage].
- *
- * @author sen
- * @since 5/25/2022
- */
-class UIImageSheet(name: String) : UIStyleSheet(name) {
- /**
- * The color of the image. Use -1 (WHITE) for the normal color.
- */
- var imageColor = -1
-
- /**
- * The radius of the image.
- */
- var imageRadius: UIRadius? = null
-
- override fun copy() = UIImageSheet(name).also {
- it.apply(this)
- it.imageColor = imageColor
- it.imageRadius = imageRadius?.copy()
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UIButton.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UIButton.kt
index 0d522f9..0c0082a 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UIButton.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UIButton.kt
@@ -4,14 +4,15 @@ import net.prismclient.aether.ui.component.UIComponent
import net.prismclient.aether.ui.style.UIStyleSheet
/**
- * [UIButton] is a simple class, which is used to create a button.
+ * [UIButton] is the default implementation of [UIComponent]. It renders the given text to the font.
*
* @author sen
- * @since 5/16/2022
- * @param T The stylesheet (used for inheritance) leave as UIStyleSheet.
+ * @since 1.0
*/
-open class UIButton(open var text: String, style: String?) : UIComponent(style) {
+open class UIButton(open var text: String) : UIComponent() {
override fun renderComponent() {
style.font?.render(text)
}
+
+ override fun createsStyle(): UIStyleSheet = UIStyleSheet()
}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UICheckbox.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UICheckbox.kt
deleted file mode 100644
index 61193d6..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UICheckbox.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package net.prismclient.aether.ui.component.type.input.button
-
-import net.prismclient.aether.ui.component.type.image.UIImage
-import net.prismclient.aether.ui.dsl.UIComponentDSL
-import net.prismclient.aether.ui.style.UIStyleSheet
-
-open class UICheckbox(
- checked: Boolean = false,
- var selectedImageName: String = "checkbox",
- var deselectedImageName: String = "",
- var imageStyle: String,
- style: String?
-) : UISelectableButton(checked, "", style) {
- lateinit var selectedImage: UIImage
- lateinit var deselectedImage: UIImage
-
- init {
- onCheckChange { _, isSelected ->
- if (isSelected) {
- selectedImage.visible = true
- deselectedImage.visible = false
- } else {
- selectedImage.visible = false
- deselectedImage.visible = true
- }
- }
- }
-
- override fun initialize() {
- selectedImage = UIImage(selectedImageName, imageStyle)
- deselectedImage = UIImage(deselectedImageName, imageStyle)
- if (deselectedImageName.isEmpty()) // Make the deselected image invisible
- deselectedImage.style.imageColor = 0
- UIComponentDSL.pushComponent(selectedImage)
- UIComponentDSL.pushComponent(deselectedImage)
- }
-
- override fun renderComponent() {}
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UIIconButton.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UIIconButton.kt
deleted file mode 100644
index c09b6b0..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UIIconButton.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package net.prismclient.aether.ui.component.type.input.button
-
-class UIIconButton
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UIImageButton.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UIImageButton.kt
deleted file mode 100644
index a8f64f4..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UIImageButton.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package net.prismclient.aether.ui.component.type.input.button
-
-import net.prismclient.aether.ui.component.type.image.UIImage
-import net.prismclient.aether.ui.style.UIStyleSheet
-import net.prismclient.aether.ui.util.ucreate
-
-/**
- * Like [UIButton], but with an image, or icon.
- *
- * @author sen
- * @since 5/9/2022
- */
-class UIImageButton(private val imageName: String, private val imageStyle: String, text: String, style: String?) :
- UIButton(text, style) {
- lateinit var image: UIImage
-
- override fun initialize() {
- ucreate {
- image = image(imageName, style = imageStyle)
- }
- super.initialize()
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UISelectableButton.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UISelectableButton.kt
deleted file mode 100644
index 00d91cc..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UISelectableButton.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package net.prismclient.aether.ui.component.type.input.button
-
-import net.prismclient.aether.ui.style.UIStyleSheet
-import java.util.function.BiConsumer
-
-/**
- * [UISelectableButton] is like a [UIButton], except with the ability
- * to be selected. To do an action when the button selected, use [onCheckChange]
- * which passes this component, and a boolean indicating whether the component
- * is selected or not.
- *
- * @author sen
- * @since 5/24/2022
- */
-open class UISelectableButton(checked: Boolean = false, text: String, style: String?) :
- UIButton(text, style) {
- var checked = checked
- set(value) {
- field = value
- checkListeners?.forEach { it.accept(this, checked) }
- }
- var checkListeners: MutableList, Boolean>>? = null
-
- init {
- onMousePressed {
- if (isMouseInside()) {
- this.checked = !this.checked
- }
- }
- }
-
- open fun onCheckChange(block: BiConsumer, Boolean>): UISelectableButton {
- checkListeners = checkListeners ?: mutableListOf()
- checkListeners!!.add(block)
- return this
- }
-}
\ 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 b1737bf..40dfe84 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
@@ -2,6 +2,9 @@ package net.prismclient.aether.ui.component.type.input.slider
import net.prismclient.aether.ui.component.UIComponent
import net.prismclient.aether.ui.event.input.UIMouseEvent
+import net.prismclient.aether.ui.style.UIStyleSheet
+import net.prismclient.aether.ui.util.Block
+import net.prismclient.aether.ui.util.name
import java.util.function.BiConsumer
import kotlin.math.roundToInt
@@ -17,16 +20,15 @@ import kotlin.math.roundToInt
* @see UISliderShape
*/
open class UISlider(
- value: Double, var range: ClosedFloatingPointRange, var step: Double, style: String?
-) : UIComponent(style) {
+ value: Double, var range: ClosedFloatingPointRange, var step: Double
+) : UIComponent() {
/**
* The value of this slider.
*/
var value: Double = 0.0
set(value) {
val different = value != field
- field = ((value / step.coerceAtLeast(Double.MIN_VALUE)).roundToInt() * step)
- .coerceAtLeast(range.start)
+ field = ((value / step.coerceAtLeast(Double.MIN_VALUE)).roundToInt() * step).coerceAtLeast(range.start)
.coerceAtMost(range.endInclusive)
// normalizedValue = value / (range.endInclusive - range.start)
if (different) valueChangeListeners?.forEach { it.value.accept(this, value) }
@@ -110,4 +112,20 @@ open class UISlider(
valueChangeListeners = valueChangeListeners ?: hashMapOf()
valueChangeListeners!![eventName] = event
}
+
+ override fun createsStyle(): UISliderSheet = UISliderSheet()
+}
+
+class UISliderSheet : UIStyleSheet() {
+ /**
+ * The slider shape. The [UISliderShape.x] dictates the offset of the slider.
+ */
+ var control: UISliderShape = UISliderShape()
+
+ inline fun control(block: Block) = control.block()
+
+ override fun copy(): UISliderSheet = UISliderSheet().name(name).also {
+ it.apply(this)
+ it.control = control.copy()
+ }
}
\ 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
deleted file mode 100644
index a3efd8e..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/slider/UISliderSheet.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package net.prismclient.aether.ui.component.type.input.slider
-
-import net.prismclient.aether.ui.style.UIStyleSheet
-import net.prismclient.aether.ui.util.Block
-
-/**
- * [UISliderSheet] is the corresponding sheet to [UISlider]. It contains the slider shape
- * which is the shape which is moved to control the value of the slider
- *
- * @author sen
- * @since 1.0
- * @see UISlider
- * @see UISliderShape
- * @see UISliderSheet.control The slider shape.
- */
-class UISliderSheet @JvmOverloads constructor(name: String = "") : UIStyleSheet(name) {
- /**
- * The slider shape. The [UISliderShape.x] dictates the offset of the slider.
- */
- var control: UISliderShape = UISliderShape()
-
- inline fun control(block: Block) = control.block()
-
- override fun copy(): UISliderSheet = UISliderSheet(name).also {
- it.apply(this)
- it.control = control.copy()
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/textfield/UITextField.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/textfield/UITextField.kt
index 1f85f76..020a740 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/textfield/UITextField.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/textfield/UITextField.kt
@@ -1,12 +1,19 @@
package net.prismclient.aether.ui.component.type.input.textfield
import net.prismclient.aether.ui.Aether
+import net.prismclient.aether.ui.component.UIComponent
import net.prismclient.aether.ui.component.type.input.button.UIButton
+import net.prismclient.aether.ui.component.type.input.textfield.caret.UICaret
import net.prismclient.aether.ui.event.input.UIMouseEvent
import net.prismclient.aether.ui.renderer.impl.font.UIFont
+import net.prismclient.aether.ui.style.UIStyleSheet
+import net.prismclient.aether.ui.util.UIColor
+import net.prismclient.aether.ui.util.extensions.em
+import net.prismclient.aether.ui.util.extensions.px
import net.prismclient.aether.ui.util.extensions.renderer
import net.prismclient.aether.ui.util.input.UIModifierKey
import net.prismclient.aether.ui.util.interfaces.UIFocusable
+import net.prismclient.aether.ui.util.name
import net.prismclient.aether.ui.util.warn
import java.lang.Integer.max
import java.lang.Integer.min
@@ -20,12 +27,11 @@ import java.util.function.Consumer
* length of the overall text.
*
* @author sen
- * @since 6/6/2022
- * @see UITextField.filter Pre-made text filters.
+ * @since 1.0
+ * @see UITextField.Filters pre-made text filters.
*/
-open class UITextField(text: String, var placeholder: String? = null, var filter: TextFilter, style: String?) :
- UIButton(text, style), UIFocusable {
- override var text: String = text
+open class UITextField(text: String, var placeholder: String? = null, var filter: TextFilter) : UIComponent(), UIFocusable {
+ var text: String = text
set(value) {
field = value
textChangedListener?.forEach { it.value.accept(this) }
@@ -46,7 +52,7 @@ open class UITextField(text: String, var placeholder: String? = null, var filter
/** Blink **/
protected var timeSinceLastBlink: Long = 0L
- protected var blink: Boolean = false
+ protected var blink: Boolean = true
init {
Aether.onModifierKeyChange(this.toString()) { key, value ->
@@ -111,7 +117,7 @@ open class UITextField(text: String, var placeholder: String? = null, var filter
//style.caret.offsetY = font.cachedY + boundsOf(style.font!!.cachedText)[1] - y
if (blink && isFocused()) style.caret.render()
- if (timeSinceLastBlink + style.blinkRate <= System.currentTimeMillis()) {
+ if (style.blinkRate > 0 && (timeSinceLastBlink + style.blinkRate <= System.currentTimeMillis())) {
blink = !blink
timeSinceLastBlink = System.currentTimeMillis()
}
@@ -231,6 +237,8 @@ open class UITextField(text: String, var placeholder: String? = null, var filter
textChangedListener!![eventName] = event
}
+ override fun createsStyle(): UITextFieldSheet = UITextFieldSheet()
+
/**
* [TextFilter] holds a string which is compared to the input character. If the character
* is found within the string, it will be added to the text field, else it will not. Furthermore,
@@ -259,4 +267,38 @@ open class UITextField(text: String, var placeholder: String? = null, var filter
@JvmStatic
val hex = TextFilter("#ABCDEFabcdef0123456789")
}
+}
+
+class UITextFieldSheet : UIStyleSheet() {
+ /**
+ * The caret shape which is drawn to display the caret.
+ */
+ var caret: UICaret = UICaret().apply {
+ this.width = px(2)
+ this.height = em(1)
+ }
+
+ /**
+ * The rate at which the caret blinks at. 0 = never
+ */
+ var blinkRate: Long = 500L
+
+ /**
+ * The color of the text when the text field is not focused
+ */
+ var placeholderColor: UIColor? = null
+
+ /**
+ * Creates a caret DSL block.
+ */
+ inline fun caret(block: UICaret.() -> Unit) {
+ block.invoke(caret)
+ }
+
+ override fun copy(): UITextFieldSheet = UITextFieldSheet().name(name).also {
+ it.apply(this)
+ it.caret = caret.copy()
+ it.blinkRate = blinkRate
+ it.placeholderColor = placeholderColor
+ }
}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/textfield/UITextFieldSheet.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/textfield/UITextFieldSheet.kt
deleted file mode 100644
index 0ae3407..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/input/textfield/UITextFieldSheet.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package net.prismclient.aether.ui.component.type.input.textfield
-
-import net.prismclient.aether.ui.component.type.input.textfield.caret.UICaret
-import net.prismclient.aether.ui.style.UIStyleSheet
-import net.prismclient.aether.ui.util.UIColor
-import net.prismclient.aether.ui.util.extensions.em
-import net.prismclient.aether.ui.util.extensions.px
-
-/**
- * The corresponding style sheet for text fields. It contains basic styling information
- * such as the placeholder color. It also contains caret controls.
- *
- * @author sen
- * @since 5/11/2022
- */
-class UITextFieldSheet(name: String) : UIStyleSheet(name) {
- /**
- * The caret shape which is drawn to display the caret.
- */
- var caret: UICaret = UICaret().apply {
- this.width = px(2)
- this.height = em(1)
- }
-
- /**
- * The rate at which the caret blinks at. 0 = never
- */
- var blinkRate: Long = 500L
-
- /**
- * The color of the text when the text field is not focused
- */
- var placeholderColor: UIColor? = null
-
- /**
- * Creates a caret DSL block.
- */
- inline fun caret(block: UICaret.() -> Unit) {
- block.invoke(caret)
- }
-
- override fun copy(): UITextFieldSheet = UITextFieldSheet(name).also {
- it.apply(this)
- it.caret = caret.copy()
- it.blinkRate = blinkRate
- it.placeholderColor = placeholderColor
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/auto/UIAutoLayout.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIAutoLayout.kt
similarity index 84%
rename from src/main/kotlin/net/prismclient/aether/ui/component/type/layout/auto/UIAutoLayout.kt
rename to src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIAutoLayout.kt
index f29b036..a731d21 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/auto/UIAutoLayout.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIAutoLayout.kt
@@ -1,13 +1,12 @@
-package net.prismclient.aether.ui.component.type.layout.auto
+package net.prismclient.aether.ui.component.type.layout
import net.prismclient.aether.ui.component.UIComponent
-import net.prismclient.aether.ui.component.type.layout.list.UIListLayout
-import net.prismclient.aether.ui.component.type.layout.styles.UIFrameSheet
import net.prismclient.aether.ui.component.util.enums.UIAlignment
import net.prismclient.aether.ui.component.util.enums.UIAlignment.*
import net.prismclient.aether.ui.renderer.UIProvider
import net.prismclient.aether.ui.renderer.impl.property.UIPadding
import net.prismclient.aether.ui.util.interfaces.UICopy
+import net.prismclient.aether.ui.util.name
/**
* [UIAutoLayout] is a layout which is designed to mimic the behavior of Figma's auto
@@ -27,10 +26,8 @@ import net.prismclient.aether.ui.util.interfaces.UICopy
* @author sen
* @since 1.1
*/
-class UIAutoLayout @JvmOverloads constructor(
- listDirection: ListDirection = ListDirection.Horizontal,
- style: String?
-) : UIListLayout(listDirection, ListOrder.Forward, style), UICopy {
+class UIAutoLayout @JvmOverloads constructor(listDirection: ListDirection = ListDirection.Horizontal) :
+ UIListLayout(listDirection, ListOrder.Forward), UICopy {
/**
* Defines how the width should be sized. [ResizingMode.Hug] resizes based on the components and
* the padding and spacing properties, and [ResizingMode.Fixed] acts like a normal component.
@@ -67,6 +64,14 @@ class UIAutoLayout @JvmOverloads constructor(
*/
var componentAlignment: UIAlignment = TOPLEFT
+ /**
+ * Sets the [verticalResizing] and [horizontalResizing] to [vertical] and [horizontal] respectively.
+ */
+ fun resize(vertical: ResizingMode, horizontal: ResizingMode) {
+ verticalResizing = vertical
+ horizontalResizing = horizontal
+ }
+
override fun updateLayout() {
if (components.isEmpty()) return
@@ -82,28 +87,27 @@ class UIAutoLayout @JvmOverloads constructor(
var h = 0f
for (i in components.indices) {
+ val component = components[i]
if (horizontalResizing == ResizingMode.Hug) {
w = if (listDirection == ListDirection.Horizontal) {
- w + components[i].relWidth + if (i < components.size - 1) spacing else 0f
+ w + component.relWidth + component.marginLeft + component.marginRight + if (i < components.size - 1) spacing else 0f
} else {
- components[i].relWidth.coerceAtLeast(w)
+ (component.relWidth + component.marginLeft + component.marginRight).coerceAtLeast(w)
}
}
if (verticalResizing == ResizingMode.Hug) {
h = if (listDirection == ListDirection.Vertical) {
- h + components[i].relHeight + if (i < components.size - 1) spacing else 0f
+ h + component.relHeight + component.marginTop + component.marginBottom + if (i < components.size - 1) spacing else 0f
} else {
- components[i].relHeight.coerceAtLeast(h)
+ (component.relHeight + component.marginTop + component.marginBottom).coerceAtLeast(h)
}
}
}
// Adjust the width and/or height of the component based on the calculated
// size, and ensure that the size is at least the size prior to this.
- if (horizontalResizing == ResizingMode.Hug)
- width = (w + left + right).coerceAtLeast(width)
- if (verticalResizing == ResizingMode.Hug)
- height = (h + top + bottom).coerceAtLeast(height)
+ if (horizontalResizing == ResizingMode.Hug) width = (w + left + right).coerceAtLeast(width)
+ if (verticalResizing == ResizingMode.Hug) height = (h + top + bottom).coerceAtLeast(height)
// Update
calculateBounds()
@@ -140,7 +144,7 @@ class UIAutoLayout @JvmOverloads constructor(
MIDDLELEFT, CENTER, MIDDLERIGHT -> (height - c.height - top - bottom) / 2f
BOTTOMLEFT, BOTTOMCENTER, BOTTOMRIGHT -> (height - c.height - left - right)
else -> 0f
- }
+ } + c.marginTop - c.marginBottom
x += c.width + spacing
} else if (listDirection == ListDirection.Vertical) {
c.x = x + when (componentAlignment) {
@@ -148,7 +152,7 @@ class UIAutoLayout @JvmOverloads constructor(
TOPCENTER, CENTER, BOTTOMCENTER -> (width - c.width - left - right) / 2f
TOPRIGHT, MIDDLERIGHT, BOTTOMRIGHT -> (width - c.width - left - right)
else -> 0f
- }
+ } + c.marginLeft - c.marginRight
c.y = y
y += c.height + spacing
}
@@ -164,7 +168,7 @@ class UIAutoLayout @JvmOverloads constructor(
/**
* Copy the properties of this layout to a new one (excluding components).
*/
- override fun copy(): UIAutoLayout = UIAutoLayout(listDirection, style.name).also {
+ override fun copy(): UIAutoLayout = UIAutoLayout(listDirection).also {
// UIAutoLayout properties
it.horizontalResizing = horizontalResizing
it.verticalResizing = verticalResizing
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIContainer.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIContainer.kt
new file mode 100644
index 0000000..5459dd0
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIContainer.kt
@@ -0,0 +1,275 @@
+package net.prismclient.aether.ui.component.type.layout
+
+import net.prismclient.aether.ui.component.UIComponent
+import net.prismclient.aether.ui.component.util.interfaces.UILayout
+import net.prismclient.aether.ui.event.input.UIMouseEvent
+import net.prismclient.aether.ui.renderer.impl.scrollbar.UIScrollbar
+import net.prismclient.aether.ui.style.UIStyleSheet
+import net.prismclient.aether.ui.util.extensions.renderer
+import net.prismclient.aether.ui.util.interfaces.UIFocusable
+
+/**
+ * [UIContainer] is the default implementation for [UIFrame]. It introduces scrollbars which automatically
+ * resize to content being added/removed. It is considered to be a [UIFocusable], so when the mouse is scrolled
+ * within the container the focused component will become this.
+ *
+ * @author sen
+ * @since 1.0
+ */
+open class UIContainer : UIFrame(), UIFocusable, UILayout {
+ /**
+ * How sensitive the scrolling will be
+ */
+ var scrollSensitivity: Float = 10f
+
+ /**
+ * The expanded width determined by components that leave the component bounds
+ */
+ var expandedWidth = 0f
+ protected set
+
+ /**
+ * The expanded height determined by components that leave the components bounds
+ */
+ var expandedHeight = 0f
+ protected set
+
+ override fun update() {
+ super.update()
+ updateLayout()
+
+ // Calculate the distance of the components
+ // and find the largest of them on both axes
+ var w = 0f
+ var h = 0f
+
+ for (i in 0 until components.size) {
+ val c = components[i]
+ w = (c.relX + c.relWidth + c.marginRight).coerceAtLeast(w)
+ h = (c.relY + c.relHeight + c.marginBottom).coerceAtLeast(h)
+ }
+
+ val x = if (style.useFBO) 0f else relX
+ val y = if (style.useFBO) 0f else relY
+
+ expandedWidth = (w - relWidth - x).coerceAtLeast(0f)
+ expandedHeight = (h - relHeight - y).coerceAtLeast(0f)
+
+ updateScrollbar()
+ }
+
+ override fun updateLayout() {
+ components.forEach { it.update() }
+ }
+
+ open fun updateScrollbar() {
+ style.verticalScrollbar.update(this)
+ style.horizontalScrollbar.update(this)
+ }
+
+ open fun renderScrollbar() {
+ style.verticalScrollbar.render()
+ style.horizontalScrollbar.render()
+ }
+
+ override fun renderContent() {
+ if (style.useFBO && (requiresUpdate || !style.optimizeRenderer)) {
+ renderer {
+ if (fbo == null) updateFBO()
+ fbo!!.renderToFramebuffer {
+ translate(
+ -(style.horizontalScrollbar.value * expandedWidth),
+ -(style.verticalScrollbar.value * expandedHeight)
+ ) {
+ components.forEach(UIComponent<*>::render)
+ }
+ }
+ }
+ requiresUpdate = false
+ }
+ }
+
+ override fun renderComponent() {
+ if (style.useFBO) {
+ renderer {
+ path {
+ imagePattern(fbo!!.imagePattern, relX, relY, relWidth, relHeight, 0f, 1f)
+ rect(relX, relY, relWidth, relHeight)
+ }.fillPaint()
+ }
+ } else {
+ renderer {
+ if (style.clipContent) {
+ scissor(relX, relY, relWidth, relHeight) {
+ translate(
+ -(style.horizontalScrollbar.value * expandedWidth),
+ -(style.verticalScrollbar.value * expandedHeight)
+ ) {
+ components.forEach(UIComponent<*>::render)
+ }
+ }
+ } else {
+ translate(
+ -(style.horizontalScrollbar.value * expandedWidth),
+ -(style.verticalScrollbar.value * expandedHeight)
+ ) {
+ components.forEach(UIComponent<*>::render)
+ }
+ }
+ }
+ }
+ }
+
+ override fun render() {
+ super.render()
+ renderScrollbar()
+ }
+
+ override fun updateAnimation() {
+ if (animations != null) {
+ animations!!.forEach { it.value.update() }
+ animations!!.entries.removeIf { it.value.isCompleted }
+ if (animations!!.isEmpty())
+ animations = null
+ updateLayout()
+ components.forEach { it.requestUpdate() }
+ }
+ }
+
+ override fun mousePressed(event: UIMouseEvent) {
+ super.mousePressed(event)
+ if (style.verticalScrollbar.mousePressed() || style.horizontalScrollbar.mousePressed()) focus()
+ }
+
+ override fun mouseReleased(mouseX: Float, mouseY: Float) {
+ super.mouseReleased(
+ mouseX - (style.horizontalScrollbar.value * expandedWidth),
+ mouseY - (style.verticalScrollbar.value * expandedHeight)
+ )
+ style.verticalScrollbar.release()
+ style.horizontalScrollbar.release()
+ }
+
+ override fun mouseMoved(mouseX: Float, mouseY: Float) {
+ super.mouseMoved(
+ mouseX - (style.horizontalScrollbar.value * expandedWidth),
+ mouseY - (style.verticalScrollbar.value * expandedHeight)
+ )
+ style.verticalScrollbar.mouseMoved()
+ style.horizontalScrollbar.mouseMoved()
+ if (style.verticalScrollbar.selected || style.horizontalScrollbar.selected)
+ requestUpdate()
+ }
+
+ override fun mouseScrolled(mouseX: Float, mouseY: Float, scrollAmount: Float) {
+ if (isFocused()) {
+ style.verticalScrollbar.value -= ((scrollAmount * scrollSensitivity) / style.verticalScrollbar.cachedHeight)
+ mouseMoved(mouseX, mouseY)
+ }
+ super.mouseScrolled(mouseX, mouseY, scrollAmount)
+ }
+
+ override fun createsStyle(): T = UIContainerSheet() as T
+}
+
+open class UIContainerSheet : UIFrameSheet() {
+ /**
+ * Describes when to introduce the scrollbar
+ *
+ * @see Overflow
+ */
+ var overflowX: Overflow = Overflow.Auto
+
+ /**
+ * Describes when to introduce the scrollbar
+ *
+ * @see Overflow
+ */
+ var overflowY: Overflow = Overflow.Auto
+
+ var scrollBehaviour: ScrollBehaviour = ScrollBehaviour.Fixed
+
+ var verticalScrollbar: UIScrollbar = UIScrollbar(UIScrollbar.Scrollbar.Vertical)
+ var horizontalScrollbar: UIScrollbar = UIScrollbar(UIScrollbar.Scrollbar.Horizontal)
+
+ /**
+ * Creates a DSL block for the vertical scrollbar
+ */
+ inline fun verticalScrollbar(block: UIScrollbar.() -> Unit) = verticalScrollbar.block()
+
+ /**
+ * Creates a DSL block for the horizontal scrollbar
+ */
+ inline fun horizontalScrollbar(block: UIScrollbar.() -> Unit) = horizontalScrollbar.block()
+
+ /**
+ * [Overflow] defines what the vertical, and horizontal scrollbars
+ * are supposed to do when content leaves the screen. Check the enum
+ * documentation for more information.
+ */
+ enum class Overflow {
+ /**
+ * Does not introduce a scrollbar on the given axis
+ */
+ None,
+
+ /**
+ * Creates a scrollbar on the given axis regardless if content leaves the window
+ */
+ Always,
+
+ /**
+ * Like scroll, but only adds the scrollbar on the given axis if content leaves the window
+ */
+ Auto
+ }
+
+ /**
+ * [ScrollBehaviour] defines how the encompassing container behaves when the mouse is scrolled.
+ */
+ enum class ScrollBehaviour {
+ /**
+ * Clamps the content
+ */
+ Fixed,
+
+ /**
+ * Introduces elasticity to the encompassing container.
+ */
+ Elastic
+ }
+
+ override fun apply(sheet: UIStyleSheet): UIContainerSheet {
+ // Override the default apply function because
+ // this is an inheritable class.
+ this.immutableStyle = sheet.immutableStyle
+ this.name = sheet.name
+
+ this.background = sheet.background?.copy()
+ this.font = sheet.font?.copy()
+
+ this.x = sheet.x?.copy()
+ this.y = sheet.y?.copy()
+ this.width = sheet.width?.copy()
+ this.height = sheet.height?.copy()
+
+ this.padding = sheet.padding?.copy()
+ this.margin = sheet.margin?.copy()
+ this.anchor = sheet.anchor?.copy()
+ this.clipContent = sheet.clipContent
+
+ // Frame properties
+ if (sheet is UIContainerSheet) {
+ this.useFBO = sheet.useFBO
+ this.optimizeRenderer = sheet.optimizeRenderer
+ this.overflowX = sheet.overflowX
+ this.overflowY = sheet.overflowY
+ this.verticalScrollbar = sheet.verticalScrollbar.copy()
+ this.horizontalScrollbar = sheet.horizontalScrollbar.copy()
+ }
+
+ return this
+ }
+
+ override fun copy(): UIContainerSheet = UIContainerSheet().apply(this)
+}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIFrame.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIFrame.kt
index 73d348d..6e30a6e 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIFrame.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIFrame.kt
@@ -2,12 +2,13 @@ package net.prismclient.aether.ui.component.type.layout
import net.prismclient.aether.ui.Aether
import net.prismclient.aether.ui.component.UIComponent
-import net.prismclient.aether.ui.component.type.layout.styles.UIFrameSheet
import net.prismclient.aether.ui.dsl.UIRendererDSL
import net.prismclient.aether.ui.event.input.UIMouseEvent
import net.prismclient.aether.ui.renderer.other.UIContentFBO
+import net.prismclient.aether.ui.style.UIStyleSheet
import net.prismclient.aether.ui.util.extensions.renderer
import net.prismclient.aether.ui.util.interfaces.UIFocusable
+import net.prismclient.aether.ui.util.name
import net.prismclient.aether.ui.util.warn
/**
@@ -27,7 +28,7 @@ import net.prismclient.aether.ui.util.warn
* @author sen
* @since 1.0
*/
-abstract class UIFrame(style: String?) : UIComponent(style), UIFocusable {
+abstract class UIFrame : UIComponent(), UIFocusable {
/**
* The components of this frame.
*/
@@ -59,7 +60,16 @@ abstract class UIFrame(style: String?) : UIComponent(style)
* frame has been updated, but prior to the first render.
*/
open fun updateFBO() {
- if (style.useFBO) fbo = Aether.renderer.createFBO(relWidth, relHeight)
+ // Deallocate any existing framebuffer
+ if (fbo != null) {
+ Aether.renderer.deleteFBO(fbo!!)
+ fbo = null
+ }
+ if (style.useFBO) {
+ if ((fbo != null && (fbo!!.width != relWidth || fbo!!.height != relHeight)) || fbo == null) {
+ fbo = Aether.renderer.createFBO(relWidth, relHeight)
+ }
+ }
}
/**
@@ -93,11 +103,10 @@ abstract class UIFrame(style: String?) : UIComponent(style)
components.forEach(UIComponent<*>::render)
}
}
- requiresUpdate = false
}
+ requiresUpdate = false
}
-
override fun render() {
updateAnimation()
style.background?.render()
@@ -106,17 +115,12 @@ abstract class UIFrame(style: String?) : UIComponent(style)
override fun renderComponent() {
if (style.useFBO) {
- Aether.renderer.renderFbo(
- fbo!!,
- relX,
- relY,
- relWidth,
- relHeight,
- style.background?.radius?.topLeft ?: 0f,
- style.background?.radius?.topRight ?: 0f,
- style.background?.radius?.bottomLeft ?: 0f,
- style.background?.radius?.bottomRight ?: 0f
- )
+ renderer {
+ path {
+ imagePattern(fbo!!.imagePattern, relX, relY, relWidth, relHeight, 0f, 1f)
+ rect(relX, relY, relWidth, relHeight)
+ }.fillPaint()
+ }
} else {
if (style.clipContent) UIRendererDSL.scissor(relX, relY, relWidth, relHeight) {
components.forEach(UIComponent<*>::render)
@@ -162,4 +166,53 @@ abstract class UIFrame(style: String?) : UIComponent(style)
components.forEach { it.mouseScrolled(mouseX, mouseY, scrollAmount) }
requestUpdate()
}
+
+ override fun createsStyle(): T = UIFrameSheet() as T
+}
+
+open class UIFrameSheet : UIStyleSheet() {
+ /**
+ * If true, the frame will use an FBO to render content.
+ */
+ var useFBO: Boolean = false
+
+ /**
+ * If true certain optimizations will be applied when
+ * rendering. This only works with [useFBO] as true.
+ */
+ var optimizeRenderer: Boolean = false
+ set(value) {
+ if (!useFBO && value) warn("attempted to set optimizeRenderer when useFBO is false. ($name)")
+ field = value
+ }
+
+ override fun apply(sheet: UIStyleSheet): UIFrameSheet {
+ // Override the default apply function because
+ // this is an inheritable class.
+ this.immutableStyle = sheet.immutableStyle
+ this.name = sheet.name
+
+ this.background = sheet.background?.copy()
+ this.font = sheet.font?.copy()
+
+ this.x = sheet.x?.copy()
+ this.y = sheet.y?.copy()
+ this.width = sheet.width?.copy()
+ this.height = sheet.height?.copy()
+
+ this.padding = sheet.padding?.copy()
+ this.margin = sheet.margin?.copy()
+ this.anchor = sheet.anchor?.copy()
+ this.clipContent = sheet.clipContent
+
+ // Frame properties
+ if (sheet is UIFrameSheet) {
+ this.useFBO = sheet.useFBO
+ this.optimizeRenderer = sheet.optimizeRenderer
+ }
+
+ return this
+ }
+
+ override fun copy(): UIFrameSheet = UIFrameSheet().name(name).apply(this)
}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIGridLayout.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIGridLayout.kt
new file mode 100644
index 0000000..2c39452
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIGridLayout.kt
@@ -0,0 +1,9 @@
+package net.prismclient.aether.ui.component.type.layout
+
+/**
+ *
+ *
+ * @author sen
+ * @since 1.3
+ */
+class UIGridLayout
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/list/UIListLayout.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIListLayout.kt
similarity index 88%
rename from src/main/kotlin/net/prismclient/aether/ui/component/type/layout/list/UIListLayout.kt
rename to src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIListLayout.kt
index 692b393..1b6a63f 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/list/UIListLayout.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIListLayout.kt
@@ -1,9 +1,7 @@
-package net.prismclient.aether.ui.component.type.layout.list
+package net.prismclient.aether.ui.component.type.layout
-import net.prismclient.aether.ui.component.type.layout.container.UIContainer
-import net.prismclient.aether.ui.component.type.layout.list.UIListLayout.ListOrder.Backwards
-import net.prismclient.aether.ui.component.type.layout.list.UIListLayout.ListOrder.Forward
-import net.prismclient.aether.ui.component.type.layout.styles.UIContainerSheet
+import net.prismclient.aether.ui.component.type.layout.UIListLayout.ListOrder.Backwards
+import net.prismclient.aether.ui.component.type.layout.UIListLayout.ListOrder.Forward
import net.prismclient.aether.ui.unit.UIUnit
/**
@@ -16,8 +14,7 @@ import net.prismclient.aether.ui.unit.UIUnit
open class UIListLayout @JvmOverloads constructor(
var listDirection: ListDirection = ListDirection.Vertical,
var listOrder: ListOrder = Forward,
- style: String?
-) : UIContainer(style) {
+) : UIContainer() {
/**
* The spacing between each component in the layout.
*/
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/tab/UITab.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UITab.kt
similarity index 53%
rename from src/main/kotlin/net/prismclient/aether/ui/component/type/layout/tab/UITab.kt
rename to src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UITab.kt
index 2b1a715..c47a21e 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/tab/UITab.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UITab.kt
@@ -1,3 +1,3 @@
-package net.prismclient.aether.ui.component.type.layout.tab
+package net.prismclient.aether.ui.component.type.layout
class UITab // https://chakra-ui.com/docs/components/disclosure/tabs
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/auto/UIAutoLayoutSheet.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/auto/UIAutoLayoutSheet.kt
deleted file mode 100644
index 7197cc7..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/auto/UIAutoLayoutSheet.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package net.prismclient.aether.ui.component.type.layout.auto
-
-import net.prismclient.aether.ui.component.type.layout.styles.UIContainerSheet
-
-/**
- * [UIAutoLayoutSheet] is the corresponding style sheet to [UIAutoLayout]. See
- * the field documentation for more information.
- *
- * @author sen
- * @since 1.0
- */
-class UIAutoLayoutSheet(name: String) : UIContainerSheet(name) {
- override fun copy(): UIAutoLayoutSheet = UIAutoLayoutSheet(name).also {
- it.apply(this)
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/container/UIContainer.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/container/UIContainer.kt
deleted file mode 100644
index 315d789..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/container/UIContainer.kt
+++ /dev/null
@@ -1,173 +0,0 @@
-package net.prismclient.aether.ui.component.type.layout.container
-
-import net.prismclient.aether.ui.Aether
-import net.prismclient.aether.ui.component.UIComponent
-import net.prismclient.aether.ui.component.type.layout.UIFrame
-import net.prismclient.aether.ui.component.type.layout.styles.UIContainerSheet
-import net.prismclient.aether.ui.component.util.interfaces.UILayout
-import net.prismclient.aether.ui.event.input.UIMouseEvent
-import net.prismclient.aether.ui.util.extensions.renderer
-import net.prismclient.aether.ui.util.interfaces.UIFocusable
-
-/**
- * [UIContainer] is the default implementation for [UIFrame]. It introduces
- * scrollbars which automatically resize to content being added/removed. It is
- * considered to be a [UIFocusable], so when the mouse is scrolled within the
- * container the focused component will become this.
- *
- *
- *
- * @author sen
- * @since 5/12/2022
- */
-open class UIContainer(style: String?) : UIFrame(style), UIFocusable, UILayout {
- /**
- * How sensitive the scrolling will be
- */
- var scrollSensitivity: Float = 10f
-
- /**
- * The expanded width determined by components that leave the component bounds
- */
- var expandedWidth = 0f
- protected set
-
- /**
- * The expanded height determined by components that leave the components bounds
- */
- var expandedHeight = 0f
- protected set
-
- override fun update() {
- super.update()
- updateLayout()
-
- // Calculate the distance of the components
- // and find the largest of them on both axes
- var w = 0f
- var h = 0f
-
- for (i in 0 until components.size) {
- val c = components[i]
- w = (c.relX + c.relWidth + c.marginRight).coerceAtLeast(w)
- h = (c.relY + c.relHeight + c.marginBottom).coerceAtLeast(h)
- }
-
- val x = if (style.useFBO) 0f else relX
- val y = if (style.useFBO) 0f else relY
-
- expandedWidth = (w - relWidth - x).coerceAtLeast(0f)
- expandedHeight = (h - relHeight - y).coerceAtLeast(0f)
-
- updateScrollbar()
- }
-
- override fun updateLayout() {
- components.forEach { it.update() }
- }
-
- open fun updateScrollbar() {
- style.verticalScrollbar.update(this)
- style.horizontalScrollbar.update(this)
- }
-
- open fun renderScrollbar() {
- style.verticalScrollbar.render()
- style.horizontalScrollbar.render()
- }
-
- override fun renderContent() {
- if (style.useFBO && (requiresUpdate || !style.optimizeRenderer)) {
- if (fbo == null) {
- updateFBO()
- }
- renderer {
- fbo!!.renderToFramebuffer {
- translate(
- -(style.horizontalScrollbar.value * expandedWidth),
- -(style.verticalScrollbar.value * expandedHeight)
- ) {
- components.forEach(UIComponent<*>::render)
- }
- }
- }
- requiresUpdate = false
- }
- }
-
- override fun renderComponent() {
- if (style.useFBO) {
- Aether.renderer.renderFbo(
- fbo!!,
- relX,
- relY,
- relWidth,
- relHeight,
- style.background?.radius?.topLeft ?: 0f,
- style.background?.radius?.topRight ?: 0f,
- style.background?.radius?.bottomLeft ?: 0f,
- style.background?.radius?.bottomRight ?: 0f
- )
- } else {
- renderer {
- translate(
- -(style.horizontalScrollbar.value * expandedWidth),
- -(style.verticalScrollbar.value * expandedHeight)
- ) {
- if (style.clipContent) scissor(relX, relY, relWidth, relHeight) {
- components.forEach(UIComponent<*>::render)
- }
- else components.forEach(UIComponent<*>::render)
- }
- }
- }
- }
-
- override fun render() {
- super.render()
- renderScrollbar()
- }
-
- override fun updateAnimation() {
- if (animations != null) {
- animations!!.forEach { it.value.update() }
- animations!!.entries.removeIf { it.value.isCompleted }
- if (animations!!.isEmpty())
- animations = null
- updateLayout()
- }
- }
-
- override fun mousePressed(event: UIMouseEvent) {
- super.mousePressed(event)
- if (style.verticalScrollbar.mousePressed() || style.horizontalScrollbar.mousePressed()) focus()
- }
-
- override fun mouseReleased(mouseX: Float, mouseY: Float) {
- super.mouseReleased(
- mouseX - (style.horizontalScrollbar.value * expandedWidth),
- mouseY - (style.verticalScrollbar.value * expandedHeight)
- )
- style.verticalScrollbar.release()
- style.horizontalScrollbar.release()
- }
-
- override fun mouseMoved(mouseX: Float, mouseY: Float) {
- super.mouseMoved(
- mouseX - (style.horizontalScrollbar.value * expandedWidth),
- mouseY - (style.verticalScrollbar.value * expandedHeight)
- )
- style.verticalScrollbar.mouseMoved()
- style.horizontalScrollbar.mouseMoved()
- if (style.verticalScrollbar.selected || style.horizontalScrollbar.selected)
- requestUpdate()
- }
-
- override fun mouseScrolled(mouseX: Float, mouseY: Float, scrollAmount: Float) {
- if (isFocused()) {
- style.verticalScrollbar.value -= ((scrollAmount * scrollSensitivity) / style.verticalScrollbar.cachedHeight)
- mouseMoved(mouseX, mouseY)
- }
- super.mouseScrolled(mouseX, mouseY, scrollAmount)
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/grid/UIGridLayout.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/grid/UIGridLayout.kt
deleted file mode 100644
index 624d639..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/grid/UIGridLayout.kt
+++ /dev/null
@@ -1,155 +0,0 @@
-package net.prismclient.aether.ui.component.type.layout.grid
-
-class UIGridLayout
-
-//open class UIGridLayoutComponent(style: String?) : UIContainer(style) {
-// protected var columns: Array = arrayOf()
-// protected var rows: Array = arrayOf()
-//
-// var columnSpacing: UIPixel = px(0)
-// var rowSpacing: UIPixel = px(0)
-//
-// var retainSpace = true /* If there is a missing component the grid will take the previous row's spacing to fill */
-//
-// fun width(): Float = width
-//
-// fun height(): Float = height
-//
-// override fun updateLayout() {
-// var i = 0
-//
-// val leftoverWidth = FloatArray(rows.size) { 0f }
-// var leftoverHeight = 0f
-// val heights = FloatArray(rows.size) { 0f }
-// var fractionWidthValue = 0f
-// var fractionHeightValue = 0f
-// var widthAutoCount = 0
-// var heightAutoCount = 0
-//
-// var hoffset = 0f
-// for ((r, row) in rows.withIndex()) {
-// var woffset = 0f
-// for ((c, column) in columns.withIndex()) {
-// val child = children.getOrNull(i).also {
-// it?.updateSize()
-// it?.overrided = true
-// }
-// var w = when (column.type) {
-// INITIAL -> width(i)
-// PIXELS -> column.value
-// RELATIVE -> column.value * width()
-// FRACTION -> {
-// if (r == 0) { fractionWidthValue += column.value }
-// width(i)
-// }
-// AUTO -> {
-// if (r == 0) { widthAutoCount++ }
-// width(i)
-// }
-// else -> throw UnsupportedOperationException()
-// }
-// var h = when (row.type) {
-// INITIAL -> height(i)
-// PIXELS -> row.value
-// RELATIVE -> row.value * height()
-// FRACTION, AUTO -> height(i)
-// else -> throw UnsupportedOperationException()
-// }
-//
-// if (child != null) {
-// if (heights[r] < h) {
-// heights[r] = h
-// }
-//
-// child.x = x + woffset
-// child.y = y// + hoffset
-// child.width = w
-// child.height = h
-// w = child.relWidth
-// }
-//
-// woffset += w + if (c < columns.size - 1) columnSpacing.calculate(width) else 0f
-//
-// i++
-// }
-// if (row.type == AUTO) {
-// heightAutoCount++
-// } else if (row.type == FRACTION) {
-// fractionHeightValue += row.value
-// }
-// leftoverWidth[r] = width() - woffset
-// hoffset += heights[r] + if (r < rows.size - 2) rowSpacing.calculate(height) else 0f
-// }
-// leftoverHeight = height() - hoffset
-//
-// i = 0
-// var hp = 0f
-//
-// for ((r, row) in rows.withIndex()) {
-// val widthFractionPush = leftoverWidth[r] / max(1f, fractionWidthValue)
-// val heightFractionPush = leftoverHeight / max(1f, fractionHeightValue)
-// var totalHeight = 0f
-// for (column in columns) {
-// val child = children.getOrNull(i)
-//
-// if (child != null) {
-// child.height = heights[r]
-//
-// var wpush = 0f
-// var hpush = 0f
-//
-// if (column.type == AUTO) {
-// if (fractionWidthValue < 1f) {
-// wpush = (leftoverWidth[r] - (widthFractionPush * fractionWidthValue)) / widthAutoCount
-// }
-// } else if (column.type == FRACTION) {
-// wpush = widthFractionPush * column.value
-// }
-//
-// if (row.type == AUTO) {
-// if (fractionHeightValue < 1f) {
-// hpush = (leftoverHeight / heightAutoCount)
-// }
-// } else if (row.type == FRACTION) {
-// hpush = heightFractionPush * row.value//leftoverHeight * row.value
-// }
-//
-// if (wpush != 0f || hpush != 0f) {
-// child.y += hp
-// child.width += wpush
-// child.height += hpush
-// totalHeight = child.height
-//
-//
-// for (j in i + 1 until (columns.size * (r + 1))) {
-// (children.getOrNull(j) ?: break).x += wpush
-// }
-// }
-// }
-// i++
-// }
-// hp += totalHeight + if (r < rows.size - 1) rowSpacing.calculate(height) else 0f
-// }
-// children.forEach(UIComponent::update)
-// }
-//
-// private fun width(index: Int) = children.getOrNull(index)?.width ?: 0f
-//
-// private fun height(index: Int) = children.getOrNull(index)?.height ?: 0f
-//
-// fun column(vararg units: UIPixel) {
-// columns = arrayOf(*units)
-// }
-//
-// fun row(vararg units: UIPixel) {
-// rows = arrayOf(*units)
-// }
-//
-// fun acolumn(units: Array) {
-// columns = units
-// }
-//
-// fun arow(units: Array) {
-// rows = units
-// }
-//}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/styles/UIContainerSheet.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/styles/UIContainerSheet.kt
deleted file mode 100644
index 210ac5e..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/styles/UIContainerSheet.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package net.prismclient.aether.ui.component.type.layout.styles
-
-import net.prismclient.aether.ui.component.type.layout.styles.UIContainerSheet.Overflow
-import net.prismclient.aether.ui.renderer.impl.scrollbar.UIScrollbar
-import net.prismclient.aether.ui.style.UIStyleSheet
-
-/**
- * [UIContainerSheet] is the corresponding style sheet for containers. It provides
- * properties for the scrollbar and when to introduce them
- *
- * @author sen
- * @since 5/12/2022
- * @see UIScrollbar
- * @see Overflow
- */
-open class UIContainerSheet @JvmOverloads constructor(name: String = "") : UIFrameSheet(name) {
- /**
- * Describes when to introduce the scrollbar
- *
- * @see Overflow
- */
- var overflowX: Overflow = Overflow.Auto
-
- /**
- * Describes when to introduce the scrollbar
- *
- * @see Overflow
- */
- var overflowY: Overflow = Overflow.Auto
-
- var verticalScrollbar: UIScrollbar = UIScrollbar(UIScrollbar.Scrollbar.Vertical)
- var horizontalScrollbar: UIScrollbar = UIScrollbar(UIScrollbar.Scrollbar.Horizontal)
-
- /**
- * Creates a DSL block for the vertical scrollbar
- */
- inline fun verticalScrollbar(block: UIScrollbar.() -> Unit) = verticalScrollbar.block()
-
- /**
- * Creates a DSL block for the horizontal scrollbar
- */
- inline fun horizontalScrollbar(block: UIScrollbar.() -> Unit) = horizontalScrollbar.block()
-
- /**
- * [Overflow] defines what the vertical, and horizontal scrollbars
- * are supposed to do when content leaves the screen. Check the enum
- * documentation for more information.
- *
- * @author sen
- * @since 5/12/2022
- */
- enum class Overflow {
- /**
- * Does not introduce a scrollbar on the given axis
- */
- None,
-
- /**
- * Creates a scrollbar on the given axis regardless if content leaves the window
- */
- Always,
-
- /**
- * Like scroll, but only adds the scrollbar on the given axis if content leaves the window
- */
- Auto
- }
-
- override fun apply(sheet: UIStyleSheet): UIContainerSheet {
- // Override the default apply function because
- // this is an inheritable class.
- this.immutableStyle = sheet.immutableStyle
- this.name = sheet.name
-
- this.background = sheet.background?.copy()
- this.font = sheet.font?.copy()
-
- this.x = sheet.x?.copy()
- this.y = sheet.y?.copy()
- this.width = sheet.width?.copy()
- this.height = sheet.height?.copy()
-
- this.padding = sheet.padding?.copy()
- this.margin = sheet.margin?.copy()
- this.anchor = sheet.anchor?.copy()
- this.clipContent = sheet.clipContent
-
- // Frame properties
- if (sheet is UIContainerSheet) {
- this.useFBO = sheet.useFBO
- this.optimizeRenderer = sheet.optimizeRenderer
- this.overflowX = sheet.overflowX
- this.overflowY = sheet.overflowY
- this.verticalScrollbar = sheet.verticalScrollbar.copy()
- this.horizontalScrollbar = sheet.horizontalScrollbar.copy()
- }
-
- return this
- }
-
- override fun copy(): UIContainerSheet = UIContainerSheet(name).apply(this)
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/styles/UIFrameSheet.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/styles/UIFrameSheet.kt
deleted file mode 100644
index ff31030..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/styles/UIFrameSheet.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package net.prismclient.aether.ui.component.type.layout.styles
-
-import net.prismclient.aether.ui.style.UIStyleSheet
-
-/**
- * [UIFrameSheet] is the corresponding style sheet for frames. See field
- * documentation for more information.
- *
- * @author sen
- * @since 1.0
- */
-open class UIFrameSheet(name: String) : UIStyleSheet(name) {
- /**
- * If true, the frame will use an FBO to render content.
- */
- var useFBO: Boolean = false
-
- /**
- * If true certain optimizations will be applied when
- * rendering. This only works with [useFBO] as true.
- */
- var optimizeRenderer: Boolean = true
-
- override fun apply(sheet: UIStyleSheet): UIFrameSheet {
- // Override the default apply function because
- // this is an inheritable class.
- this.immutableStyle = sheet.immutableStyle
- this.name = sheet.name
-
- this.background = sheet.background?.copy()
- this.font = sheet.font?.copy()
-
- this.x = sheet.x?.copy()
- this.y = sheet.y?.copy()
- this.width = sheet.width?.copy()
- this.height = sheet.height?.copy()
-
- this.padding = sheet.padding?.copy()
- this.margin = sheet.margin?.copy()
- this.anchor = sheet.anchor?.copy()
- this.clipContent = sheet.clipContent
-
- // Frame properties
- if (sheet is UIFrameSheet) {
- this.useFBO = sheet.useFBO
- this.optimizeRenderer = sheet.optimizeRenderer
- }
-
- return this
- }
-
- override fun copy(): UIFrameSheet = UIFrameSheet(name).apply(this)
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/other/progress/UIProgress.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/other/UIProgress.kt
similarity index 58%
rename from src/main/kotlin/net/prismclient/aether/ui/component/type/other/progress/UIProgress.kt
rename to src/main/kotlin/net/prismclient/aether/ui/component/type/other/UIProgress.kt
index 583f357..69bd0d2 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/other/progress/UIProgress.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/other/UIProgress.kt
@@ -1,7 +1,10 @@
-package net.prismclient.aether.ui.component.type.other.progress
+package net.prismclient.aether.ui.component.type.other
import net.prismclient.aether.ui.component.UIComponent
+import net.prismclient.aether.ui.style.UIStyleSheet
+import net.prismclient.aether.ui.util.UIColor
import net.prismclient.aether.ui.util.extensions.renderer
+import net.prismclient.aether.ui.util.name
/**
* [UIProgress] is a component which displays the distance between the x and width
@@ -12,14 +15,27 @@ import net.prismclient.aether.ui.util.extensions.renderer
* To configure the color see [UIProgressSheet.progressColor].
*
* @author sen
- * @since 6/23/2022
+ * @since 1.0
*/
-class UIProgress @JvmOverloads constructor(var progress: Float = 0f, style: String?) :
- UIComponent(style) {
+class UIProgress @JvmOverloads constructor(var progress: Float = 0f) : UIComponent() {
override fun renderComponent() {
renderer {
color(style.progressColor)
rect(relX, relY, relWidth * progress, relHeight, style.background?.radius)
}
}
+
+ override fun createsStyle(): UIProgressSheet = UIProgressSheet()
+}
+
+class UIProgressSheet : UIStyleSheet() {
+ /**
+ * The color of the actual progress bar.
+ */
+ var progressColor: UIColor? = null
+
+ override fun copy() = UIProgressSheet().name(name).also {
+ it.apply(this)
+ it.progressColor = progressColor
+ }
}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/type/other/progress/UIProgressSheet.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/other/progress/UIProgressSheet.kt
deleted file mode 100644
index 44b73e9..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/other/progress/UIProgressSheet.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package net.prismclient.aether.ui.component.type.other.progress
-
-import net.prismclient.aether.ui.style.UIStyleSheet
-
-/**
- * [UIProgressSheet] is the corresponding sheet to [UIProgress].
- *
- * @author sen
- * @since 6/23/2022
- */
-class UIProgressSheet(name: String) : UIStyleSheet(name) {
- /**
- * The color of the progress
- */
- var progressColor = -1
-
- override fun copy() = UIProgressSheet(name).also {
- it.apply(this)
- it.progressColor = progressColor
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/util/enums/UIAlignment.kt b/src/main/kotlin/net/prismclient/aether/ui/component/util/enums/UIAlignment.kt
index 62f9ea8..12aaeb6 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/util/enums/UIAlignment.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/util/enums/UIAlignment.kt
@@ -7,7 +7,7 @@ package net.prismclient.aether.ui.component.util.enums
* @author sen
* @since 1.0
*/
-@Suppress("unused", "SpellCheckingInspection")
+@Suppress("Unused", "SpellCheckingInspection")
enum class UIAlignment {
CUSTOM,
TOPLEFT, TOPCENTER, TOPRIGHT,
diff --git a/src/main/kotlin/net/prismclient/aether/ui/component/util/enums/UIOverflow.kt b/src/main/kotlin/net/prismclient/aether/ui/component/util/enums/UIOverflow.kt
deleted file mode 100644
index d1037de..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/util/enums/UIOverflow.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package net.prismclient.aether.ui.component.util.enums
-
-enum class UIOverflow {
- VISIBLE,
- HIDDEN,
- SCROLL,
- AUTO
-}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/dsl/UIAssetDSL.kt b/src/main/kotlin/net/prismclient/aether/ui/dsl/UIAssetDSL.kt
index 73a338b..83125fe 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/dsl/UIAssetDSL.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/dsl/UIAssetDSL.kt
@@ -108,12 +108,12 @@ object UIAssetDSL {
svgScale: Float = Aether.devicePxRatio
): Int {
val file = Aether.javaClass.getResource(folderLocation) ?: run {
- warn("Failed to bulk load [$folderLocation] as the file was null.")
+ error("Failed to bulk load [$folderLocation] as the file was null.")
return 0
}
return internalBulkLoad(File(file.toURI()), deep, appendedString, imageFlags, svgScale).also {
- warn("Bulk loaded $it files.")
+ inform("Bulk loaded $it files.")
}
}
diff --git a/src/main/kotlin/net/prismclient/aether/ui/dsl/UIComponentDSL.kt b/src/main/kotlin/net/prismclient/aether/ui/dsl/UIComponentDSL.kt
index f4649bd..ded9ef4 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/dsl/UIComponentDSL.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/dsl/UIComponentDSL.kt
@@ -7,15 +7,14 @@ import net.prismclient.aether.ui.component.controller.impl.selection.UISelectabl
import net.prismclient.aether.ui.component.type.UILabel
import net.prismclient.aether.ui.component.type.image.UIImage
import net.prismclient.aether.ui.component.type.input.button.UIButton
-import net.prismclient.aether.ui.component.type.input.button.UICheckbox
import net.prismclient.aether.ui.component.type.input.slider.UISlider
import net.prismclient.aether.ui.component.type.layout.UIFrame
-import net.prismclient.aether.ui.component.type.layout.auto.UIAutoLayout
-import net.prismclient.aether.ui.component.type.layout.container.UIContainer
-import net.prismclient.aether.ui.component.type.layout.list.UIListLayout
-import net.prismclient.aether.ui.component.type.layout.styles.UIContainerSheet
+import net.prismclient.aether.ui.component.type.layout.UIAutoLayout
+import net.prismclient.aether.ui.component.type.layout.UIContainer
+import net.prismclient.aether.ui.component.type.layout.UIListLayout
+import net.prismclient.aether.ui.component.type.layout.UIContainerSheet
import net.prismclient.aether.ui.dsl.UIComponentDSL.activeStyle
-import net.prismclient.aether.ui.style.UIStyleSheet
+import net.prismclient.aether.ui.util.Block
import net.prismclient.aether.ui.util.interfaces.UIDependable
import java.util.*
@@ -136,8 +135,9 @@ object UIComponentDSL {
*
* @return T The component
*/
- inline fun > component(component: T, block: T.() -> Unit): T {
+ inline fun > component(component: T, style: String?, block: Block): T {
pushComponent(component)
+ component.applyStyle(style)
component.block()
component.initialize()
popComponent(component)
@@ -151,7 +151,7 @@ object UIComponentDSL {
*
* @see ignore
*/
- inline fun , T : UIComponent<*>> controller(controller: C, block: C.() -> Unit) {
+ inline fun , T : UIComponent<*>> controller(controller: C, block: Block) {
Aether.instance.controllers!!.add(controller)
activeController = controller
controller.block()
@@ -161,7 +161,7 @@ object UIComponentDSL {
/**
* Creates a block where the [activeController] is ignored.
*/
- inline fun ignore(block: UIComponentDSL.() -> Unit): UIComponentDSL {
+ inline fun ignore(block: Block): UIComponentDSL {
ignoreController = true
block(this)
ignoreController = false
@@ -171,7 +171,7 @@ object UIComponentDSL {
/**
* Creates a block where the style is set to the given value.
*/
- inline fun style(styleName: String, block: UIComponentDSL.() -> Unit) {
+ inline fun style(styleName: String, block: Block) {
// Technically this function supports nesting, soooooo the documentation
// is "technically" wrong but whatever. In the case of controllers, it
// doesn't actually make sense to have the ability to nest them.
@@ -187,8 +187,7 @@ object UIComponentDSL {
*/
fun include(dependable: UIDependable) = dependable.load()
- fun getActiveComponent(): UIComponent<*>? =
- if (componentStack.isNullOrEmpty()) null else componentStack!!.peek()
+ fun getActiveComponent(): UIComponent<*>? = if (componentStack.isNullOrEmpty()) null else componentStack!!.peek()
fun getActiveFrame(): UIFrame<*>? = if (frameStack.isNullOrEmpty()) null else frameStack!!.peek()
@@ -207,8 +206,8 @@ object UIComponentDSL {
* @see label
*/
@JvmOverloads
- inline fun text(text: String, style: String? = activeStyle, block: UILabel.() -> Unit = {}) =
- component(UILabel(text, style), block)
+ inline fun text(text: String, style: String? = activeStyle, block: Block = {}) =
+ component(UILabel(text), style, block)
/**
* An alternative to [text]. Creates a [UILabel] with the provided [text].
@@ -216,27 +215,14 @@ object UIComponentDSL {
* @see text
*/
@JvmOverloads
- inline fun label(text: String, style: String? = activeStyle, block: UILabel.() -> Unit = {}) =
- component(UILabel(text, style), block)
+ inline fun label(text: String, style: String? = activeStyle, block: Block = {}) =
+ component(UILabel(text), style, block)
/**
* Creates a [UIButton] with the provided [text], like a label.
*/
- inline fun button(text: String, style: String? = activeStyle, block: UIButton.() -> Unit = {}) =
- component(UIButton(text, style), block)
-
- /**
- * Creates a [UICheckbox] from the given [selectedImageName], [deselectedImageName] and [imageStyle].
- */
- @JvmOverloads
- inline fun checkbox(
- checked: Boolean = false,
- selectedImageName: String = "checkbox",
- deselectedImageName: String = "",
- imageStyle: String,
- style: String? = activeStyle,
- block: UICheckbox.() -> Unit
- ) = component(UICheckbox(checked, selectedImageName, deselectedImageName, imageStyle, style), block)
+ inline fun button(text: String, style: String? = activeStyle, block: Block = {}) =
+ component(UIButton(text), style, block)
/**
* Creates a [UISlider] with the given [value] which stays within the [range] and steps by the
@@ -248,22 +234,22 @@ object UIComponentDSL {
range: ClosedFloatingPointRange,
step: Number,
style: String? = activeStyle,
- block: UISlider.() -> Unit = {}
- ) = component(UISlider(value.toDouble(), range, step.toDouble(), style), block)
+ block: Block = {}
+ ) = component(UISlider(value.toDouble(), range, step.toDouble()), style, block)
/**
* Creates a [UIImage] with the [imageName] as the image to be rendered.
*/
@JvmOverloads
- inline fun image(imageName: String, style: String? = activeStyle, block: UIImage.() -> Unit = {}) =
- component(UIImage(imageName, style), block)
+ inline fun image(imageName: String, style: String? = activeStyle, block: Block = {}) =
+ component(UIImage(imageName), style, block)
/**
* Creates a [UIContainer]. Anything within the block will be added to the component list within this.
*/
@JvmOverloads
- inline fun container(style: String? = activeStyle, block: UIContainer.() -> Unit = {}) =
- component(UIContainer(style), block)
+ inline fun container(style: String? = activeStyle, block: Block> = {}) =
+ component(UIContainer(), style, block)
/**
* Creates a [UIListLayout] with the given [listDirection], which defines the direction that it
@@ -275,14 +261,50 @@ object UIComponentDSL {
listDirection: UIListLayout.ListDirection,
listOrder: UIListLayout.ListOrder = UIListLayout.ListOrder.Forward,
style: String? = activeStyle,
- block: UIListLayout.() -> Unit = {}
- ) = component(UIListLayout(listDirection, listOrder, style), block)
+ block: Block = {}
+ ) = component(UIListLayout(listDirection, listOrder), style, block)
+
+ /**
+ * Creates a vertical list via [list].
+ *
+ * @see list
+ */
+ inline fun verticalList(
+ listOrder: UIListLayout.ListOrder = UIListLayout.ListOrder.Forward,
+ style: String? = activeStyle,
+ block: Block = {}
+ ) = list(UIListLayout.ListDirection.Vertical, listOrder, style, block)
+
+ /**
+ * Creates a horizontal list via [list].
+ *
+ * @see list
+ */
+ inline fun horizontalList(
+ listOrder: UIListLayout.ListOrder = UIListLayout.ListOrder.Forward,
+ style: String? = activeStyle,
+ block: Block = {}
+ ) = list(UIListLayout.ListDirection.Horizontal, listOrder, style, block)
/**
* Creates a copy of the given layout and creates a normal block of [UIAutoLayout] where
* components can be defined.
*/
- inline fun autoLayout(layout: UIAutoLayout, block: UIAutoLayout.() -> Unit) = component(layout.copy(), block)
+ @JvmOverloads
+ inline fun autoLayout(layout: UIAutoLayout, block: Block = {}) = component(layout.copy(), activeStyle, block)
+
+ /**
+ * Creates an [UIAutoLayout] from the given [listDirection]. [UIAutoLayout] are designed to mimic Figma's
+ * auto layout feature. See [UIAutoLayout] for more information.
+ *
+ * @see UIAutoLayout
+ */
+ @JvmOverloads
+ inline fun autoLayout(
+ listDirection: UIListLayout.ListDirection,
+ style: String? = null,
+ block: Block = {}
+ ) = component(UIAutoLayout(listDirection), style, block)
/**
* Creates a [UISelectableController] which is a controller that has a single selected
@@ -291,6 +313,7 @@ object UIComponentDSL {
* @see controller
* @see UISelectableController
*/
- inline fun > selectable(block: UISelectableController.() -> Unit) =
+ @JvmOverloads
+ inline fun > selectable(block: Block> = {}) =
controller(UISelectableController(T::class), block)
}
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/dsl/UIPathDSL.kt b/src/main/kotlin/net/prismclient/aether/ui/dsl/UIPathDSL.kt
index b49d5aa..3d33bfb 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/dsl/UIPathDSL.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/dsl/UIPathDSL.kt
@@ -2,6 +2,8 @@ package net.prismclient.aether.ui.dsl
import net.prismclient.aether.ui.Aether
import net.prismclient.aether.ui.renderer.UIRenderer
+import net.prismclient.aether.ui.renderer.impl.property.UIRadius
+import net.prismclient.aether.ui.util.Block
/**
* [UIPathDSL] is a DSL for paths. [UIRendererDSL] utilizes this to apply the paths.
@@ -149,6 +151,21 @@ object UIPathDSL {
x: Float, y: Float, radius: Float, startAngle: Float, endAngle: Float, windingOrder: UIRenderer.WindingOrder
) = renderer.arc(x, y, radius, startAngle, endAngle, windingOrder)
+ /**
+ * Renders a rectangle sub-path with the given bounds and [radius].
+ */
+ @JvmStatic
+ fun rect(x: Float, y: Float, width: Float, height: Float, radius: UIRadius?) = rect(
+ x,
+ y,
+ width,
+ height,
+ radius?.topLeft ?: 0f,
+ radius?.topRight ?: 0f,
+ radius?.bottomRight ?: 0f,
+ radius?.bottomLeft ?: 0f
+ )
+
/**
* Creates a rectangle sub-path with a single radius value.
*/
@@ -216,8 +233,8 @@ object UIPathDSL {
* @see See NanoVG Composite paths
*/
@JvmStatic
- inline fun hole(block: UIPathDSL.() -> Unit): UIPathDSL {
- block()
+ inline fun hole(block: Block): UIPathDSL {
+ UIPathDSL.block()
renderer.pathHole(true)
return this
}
diff --git a/src/main/kotlin/net/prismclient/aether/ui/dsl/UIRendererDSL.kt b/src/main/kotlin/net/prismclient/aether/ui/dsl/UIRendererDSL.kt
index 926acc8..cf0f1c0 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/dsl/UIRendererDSL.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/dsl/UIRendererDSL.kt
@@ -8,8 +8,7 @@ import net.prismclient.aether.ui.renderer.impl.border.UIStrokeDirection
import net.prismclient.aether.ui.renderer.impl.font.UIFont
import net.prismclient.aether.ui.renderer.impl.property.UIRadius
import net.prismclient.aether.ui.renderer.other.UIContentFBO
-import net.prismclient.aether.ui.util.Block
-import net.prismclient.aether.ui.util.UIColor
+import net.prismclient.aether.ui.util.*
/**
* [UIRendererDSL] wraps the [UIRenderer] class to minimize the amount of calls
@@ -77,7 +76,7 @@ object UIRendererDSL {
*/
@JvmStatic
fun color(color: Int) {
- UIRendererDSL.activeColor = color
+ activeColor = color
renderer.color(color)
}
@@ -154,13 +153,13 @@ object UIRendererDSL {
* @see fontBounds
*/
@JvmStatic
- fun fontWidth(): Float = fontBounds()[2] - fontBounds()[0]
+ fun fontWidth(): Float = fontBounds().maxX() - fontBounds().minX()
/**
* Returns the height of the most recent text render call.
*/
@JvmStatic
- fun fontHeight(): Float = fontBounds()[3] - fontBounds()[1]
+ fun fontHeight(): Float = fontBounds().maxY() - fontBounds().minY()
/**
* Returns the ascender of the most recent text render call.
@@ -176,6 +175,9 @@ object UIRendererDSL {
// -- General Rendering -- //
+ /**
+ * renders a rectangle with the given bounds and [radius].
+ */
@JvmStatic
fun rect(x: Float, y: Float, width: Float, height: Float, radius: UIRadius?) = rect(
x,
@@ -399,7 +401,7 @@ object UIRendererDSL {
@JvmStatic
inline fun UIContentFBO.renderToFramebuffer(block: Block): UIRendererDSL {
renderer.bindFBO(this)
- beginFrame(this.scaledWidth, this.scaledHeight, this.contentScale)
+ beginFrame(this.width, this.height, this.contentScale)
UIRendererDSL.block()
endFrame()
renderer.unbindFBO()
diff --git a/src/main/kotlin/net/prismclient/aether/ui/renderer/UIProvider.kt b/src/main/kotlin/net/prismclient/aether/ui/renderer/UIProvider.kt
index 1b94e72..5727146 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/renderer/UIProvider.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/renderer/UIProvider.kt
@@ -48,10 +48,9 @@ object UIProvider {
* Returns the name of the images given the [UIImageData]
*/
fun getImageName(imageData: UIImageData): String? {
- for (image in images) {
- if (image.value == imageData) {
- return image.key
- }
+ for ((name, image) in images) {
+ if (image == imageData)
+ return name
}
return null
}
diff --git a/src/main/kotlin/net/prismclient/aether/ui/renderer/UIRenderer.kt b/src/main/kotlin/net/prismclient/aether/ui/renderer/UIRenderer.kt
index a7ba8af..cc7ac79 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/renderer/UIRenderer.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/renderer/UIRenderer.kt
@@ -1,7 +1,6 @@
package net.prismclient.aether.ui.renderer
import net.prismclient.aether.ui.renderer.image.UIImageData
-import net.prismclient.aether.ui.renderer.impl.property.UIRadius
import net.prismclient.aether.ui.renderer.other.UIContentFBO
import java.nio.ByteBuffer
@@ -139,18 +138,6 @@ interface UIRenderer {
*/
fun unbindFBO()
- fun renderFbo(
- fbo: UIContentFBO,
- x: Float,
- y: Float,
- width: Float,
- height: Float,
- topLeft: Float,
- topRight: Float,
- bottomRight: Float,
- bottomLeft: Float
- )
-
// -- Asset Loading --/
/**
* Creates an image from the given [data] registered to the [imageName].
diff --git a/src/main/kotlin/net/prismclient/aether/ui/renderer/impl/background/UIGradientBackground.kt b/src/main/kotlin/net/prismclient/aether/ui/renderer/impl/background/UIGradientBackground.kt
index 985502f..6097eb6 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/renderer/impl/background/UIGradientBackground.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/renderer/impl/background/UIGradientBackground.kt
@@ -2,18 +2,19 @@ package net.prismclient.aether.ui.renderer.impl.background
import net.prismclient.aether.ui.component.UIComponent
import net.prismclient.aether.ui.unit.UIUnit
+import net.prismclient.aether.ui.util.UIColor
import net.prismclient.aether.ui.util.extensions.calculate
import net.prismclient.aether.ui.util.extensions.renderer
/**
- * [UIGradientBackground] is the background renderer for a component which requests a gradient instead of a solid
+ * [UIGradientBackground] is the background renderer for a component which requests a gradient instead of a solid.
*
* @author sen
- * @since 4/26/2022
+ * @since 41.0
*/
class UIGradientBackground : UIBackground() {
- var gradientStartColor = 0
- var gradientEndColor = 0
+ var gradientStartColor: UIColor? = null
+ var gradientEndColor: UIColor? = null
var gradientX: UIUnit? = null
var gradientY: UIUnit? = null
var gradientWidth: UIUnit? = null
@@ -44,15 +45,18 @@ class UIGradientBackground : UIBackground() {
component?.relHeight ?: 0f,
true
)
- gradientWidthCache =
+ gradientWidthCache = gradientXCache +
calculate(gradientWidth, component, component?.relWidth ?: 0f, component?.relHeight ?: 0f, false)
- gradientHeightCache =
+ gradientHeightCache = gradientYCache +
calculate(gradientHeight, component, component?.relWidth ?: 0f, component?.relHeight ?: 0f, true)
}
override fun render() {
renderer {
- TODO("Gradient suport")
+ path {
+ linearGradient(gradientXCache, gradientYCache, gradientWidthCache, gradientHeightCache, gradientStartColor?.rgba ?: 0, gradientEndColor?.rgba ?: 0)
+ rect(cachedX, cachedY, cachedWidth, cachedHeight, radius)
+ }.fillPaint()
}
}
diff --git a/src/main/kotlin/net/prismclient/aether/ui/renderer/impl/scrollbar/UIScrollbar.kt b/src/main/kotlin/net/prismclient/aether/ui/renderer/impl/scrollbar/UIScrollbar.kt
index a2e837d..ee79d69 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/renderer/impl/scrollbar/UIScrollbar.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/renderer/impl/scrollbar/UIScrollbar.kt
@@ -1,8 +1,8 @@
package net.prismclient.aether.ui.renderer.impl.scrollbar
import net.prismclient.aether.ui.component.UIComponent
-import net.prismclient.aether.ui.component.type.layout.container.UIContainer
-import net.prismclient.aether.ui.component.type.layout.styles.UIContainerSheet
+import net.prismclient.aether.ui.component.type.layout.UIContainer
+import net.prismclient.aether.ui.component.type.layout.UIContainerSheet
import net.prismclient.aether.ui.renderer.impl.background.UIBackground
import net.prismclient.aether.ui.renderer.impl.border.UIBorder
import net.prismclient.aether.ui.renderer.impl.property.UIRadius
@@ -56,7 +56,7 @@ class UIScrollbar(val type: Scrollbar) : UIColoredShape() {
}
fun shouldRender() {
- val container = component as UIContainer<*>
+ val container = component as UIContainer
// Check based on the overflow if the scrollbar should be rendered or not
shouldRender = if (type == Scrollbar.Vertical) {
@@ -121,12 +121,25 @@ class UIScrollbar(val type: Scrollbar) : UIColoredShape() {
val mouseX = component!!.getMouseX()
val mouseY = component!!.getMouseY()
- if (mouseX >= x && mouseX <= x + w && mouseY >= y && mouseY <= y + h) {
+ val isSliderSelected = mouseX >= x && mouseX <= x + w && mouseY >= y && mouseY <= y + h
+ val isScrollbarSelected = mouseX >= cachedX && mouseX <= cachedX + cachedWidth && mouseY >= cachedY && mouseY <= cachedY + cachedHeight
+
+ if (isSliderSelected) {
selected = true
mouseOffset = if (type == Scrollbar.Vertical) mouseY - y else mouseX - x
- return true
+ } else if (isScrollbarSelected) {
+ println("scrollbarSelected")
+
+// value = if (type == Scrollbar.Vertical) {
+// (mouseY - cachedY - (mouseY - y)) / (cachedHeight - sliderSize).coerceAtLeast(Float.MIN_VALUE)
+// } else {
+// (mouseX - cachedX - (mouseX - x)) / (cachedWidth - sliderSize).coerceAtLeast(Float.MIN_VALUE)
+// }
+ } else {
+ return false
}
- return false
+
+ return true
}
fun mouseMoved() {
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 eb9ed72..353084a 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/style/UIStyleSheet.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/style/UIStyleSheet.kt
@@ -2,7 +2,7 @@ package net.prismclient.aether.ui.style
import net.prismclient.aether.ui.animation.UIAnimation
import net.prismclient.aether.ui.component.UIComponent
-import net.prismclient.aether.ui.component.type.layout.styles.UIFrameSheet
+import net.prismclient.aether.ui.component.type.layout.UIFrameSheet
import net.prismclient.aether.ui.component.util.enums.UIAlignment
import net.prismclient.aether.ui.component.util.enums.UIAlignment.*
import net.prismclient.aether.ui.renderer.impl.background.UIBackground
@@ -19,6 +19,7 @@ import net.prismclient.aether.ui.util.extensions.px
import net.prismclient.aether.ui.util.extensions.rel
import net.prismclient.aether.ui.util.interfaces.UIAnimatable
import net.prismclient.aether.ui.util.interfaces.UICopy
+import net.prismclient.aether.ui.util.name
/**
* [UIStyleSheet] is the superclass of all styles. It holds the general
@@ -39,11 +40,13 @@ import net.prismclient.aether.ui.util.interfaces.UICopy
* are thrown when used. If the style sheet is intended on being inheritable, the apply method
* should also be overridden. See [UIFrameSheet] for an example.
*
+ * @since 1.0
* @see Styles
* @see How to create styles
*/
-open class UIStyleSheet @JvmOverloads constructor(var name: String = "") : UICopy,
- UIAnimatable {
+open class UIStyleSheet() : UICopy, UIAnimatable {
+ var name: String = ""
+
/**
* When true, the property will not be cleared when Aether cleans up styles.
*/
@@ -82,10 +85,12 @@ open class UIStyleSheet @JvmOverloads constructor(var name: String = "") : UICop
) {
val component = animation.component
- component.x = previous?.x.lerp(current?.x, component, x, progress, false)
- component.y = previous?.y.lerp(current?.y, component, y, progress, true)
- component.width = previous?.width.lerp(current?.width, component, width, progress, false)
- component.height = previous?.height.lerp(current?.height, component, height, progress, true)
+ if (!component.overridden) {
+ component.x = previous?.x.lerp(current?.x, component, x, progress, false)
+ component.y = previous?.y.lerp(current?.y, component, y, progress, true)
+ component.width = previous?.width.lerp(current?.width, component, width, progress, false)
+ component.height = previous?.height.lerp(current?.height, component, height, progress, true)
+ }
if (previous?.background != null || current?.background != null) {
background = background ?: UIBackground()
@@ -344,7 +349,7 @@ open class UIStyleSheet @JvmOverloads constructor(var name: String = "") : UICop
this.marginLeft = marginLeft
}
- override fun copy(): UIStyleSheet = UIStyleSheet(name).apply(this)
+ override fun copy(): UIStyleSheet = UIStyleSheet().name(name).apply(this)
/**
* Applies the properties of an existing sheet to this
diff --git a/src/main/kotlin/net/prismclient/aether/ui/style/util/UIAnchorPoint.kt b/src/main/kotlin/net/prismclient/aether/ui/style/util/UIAnchorPoint.kt
index db2bfe2..1aa6ca5 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/style/util/UIAnchorPoint.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/style/util/UIAnchorPoint.kt
@@ -3,6 +3,7 @@ package net.prismclient.aether.ui.style.util
import net.prismclient.aether.ui.animation.UIAnimation
import net.prismclient.aether.ui.component.util.enums.UIAlignment
import net.prismclient.aether.ui.unit.UIUnit
+import net.prismclient.aether.ui.util.extensions.align
import net.prismclient.aether.ui.util.extensions.lerp
import net.prismclient.aether.ui.util.extensions.px
import net.prismclient.aether.ui.util.interfaces.UIAnimatable
@@ -22,7 +23,7 @@ class UIAnchorPoint : UIAnimatable {
infix fun align(alignment: UIAlignment) {
x ?: run { x = px(0) }
y ?: run { y = px(0) }
- net.prismclient.aether.ui.util.extensions.align(alignment, x!!, y!!)
+ align(alignment, x!!, y!!)
}
override fun animate(
diff --git a/src/main/kotlin/net/prismclient/aether/ui/util/Shorthands.kt b/src/main/kotlin/net/prismclient/aether/ui/util/Shorthands.kt
index e1e85f7..a58938d 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/util/Shorthands.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/util/Shorthands.kt
@@ -4,6 +4,7 @@ import net.prismclient.aether.ui.animation.UIAnimation
import net.prismclient.aether.ui.component.UIComponent
import net.prismclient.aether.ui.dsl.UIComponentDSL
import net.prismclient.aether.ui.renderer.UIProvider
+import net.prismclient.aether.ui.renderer.impl.background.UIGradientBackground
import net.prismclient.aether.ui.renderer.impl.property.UIMargin
import net.prismclient.aether.ui.renderer.impl.property.UIPadding
import net.prismclient.aether.ui.renderer.impl.property.UIRadius
@@ -34,47 +35,50 @@ const val NEAREST = 32
/**
* Creates a DSL block from the given [obj] of type [T].
*/
-inline fun blockFrom(obj: T, block: T.() -> Unit) = obj.block()
+inline fun blockFrom(obj: T, block: Block) = obj.block()
/**
- * Registers a style sheet for the given style, [S].
+ * Creates a style sheet [block] from the given style sheet [S], sets the [name] and registers the style.
*/
-inline fun style(style: S, block: S.() -> Unit) {
- style.block()
- UIProvider.registerStyle(style)
+@JvmName("styleExtension")
+inline fun S.style(name: String, block: Block): S = apply {
+ this.name = name
+ this.block()
+ UIProvider.registerStyle(this)
}
/**
- * Registers a style sheet for the given style, [S]. Alternative to [style].
- *
- * @see style
+ * Creates a style sheet [block] from the given style sheet [S], sets the [name] and registers the style.
*/
-inline fun styleOf(style: S, block: S.() -> Unit) = style(style, block)
-
-@JvmName("styleExtension")
-inline fun S.style(block: S.() -> Unit) = style(this, block)
+inline fun style(sheet: S, name: String, block: Block): S {
+ sheet.name = name
+ sheet.block()
+ UIProvider.registerStyle(sheet)
+ return sheet
+}
/**
- * Registers a style sheet in the component scope. This is used when no style is provided
- * to the component, and instead, the style is provided at the component level.
+ * Creates a style [block] from the given component [C]. If the style has not been created
+ * one is automatically allocated with the name of
*
- * If the style's name is blank, it will be formatted as "Gen-${component.toString()}".
- */
-inline fun , S : UIStyleSheet> C.style(style: S, block: S.() -> Unit): C = apply {
- UIComponentDSL.updateState(this)
- if (style.name.isEmpty()) style.name = "Gen-$this"
- styleOf(style, block)
- this.applyStyle(style.name)
- UIComponentDSL.restoreState(this)
-}
+ * "$C.toString()-sheet"
+ */
+inline fun , S : UIStyleSheet> C.style(block: Block): C = this.style("$this-sheet", block)
/**
- * Creates a style [block] on a [UIComponent] of the style sheet [S].
+ * Creates a style [block] from the given component [C]. If the style has not been
+ * created, one is automatically allocated with the name of [name].
*/
-inline fun , S : UIStyleSheet> C.style(block: S.() -> Unit): C = apply {
- UIComponentDSL.updateState(this)
+inline fun , S : UIStyleSheet> C.style(name: String, block: Block): C = also {
+ if (!this.hasStyle()) {
+ this.createsStyle().run {
+ this.name = name
+ UIProvider.registerStyle(this)
+ }
+ this.applyStyle(name)
+ }
+
this.style.block()
- UIComponentDSL.restoreState(this)
}
/**
@@ -135,7 +139,7 @@ fun marginOf(top: UIUnit, right: UIUnit, bottom: UIUnit, left: UIUnit): UIMargin
*
* @see ucreate
*/
-inline fun create(block: UIComponentDSL.() -> Unit) {
+inline fun create(block: Block) {
UIComponentDSL.begin()
UIComponentDSL.block()
UIComponentDSL.complete()
@@ -144,7 +148,7 @@ inline fun create(block: UIComponentDSL.() -> Unit) {
/**
* Unsafe version of [build]. Does not allocate/deallocate the stacks, thus nothing will be reset
*/
-inline fun ucreate(block: UIComponentDSL.() -> Unit) = UIComponentDSL.block()
+inline fun ucreate(block: Block) = UIComponentDSL.block()
/**
* Loads the [dependable].
@@ -152,12 +156,11 @@ inline fun ucreate(block: UIComponentDSL.() -> Unit) = UIComponentDSL.block()
fun include(dependable: UIDependable) = dependable.load()
/**
- * Creates an animation where the component is [C], the style of that component is [S], the animation
- * name is [animationName], and an instance of the component's style is passed as [style]. A block is
- * given to add the keyframes and modify other properties of the [UIAnimation]. The animation is
- * automatically registered under the name given.
+ * Creates an animation for the stylesheet [S], with the name [animationName].
+ * A block is given to add the keyframes and modify other properties of the [UIAnimation].
+ * The animation is automatically registered under the name given.
*/
-fun animationOf(animationName: String, style: S, block: UIAnimation.() -> Unit): UIAnimation {
+inline fun animationOf(animationName: String, style: S, block: UIAnimation.() -> Unit): UIAnimation {
val animation = UIAnimation(animationName, style)
animation.block()
UIProvider.registerAnimation(animationName, animation)
@@ -165,7 +168,6 @@ fun animationOf(animationName: String, style: S, block: UIAni
}
/**
- * Type alias for a function which has a receiver of [T] and accepts, and returns
- * nothing. The block is intended to apply properties to the receiver [T].
+ * Creates a [UIGradientBackground], applies the given block to it, and returns it.
*/
-typealias Block = T.() -> Unit
\ No newline at end of file
+inline fun gradient(block: Block): UIGradientBackground = UIGradientBackground().also(block)
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/util/UIAetherLogger.kt b/src/main/kotlin/net/prismclient/aether/ui/util/UIAetherLogger.kt
index 4509113..46696b9 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/util/UIAetherLogger.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/util/UIAetherLogger.kt
@@ -2,14 +2,33 @@ package net.prismclient.aether.ui.util
import net.prismclient.aether.ui.Aether
-internal fun inform(message: String) {
- if (Aether.debug)
- println("[Aether]: $message")
+enum class LogLevel {
+ DEBUG,
+ GLOBAL
+}
+
+internal inline fun timed(message: String, level: LogLevel = LogLevel.DEBUG, block: () -> Unit) {
+ if (level != LogLevel.DEBUG || Aether.debug) {
+ println("[Aether]: TIMED -> $message")
+ val start = System.currentTimeMillis()
+ block()
+ println("[Aether]: TIMED -> took ${System.currentTimeMillis() - start}ms")
+ }
}
-internal fun warn(message: String) {
+internal fun inform(message: String, level: LogLevel = LogLevel.DEBUG) {
+ if (level != LogLevel.DEBUG || Aether.debug)
+ println("[Aether]: INFO -> $message")
+}
+
+internal fun debug(message: String) {
if (Aether.debug)
- println("[Aether]: $message")
+ println("[Aether]: DEBUG -> $message")
+}
+
+internal fun warn(message: String, level: LogLevel = LogLevel.DEBUG) {
+ if (level != LogLevel.DEBUG || Aether.debug)
+ println("[Aether]: WARNING -> $message")
}
internal fun error(message: String) = println("[Aether]: ERR -> $message")
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/util/Util.kt b/src/main/kotlin/net/prismclient/aether/ui/util/Util.kt
new file mode 100644
index 0000000..33477f8
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/util/Util.kt
@@ -0,0 +1,31 @@
+package net.prismclient.aether.ui.util
+
+import net.prismclient.aether.ui.style.UIStyleSheet
+
+/**
+ * Type alias for a function which has a receiver of [T] and accepts, and returns
+ * nothing. The block is intended to apply properties to the receiver [T].
+ */
+typealias Block = T.() -> Unit
+
+fun T.name(styleName: String): T {
+ this.name = styleName
+ return this
+}
+
+fun FloatArray.minX(): Float = this[0]
+fun FloatArray.minY(): Float = this[1]
+fun FloatArray.maxX(): Float = this[2]
+fun FloatArray.maxY(): Float = this[3]
+
+fun FloatArray.x(): Float = this[0]
+fun FloatArray.y(): Float = this[1]
+fun FloatArray.width(): Float = this[2]
+fun FloatArray.height(): Float = this[3]
+
+fun FloatArray.red(): Float = this[0]
+fun FloatArray.green(): Float = this[1]
+fun FloatArray.blue(): Float = this[2]
+fun FloatArray.alpha(): Float = this[3]
+
+fun FloatArray.advance(): Float = this[4]
\ No newline at end of file
diff --git a/src/main/kotlin/net/prismclient/aether/ui/util/interfaces/UITriConsumer.kt b/src/main/kotlin/net/prismclient/aether/ui/util/interfaces/UITriConsumer.kt
deleted file mode 100644
index 2365d7d..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/util/interfaces/UITriConsumer.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package net.prismclient.aether.ui.util.interfaces
-
-import java.util.function.BiConsumer
-import java.util.function.Consumer
-
-/**
- * [UITriConsumer] is like a normal [Consumer] or [BiConsumer] except it accepts three
- * arguments instead of one or two.
- *
- * @author sen
- * @since 6/5/2022
- */
-fun interface UITriConsumer {
- fun accept(a: A, b: B, c: C)
-
- @JvmDefault
- fun andThen(after: UITriConsumer): UITriConsumer? {
- return UITriConsumer { a: A, b: B, c: C ->
- accept(a, b, c)
- after.accept(a, b, c)
- }
- }
-}
\ No newline at end of file
diff --git a/src/test/kotlin/Renderer.kt b/src/test/kotlin/Renderer.kt
index e736fd8..3bde412 100644
--- a/src/test/kotlin/Renderer.kt
+++ b/src/test/kotlin/Renderer.kt
@@ -30,6 +30,8 @@ object Renderer : UIRenderer {
private val ctx: Long = nvgCreate(NVG_ANTIALIAS)
private val fillColor: NVGColor = NVGColor.create()
private val strokeColor: NVGColor = NVGColor.create()
+ private val gradient1: NVGColor = NVGColor.create()
+ private val gradient2: NVGColor = NVGColor.create()
private var paint: NVGPaint? = null
private var activeColor: Int = 0
@@ -53,15 +55,8 @@ object Renderer : UIRenderer {
override fun color(color: Int) {
activeColor = color
- nvgFillColor(
- ctx, nvgRGBA(
- color.getRed().toByte(),
- color.getGreen().toByte(),
- color.getBlue().toByte(),
- color.getAlpha().toByte(),
- this.fillColor
- )
- )
+ nvgColor(color, fillColor)
+ nvgFillColor(ctx, fillColor)
}
override fun globalAlpha(alpha: Float) = nvgGlobalAlpha(ctx, alpha)
@@ -94,7 +89,7 @@ object Renderer : UIRenderer {
ctx, (width * contentScale).toInt(), (height * contentScale).toInt(), NVG_IMAGE_REPEATX or NVG_IMAGE_REPEATY
) ?: throw RuntimeException("Failed to create the framebuffer. w: $width, h: $height")
val fbo = UIContentFBO(
- framebuffer.fbo(), width * contentScale, height * contentScale, width, height, contentScale
+ framebuffer.image(), width, height, width * contentScale, height * contentScale, contentScale
)
framebuffers[fbo] = framebuffer
return fbo
@@ -114,7 +109,7 @@ object Renderer : UIRenderer {
nvgluBindFramebuffer(
ctx, framebuffers[fbo] ?: throw NullPointerException("Unable to find the framebuffer $fbo.")
)
- GL11.glViewport(0, 0, fbo.width.toInt(), fbo.height.toInt())
+ GL11.glViewport(0, 0, fbo.scaledWidth.toInt(), fbo.scaledHeight.toInt())
GL11.glClearColor(0f, 0f, 0f, 0f)
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT or GL11.GL_STENCIL_BUFFER_BIT)
}
@@ -123,28 +118,6 @@ object Renderer : UIRenderer {
nvgluBindFramebuffer(ctx, null)
}
- override fun renderFbo(
- fbo: UIContentFBO,
- x: Float,
- y: Float,
- width: Float,
- height: Float,
- topLeft: Float,
- topRight: Float,
- bottomRight: Float,
- bottomLeft: Float,
- ) {
- allocPaint()
- nvgImagePattern(ctx, x, y, width, height, 0f, framebuffers[fbo]!!.image(), 1f, paint!!)
- nvgBeginPath(ctx)
- color(-1)
- paint!!.innerColor(fillColor)
- paint!!.outerColor(fillColor)
- nvgRoundedRectVarying(ctx, x, y, width, height, topLeft, topRight, bottomRight, bottomLeft)
- nvgFillPaint(ctx, paint!!)
- nvgFill(ctx)
- }
-
override fun createImage(imageName: String, data: ByteBuffer, flags: Int): UIImageData {
val imageData = UIImageData()
val width = IntArray(1)
@@ -302,13 +275,7 @@ object Renderer : UIRenderer {
override fun strokeWidth(size: Float) = nvgStrokeWidth(ctx, size)
override fun strokeColor(color: Int) {
- nvgRGBA(
- color.getRed().toByte(),
- color.getGreen().toByte(),
- color.getBlue().toByte(),
- color.getAlpha().toByte(),
- strokeColor
- )
+ nvgColor(color, strokeColor)
nvgStrokeColor(ctx, strokeColor)
}
@@ -349,22 +316,18 @@ object Renderer : UIRenderer {
override fun linearGradient(x: Float, y: Float, x2: Float, y2: Float, startColor: Int, endColor: Int) {
allocPaint()
- val color1 = createColor(startColor)
- val color2 = createColor(endColor)
- nvgLinearGradient(ctx, x, y, x2, y2, color1, color2, paint!!)
- color1.free()
- color2.free()
+ nvgColor(startColor, gradient1)
+ nvgColor(endColor, gradient2)
+ nvgLinearGradient(ctx, x, y, x2, y2, gradient1, gradient2, paint!!)
}
override fun radialGradient(
x: Float, y: Float, innerRadius: Float, outerRadius: Float, startColor: Int, endColor: Int
) {
allocPaint()
- val color1 = createColor(startColor)
- val color2 = createColor(endColor)
- nvgRadialGradient(ctx, x, y, innerRadius, outerRadius, color1, color2, paint!!)
- color1.free()
- color2.free()
+ nvgColor(startColor, gradient1)
+ nvgColor(endColor, gradient2)
+ nvgRadialGradient(ctx, x, y, innerRadius, outerRadius, gradient1, gradient2, paint!!)
}
override fun allocPaint() {
@@ -380,8 +343,7 @@ object Renderer : UIRenderer {
override fun radToDeg(rad: Float): Float = nvgRadToDeg(rad)
- private fun createColor(color: Int): NVGColor {
- val nvgColor = NVGColor.calloc()
+ private fun nvgColor(color: Int, nvgColor: NVGColor) {
nvgRGBA(
color.getRed().toByte(),
color.getGreen().toByte(),
@@ -389,6 +351,5 @@ object Renderer : UIRenderer {
color.getAlpha().toByte(),
nvgColor
)
- return nvgColor
}
}
\ No newline at end of file
diff --git a/src/test/kotlin/Runner.kt b/src/test/kotlin/Runner.kt
index 548805a..0756640 100644
--- a/src/test/kotlin/Runner.kt
+++ b/src/test/kotlin/Runner.kt
@@ -1,7 +1,7 @@
import examples.Animations
-import examples.AutoLayouts
import examples.Default
import examples.PathRendering
+
import net.prismclient.aether.ui.Aether
import net.prismclient.aether.ui.Aether.Properties.updateMouse
import net.prismclient.aether.ui.util.input.UIKey
@@ -84,9 +84,11 @@ object Runner {
}
glfwSetFramebufferSizeCallback(window) { _: Long, width: Int, height: Int ->
- framebufferWidth = width
- framebufferHeight = height
- core!!.update(width / contentScaleX, height / contentScaleY, max(contentScaleX, contentScaleY))
+ if (width > 0 && height > 0) {
+ framebufferWidth = width
+ framebufferHeight = height
+ core!!.update(width / contentScaleX, height / contentScaleY, max(contentScaleX, contentScaleY))
+ }
}
glfwSetKeyCallback(window) { _: Long, keyCode: Int, scanCode: Int, action: Int, _: Int ->
@@ -190,11 +192,10 @@ object Runner {
Aether.displayScreen(
when (args[0]) {
"Animations" -> Animations()
- "AutoLayouts" -> AutoLayouts()
"PathRendering" -> PathRendering()
else -> Default()
}
)
- }
+ } else Aether.displayScreen(Default())
}
}
\ No newline at end of file
diff --git a/src/test/kotlin/examples/Animations.kt b/src/test/kotlin/examples/Animations.kt
index 1eb9896..4262ebd 100644
--- a/src/test/kotlin/examples/Animations.kt
+++ b/src/test/kotlin/examples/Animations.kt
@@ -22,68 +22,15 @@ import net.prismclient.aether.ui.util.extensions.px
class Animations : UIScreen {
override fun build() {
create {
- include(Generic())
+ button("Hello").style {
- UIStyleSheet("button").style {
- align(UIAlignment.CENTER)
- size(400, 40)
- background(colorOf(asRGBA(0f, 0f, 0f, 0.3f)), radiusOf(0f)) {
- border {
- borderWidth = px(10)
- borderColor = colorOf(asRGBA(255, 0, 0))
- }
- }
- font("Montserrat", px(16f), colorOf(-1), left or top)
}
- animationOf("someAnimation", UIStyleSheet()) {
- UIQuart(1000L) repeat {
- kf {}
- kf {
- position(50, 0)
- }
- kf {
- position(0, 0)
- }
- kf {
- position(0, 50)
- }
- kf {
- position(50, 50)
- }
- }
- onCompletion {
- UIProvider.dispatchAnimation("someAnimation", it.component)
- }
- }
-
- animationOf("test", UIStyleSheet()) {
- kf {}
- UILinear(1000L) to {
- background {
-// backgroundColor = colorOf(asRGBA(0, 0, 255))
- border {
- borderColor = colorOf(asRGBA(0f, 1f, 0f))
- borderWidth = px(1)
- }
- }
- }
- }
+ verticalList {
- animationOf("move", UIStyleSheet()) {
- kf {}
- kf {
- x = px(50)
- }
- }
-
- val l = label("Some text", "button").onMousePressed {
- UIProvider.dispatchAnimation("test", it)
- println("pressed")
}.style {
- x = px(-50)
+
}
- UIProvider.dispatchAnimation("move", l)
}
}
}
\ No newline at end of file
diff --git a/src/test/kotlin/examples/AutoLayouts.kt b/src/test/kotlin/examples/AutoLayouts.kt
deleted file mode 100644
index 46584d1..0000000
--- a/src/test/kotlin/examples/AutoLayouts.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-package examples
-
-import examples.deps.Generic
-import net.prismclient.aether.ui.component.type.layout.UIFrame
-import net.prismclient.aether.ui.component.type.layout.auto.UIAutoLayout
-import net.prismclient.aether.ui.component.type.layout.list.UIListLayout
-import net.prismclient.aether.ui.component.type.layout.styles.UIContainerSheet
-import net.prismclient.aether.ui.component.util.enums.UIAlignment
-import net.prismclient.aether.ui.screen.UIScreen
-import net.prismclient.aether.ui.util.*
-import net.prismclient.aether.ui.util.extensions.asRGBA
-import net.prismclient.aether.ui.util.extensions.colorOf
-import net.prismclient.aether.ui.util.extensions.px
-
-/**
- * Auto Layouts are a neat feature which is designed to mimic the auto layout
- * of Figma. Here is an example screen which utilizes these auto layouts to create
- * a list of buttons.
- *
- * Auto Layouts, in layman terms, are a more advanced version of list layouts. It
- * gives spacing and padding properties as well as better alignment support which
- * list layouts and other layouts lack. It is one of the few [UIFrame] which are
- * designed to be used in mass amounts.
- *
- * @author sen
- * @since 7/3/2022
- * @see UIAutoLayout For more information about the specifics.
- */
-class AutoLayouts : UIScreen {
- override fun build() {
- // Start a `create` block as usual to get a UIComponentDSL block
- create {
- // Include the UIDependable, so we can use the styles from it
- include(Generic())
-
- // There are two (suggested) ways to declare Auto Layouts. If intend
- // to use auto layout, once (uncommon), you can simply declare the
- // layout using `UIComponentDSL.component()
-
- /*
- component(UIAutoLayout(UIListLayout.ListDirection.Horizontal, null)) {
- Define properties of the layout, and components...
- }
- */
-
- // Generally you will need to reuse the same layout so instead
- // define the layout as a variable instead.
-
- // ListDirection -> Vertical or Horizontal
- val layout =
- UIAutoLayout(UIListLayout.ListDirection.Horizontal, null).style(UIContainerSheet(("someStyle"))) {
- // Declare the style inline, as we only use it once
- background(colorOf(asRGBA(59, 145, 255)), radiusOf(9f))
- }
-
- // Create a DSL block using this shorthand
- blockFrom(layout) {
- // Let's say we want an icon on the left, and text on the right
- // which is all centered to the middle component. Oh, and let's
- // give some padding to the edges, and let it automatically size itself
- // (We declare the components below)
- componentAlignment = UIAlignment.CENTER // Align the stuff to the center
-
- // Resize to the size of the layout. (Not) Fun Fact: Setting of the size
- // of the component ensures that this it at least that value.
- horizontalResizing = UIAutoLayout.ResizingMode.Hug
- verticalResizing = UIAutoLayout.ResizingMode.Hug
-
- // Space the components by 10px...
- componentSpacing = px(10)
-
- // Set the padding to 10
- // Alternatively paddingOf(Unit/Number, Unit/Number, Unit/Number, Unit/Number)
- layoutPadding = paddingOf(10)
- }
-
- // Let's make a vertical list, so we can better see what is happening!
- // This simply stacks the components vertically.
- list(UIListLayout.ListDirection.Vertical, UIListLayout.ListOrder.Forward) {
- componentSpacing = px(10) // With a spacing of 10px
-
- autoLayout(layout) {
- // Define your components here!
- image("ui", "icon24x")
- text("User Interface", "generic-font")
- }.style { // Create a style block so we can add a 25px margin at the top
- margin {
- marginTop = px(25)
- }
- }
-
- // Add another
- autoLayout(layout) {
- image("ui", "icon24x")
- text("Some more user interface!!!!", "generic-font")
- }
-
- // Sure, why not another!
- autoLayout(layout) {
- text("Reversed icons???", "generic-font")
- image("ui", "icon24x")
- }
- }.style(UIContainerSheet("autoLayoutList")) {
- // Make the size of this vertical layout
- size(400, 500)
- // There is no background, as the background was not defined
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/test/kotlin/examples/Default.kt b/src/test/kotlin/examples/Default.kt
index 1c3612a..3e8d79b 100644
--- a/src/test/kotlin/examples/Default.kt
+++ b/src/test/kotlin/examples/Default.kt
@@ -1,62 +1,23 @@
package examples
+import examples.deps.Generic
import net.prismclient.aether.ui.animation.ease.impl.UIQuart
-import net.prismclient.aether.ui.component.type.layout.auto.UIAutoLayout
-import net.prismclient.aether.ui.component.type.layout.list.UIListLayout
-import net.prismclient.aether.ui.component.type.layout.styles.UIContainerSheet
+import net.prismclient.aether.ui.component.type.UILabel
+import net.prismclient.aether.ui.component.type.image.UIImage
+import net.prismclient.aether.ui.component.type.image.UIImageSheet
+import net.prismclient.aether.ui.component.type.input.button.UIButton
+import net.prismclient.aether.ui.component.type.layout.UIAutoLayout
+import net.prismclient.aether.ui.component.type.layout.UIListLayout
+import net.prismclient.aether.ui.component.type.layout.UIContainerSheet
import net.prismclient.aether.ui.component.util.enums.UIAlignment
+import net.prismclient.aether.ui.dsl.UIAssetDSL
import net.prismclient.aether.ui.renderer.UIProvider
+import net.prismclient.aether.ui.renderer.impl.font.UIFont
import net.prismclient.aether.ui.screen.UIScreen
import net.prismclient.aether.ui.style.UIStyleSheet
import net.prismclient.aether.ui.util.*
import net.prismclient.aether.ui.util.extensions.*
class Default : UIScreen {
- override fun build() {
- create {
- styleOf(UIStyleSheet("btn")) {
- size(200, 200)
- background(colorOf(1f, 0f, 0f, 0.3f), radiusOf(25f))
- }
-
- val list = list(UIListLayout.ListDirection.Vertical) {
- button("Some text", "btn")
- button("Some text", "btn")
- button("Some text", "btn")
- button("Some text", "btn")
- }.style(UIContainerSheet("")) {
- control(UIAlignment.CENTER)
- size(500, 500)
- background(colorOf(-1), radiusOf(8f))
- useFBO = true
- verticalScrollbar {
- background {
- backgroundColor = colorOf(0f, 0f, 0f ,0.3f)
- }
- y = rel(0.1)
- x = rel(1) - px(10)
- height = rel(0.8)
- width = px(5)
- color = colorOf(48, 48, 48)
- radius = radiusOf(2.5f)
- }
- }
-
- animationOf("someAnimation", UIContainerSheet()) {
- kf {
- x = px(0)
- }
- UIQuart(1000L) to {
- x = px(200)
- }
- UIQuart(1000L) to {
- x = px(0)
- }
- onCompletion {
- UIProvider.dispatchAnimation("someAnimation", it.component)
- }
- }
-// UIProvider.dispatchAnimation("someAnimation", list)
- }
- }
+ override fun build() {}
}
\ No newline at end of file
diff --git a/src/test/kotlin/examples/components/Chart.kt b/src/test/kotlin/examples/components/Chart.kt
index e115d40..8e32fc3 100644
--- a/src/test/kotlin/examples/components/Chart.kt
+++ b/src/test/kotlin/examples/components/Chart.kt
@@ -6,8 +6,12 @@ import net.prismclient.aether.ui.style.UIStyleSheet
/**
* An example component which draws a chart.
*/
-class Chart(style: String?) : UIComponent(style) {
+class Chart() : UIComponent() {
override fun renderComponent() {
}
+
+ override fun createsStyle(): UIStyleSheet {
+ TODO("Not yet implemented")
+ }
}
\ No newline at end of file
diff --git a/src/test/kotlin/examples/deps/Generic.kt b/src/test/kotlin/examples/deps/Generic.kt
index eb8fc8c..831b3ed 100644
--- a/src/test/kotlin/examples/deps/Generic.kt
+++ b/src/test/kotlin/examples/deps/Generic.kt
@@ -9,7 +9,7 @@ import net.prismclient.aether.ui.util.extensions.colorOf
import net.prismclient.aether.ui.util.extensions.px
import net.prismclient.aether.ui.util.interfaces.UIDependable
import net.prismclient.aether.ui.util.left
-import net.prismclient.aether.ui.util.styleOf
+import net.prismclient.aether.ui.util.style
import net.prismclient.aether.ui.util.top
/**
@@ -41,18 +41,17 @@ class Generic : UIDependable {
// Load some assets into memory. Aether is intended to support mainly JPEG, PNG, and SVG.
// Either explicitly state the type
// Reference the image with the name "ui"
- UIAssetDSL.svg("ui", "/prism/icons/navbar/ui.svg")
+ //UIAssetDSL.svg("ui", "/prism/icons/navbar/ui.svg")
// loadImage()
// or let Aether figure it out
// assumeLoadImage()
// A 24x24 icon
- styleOf(UIImageSheet("icon24x")) {
+ UIImageSheet().style("icon24x") {
size(24, 24)
}
- // A example font
- styleOf(UIStyleSheet("generic-font")) {
+ UIStyleSheet().style("generic-font") {
// FontFamily to Montserrat
// FontSize -> 16f
// FontColor -> -1 = asRGBA(255, 255, 255) (aka white)