Skip to content

Commit

Permalink
feat: [project-sequencer-statemachine] CursorStateの更新を行うようにする (#2520)
Browse files Browse the repository at this point in the history
Co-authored-by: Hiroshiba <[email protected]>
  • Loading branch information
sigprogramming and Hiroshiba authored Feb 8, 2025
1 parent 036029a commit 8cad767
Show file tree
Hide file tree
Showing 13 changed files with 335 additions and 213 deletions.
12 changes: 12 additions & 0 deletions src/composables/useSequencerStateMachine.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { computed, ref } from "vue";
import { useCommandOrControlKey, useShiftKey } from "./useModifierKey";
import {
ComputedRefs,
IdleStateId,
Expand All @@ -12,6 +13,9 @@ export const useSequencerStateMachine = (
store: PartialStore,
initialStateId: IdleStateId,
) => {
const isShiftKeyDown = useShiftKey();
const isCommandOrCtrlKeyDown = useCommandOrControlKey();

const computedRefs: ComputedRefs = {
snapTicks: computed(() =>
getNoteDuration(store.state.sequencerSnapType, store.state.tpqn),
Expand All @@ -21,14 +25,19 @@ export const useSequencerStateMachine = (
notesInSelectedTrack: computed(() => store.getters.SELECTED_TRACK.notes),
selectedNoteIds: computed(() => store.getters.SELECTED_NOTE_IDS),
editorFrameRate: computed(() => store.state.editorFrameRate),
isShiftKeyDown: computed(() => isShiftKeyDown.value),
isCommandOrCtrlKeyDown: computed(() => isCommandOrCtrlKeyDown.value),
};

const refs: Refs = {
nowPreviewing: ref(false),
previewNotes: ref([]),
previewRectForRectSelect: ref(undefined),
previewPitchEdit: ref(undefined),
cursorState: ref("UNSET"),
guideLineTicks: ref(0),
};

const stateMachine = createSequencerStateMachine(
{
...computedRefs,
Expand All @@ -37,13 +46,16 @@ export const useSequencerStateMachine = (
},
initialStateId,
);

return {
stateMachine,
nowPreviewing: computed(() => refs.nowPreviewing.value),
previewNotes: computed(() => refs.previewNotes.value),
previewRectForRectSelect: computed(
() => refs.previewRectForRectSelect.value,
),
previewPitchEdit: computed(() => refs.previewPitchEdit.value),
cursorState: computed(() => refs.cursorState.value),
guideLineTicks: computed(() => refs.guideLineTicks.value),
};
};
14 changes: 13 additions & 1 deletion src/sing/sequencerStateMachine/common.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ComputedRef, Ref } from "vue";
import { StateDefinitions } from "@/sing/stateMachine";
import { Rect } from "@/sing/utility";
import { PREVIEW_SOUND_DURATION } from "@/sing/viewHelper";
import { CursorState, PREVIEW_SOUND_DURATION } from "@/sing/viewHelper";
import { Store } from "@/store";
import { Note, SequencerEditTarget } from "@/store/type";
import { isOnCommandOrCtrlKeyDown } from "@/store/utility";
Expand All @@ -18,23 +18,32 @@ export type PositionOnSequencer = {

export type Input =
| {
readonly type: "keyboardEvent";
readonly targetArea: "SequencerBody";
readonly keyboardEvent: KeyboardEvent;
}
| {
readonly type: "mouseEvent";
readonly targetArea: "SequencerBody";
readonly mouseEvent: MouseEvent;
readonly cursorPos: PositionOnSequencer;
}
| {
readonly type: "mouseEvent";
readonly targetArea: "Note";
readonly mouseEvent: MouseEvent;
readonly cursorPos: PositionOnSequencer;
readonly note: Note;
}
| {
readonly type: "mouseEvent";
readonly targetArea: "NoteLeftEdge";
readonly mouseEvent: MouseEvent;
readonly cursorPos: PositionOnSequencer;
readonly note: Note;
}
| {
readonly type: "mouseEvent";
readonly targetArea: "NoteRightEdge";
readonly mouseEvent: MouseEvent;
readonly cursorPos: PositionOnSequencer;
Expand All @@ -48,6 +57,8 @@ export type ComputedRefs = {
readonly notesInSelectedTrack: ComputedRef<Note[]>;
readonly selectedNoteIds: ComputedRef<Set<NoteId>>;
readonly editorFrameRate: ComputedRef<number>;
readonly isShiftKeyDown: ComputedRef<boolean>;
readonly isCommandOrCtrlKeyDown: ComputedRef<boolean>;
};

export type Refs = {
Expand All @@ -59,6 +70,7 @@ export type Refs = {
| { type: "erase"; startFrame: number; frameLength: number }
| undefined
>;
readonly cursorState: Ref<CursorState>;
readonly guideLineTicks: Ref<number>;
};

Expand Down
27 changes: 16 additions & 11 deletions src/sing/sequencerStateMachine/states/addNoteState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export class AddNoteState
const noteEndPos = noteToAdd.position + noteToAdd.duration;

context.previewNotes.value = [noteToAdd];
context.cursorState.value = "DRAW";
context.guideLineTicks.value = noteEndPos;
context.nowPreviewing.value = true;

Expand Down Expand Up @@ -121,17 +122,20 @@ export class AddNoteState
if (this.innerContext == undefined) {
throw new Error("innerContext is undefined.");
}
const mouseButton = getButton(input.mouseEvent);
if (input.targetArea === "SequencerBody") {
if (input.mouseEvent.type === "mousemove") {
this.currentCursorPos = input.cursorPos;
this.innerContext.executePreviewProcess = true;
} else if (
input.mouseEvent.type === "mouseup" &&
mouseButton === "LEFT_BUTTON"
) {
this.applyPreview = true;
setNextState(this.returnStateId, undefined);
if (input.type === "mouseEvent") {
const mouseButton = getButton(input.mouseEvent);

if (input.targetArea === "SequencerBody") {
if (input.mouseEvent.type === "mousemove") {
this.currentCursorPos = input.cursorPos;
this.innerContext.executePreviewProcess = true;
} else if (
input.mouseEvent.type === "mouseup" &&
mouseButton === "LEFT_BUTTON"
) {
this.applyPreview = true;
setNextState(this.returnStateId, undefined);
}
}
}
}
Expand Down Expand Up @@ -161,6 +165,7 @@ export class AddNoteState
}

context.previewNotes.value = [];
context.cursorState.value = "UNSET";
context.nowPreviewing.value = false;
}
}
35 changes: 20 additions & 15 deletions src/sing/sequencerStateMachine/states/drawPitchState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export class DrawPitchState
data: [this.cursorPosAtStart.frequency],
startFrame: this.cursorPosAtStart.frame,
};
context.cursorState.value = "UNSET";
context.nowPreviewing.value = true;

const previewIfNeeded = () => {
Expand Down Expand Up @@ -164,21 +165,24 @@ export class DrawPitchState
if (context.previewPitchEdit.value.type !== "draw") {
throw new Error("previewPitchEdit.type is not draw.");
}
const mouseButton = getButton(input.mouseEvent);
if (input.targetArea === "SequencerBody") {
if (input.mouseEvent.type === "mousemove") {
this.currentCursorPos = input.cursorPos;
this.innerContext.executePreviewProcess = true;
} else if (
input.mouseEvent.type === "mouseup" &&
mouseButton === "LEFT_BUTTON"
) {
// カーソルを動かさずにマウスのボタンを離したときに1フレームのみの変更になり、
// 1フレームの変更はピッチ編集ラインとして表示されないので、無視する
const previewPitchEditDataLength =
context.previewPitchEdit.value.data.length;
this.applyPreview = previewPitchEditDataLength >= 2;
setNextState(this.returnStateId, undefined);
if (input.type === "mouseEvent") {
const mouseButton = getButton(input.mouseEvent);

if (input.targetArea === "SequencerBody") {
if (input.mouseEvent.type === "mousemove") {
this.currentCursorPos = input.cursorPos;
this.innerContext.executePreviewProcess = true;
} else if (
input.mouseEvent.type === "mouseup" &&
mouseButton === "LEFT_BUTTON"
) {
// カーソルを動かさずにマウスのボタンを離したときに1フレームのみの変更になり、
// 1フレームの変更はピッチ編集ラインとして表示されないので、無視する
const previewPitchEditDataLength =
context.previewPitchEdit.value.data.length;
this.applyPreview = previewPitchEditDataLength >= 2;
setNextState(this.returnStateId, undefined);
}
}
}
}
Expand Down Expand Up @@ -211,6 +215,7 @@ export class DrawPitchState
}

context.previewPitchEdit.value = undefined;
context.cursorState.value = "UNSET";
context.nowPreviewing.value = false;
}
}
61 changes: 40 additions & 21 deletions src/sing/sequencerStateMachine/states/drawPitchToolIdleState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ export class DrawPitchToolIdleState
{
readonly id = "drawPitchToolIdle";

onEnter() {}
onEnter(context: Context) {
this.updateCursorState(context, context.isCommandOrCtrlKeyDown.value);
}

process({
input,
Expand All @@ -23,29 +25,46 @@ export class DrawPitchToolIdleState
context: Context;
setNextState: SetNextState<SequencerStateDefinitions>;
}) {
const mouseButton = getButton(input.mouseEvent);
const selectedTrackId = context.selectedTrackId.value;
if (input.type === "keyboardEvent") {
this.updateCursorState(
context,
isOnCommandOrCtrlKeyDown(input.keyboardEvent),
);
} else if (input.type === "mouseEvent") {
const mouseButton = getButton(input.mouseEvent);
const selectedTrackId = context.selectedTrackId.value;

if (
input.mouseEvent.type === "mousedown" &&
mouseButton === "LEFT_BUTTON" &&
input.targetArea === "SequencerBody"
) {
if (isOnCommandOrCtrlKeyDown(input.mouseEvent)) {
setNextState("erasePitch", {
cursorPosAtStart: input.cursorPos,
targetTrackId: selectedTrackId,
returnStateId: this.id,
});
} else {
setNextState("drawPitch", {
cursorPosAtStart: input.cursorPos,
targetTrackId: selectedTrackId,
returnStateId: this.id,
});
if (
input.mouseEvent.type === "mousedown" &&
mouseButton === "LEFT_BUTTON" &&
input.targetArea === "SequencerBody"
) {
if (isOnCommandOrCtrlKeyDown(input.mouseEvent)) {
setNextState("erasePitch", {
cursorPosAtStart: input.cursorPos,
targetTrackId: selectedTrackId,
returnStateId: this.id,
});
} else {
setNextState("drawPitch", {
cursorPosAtStart: input.cursorPos,
targetTrackId: selectedTrackId,
returnStateId: this.id,
});
}
}
}
}

onExit() {}
onExit(context: Context) {
context.cursorState.value = "UNSET";
}

private updateCursorState(context: Context, isCommandOrCtrlKeyDown: boolean) {
if (isCommandOrCtrlKeyDown) {
context.cursorState.value = "ERASE";
} else {
context.cursorState.value = "DRAW";
}
}
}
Loading

0 comments on commit 8cad767

Please sign in to comment.