diff --git a/src/main/kotlin/net/prismclient/aether/ui/Aether.kt b/src/main/kotlin/net/prismclient/aether/ui/Aether.kt
index a9a0fba..bc7ee23 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.UIContainer
-import net.prismclient.aether.ui.component.type.layout.UIContainerSheet
+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.event.input.UIMouseEvent
import net.prismclient.aether.ui.renderer.UIProvider
import net.prismclient.aether.ui.renderer.UIRenderer
@@ -33,10 +33,9 @@ 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) {
@@ -109,14 +108,12 @@ 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()
}
}
}
@@ -126,7 +123,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].
*/
- open fun mouseMoved(mouseX: Float, mouseY: Float) {
+ 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)
@@ -142,10 +139,10 @@ open class Aether(renderer: UIRenderer) {
*
* @see mouseScrolled
*/
- open fun mouseChanged(mouseButton: Int, isRelease: Boolean) {
+ fun mouseChanged(mouseButton: Int, isRelease: Boolean) {
if (isRelease) {
mouseReleasedListeners?.forEach { it.value.run() }
- components?.forEach { it.mouseReleased(it.getMouseX(), it.getMouseY()) }
+ components?.forEach { it.mouseReleased(mouseX, mouseY) }
return
}
@@ -185,7 +182,7 @@ open class Aether(renderer: UIRenderer) {
return if (component != null) {
component.focus()
- component.mousePressed(UIMouseEvent(component.getMouseX(), component.getMouseY(), mouseButton, clickCount))
+ component.mousePressed(UIMouseEvent(mouseX, mouseY, mouseButton, clickCount))
true
} else false
}
@@ -214,7 +211,7 @@ open class Aether(renderer: UIRenderer) {
i++
}
c?.focus()
- c?.mousePressed(UIMouseEvent(c.getMouseX(), c.getMouseY(), mouseButton, clickCount))
+ c?.mousePressed(UIMouseEvent(mouseX, mouseY, mouseButton, clickCount))
}
/**
@@ -223,7 +220,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...
*/
- open fun keyPressed(character: Char) {
+ fun keyPressed(character: Char) {
keyPressListeners?.forEach { it.value.accept(character) }
(focusedComponent as? UIComponent<*>)?.keyPressed(character)
}
@@ -233,7 +230,6 @@ 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) }
@@ -245,20 +241,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).
@@ -269,36 +265,42 @@ 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
@@ -306,42 +308,49 @@ 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
@@ -493,7 +502,13 @@ open class Aether(renderer: UIRenderer) {
* Focuses the component. Please use [UIComponent.focus] instead.
*/
@JvmStatic
- fun focus(component: T) where T : UIComponent<*>, T : UIFocusable {
+ 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.")
+ }
focusedComponent = component
}
@@ -533,7 +548,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
deleted file mode 100644
index 9218a08..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/Timings.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-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
new file mode 100644
index 0000000..0751f94
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/callback/UICoreCallback.kt
@@ -0,0 +1,17 @@
+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 c6b0a79..c721c96 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.UIFrameSheet
+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.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 {
+abstract class UIComponent(style: String?) {
/**
* The style of the component.
*/
@@ -193,6 +193,10 @@ abstract class UIComponent {
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
@@ -204,7 +208,8 @@ abstract class UIComponent {
* @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.
@@ -241,10 +246,8 @@ abstract class UIComponent {
* might request for this method to be invoked.
*/
open fun update() {
- if (!this::style.isInitialized) {
- println("creating style...")
- style = createsStyle()
- }
+ if (!this::style.isInitialized)
+ throw UninitializedStyleSheetException(this)
calculateBounds()
// Update the size, then the anchor, and then the position
@@ -333,7 +336,8 @@ abstract class UIComponent {
if (animations != null) {
animations!!.forEach { it.value.update() }
animations!!.entries.removeIf { it.value.isCompleted }
- if (animations!!.isEmpty()) animations = null
+ if (animations!!.isEmpty())
+ animations = null
}
}
@@ -361,17 +365,7 @@ abstract class UIComponent {
*/
abstract fun renderComponent()
- /**
- * 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 -- //
+ /** Input **/
/**
* Invoked when the mouse moves
@@ -437,7 +431,7 @@ abstract class UIComponent {
mouseScrollListeners?.forEach { it.value.accept(this, scrollAmount) }
}
- // -- Event -- //
+ /** Event **/
/**
* Invoked once on the initialization of the component.
@@ -607,28 +601,36 @@ abstract class UIComponent {
open fun getMouseY(): Float = Aether.mouseY - getParentYOffset()
/**
- * 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.
+ * Returns the offset of the parent on the x-axis. It incorporates for [UIFrame] and [UIContainer] scroll offsets.
*/
- 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
+ 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
} else 0f
- } else 0f
+ }
/**
- * 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.
+ * Returns the offset of the parent on the y-axis. It incorporates for [UIFrame] and [UIContainer] scrolling offsets
*/
- 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
+ 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
} else 0f
- } else 0f
+ }
/**
* Shorthand for calculating the x or width of this component
@@ -656,7 +658,7 @@ abstract class UIComponent {
open fun focus() {
if (this is UIFocusable) {
Aether.focus(this)
- focusListeners?.forEach { it.value.accept(this, true) }
+ focusListeners?.forEach { it.value.accept(this as UIComponent, 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 b0dd7de..0d224f0 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,7 +2,6 @@ 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
@@ -58,20 +57,8 @@ 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 fac02bd..dfa4489 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,12 +7,10 @@ import net.prismclient.aether.ui.style.UIStyleSheet
* [UILabel] is a component which draws a label, or string on screen.
*
* @author sen
- * @since 1.0
+ * @since 5/15/2022
*/
-class UILabel(var text: String) : UIComponent() {
+class UILabel(var text: String, style: String?) : UIComponent(style) {
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
new file mode 100644
index 0000000..699c807
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/color/UIColorCursor.kt
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 0000000..5c056bb
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/color/UIColorPicker.kt
@@ -0,0 +1,10 @@
+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
new file mode 100644
index 0000000..7b82376
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/color/UIColorSwatchSheet.kt
@@ -0,0 +1,16 @@
+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 3421eda..85131de 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,21 +4,16 @@ 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 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.
+ * [UIImage] is the default implementation for rendering images on screen. It accepts`
+ * an image
*
* @author sen
- * @since 1.0
+ * @since 5/20/2022
*/
-class UIImage(name: String) : UIComponent() {
+class UIImage(name: String, style: String?) : UIComponent(style) {
var image: String = name
set(value) {
field = value
@@ -29,9 +24,12 @@ class UIImage(name: String) : UIComponent() {
/**
* Loads am image or svg from the specified location with a given name
*/
- constructor(name: String, location: String) : this(
- name
- ) { UIAssetDSL.image(name, location) }
+ constructor(name: String, location: String, style: String?) : this(
+ name,
+ style
+ ) {
+ UIAssetDSL.image(name, location)
+ }
init {
activeImage = UIProvider.getImage(image)
@@ -39,7 +37,7 @@ class UIImage(name: String) : UIComponent() {
override fun renderComponent() {
renderer {
- color(style.imageColor?.rgba ?: -1)
+ color(style.imageColor)
renderImage(
image, x, y, width, height,
style.imageRadius?.topLeft ?: 0f,
@@ -49,24 +47,4 @@ class UIImage(name: String) : UIComponent() {
)
}
}
-
- 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
new file mode 100644
index 0000000..e63bada
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/image/UIImageSheet.kt
@@ -0,0 +1,28 @@
+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 0c0082a..0d522f9 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,15 +4,14 @@ import net.prismclient.aether.ui.component.UIComponent
import net.prismclient.aether.ui.style.UIStyleSheet
/**
- * [UIButton] is the default implementation of [UIComponent]. It renders the given text to the font.
+ * [UIButton] is a simple class, which is used to create a button.
*
* @author sen
- * @since 1.0
+ * @since 5/16/2022
+ * @param T The stylesheet (used for inheritance) leave as UIStyleSheet.
*/
-open class UIButton(open var text: String) : UIComponent() {
+open class UIButton(open var text: String, style: String?) : UIComponent(style) {
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
new file mode 100644
index 0000000..61193d6
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UICheckbox.kt
@@ -0,0 +1,39 @@
+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
new file mode 100644
index 0000000..c09b6b0
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UIIconButton.kt
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 0000000..a8f64f4
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UIImageButton.kt
@@ -0,0 +1,23 @@
+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
new file mode 100644
index 0000000..00d91cc
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/button/UISelectableButton.kt
@@ -0,0 +1,37 @@
+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 40dfe84..b1737bf 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,9 +2,6 @@ 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
@@ -20,15 +17,16 @@ import kotlin.math.roundToInt
* @see UISliderShape
*/
open class UISlider(
- value: Double, var range: ClosedFloatingPointRange, var step: Double
-) : UIComponent() {
+ value: Double, var range: ClosedFloatingPointRange, var step: Double, style: String?
+) : UIComponent(style) {
/**
* 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) }
@@ -112,20 +110,4 @@ 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
new file mode 100644
index 0000000..a3efd8e
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/slider/UISliderSheet.kt
@@ -0,0 +1,28 @@
+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 020a740..1f85f76 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,19 +1,12 @@
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
@@ -27,11 +20,12 @@ import java.util.function.Consumer
* length of the overall text.
*
* @author sen
- * @since 1.0
- * @see UITextField.Filters pre-made text filters.
+ * @since 6/6/2022
+ * @see UITextField.filter Pre-made text filters.
*/
-open class UITextField(text: String, var placeholder: String? = null, var filter: TextFilter) : UIComponent(), UIFocusable {
- var text: String = text
+open class UITextField(text: String, var placeholder: String? = null, var filter: TextFilter, style: String?) :
+ UIButton(text, style), UIFocusable {
+ override var text: String = text
set(value) {
field = value
textChangedListener?.forEach { it.value.accept(this) }
@@ -52,7 +46,7 @@ open class UITextField(text: String, var placeholder: String? = null, var filter
/** Blink **/
protected var timeSinceLastBlink: Long = 0L
- protected var blink: Boolean = true
+ protected var blink: Boolean = false
init {
Aether.onModifierKeyChange(this.toString()) { key, value ->
@@ -117,7 +111,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 (style.blinkRate > 0 && (timeSinceLastBlink + style.blinkRate <= System.currentTimeMillis())) {
+ if (timeSinceLastBlink + style.blinkRate <= System.currentTimeMillis()) {
blink = !blink
timeSinceLastBlink = System.currentTimeMillis()
}
@@ -237,8 +231,6 @@ 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,
@@ -267,38 +259,4 @@ 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
new file mode 100644
index 0000000..0ae3407
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/input/textfield/UITextFieldSheet.kt
@@ -0,0 +1,48 @@
+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/UIContainer.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIContainer.kt
deleted file mode 100644
index 5459dd0..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIContainer.kt
+++ /dev/null
@@ -1,275 +0,0 @@
-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 6e30a6e..73d348d 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,13 +2,12 @@ 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
/**
@@ -28,7 +27,7 @@ import net.prismclient.aether.ui.util.warn
* @author sen
* @since 1.0
*/
-abstract class UIFrame : UIComponent(), UIFocusable {
+abstract class UIFrame(style: String?) : UIComponent(style), UIFocusable {
/**
* The components of this frame.
*/
@@ -60,16 +59,7 @@ abstract class UIFrame : UIComponent(), UIFocusable {
* frame has been updated, but prior to the first render.
*/
open fun updateFBO() {
- // 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)
- }
- }
+ if (style.useFBO) fbo = Aether.renderer.createFBO(relWidth, relHeight)
}
/**
@@ -103,10 +93,11 @@ abstract class UIFrame : UIComponent(), UIFocusable {
components.forEach(UIComponent<*>::render)
}
}
+ requiresUpdate = false
}
- requiresUpdate = false
}
+
override fun render() {
updateAnimation()
style.background?.render()
@@ -115,12 +106,17 @@ abstract class UIFrame : UIComponent(), UIFocusable {
override fun renderComponent() {
if (style.useFBO) {
- renderer {
- path {
- imagePattern(fbo!!.imagePattern, relX, relY, relWidth, relHeight, 0f, 1f)
- rect(relX, relY, relWidth, relHeight)
- }.fillPaint()
- }
+ 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 {
if (style.clipContent) UIRendererDSL.scissor(relX, relY, relWidth, relHeight) {
components.forEach(UIComponent<*>::render)
@@ -166,53 +162,4 @@ abstract class UIFrame : UIComponent(), UIFocusable {
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
deleted file mode 100644
index 2c39452..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIGridLayout.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-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/UIAutoLayout.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/auto/UIAutoLayout.kt
similarity index 84%
rename from src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIAutoLayout.kt
rename to src/main/kotlin/net/prismclient/aether/ui/component/type/layout/auto/UIAutoLayout.kt
index a731d21..f29b036 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIAutoLayout.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/auto/UIAutoLayout.kt
@@ -1,12 +1,13 @@
-package net.prismclient.aether.ui.component.type.layout
+package net.prismclient.aether.ui.component.type.layout.auto
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
@@ -26,8 +27,10 @@ import net.prismclient.aether.ui.util.name
* @author sen
* @since 1.1
*/
-class UIAutoLayout @JvmOverloads constructor(listDirection: ListDirection = ListDirection.Horizontal) :
- UIListLayout(listDirection, ListOrder.Forward), UICopy {
+class UIAutoLayout @JvmOverloads constructor(
+ listDirection: ListDirection = ListDirection.Horizontal,
+ style: String?
+) : UIListLayout(listDirection, ListOrder.Forward, style), 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.
@@ -64,14 +67,6 @@ class UIAutoLayout @JvmOverloads constructor(listDirection: ListDirection = List
*/
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
@@ -87,27 +82,28 @@ class UIAutoLayout @JvmOverloads constructor(listDirection: ListDirection = List
var h = 0f
for (i in components.indices) {
- val component = components[i]
if (horizontalResizing == ResizingMode.Hug) {
w = if (listDirection == ListDirection.Horizontal) {
- w + component.relWidth + component.marginLeft + component.marginRight + if (i < components.size - 1) spacing else 0f
+ w + components[i].relWidth + if (i < components.size - 1) spacing else 0f
} else {
- (component.relWidth + component.marginLeft + component.marginRight).coerceAtLeast(w)
+ components[i].relWidth.coerceAtLeast(w)
}
}
if (verticalResizing == ResizingMode.Hug) {
h = if (listDirection == ListDirection.Vertical) {
- h + component.relHeight + component.marginTop + component.marginBottom + if (i < components.size - 1) spacing else 0f
+ h + components[i].relHeight + if (i < components.size - 1) spacing else 0f
} else {
- (component.relHeight + component.marginTop + component.marginBottom).coerceAtLeast(h)
+ components[i].relHeight.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()
@@ -144,7 +140,7 @@ class UIAutoLayout @JvmOverloads constructor(listDirection: ListDirection = List
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) {
@@ -152,7 +148,7 @@ class UIAutoLayout @JvmOverloads constructor(listDirection: ListDirection = List
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
}
@@ -168,7 +164,7 @@ class UIAutoLayout @JvmOverloads constructor(listDirection: ListDirection = List
/**
* Copy the properties of this layout to a new one (excluding components).
*/
- override fun copy(): UIAutoLayout = UIAutoLayout(listDirection).also {
+ override fun copy(): UIAutoLayout = UIAutoLayout(listDirection, style.name).also {
// UIAutoLayout properties
it.horizontalResizing = horizontalResizing
it.verticalResizing = verticalResizing
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
new file mode 100644
index 0000000..7197cc7
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/auto/UIAutoLayoutSheet.kt
@@ -0,0 +1,16 @@
+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
new file mode 100644
index 0000000..315d789
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/container/UIContainer.kt
@@ -0,0 +1,173 @@
+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
new file mode 100644
index 0000000..624d639
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/grid/UIGridLayout.kt
@@ -0,0 +1,155 @@
+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/UIListLayout.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/list/UIListLayout.kt
similarity index 88%
rename from src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIListLayout.kt
rename to src/main/kotlin/net/prismclient/aether/ui/component/type/layout/list/UIListLayout.kt
index 1b6a63f..692b393 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UIListLayout.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/list/UIListLayout.kt
@@ -1,7 +1,9 @@
-package net.prismclient.aether.ui.component.type.layout
+package net.prismclient.aether.ui.component.type.layout.list
-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.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.unit.UIUnit
/**
@@ -14,7 +16,8 @@ import net.prismclient.aether.ui.unit.UIUnit
open class UIListLayout @JvmOverloads constructor(
var listDirection: ListDirection = ListDirection.Vertical,
var listOrder: ListOrder = Forward,
-) : UIContainer() {
+ style: String?
+) : UIContainer(style) {
/**
* The spacing between each component in the layout.
*/
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
new file mode 100644
index 0000000..210ac5e
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/styles/UIContainerSheet.kt
@@ -0,0 +1,102 @@
+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
new file mode 100644
index 0000000..ff31030
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/styles/UIFrameSheet.kt
@@ -0,0 +1,53 @@
+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/layout/UITab.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/tab/UITab.kt
similarity index 53%
rename from src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UITab.kt
rename to src/main/kotlin/net/prismclient/aether/ui/component/type/layout/tab/UITab.kt
index c47a21e..2b1a715 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/UITab.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/layout/tab/UITab.kt
@@ -1,3 +1,3 @@
-package net.prismclient.aether.ui.component.type.layout
+package net.prismclient.aether.ui.component.type.layout.tab
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/other/UIProgress.kt b/src/main/kotlin/net/prismclient/aether/ui/component/type/other/progress/UIProgress.kt
similarity index 58%
rename from src/main/kotlin/net/prismclient/aether/ui/component/type/other/UIProgress.kt
rename to src/main/kotlin/net/prismclient/aether/ui/component/type/other/progress/UIProgress.kt
index 69bd0d2..583f357 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/component/type/other/UIProgress.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/other/progress/UIProgress.kt
@@ -1,10 +1,7 @@
-package net.prismclient.aether.ui.component.type.other
+package net.prismclient.aether.ui.component.type.other.progress
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
@@ -15,27 +12,14 @@ import net.prismclient.aether.ui.util.name
* To configure the color see [UIProgressSheet.progressColor].
*
* @author sen
- * @since 1.0
+ * @since 6/23/2022
*/
-class UIProgress @JvmOverloads constructor(var progress: Float = 0f) : UIComponent() {
+class UIProgress @JvmOverloads constructor(var progress: Float = 0f, style: String?) :
+ UIComponent(style) {
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
new file mode 100644
index 0000000..44b73e9
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/type/other/progress/UIProgressSheet.kt
@@ -0,0 +1,21 @@
+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 12aaeb6..62f9ea8 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
new file mode 100644
index 0000000..d1037de
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/component/util/enums/UIOverflow.kt
@@ -0,0 +1,8 @@
+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 83125fe..73a338b 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 {
- error("Failed to bulk load [$folderLocation] as the file was null.")
+ warn("Failed to bulk load [$folderLocation] as the file was null.")
return 0
}
return internalBulkLoad(File(file.toURI()), deep, appendedString, imageFlags, svgScale).also {
- inform("Bulk loaded $it files.")
+ warn("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 ded9ef4..f4649bd 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/dsl/UIComponentDSL.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/dsl/UIComponentDSL.kt
@@ -7,14 +7,15 @@ 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.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.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.dsl.UIComponentDSL.activeStyle
-import net.prismclient.aether.ui.util.Block
+import net.prismclient.aether.ui.style.UIStyleSheet
import net.prismclient.aether.ui.util.interfaces.UIDependable
import java.util.*
@@ -135,9 +136,8 @@ object UIComponentDSL {
*
* @return T The component
*/
- inline fun > component(component: T, style: String?, block: Block): T {
+ inline fun > component(component: T, block: T.() -> Unit): 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: Block) {
+ inline fun , T : UIComponent<*>> controller(controller: C, block: C.() -> Unit) {
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: Block): UIComponentDSL {
+ inline fun ignore(block: UIComponentDSL.() -> Unit): 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: Block) {
+ inline fun style(styleName: String, block: UIComponentDSL.() -> Unit) {
// 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,7 +187,8 @@ 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()
@@ -206,8 +207,8 @@ object UIComponentDSL {
* @see label
*/
@JvmOverloads
- inline fun text(text: String, style: String? = activeStyle, block: Block = {}) =
- component(UILabel(text), style, block)
+ inline fun text(text: String, style: String? = activeStyle, block: UILabel.() -> Unit = {}) =
+ component(UILabel(text, style), block)
/**
* An alternative to [text]. Creates a [UILabel] with the provided [text].
@@ -215,14 +216,27 @@ object UIComponentDSL {
* @see text
*/
@JvmOverloads
- inline fun label(text: String, style: String? = activeStyle, block: Block = {}) =
- component(UILabel(text), style, block)
+ inline fun label(text: String, style: String? = activeStyle, block: UILabel.() -> Unit = {}) =
+ component(UILabel(text, style), block)
/**
* Creates a [UIButton] with the provided [text], like a label.
*/
- inline fun button(text: String, style: String? = activeStyle, block: Block = {}) =
- component(UIButton(text), style, block)
+ 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)
/**
* Creates a [UISlider] with the given [value] which stays within the [range] and steps by the
@@ -234,22 +248,22 @@ object UIComponentDSL {
range: ClosedFloatingPointRange,
step: Number,
style: String? = activeStyle,
- block: Block = {}
- ) = component(UISlider(value.toDouble(), range, step.toDouble()), style, block)
+ block: UISlider.() -> Unit = {}
+ ) = 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: Block = {}) =
- component(UIImage(imageName), style, block)
+ inline fun image(imageName: String, style: String? = activeStyle, block: UIImage.() -> Unit = {}) =
+ 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: Block> = {}) =
- component(UIContainer(), style, block)
+ inline fun container(style: String? = activeStyle, block: UIContainer.() -> Unit = {}) =
+ component(UIContainer(style), block)
/**
* Creates a [UIListLayout] with the given [listDirection], which defines the direction that it
@@ -261,50 +275,14 @@ object UIComponentDSL {
listDirection: UIListLayout.ListDirection,
listOrder: UIListLayout.ListOrder = UIListLayout.ListOrder.Forward,
style: String? = activeStyle,
- 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)
+ block: UIListLayout.() -> Unit = {}
+ ) = component(UIListLayout(listDirection, listOrder, style), block)
/**
* Creates a copy of the given layout and creates a normal block of [UIAutoLayout] where
* components can be defined.
*/
- @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)
+ inline fun autoLayout(layout: UIAutoLayout, block: UIAutoLayout.() -> Unit) = component(layout.copy(), block)
/**
* Creates a [UISelectableController] which is a controller that has a single selected
@@ -313,7 +291,6 @@ object UIComponentDSL {
* @see controller
* @see UISelectableController
*/
- @JvmOverloads
- inline fun > selectable(block: Block> = {}) =
+ inline fun > selectable(block: UISelectableController.() -> Unit) =
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 3d33bfb..b49d5aa 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/dsl/UIPathDSL.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/dsl/UIPathDSL.kt
@@ -2,8 +2,6 @@ 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.
@@ -151,21 +149,6 @@ 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.
*/
@@ -233,8 +216,8 @@ object UIPathDSL {
* @see See NanoVG Composite paths
*/
@JvmStatic
- inline fun hole(block: Block): UIPathDSL {
- UIPathDSL.block()
+ inline fun hole(block: UIPathDSL.() -> Unit): 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 cf0f1c0..926acc8 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/dsl/UIRendererDSL.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/dsl/UIRendererDSL.kt
@@ -8,7 +8,8 @@ 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.*
+import net.prismclient.aether.ui.util.Block
+import net.prismclient.aether.ui.util.UIColor
/**
* [UIRendererDSL] wraps the [UIRenderer] class to minimize the amount of calls
@@ -76,7 +77,7 @@ object UIRendererDSL {
*/
@JvmStatic
fun color(color: Int) {
- activeColor = color
+ UIRendererDSL.activeColor = color
renderer.color(color)
}
@@ -153,13 +154,13 @@ object UIRendererDSL {
* @see fontBounds
*/
@JvmStatic
- fun fontWidth(): Float = fontBounds().maxX() - fontBounds().minX()
+ fun fontWidth(): Float = fontBounds()[2] - fontBounds()[0]
/**
* Returns the height of the most recent text render call.
*/
@JvmStatic
- fun fontHeight(): Float = fontBounds().maxY() - fontBounds().minY()
+ fun fontHeight(): Float = fontBounds()[3] - fontBounds()[1]
/**
* Returns the ascender of the most recent text render call.
@@ -175,9 +176,6 @@ 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,
@@ -401,7 +399,7 @@ object UIRendererDSL {
@JvmStatic
inline fun UIContentFBO.renderToFramebuffer(block: Block): UIRendererDSL {
renderer.bindFBO(this)
- beginFrame(this.width, this.height, this.contentScale)
+ beginFrame(this.scaledWidth, this.scaledHeight, 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 5727146..1b94e72 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/renderer/UIProvider.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/renderer/UIProvider.kt
@@ -48,9 +48,10 @@ object UIProvider {
* Returns the name of the images given the [UIImageData]
*/
fun getImageName(imageData: UIImageData): String? {
- for ((name, image) in images) {
- if (image == imageData)
- return name
+ for (image in images) {
+ if (image.value == imageData) {
+ return image.key
+ }
}
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 cc7ac79..a7ba8af 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/renderer/UIRenderer.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/renderer/UIRenderer.kt
@@ -1,6 +1,7 @@
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
@@ -138,6 +139,18 @@ 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 6097eb6..985502f 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,19 +2,18 @@ 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 41.0
+ * @since 4/26/2022
*/
class UIGradientBackground : UIBackground() {
- var gradientStartColor: UIColor? = null
- var gradientEndColor: UIColor? = null
+ var gradientStartColor = 0
+ var gradientEndColor = 0
var gradientX: UIUnit? = null
var gradientY: UIUnit? = null
var gradientWidth: UIUnit? = null
@@ -45,18 +44,15 @@ class UIGradientBackground : UIBackground() {
component?.relHeight ?: 0f,
true
)
- gradientWidthCache = gradientXCache +
+ gradientWidthCache =
calculate(gradientWidth, component, component?.relWidth ?: 0f, component?.relHeight ?: 0f, false)
- gradientHeightCache = gradientYCache +
+ gradientHeightCache =
calculate(gradientHeight, component, component?.relWidth ?: 0f, component?.relHeight ?: 0f, true)
}
override fun render() {
renderer {
- path {
- linearGradient(gradientXCache, gradientYCache, gradientWidthCache, gradientHeightCache, gradientStartColor?.rgba ?: 0, gradientEndColor?.rgba ?: 0)
- rect(cachedX, cachedY, cachedWidth, cachedHeight, radius)
- }.fillPaint()
+ TODO("Gradient suport")
}
}
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 ee79d69..a2e837d 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.UIContainer
-import net.prismclient.aether.ui.component.type.layout.UIContainerSheet
+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.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,25 +121,12 @@ class UIScrollbar(val type: Scrollbar) : UIColoredShape() {
val mouseX = component!!.getMouseX()
val mouseY = component!!.getMouseY()
- 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) {
+ if (mouseX >= x && mouseX <= x + w && mouseY >= y && mouseY <= y + h) {
selected = true
mouseOffset = if (type == Scrollbar.Vertical) mouseY - y else mouseX - x
- } 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 true
}
-
- return true
+ return false
}
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 353084a..eb9ed72 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.UIFrameSheet
+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.impl.background.UIBackground
@@ -19,7 +19,6 @@ 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
@@ -40,13 +39,11 @@ import net.prismclient.aether.ui.util.name
* 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() : UICopy, UIAnimatable {
- var name: String = ""
-
+open class UIStyleSheet @JvmOverloads constructor(var name: String = "") : UICopy,
+ UIAnimatable {
/**
* When true, the property will not be cleared when Aether cleans up styles.
*/
@@ -85,12 +82,10 @@ open class UIStyleSheet() : UICopy, UIAnimatable {
) {
val component = animation.component
- 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)
- }
+ 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()
@@ -349,7 +344,7 @@ open class UIStyleSheet() : UICopy, UIAnimatable {
this.marginLeft = marginLeft
}
- override fun copy(): UIStyleSheet = UIStyleSheet().name(name).apply(this)
+ override fun copy(): UIStyleSheet = UIStyleSheet(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 1aa6ca5..db2bfe2 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,7 +3,6 @@ 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
@@ -23,7 +22,7 @@ class UIAnchorPoint : UIAnimatable {
infix fun align(alignment: UIAlignment) {
x ?: run { x = px(0) }
y ?: run { y = px(0) }
- align(alignment, x!!, y!!)
+ net.prismclient.aether.ui.util.extensions.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 a58938d..e1e85f7 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/util/Shorthands.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/util/Shorthands.kt
@@ -4,7 +4,6 @@ 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
@@ -35,50 +34,47 @@ const val NEAREST = 32
/**
* Creates a DSL block from the given [obj] of type [T].
*/
-inline fun blockFrom(obj: T, block: Block) = obj.block()
+inline fun blockFrom(obj: T, block: T.() -> Unit) = obj.block()
/**
- * Creates a style sheet [block] from the given style sheet [S], sets the [name] and registers the style.
+ * Registers a style sheet for the given style, [S].
*/
-@JvmName("styleExtension")
-inline fun S.style(name: String, block: Block): S = apply {
- this.name = name
- this.block()
- UIProvider.registerStyle(this)
+inline fun style(style: S, block: S.() -> Unit) {
+ style.block()
+ UIProvider.registerStyle(style)
}
/**
- * Creates a style sheet [block] from the given style sheet [S], sets the [name] and registers the style.
+ * Registers a style sheet for the given style, [S]. Alternative to [style].
+ *
+ * @see style
*/
-inline fun style(sheet: S, name: String, block: Block): S {
- sheet.name = name
- sheet.block()
- UIProvider.registerStyle(sheet)
- return sheet
-}
+inline fun styleOf(style: S, block: S.() -> Unit) = style(style, block)
+
+@JvmName("styleExtension")
+inline fun S.style(block: S.() -> Unit) = style(this, block)
/**
- * Creates a style [block] from the given component [C]. If the style has not been created
- * one is automatically allocated with the name of
+ * 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.
*
- * "$C.toString()-sheet"
- */
-inline fun , S : UIStyleSheet> C.style(block: Block): C = this.style("$this-sheet", block)
+ * 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)
+}
/**
- * 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].
+ * Creates a style [block] on a [UIComponent] of the style sheet [S].
*/
-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)
- }
-
+inline fun , S : UIStyleSheet> C.style(block: S.() -> Unit): C = apply {
+ UIComponentDSL.updateState(this)
this.style.block()
+ UIComponentDSL.restoreState(this)
}
/**
@@ -139,7 +135,7 @@ fun marginOf(top: UIUnit, right: UIUnit, bottom: UIUnit, left: UIUnit): UIMargin
*
* @see ucreate
*/
-inline fun create(block: Block) {
+inline fun create(block: UIComponentDSL.() -> Unit) {
UIComponentDSL.begin()
UIComponentDSL.block()
UIComponentDSL.complete()
@@ -148,7 +144,7 @@ inline fun create(block: Block) {
/**
* Unsafe version of [build]. Does not allocate/deallocate the stacks, thus nothing will be reset
*/
-inline fun ucreate(block: Block) = UIComponentDSL.block()
+inline fun ucreate(block: UIComponentDSL.() -> Unit) = UIComponentDSL.block()
/**
* Loads the [dependable].
@@ -156,11 +152,12 @@ inline fun ucreate(block: Block) = UIComponentDSL.block()
fun include(dependable: UIDependable) = dependable.load()
/**
- * 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.
+ * 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.
*/
-inline fun animationOf(animationName: String, style: S, block: UIAnimation.() -> Unit): UIAnimation {
+fun animationOf(animationName: String, style: S, block: UIAnimation.() -> Unit): UIAnimation {
val animation = UIAnimation(animationName, style)
animation.block()
UIProvider.registerAnimation(animationName, animation)
@@ -168,6 +165,7 @@ inline fun animationOf(animationName: String, style: S, block
}
/**
- * Creates a [UIGradientBackground], applies the given block to it, and returns it.
+ * 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].
*/
-inline fun gradient(block: Block): UIGradientBackground = UIGradientBackground().also(block)
\ No newline at end of file
+typealias Block = T.() -> Unit
\ 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 46696b9..4509113 100644
--- a/src/main/kotlin/net/prismclient/aether/ui/util/UIAetherLogger.kt
+++ b/src/main/kotlin/net/prismclient/aether/ui/util/UIAetherLogger.kt
@@ -2,33 +2,14 @@ package net.prismclient.aether.ui.util
import net.prismclient.aether.ui.Aether
-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 inform(message: String, level: LogLevel = LogLevel.DEBUG) {
- if (level != LogLevel.DEBUG || Aether.debug)
- println("[Aether]: INFO -> $message")
-}
-
-internal fun debug(message: String) {
+internal fun inform(message: String) {
if (Aether.debug)
- println("[Aether]: DEBUG -> $message")
+ println("[Aether]: $message")
}
-internal fun warn(message: String, level: LogLevel = LogLevel.DEBUG) {
- if (level != LogLevel.DEBUG || Aether.debug)
- println("[Aether]: WARNING -> $message")
+internal fun warn(message: String) {
+ if (Aether.debug)
+ println("[Aether]: $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
deleted file mode 100644
index 33477f8..0000000
--- a/src/main/kotlin/net/prismclient/aether/ui/util/Util.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-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
new file mode 100644
index 0000000..2365d7d
--- /dev/null
+++ b/src/main/kotlin/net/prismclient/aether/ui/util/interfaces/UITriConsumer.kt
@@ -0,0 +1,23 @@
+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 3bde412..e736fd8 100644
--- a/src/test/kotlin/Renderer.kt
+++ b/src/test/kotlin/Renderer.kt
@@ -30,8 +30,6 @@ 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
@@ -55,8 +53,15 @@ object Renderer : UIRenderer {
override fun color(color: Int) {
activeColor = color
- nvgColor(color, fillColor)
- nvgFillColor(ctx, fillColor)
+ nvgFillColor(
+ ctx, nvgRGBA(
+ color.getRed().toByte(),
+ color.getGreen().toByte(),
+ color.getBlue().toByte(),
+ color.getAlpha().toByte(),
+ this.fillColor
+ )
+ )
}
override fun globalAlpha(alpha: Float) = nvgGlobalAlpha(ctx, alpha)
@@ -89,7 +94,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.image(), width, height, width * contentScale, height * contentScale, contentScale
+ framebuffer.fbo(), width * contentScale, height * contentScale, width, height, contentScale
)
framebuffers[fbo] = framebuffer
return fbo
@@ -109,7 +114,7 @@ object Renderer : UIRenderer {
nvgluBindFramebuffer(
ctx, framebuffers[fbo] ?: throw NullPointerException("Unable to find the framebuffer $fbo.")
)
- GL11.glViewport(0, 0, fbo.scaledWidth.toInt(), fbo.scaledHeight.toInt())
+ GL11.glViewport(0, 0, fbo.width.toInt(), fbo.height.toInt())
GL11.glClearColor(0f, 0f, 0f, 0f)
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT or GL11.GL_STENCIL_BUFFER_BIT)
}
@@ -118,6 +123,28 @@ 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)
@@ -275,7 +302,13 @@ object Renderer : UIRenderer {
override fun strokeWidth(size: Float) = nvgStrokeWidth(ctx, size)
override fun strokeColor(color: Int) {
- nvgColor(color, strokeColor)
+ nvgRGBA(
+ color.getRed().toByte(),
+ color.getGreen().toByte(),
+ color.getBlue().toByte(),
+ color.getAlpha().toByte(),
+ strokeColor
+ )
nvgStrokeColor(ctx, strokeColor)
}
@@ -316,18 +349,22 @@ object Renderer : UIRenderer {
override fun linearGradient(x: Float, y: Float, x2: Float, y2: Float, startColor: Int, endColor: Int) {
allocPaint()
- nvgColor(startColor, gradient1)
- nvgColor(endColor, gradient2)
- nvgLinearGradient(ctx, x, y, x2, y2, gradient1, gradient2, paint!!)
+ val color1 = createColor(startColor)
+ val color2 = createColor(endColor)
+ nvgLinearGradient(ctx, x, y, x2, y2, color1, color2, paint!!)
+ color1.free()
+ color2.free()
}
override fun radialGradient(
x: Float, y: Float, innerRadius: Float, outerRadius: Float, startColor: Int, endColor: Int
) {
allocPaint()
- nvgColor(startColor, gradient1)
- nvgColor(endColor, gradient2)
- nvgRadialGradient(ctx, x, y, innerRadius, outerRadius, gradient1, gradient2, paint!!)
+ val color1 = createColor(startColor)
+ val color2 = createColor(endColor)
+ nvgRadialGradient(ctx, x, y, innerRadius, outerRadius, color1, color2, paint!!)
+ color1.free()
+ color2.free()
}
override fun allocPaint() {
@@ -343,7 +380,8 @@ object Renderer : UIRenderer {
override fun radToDeg(rad: Float): Float = nvgRadToDeg(rad)
- private fun nvgColor(color: Int, nvgColor: NVGColor) {
+ private fun createColor(color: Int): NVGColor {
+ val nvgColor = NVGColor.calloc()
nvgRGBA(
color.getRed().toByte(),
color.getGreen().toByte(),
@@ -351,5 +389,6 @@ 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 0756640..548805a 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,11 +84,9 @@ object Runner {
}
glfwSetFramebufferSizeCallback(window) { _: Long, width: Int, height: Int ->
- if (width > 0 && height > 0) {
- framebufferWidth = width
- framebufferHeight = height
- core!!.update(width / contentScaleX, height / contentScaleY, max(contentScaleX, contentScaleY))
- }
+ framebufferWidth = width
+ framebufferHeight = height
+ core!!.update(width / contentScaleX, height / contentScaleY, max(contentScaleX, contentScaleY))
}
glfwSetKeyCallback(window) { _: Long, keyCode: Int, scanCode: Int, action: Int, _: Int ->
@@ -192,10 +190,11 @@ 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 4262ebd..1eb9896 100644
--- a/src/test/kotlin/examples/Animations.kt
+++ b/src/test/kotlin/examples/Animations.kt
@@ -22,15 +22,68 @@ import net.prismclient.aether.ui.util.extensions.px
class Animations : UIScreen {
override fun build() {
create {
- button("Hello").style {
+ include(Generic())
+ 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)
}
- verticalList {
+ 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)
+ }
+ }
- }.style {
+ animationOf("test", UIStyleSheet()) {
+ kf {}
+ UILinear(1000L) to {
+ background {
+// backgroundColor = colorOf(asRGBA(0, 0, 255))
+ border {
+ borderColor = colorOf(asRGBA(0f, 1f, 0f))
+ borderWidth = px(1)
+ }
+ }
+ }
+ }
+ 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
new file mode 100644
index 0000000..46584d1
--- /dev/null
+++ b/src/test/kotlin/examples/AutoLayouts.kt
@@ -0,0 +1,110 @@
+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 3e8d79b..1c3612a 100644
--- a/src/test/kotlin/examples/Default.kt
+++ b/src/test/kotlin/examples/Default.kt
@@ -1,23 +1,62 @@
package examples
-import examples.deps.Generic
import net.prismclient.aether.ui.animation.ease.impl.UIQuart
-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.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.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() {}
+ 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)
+ }
+ }
}
\ 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 8e32fc3..e115d40 100644
--- a/src/test/kotlin/examples/components/Chart.kt
+++ b/src/test/kotlin/examples/components/Chart.kt
@@ -6,12 +6,8 @@ import net.prismclient.aether.ui.style.UIStyleSheet
/**
* An example component which draws a chart.
*/
-class Chart() : UIComponent() {
+class Chart(style: String?) : UIComponent(style) {
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 831b3ed..eb8fc8c 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.style
+import net.prismclient.aether.ui.util.styleOf
import net.prismclient.aether.ui.util.top
/**
@@ -41,17 +41,18 @@ 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
- UIImageSheet().style("icon24x") {
+ styleOf(UIImageSheet("icon24x")) {
size(24, 24)
}
- UIStyleSheet().style("generic-font") {
+ // A example font
+ styleOf(UIStyleSheet("generic-font")) {
// FontFamily to Montserrat
// FontSize -> 16f
// FontColor -> -1 = asRGBA(255, 255, 255) (aka white)