diff --git a/src/jsMain/kotlin/nl/astraeus/vst/ui/components/KeyboardComponent.kt b/src/jsMain/kotlin/nl/astraeus/vst/ui/components/KeyboardComponent.kt index db787fe..a0f6c50 100644 --- a/src/jsMain/kotlin/nl/astraeus/vst/ui/components/KeyboardComponent.kt +++ b/src/jsMain/kotlin/nl/astraeus/vst/ui/components/KeyboardComponent.kt @@ -42,21 +42,27 @@ class KeyboardComponent( val onNoteUp: (Int) -> Unit = {} ) : Komponent() { + // Define a data class for keyboard state + data class KeyboardState( + val octave: Int, + val pressedNotes: Set = emptySet() + ) + + // Use immutable state + private var state = KeyboardState(initialOctave) + // Current octave with range validation - private var _octave: Int = initialOctave var octave: Int - get() = _octave + get() = state.octave set(value) { - _octave = when { - value < MIN_OCTAVE -> MIN_OCTAVE - value > MAX_OCTAVE -> MAX_OCTAVE - else -> value - } - requestUpdate() + updateOctave(value) } - // Set to track which notes are currently pressed - private val pressedNotes = mutableSetOf() + // Update state with functions that return new state + private fun updateOctave(newOctave: Int) { + state = state.copy(octave = newOctave.coerceIn(MIN_OCTAVE, MAX_OCTAVE)) + requestUpdate() + } // MIDI note numbers for one octave private val whiteKeys = BASE_WHITE_KEYS @@ -77,25 +83,25 @@ class KeyboardComponent( ) fun noteDown(midiNote: Int) { - pressedNotes.add(midiNote) + state = state.copy(pressedNotes = state.pressedNotes + midiNote) onNoteDown(midiNote) requestUpdate() } fun noteUp(midiNote: Int) { - pressedNotes.remove(midiNote) + state = state.copy(pressedNotes = state.pressedNotes - midiNote) onNoteUp(midiNote) requestUpdate() } private fun releaseAllNotes() { // Create a copy of the set to avoid concurrent modification - val notesToRelease = pressedNotes.toSet() + val notesToRelease = state.pressedNotes.toSet() for (note in notesToRelease) { noteUp(note) } - // Clear the set just to be safe - pressedNotes.clear() + // Just to be safe, clear all pressed notes + state = state.copy(pressedNotes = emptySet()) } private fun getOctaveOffset(): Int = (octave - OCTAVE_BASE) * NOTES_PER_OCTAVE @@ -142,7 +148,7 @@ class KeyboardComponent( +"<" onMouseDownFunction = { event -> if (event is MouseEvent) { - octave-- + updateOctave(octave - 1) event.preventDefault() } } @@ -167,7 +173,7 @@ class KeyboardComponent( +">" onMouseDownFunction = { event -> if (event is MouseEvent) { - octave++ + updateOctave(octave + 1) event.preventDefault() } } @@ -210,7 +216,7 @@ class KeyboardComponent( private fun SVG.renderWhiteKeys() { for (i in 0 until WHITE_KEYS_PER_OCTAVE) { val midiNote = whiteKeys[i] + getOctaveOffset() - val isPressed = pressedNotes.contains(midiNote) + val isPressed = state.pressedNotes.contains(midiNote) rect( i * whiteKeyWidth, 0, @@ -226,7 +232,7 @@ class KeyboardComponent( private fun SVG.renderBlackKeys() { for (i in 0 until BLACK_KEYS_PER_OCTAVE) { val midiNote = blackKeys[i] + getOctaveOffset() - val isPressed = pressedNotes.contains(midiNote) + val isPressed = state.pressedNotes.contains(midiNote) rect( blackKeyPositions[i], 0,