Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade inspector with new capabilities and detached window mode. #86

Open
wants to merge 60 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
1fcdef0
Components: Replace direct calls to UResolution with ResolutionManage…
Sk1er Aug 11, 2022
7e8301a
Components: Replace direct calls to UMouse with MousePositionManager …
Sk1er Aug 11, 2022
02b582c
Components: Replace direct calls to UKeyboard with KeyboardManager to…
Sk1er Aug 11, 2022
012beb2
AspectConstraint: Use state to track value
Sk1er Aug 11, 2022
8e27b32
ChildBasedConstraint: Use state to track padding
Sk1er Aug 11, 2022
bb8f2ed
StateRegistry: Create state registry for providing additional informa…
Sk1er Aug 11, 2022
893c315
Debug: Allow setting of elementa debug at runtime without requiring t…
Sk1er Aug 12, 2022
3f64066
Inspector: Upgrade inspector with new capabilities and detached windo…
Sk1er Aug 12, 2022
0c240f3
Platform: Create `runOnMinecraftThread` function
Sk1er Aug 29, 2022
7a07184
Inspector: Move external display logic into main project
Sk1er Aug 29, 2022
5690908
Inspector: Delete InspectorManager and manage inspector window inside…
Sk1er Aug 29, 2022
310d21c
Window: Set currentWindow inside try block and unset in finally
Sk1er Sep 28, 2022
c0ed8bf
API: Temporarily remove deprecation on guiHint and roundToRealPixels
Sk1er Sep 28, 2022
5ec4812
ScissorEffect: Remove mutability of unroundedScissorBounds and rounde…
Sk1er Sep 28, 2022
01f154f
Extensions: Remove unnecessary this from *Manager utility properties
Sk1er Sep 28, 2022
a2af9c3
KeyboardManager: Remove overloads that do not use the argument
Sk1er Sep 28, 2022
b271326
KeyboardManager: Adjust code style of isCtrlKeyDown
Sk1er Sep 28, 2022
9f558ad
StateRegistry: Import inspector and cleanup comments
Sk1er Sep 28, 2022
992bb3c
StateRegistry: Migrate managedStates to property
Sk1er Sep 28, 2022
3251bfc
StateRegistry: Rename ManagedColorStateNullable to ManagedNullableCol…
Sk1er Sep 28, 2022
6e9ed17
StateRegistry: Rename managed state objects to avoid repetitive `Mana…
Sk1er Sep 28, 2022
4f140c7
StateRegistry: Remove InspectorDisplay interface and require a displa…
Sk1er Sep 28, 2022
e8b3232
StateRegistry: Rename OfObject to OfEnumerable for increased clarity
Sk1er Sep 28, 2022
e4f59a6
RainbowColorConstraint: Map constructor states to self to create muta…
Sk1er Sep 28, 2022
9b43b46
RelativeWindowConstraint: Map constructor states to self to create mu…
Sk1er Sep 28, 2022
162dca0
RoundingConstraint: Map constructor states to self to create mutable …
Sk1er Sep 28, 2022
b6230d8
ScaledTextConstraint: Map constructor states to self to create mutabl…
Sk1er Sep 28, 2022
c8e7628
SiblingConstraint: Map constructor states to self to create mutable copy
Sk1er Sep 29, 2022
1d3a98b
ColumnPositionConstraint: Add documentation and improve variable names
Sk1er Sep 29, 2022
ee8bb8d
ColumnPositionConstraint: Return no horizontal padding if the compone…
Sk1er Sep 29, 2022
6540bc6
ColumnPositionConstraint: DRY component.parent
Sk1er Sep 29, 2022
8aee863
ConstraintResolutionGui: Revert now unnecessary constructor changes
Sk1er Sep 29, 2022
3738cda
Inspector: Toggle hotkeys in inspector with F12
Sk1er Sep 29, 2022
3d82fc9
Inspector: Reset Elementa debug state in finally block
Sk1er Sep 29, 2022
8d13007
Inspector: Move ensureLastComponent call to animationFrame
Sk1er Sep 29, 2022
14c9285
Inspector: Rename WindowDrawObserver to WindowOverlay
Sk1er Sep 29, 2022
6b173f5
Inspector: Fix balanced before/after draw call if component is not mo…
Sk1er Sep 29, 2022
35dbd0e
Inspector: Toggle measuring distance with M instead of dedicated off key
Sk1er Sep 29, 2022
e08ca3a
Inspector: Round distance measurement rendering to real pixels
Sk1er Sep 29, 2022
cb0cf09
Inspector: DRY
Sk1er Sep 29, 2022
84b9714
Inspector: DRY distance drawing text
Sk1er Sep 29, 2022
69a7f19
Inspector: Cleanup distance measurement format
Sk1er Sep 29, 2022
900ef47
MappedTextInput: Rename to StateTextInput
Sk1er Sep 29, 2022
17b05ff
StateTextInput: Clone from Essential
Sk1er Sep 29, 2022
cc0d78d
StateRegistry: Update use of StateTextInput
Sk1er Sep 29, 2022
6bb1042
FrameBufferedWindow: Import platform instead of declaring as property
Sk1er Sep 29, 2022
74273d4
FrameBufferedWindow: Rename parameter for increased clarity
Sk1er Sep 29, 2022
c327dd2
FrameBufferedWindow: Rename renderDirect to renderFrameBufferTexture
Sk1er Sep 29, 2022
da79f0e
FrameBufferedWindow: Simplify render method
Sk1er Sep 29, 2022
ee2d861
FrameBufferedWindow: DRY
Sk1er Sep 29, 2022
a86df75
StateRegistry: Handle parsing of null color case
Sk1er Sep 29, 2022
65dea1d
StateRegistry: DRY
Sk1er Sep 29, 2022
dc71b4c
StateRegistry: Display and allow editing of oppacity byte of color
Sk1er Sep 29, 2022
9f18e2b
Extensions: Remove State<String>.empty
Sk1er Sep 29, 2022
420031b
Extensions: Use mousePositionManger in hoveredState
Sk1er Sep 29, 2022
84482b0
Extensions: Add isInComponentTree
Sk1er Sep 29, 2022
c499f13
Extensions: Check if component is in component tree before performing…
Sk1er Sep 29, 2022
92af06d
AwtMousePositionManager: Update mouse position from events to avoid q…
Sk1er Sep 29, 2022
7ed0e6e
Build: Update ABI
Sk1er Sep 29, 2022
6706faf
Merge branch 'master' into feature/inspector_improvements
Sk1er Oct 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 73 additions & 14 deletions api/Elementa.api

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,20 @@ dependencies {
implementation(prebundle(internal))

// Depending on LWJGL3 instead of 2 so we can choose opengl bindings only
compileOnly("org.lwjgl:lwjgl-opengl:3.3.1")
compileOnly("org.lwjgl:lwjgl-opengl:3.2.2")
// Depending on 1.8.9 for all of these because that's the oldest version we support
compileOnly(libs.versions.universalcraft.map { "gg.essential:universalcraft-1.8.9-forge:$it" }) {
attributes { attribute(common, true) }
}
compileOnly("com.google.code.gson:gson:2.2.4")

// For external inspector display on LWJGL2
compileOnly("org.lwjgl.lwjgl:lwjgl:2.9.3")
compileOnly("org.lwjgl.lwjgl:lwjgl_util:2.9.3")

// For external inspector display on LWJGL3
compileOnly("org.lwjgl:lwjgl:3.2.2")
compileOnly("org.lwjgl:lwjgl-glfw:3.2.2")
}

apiValidation {
Expand Down
126 changes: 101 additions & 25 deletions src/main/java/com/example/examplemod/ComponentsGui.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,30 @@ import gg.essential.elementa.components.*
import gg.essential.elementa.components.image.BlurHashImage
import gg.essential.elementa.components.input.UIMultilineTextInput
import gg.essential.elementa.components.input.UITextInput
import gg.essential.elementa.components.inspector.CompactToggle
import gg.essential.elementa.components.inspector.Inspector
import gg.essential.elementa.constraints.*
import gg.essential.elementa.dsl.*
import gg.essential.elementa.effects.OutlineEffect
import gg.essential.elementa.markdown.MarkdownComponent
import gg.essential.elementa.markdown.*
import gg.essential.elementa.state.BasicState
import java.awt.Color
import java.net.URL

class ComponentsGui : WindowScreen(ElementaVersion.V2) {
init {
ComponentType("UIContainer") {
val bar = UIBlock().constrain {
// Components declared through a delegated properly will have their component
// name set to the property name in the inspector. In this case, the component
// will be called "bar
val containerExample by ComponentType("UIContainer") {
val bar by UIBlock().constrain {
Comment on lines +21 to +25
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's nice, but it doesn't really belong in this commit. Please keep at it with trying to separate unrelated things into separate commits. The first few commits were great, but this one has a bunch of stuff that really should have been separate. This doesn't even really belong in this PR.

x = 2.pixels()
y = SiblingConstraint() + 5.pixels()
width = 150.pixels()
height = 50.pixels()
} childOf this

val container = UIContainer().constrain {
val container by UIContainer().constrain {
x = 0.pixels(true)
width = ChildBasedSizeConstraint(padding = 2f)
height = ChildBasedMaxSizeConstraint()
Expand All @@ -39,7 +44,7 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
}
} childOf window

ComponentType("UIBlock") {
val blockExample by ComponentType("UIBlock") {
UIBlock().constrain {
x = 2.pixels()
y = SiblingConstraint() + 5.pixels()
Expand All @@ -48,7 +53,7 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
} childOf this
} childOf window

ComponentType("UIText") {
val textExample by ComponentType("UIText") {
UIText("This is my non-wrapping text").constrain {
x = 2.pixels()
y = SiblingConstraint() + 5.pixels()
Expand All @@ -70,7 +75,7 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
} childOf this
} childOf window

ComponentType("UIWrappedText") {
val wrappedTextExample by ComponentType("UIWrappedText") {
UIWrappedText("This is my text that is wrapping at 100 pixels!").constrain {
x = 2.pixels()
y = SiblingConstraint() + 5.pixels()
Expand All @@ -93,7 +98,7 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
} childOf this
} childOf window

ComponentType("UIRoundedRectangle") {
val rectangleExample by ComponentType("UIRoundedRectangle") {
UIRoundedRectangle(2f).constrain {
x = 2.pixels()
y = SiblingConstraint() + 5.pixels()
Expand All @@ -111,7 +116,7 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
} childOf this
} childOf window

ComponentType("UICircle") {
val circleExample by ComponentType("UICircle") {
UICircle().constrain {
// These x & y positions describe the CENTER of the circle
x = 30.pixels()
Expand All @@ -128,7 +133,7 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
} childOf this
} childOf window

ComponentType("UIShape") {
val shapeExample by ComponentType("UIShape") {
val shapeHolder = UIContainer().constrain {
x = 2.pixels()
y = SiblingConstraint() + 5.pixels()
Expand Down Expand Up @@ -166,7 +171,7 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
}
} childOf window

ComponentType("UIImage") {
val imageExample by ComponentType("UIImage") {
UIImage.ofURL(URL("https://i.imgur.com/Pc6iMw3.png")).constrain {
x = 2.pixels()
y = SiblingConstraint() + 5.pixels()
Expand All @@ -184,7 +189,7 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
} childOf this
} childOf window

ComponentType("BlurHashImage") {
val blurHashImageExample by ComponentType("BlurHashImage") {
BlurHashImage("L4ESU,OD1e#:=GwwJSAr1M,r|]Ar").constrain {
x = 2.pixels()
y = SiblingConstraint() + 5.pixels()
Expand All @@ -202,16 +207,16 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
} childOf this
} childOf window

ComponentType("Text Input") {
val box1 = UIBlock(Color(50, 50, 50)).constrain {
val textInputExample by ComponentType("Text Input") {
val box1 by UIBlock(Color(50, 50, 50)).constrain {
x = 2.pixels()
y = SiblingConstraint() + 5.pixels()

width = 100.pixels()
height = 12.pixels()
} childOf this

val textInput1 = UITextInput("My single line text input!").constrain {
val textInput1 by UITextInput("My single line text input!").constrain {
x = 2.pixels()
y = 2.pixels()

Expand All @@ -220,15 +225,15 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {

box1.onMouseClick { textInput1.grabWindowFocus() }

val box2 = UIBlock(Color(50, 50, 50)).constrain {
val box2 by UIBlock(Color(50, 50, 50)).constrain {
x = 2.pixels()
y = SiblingConstraint() + 5.pixels()

width = 100.pixels()
height = ChildBasedSizeConstraint() + 4.pixels()
} childOf this

val textInput2 = UIMultilineTextInput("My multiline text input!").constrain {
val textInput2 by UIMultilineTextInput("My multiline text input!").constrain {
x = 2.pixels()
y = 2.pixels()

Expand All @@ -238,8 +243,8 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
box2.onMouseClick { textInput2.grabWindowFocus() }
} childOf window

ComponentType("ScrollComponent") {
val scroll1 = ScrollComponent().constrain {
val scrollComponentExample by ComponentType("ScrollComponent") {
val scroll1 by ScrollComponent().constrain {
x = 2.pixels()
y = SiblingConstraint() + 5.pixels()

Expand All @@ -266,7 +271,7 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
} childOf this
} childOf window

ComponentType("Markdown") {
val markdownExample by ComponentType("Markdown") {
MarkdownComponent(
"""
# Markdown!
Expand All @@ -285,7 +290,7 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
} childOf this
} childOf window

ComponentType("SVG") {
val svgExample by ComponentType("SVG") {
SVGComponent.ofResource("/svg/test.svg").constrain {
x = 2.pixels()
y = SiblingConstraint(padding = 2f)
Expand All @@ -294,7 +299,7 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
} childOf this
} childOf window

ComponentType("Gradient") {
val gradientExample by ComponentType("Gradient") {
GradientComponent(Color.BLACK, Color.PINK).constrain {
x = 2.pixels()
y = SiblingConstraint() + 5.pixels()
Expand All @@ -303,10 +308,81 @@ class ComponentsGui : WindowScreen(ElementaVersion.V2) {
} childOf this
} childOf window

Inspector(window).constrain {
x = 10.pixels(true)
y = 10.pixels(true)
val inspectorExample by ComponentType("Inspector") {
constrain {
width = 250.pixels
}
val startDetached = Inspector.startDetached
Inspector.startDetached = false
val inspector = Inspector(window)
Inspector.startDetached = startDetached

val inspectorTextDescription by MarkdownComponent(
"""The Elementa inspector provides useful debug features for creating UIs with Elementa.
It can operate within a Window as an overlay or in an external window.
It's initial position can be configured by adding
`-Delementa.inspector.detached=<true/false>` to the JVM arguments.
""",
config = MarkdownConfig(
paragraphConfig = ParagraphConfig(
spaceBetweenLines = 0f
),
inlineCodeConfig = InlineCodeConfig(
backgroundColor = Color.GRAY,
outlineWidth = 0f,
verticalPadding = 1f,
)
)
).constrain {
width = 100.percent
y = SiblingConstraint(2f)
} childOf this

val inspectorExternal = BasicState(false)

val toggleRow by UIContainer().constrain {
y = SiblingConstraint(10f)
width = 100.percent
height = ChildBasedMaxSizeConstraint()
}.addChildren(
UIText("Detached Inspector: "),
CompactToggle(inspectorExternal).constrain {
x = SiblingConstraint(5f)
y = CenterConstraint()
}
) childOf this


inspectorExternal.onSetValue {
inspector.setDetached(it)
}

val inspectorFeaturesDescription by UIWrappedText(
"The inspector allows you to configure the underlying states in components and constraints that" +
" implement the StateRegistry. "
).constrain {
width = 100.percent
y = SiblingConstraint(10f)
} childOf this

val inspectorHotkeyDescription by UIWrappedText(
"You can also use hotkeys to interact with the inspector while using your UI.\n" +
"'C' activtes the constraints tab\n" +
"'V' activates the values tab\n" +
"'B' activates the states tab\n" +
"'S' activates the selection tool\n" +
"'M' activates the measure tool\n" +
"'N' disables the measure tool\n" +
"'D' toggles Elementa debug mode\n"
).constrain {
width = 100.percent
y = SiblingConstraint(10f)
} childOf this

inspector childOf window

} childOf window

}

class ComponentType(componentName: String, initBlock: ComponentType.() -> Unit) : UIContainer() {
Expand Down
39 changes: 27 additions & 12 deletions src/main/kotlin/gg/essential/elementa/UIComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import gg.essential.elementa.utils.*
import gg.essential.elementa.utils.requireMainThread
import gg.essential.elementa.utils.requireState
import gg.essential.universal.UMatrixStack
import gg.essential.universal.UMouse
import gg.essential.universal.UResolution
import org.lwjgl.opengl.GL11
import java.awt.Color
import java.util.*
Expand Down Expand Up @@ -383,13 +381,15 @@ abstract class UIComponent : Observable() {
}

protected fun getMousePosition(): Pair<Float, Float> {
return pixelCoordinatesToPixelCenter(UMouse.Scaled.x, UMouse.Scaled.y).let { (x, y) -> x.toFloat() to y.toFloat() }
return pixelCoordinatesToPixelCenter(mousePositionManager.scaledX, mousePositionManager.scaledY)
.let { (x, y) -> x.toFloat() to y.toFloat() }
}

internal fun pixelCoordinatesToPixelCenter(mouseX: Double, mouseY: Double): Pair<Double, Double> {
// Move the position of a click to the center of a pixel. See [ElementaVersion.v2] for more info
return if ((Window.ofOrNull(this)?.version ?: ElementaVersion.v0) >= ElementaVersion.v2) {
val halfPixel = 0.5 / UResolution.scaleFactor
val window = Window.ofOrNull(this)
return if (window !=null && window.version >= ElementaVersion.v2) {
val halfPixel = 0.5 / window.resolutionManager.scaleFactor
mouseX + halfPixel to mouseY + halfPixel
} else {
mouseX to mouseY
Expand Down Expand Up @@ -1237,8 +1237,9 @@ abstract class UIComponent : Observable() {
/**
* Hints a number with respect to the current GUI scale.
*/
// @Deprecated("This relies on global states", replaceWith = ReplaceWith("guiHint(number, roundDown, component)"))
fun guiHint(number: Float, roundDown: Boolean): Float {
val factor = UResolution.scaleFactor.toFloat()
val factor = Window.resolutionManager.scaleFactor.toFloat()
return (number * factor).let {
if (roundDown) floor(it) else ceil(it)
} / factor
Expand All @@ -1247,19 +1248,33 @@ abstract class UIComponent : Observable() {
/**
* Hints a number with respect to the current GUI scale.
*/
fun guiHint(number: Double, roundDown: Boolean): Double {
val factor = UResolution.scaleFactor
fun guiHint(number: Float, roundDown: Boolean, component: UIComponent): Float {
val factor = component.resolutionManager.scaleFactor.toFloat()
return (number * factor).let {
if (roundDown) floor(it) else ceil(it)
} / factor
}

internal fun getMouseX(): Float {
return UMouse.Scaled.x.toFloat()
/**
* Hints a number with respect to the current GUI scale.
*/
// @Deprecated("This relies on global states", replaceWith = ReplaceWith("guiHint(number, roundDown, component)"))
fun guiHint(number: Double, roundDown: Boolean): Double {
val factor = Window.resolutionManager.scaleFactor
return (number * factor).let {
if (roundDown) floor(it) else ceil(it)
} / factor
}

internal fun getMouseY(): Float {
return UMouse.Scaled.y.toFloat()
/**
* Hints a number with respect to the current GUI scale.
*/
fun guiHint(number: Double, roundDown: Boolean, component: UIComponent): Double {
val factor = component.resolutionManager.scaleFactor
return (number * factor).let {
if (roundDown) floor(it) else ceil(it)
} / factor
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class VanillaFontRenderer : FontProvider {
override fun getStringHeight(string: String, pointSize: Float): Float =
UGraphics.getFontHeight().toFloat()

@Suppress("DEPRECATION")
override fun drawString(
matrixStack: UMatrixStack,
string: String,
Expand Down
Loading