Refactor KeyboardComponent to use immutable state for octave and pressed notes
Replaced mutable state with an immutable `KeyboardState` data class to track octave and pressed notes. Updated state management logic with functional updates for improved consistency and immutability. Simplified note handling and rendering to reference the unified state object.
This commit is contained in:
@@ -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<Int> = 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<Int>()
|
||||
// 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,
|
||||
|
||||
Reference in New Issue
Block a user