Skip to content

Commit

Permalink
zodで型を定義およびuseCursorStateのリファクタリングやテスト
Browse files Browse the repository at this point in the history
  • Loading branch information
romot-co committed Nov 23, 2024
1 parent cb452b6 commit 756d44e
Show file tree
Hide file tree
Showing 6 changed files with 550 additions and 184 deletions.
53 changes: 29 additions & 24 deletions src/composables/useCursorState.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { computed, watch, Ref } from "vue";
import { SequencerEditTarget, NoteEditTool, PitchEditTool } from "@/store/type";
import { PreviewMode } from "@/sing/viewHelper";
import { PreviewMode, CursorState } from "@/sing/viewHelper";

// カーソル状態の外部コンテキスト
export interface CursorStateContext {
Expand All @@ -13,19 +13,27 @@ export interface CursorStateContext {
readonly previewMode: Ref<PreviewMode>;
}

export function useCursorState(context: CursorStateContext) {
export function useCursorState(cursorStateContext: CursorStateContext) {
const {
ctrlKey,
shiftKey,
nowPreviewing,
editTarget,
selectedNoteTool,
selectedPitchTool,
previewMode,
} = cursorStateContext;

// カーソルの状態
const cursorState = computed(() => {
return resolveCursorBehavior();
});
const cursorState: Ref<CursorState> = computed(() => resolveCursorBehavior());

/**
* カーソルの状態を関連するコンテキストから取得する
*/
const resolveCursorBehavior = () => {
const resolveCursorBehavior = (): CursorState => {
// プレビューの場合
if (context.nowPreviewing.value && context.previewMode.value !== "IDLE") {
switch (context.previewMode.value) {
if (nowPreviewing.value && previewMode.value !== "IDLE") {
switch (previewMode.value) {
case "ADD_NOTE":
return "DRAW";
case "MOVE_NOTE":
Expand All @@ -43,31 +51,28 @@ export function useCursorState(context: CursorStateContext) {
}

// ノート編集の場合
if (context.editTarget.value === "NOTE") {
if (editTarget.value === "NOTE") {
// シフトキーが押されていたら常に十字カーソル
if (context.shiftKey.value) {
if (shiftKey.value) {
return "CROSSHAIR";
}
// ノート編集ツールが選択されていたら描画カーソル
if (context.selectedNoteTool.value === "EDIT_FIRST") {
if (selectedNoteTool.value === "EDIT_FIRST") {
return "DRAW";
}
// それ以外は未設定
return "UNSET";
}

// ピッチ編集の場合
if (context.editTarget.value === "PITCH") {
if (editTarget.value === "PITCH") {
// Ctrlキーが押されていたもしくは削除ツールが選択されていたら消しゴムカーソル
if (
context.ctrlKey.value ||
context.selectedPitchTool.value === "ERASE"
) {
if (ctrlKey.value || selectedPitchTool.value === "ERASE") {
return "ERASE";
}

// 描画ツールが選択されていたら描画カーソル
if (context.selectedPitchTool.value === "DRAW") {
if (selectedPitchTool.value === "DRAW") {
return "DRAW";
}
}
Expand Down Expand Up @@ -95,13 +100,13 @@ export function useCursorState(context: CursorStateContext) {
// カーソルに関連するコンテキストが更新されたらカーソルの状態を変更
watch(
[
context.ctrlKey,
context.shiftKey,
context.nowPreviewing,
context.editTarget,
context.selectedNoteTool,
context.selectedPitchTool,
context.previewMode,
ctrlKey,
shiftKey,
nowPreviewing,
editTarget,
selectedNoteTool,
selectedPitchTool,
previewMode,
],
() => {},
{ immediate: true },
Expand Down
102 changes: 54 additions & 48 deletions src/composables/useEditMode.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
import { watch, Ref } from "vue";
import { watch, ref, Ref } from "vue";
import { NoteId } from "@/type/preload";
import { NoteEditTool, PitchEditTool, SequencerEditTarget } from "@/store/type";
import { MouseButton } from "@/sing/viewHelper";

// マウスダウン時の振る舞い
export type MouseDownBehavior =
| "IGNORE"
| "DESELECT_ALL"
| "ADD_NOTE"
| "START_RECT_SELECT"
| "DRAW_PITCH"
| "ERASE_PITCH";

// ダブルクリック時の振る舞い
export type MouseDoubleClickBehavior = "IGNORE" | "ADD_NOTE";
import {
MouseButton,
MouseDownBehavior,
MouseDoubleClickBehavior,
} from "@/sing/viewHelper";

// 編集モードの外部コンテキスト
export interface EditModeContext {
Expand All @@ -33,44 +25,51 @@ export interface MouseDownBehaviorContext {
}

export function useEditMode(editModeContext: EditModeContext) {
const {
ctrlKey,
shiftKey,
nowPreviewing,
editTarget,
selectedNoteTool,
selectedPitchTool,
} = editModeContext;

/**
* マウスダウン時の振る舞いを判定する
* 条件の判定のみを行い、実際の処理は呼び出し側で行う
*/
const resolveMouseDownBehavior = (
mouseDownContext: MouseDownBehaviorContext,
): MouseDownBehavior => {
// マウスダウン時のコンテキストも使う
const context = {
...editModeContext,
...mouseDownContext,
};
const { isSelfEventTarget, mouseButton, editingLyricNoteId } =
mouseDownContext;

// プレビュー中は無視
if (context.nowPreviewing.value) return "IGNORE";
if (nowPreviewing.value) return "IGNORE";

// ノート編集の場合
if (context.editTarget.value === "NOTE") {
if (editTarget.value === "NOTE") {
// イベントが来ていない場合は無視
if (!context.isSelfEventTarget) return "IGNORE";
if (!isSelfEventTarget) return "IGNORE";
// 歌詞編集中は無視
if (context.editingLyricNoteId != undefined) return "IGNORE";
if (editingLyricNoteId != undefined) return "IGNORE";

// 左クリックの場合
if (context.mouseButton === "LEFT_BUTTON") {
if (mouseButton === "LEFT_BUTTON") {
// シフトキーが押されている場合は常に矩形選択開始
if (context.shiftKey.value) return "START_RECT_SELECT";
if (shiftKey.value) return "START_RECT_SELECT";

// 編集優先ツールの場合
if (context.selectedNoteTool.value === "EDIT_FIRST") {
if (selectedNoteTool.value === "EDIT_FIRST") {
// コントロールキーが押されている場合は全選択解除
if (context.ctrlKey.value) {
if (ctrlKey.value) {
return "DESELECT_ALL";
}
return "ADD_NOTE";
}

// 選択優先ツールの場合
if (context.selectedNoteTool.value === "SELECT_FIRST") {
if (selectedNoteTool.value === "SELECT_FIRST") {
// 矩形選択開始
return "START_RECT_SELECT";
}
Expand All @@ -80,19 +79,16 @@ export function useEditMode(editModeContext: EditModeContext) {
}

// ピッチ編集の場合
if (context.editTarget.value === "PITCH") {
if (editTarget.value === "PITCH") {
// 左クリック以外は無視
if (context.mouseButton !== "LEFT_BUTTON") return "IGNORE";
if (mouseButton !== "LEFT_BUTTON") return "IGNORE";

// ピッチ削除ツールが選択されているかコントロールキーが押されている場合はピッチ削除
if (
context.selectedPitchTool.value === "ERASE" ||
context.ctrlKey.value
) {
if (selectedPitchTool.value === "ERASE" || ctrlKey.value) {
return "ERASE_PITCH";
}

// それ以外はピッチ描画
// それ以外はピッチ編集
return "DRAW_PITCH";
}

Expand All @@ -103,37 +99,47 @@ export function useEditMode(editModeContext: EditModeContext) {
* ダブルクリック時の振る舞いを判定する
*/
const resolveDoubleClickBehavior = (): MouseDoubleClickBehavior => {
const context = {
...editModeContext,
};
// プレビュー中は無視
if (context.nowPreviewing.value) return "IGNORE";
if (nowPreviewing.value) return "IGNORE";

// ノート編集の選択優先ツールではノート追加
if (
context.editTarget.value === "NOTE" &&
context.selectedNoteTool.value === "SELECT_FIRST"
editTarget.value === "NOTE" &&
selectedNoteTool.value === "SELECT_FIRST"
) {
return "ADD_NOTE";
}

return "IGNORE";
};

// Ctrlキーが押されたときにピッチツールを変更したかどうか
const toolChangedByCtrl = ref(false);

// ピッチ編集モードにおいてCtrlキーが押されたときにピッチツールを消しゴムツールにする
watch(
[editModeContext.ctrlKey],
[ctrlKey],
() => {
// ピッチ編集モードでない場合は無視
if (editModeContext.editTarget.value !== "PITCH") {
if (editTarget.value !== "PITCH") {
return;
}

// Ctrlキーが押されたとき
if (editModeContext.ctrlKey.value) {
// ピッチ描画ツールの場合はピッチ削除ツールに変更
if (editModeContext.selectedPitchTool.value === "DRAW") {
editModeContext.selectedPitchTool.value = "ERASE";
// 現在のツールがピッチ描画ツールの場合
if (selectedPitchTool.value === "DRAW") {
// Ctrlキーが押されたときはピッチ削除ツールに変更
if (ctrlKey.value) {
selectedPitchTool.value = "ERASE";
toolChangedByCtrl.value = true;
}
}

// 現在のツールがピッチ削除ツールかつCtrlキーが離されたとき
if (selectedPitchTool.value === "ERASE" && toolChangedByCtrl.value) {
// ピッチ描画ツールに戻す
if (!ctrlKey.value) {
selectedPitchTool.value = "DRAW";
toolChangedByCtrl.value = false;
}
}
},
Expand Down
28 changes: 28 additions & 0 deletions src/sing/viewHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,23 @@ export type PreviewMode =
| "DRAW_PITCH"
| "ERASE_PITCH";

// マウスダウン時の振る舞い
export const mouseDownBehaviorSchema = z.enum([
"IGNORE",
"DESELECT_ALL",
"ADD_NOTE",
"START_RECT_SELECT",
"DRAW_PITCH",
"ERASE_PITCH",
]);
export type MouseDownBehavior = z.infer<typeof mouseDownBehaviorSchema>;

// ダブルクリック時の振る舞い
export const mouseDoubleClickBehaviorSchema = z.enum(["IGNORE", "ADD_NOTE"]);
export type MouseDoubleClickBehavior = z.infer<
typeof mouseDoubleClickBehaviorSchema
>;

export function getButton(event: MouseEvent): MouseButton {
// macOSの場合、Ctrl+クリックは右クリック
if (isMac && event.button === 0 && event.ctrlKey) {
Expand All @@ -157,3 +174,14 @@ export function getButton(event: MouseEvent): MouseButton {
return "OTHER_BUTTON";
}
}

// カーソルの状態
export const cursorStateSchema = z.enum([
"UNSET",
"DRAW",
"MOVE",
"EW_RESIZE",
"CROSSHAIR",
"ERASE",
]);
export type CursorState = z.infer<typeof cursorStateSchema>;
19 changes: 7 additions & 12 deletions src/store/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -840,20 +840,15 @@ export const PhraseKey = (id: string): PhraseKey => phraseKeySchema.parse(id);

// 編集対象 ノート or ピッチ
// ボリュームを足すのであれば"VOLUME"を追加する
export type SequencerEditTarget = "NOTE" | "PITCH";
export const sequencerEditTargetSchema = z.enum(["NOTE", "PITCH"]);
export type SequencerEditTarget = z.infer<typeof sequencerEditTargetSchema>;

// ノート編集ツール
export type NoteEditTool = "SELECT_FIRST" | "EDIT_FIRST";
export const noteEditToolSchema = z.enum(["SELECT_FIRST", "EDIT_FIRST"]);
export type NoteEditTool = z.infer<typeof noteEditToolSchema>;
// ピッチ編集ツール
export type PitchEditTool = "DRAW" | "ERASE";

// カーソルの状態
export type CursorState =
| "UNSET"
| "DRAW"
| "MOVE"
| "EW_RESIZE"
| "CROSSHAIR"
| "ERASE";
export const pitchEditToolSchema = z.enum(["DRAW", "ERASE"]);
export type PitchEditTool = z.infer<typeof pitchEditToolSchema>;

export type TrackParameters = {
gain: boolean;
Expand Down
Loading

0 comments on commit 756d44e

Please sign in to comment.