Skip to content

Commit

Permalink
Merge pull request #23 from Prism-Client/dev
Browse files Browse the repository at this point in the history
1.3.0 Figma Fonts
  • Loading branch information
senxd authored Jul 20, 2022
2 parents 1162926 + 7c07030 commit 1ad0b71
Show file tree
Hide file tree
Showing 67 changed files with 2,097 additions and 2,552 deletions.
43 changes: 23 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# Aether UI

<img src="/docs/assets/client-logo-rounded.png" align="right" width="230" height="230">
<img src="/assets/client-logo-rounded.png" align="right" width="230" height="230">

**Aether is a UI component library engine for Minecraft** and LWJGL projects (or anything really). You can create your
own renderer implementation or use the default implementation
with *[NanoVG](https://github.com/memononen/nanovg "An anti-aliased vector graphics library")*, which is an **
Anti-Aliased vector graphics library**. **Please note the project is in the early stages of development.** Bugs may
arise, and there might be incomplete/missing features. The library is licensed under GPL-v2.0 license.
Aether is a UI component engine for Minecraft. It's designed with Kotlin, Figma, and Minecraft in mind. Aether is
designed to allow you to customize the renderer to your own, or the default one
with *[NanoVG, an anti-aliased 2D vector graphics library](https://github.com/memononen/nanovg "An anti-aliased vector graphics library")*.
As mentioned before, Aether is designed with Figma in mind. Features such as [Auto Layouts](https://help.figma.com/hc/en-us/articles/360040451373-Explore-auto-layout-properties)
and [Figma Font](https://help.figma.com/hc/en-us/articles/360039956434-Getting-started-with-text) are implemented to
streamline the process of creating UIs from Figma. The library is licensed under GPL-v2.0 license.

Ready to get started? [Check out the docs!](https://aether.prismclient.net/)
Convinced? [Check out the docs!](https://aether.prismclient.net/)

# Including the project

Expand All @@ -18,11 +19,11 @@ Ready to get started? [Check out the docs!](https://aether.prismclient.net/)

```groovy
repositories {
maven { url "https://jitpack.io" }
maven { url "https://jitpack.io" }
}
dependencies {
implementation "com.github.Prism-Client:Aether-UI:Release"
implementation "com.github.Prism-Client:Aether-UI:Release"
}
```

Expand All @@ -33,18 +34,19 @@ dependencies {
<summary>Maven</summary>

```xml

<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.Prism-Client</groupId>
<artifactId>Aether-UI</artifactId>
<version>Release</version>
</dependency>
<dependency>
<groupId>com.github.Prism-Client</groupId>
<artifactId>Aether-UI</artifactId>
<version>Release</version>
</dependency>
</dependencies>
```

Expand All @@ -55,16 +57,17 @@ dependencies {
Personally, the greatest feat in making a Minecraft client is making appealing, flexible, and highly customizable UI.
Very few are able to reach even 2/3 of my goals. I found that popular clients lacked quality (anti-aliasing) in their
client, and in other cases, it simply wasn't that appealing (though that might be more related to the designer). Around
the time of the intial creation of this library, I picked up Kotlin. Kotlin introduces a ton of useful features
especially the DSL feature. It makes creating UIs just feel a whole lot easier. Because of it, I decided to design it
the time of the initial creation of this library, I picked up Kotlin. Kotlin introduces a ton of useful features
especially the DSL feature. It makes creating UIs just feel a lot easier. Because of it, I decided to design it
with Kotlin as the main programming language. Java is still supported, however I <ins>highly</ins> suggest for you to
use Kotlin.

# Design

I initially created the engine to act a bit like HTML and CSS, where you would create "style sheets" and define them as
components. This design, dare I say pattern, is essentially is how it is today. However, that is about the only thing
related to web stuff.
I initially created the engine to act a bit like HTML and CSS, however, I decided to swerve from that. I kept the idea
of styles, however, a lot of things are different from web. After all, it was initially designed for newspapers! (Obviously,
it's changed a lot since then). Because I had Figma in mind, quite a few different features (such as Auto Layouts and Text) are
somewhat "loosely" based on Figma.

# Development

Expand Down
Binary file added assets/client-logo-rounded.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 1 addition & 13 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,12 @@ dependencies {
testRuntimeOnly "org.lwjgl:lwjgl-stb:$lwjglVersion:$lwjglNatives"
}

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
languageVersion = "1.7"
freeCompilerArgs += [
'-Xno-call-assertions',
'-Xno-receiver-assertions',
'-Xno-param-assertions',
'-Xjvm-default=enable'
]
}
}

publishing {
publications {
maven(MavenPublication) {
groupId = 'com.github.Prism-Client'
artifactId = 'Aether-UI'
version = '1.2-release'
version = '1.2-Production'
from components.kotlin
}
}
Expand Down
78 changes: 31 additions & 47 deletions src/main/kotlin/net/prismclient/aether/ui/Aether.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package net.prismclient.aether.ui
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.UIContainer
import net.prismclient.aether.ui.component.type.layout.UIContainerSheet
import net.prismclient.aether.ui.component.type.layout.UIFrame
import net.prismclient.aether.ui.component.type.layout.container.UIContainer
import net.prismclient.aether.ui.component.type.layout.styles.UIContainerSheet
import net.prismclient.aether.ui.event.input.UIMouseEvent
import net.prismclient.aether.ui.renderer.UIProvider
import net.prismclient.aether.ui.renderer.UIRenderer
Expand Down Expand Up @@ -33,9 +33,10 @@ import java.util.function.Consumer
* functions [Properties.updateSize] and [Properties.updateMouse] to update the values without
* invoking the [update], and [mouseMoved] functions.
*
* [Aether Documentation](https://aether.prismclient.net/getting-started)
*
* @author sen
* @since 1.0
* @see <a href="https://aether.prismclient.net/getting-started">UICore documentation</a>
* @see UIProvider
*/
open class Aether(renderer: UIRenderer) {
Expand Down Expand Up @@ -108,12 +109,14 @@ open class Aether(renderer: UIRenderer) {
open fun render() {
renderer {
if (activeScreen != null) {
timings.onFrameRenderStart()
beginFrame(width, height, devicePxRatio)
for (i in 0 until components!!.size) {
val component = components!![i]
if (component.visible) component.render()
}
endFrame()
timings.onFrameRenderEnd()
}
}
}
Expand All @@ -123,7 +126,7 @@ open class Aether(renderer: UIRenderer) {
* eligibility to be focused or bubbled. The [Properties.mouseX] and [Properties.mouseY]
* properties can be found in [Aether.Properties].
*/
fun mouseMoved(mouseX: Float, mouseY: Float) {
open fun mouseMoved(mouseX: Float, mouseY: Float) {
updateMouse(mouseX, mouseY)
mouseMoveListeners?.forEach { it.value.run() }
if (activeScreen != null) for (i in 0 until components!!.size) components!![i].mouseMoved(mouseX, mouseY)
Expand All @@ -139,10 +142,10 @@ open class Aether(renderer: UIRenderer) {
*
* @see mouseScrolled
*/
fun mouseChanged(mouseButton: Int, isRelease: Boolean) {
open fun mouseChanged(mouseButton: Int, isRelease: Boolean) {
if (isRelease) {
mouseReleasedListeners?.forEach { it.value.run() }
components?.forEach { it.mouseReleased(mouseX, mouseY) }
components?.forEach { it.mouseReleased(it.getMouseX(), it.getMouseY()) }
return
}

Expand All @@ -162,27 +165,25 @@ open class Aether(renderer: UIRenderer) {
* mouse coordinates. Index is the index of the component.
*/
fun peek(list: ArrayList<UIComponent<*>>, index: Int, clickCount: Int): Boolean {
var component: UIComponent<*>? = null
var foundComponent: UIComponent<*>? = null

for (i in index until list.size) {
val child = list[i]
val check = child.isMouseInsideBounds()
if (child.isMouseInsideBounds() || !child.style.clipContent) {
if (child.childrenCount > 0) {
if (peek(
if (child is UIFrame) child.components else list,
i + if (child is UIFrame) 0 else 1,
clickCount
)
) return true

// If the mouse is inside the component
if (child.isMouseInside()) {
// Check if the component is a UIFrame. If it is, integrate through the children
// of this frame and find the one, if applicable that should be invoked.
if (child is UIFrame) {
// Break the loop by returning true if the component is found
if (peek(child.components, 0, clickCount)) return true
}
if (check) component = child
foundComponent = child
}
}

return if (component != null) {
component.focus()
component.mousePressed(UIMouseEvent(mouseX, mouseY, mouseButton, clickCount))
return if (foundComponent != null) {
foundComponent.focus()
foundComponent.mousePressed(UIMouseEvent(foundComponent.getMouseX(), foundComponent.getMouseY(), mouseButton, clickCount))
true
} else false
}
Expand Down Expand Up @@ -211,7 +212,7 @@ open class Aether(renderer: UIRenderer) {
i++
}
c?.focus()
c?.mousePressed(UIMouseEvent(mouseX, mouseY, mouseButton, clickCount))
c?.mousePressed(UIMouseEvent(c.getMouseX(), c.getMouseY(), mouseButton, clickCount))
}

/**
Expand All @@ -220,7 +221,7 @@ open class Aether(renderer: UIRenderer) {
* @param character The key which was pressed or '\u0000'
* @see updateModifierKey To update keys such as Shift, Alt, Tab etc...
*/
fun keyPressed(character: Char) {
open fun keyPressed(character: Char) {
keyPressListeners?.forEach { it.value.accept(character) }
(focusedComponent as? UIComponent<*>)?.keyPressed(character)
}
Expand All @@ -230,6 +231,7 @@ open class Aether(renderer: UIRenderer) {
* of their eligibility to be focused or bubbled.
*/
open fun mouseScrolled(scrollAmount: Float) {
if (scrollAmount == 0f) return
tryFocus()
mouseScrollListeners?.forEach { it.value.accept(scrollAmount) }
components?.forEach { it.mouseScrolled(mouseX, mouseY, scrollAmount) }
Expand All @@ -241,20 +243,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).
Expand All @@ -265,92 +267,79 @@ 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
* is resized or created. Invoked prior to components.
*/
@JvmStatic
var updateListeners: HashMap<String, Runnable>? = null
protected set

/**
* The listeners for then the mouse is moved. Invoked prior to components.
*/
@JvmStatic
var mouseMoveListeners: HashMap<String, Runnable>? = null
protected set

/**
* Invoked when the mouse is pressed. Invoked prior to components.
*/
@JvmStatic
var mousePressedListeners: HashMap<String, Runnable>? = null
protected set

/**
* Invoked when the mouse is released. Invoked prior to components.
*/
@JvmStatic
var mouseReleasedListeners: HashMap<String, Runnable>? = null
protected set

/**
* Invoked when a key is pressed. Invoked prior to components.
*/
@JvmStatic
var keyPressListeners: HashMap<String, Consumer<Char>>? = null
protected set

/**
* Invoked when the mouse is scrolled. Invoked prior to components.
*/
@JvmStatic
var mouseScrollListeners: HashMap<String, Consumer<Float>>? = null
protected set

/**
* Invoked when the screen is deleted. This is used to deallocate listeners added to UICore.
*/
@JvmStatic
var deallocationListeners: HashMap<String, Runnable>? = null
protected set

/**
* The list of modifier keys. The value is if the key is pressed
Expand Down Expand Up @@ -502,13 +491,7 @@ open class Aether(renderer: UIRenderer) {
* Focuses the component. Please use [UIComponent.focus] instead.
*/
@JvmStatic
fun focus(component: UIFocusable) {
// Check if the given value is a valid instance of UIComponent
try {
component as UIComponent<*>
} catch (castException: ClassCastException) {
throw RuntimeException("When trying to focus, the provided value is not an instance of UIComponent. Make sure you are only using the UIFocus interface to focus UIComponents.")
}
fun <T> focus(component: T) where T : UIComponent<*>, T : UIFocusable {
focusedComponent = component
}

Expand Down Expand Up @@ -548,7 +531,8 @@ 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
@Suppress("UNCHECKED_CAST")
val container = instance.frames!![i] as? UIContainer<UIContainerSheet> ?: 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
Expand Down
Loading

0 comments on commit 1ad0b71

Please sign in to comment.