Skip to content

Commit

Permalink
bug fixes and annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
nextdayy committed May 12, 2024
1 parent 1fb9836 commit 8f84670
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 41 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version=1.1.71
version=1.1.72

# target Kotlin version for the project.
kotlin.target=1.9
Expand Down
9 changes: 5 additions & 4 deletions src/main/kotlin/org/polyfrost/polyui/PolyUI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,13 @@ class PolyUI @JvmOverloads constructor(
*/
var window: Window? = null
set(value) {
if (value == null) return
if (field === value) return
if (field != null && settings.debug) LOGGER.info("window change: $field -> $value")
field = value
if (value != null) {
if (field != null) {
if (settings.debug) LOGGER.info("window change: $field -> $value")
field = value
resize(value.width.toFloat(), value.height.toFloat(), false)
}
} else field = value
}

/**
Expand Down
44 changes: 29 additions & 15 deletions src/main/kotlin/org/polyfrost/polyui/component/Drawable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ abstract class Drawable(
field = value
}

@SideEffects(["_parent"])
inline var parent: Drawable
get() = _parent ?: error("cannot move outside of component tree")
set(value) {
Expand Down Expand Up @@ -175,6 +176,7 @@ abstract class Drawable(

private var _palette: Colors.Palette? = null

@SideEffects(["color", "palette"])
var palette: Colors.Palette
get() = _palette ?: throw UninitializedPropertyAccessException("Palette is not initialized")
set(value) {
Expand All @@ -201,6 +203,7 @@ abstract class Drawable(
@ApiStatus.Internal
var _y = y

@SideEffects(["x", "_x", "atValid", "this.children::x"], "value != x")
var x: Float
inline get() = _x
set(value) {
Expand All @@ -213,6 +216,7 @@ abstract class Drawable(
}
}

@SideEffects(["y", "_y", "atValid", "this.children::y"], "value != y")
var y: Float
inline get() = _y
set(value) {
Expand Down Expand Up @@ -284,6 +288,7 @@ abstract class Drawable(

val scrolling get() = xScroll != null || yScroll != null

@SideEffects(["_parent.needsRedraw"], `when` = "field != value")
var needsRedraw = true
set(value) {
if (value && !field) {
Expand All @@ -308,6 +313,10 @@ abstract class Drawable(
* **Do not** modify this value!
* @since 1.0.0
*/
@Dispatches("Lifetime.Disabled", "value == INPUT_DISABLED")
@Dispatches("Lifetime.Enabled", "value != INPUT_DISABLED")
@Dispatches("Mouse.Entered", "value > INPUT_NONE")
@Dispatches("Mouse.Exited", "value == INPUT_NONE")
var inputState = INPUT_NONE
set(value) {
if (field == value) return
Expand Down Expand Up @@ -344,6 +353,7 @@ abstract class Drawable(
*/
open var renders = true
set(value) {
if (field == value) return
field = value
needsRedraw = true
}
Expand All @@ -353,6 +363,7 @@ abstract class Drawable(
*
* @since 0.21.4
*/
@SideEffects(["inputState"])
inline var enabled
get() = inputState > INPUT_DISABLED
set(value) {
Expand All @@ -368,9 +379,10 @@ abstract class Drawable(
* note: this method locks due to the fact the object needs to be translated to the center, rotated, and then translated back.
* It only locks if the value is `0.0`.
*/
@Locking
@Locking(`when` = "value == 0.0")
var rotation: Double = 0.0
set(value) {
if (field == value) return
if (value == 0.0) {
synchronized(this) {
// lock required!
Expand All @@ -384,9 +396,10 @@ abstract class Drawable(
*
* locking if set to `0.0`. See [rotation].
*/
@Locking
@Locking(`when` = "value == 0.0")
var skewX: Double = 0.0
set(value) {
if (field == value) return
if (value == 0.0) {
synchronized(this) {
field = value
Expand All @@ -400,9 +413,10 @@ abstract class Drawable(
*
* Locking if set to `0.0`. See [rotation].
*/
@Locking
@Locking(`when` = "value == 0.0")
var skewY: Double = 0.0
set(value) {
if (field == value) return
if (value == 0.0) {
synchronized(this) {
field = value
Expand All @@ -414,13 +428,15 @@ abstract class Drawable(
/** current scale in x dimension of this drawable. */
var scaleX: Float = 1f
set(value) {
if (field == value) return
field = value
needsRedraw = true
}

/** current scale in y dimension of this drawable. */
var scaleY: Float = 1f
set(value) {
if (field == value) return
field = value
needsRedraw = true
}
Expand All @@ -430,10 +446,6 @@ abstract class Drawable(
* @since 0.20.0
*/
var alpha = 1f
set(value) {
field = value
needsRedraw = true
}

/** **a**t **c**ache **x** for transformations. */
private var acx = 0f
Expand Down Expand Up @@ -719,7 +731,7 @@ abstract class Drawable(
return true
}

@Locking
@Locking(`when` = "this.shouldScroll && this.hasVisibleSize && this.visibleSize > this.size")
fun tryMakeScrolling() {
if (shouldScroll && hasVisibleSize) {
var scrolling = false
Expand Down Expand Up @@ -763,9 +775,10 @@ abstract class Drawable(
* @since 1.0.2
*/
@ApiStatus.Experimental
@Locking
@Locking(`when` = "this.children != null")
@Synchronized
fun repositionChildren() {
if (children == null) return
children?.fastEach {
it.x = 0f
it.y = 0f
Expand All @@ -782,9 +795,10 @@ abstract class Drawable(
* @since 1.0.7
*/
@ApiStatus.Experimental
@Locking
@Locking(`when` = "this.children != null")
@Synchronized
fun recalculate() {
if (children == null) return
val sz = this.size
val oldW = sz.x
val oldH = sz.y
Expand Down Expand Up @@ -859,7 +873,7 @@ abstract class Drawable(
* Add a [DrawableOp] to this drawable.
* @return `true` if the operation was added, `false` if it was replaced.
*/
@Locking
@Locking(`when` = "drawableOp.verify() == true")
@Synchronized
fun addOperation(drawableOp: DrawableOp): Boolean {
if (!drawableOp.verify()) {
Expand All @@ -879,7 +893,7 @@ abstract class Drawable(
/**
* Remove an operation from this drawable.
*/
@Locking
@Locking(`when` = "operations != null")
@Synchronized
fun removeOperation(drawableOp: DrawableOp) {
this.operations?.apply {
Expand Down Expand Up @@ -918,16 +932,16 @@ abstract class Drawable(
}
}

@Locking
@Locking(`when` = "children.size != 0")
fun addChild(vararg children: Drawable) {
for (child in children) addChild(child)
}

@Locking
fun removeChild(child: Drawable) {
fun removeChild(child: Drawable, recalculate: Boolean = true) {
val i = children?.indexOf(child) ?: throw NoSuchElementException("no children on $this")
require(i != -1) { "Drawable $child is not a child of $this" }
removeChild(i)
removeChild(i, recalculate)
}

@Locking
Expand Down
34 changes: 18 additions & 16 deletions src/main/kotlin/org/polyfrost/polyui/component/extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -280,22 +280,6 @@ fun <S : Drawable> S.setPalette(palette: Colors.() -> Colors.Palette): S {
return this
}

/**
* Call this function to ignore this drawable during the calculation of the parent's positioning and size.
* It is equivalent to the following:
* ```
* renders = false
* afterParentInit { renders = true }
* ```
*
* @since 1.1.4
*/
fun <S : Drawable> S.ignoreLayout(): S {
renders = false
afterParentInit { renders = true }
return this
}

/**
* Set the font of this text component during initialization, using the PolyUI fonts instance.
* @since 1.1.3
Expand Down Expand Up @@ -575,6 +559,24 @@ fun <S : Drawable> PolyColor.makeChroma(speedNanos: LongRef = 5.seconds.toChroma
return p
}

/**
* Locate a drawable by its name.
*
* This method is recursive, meaning it will search through all children of this drawable.
* @param id the name of the drawable to locate.
* @return the drawable with the given name, or `null` if it was not found.
* @since 1.1.72
*/
fun <S : Drawable> Drawable.locate(id: String): S? {
@Suppress("UNCHECKED_CAST")
if (this.simpleName == id) return this as S
children?.fastEach {
val res = it.locate<S>(id)
if (res != null) return res
}
return null
}

/**
* Bulk add method for all the builtin drawable operations in PolyUI.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ fun Radiobutton(vararg entries: Pair<PolyImage?, String?>, at: Vec2? = null, ini
it.palette = polyUI.colors.brand.fg
}
addChild(it, recalculate = false)
it.size.set(target.size)
it.relegate()
}.namedId("Radiobutton")
}
Expand Down Expand Up @@ -417,7 +418,7 @@ fun PopupMenu(vararg children: Drawable, size: Vec2? = null, align: Align = Alig
}
Event.Focused.Lost then {
Fade(this, 0f, false, Animations.EaseInOutQuad.create(0.2.seconds)) {
this.polyUI.master.removeChild(this)
this.polyUI.master.removeChild(this, recalculate = false)
}.add()
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/main/kotlin/org/polyfrost/polyui/event/Dispatches.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.polyfrost.polyui.event

import org.jetbrains.annotations.ApiStatus

/**
* An annotation to mark a function or property as dispatching events.
* @param `when` condition under which the events are dispatched.
* @param event the events that are dispatched.
*/
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
@MustBeDocumented
@Repeatable
@Retention(AnnotationRetention.SOURCE)
@ApiStatus.Experimental
annotation class Dispatches(val event: String = "", val `when`: String = "")
8 changes: 5 additions & 3 deletions src/main/kotlin/org/polyfrost/polyui/input/Translator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class Translator(private val settings: Settings, private val translationDir: Str
PolyUI.LOGGER.warn("Duplicate key: '${split[0]}', overwriting with $resource -> ${split[1]}")
v.string = split[1]
} else {
require('.' in split[0]) { "Invalid key ${split[0]}: keys must contain at least one dot" }
map[split[0]] = Text.Simple(split[1])
}
} ?: PolyUI.LOGGER.warn("\t\t> Table not found!")
Expand Down Expand Up @@ -165,10 +166,12 @@ class Translator(private val settings: Settings, private val translationDir: Str
* @since 0.17.5
*/
fun addKey(key: String, value: String) {
require('.' in key) { "Invalid key $key: keys must contain at least one dot" }
val v = map[key]
if (v == null) {
map[key] = Text.Simple(value)
} else {
PolyUI.LOGGER.warn("Duplicate key: '$key', overwriting with $value")
v.string = value
}
}
Expand Down Expand Up @@ -207,7 +210,7 @@ class Translator(private val settings: Settings, private val translationDir: Str
*/
fun translate(key: String): Text {
if (key.isEmpty()) return Text.Simple("").dont()
var ran = false
if ('.' !in key) return Text.Simple(key).dont()
val text = map.getOrPut(key) {
while (queue.isNotEmpty()) {
loadKeys(queue.removeFirst(), true)
Expand All @@ -234,10 +237,9 @@ class Translator(private val settings: Settings, private val translationDir: Str
val v = map[key]
if (v != null) return@getOrPut v
}
ran = true
if(!dontWarn) PolyUI.LOGGER.warn("No translation for '$key'!")
Text.Simple(key)
}
if (!dontWarn && ran && text.string == key && '.' in key) PolyUI.LOGGER.warn("No translation for '$key'!")
return text
}

Expand Down
5 changes: 4 additions & 1 deletion src/main/kotlin/org/polyfrost/polyui/utils/Locking.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ import org.jetbrains.annotations.Blocking
/**
* Marker class for future use, which will be used to indicate that the given function or property
* will block until the lock is freed (when it finishes the frame)
*
* @param when if applied to a property, it is assumed that the lock is held when `value` is equal to [when].
* else, the [when] property should be a simple statement regarding when the lock will be held.
*/
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.BINARY)
@Blocking
@MustBeDocumented
@ApiStatus.Experimental
annotation class Locking
annotation class Locking(val `when`: String = "")
15 changes: 15 additions & 0 deletions src/main/kotlin/org/polyfrost/polyui/utils/SideEffects.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.polyfrost.polyui.utils

import org.jetbrains.annotations.ApiStatus

/**
* An annotation to mark a function or property as having side effects.
* @param `when` condition under which the side effects occur.
* @param values the side effects that occur.
*/
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
@MustBeDocumented
@Repeatable
@Retention(AnnotationRetention.SOURCE)
@ApiStatus.Experimental
annotation class SideEffects(val values: Array<String> = [], val `when`: String = "")

0 comments on commit 8f84670

Please sign in to comment.