Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [project-sequencer-statemachine] 初期ステートの設定周りを変更 #2518

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions src/composables/useSequencerStateMachine.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { computed, ref } from "vue";
import {
ComputedRefs,
IdleStateId,
PartialStore,
Refs,
} from "@/sing/sequencerStateMachine/common";
import { getNoteDuration } from "@/sing/domain";
import { createSequencerStateMachine } from "@/sing/sequencerStateMachine";

export const useSequencerStateMachine = (store: PartialStore) => {
export const useSequencerStateMachine = (
store: PartialStore,
initialStateId: IdleStateId,
) => {
const computedRefs: ComputedRefs = {
snapTicks: computed(() =>
getNoteDuration(store.state.sequencerSnapType, store.state.tpqn),
Expand All @@ -25,11 +29,14 @@ export const useSequencerStateMachine = (store: PartialStore) => {
previewPitchEdit: ref(undefined),
guideLineTicks: ref(0),
};
const stateMachine = createSequencerStateMachine({
...computedRefs,
...refs,
store,
});
const stateMachine = createSequencerStateMachine(
{
...computedRefs,
...refs,
store,
},
initialStateId,
);
return {
stateMachine,
nowPreviewing: computed(() => refs.nowPreviewing.value),
Expand Down
8 changes: 6 additions & 2 deletions src/sing/sequencerStateMachine/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Context,
IdleStateId,
Input,
SequencerStateDefinitions,
} from "@/sing/sequencerStateMachine/common";
Expand All @@ -17,7 +18,10 @@ import { SelectNotesWithRectState } from "@/sing/sequencerStateMachine/states/se
import { DrawPitchState } from "@/sing/sequencerStateMachine/states/drawPitchState";
import { ErasePitchState } from "@/sing/sequencerStateMachine/states/erasePitchState";

export const createSequencerStateMachine = (context: Context) => {
export const createSequencerStateMachine = (
context: Context,
initialStateId: IdleStateId,
) => {
return new StateMachine<SequencerStateDefinitions, Input, Context>(
{
selectNotesToolIdle: () => new SelectNotesToolIdleState(),
Expand All @@ -32,7 +36,7 @@ export const createSequencerStateMachine = (context: Context) => {
drawPitch: (args) => new DrawPitchState(args),
erasePitch: (args) => new ErasePitchState(args),
},
new SelectNotesToolIdleState(),
context,
initialStateId,
);
};
52 changes: 48 additions & 4 deletions src/sing/stateMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ type StateFactories<T extends StateDefinition[], Input, Context> = {
) => State<T, Input, Context> & { readonly id: U };
};

/**
* 初期ステートとして設定可能なステートのIDを表す型。
Copy link
Member

@Hiroshiba Hiroshiba Feb 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

「設定可能なステートのID」と書いていて1つの ID ではないことがわかるので、「表す型」は自明かも。

Suggested change
* 初期ステートとして設定可能なステートのIDを表す型
* 初期ステートとして設定可能なステートのID

だけど他の定義にはそう書いてあるので、ここは書いた方が良さそう!
変えるなら全部変えた方が良さそう。

*/
type InitialStateId<T extends StateDefinition[]> = T[number] extends infer U
? U extends { id: string; factoryArgs: undefined }
? U["id"]
: never
: never;

/**
* ステートマシンを表すクラス。
*
Expand All @@ -111,6 +120,7 @@ export class StateMachine<
private readonly context: Context;

private currentState: State<StateDefinitions, Input, Context>;
private isDisposed = false;

/**
* ステートマシンの現在のステートのID。
Expand All @@ -120,19 +130,36 @@ export class StateMachine<
}

/**
* @param initialState ステートマシンの初期ステート。
* @param context ステート間で共有されるコンテキスト。
* @param stateFactories ステートのファクトリー関数。
* @param context ステートマシンのコンテキスト。
* @param initialStateId ステートマシンの初期ステートのID。
*/
constructor(
stateFactories: StateFactories<StateDefinitions, Input, Context>,
initialState: State<StateDefinitions, Input, Context>,
context: Context,
initialStateId: InitialStateId<StateDefinitions>,
) {
this.stateFactories = stateFactories;
this.context = context;

this.currentState = initialState;
this.currentState = stateFactories[initialStateId](undefined);
}

/**
* ステートを遷移し、ライフサイクルイベントを実行する。
*
* @param id 遷移先のステートのID。
* @param factoryArgs 遷移先のステートのファクトリー関数の引数。
*/
transitionTo<T extends StateId<StateDefinitions>>(
id: T,
factoryArgs: FactoryArgs<StateDefinitions, T>,
) {
if (this.isDisposed) {
throw new Error("This state machine is already disposed.");
}
this.currentState.onExit(this.context);
this.currentState = this.stateFactories[id](factoryArgs);
this.currentState.onEnter(this.context);
}

Expand All @@ -142,19 +169,36 @@ export class StateMachine<
* @param input 処理する入力。
*/
process(input: Input) {
if (this.isDisposed) {
throw new Error("This state machine is already disposed.");
}

let nextState: State<StateDefinitions, Input, Context> | undefined =
undefined;

this.currentState.process({
input,
context: this.context,
setNextState: (id, factoryArgs) => {
nextState = this.stateFactories[id](factoryArgs);
},
});

if (nextState != undefined) {
this.currentState.onExit(this.context);
this.currentState = nextState;
this.currentState.onEnter(this.context);
}
}

/**
* ステートマシンを破棄する。
*/
dispose() {
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
if (this.isDisposed) {
throw new Error("Already disposed.");
}
this.isDisposed = true;
this.currentState.onExit(this.context);
}
}
Loading