Refactor vst-ui-base and test-app: remove inline CSS definitions in Kotlin, migrate styles to external vst-ui-base.css. Add WebResourceHandler for serving CSS. Enhance KeyboardComponent with keyboard event mapping. Increment version to 2.2.4.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="JS" type="JavascriptDebugType" engineId="98ca6316-2f89-46d9-a9e5-fa9e2b0625b3" uri="http://localhost:9999">
|
||||
<mapping url="http://localhost:9999/vst-ui-base.css" local-file="$PROJECT_DIR$/vst-ui-base/src/jvmMain/resources/vst-ui-base.css" />
|
||||
<method v="2">
|
||||
<option name="Gradle.BeforeRunTask" enabled="true" tasks="jsBrowserDevelopmentExecutableDistribution" externalProjectPath="$PROJECT_DIR$/test-app" vmOptions="" scriptParameters="" />
|
||||
</method>
|
||||
|
||||
@@ -2,7 +2,7 @@ project.extra.set("devMode", true)
|
||||
project.extra.set("enabledSourceMaps", true)
|
||||
|
||||
group = "nl.astraeus"
|
||||
version = "2.2.3"
|
||||
version = "2.2.4"
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
|
||||
@@ -2,25 +2,29 @@
|
||||
|
||||
package nl.astraeus.vst.ui.view
|
||||
|
||||
import kotlinx.html.*
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.html.InputType
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.h1
|
||||
import kotlinx.html.hr
|
||||
import kotlinx.html.input
|
||||
import kotlinx.html.js.onChangeFunction
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import kotlinx.html.js.onInputFunction
|
||||
import kotlinx.html.option
|
||||
import kotlinx.html.org.w3c.dom.events.Event
|
||||
import nl.astraeus.css.properties.*
|
||||
import nl.astraeus.css.style.Style
|
||||
import nl.astraeus.css.style.cls
|
||||
import kotlinx.html.select
|
||||
import kotlinx.html.span
|
||||
import kotlinx.html.style
|
||||
import nl.astraeus.komp.HtmlBuilder
|
||||
import nl.astraeus.komp.Komponent
|
||||
import nl.astraeus.vst.ui.components.ExpKnobComponent
|
||||
import nl.astraeus.vst.ui.components.KeyboardComponent
|
||||
import nl.astraeus.vst.ui.components.KnobComponent
|
||||
import nl.astraeus.vst.ui.css.Css
|
||||
import nl.astraeus.vst.ui.css.Css.defineCss
|
||||
import nl.astraeus.vst.ui.css.Css.noTextSelect
|
||||
import nl.astraeus.vst.ui.css.CssName
|
||||
import nl.astraeus.vst.ui.css.hover
|
||||
import nl.astraeus.vst.ui.ws.WebsocketClient
|
||||
import org.w3c.dom.HTMLInputElement
|
||||
import org.w3c.dom.events.KeyboardEvent
|
||||
import org.w3c.files.File
|
||||
import org.w3c.files.FileList
|
||||
import org.w3c.files.get
|
||||
@@ -29,6 +33,17 @@ class MainView : Komponent() {
|
||||
private var messages: MutableList<String> = ArrayList()
|
||||
var started = true
|
||||
var keyboardWidth = 500
|
||||
val keyboard = KeyboardComponent(
|
||||
keyboardWidth = keyboardWidth,
|
||||
keyboardHeight = keyboardWidth / 2,
|
||||
onNoteDown = { println("Note down: $it") },
|
||||
onNoteUp = { println("Note up: $it") },
|
||||
)
|
||||
|
||||
init {
|
||||
document.body?.addEventListener("keydown", { event -> keyboard.handleKeyboardEvent(event as KeyboardEvent) })
|
||||
document.body?.addEventListener("keyup", { event -> keyboard.handleKeyboardEvent(event as KeyboardEvent) })
|
||||
}
|
||||
|
||||
fun addMessage(message: String) {
|
||||
messages.add(message)
|
||||
@@ -45,7 +60,7 @@ class MainView : Komponent() {
|
||||
|
||||
override fun HtmlBuilder.render() {
|
||||
div(MainDivCss.name) {
|
||||
style = "transform: scale(0.5);"
|
||||
//style = "transform: scale(0.5);"
|
||||
if (!started) {
|
||||
div(StartSplashCss.name) {
|
||||
div(StartBoxCss.name) {
|
||||
@@ -89,26 +104,27 @@ class MainView : Komponent() {
|
||||
}
|
||||
span {
|
||||
+"channel:"
|
||||
/*
|
||||
input {
|
||||
type = InputType.number
|
||||
value = VstChipWorklet.midiChannel.toString()
|
||||
value = "1"
|
||||
min = "1"
|
||||
max = "16"
|
||||
onInputFunction = { event ->
|
||||
val target = event.target as HTMLInputElement
|
||||
kotlin.io.println("onInput channel: $target")
|
||||
VstChipWorklet.midiChannel = target.value.toInt()
|
||||
println("onInput channel: $target")
|
||||
//VstChipWorklet.midiChannel = target.value.toInt()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
span {
|
||||
+"Upload file: "
|
||||
input {
|
||||
type = InputType.file
|
||||
onChangeFunction = {
|
||||
fileInputSelectHandler(it)
|
||||
requestUpdate()
|
||||
}
|
||||
}
|
||||
div {
|
||||
+"Upload file: "
|
||||
input {
|
||||
type = InputType.file
|
||||
onChangeFunction = {
|
||||
fileInputSelectHandler(it)
|
||||
requestUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,12 +149,7 @@ class MainView : Komponent() {
|
||||
hr {}
|
||||
div {
|
||||
include(
|
||||
KeyboardComponent(
|
||||
keyboardWidth = keyboardWidth,
|
||||
keyboardHeight = keyboardWidth / 2,
|
||||
onNoteDown = { println("Note down: $it") },
|
||||
onNoteUp = { println("Note up: $it") },
|
||||
)
|
||||
keyboard
|
||||
)
|
||||
}
|
||||
/*
|
||||
@@ -226,132 +237,10 @@ class MainView : Komponent() {
|
||||
|
||||
companion object MainViewCss : CssName() {
|
||||
object MainDivCss : CssName()
|
||||
object ActiveCss : CssName()
|
||||
object ButtonCss : CssName()
|
||||
object ButtonBarCss : CssName()
|
||||
object SelectedCss : CssName()
|
||||
object NoteBarCss : CssName()
|
||||
object StartSplashCss : CssName()
|
||||
object StartBoxCss : CssName()
|
||||
object StartButtonCss : CssName()
|
||||
object ControlsCss : CssName()
|
||||
|
||||
init {
|
||||
defineCss {
|
||||
select("*") {
|
||||
select("*:before") {
|
||||
select("*:after") {
|
||||
boxSizing(BoxSizing.borderBox)
|
||||
}
|
||||
}
|
||||
}
|
||||
select("html", "body") {
|
||||
margin(0.px)
|
||||
padding(0.px)
|
||||
height(100.prc)
|
||||
}
|
||||
select("html", "body") {
|
||||
backgroundColor(Css.currentStyle.mainBackgroundColor)
|
||||
color(Css.currentStyle.mainFontColor)
|
||||
|
||||
fontFamily("JetbrainsMono, monospace")
|
||||
fontSize(14.px)
|
||||
fontWeight(FontWeight.bold)
|
||||
|
||||
//transition()
|
||||
noTextSelect()
|
||||
}
|
||||
select("input", "textarea") {
|
||||
backgroundColor(Css.currentStyle.inputBackgroundColor)
|
||||
color(Css.currentStyle.mainFontColor)
|
||||
border("none")
|
||||
}
|
||||
select(cls(ButtonCss)) {
|
||||
margin(1.rem)
|
||||
commonButton()
|
||||
}
|
||||
select(cls(ButtonBarCss)) {
|
||||
margin(1.rem, 0.px)
|
||||
commonButton()
|
||||
}
|
||||
select(cls(ActiveCss)) {
|
||||
//backgroundColor(Css.currentStyle.selectedBackgroundColor)
|
||||
}
|
||||
select(cls(NoteBarCss)) {
|
||||
minHeight(4.rem)
|
||||
}
|
||||
select(cls(MainDivCss)) {
|
||||
margin(1.rem)
|
||||
}
|
||||
select("select") {
|
||||
plain("appearance", "none")
|
||||
border("0")
|
||||
outline("0")
|
||||
width(20.rem)
|
||||
padding(0.5.rem, 2.rem, 0.5.rem, 0.5.rem)
|
||||
backgroundImage("url('https://upload.wikimedia.org/wikipedia/commons/9/9d/Caret_down_font_awesome_whitevariation.svg')")
|
||||
background("right 0.8em center/1.4em")
|
||||
backgroundColor(Css.currentStyle.inputBackgroundColor)
|
||||
color(Css.currentStyle.mainFontColor)
|
||||
borderRadius(0.25.em)
|
||||
}
|
||||
select(cls(StartSplashCss)) {
|
||||
position(Position.fixed)
|
||||
left(0.px)
|
||||
top(0.px)
|
||||
width(100.vw)
|
||||
height(100.vh)
|
||||
zIndex(100)
|
||||
backgroundColor(hsla(32, 0, 5, 0.65))
|
||||
|
||||
select(cls(StartBoxCss)) {
|
||||
position(Position.relative)
|
||||
left(25.vw)
|
||||
top(25.vh)
|
||||
width(50.vw)
|
||||
height(50.vh)
|
||||
backgroundColor(hsla(239, 50, 10, 1.0))
|
||||
borderColor(Css.currentStyle.mainFontColor)
|
||||
borderWidth(2.px)
|
||||
|
||||
select(cls(StartButtonCss)) {
|
||||
position(Position.absolute)
|
||||
left(50.prc)
|
||||
top(50.prc)
|
||||
transform(Transform("translate(-50%, -50%)"))
|
||||
padding(1.rem)
|
||||
backgroundColor(Css.currentStyle.buttonBackgroundColor)
|
||||
cursor("pointer")
|
||||
}
|
||||
}
|
||||
}
|
||||
select(ControlsCss.cls()) {
|
||||
display(Display.flex)
|
||||
flexDirection(FlexDirection.row)
|
||||
justifyContent(JustifyContent.flexStart)
|
||||
alignItems(AlignItems.center)
|
||||
margin(1.rem)
|
||||
padding(1.rem)
|
||||
backgroundColor(Css.currentStyle.mainBackgroundColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Style.commonButton() {
|
||||
display(Display.inlineBlock)
|
||||
padding(1.rem)
|
||||
backgroundColor(Css.currentStyle.buttonBackgroundColor)
|
||||
borderColor(Css.currentStyle.buttonBorderColor)
|
||||
borderWidth(Css.currentStyle.buttonBorderWidth)
|
||||
color(Css.currentStyle.mainFontColor)
|
||||
|
||||
hover {
|
||||
backgroundColor(Css.currentStyle.buttonBackgroundColor.hover())
|
||||
}
|
||||
and(SelectedCss.cls()) {
|
||||
backgroundColor(Css.currentStyle.buttonBackgroundColor.hover().hover().hover())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,11 +33,7 @@ kotlin {
|
||||
jvm {}
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
api("nl.astraeus:kotlin-css-generator:1.0.10")
|
||||
}
|
||||
}
|
||||
val commonMain by getting
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
api("nl.astraeus:kotlin-komponent:1.2.8")
|
||||
@@ -52,8 +48,8 @@ kotlin {
|
||||
dependencies {
|
||||
api("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0")
|
||||
|
||||
api("io.undertow:undertow-core:2.3.21.Final")
|
||||
implementation("io.undertow:undertow-websockets-jsr:2.3.21.Final")
|
||||
api("io.undertow:undertow-core:2.3.23.Final")
|
||||
implementation("io.undertow:undertow-websockets-jsr:2.3.23.Final")
|
||||
//implementation("org.jboss.xnio:xnio-nio:3.8.16.Final")
|
||||
|
||||
implementation("org.xerial:sqlite-jdbc:3.46.0.0")
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
package nl.astraeus.vst.ui.components
|
||||
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.html.*
|
||||
import kotlinx.html.classes
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.js.onMouseDownFunction
|
||||
import kotlinx.html.js.onMouseMoveFunction
|
||||
import kotlinx.html.js.onMouseUpFunction
|
||||
import kotlinx.html.js.onWheelFunction
|
||||
import kotlinx.html.org.w3c.dom.events.Event
|
||||
import nl.astraeus.css.properties.*
|
||||
import nl.astraeus.css.style.cls
|
||||
import kotlinx.html.span
|
||||
import kotlinx.html.style
|
||||
import kotlinx.html.svg
|
||||
import nl.astraeus.komp.HtmlBuilder
|
||||
import nl.astraeus.komp.Komponent
|
||||
import nl.astraeus.komp.currentElement
|
||||
import nl.astraeus.vst.ui.css.ActiveCls
|
||||
import nl.astraeus.vst.ui.css.Css
|
||||
import nl.astraeus.vst.ui.css.Css.defineCss
|
||||
import nl.astraeus.vst.ui.css.CssId
|
||||
import nl.astraeus.vst.ui.css.CssName
|
||||
import nl.astraeus.vst.ui.util.arc
|
||||
@@ -327,6 +326,7 @@ open class BaseKnobComponent(
|
||||
}
|
||||
|
||||
companion object : CssId("knob") {
|
||||
object ActiveCls : CssName( "active")
|
||||
object KnobCls : CssName()
|
||||
object KnobSvgCls : CssName()
|
||||
object KnobTextCls : CssName()
|
||||
@@ -334,69 +334,6 @@ open class BaseKnobComponent(
|
||||
|
||||
object KnobVolumeCls : CssName()
|
||||
object KnobVolumeBackgroundCls : CssName()
|
||||
|
||||
init {
|
||||
defineCss {
|
||||
select(cls(KnobCls)) {
|
||||
position(Position.relative)
|
||||
margin(5.px)
|
||||
|
||||
and(cls(ActiveCls)) {
|
||||
backgroundColor(hsla(178, 70, 55, 0.1))
|
||||
borderRadius(0.5.rem)
|
||||
}
|
||||
|
||||
select(cls(KnobSvgCls)) {
|
||||
plain("stroke", "none")
|
||||
plain("stroke-opacity", "1.0")
|
||||
plain("fill", "none")
|
||||
plain("fill-opacity", "0.0")
|
||||
position(Position.absolute)
|
||||
backgroundColor(Color.transparent)
|
||||
|
||||
and(cls(ActiveCls)) {
|
||||
color(Css.currentStyle.mainFontColor)
|
||||
borderRadius(4.px)
|
||||
}
|
||||
}
|
||||
|
||||
select(cls(KnobTextCls)) {
|
||||
position(Position.absolute)
|
||||
width(100.prc)
|
||||
textAlign(TextAlign.center)
|
||||
fontSize(1.0.em)
|
||||
color(Css.currentStyle.mainFontColor)
|
||||
}
|
||||
|
||||
select(cls(KnobValueCls)) {
|
||||
position(Position.absolute)
|
||||
width(100.prc)
|
||||
top((48).prc)
|
||||
textAlign(TextAlign.center)
|
||||
fontSize(1.1.em)
|
||||
color(Css.currentStyle.mainFontColor)
|
||||
}
|
||||
}
|
||||
|
||||
select(cls(KnobVolumeCls)) {
|
||||
plain("fill", Color.transparent)
|
||||
plain("stroke", Css.currentStyle.mainFontColor)
|
||||
color(Css.currentStyle.mainFontColor)
|
||||
plain("stroke-width", "8")
|
||||
//plain("stroke-dasharray", "4")
|
||||
plain("fill-opacity", "0.5")
|
||||
}
|
||||
|
||||
select(cls(KnobVolumeBackgroundCls)) {
|
||||
plain("fill", Color.transparent)
|
||||
plain("stroke", Css.currentStyle.mainFontColor.darken(40))
|
||||
color(Css.currentStyle.mainFontColor.darken(20))
|
||||
plain("stroke-width", "5")
|
||||
//plain("stroke-dasharray", "4")
|
||||
plain("fill-opacity", "0.5")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,35 +7,66 @@ import kotlinx.html.js.onMouseLeaveFunction
|
||||
import kotlinx.html.js.onMouseUpFunction
|
||||
import kotlinx.html.style
|
||||
import kotlinx.html.svg
|
||||
import nl.astraeus.css.properties.AlignItems
|
||||
import nl.astraeus.css.properties.Display
|
||||
import nl.astraeus.css.properties.FlexDirection
|
||||
import nl.astraeus.css.properties.FontWeight
|
||||
import nl.astraeus.css.properties.JustifyContent
|
||||
import nl.astraeus.css.properties.Position
|
||||
import nl.astraeus.css.properties.TextAlign
|
||||
import nl.astraeus.css.properties.prc
|
||||
import nl.astraeus.css.properties.px
|
||||
import nl.astraeus.css.properties.rem
|
||||
import nl.astraeus.css.style.cls
|
||||
import nl.astraeus.komp.HtmlBuilder
|
||||
import nl.astraeus.komp.Komponent
|
||||
import nl.astraeus.vst.ui.css.Css
|
||||
import nl.astraeus.vst.ui.css.Css.defineCss
|
||||
import nl.astraeus.vst.ui.css.CssId
|
||||
import nl.astraeus.vst.ui.css.CssName
|
||||
import nl.astraeus.vst.ui.util.height
|
||||
import nl.astraeus.vst.ui.util.rect
|
||||
import nl.astraeus.vst.ui.util.width
|
||||
import org.w3c.dom.events.KeyboardEvent
|
||||
import org.w3c.dom.events.MouseEvent
|
||||
|
||||
private val KEYBOARD_MAPPING = mapOf(
|
||||
"KeyZ" to 60,
|
||||
"KeyS" to 61,
|
||||
"KeyX" to 62,
|
||||
"KeyD" to 63,
|
||||
"KeyC" to 64,
|
||||
"KeyV" to 65,
|
||||
"KeyG" to 66,
|
||||
"KeyB" to 67,
|
||||
"KeyH" to 68,
|
||||
"KeyN" to 69,
|
||||
"KeyJ" to 70,
|
||||
"KeyM" to 71, // b
|
||||
"Comma" to 72,
|
||||
"KeyL" to 73,
|
||||
"Period" to 74,
|
||||
"Semicolon" to 75,
|
||||
"Slash" to 76,
|
||||
|
||||
"KeyQ" to 72,
|
||||
"Digit2" to 73,
|
||||
"KeyW" to 74,
|
||||
"Digit3" to 75,
|
||||
"KeyE" to 76,
|
||||
"KeyR" to 77,
|
||||
"Digit5" to 78,
|
||||
"KeyT" to 79,
|
||||
"Digit6" to 80,
|
||||
"KeyY" to 81,
|
||||
"Digit7" to 82,
|
||||
"KeyU" to 83, // b
|
||||
"KeyI" to 84,
|
||||
"Digit9" to 85,
|
||||
"KeyO" to 86,
|
||||
"Digit0" to 87,
|
||||
"KeyP" to 88,
|
||||
"BracketLeft" to 89,
|
||||
"Equal" to 90,
|
||||
"BracketRight" to 91,
|
||||
)
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The keyboard component shows 1 octabe of a (piano) keyboard and
|
||||
* calls the noteDown and noteUp methods when the keys are clicked
|
||||
*/
|
||||
class KeyboardComponent(
|
||||
val title: String = "Keyboard",
|
||||
initialOctave: Int = 4,
|
||||
val initialOctave: Int = 4,
|
||||
val keyboardWidth: Int = 210,
|
||||
val keyboardHeight: Int = keyboardWidth / 2,
|
||||
val rounding: Int = 4,
|
||||
@@ -95,6 +126,13 @@ class KeyboardComponent(
|
||||
requestUpdate()
|
||||
}
|
||||
|
||||
fun handleKeyboardEvent(event: KeyboardEvent) {
|
||||
KEYBOARD_MAPPING[event.code]?.also { midi ->
|
||||
val transpose = (state.octave - initialOctave) * 12
|
||||
console.log("Keyboard event: ", event, midi + transpose)
|
||||
}
|
||||
}
|
||||
|
||||
private fun releaseAllNotes() {
|
||||
// Create a copy of the set to avoid concurrent modification
|
||||
val notesToRelease = state.pressedNotes.toSet()
|
||||
@@ -274,91 +312,5 @@ class KeyboardComponent(
|
||||
// MIDI note numbers for C5 to B5 (one octave)
|
||||
private val BASE_WHITE_KEYS = listOf(60, 62, 64, 65, 67, 69, 71) // C, D, E, F, G, A, B
|
||||
private val BASE_BLACK_KEYS = listOf(61, 63, 66, 68, 70) // C#, D#, F#, G#, A#
|
||||
|
||||
init {
|
||||
defineCss {
|
||||
select(cls(KeyboardCls)) {
|
||||
position(Position.relative)
|
||||
margin(5.px)
|
||||
|
||||
select(cls(KeyboardControlsCls)) {
|
||||
position(Position.relative)
|
||||
display(Display.flex)
|
||||
flexDirection(FlexDirection.row)
|
||||
justifyContent(JustifyContent.spaceBetween)
|
||||
alignItems(AlignItems.center)
|
||||
width(100.prc)
|
||||
height(50.px)
|
||||
marginBottom(10.px)
|
||||
}
|
||||
|
||||
select(cls(KeyboardInfoCls)) {
|
||||
display(Display.flex)
|
||||
flexDirection(FlexDirection.column)
|
||||
alignItems(AlignItems.center)
|
||||
justifyContent(JustifyContent.center)
|
||||
flex("1")
|
||||
}
|
||||
|
||||
select(cls(KeyboardTitleCls)) {
|
||||
textAlign(TextAlign.center)
|
||||
fontSize(1.2.rem)
|
||||
color(Css.currentStyle.mainFontColor)
|
||||
}
|
||||
|
||||
select(cls(KeyboardOctaveCls)) {
|
||||
textAlign(TextAlign.center)
|
||||
fontSize(1.0.rem)
|
||||
color(Css.currentStyle.mainFontColor)
|
||||
marginTop(5.px)
|
||||
}
|
||||
|
||||
select(cls(OctaveButtonCls)) {
|
||||
height(50.px)
|
||||
plain("background-color", Css.currentStyle.buttonBackgroundColor.toString())
|
||||
color(Css.currentStyle.mainFontColor)
|
||||
border("1px solid ${Css.currentStyle.buttonBorderColor}")
|
||||
textAlign(TextAlign.center)
|
||||
lineHeight(50.px)
|
||||
fontSize(1.5.rem)
|
||||
fontWeight(FontWeight.bold)
|
||||
cursor("pointer")
|
||||
plain("-webkit-touch-callout", "none")
|
||||
plain("-webkit-user-select", "none")
|
||||
plain("-moz-user-select", "none")
|
||||
plain("-ms-user-select", "none")
|
||||
plain("user-select", "none")
|
||||
}
|
||||
|
||||
select(cls(KeyboardKeysCls)) {
|
||||
position(Position.relative)
|
||||
}
|
||||
}
|
||||
|
||||
select(cls(WhiteKeyCls)) {
|
||||
plain("fill", "#FFFFFF")
|
||||
plain("stroke", "#000000")
|
||||
plain("stroke-width", "1")
|
||||
}
|
||||
|
||||
select(cls(WhiteKeyPressedCls)) {
|
||||
plain("fill", "#E6E6E6") // 10% darker than white
|
||||
plain("stroke", "#000000")
|
||||
plain("stroke-width", "1")
|
||||
}
|
||||
|
||||
select(cls(BlackKeyCls)) {
|
||||
plain("fill", "#000000")
|
||||
plain("stroke", "#000000")
|
||||
plain("stroke-width", "1")
|
||||
}
|
||||
|
||||
select(cls(BlackKeyPressedCls)) {
|
||||
plain("fill", "#333333") // 10% lighter than black
|
||||
plain("stroke", "#000000")
|
||||
plain("stroke-width", "1")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
package nl.astraeus.vst.ui.css
|
||||
|
||||
import kotlinx.browser.document
|
||||
import nl.astraeus.css.properties.Color
|
||||
import nl.astraeus.css.properties.Measurement
|
||||
import nl.astraeus.css.properties.UserSelect
|
||||
import nl.astraeus.css.properties.hsl
|
||||
import nl.astraeus.css.properties.hsla
|
||||
import nl.astraeus.css.properties.px
|
||||
import nl.astraeus.css.style
|
||||
import nl.astraeus.css.style.ConditionalStyle
|
||||
import nl.astraeus.css.style.DescriptionProvider
|
||||
import nl.astraeus.css.style.Style
|
||||
|
||||
class StyleDefinition(
|
||||
val mainFontColor: Color = hsla(178, 70, 55, 1.0),
|
||||
val mainBackgroundColor: Color = hsl(239, 50, 10),
|
||||
//val entryFontColor: Color = hsl(Css.mainFontColorNumber, 70, 55),
|
||||
val inputBackgroundColor : Color = mainBackgroundColor.lighten(15),
|
||||
val buttonBackgroundColor : Color = mainBackgroundColor.lighten(15),
|
||||
val buttonBorderColor : Color = mainFontColor.changeAlpha(0.25),
|
||||
val buttonBorderWidth : Measurement = 1.px,
|
||||
)
|
||||
|
||||
object NoTextSelectCls : CssName("no-text-select")
|
||||
object SelectedCls : CssName("selected")
|
||||
object ActiveCls : CssName( "active")
|
||||
|
||||
fun Color.hover(): Color = if (Css.currentStyle == Css.darkStyle) {
|
||||
this.lighten(15)
|
||||
} else {
|
||||
this.darken(15)
|
||||
}
|
||||
|
||||
object Css {
|
||||
var dynamicStyles = mutableMapOf<DescriptionProvider, ConditionalStyle.() -> Unit>()
|
||||
|
||||
fun DescriptionProvider.defineCss(conditionalStyle: ConditionalStyle.() -> Unit) {
|
||||
check(!dynamicStyles.containsKey(this)) {
|
||||
"CssId with name ${this.description()} already defined!"
|
||||
}
|
||||
|
||||
updateCss(conditionalStyle)
|
||||
}
|
||||
|
||||
private fun DescriptionProvider.updateCss(conditionalStyle: ConditionalStyle.() -> Unit) {
|
||||
val elementId = this.description()
|
||||
var dynamicStyleElement = document.getElementById(elementId)
|
||||
|
||||
dynamicStyles[this] = conditionalStyle
|
||||
|
||||
if (dynamicStyleElement == null) {
|
||||
dynamicStyleElement = document.createElement("style")
|
||||
dynamicStyleElement.id = elementId
|
||||
|
||||
document.head?.append(dynamicStyleElement)
|
||||
}
|
||||
|
||||
val css = style(conditionalStyle)
|
||||
|
||||
dynamicStyleElement.innerHTML = css.generateCss(minified = CssSettings.minified)
|
||||
}
|
||||
|
||||
var darkStyle = StyleDefinition(
|
||||
)
|
||||
|
||||
var lightStyle = StyleDefinition(
|
||||
mainBackgroundColor = hsl(239+180, 50, 15),
|
||||
)
|
||||
|
||||
var currentStyle: StyleDefinition = darkStyle
|
||||
|
||||
fun updateStyle() {
|
||||
for ((cssId, dynStyle) in dynamicStyles) {
|
||||
cssId.apply {
|
||||
updateCss(dynStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun switchLayout() {
|
||||
currentStyle = if (currentStyle == darkStyle) {
|
||||
lightStyle
|
||||
} else {
|
||||
darkStyle
|
||||
}
|
||||
|
||||
updateStyle()
|
||||
}
|
||||
|
||||
fun Style.transition() {
|
||||
transition("all 0.5s ease")
|
||||
}
|
||||
|
||||
fun Style.noTextSelect() {
|
||||
plain("-webkit-touch-callout", "none")
|
||||
plain("-webkit-user-select", "none")
|
||||
plain("-moz-user-select", "none")
|
||||
plain("-ms-user-select", "none")
|
||||
|
||||
userSelect(UserSelect.none)
|
||||
|
||||
select("::selection") {
|
||||
background("none")
|
||||
}
|
||||
}
|
||||
|
||||
object GenericCss : CssId("generic") {
|
||||
init {
|
||||
fun generateStyle(): String {
|
||||
val css = style {
|
||||
|
||||
}
|
||||
|
||||
return css.generateCss(minified = CssSettings.minified)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
package nl.astraeus.vst.ui.css
|
||||
|
||||
import nl.astraeus.css.style.DescriptionProvider
|
||||
import nl.astraeus.css.style.cls
|
||||
|
||||
private val CAPITAL_LETTER = Regex("[A-Z]")
|
||||
|
||||
fun String.hyphenize(): String {
|
||||
@@ -39,6 +36,19 @@ private fun nextShortId(): String {
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
interface DescriptionProvider {
|
||||
fun description(): String
|
||||
}
|
||||
|
||||
class ValueDescriptionProvider(
|
||||
val value: String
|
||||
) : DescriptionProvider {
|
||||
override fun description() = value
|
||||
}
|
||||
|
||||
fun cls(name: String): DescriptionProvider = ValueDescriptionProvider(".$name")
|
||||
fun cls(name: DescriptionProvider): DescriptionProvider = ValueDescriptionProvider(".$name")
|
||||
|
||||
abstract class CssName(
|
||||
overrideName: String? = null
|
||||
) : DescriptionProvider {
|
||||
@@ -61,6 +71,7 @@ abstract class CssName(
|
||||
override fun description() = name
|
||||
}
|
||||
|
||||
|
||||
open class CssId(name: String) : DescriptionProvider {
|
||||
val name: String = if (CssSettings.shortId) {
|
||||
nextShortId()
|
||||
|
||||
@@ -3,21 +3,8 @@ package nl.astraeus.vst.ui.view
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import kotlinx.html.org.w3c.dom.events.Event
|
||||
import nl.astraeus.css.properties.Display
|
||||
import nl.astraeus.css.properties.FlexDirection
|
||||
import nl.astraeus.css.properties.Position
|
||||
import nl.astraeus.css.properties.Transform
|
||||
import nl.astraeus.css.properties.hsla
|
||||
import nl.astraeus.css.properties.prc
|
||||
import nl.astraeus.css.properties.px
|
||||
import nl.astraeus.css.properties.rem
|
||||
import nl.astraeus.css.properties.vh
|
||||
import nl.astraeus.css.properties.vw
|
||||
import nl.astraeus.css.style.cls
|
||||
import nl.astraeus.komp.HtmlBuilder
|
||||
import nl.astraeus.komp.Komponent
|
||||
import nl.astraeus.vst.ui.css.Css
|
||||
import nl.astraeus.vst.ui.css.Css.defineCss
|
||||
import nl.astraeus.vst.ui.css.CssId
|
||||
import nl.astraeus.vst.ui.css.CssName
|
||||
|
||||
@@ -53,46 +40,6 @@ class BaseVstView(
|
||||
object StartSplashCss : CssName()
|
||||
object StartBoxCss : CssName()
|
||||
object StartButtonCss : CssName()
|
||||
|
||||
init {
|
||||
defineCss {
|
||||
select(BaseVstCss.cls()) {
|
||||
display(Display.flex)
|
||||
flexDirection(FlexDirection.column)
|
||||
}
|
||||
|
||||
select(cls(StartSplashCss)) {
|
||||
position(Position.fixed)
|
||||
left(0.px)
|
||||
top(0.px)
|
||||
width(100.vw)
|
||||
height(100.vh)
|
||||
zIndex(100)
|
||||
backgroundColor(hsla(32, 0, 5, 0.65))
|
||||
|
||||
select(cls(StartBoxCss)) {
|
||||
position(Position.relative)
|
||||
left(25.vw)
|
||||
top(25.vh)
|
||||
width(50.vw)
|
||||
height(50.vh)
|
||||
backgroundColor(hsla(239, 50, 10, 1.0))
|
||||
borderColor(Css.currentStyle.mainFontColor)
|
||||
borderWidth(2.px)
|
||||
|
||||
select(cls(StartButtonCss)) {
|
||||
position(Position.absolute)
|
||||
left(50.prc)
|
||||
top(50.prc)
|
||||
transform(Transform("translate(-50%, -50%)"))
|
||||
padding(1.rem)
|
||||
backgroundColor(Css.currentStyle.buttonBackgroundColor)
|
||||
cursor("pointer")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
package nl.astraeus.vst.base.web
|
||||
|
||||
import kotlinx.html.*
|
||||
import kotlinx.html.body
|
||||
import kotlinx.html.head
|
||||
import kotlinx.html.html
|
||||
import kotlinx.html.link
|
||||
import kotlinx.html.meta
|
||||
import kotlinx.html.script
|
||||
import kotlinx.html.stream.appendHTML
|
||||
import kotlinx.html.title
|
||||
|
||||
fun generateIndex(
|
||||
title: String,
|
||||
@@ -15,6 +21,9 @@ fun generateIndex(
|
||||
result.appendHTML(true).html {
|
||||
head {
|
||||
title { +title }
|
||||
link(rel = "stylesheet", href = "/vst-ui-base.css") {
|
||||
type = "text/css"
|
||||
}
|
||||
}
|
||||
body {
|
||||
script {
|
||||
@@ -26,7 +35,7 @@ fun generateIndex(
|
||||
} else {
|
||||
result.appendHTML(true).html {
|
||||
head {
|
||||
title { +"VST Chip" }
|
||||
title { +title }
|
||||
meta {
|
||||
httpEquiv = "refresh"
|
||||
content = "0; url=/patch/$patch"
|
||||
|
||||
@@ -44,6 +44,7 @@ class RequestHandler(
|
||||
pathHandler.addExactPath("/", patchHandler)
|
||||
pathHandler.addExactPath("/index.html", patchHandler)
|
||||
pathHandler.addPrefixPath("/patch", patchHandler)
|
||||
pathHandler.addPrefixPath("/vst-ui-base.css", WebResourceHandler("text/css", "vst-ui-base.css"))
|
||||
//pathHandler.addPrefixPath("/data", urlDataHandler)
|
||||
pathHandler.addExactPath("/ws", WebsocketConnectHandler)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package nl.astraeus.vst.base.web
|
||||
|
||||
import io.undertow.server.HttpHandler
|
||||
import io.undertow.server.HttpServerExchange
|
||||
import io.undertow.util.Headers
|
||||
import io.undertow.util.StatusCodes
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class WebResourceHandler(
|
||||
val mimeType: String,
|
||||
val resourceName: String,
|
||||
) : HttpHandler {
|
||||
|
||||
override fun handleRequest(exchange: HttpServerExchange) {
|
||||
val stream = Thread.currentThread().contextClassLoader.getResourceAsStream(resourceName)
|
||||
|
||||
if (stream != null) {
|
||||
exchange.responseHeaders.put(Headers.CONTENT_TYPE, mimeType)
|
||||
exchange.responseSender.send(ByteBuffer.wrap(stream.readBytes()))
|
||||
} else {
|
||||
exchange.responseHeaders.put(Headers.CONTENT_TYPE, "text/plain")
|
||||
exchange.statusCode = StatusCodes.NOT_FOUND
|
||||
exchange.responseSender.send("Not found", Charsets.UTF_8)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
282
vst-ui-base/src/jvmMain/resources/vst-ui-base.css
Normal file
282
vst-ui-base/src/jvmMain/resources/vst-ui-base.css
Normal file
@@ -0,0 +1,282 @@
|
||||
:root {
|
||||
--vst-main-font-color: hsla(178, 70%, 55%, 1);
|
||||
--vst-main-background-color: hsl(239, 50%, 10%);
|
||||
--vst-input-background-color: hsl(239, 50%, 25%);
|
||||
--vst-button-background-color: hsl(239, 50%, 25%);
|
||||
--vst-button-border-color: hsla(178, 70%, 55%, 0.25);
|
||||
--vst-button-border-width: 1px;
|
||||
--vst-main-font-color-darken-20: hsl(178, 70%, 35%);
|
||||
--vst-main-font-color-darken-40: hsl(178, 70%, 15%);
|
||||
}
|
||||
|
||||
*, *:before,*:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
|
||||
background-color: var(--vst-main-background-color);
|
||||
color: var(--vst-main-font-color);
|
||||
|
||||
font-family: JetbrainsMono, monospace;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
|
||||
//transition()
|
||||
//noTextSelect()
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
background-color: var(--vst-input-background-color);
|
||||
color: var(--vst-main-font-color);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.vst-main-div-css {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
select {
|
||||
appearance: none;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
width: 20rem;
|
||||
padding: 0.5rem 2rem 0.5rem 0.5rem;
|
||||
color: var(--vst-main-font-color);
|
||||
border-radius: 0.25rem;
|
||||
background: var(--vst-input-background-color);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
select(cls(ButtonCss)) {
|
||||
margin(1.rem)
|
||||
commonButton()
|
||||
}
|
||||
select(cls(ButtonBarCss)) {
|
||||
margin(1.rem, 0.px)
|
||||
commonButton()
|
||||
}
|
||||
select(cls(NoteBarCss)) {
|
||||
minHeight(4.rem)
|
||||
}
|
||||
select(cls(StartSplashCss)) {
|
||||
position(Position.fixed)
|
||||
left(0.px)
|
||||
top(0.px)
|
||||
width(100.vw)
|
||||
height(100.vh)
|
||||
zIndex(100)
|
||||
backgroundColor(hsla(32, 0, 5, 0.65))
|
||||
|
||||
select(cls(StartBoxCss)) {
|
||||
position(Position.relative)
|
||||
left(25.vw)
|
||||
top(25.vh)
|
||||
width(50.vw)
|
||||
height(50.vh)
|
||||
backgroundColor(hsla(239, 50, 10, 1.0))
|
||||
borderColor(Css.currentStyle.mainFontColor)
|
||||
borderWidth(2.px)
|
||||
|
||||
select(cls(StartButtonCss)) {
|
||||
position(Position.absolute)
|
||||
left(50.prc)
|
||||
top(50.prc)
|
||||
transform(Transform("translate(-50%, -50%)"))
|
||||
padding(1.rem)
|
||||
backgroundColor(Css.currentStyle.buttonBackgroundColor)
|
||||
cursor("pointer")
|
||||
}
|
||||
}
|
||||
}
|
||||
select(ControlsCss.cls()) {
|
||||
display(Display.flex)
|
||||
flexDirection(FlexDirection.row)
|
||||
justifyContent(JustifyContent.flexStart)
|
||||
alignItems(AlignItems.center)
|
||||
margin(1.rem)
|
||||
padding(1.rem)
|
||||
backgroundColor(Css.currentStyle.mainBackgroundColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
.vst-base-vst-css {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.vst-start-splash-css {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 100;
|
||||
background-color: hsla(32, 0%, 5%, 0.65);
|
||||
}
|
||||
|
||||
.vst-start-splash-css .vst-start-box-css {
|
||||
position: relative;
|
||||
left: 25vw;
|
||||
top: 25vh;
|
||||
width: 50vw;
|
||||
height: 50vh;
|
||||
background-color: hsla(239, 50%, 10%, 1);
|
||||
border-color: var(--vst-main-font-color);
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.vst-start-splash-css .vst-start-box-css .vst-start-button-css {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 1rem;
|
||||
background-color: var(--vst-button-background-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vst-knob-cls {
|
||||
position: relative;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.vst-knob-cls.vst-active {
|
||||
background-color: hsla(178, 70%, 55%, 0.1);
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.vst-knob-cls .vst-knob-svg-cls {
|
||||
stroke: none;
|
||||
stroke-opacity: 1;
|
||||
fill: none;
|
||||
fill-opacity: 0;
|
||||
position: absolute;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.vst-knob-cls .vst-knob-svg-cls.vst-active {
|
||||
color: var(--vst-main-font-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.vst-knob-cls .vst-knob-text-cls {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 1em;
|
||||
color: var(--vst-main-font-color);
|
||||
}
|
||||
|
||||
.vst-knob-cls .vst-knob-value-cls {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 48%;
|
||||
text-align: center;
|
||||
font-size: 1.1em;
|
||||
color: var(--vst-main-font-color);
|
||||
}
|
||||
|
||||
.vst-knob-volume-cls {
|
||||
fill: transparent;
|
||||
stroke: var(--vst-main-font-color);
|
||||
color: var(--vst-main-font-color);
|
||||
stroke-width: 8;
|
||||
fill-opacity: 0.5;
|
||||
}
|
||||
|
||||
.vst-knob-volume-background-cls {
|
||||
fill: transparent;
|
||||
stroke: var(--vst-main-font-color-darken-40);
|
||||
color: var(--vst-main-font-color-darken-20);
|
||||
stroke-width: 5;
|
||||
fill-opacity: 0.5;
|
||||
}
|
||||
|
||||
.vst-keyboard-cls {
|
||||
position: relative;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.vst-keyboard-cls .vst-keyboard-controls-cls {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.vst-keyboard-cls .vst-keyboard-info-cls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.vst-keyboard-cls .vst-keyboard-title-cls {
|
||||
text-align: center;
|
||||
font-size: 1.2rem;
|
||||
color: var(--vst-main-font-color);
|
||||
}
|
||||
|
||||
.vst-keyboard-cls .vst-keyboard-octave-cls {
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
color: var(--vst-main-font-color);
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.vst-keyboard-cls .vst-octave-button-cls {
|
||||
height: 50px;
|
||||
background-color: var(--vst-button-background-color);
|
||||
color: var(--vst-main-font-color);
|
||||
border: 1px solid var(--vst-button-border-color);
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.vst-keyboard-cls .vst-keyboard-keys-cls {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.vst-white-key-cls {
|
||||
fill: #ffffff;
|
||||
stroke: #000000;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.vst-white-key-pressed-cls {
|
||||
fill: #e6e6e6;
|
||||
stroke: #000000;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.vst-black-key-cls {
|
||||
fill: #000000;
|
||||
stroke: #000000;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.vst-black-key-pressed-cls {
|
||||
fill: #333333;
|
||||
stroke: #000000;
|
||||
stroke-width: 1;
|
||||
}
|
||||
Reference in New Issue
Block a user