diff --git a/render-compose-html/build.gradle.kts b/render-compose-html/build.gradle.kts index ebb4ef3..f632408 100644 --- a/render-compose-html/build.gradle.kts +++ b/render-compose-html/build.gradle.kts @@ -16,6 +16,7 @@ kotlin { implementation(compose.runtime) implementation(libs.kotlinx.coroutines.core) api(projects.structures) + implementation(projects.renderWebCommon) } } } diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/HtmlComposeRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/HtmlComposeRenderer.kt index 0e9cfa5..9223af6 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/HtmlComposeRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/HtmlComposeRenderer.kt @@ -3,9 +3,11 @@ package ink.ui.render.compose.html import androidx.compose.runtime.Composable import ink.ui.render.compose.html.renderer.* import ink.ui.render.compose.html.renderer.CompositeElementRenderer +import ink.ui.render.web.gridTemplateColumns import ink.ui.structures.Positioning import ink.ui.structures.elements.UiElement import ink.ui.structures.layouts.* +import ink.ui.structures.render.RenderResult import org.jetbrains.compose.web.css.* import org.jetbrains.compose.web.dom.Div import org.jetbrains.compose.web.dom.Section @@ -41,7 +43,7 @@ class HtmlComposeRenderer( attrs = { classes("fixed-grid") style { - gridTemplateColumns((0 until uiLayout.columns).joinToString(" ") { "auto" }) + gridTemplateColumns(uiLayout.gridTemplateColumns) } } ) { @@ -50,7 +52,6 @@ class HtmlComposeRenderer( attrs = { style { gridColumn("span ${it.span}") - display(DisplayStyle.Flex) when (it.horizontalPositioning) { Positioning.Start -> { justifyContent(JustifyContent.Start) @@ -95,8 +96,9 @@ class HtmlComposeRenderer( @Composable fun renderElement(element: UiElement) { - when (uiRenderer.render(element, uiRenderer)) { - RenderResult.NotRendered -> throw IllegalArgumentException("No renderer registered for ${element::class.simpleName}") + when (val result = uiRenderer.render(element, uiRenderer)) { + RenderResult.Skipped -> throw IllegalArgumentException("No renderer registered for ${element::class.simpleName}") + is RenderResult.Failed -> throw result.exception RenderResult.Rendered -> {} } } diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ActivityRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ActivityRenderer.kt index 41a9c93..e89fbf2 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ActivityRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ActivityRenderer.kt @@ -1,10 +1,12 @@ package ink.ui.render.compose.html.renderer import androidx.compose.runtime.* +import ink.ui.render.web.toCssClass import ink.ui.structures.Sentiment import ink.ui.structures.elements.ProgressElement import ink.ui.structures.elements.ThrobberElement import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult import kotlinx.coroutines.delay import org.jetbrains.compose.web.css.* import org.jetbrains.compose.web.dom.* @@ -18,7 +20,7 @@ object ActivityRenderer: ElementRenderer { when (element) { is ProgressElement -> ProgressBar(element) is ThrobberElement -> Throbber(element.caption, element.sentiment) - else -> return RenderResult.NotRendered + else -> return RenderResult.Skipped } return RenderResult.Rendered diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ButtonRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ButtonRenderer.kt index 67964e4..2782074 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ButtonRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ButtonRenderer.kt @@ -1,37 +1,30 @@ package ink.ui.render.compose.html.renderer -import androidx.compose.runtime.Composable +import ink.ui.render.web.svgSrc +import ink.ui.render.web.toCssClass import ink.ui.structures.elements.ButtonElement -import ink.ui.structures.elements.UiElement import org.jetbrains.compose.web.dom.Button import org.jetbrains.compose.web.dom.Img import org.jetbrains.compose.web.dom.Text -object ButtonRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, parent: ElementRenderer): RenderResult { - if (element !is ButtonElement) return RenderResult.NotRendered - - Button( - attrs = { - classes(element.sentiment.toCssClass()) - onClick { - element.onClick() - } - } - ) { - val leadingSymbol = element.leadingSymbol - if (leadingSymbol != null) { - Img( - attrs = { - classes("icon", "svg-fill", element.sentiment.toCssClass()) - }, - src = leadingSymbol.svgSrc, - ) +val ButtonRenderer = renderer { element -> + Button( + attrs = { + classes(element.sentiment.toCssClass()) + onClick { + element.onClick() } - Text(element.text) } - - return RenderResult.Rendered + ) { + val leadingSymbol = element.leadingSymbol + if (leadingSymbol != null) { + Img( + attrs = { + classes("icon", "svg-fill", element.sentiment.toCssClass()) + }, + src = leadingSymbol.svgSrc, + ) + } + Text(element.text) } } diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/CheckBoxRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/CheckBoxRenderer.kt index 8ceb3e2..ad0a697 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/CheckBoxRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/CheckBoxRenderer.kt @@ -1,24 +1,15 @@ package ink.ui.render.compose.html.renderer -import androidx.compose.runtime.Composable import ink.ui.structures.elements.CheckBoxElement -import ink.ui.structures.elements.UiElement import org.jetbrains.compose.web.attributes.InputType import org.jetbrains.compose.web.dom.Input -object CheckBoxRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, parent: ElementRenderer): RenderResult { - if (element !is CheckBoxElement) return RenderResult.NotRendered - - Input( - type = InputType.Checkbox, - attrs = { - onClick { element.onClick() } - checked(element.checked) - } - ) - - return RenderResult.Rendered - } +val CheckBoxRenderer = renderer { element -> + Input( + type = InputType.Checkbox, + attrs = { + onClick { element.onClick() } + checked(element.checked) + } + ) } diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/CompositeElementRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/CompositeElementRenderer.kt index b42e597..5c17a6a 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/CompositeElementRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/CompositeElementRenderer.kt @@ -2,6 +2,8 @@ package ink.ui.render.compose.html.renderer import androidx.compose.runtime.Composable import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult +import ink.ui.structures.render.renderCatching internal class CompositeElementRenderer( private val renderers: List = emptyList(), @@ -9,10 +11,12 @@ internal class CompositeElementRenderer( @Composable override fun render(element: UiElement, parent: ElementRenderer): RenderResult { renderers.forEach { renderer -> - if (renderer.render(element, this) == RenderResult.Rendered) { - return RenderResult.Rendered + val result = renderCatching { renderer.render(element, this) } + + if (result != RenderResult.Skipped) { + return result } } - return RenderResult.NotRendered + return RenderResult.Skipped } } diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ElementRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ElementRenderer.kt index 332d2d6..ad3bc7e 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ElementRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ElementRenderer.kt @@ -2,6 +2,8 @@ package ink.ui.render.compose.html.renderer import androidx.compose.runtime.Composable import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult +import ink.ui.structures.render.renderCatching /** * Renders a UI element in compose. @@ -13,3 +15,19 @@ interface ElementRenderer { parent: ElementRenderer ): RenderResult } + +inline fun renderer( + crossinline render: @Composable (element: T) -> Unit +): ElementRenderer = object: ElementRenderer { + @Composable + override fun render( + element: UiElement, + parent: ElementRenderer + ): RenderResult { + if (element !is T) return RenderResult.Skipped + return renderCatching { + render(element) + RenderResult.Rendered + } + } +} diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/EmptyRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/EmptyRenderer.kt index 0241113..ed8eb82 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/EmptyRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/EmptyRenderer.kt @@ -1,17 +1,8 @@ package ink.ui.render.compose.html.renderer -import androidx.compose.runtime.Composable import ink.ui.structures.elements.EmptyElement -import ink.ui.structures.elements.UiElement import org.jetbrains.compose.web.dom.Span -object EmptyRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, parent: ElementRenderer): RenderResult { - if (element !is EmptyElement) return RenderResult.NotRendered - +val EmptyRenderer = renderer { element -> Span {} - - return RenderResult.Rendered - } } diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/IconRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/IconRenderer.kt index f6de572..5b37c29 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/IconRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/IconRenderer.kt @@ -1,22 +1,15 @@ package ink.ui.render.compose.html.renderer -import androidx.compose.runtime.Composable +import ink.ui.render.web.svgSrc +import ink.ui.render.web.toCssClass import ink.ui.structures.elements.IconElement -import ink.ui.structures.elements.UiElement import org.jetbrains.compose.web.dom.Img -object IconRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, parent: ElementRenderer): RenderResult { - if (element !is IconElement) return RenderResult.NotRendered - - Img( - src = element.symbol.svgSrc, - attrs = { - classes("icon", "svg-fill", element.sentiment.toCssClass()) - } - ) - - return RenderResult.Rendered - } +val IconRenderer = renderer { element -> + Img( + src = element.symbol.svgSrc, + attrs = { + classes("icon", "svg-fill", element.sentiment.toCssClass()) + } + ) } diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ListRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ListRenderer.kt index 90b0c88..968c285 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ListRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/ListRenderer.kt @@ -5,6 +5,7 @@ import ink.ui.structures.GroupingStyle import ink.ui.structures.Positioning import ink.ui.structures.elements.ElementList import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult import org.jetbrains.compose.web.css.JustifyContent import org.jetbrains.compose.web.css.justifyContent import org.jetbrains.compose.web.dom.Div @@ -12,7 +13,7 @@ import org.jetbrains.compose.web.dom.Div object ListRenderer: ElementRenderer { @Composable override fun render(element: UiElement, parent: ElementRenderer): RenderResult { - if (element !is ElementList) return RenderResult.NotRendered + if (element !is ElementList) return RenderResult.Skipped Div( attrs = { diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/MenuRowRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/MenuRowRenderer.kt index d0340a0..31336db 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/MenuRowRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/MenuRowRenderer.kt @@ -3,12 +3,13 @@ package ink.ui.render.compose.html.renderer import androidx.compose.runtime.Composable import ink.ui.structures.elements.MenuRowElement import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult import org.jetbrains.compose.web.dom.* object MenuRowRenderer: ElementRenderer { @Composable override fun render(element: UiElement, parent: ElementRenderer): RenderResult { - if (element !is MenuRowElement) return RenderResult.NotRendered + if (element !is MenuRowElement) return RenderResult.Skipped val elementOnClick = element.onClick Div( attrs = { diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/RenderResult.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/RenderResult.kt deleted file mode 100644 index 3ea8e14..0000000 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/RenderResult.kt +++ /dev/null @@ -1,6 +0,0 @@ -package ink.ui.render.compose.html.renderer - -enum class RenderResult { - Rendered, - NotRendered, -} diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/Sentiments.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/Sentiments.kt deleted file mode 100644 index 46517c0..0000000 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/Sentiments.kt +++ /dev/null @@ -1,14 +0,0 @@ -package ink.ui.render.compose.html.renderer - -import ink.ui.structures.Sentiment - -fun Sentiment.toCssClass(): String { - return when (this) { - Sentiment.Nominal -> "nominal" - Sentiment.Primary -> "primary" - Sentiment.Positive -> "positive" - Sentiment.Negative -> "negative" - Sentiment.Caution -> "caution" - Sentiment.Idle -> "idle" - } -} diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/SpinnerRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/SpinnerRenderer.kt index 2cd95e1..86233af 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/SpinnerRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/SpinnerRenderer.kt @@ -1,54 +1,45 @@ package ink.ui.render.compose.html.renderer -import androidx.compose.runtime.Composable import ink.ui.structures.elements.SpinnerElement -import ink.ui.structures.elements.UiElement import org.jetbrains.compose.web.attributes.disabled import org.jetbrains.compose.web.dom.* -object SpinnerRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, parent: ElementRenderer): RenderResult { - if (element !is SpinnerElement) return RenderResult.NotRendered - - Div( +val SpinnerRenderer = renderer { element -> + Div( + attrs = { + classes("spinner") + } + ) { + Button( attrs = { - classes("spinner") - } - ) { - Button( - attrs = { - if (element.hasPreviousValue) { - onClick { element.onPreviousValue } - } else { - disabled() - } + if (element.hasPreviousValue) { + onClick { element.onPreviousValue } + } else { + disabled() } - ) { - Text("<") } + ) { + Text("<") + } - Span( - attrs = { - classes("spinner-value") - } - ) { - Text(element.value) + Span( + attrs = { + classes("spinner-value") } + ) { + Text(element.value) + } - Button( - attrs = { - if (element.hasNextValue) { - onClick { element.onNextValue } - } else { - disabled() - } + Button( + attrs = { + if (element.hasNextValue) { + onClick { element.onNextValue } + } else { + disabled() } - ) { - Text(">") } + ) { + Text(">") } - - return RenderResult.Rendered } } diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/StatusRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/StatusRenderer.kt index 8c402e3..4c0a897 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/StatusRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/StatusRenderer.kt @@ -1,34 +1,27 @@ package ink.ui.render.compose.html.renderer -import androidx.compose.runtime.Composable +import ink.ui.render.web.RESOURCE_SVG_PATH +import ink.ui.render.web.toCssClass import ink.ui.structures.elements.StatusIndicatorElement -import ink.ui.structures.elements.UiElement import org.jetbrains.compose.web.dom.Div import org.jetbrains.compose.web.dom.Img import org.jetbrains.compose.web.dom.Span import org.jetbrains.compose.web.dom.Text -object StatusRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, parent: ElementRenderer): RenderResult { - if (element !is StatusIndicatorElement) return RenderResult.NotRendered - - Div( +val StatusRenderer = renderer { element -> + Div( + attrs = { + classes("status-indicator") + } + ) { + Img( attrs = { - classes("status-indicator") - } - ) { - Img( - attrs = { - classes("status-indicator-blip", "svg-fill", element.sentiment.toCssClass()) - }, - src = "$PATH/blip.svg", - ) - Span { - Text(element.text) - } + classes("status-indicator-blip", "svg-fill", element.sentiment.toCssClass()) + }, + src = "$RESOURCE_SVG_PATH/blip.svg", + ) + Span { + Text(element.text) } - - return RenderResult.Rendered } } diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/Symbols.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/Symbols.kt deleted file mode 100644 index 7fd082c..0000000 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/Symbols.kt +++ /dev/null @@ -1,33 +0,0 @@ -package ink.ui.render.compose.html.renderer - -import ink.ui.structures.Symbol - -internal const val PATH = "composeResources/com.inkapplications.ui.render_compose_html.generated.resources/svg" - -val Symbol.svgSrc: String get() = when (this) { - Symbol.Add -> "add.svg" - Symbol.Alarm -> "alarm.svg" - Symbol.AlarmOff -> "alarm_off.svg" - Symbol.ArrowBackward -> "arrow_backward.svg" - Symbol.ArrowForward -> "arrow_forward.svg" - Symbol.Bed -> "bed.svg" - Symbol.Caution -> "caution.svg" - Symbol.Close -> "close.svg" - Symbol.Cloud -> "cloud.svg" - Symbol.Done -> "done.svg" - Symbol.Edit -> "edit.svg" - Symbol.Error -> "error.svg" - Symbol.Group -> "group.svg" - Symbol.House -> "house.svg" - Symbol.Lock -> "lock.svg" - Symbol.LockOpen -> "lock_open.svg" - Symbol.Moon -> "moon.svg" - Symbol.Movie -> "movie.svg" - Symbol.Person -> "person.svg" - Symbol.Rain -> "rain.svg" - Symbol.Remove -> "remove.svg" - Symbol.Sunshine -> "sunshine.svg" - Symbol.Temperature -> "temperature.svg" - Symbol.Water -> "water.svg" - else -> "unknown.svg" -}.let { "$PATH/$it" } diff --git a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/TextRenderer.kt b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/TextRenderer.kt index 608e824..8c813dc 100644 --- a/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/TextRenderer.kt +++ b/render-compose-html/src/jsMain/kotlin/ink/ui/render/compose/html/renderer/TextRenderer.kt @@ -3,48 +3,40 @@ package ink.ui.render.compose.html.renderer import androidx.compose.runtime.Composable import ink.ui.structures.TextStyle import ink.ui.structures.elements.TextElement -import ink.ui.structures.elements.UiElement import org.jetbrains.compose.web.dom.* -object TextRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, parent: ElementRenderer): RenderResult { - if (element !is TextElement) return RenderResult.NotRendered - - when (element.style) { - TextStyle.H1 -> H1 { - Text(element.text) - } - TextStyle.H2 -> H2 { - Text(element.text) - } - TextStyle.H3 -> H3 { - Text(element.text) - } - TextStyle.Body -> P( - attrs = {} - ) { - TextWithBreaks(element.text) - } - TextStyle.Caption -> P( - attrs = { - classes("caption") - } - ) { - TextWithBreaks(element.text) +val TextRenderer = renderer { element -> + when (element.style) { + TextStyle.H1 -> H1 { + Text(element.text) + } + TextStyle.H2 -> H2 { + Text(element.text) + } + TextStyle.H3 -> H3 { + Text(element.text) + } + TextStyle.Body -> P( + attrs = {} + ) { + TextWithBreaks(element.text) + } + TextStyle.Caption -> P( + attrs = { + classes("caption") } + ) { + TextWithBreaks(element.text) } - - return RenderResult.Rendered } +} - @Composable - private fun TextWithBreaks(text: String) { - text.split("\n").forEachIndexed { index, line -> - if (index > 0) { - Br() - } - Text(line) +@Composable +private fun TextWithBreaks(text: String) { + text.split("\n").forEachIndexed { index, line -> + if (index > 0) { + Br() } + Text(line) } } diff --git a/render-compose/api/android/render-compose.api b/render-compose/api/android/render-compose.api index 511dda7..0267baf 100644 --- a/render-compose/api/android/render-compose.api +++ b/render-compose/api/android/render-compose.api @@ -11,27 +11,7 @@ public final class ink/ui/render/compose/elements/ButtonKt { } public abstract interface class ink/ui/render/compose/renderer/ElementRenderer { - public abstract fun render (Link/ui/structures/elements/UiElement;Link/ui/render/compose/theme/ComposeRenderTheme;Link/ui/render/compose/renderer/ElementRenderer;Landroidx/compose/runtime/Composer;I)Link/ui/render/compose/renderer/RenderResult; -} - -public final class ink/ui/render/compose/renderer/IconRenderer : ink/ui/render/compose/renderer/ElementRenderer { - public static final field $stable I - public static final field INSTANCE Link/ui/render/compose/renderer/IconRenderer; - public fun render (Link/ui/structures/elements/UiElement;Link/ui/render/compose/theme/ComposeRenderTheme;Link/ui/render/compose/renderer/ElementRenderer;Landroidx/compose/runtime/Composer;I)Link/ui/render/compose/renderer/RenderResult; -} - -public final class ink/ui/render/compose/renderer/RenderResult : java/lang/Enum { - public static final field NotRendered Link/ui/render/compose/renderer/RenderResult; - public static final field Rendered Link/ui/render/compose/renderer/RenderResult; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public static fun valueOf (Ljava/lang/String;)Link/ui/render/compose/renderer/RenderResult; - public static fun values ()[Link/ui/render/compose/renderer/RenderResult; -} - -public final class ink/ui/render/compose/renderer/SpinnerRenderer : ink/ui/render/compose/renderer/ElementRenderer { - public static final field $stable I - public static final field INSTANCE Link/ui/render/compose/renderer/SpinnerRenderer; - public fun render (Link/ui/structures/elements/UiElement;Link/ui/render/compose/theme/ComposeRenderTheme;Link/ui/render/compose/renderer/ElementRenderer;Landroidx/compose/runtime/Composer;I)Link/ui/render/compose/renderer/RenderResult; + public abstract fun render (Link/ui/structures/elements/UiElement;Link/ui/render/compose/theme/ComposeRenderTheme;Link/ui/render/compose/renderer/ElementRenderer;Landroidx/compose/runtime/Composer;I)Link/ui/structures/render/RenderResult; } public final class ink/ui/render/compose/renderer/SymbolsKt { diff --git a/render-compose/api/jvm/render-compose.api b/render-compose/api/jvm/render-compose.api index 511dda7..0267baf 100644 --- a/render-compose/api/jvm/render-compose.api +++ b/render-compose/api/jvm/render-compose.api @@ -11,27 +11,7 @@ public final class ink/ui/render/compose/elements/ButtonKt { } public abstract interface class ink/ui/render/compose/renderer/ElementRenderer { - public abstract fun render (Link/ui/structures/elements/UiElement;Link/ui/render/compose/theme/ComposeRenderTheme;Link/ui/render/compose/renderer/ElementRenderer;Landroidx/compose/runtime/Composer;I)Link/ui/render/compose/renderer/RenderResult; -} - -public final class ink/ui/render/compose/renderer/IconRenderer : ink/ui/render/compose/renderer/ElementRenderer { - public static final field $stable I - public static final field INSTANCE Link/ui/render/compose/renderer/IconRenderer; - public fun render (Link/ui/structures/elements/UiElement;Link/ui/render/compose/theme/ComposeRenderTheme;Link/ui/render/compose/renderer/ElementRenderer;Landroidx/compose/runtime/Composer;I)Link/ui/render/compose/renderer/RenderResult; -} - -public final class ink/ui/render/compose/renderer/RenderResult : java/lang/Enum { - public static final field NotRendered Link/ui/render/compose/renderer/RenderResult; - public static final field Rendered Link/ui/render/compose/renderer/RenderResult; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public static fun valueOf (Ljava/lang/String;)Link/ui/render/compose/renderer/RenderResult; - public static fun values ()[Link/ui/render/compose/renderer/RenderResult; -} - -public final class ink/ui/render/compose/renderer/SpinnerRenderer : ink/ui/render/compose/renderer/ElementRenderer { - public static final field $stable I - public static final field INSTANCE Link/ui/render/compose/renderer/SpinnerRenderer; - public fun render (Link/ui/structures/elements/UiElement;Link/ui/render/compose/theme/ComposeRenderTheme;Link/ui/render/compose/renderer/ElementRenderer;Landroidx/compose/runtime/Composer;I)Link/ui/render/compose/renderer/RenderResult; + public abstract fun render (Link/ui/structures/elements/UiElement;Link/ui/render/compose/theme/ComposeRenderTheme;Link/ui/render/compose/renderer/ElementRenderer;Landroidx/compose/runtime/Composer;I)Link/ui/structures/render/RenderResult; } public final class ink/ui/render/compose/renderer/SymbolsKt { diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/ComposeRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/ComposeRenderer.kt index 0134963..9dd580e 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/ComposeRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/ComposeRenderer.kt @@ -18,6 +18,7 @@ import ink.ui.render.compose.theme.defaultTheme import ink.ui.structures.Positioning import ink.ui.structures.elements.UiElement import ink.ui.structures.layouts.* +import ink.ui.structures.render.RenderResult /** * Renders UI Layouts and elements using Compose. @@ -118,8 +119,9 @@ class ComposeRenderer( @Composable fun renderElement(element: UiElement) { - when (uiRenderer.render(element, theme, uiRenderer)) { - RenderResult.NotRendered -> throw IllegalArgumentException("No renderer registered for ${element::class.simpleName}") + when (val result = uiRenderer.render(element, theme, uiRenderer)) { + RenderResult.Skipped -> throw IllegalArgumentException("No renderer registered for ${element::class.simpleName}") + is RenderResult.Failed -> throw result.exception RenderResult.Rendered -> {} } } diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ActivityRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ActivityRenderer.kt index 22e4e93..39a7bc7 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ActivityRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ActivityRenderer.kt @@ -7,6 +7,7 @@ import ink.ui.render.compose.theme.ComposeRenderTheme import ink.ui.structures.elements.ProgressElement import ink.ui.structures.elements.ThrobberElement import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult internal object ActivityRenderer: ElementRenderer { @Composable @@ -21,7 +22,7 @@ internal object ActivityRenderer: ElementRenderer { sentiment = element.sentiment, theme = theme ) - else -> return RenderResult.NotRendered + else -> return RenderResult.Skipped } return RenderResult.Rendered } diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ButtonRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ButtonRenderer.kt index 58670f3..0420e80 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ButtonRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ButtonRenderer.kt @@ -1,26 +1,16 @@ package ink.ui.render.compose.renderer -import androidx.compose.runtime.Composable import ink.ui.render.compose.elements.Button -import ink.ui.render.compose.theme.ComposeRenderTheme import ink.ui.structures.elements.ButtonElement -import ink.ui.structures.elements.UiElement -internal object ButtonRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, theme: ComposeRenderTheme, parent: ElementRenderer): RenderResult { - when (element) { - is ButtonElement -> Button( - text = element.text, - sentiment = element.sentiment, - latching = element.latchOnPress, - leadingSymbol = element.leadingSymbol, - trailingSymbol = element.trailingSymbol, - theme = theme, - onClick = element.onClick, - ) - else -> return RenderResult.NotRendered - } - return RenderResult.Rendered - } +internal val ButtonRenderer = renderer { theme, element -> + Button( + text = element.text, + sentiment = element.sentiment, + latching = element.latchOnPress, + leadingSymbol = element.leadingSymbol, + trailingSymbol = element.trailingSymbol, + theme = theme, + onClick = element.onClick, + ) } diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/CheckboxRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/CheckboxRenderer.kt index ac2c0a6..c43eff2 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/CheckboxRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/CheckboxRenderer.kt @@ -5,49 +5,39 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import ink.ui.render.compose.theme.ComposeRenderTheme import ink.ui.structures.elements.CheckBoxElement -import ink.ui.structures.elements.UiElement -internal object CheckboxRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, theme: ComposeRenderTheme, parent: ElementRenderer): RenderResult { - if (element !is CheckBoxElement) return RenderResult.NotRendered +internal val CheckboxRenderer = renderer { theme, element -> + Row( + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clickable(onClick = element.onClick) + .padding(theme.spacing.clickSafety / 2) + ) { + BasicText( + text = "[", + style = theme.typography.uiGlyph.copy( + color = theme.colors.foreground, + ), + ) - Row( - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .clickable(onClick = element.onClick) - .padding(theme.spacing.clickSafety / 2) - ) { - BasicText( - text = "[", - style = theme.typography.uiGlyph.copy( - color = theme.colors.foreground, - ), - ) + val centerChar = if (element.checked) "x" else " " + val centerColor = theme.colors.foreground - val centerChar = if (element.checked) "x" else " " - val centerColor = theme.colors.foreground - - BasicText( - text = centerChar, - style = theme.typography.uiGlyph.copy( - color = centerColor, - ), - ) - BasicText( - text = "]", - style = theme.typography.uiGlyph.copy( - color = theme.colors.foreground, - ), - ) - } - - return RenderResult.Rendered + BasicText( + text = centerChar, + style = theme.typography.uiGlyph.copy( + color = centerColor, + ), + ) + BasicText( + text = "]", + style = theme.typography.uiGlyph.copy( + color = theme.colors.foreground, + ), + ) } } diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/CompositeElementRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/CompositeElementRenderer.kt index 96f1ac6..0d36c76 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/CompositeElementRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/CompositeElementRenderer.kt @@ -3,6 +3,8 @@ package ink.ui.render.compose.renderer import androidx.compose.runtime.Composable import ink.ui.render.compose.theme.ComposeRenderTheme import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult +import ink.ui.structures.render.renderCatching internal class CompositeElementRenderer( private val renderers: List = emptyList(), @@ -10,10 +12,12 @@ internal class CompositeElementRenderer( @Composable override fun render(element: UiElement, theme: ComposeRenderTheme, parent: ElementRenderer): RenderResult { renderers.forEach { renderer -> - if (renderer.render(element, theme, this) == RenderResult.Rendered) { - return RenderResult.Rendered + val result = renderCatching { renderer.render(element, theme, this) } + + if (result != RenderResult.Skipped) { + return result } } - return RenderResult.NotRendered + return RenderResult.Skipped } } diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ElementRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ElementRenderer.kt index 275015a..9b90e05 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ElementRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ElementRenderer.kt @@ -3,6 +3,8 @@ package ink.ui.render.compose.renderer import androidx.compose.runtime.Composable import ink.ui.render.compose.theme.ComposeRenderTheme import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult +import ink.ui.structures.render.renderCatching /** * Renders a UI element in compose. @@ -15,3 +17,20 @@ interface ElementRenderer { parent: ElementRenderer ): RenderResult } + +inline fun renderer( + crossinline render: @Composable (theme: ComposeRenderTheme, element: T) -> Unit +): ElementRenderer = object: ElementRenderer { + @Composable + override fun render( + element: UiElement, + theme: ComposeRenderTheme, + parent: ElementRenderer + ): RenderResult { + if (element !is T) return RenderResult.Skipped + return renderCatching { + render(theme, element) + RenderResult.Rendered + } + } +} diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/EmptyRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/EmptyRenderer.kt index 7684a4e..9c16235 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/EmptyRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/EmptyRenderer.kt @@ -1,19 +1,9 @@ package ink.ui.render.compose.renderer import androidx.compose.foundation.layout.Spacer -import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import ink.ui.render.compose.theme.ComposeRenderTheme import ink.ui.structures.elements.EmptyElement -import ink.ui.structures.elements.UiElement -internal object EmptyRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, theme: ComposeRenderTheme, parent: ElementRenderer): RenderResult { - when (element) { - is EmptyElement -> Spacer(Modifier) - else -> return RenderResult.NotRendered - } - return RenderResult.Rendered - } +internal val EmptyRenderer = renderer { theme, element -> + Spacer(Modifier) } diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/IconRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/IconRenderer.kt index 8552b0f..9955bb7 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/IconRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/IconRenderer.kt @@ -1,24 +1,14 @@ package ink.ui.render.compose.renderer import androidx.compose.foundation.Image -import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.ColorFilter -import ink.ui.render.compose.theme.ComposeRenderTheme import ink.ui.structures.elements.IconElement -import ink.ui.structures.elements.UiElement import org.jetbrains.compose.resources.painterResource -object IconRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, theme: ComposeRenderTheme, parent: ElementRenderer): RenderResult { - if (element !is IconElement) return RenderResult.NotRendered - - Image( - painterResource(element.symbol.resource), - colorFilter = ColorFilter.tint(theme.colors.forSentiment(element.sentiment)), - contentDescription = null, - ) - - return RenderResult.Rendered - } +internal val IconRenderer = renderer { theme, element -> + Image( + painterResource(element.symbol.resource), + colorFilter = ColorFilter.tint(theme.colors.forSentiment(element.sentiment)), + contentDescription = null, + ) } diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ListRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ListRenderer.kt index f14a7a3..5039cc4 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ListRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/ListRenderer.kt @@ -11,6 +11,7 @@ import ink.ui.structures.Positioning import ink.ui.structures.GroupingStyle import ink.ui.structures.elements.ElementList import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult internal object ListRenderer: ElementRenderer { @Composable @@ -34,7 +35,7 @@ internal object ListRenderer: ElementRenderer { } } } - else -> return RenderResult.NotRendered + else -> return RenderResult.Skipped } return RenderResult.Rendered } diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/MenuRowRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/MenuRowRenderer.kt index d6d2c47..d94b803 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/MenuRowRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/MenuRowRenderer.kt @@ -13,11 +13,12 @@ import androidx.compose.ui.Modifier import ink.ui.render.compose.theme.ComposeRenderTheme import ink.ui.structures.elements.MenuRowElement import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult internal object MenuRowRenderer: ElementRenderer { @Composable override fun render(element: UiElement, theme: ComposeRenderTheme, parent: ElementRenderer): RenderResult { - if (element !is MenuRowElement) return RenderResult.NotRendered + if (element !is MenuRowElement) return RenderResult.Skipped val onClick = element.onClick Row( diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/RenderResult.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/RenderResult.kt deleted file mode 100644 index 2a24c4c..0000000 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/RenderResult.kt +++ /dev/null @@ -1,6 +0,0 @@ -package ink.ui.render.compose.renderer - -enum class RenderResult { - Rendered, - NotRendered, -} diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/SpinnerRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/SpinnerRenderer.kt index fa3137a..3a00412 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/SpinnerRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/SpinnerRenderer.kt @@ -3,54 +3,44 @@ package ink.ui.render.compose.renderer import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import ink.ui.render.compose.theme.ComposeRenderTheme import ink.ui.structures.elements.SpinnerElement -import ink.ui.structures.elements.UiElement -object SpinnerRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, theme: ComposeRenderTheme, parent: ElementRenderer): RenderResult { - if (element !is SpinnerElement) return RenderResult.NotRendered - - Row( - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, +internal val SpinnerRenderer = renderer { theme, element -> + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = Modifier + .let { if (element.hasPreviousValue) it.clickable { element.onPreviousValue() } else it } + .padding(theme.spacing.clickSafety / 2) ) { - Box( - modifier = Modifier - .let { if (element.hasPreviousValue) it.clickable { element.onPreviousValue() } else it } - .padding(theme.spacing.clickSafety / 2) - ) { - BasicText( - text = "-", - style = theme.typography.uiGlyph.copy( - color = if (element.hasPreviousValue) theme.colors.foreground else theme.colors.inactive, - ), - ) - } BasicText( - text = element.value, - style = theme.typography.body.copy( - color = theme.colors.primary, + text = "-", + style = theme.typography.uiGlyph.copy( + color = if (element.hasPreviousValue) theme.colors.foreground else theme.colors.inactive, + ), + ) + } + BasicText( + text = element.value, + style = theme.typography.body.copy( + color = theme.colors.primary, + ), + ) + Box( + modifier = Modifier + .let { if (element.hasNextValue) it.clickable { element.onNextValue() } else it } + .padding(theme.spacing.clickSafety / 2) + ) { + BasicText( + text = "+", + style = theme.typography.uiGlyph.copy( + color = if (element.hasNextValue) theme.colors.foreground else theme.colors.inactive, ), ) - Box( - modifier = Modifier - .let { if (element.hasNextValue) it.clickable { element.onNextValue() } else it } - .padding(theme.spacing.clickSafety / 2) - ) { - BasicText( - text = "+", - style = theme.typography.uiGlyph.copy( - color = if (element.hasNextValue) theme.colors.foreground else theme.colors.inactive, - ), - ) - } } - - return RenderResult.Rendered } } diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/StatusIndicatorRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/StatusIndicatorRenderer.kt index 6f3dccc..1255ab8 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/StatusIndicatorRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/StatusIndicatorRenderer.kt @@ -1,22 +1,12 @@ package ink.ui.render.compose.renderer -import androidx.compose.runtime.Composable import ink.ui.render.compose.elements.StatusIndicator -import ink.ui.render.compose.theme.ComposeRenderTheme import ink.ui.structures.elements.StatusIndicatorElement -import ink.ui.structures.elements.UiElement -internal object StatusIndicatorRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, theme: ComposeRenderTheme, parent: ElementRenderer): RenderResult { - when (element) { - is StatusIndicatorElement -> StatusIndicator( - text = element.text, - sentiment = element.sentiment, - theme = theme, - ) - else -> return RenderResult.NotRendered - } - return RenderResult.Rendered - } +internal val StatusIndicatorRenderer = renderer { theme, element -> + StatusIndicator( + text = element.text, + sentiment = element.sentiment, + theme = theme, + ) } diff --git a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/TextRenderer.kt b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/TextRenderer.kt index 477eb1d..6119357 100644 --- a/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/TextRenderer.kt +++ b/render-compose/src/commonMain/kotlin/ink/ui/render/compose/renderer/TextRenderer.kt @@ -1,28 +1,18 @@ package ink.ui.render.compose.renderer import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.Composable -import ink.ui.render.compose.theme.ComposeRenderTheme import ink.ui.structures.TextStyle import ink.ui.structures.elements.TextElement -import ink.ui.structures.elements.UiElement -internal object TextRenderer: ElementRenderer { - @Composable - override fun render(element: UiElement, theme: ComposeRenderTheme, parent: ElementRenderer): RenderResult { - when (element) { - is TextElement -> BasicText( - text = element.text, - style = when (element.style) { - TextStyle.H1 -> theme.typography.h1 - TextStyle.H2 -> theme.typography.h2 - TextStyle.H3 -> theme.typography.h3 - TextStyle.Body -> theme.typography.body - TextStyle.Caption -> theme.typography.caption - }.copy(color = theme.colors.foreground), - ) - else -> return RenderResult.NotRendered - } - return RenderResult.Rendered - } +internal val TextRenderer = renderer { theme, element -> + BasicText( + text = element.text, + style = when (element.style) { + TextStyle.H1 -> theme.typography.h1 + TextStyle.H2 -> theme.typography.h2 + TextStyle.H3 -> theme.typography.h3 + TextStyle.Body -> theme.typography.body + TextStyle.Caption -> theme.typography.caption + }.copy(color = theme.colors.foreground), + ) } diff --git a/render-static-html/api/render-static-html.api b/render-static-html/api/render-static-html.api new file mode 100644 index 0000000..436ce77 --- /dev/null +++ b/render-static-html/api/render-static-html.api @@ -0,0 +1,60 @@ +public final class ink/ui/render/statichtml/HtmlRenderer { + public fun ()V + public final fun renderDocument (Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;ZZ)Ljava/lang/String; + public static synthetic fun renderDocument$default (Link/ui/render/statichtml/HtmlRenderer;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;ZZILjava/lang/Object;)Ljava/lang/String; + public final fun renderElement (Link/ui/structures/elements/UiElement;)Lkotlin/jvm/functions/Function1; + public final fun renderLayout (Link/ui/structures/layouts/UiLayout;)Lkotlin/jvm/functions/Function1; +} + +public final class ink/ui/render/statichtml/InkUiConfig : kotlin/script/experimental/api/ScriptCompilationConfiguration { + public static final field INSTANCE Link/ui/render/statichtml/InkUiConfig; +} + +public abstract class ink/ui/render/statichtml/InkUiScript { + public static final field Companion Link/ui/render/statichtml/InkUiScript$Companion; + public fun ()V + public final fun addBody (Link/ui/structures/layouts/UiLayout;)V + public final fun addBody (Lkotlin/jvm/functions/Function1;)V + public final fun addPageHeader (Link/ui/structures/elements/UiElement;)V + public final fun addPageHeader (Lkotlin/jvm/functions/Function1;)V + public final fun addStyle (Ljava/lang/String;)V + public final fun getContentBreak ()Z + public final fun getResourceBaseUrl ()Ljava/lang/String; + public final fun getSectioned ()Z + public final fun getTitle ()Ljava/lang/String; + public final fun setContentBreak (Z)V + public final fun setResourceBaseUrl (Ljava/lang/String;)V + public final fun setSectioned (Z)V + public final fun setTitle (Ljava/lang/String;)V +} + +public final class ink/ui/render/statichtml/InkUiScript$Companion { +} + +public final class ink/ui/render/statichtml/MainKt { + public static final fun main ([Ljava/lang/String;)V +} + +public abstract interface class ink/ui/render/statichtml/renderer/ElementRenderer { + public abstract fun render (Lkotlinx/html/TagConsumer;Link/ui/structures/elements/UiElement;Link/ui/render/statichtml/renderer/ElementRenderer;)Link/ui/structures/render/RenderResult; +} + +public final class ink/ui/render/statichtml/renderer/ElementRendererKt { + public static final fun renderWith (Link/ui/structures/elements/UiElement;Lkotlinx/html/TagConsumer;Link/ui/render/statichtml/renderer/ElementRenderer;Link/ui/render/statichtml/renderer/ElementRenderer;)Link/ui/structures/render/RenderResult; + public static synthetic fun renderWith$default (Link/ui/structures/elements/UiElement;Lkotlinx/html/TagConsumer;Link/ui/render/statichtml/renderer/ElementRenderer;Link/ui/render/statichtml/renderer/ElementRenderer;ILjava/lang/Object;)Link/ui/structures/render/RenderResult; +} + +public final class ink/ui/render/statichtml/renderer/FormattedTextRendererKt { + public static final fun getFormattedTextRenderer ()Link/ui/render/statichtml/renderer/ElementRenderer; +} + +public final class ink/ui/render/statichtml/renderer/ListRenderer : ink/ui/render/statichtml/renderer/ElementRenderer { + public static final field INSTANCE Link/ui/render/statichtml/renderer/ListRenderer; + public fun render (Lkotlinx/html/TagConsumer;Link/ui/structures/elements/UiElement;Link/ui/render/statichtml/renderer/ElementRenderer;)Link/ui/structures/render/RenderResult; +} + +public final class ink/ui/render/statichtml/renderer/StatusRenderer : ink/ui/render/statichtml/renderer/ElementRenderer { + public fun (Ljava/lang/String;)V + public fun render (Lkotlinx/html/TagConsumer;Link/ui/structures/elements/UiElement;Link/ui/render/statichtml/renderer/ElementRenderer;)Link/ui/structures/render/RenderResult; +} + diff --git a/render-static-html/build.gradle.kts b/render-static-html/build.gradle.kts index 88d509c..969abdc 100644 --- a/render-static-html/build.gradle.kts +++ b/render-static-html/build.gradle.kts @@ -1,6 +1,7 @@ plugins { application kotlin("jvm") + id("ink.publishing") } application { @@ -15,4 +16,5 @@ dependencies { implementation(libs.kotlin.scripting.jvm.host) implementation(libs.kotlinx.html) api(projects.structures) + implementation(projects.renderWebCommon) } diff --git a/render-static-html/src/main/java/ink/ui/render/statichtml/HtmlRenderer.kt b/render-static-html/src/main/java/ink/ui/render/statichtml/HtmlRenderer.kt index bf53b83..beb5485 100644 --- a/render-static-html/src/main/java/ink/ui/render/statichtml/HtmlRenderer.kt +++ b/render-static-html/src/main/java/ink/ui/render/statichtml/HtmlRenderer.kt @@ -3,6 +3,9 @@ package ink.ui.render.statichtml import ink.ui.render.statichtml.renderer.* import ink.ui.render.statichtml.renderer.CompositeElementRenderer import ink.ui.render.statichtml.renderer.TextRenderer +import ink.ui.render.web.gridTemplateColumns +import ink.ui.structures.Positioning +import ink.ui.structures.elements.ElementList import ink.ui.structures.elements.UiElement import ink.ui.structures.layouts.* import kotlinx.html.* @@ -13,6 +16,7 @@ class HtmlRenderer { private val builtInRenderers = listOf( ListRenderer, TextRenderer, + FormattedTextRenderer, StatusRenderer(null) ) private val renderer = CompositeElementRenderer(builtInRenderers) @@ -25,17 +29,51 @@ class HtmlRenderer { ) } - fun renderLayout(uiLayout: UiLayout): TagConsumer<*>.() -> Unit = { + fun renderLayout(uiLayout: UiLayout): TagConsumer<*>.() -> Unit = consumer@ { when (uiLayout) { - is CenteredElementLayout -> TODO() - is FixedGridLayout -> TODO() - is PageLayout -> section { - with(renderer) { - render(uiLayout.body, renderer) - } + is CenteredElementLayout -> section("element-center") { + renderWith( + element = uiLayout.body, + consumer = consumer, + renderer = renderer, + ) } + is FixedGridLayout -> section("fixed-grid") { + attributes["style"] = "grid-template-columns: ${uiLayout.gridTemplateColumns};" - is ScrollingListLayout -> TODO() + uiLayout.items.forEach { item -> + div { + val horizontalPosition = when (item.horizontalPositioning) { + Positioning.Start -> "start" + Positioning.Center -> "center" + } + val verticalPosition = when (item.verticalPositioning) { + Positioning.Start -> "start" + Positioning.Center -> "center" + } + attributes["style"] = "grid-column: span ${item.span}; align-items: $verticalPosition; justify-content: $horizontalPosition;" + renderWith( + element = item.body, + consumer = consumer, + renderer = renderer, + ) + } + } + } + is PageLayout -> section { + renderWith( + element = uiLayout.body, + consumer = consumer, + renderer = renderer, + ) + } + is ScrollingListLayout -> section { + renderWith( + element = ElementList(uiLayout.items), + consumer = consumer, + renderer = renderer, + ) + } } } diff --git a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/CompositeElementRenderer.kt b/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/CompositeElementRenderer.kt index f5ebce2..9e43e14 100644 --- a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/CompositeElementRenderer.kt +++ b/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/CompositeElementRenderer.kt @@ -1,6 +1,8 @@ package ink.ui.render.statichtml.renderer import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult +import ink.ui.structures.render.renderCatching import kotlinx.html.TagConsumer internal class CompositeElementRenderer( @@ -8,16 +10,18 @@ internal class CompositeElementRenderer( ): ElementRenderer { override fun TagConsumer<*>.render(element: UiElement, parent: ElementRenderer): RenderResult { renderers.forEach { renderer -> - val result = renderWith( - element = element, - consumer = this@render, - renderer = renderer, - parent = this@CompositeElementRenderer, - ) - if (result == RenderResult.Rendered) { - return RenderResult.Rendered + val result = renderCatching { + renderWith( + element = element, + consumer = this@render, + renderer = renderer, + parent = this@CompositeElementRenderer, + ) + } + if (result != RenderResult.Skipped) { + return result } } - return RenderResult.NotRendered + return RenderResult.Skipped } } diff --git a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/ElementRenderer.kt b/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/ElementRenderer.kt index 5dfbf48..93a66fd 100644 --- a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/ElementRenderer.kt +++ b/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/ElementRenderer.kt @@ -1,19 +1,20 @@ package ink.ui.render.statichtml.renderer import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult import kotlinx.html.TagConsumer interface ElementRenderer { fun TagConsumer<*>.render(element: UiElement, parent: ElementRenderer): RenderResult } -inline fun renderer(crossinline render: TagConsumer<*>.(T) -> Unit) = object: ElementRenderer{ +inline fun renderer(crossinline render: TagConsumer<*>.(T) -> Unit) = object: ElementRenderer { override fun TagConsumer<*>.render(element: UiElement, parent: ElementRenderer): RenderResult { if (element is T) { render(element) return RenderResult.Rendered } - return RenderResult.NotRendered + return RenderResult.Skipped } } diff --git a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/FormattedTextRenderer.kt b/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/FormattedTextRenderer.kt new file mode 100644 index 0000000..4d68d72 --- /dev/null +++ b/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/FormattedTextRenderer.kt @@ -0,0 +1,33 @@ +package ink.ui.render.statichtml.renderer + +import ink.ui.structures.elements.FormattedText +import kotlinx.html.TagConsumer +import kotlinx.html.* + +val FormattedTextRenderer = renderer { element -> + p { + render(consumer, element.spans) + } +} + +private fun Tag.render( + consumer: TagConsumer<*>, + spans: List, +) { + spans.forEachIndexed { index, span -> + when (span) { + is FormattedText.Span.Text -> +span.text + is FormattedText.Span.Emphasis -> consumer.em { + render(consumer, span.inner) + } + is FormattedText.Span.Link -> consumer.a( + href = span.url, + ) { + render(consumer, span.inner) + } + is FormattedText.Span.Strong -> consumer.strong { + render(consumer, span.inner) + } + } + } +} diff --git a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/ListRenderer.kt b/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/ListRenderer.kt index 7cf846d..0060969 100644 --- a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/ListRenderer.kt +++ b/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/ListRenderer.kt @@ -4,11 +4,12 @@ import ink.ui.structures.GroupingStyle import ink.ui.structures.Positioning import ink.ui.structures.elements.ElementList import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult import kotlinx.html.* object ListRenderer: ElementRenderer { override fun TagConsumer<*>.render(element: UiElement, parent: ElementRenderer): RenderResult { - if (element !is ElementList) return RenderResult.NotRendered + if (element !is ElementList) return RenderResult.Skipped div( classes = when (element.groupingStyle) { diff --git a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/RenderResult.kt b/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/RenderResult.kt deleted file mode 100644 index 2a28460..0000000 --- a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/RenderResult.kt +++ /dev/null @@ -1,6 +0,0 @@ -package ink.ui.render.statichtml.renderer - -enum class RenderResult { - Rendered, - NotRendered, -} diff --git a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/StatusRenderer.kt b/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/StatusRenderer.kt index d14117d..7db7c18 100644 --- a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/StatusRenderer.kt +++ b/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/StatusRenderer.kt @@ -1,14 +1,16 @@ package ink.ui.render.statichtml.renderer +import ink.ui.render.web.toCssClass import ink.ui.structures.elements.StatusIndicatorElement import ink.ui.structures.elements.UiElement +import ink.ui.structures.render.RenderResult import kotlinx.html.* class StatusRenderer( private val iconPath: String? ): ElementRenderer { override fun TagConsumer<*>.render(element: UiElement, parent: ElementRenderer): RenderResult { - if (element !is StatusIndicatorElement) return RenderResult.NotRendered + if (element !is StatusIndicatorElement) return RenderResult.Skipped div(classes = "status-indicator") { if(iconPath != null) { eagerImg( diff --git a/render-web-common/api/render-web-common.api b/render-web-common/api/render-web-common.api new file mode 100644 index 0000000..1204a95 --- /dev/null +++ b/render-web-common/api/render-web-common.api @@ -0,0 +1,13 @@ +public final class ink/ui/render/web/FixedGridLayoutsKt { + public static final fun getGridTemplateColumns (Link/ui/structures/layouts/FixedGridLayout;)Ljava/lang/String; +} + +public final class ink/ui/render/web/SentimentsKt { + public static final fun toCssClass (Link/ui/structures/Sentiment;)Ljava/lang/String; +} + +public final class ink/ui/render/web/SymbolsKt { + public static final field RESOURCE_SVG_PATH Ljava/lang/String; + public static final fun getSvgSrc-TN8vZ0Q (Ljava/lang/String;)Ljava/lang/String; +} + diff --git a/render-web-common/build.gradle.kts b/render-web-common/build.gradle.kts new file mode 100644 index 0000000..4f2adb4 --- /dev/null +++ b/render-web-common/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + kotlin("multiplatform") + id("org.jetbrains.compose") + id("org.jetbrains.kotlin.plugin.compose") + id("ink.publishing") +} +kotlin { + js { + browser() + binaries.executable() + } + jvm() + + sourceSets { + commonMain.dependencies { + api(libs.kotlinx.coroutines.core) + implementation(compose.runtime) + api(projects.structures) + } + } +} diff --git a/render-compose-html/src/commonMain/composeResources/css/main-2.0.css b/render-web-common/src/commonMain/composeResources/css/main-2.0.css similarity index 99% rename from render-compose-html/src/commonMain/composeResources/css/main-2.0.css rename to render-web-common/src/commonMain/composeResources/css/main-2.0.css index fbf07cc..da54da7 100644 --- a/render-compose-html/src/commonMain/composeResources/css/main-2.0.css +++ b/render-web-common/src/commonMain/composeResources/css/main-2.0.css @@ -241,6 +241,10 @@ section nav a:not(.button):after display: grid; gap: .25rem; } +.fixed-grid > * +{ + display: flex; +} .item-list, .unified-list, diff --git a/render-compose-html/src/commonMain/composeResources/css/reset.css b/render-web-common/src/commonMain/composeResources/css/reset.css similarity index 100% rename from render-compose-html/src/commonMain/composeResources/css/reset.css rename to render-web-common/src/commonMain/composeResources/css/reset.css diff --git a/render-compose-html/src/commonMain/composeResources/svg/add.svg b/render-web-common/src/commonMain/composeResources/svg/add.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/add.svg rename to render-web-common/src/commonMain/composeResources/svg/add.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/alarm.svg b/render-web-common/src/commonMain/composeResources/svg/alarm.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/alarm.svg rename to render-web-common/src/commonMain/composeResources/svg/alarm.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/alarm_off.svg b/render-web-common/src/commonMain/composeResources/svg/alarm_off.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/alarm_off.svg rename to render-web-common/src/commonMain/composeResources/svg/alarm_off.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/arrow_back.svg b/render-web-common/src/commonMain/composeResources/svg/arrow_back.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/arrow_back.svg rename to render-web-common/src/commonMain/composeResources/svg/arrow_back.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/arrow_forward.svg b/render-web-common/src/commonMain/composeResources/svg/arrow_forward.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/arrow_forward.svg rename to render-web-common/src/commonMain/composeResources/svg/arrow_forward.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/bed.svg b/render-web-common/src/commonMain/composeResources/svg/bed.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/bed.svg rename to render-web-common/src/commonMain/composeResources/svg/bed.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/blip.svg b/render-web-common/src/commonMain/composeResources/svg/blip.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/blip.svg rename to render-web-common/src/commonMain/composeResources/svg/blip.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/caution.svg b/render-web-common/src/commonMain/composeResources/svg/caution.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/caution.svg rename to render-web-common/src/commonMain/composeResources/svg/caution.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/close.svg b/render-web-common/src/commonMain/composeResources/svg/close.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/close.svg rename to render-web-common/src/commonMain/composeResources/svg/close.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/cloud.svg b/render-web-common/src/commonMain/composeResources/svg/cloud.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/cloud.svg rename to render-web-common/src/commonMain/composeResources/svg/cloud.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/done.svg b/render-web-common/src/commonMain/composeResources/svg/done.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/done.svg rename to render-web-common/src/commonMain/composeResources/svg/done.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/edit.svg b/render-web-common/src/commonMain/composeResources/svg/edit.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/edit.svg rename to render-web-common/src/commonMain/composeResources/svg/edit.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/error.svg b/render-web-common/src/commonMain/composeResources/svg/error.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/error.svg rename to render-web-common/src/commonMain/composeResources/svg/error.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/group.svg b/render-web-common/src/commonMain/composeResources/svg/group.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/group.svg rename to render-web-common/src/commonMain/composeResources/svg/group.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/house.svg b/render-web-common/src/commonMain/composeResources/svg/house.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/house.svg rename to render-web-common/src/commonMain/composeResources/svg/house.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/lock.svg b/render-web-common/src/commonMain/composeResources/svg/lock.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/lock.svg rename to render-web-common/src/commonMain/composeResources/svg/lock.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/lock_open.svg b/render-web-common/src/commonMain/composeResources/svg/lock_open.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/lock_open.svg rename to render-web-common/src/commonMain/composeResources/svg/lock_open.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/moon.svg b/render-web-common/src/commonMain/composeResources/svg/moon.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/moon.svg rename to render-web-common/src/commonMain/composeResources/svg/moon.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/movie.svg b/render-web-common/src/commonMain/composeResources/svg/movie.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/movie.svg rename to render-web-common/src/commonMain/composeResources/svg/movie.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/person.svg b/render-web-common/src/commonMain/composeResources/svg/person.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/person.svg rename to render-web-common/src/commonMain/composeResources/svg/person.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/rain.svg b/render-web-common/src/commonMain/composeResources/svg/rain.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/rain.svg rename to render-web-common/src/commonMain/composeResources/svg/rain.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/remove.svg b/render-web-common/src/commonMain/composeResources/svg/remove.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/remove.svg rename to render-web-common/src/commonMain/composeResources/svg/remove.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/sunshine.svg b/render-web-common/src/commonMain/composeResources/svg/sunshine.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/sunshine.svg rename to render-web-common/src/commonMain/composeResources/svg/sunshine.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/temperature.svg b/render-web-common/src/commonMain/composeResources/svg/temperature.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/temperature.svg rename to render-web-common/src/commonMain/composeResources/svg/temperature.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/unknown.svg b/render-web-common/src/commonMain/composeResources/svg/unknown.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/unknown.svg rename to render-web-common/src/commonMain/composeResources/svg/unknown.svg diff --git a/render-compose-html/src/commonMain/composeResources/svg/water.svg b/render-web-common/src/commonMain/composeResources/svg/water.svg similarity index 100% rename from render-compose-html/src/commonMain/composeResources/svg/water.svg rename to render-web-common/src/commonMain/composeResources/svg/water.svg diff --git a/render-web-common/src/commonMain/kotlin/ink/ui/render/web/FixedGridLayouts.kt b/render-web-common/src/commonMain/kotlin/ink/ui/render/web/FixedGridLayouts.kt new file mode 100644 index 0000000..0136fec --- /dev/null +++ b/render-web-common/src/commonMain/kotlin/ink/ui/render/web/FixedGridLayouts.kt @@ -0,0 +1,7 @@ +package ink.ui.render.web + +import ink.ui.structures.layouts.FixedGridLayout + +val FixedGridLayout.gridTemplateColumns: String get() { + return (0 until columns).joinToString(" ") { "auto" } +} diff --git a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/Sentiments.kt b/render-web-common/src/commonMain/kotlin/ink/ui/render/web/Sentiments.kt similarity index 88% rename from render-static-html/src/main/java/ink/ui/render/statichtml/renderer/Sentiments.kt rename to render-web-common/src/commonMain/kotlin/ink/ui/render/web/Sentiments.kt index 0dde080..c5fb2fc 100644 --- a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/Sentiments.kt +++ b/render-web-common/src/commonMain/kotlin/ink/ui/render/web/Sentiments.kt @@ -1,4 +1,4 @@ -package ink.ui.render.statichtml.renderer +package ink.ui.render.web import ink.ui.structures.Sentiment diff --git a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/Symbols.kt b/render-web-common/src/commonMain/kotlin/ink/ui/render/web/Symbols.kt similarity index 84% rename from render-static-html/src/main/java/ink/ui/render/statichtml/renderer/Symbols.kt rename to render-web-common/src/commonMain/kotlin/ink/ui/render/web/Symbols.kt index 5999155..bfbd85d 100644 --- a/render-static-html/src/main/java/ink/ui/render/statichtml/renderer/Symbols.kt +++ b/render-web-common/src/commonMain/kotlin/ink/ui/render/web/Symbols.kt @@ -1,8 +1,8 @@ -package ink.ui.render.statichtml.renderer +package ink.ui.render.web import ink.ui.structures.Symbol -internal const val PATH = "composeResources/com.inkapplications.ui.render_compose_html.generated.resources/svg" +const val RESOURCE_SVG_PATH = "composeResources/com.inkapplications.ui.render_web_common.generated.resources/svg" val Symbol.svgSrc: String get() = when (this) { Symbol.Add -> "add.svg" @@ -30,4 +30,4 @@ val Symbol.svgSrc: String get() = when (this) { Symbol.Temperature -> "temperature.svg" Symbol.Water -> "water.svg" else -> "unknown.svg" -}.let { "$PATH/$it" } +}.let { "$RESOURCE_SVG_PATH/$it" } diff --git a/sample-web/build.gradle.kts b/sample-web/build.gradle.kts index e816a43..1cd28bf 100644 --- a/sample-web/build.gradle.kts +++ b/sample-web/build.gradle.kts @@ -38,7 +38,7 @@ tasks.register("buildStatic") { } copy { staticResDir.createDirectory() - from(projects.renderComposeHtml.dependencyProject.layout.projectDirectory.dir("src/commonMain/composeResources")) + from(projects.renderWebCommon.dependencyProject.layout.projectDirectory.dir("src/commonMain/composeResources")) into(staticResDir) } } diff --git a/sample-web/src/jsMain/resources/index.html b/sample-web/src/jsMain/resources/index.html index fa908ec..f2b074c 100644 --- a/sample-web/src/jsMain/resources/index.html +++ b/sample-web/src/jsMain/resources/index.html @@ -3,7 +3,7 @@ Sample - + diff --git a/sample-web/src/staticMain/Sample.inkui.kts b/sample-web/src/staticMain/Sample.inkui.kts index 3e9e832..24516b2 100644 --- a/sample-web/src/staticMain/Sample.inkui.kts +++ b/sample-web/src/staticMain/Sample.inkui.kts @@ -14,6 +14,18 @@ addBody( TextElement("Body Text", TextStyle.Body), TextElement("Caption", TextStyle.Caption), StatusIndicatorElement("Nominal Status", Sentiment.Nominal), + FormattedText { + link(url="javascript:void(0)") { + text("Formatted") + } + space() + strong { + text("Text") + emphasis { + text("!!!") + } + } + } ) ) ) diff --git a/settings.gradle.kts b/settings.gradle.kts index 42d683f..4b56e48 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,6 +5,7 @@ rootProject.name = "ink-ui" include("render-compose") include("render-compose-html") include("render-static-html") +include("render-web-common") include("structures") include("sample-android") include("sample-web") diff --git a/structures/api/structures.api b/structures/api/structures.api index 2993e39..818facb 100644 --- a/structures/api/structures.api +++ b/structures/api/structures.api @@ -149,6 +149,104 @@ public final class ink/ui/structures/elements/EmptyElement : ink/ui/structures/e public fun toString ()Ljava/lang/String; } +public final class ink/ui/structures/elements/FormattedText : ink/ui/structures/elements/UiElement$Static { + public fun (Ljava/util/List;)V + public fun (Lkotlin/jvm/functions/Function1;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Link/ui/structures/elements/FormattedText; + public static synthetic fun copy$default (Link/ui/structures/elements/FormattedText;Ljava/util/List;ILjava/lang/Object;)Link/ui/structures/elements/FormattedText; + public fun equals (Ljava/lang/Object;)Z + public final fun getSpans ()Ljava/util/List; + public fun hashCode ()I + public final fun toPlainTextElement (Link/ui/structures/TextStyle;)Link/ui/structures/elements/TextElement; + public fun toString ()Ljava/lang/String; +} + +public final class ink/ui/structures/elements/FormattedText$Builder { + public fun ()V + public fun (Ljava/util/List;)V + public synthetic fun (Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Link/ui/structures/elements/FormattedText$Builder; + public static synthetic fun copy$default (Link/ui/structures/elements/FormattedText$Builder;Ljava/util/List;ILjava/lang/Object;)Link/ui/structures/elements/FormattedText$Builder; + public final fun emphasis (Lkotlin/jvm/functions/Function1;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getSpans ()Ljava/util/List; + public fun hashCode ()I + public final fun link (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V + public final fun setSpans (Ljava/util/List;)V + public final fun space ()V + public final fun strong (Lkotlin/jvm/functions/Function1;)V + public final fun text (Ljava/lang/String;)V + public fun toString ()Ljava/lang/String; +} + +public abstract interface class ink/ui/structures/elements/FormattedText$Span { + public abstract fun toPlainString ()Ljava/lang/String; +} + +public abstract interface class ink/ui/structures/elements/FormattedText$Span$Composite : ink/ui/structures/elements/FormattedText$Span { + public abstract fun getInner ()Ljava/util/List; +} + +public final class ink/ui/structures/elements/FormattedText$Span$Composite$DefaultImpls { + public static fun toPlainString (Link/ui/structures/elements/FormattedText$Span$Composite;)Ljava/lang/String; +} + +public final class ink/ui/structures/elements/FormattedText$Span$DefaultImpls { + public static fun toPlainString (Link/ui/structures/elements/FormattedText$Span;)Ljava/lang/String; +} + +public final class ink/ui/structures/elements/FormattedText$Span$Emphasis : ink/ui/structures/elements/FormattedText$Span$Composite { + public fun (Ljava/util/List;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Link/ui/structures/elements/FormattedText$Span$Emphasis; + public static synthetic fun copy$default (Link/ui/structures/elements/FormattedText$Span$Emphasis;Ljava/util/List;ILjava/lang/Object;)Link/ui/structures/elements/FormattedText$Span$Emphasis; + public fun equals (Ljava/lang/Object;)Z + public fun getInner ()Ljava/util/List; + public fun hashCode ()I + public fun toPlainString ()Ljava/lang/String; + public fun toString ()Ljava/lang/String; +} + +public final class ink/ui/structures/elements/FormattedText$Span$Link : ink/ui/structures/elements/FormattedText$Span$Composite { + public fun (Ljava/util/List;Ljava/lang/String;)V + public final fun component1 ()Ljava/util/List; + public final fun component2 ()Ljava/lang/String; + public final fun copy (Ljava/util/List;Ljava/lang/String;)Link/ui/structures/elements/FormattedText$Span$Link; + public static synthetic fun copy$default (Link/ui/structures/elements/FormattedText$Span$Link;Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)Link/ui/structures/elements/FormattedText$Span$Link; + public fun equals (Ljava/lang/Object;)Z + public fun getInner ()Ljava/util/List; + public final fun getUrl ()Ljava/lang/String; + public fun hashCode ()I + public fun toPlainString ()Ljava/lang/String; + public fun toString ()Ljava/lang/String; +} + +public final class ink/ui/structures/elements/FormattedText$Span$Strong : ink/ui/structures/elements/FormattedText$Span$Composite { + public fun (Ljava/util/List;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Link/ui/structures/elements/FormattedText$Span$Strong; + public static synthetic fun copy$default (Link/ui/structures/elements/FormattedText$Span$Strong;Ljava/util/List;ILjava/lang/Object;)Link/ui/structures/elements/FormattedText$Span$Strong; + public fun equals (Ljava/lang/Object;)Z + public fun getInner ()Ljava/util/List; + public fun hashCode ()I + public fun toPlainString ()Ljava/lang/String; + public fun toString ()Ljava/lang/String; +} + +public final class ink/ui/structures/elements/FormattedText$Span$Text : ink/ui/structures/elements/FormattedText$Span { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Link/ui/structures/elements/FormattedText$Span$Text; + public static synthetic fun copy$default (Link/ui/structures/elements/FormattedText$Span$Text;Ljava/lang/String;ILjava/lang/Object;)Link/ui/structures/elements/FormattedText$Span$Text; + public fun equals (Ljava/lang/Object;)Z + public final fun getText ()Ljava/lang/String; + public fun hashCode ()I + public fun toPlainString ()Ljava/lang/String; + public fun toString ()Ljava/lang/String; +} + public final class ink/ui/structures/elements/IconElement : ink/ui/structures/elements/UiElement$Static { public synthetic fun (Ljava/lang/String;Link/ui/structures/Sentiment;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun (Ljava/lang/String;Link/ui/structures/Sentiment;Lkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -353,3 +451,35 @@ public final class ink/ui/structures/layouts/ScrollingListLayout : ink/ui/struct public abstract interface class ink/ui/structures/layouts/UiLayout { } +public abstract interface class ink/ui/structures/render/RenderResult { +} + +public final class ink/ui/structures/render/RenderResult$Failed : ink/ui/structures/render/RenderResult { + public fun (Ljava/lang/Throwable;)V + public final fun component1 ()Ljava/lang/Throwable; + public final fun copy (Ljava/lang/Throwable;)Link/ui/structures/render/RenderResult$Failed; + public static synthetic fun copy$default (Link/ui/structures/render/RenderResult$Failed;Ljava/lang/Throwable;ILjava/lang/Object;)Link/ui/structures/render/RenderResult$Failed; + public fun equals (Ljava/lang/Object;)Z + public final fun getException ()Ljava/lang/Throwable; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class ink/ui/structures/render/RenderResult$Rendered : ink/ui/structures/render/RenderResult { + public static final field INSTANCE Link/ui/structures/render/RenderResult$Rendered; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class ink/ui/structures/render/RenderResult$Skipped : ink/ui/structures/render/RenderResult { + public static final field INSTANCE Link/ui/structures/render/RenderResult$Skipped; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class ink/ui/structures/render/RenderResultKt { + public static final fun renderCatching (Lkotlin/jvm/functions/Function0;)Link/ui/structures/render/RenderResult; +} + diff --git a/structures/src/commonMain/kotlin/ink/ui/structures/elements/FormattedText.kt b/structures/src/commonMain/kotlin/ink/ui/structures/elements/FormattedText.kt new file mode 100644 index 0000000..6cff0b4 --- /dev/null +++ b/structures/src/commonMain/kotlin/ink/ui/structures/elements/FormattedText.kt @@ -0,0 +1,71 @@ +package ink.ui.structures.elements + +import ink.ui.structures.TextStyle + +data class FormattedText( + val spans: List +): UiElement.Static { + constructor(builder: FormattedText.Builder.() -> Unit): this( + spans = Builder().apply(builder).spans + ) + + fun toPlainTextElement( + style: TextStyle, + ): TextElement { + return TextElement( + text = spans.joinToString("") { span -> + when (span) { + is Span.Text -> span.text + is Span.Composite -> span.inner.joinToString("") { it.toPlainString() } + } + }, + style = style, + ) + } + + sealed interface Span { + sealed interface Composite: Span { + val inner: List + } + data class Text( + val text: String, + ): Span + data class Strong( + override val inner: List, + ): Composite + data class Emphasis( + override val inner: List, + ): Composite + data class Link( + override val inner: List, + val url: String, + ): Composite + + fun toPlainString(): String { + return when (this) { + is Text -> text + is Composite -> inner.joinToString("") { it.toPlainString() } + } + } + } + + data class Builder( + var spans: MutableList = mutableListOf(), + ) { + fun text(text: String) { + spans.add(Span.Text(text)) + } + fun strong(builder: Builder.() -> Unit) { + spans.add(Span.Strong(Builder().apply(builder).spans)) + } + fun emphasis(builder: Builder.() -> Unit) { + spans.add(Span.Emphasis(Builder().apply(builder).spans)) + } + fun link(url: String, builder: Builder.() -> Unit) { + spans.add(Span.Link(Builder().apply(builder).spans, url)) + } + fun space() { + spans.add(Span.Text(" ")) + } + } +} diff --git a/structures/src/commonMain/kotlin/ink/ui/structures/render/RenderResult.kt b/structures/src/commonMain/kotlin/ink/ui/structures/render/RenderResult.kt new file mode 100644 index 0000000..2a47b22 --- /dev/null +++ b/structures/src/commonMain/kotlin/ink/ui/structures/render/RenderResult.kt @@ -0,0 +1,13 @@ +package ink.ui.structures.render + +sealed interface RenderResult { + data object Rendered: RenderResult + data object Skipped: RenderResult + data class Failed(val exception: Throwable): RenderResult +} + +inline fun renderCatching( + block: () -> RenderResult, +): RenderResult { + return runCatching(block).getOrElse { RenderResult.Failed(it) } +}