Skip to content

Commit

Permalink
Add polyUI rendering implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
DeDiamondPro committed Dec 12, 2024
1 parent 73e0532 commit 6d876d5
Show file tree
Hide file tree
Showing 18 changed files with 1,900 additions and 1 deletion.
8 changes: 7 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ jetbrains_annotations = "24.0.0"
# Elementa
elementa = "619"

# PolyUI
polyui = "1.7.29"

[libraries]
# Commonmark
commonmark = { module = "org.commonmark:commonmark", version.ref = "commonmark" }
Expand All @@ -22,4 +25,7 @@ tagsoup = { module = "org.ccil.cowan.tagsoup:tagsoup", version.ref = "tagsoup" }
jetbrains_annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains_annotations" }

# Elementa
elementa = { module = "gg.essential:elementa-1.8.9-forge", version.ref = "elementa" }
elementa = { module = "gg.essential:elementa-1.8.9-forge", version.ref = "elementa" }

# PolyUI
polyui = { module = "org.polyfrost:polyui", version.ref = "polyui" }
63 changes: 63 additions & 0 deletions polyui/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

/*
* This file is part of MineMark
* Copyright (C) 2024 DeDiamondPro
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License Version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

plugins {
kotlin("jvm") version "2.0.21" // Same version as PolyUI
}

repositories {
maven("https://repo.polyfrost.org/releases")
}

kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_1_8)
}
}

dependencies {
implementation(libs.polyui)
implementation(libs.commonmark.ext.striketrough)
implementation(libs.commonmark.ext.tables)

testImplementation(kotlin("test"))

// Taken from PolyUI for testing since they don't publish any rendering implementations?
// version of LWJGL to use. Recommended to be latest.
val lwjglVersion = "3.3.3"

// list of modules that this implementation needs to work.
val lwjglModules = listOf("nanovg", "opengl", "stb", "glfw", null)

// list of platforms that this implementation will support.
val nativePlatforms = listOf("windows", "linux", "macos", "macos-arm64")

for (module in lwjglModules) {
val dep = if(module == null) "org.lwjgl:lwjgl:$lwjglVersion" else "org.lwjgl:lwjgl-$module:$lwjglVersion"
testImplementation(dep)
for (platform in nativePlatforms) {
testRuntimeOnly("$dep:natives-$platform")
}
}
testImplementation("org.apache.logging.log4j:log4j-api:2.24.1")
}

tasks.test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* This file is part of MineMark
* Copyright (C) 2024 DeDiamondPro
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License Version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package dev.dediamondpro.minemark.polyui

import dev.dediamondpro.minemark.MineMarkCore
import dev.dediamondpro.minemark.MineMarkCoreBuilder
import dev.dediamondpro.minemark.elements.Elements
import dev.dediamondpro.minemark.elements.MineMarkElement
import dev.dediamondpro.minemark.polyui.elements.*
import dev.dediamondpro.minemark.utils.MouseButton
import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension
import org.commonmark.ext.gfm.tables.TablesExtension
import org.polyfrost.polyui.color.Colors
import org.polyfrost.polyui.component.Component
import org.polyfrost.polyui.component.Drawable
import org.polyfrost.polyui.component.extensions.events
import org.polyfrost.polyui.event.Event
import org.polyfrost.polyui.unit.Align
import org.polyfrost.polyui.unit.AlignDefault
import org.polyfrost.polyui.unit.Vec2
import java.io.Reader

class MarkdownComponent(
markdown: MineMarkElement<MarkdownStyle, Drawable>,
vararg children: Component? = arrayOf(),
at: Vec2 = Vec2.ZERO,
alignment: Align = AlignDefault,
size: Vec2 = Vec2.ZERO,
visibleSize: Vec2 = Vec2.ZERO,
palette: Colors.Palette? = null,
focusable: Boolean = false,
) : Drawable(children = children, at, alignment, size, visibleSize, palette, focusable) {
constructor(
markdown: String,
vararg children: Component? = arrayOf(),
at: Vec2 = Vec2.ZERO,
alignment: Align = AlignDefault,
size: Vec2 = Vec2.ZERO,
visibleSize: Vec2 = Vec2.ZERO,
palette: Colors.Palette? = null,
focusable: Boolean = false,
style: MarkdownStyle = MarkdownStyle(),
core: MineMarkCore<MarkdownStyle, Drawable> = defaultCore,
) : this(core.parse(style, markdown), children = children, at, alignment, size, visibleSize, palette, focusable)

constructor(
markdown: Reader,
vararg children: Component? = arrayOf(),
at: Vec2 = Vec2.ZERO,
alignment: Align = AlignDefault,
size: Vec2 = Vec2.ZERO,
visibleSize: Vec2 = Vec2.ZERO,
palette: Colors.Palette? = null,
focusable: Boolean = false,
style: MarkdownStyle = MarkdownStyle(),
core: MineMarkCore<MarkdownStyle, Drawable> = defaultCore,
) : this(core.parse(style, markdown), children = children, at, alignment, size, visibleSize, palette, focusable)

val parsedMarkdown = markdown.apply {
addLayoutCallback(this@MarkdownComponent::layoutCallback)
}

init {
events {
Event.Mouse.Clicked(0).then {
parsedMarkdown.onMouseClicked(x, y, MouseButton.LEFT, it.x, it.y)
}
Event.Mouse.Clicked(1).then {
parsedMarkdown.onMouseClicked(x, y, MouseButton.RIGHT, it.x, it.y)
}
Event.Mouse.Clicked(2).then {
parsedMarkdown.onMouseClicked(x, y, MouseButton.MIDDLE, it.x, it.y)
}
}
}

override fun preRender(delta: Long) {
super.preRender(delta)
parsedMarkdown.beforeDraw(x, y, width, polyUI.mouseX, polyUI.mouseY, this)
}

override fun render() {
parsedMarkdown.draw(x, y, width, polyUI.mouseX, polyUI.mouseY, this)
if (parsedMarkdown.needsLayoutRegeneration(width)) {
// If our elements have decided the layout needs to change, we need to redraw
needsRedraw = true
}
}

private fun layoutCallback(newHeight: Float) {
height = newHeight
}

companion object {
private val defaultCore = MineMarkCore.builder<MarkdownStyle, Drawable>()
.addExtension(StrikethroughExtension.create())
.addExtension(TablesExtension.create())
.addPolyUIExtensions()
.build()
}
}

fun MineMarkCoreBuilder<MarkdownStyle, Drawable>.addPolyUIExtensions(): MineMarkCoreBuilder<MarkdownStyle, Drawable> {
return this.setTextElement(::MarkdownTextElement)
.addElement(Elements.IMAGE, ::MarkdownImageElement)
.addElement(Elements.HEADING, ::MarkdownHeadingElement)
.addElement(Elements.HORIZONTAL_RULE, ::MarkdownHorizontalRuleElement)
.addElement(Elements.CODE_BLOCK, ::MarkdownCodeBlockElement)
.addElement(Elements.BLOCKQUOTE, ::MarkdownBlockquoteElement)
.addElement(Elements.LIST_ELEMENT, ::MarkdownListElement)
.addElement(Elements.TABLE_CELL, ::MarkdownTableCellElement)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* This file is part of MineMark
* Copyright (C) 2024 DeDiamondPro
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License Version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package dev.dediamondpro.minemark.polyui

import dev.dediamondpro.minemark.providers.ImageProvider
import org.polyfrost.polyui.data.PolyImage
import java.util.function.Consumer

object MarkdownImageProvider : ImageProvider<PolyImage> {
override fun getImage(
src: String,
dimensionCallback: Consumer<ImageProvider.Dimension>,
imageCallback: Consumer<PolyImage>,
) {
imageCallback.accept(PolyImage(src))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* This file is part of MineMark
* Copyright (C) 2024 DeDiamondPro
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License Version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package dev.dediamondpro.minemark.polyui

import dev.dediamondpro.minemark.providers.DefaultBrowserProvider
import dev.dediamondpro.minemark.style.*
import org.polyfrost.polyui.PolyUI
import org.polyfrost.polyui.component.Drawable
import org.polyfrost.polyui.data.Font
import java.awt.Color

class MarkdownStyle
@JvmOverloads
constructor(
private val textStyle: MarkdownTextStyle = MarkdownTextStyle(),
private val paragraphStyle: ParagraphStyleConfig = ParagraphStyleConfig(6f),
private val linkStyle: LinkStyleConfig = LinkStyleConfig(Color(65, 105, 225), DefaultBrowserProvider.INSTANCE),
private val headerStyle: HeadingStyleConfig =
HeadingStyleConfig(
HeadingLevelStyleConfig(32f, 12f, true, LINE_COLOR, 2f, 5f),
HeadingLevelStyleConfig(24f, 10f, true, LINE_COLOR, 2f, 5f),
HeadingLevelStyleConfig(19f, 8f),
HeadingLevelStyleConfig(16f, 6f),
HeadingLevelStyleConfig(13f, 4f),
HeadingLevelStyleConfig(13f, 4f),
),
private val horizontalRuleStyle: HorizontalRuleStyleConfig = HorizontalRuleStyleConfig(2f, 4f, LINE_COLOR),
private val imageStyle: ImageStyleConfig = ImageStyleConfig(MarkdownImageProvider),
private val listStyle: ListStyleConfig = ListStyleConfig(32f, 6f),
private val blockquoteBlockStyle: BlockquoteStyleConfig = BlockquoteStyleConfig(6f, 4f, 2f, 10f, LINE_COLOR),
private val codeBlockStyle: CodeBlockStyle = CodeBlockStyle(),
private val tableStyle: TableStyleConfig = TableStyleConfig(
6f, 4f, 1f, LINE_COLOR, Color(0, 0, 0, 150), Color(0, 0, 0, 0)
),
) : Style {
override fun getTextStyle(): MarkdownTextStyle = textStyle

override fun getParagraphStyle(): ParagraphStyleConfig = paragraphStyle

override fun getLinkStyle(): LinkStyleConfig = linkStyle

override fun getHeadingStyle(): HeadingStyleConfig = headerStyle

override fun getHorizontalRuleStyle(): HorizontalRuleStyleConfig = horizontalRuleStyle

override fun getImageStyle(): ImageStyleConfig = imageStyle

override fun getListStyle(): ListStyleConfig = listStyle

override fun getBlockquoteStyle(): BlockquoteStyleConfig = blockquoteBlockStyle

override fun getCodeBlockStyle(): CodeBlockStyle = codeBlockStyle

override fun getTableStyle(): TableStyleConfig = tableStyle

companion object {
internal val LINE_COLOR = Color(80, 80, 80)
}
}

class MarkdownTextStyle(
val normalFont: Font = PolyUI.defaultFonts.medium,
val boldFont: Font = PolyUI.defaultFonts.bold,
val italicNormalFont: Font = PolyUI.defaultFonts.mediumItalic,
val italicBoldFont: Font = PolyUI.defaultFonts.boldItalic,
defaultFontSize: Float = 16f,
defaultTextColor: Color = Color.WHITE,
padding: Float = (normalFont.lineSpacing - 1f) * defaultFontSize / 2f,
) : TextStyleConfig(defaultFontSize, defaultTextColor, padding)

class CodeBlockStyle(
val codeFont: Font = PolyUI.monospaceFont,
inlinePaddingLeftRight: Float = 2f,
inlinePaddingTopBottom: Float = 1f,
blockOutsidePadding: Float = 6f,
blockInsidePadding: Float = 6f,
color: Color = MarkdownStyle.LINE_COLOR,
) : CodeBlockStyleConfig(inlinePaddingLeftRight, inlinePaddingTopBottom, blockOutsidePadding, blockInsidePadding, color)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* This file is part of MineMark
* Copyright (C) 2024 DeDiamondPro
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License Version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/


package dev.dediamondpro.minemark.polyui.elements

import dev.dediamondpro.minemark.LayoutStyle
import dev.dediamondpro.minemark.elements.Element
import dev.dediamondpro.minemark.elements.impl.BlockQuoteElement
import org.polyfrost.polyui.color.PolyColor
import dev.dediamondpro.minemark.polyui.MarkdownStyle
import org.polyfrost.polyui.color.toPolyColor
import org.polyfrost.polyui.component.Drawable
import org.polyfrost.polyui.renderer.Renderer
import org.xml.sax.Attributes
import java.awt.Color

class MarkdownBlockquoteElement(
style: MarkdownStyle,
layoutStyle: LayoutStyle,
parent: Element<MarkdownStyle, Drawable>?,
qName: String,
attributes: Attributes?,
) : BlockQuoteElement<MarkdownStyle, Drawable>(style, layoutStyle, parent, qName, attributes) {
private var polyColor: PolyColor = style.blockquoteStyle.blockColor.toPolyColor()

override fun drawBlock(
x: Float,
y: Float,
width: Float,
height: Float,
color: Color,
drawable: Drawable,
) {
drawable.renderer.rect(x, y, width, height, polyColor)
}
}
Loading

0 comments on commit 6d876d5

Please sign in to comment.