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 = {}
|
val onNoteUp: (Int) -> Unit = {}
|
||||||
) : Komponent() {
|
) : 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
|
// Current octave with range validation
|
||||||
private var _octave: Int = initialOctave
|
|
||||||
var octave: Int
|
var octave: Int
|
||||||
get() = _octave
|
get() = state.octave
|
||||||
set(value) {
|
set(value) {
|
||||||
_octave = when {
|
updateOctave(value)
|
||||||
value < MIN_OCTAVE -> MIN_OCTAVE
|
|
||||||
value > MAX_OCTAVE -> MAX_OCTAVE
|
|
||||||
else -> value
|
|
||||||
}
|
|
||||||
requestUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set to track which notes are currently pressed
|
// Update state with functions that return new state
|
||||||
private val pressedNotes = mutableSetOf<Int>()
|
private fun updateOctave(newOctave: Int) {
|
||||||
|
state = state.copy(octave = newOctave.coerceIn(MIN_OCTAVE, MAX_OCTAVE))
|
||||||
|
requestUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
// MIDI note numbers for one octave
|
// MIDI note numbers for one octave
|
||||||
private val whiteKeys = BASE_WHITE_KEYS
|
private val whiteKeys = BASE_WHITE_KEYS
|
||||||
@@ -77,25 +83,25 @@ class KeyboardComponent(
|
|||||||
)
|
)
|
||||||
|
|
||||||
fun noteDown(midiNote: Int) {
|
fun noteDown(midiNote: Int) {
|
||||||
pressedNotes.add(midiNote)
|
state = state.copy(pressedNotes = state.pressedNotes + midiNote)
|
||||||
onNoteDown(midiNote)
|
onNoteDown(midiNote)
|
||||||
requestUpdate()
|
requestUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun noteUp(midiNote: Int) {
|
fun noteUp(midiNote: Int) {
|
||||||
pressedNotes.remove(midiNote)
|
state = state.copy(pressedNotes = state.pressedNotes - midiNote)
|
||||||
onNoteUp(midiNote)
|
onNoteUp(midiNote)
|
||||||
requestUpdate()
|
requestUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun releaseAllNotes() {
|
private fun releaseAllNotes() {
|
||||||
// Create a copy of the set to avoid concurrent modification
|
// Create a copy of the set to avoid concurrent modification
|
||||||
val notesToRelease = pressedNotes.toSet()
|
val notesToRelease = state.pressedNotes.toSet()
|
||||||
for (note in notesToRelease) {
|
for (note in notesToRelease) {
|
||||||
noteUp(note)
|
noteUp(note)
|
||||||
}
|
}
|
||||||
// Clear the set just to be safe
|
// Just to be safe, clear all pressed notes
|
||||||
pressedNotes.clear()
|
state = state.copy(pressedNotes = emptySet())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOctaveOffset(): Int = (octave - OCTAVE_BASE) * NOTES_PER_OCTAVE
|
private fun getOctaveOffset(): Int = (octave - OCTAVE_BASE) * NOTES_PER_OCTAVE
|
||||||
@@ -142,7 +148,7 @@ class KeyboardComponent(
|
|||||||
+"<"
|
+"<"
|
||||||
onMouseDownFunction = { event ->
|
onMouseDownFunction = { event ->
|
||||||
if (event is MouseEvent) {
|
if (event is MouseEvent) {
|
||||||
octave--
|
updateOctave(octave - 1)
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,7 +173,7 @@ class KeyboardComponent(
|
|||||||
+">"
|
+">"
|
||||||
onMouseDownFunction = { event ->
|
onMouseDownFunction = { event ->
|
||||||
if (event is MouseEvent) {
|
if (event is MouseEvent) {
|
||||||
octave++
|
updateOctave(octave + 1)
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,7 +216,7 @@ class KeyboardComponent(
|
|||||||
private fun SVG.renderWhiteKeys() {
|
private fun SVG.renderWhiteKeys() {
|
||||||
for (i in 0 until WHITE_KEYS_PER_OCTAVE) {
|
for (i in 0 until WHITE_KEYS_PER_OCTAVE) {
|
||||||
val midiNote = whiteKeys[i] + getOctaveOffset()
|
val midiNote = whiteKeys[i] + getOctaveOffset()
|
||||||
val isPressed = pressedNotes.contains(midiNote)
|
val isPressed = state.pressedNotes.contains(midiNote)
|
||||||
rect(
|
rect(
|
||||||
i * whiteKeyWidth,
|
i * whiteKeyWidth,
|
||||||
0,
|
0,
|
||||||
@@ -226,7 +232,7 @@ class KeyboardComponent(
|
|||||||
private fun SVG.renderBlackKeys() {
|
private fun SVG.renderBlackKeys() {
|
||||||
for (i in 0 until BLACK_KEYS_PER_OCTAVE) {
|
for (i in 0 until BLACK_KEYS_PER_OCTAVE) {
|
||||||
val midiNote = blackKeys[i] + getOctaveOffset()
|
val midiNote = blackKeys[i] + getOctaveOffset()
|
||||||
val isPressed = pressedNotes.contains(midiNote)
|
val isPressed = state.pressedNotes.contains(midiNote)
|
||||||
rect(
|
rect(
|
||||||
blackKeyPositions[i],
|
blackKeyPositions[i],
|
||||||
0,
|
0,
|
||||||
|
|||||||
Reference in New Issue
Block a user