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

#1877 ソング: 編集メニューの追加とノートのコピー&ペーストの実装 #1903

Merged
merged 19 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from 9 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
10 changes: 10 additions & 0 deletions src/components/Menu/MenuBar/BaseMenuBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const props =
defineProps<{
/** 「ファイル」メニューのサブメニュー */
fileSubMenuData: MenuItemData[];
editSubMenuData: MenuItemData[];
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
/** エディタの種類 */
editor: "talk" | "song";
}>();
Expand Down Expand Up @@ -327,6 +328,15 @@ const menudata = computed<MenuItemData[]>(() => [
},
],
},
{
type: "root",
label: "編集",
onClick: () => {
closeAllDialog();
},
disableWhenUiLocked: false,
subMenu: props.editSubMenuData,
},
{
type: "root",
label: "エンジン",
Expand Down
91 changes: 90 additions & 1 deletion src/components/Sing/MenuBar.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<template>
<BaseMenuBar editor="song" :file-sub-menu-data="fileSubMenuData" />
<BaseMenuBar
editor="song"
:file-sub-menu-data="fileSubMenuData"
:edit-sub-menu-data="editSubMenuData"
/>
</template>

<script setup lang="ts">
Expand All @@ -10,6 +14,10 @@ import { MenuItemData } from "@/components/Menu/type";

const store = useStore();
const uiLocked = computed(() => store.getters.UI_LOCKED);
const editor = "song";
const canUndo = computed(() => store.getters.CAN_UNDO(editor));
const canRedo = computed(() => store.getters.CAN_REDO(editor));
const isNotesSelected = computed(() => store.state.selectedNoteIds.size > 0);

const importMidiFile = async () => {
if (uiLocked.value) return;
Expand Down Expand Up @@ -53,4 +61,85 @@ const fileSubMenuData: MenuItemData[] = [
disableWhenUiLocked: true,
},
];

const editSubMenuData: MenuItemData[] = [
{
type: "button",
label: "元に戻す",
onClick: () => {
if (uiLocked.value) return;
store.dispatch("UNDO", { editor });
},
disableWhenUiLocked: true,
disabled: !canUndo.value,
},
{
type: "button",
label: "やり直す",
onClick: () => {
if (uiLocked.value) return;
store.dispatch("REDO", { editor });
},
disableWhenUiLocked: true,
disabled: !canRedo.value,
},
{ type: "separator" },
{
type: "button",
label: "コピー",
onClick: () => {
if (uiLocked.value) return;
store.dispatch("COPY_NOTES_TO_CLIPBOARD");
},
disableWhenUiLocked: true,
disabled: !isNotesSelected.value,
},
{
type: "button",
label: "切り取り",
onClick: () => {
if (uiLocked.value) return;
store.dispatch("COMMAND_CUT_NOTES_TO_CLIPBOARD");
},
disableWhenUiLocked: true,
disabled: !isNotesSelected.value,
},
{
type: "button",
label: "貼り付け",
onClick: () => {
if (uiLocked.value) return;
store.dispatch("COMMAND_PASTE_NOTES_FROM_CLIPBOARD");
},
disableWhenUiLocked: true,
},
{ type: "separator" },
{
type: "button",
label: "すべて選択",
onClick: () => {
if (uiLocked.value) return;
store.dispatch("SELECT_ALL_NOTES");
},
disableWhenUiLocked: true,
},
{
type: "button",
label: "選択解除",
onClick: () => {
if (uiLocked.value) return;
store.dispatch("DESELECT_ALL_NOTES");
},
disableWhenUiLocked: true,
},
{
type: "button",
label: "クオンタイズ",
onClick: () => {
if (uiLocked.value) return;
store.dispatch("COMMAND_QUANTIZE_SELECTED_NOTES");
},
disableWhenUiLocked: true,
},
];
</script>
124 changes: 124 additions & 0 deletions src/components/Sing/ScoreSequencer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
@mouseleave="onMouseLeave"
@wheel="onWheel"
@scroll="onScroll"
@contextmenu.prevent
>
<!-- キャラクター全身 -->
<CharacterPortrait />
Expand Down Expand Up @@ -196,6 +197,7 @@
}"
@input="setZoomY"
/>
<ContextMenu ref="contextMenu" :menudata="contextMenuData" />
</div>
</template>

Expand All @@ -209,6 +211,8 @@ import {
onDeactivated,
} from "vue";
import { v4 as uuidv4 } from "uuid";
import ContextMenu from "../Menu/ContextMenu.vue";
import { MenuItemButton } from "../Menu/type";
import { useStore } from "@/store";
import { Note } from "@/store/type";
import {
Expand Down Expand Up @@ -239,6 +243,117 @@ import SequencerPhraseIndicator from "@/components/Sing/SequencerPhraseIndicator
import CharacterPortrait from "@/components/Sing/CharacterPortrait.vue";
import SequencerPitch from "@/components/Sing/SequencerPitch.vue";
import { isOnCommandOrCtrlKeyDown } from "@/store/utility";
import { useHotkeyManager } from "@/plugins/hotkeyPlugin";

const { registerHotkeyWithCleanup } = useHotkeyManager();

registerHotkeyWithCleanup({
editor: "song",
name: "コピー",
callback: () => {
if (nowPreviewing.value) {
return;
}
if (state.selectedNoteIds.size === 0) {
return;
}
store.dispatch("COPY_NOTES_TO_CLIPBOARD");
},
});

registerHotkeyWithCleanup({
editor: "song",
name: "切り取り",
callback: () => {
if (nowPreviewing.value) {
return;
}
if (state.selectedNoteIds.size === 0) {
return;
}
store.dispatch("COMMAND_CUT_NOTES_TO_CLIPBOARD");
},
});

registerHotkeyWithCleanup({
editor: "song",
name: "貼り付け",
callback: () => {
if (nowPreviewing.value) {
return;
}
store.dispatch("COMMAND_PASTE_NOTES_FROM_CLIPBOARD");
},
});

registerHotkeyWithCleanup({
editor: "song",
name: "すべて選択",
callback: () => {
if (nowPreviewing.value) {
return;
}
store.dispatch("SELECT_ALL_NOTES");
},
});

const contextMenu = ref<InstanceType<typeof ContextMenu>>();
const contextMenuData = ref<MenuItemButton[]>([
{
type: "button",
label: "選択中を削除",
onClick: async () => {
contextMenu.value?.hide();
await store.dispatch("COMMAND_REMOVE_SELECTED_NOTES");
},
disableWhenUiLocked: true,
},
{
type: "button",
label: "コピー",
onClick: async () => {
contextMenu.value?.hide();
await store.dispatch("COPY_NOTES_TO_CLIPBOARD");
},
disableWhenUiLocked: true,
},
{
type: "button",
label: "切り取り",
onClick: async () => {
contextMenu.value?.hide();
await store.dispatch("COMMAND_CUT_NOTES_TO_CLIPBOARD");
},
disableWhenUiLocked: true,
},
{
type: "button",
label: "貼り付け",
onClick: async () => {
contextMenu.value?.hide();
await store.dispatch("COMMAND_PASTE_NOTES_FROM_CLIPBOARD");
},
disableWhenUiLocked: true,
},
{
type: "button",
label: "すべて選択",
onClick: async () => {
contextMenu.value?.hide();
await store.dispatch("SELECT_ALL_NOTES");
},
disableWhenUiLocked: true,
},
{
type: "button",
label: "選択解除",
onClick: async () => {
contextMenu.value?.hide();
await store.dispatch("DESELECT_ALL_NOTES");
},
disableWhenUiLocked: true,
},
]);

type PreviewMode = "ADD" | "MOVE" | "RESIZE_RIGHT" | "RESIZE_LEFT";

Expand Down Expand Up @@ -710,6 +825,15 @@ const onMouseDown = (event: MouseEvent) => {
if (!isSelfEventTarget(event)) {
return;
}

// OSXの場合、Ctrl+クリックが右クリックのため、その場合はノートを追加しない
if (event.ctrlKey && event.button === 0) {
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

なるほどです!!
ここにisMacがあるのでそれも加えるといいかも?


// TODO: メニューが表示されている場合はメニュー非表示のみ行いたい
Copy link
Member

Choose a reason for hiding this comment

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

ちょっと考えたのですが同意見です!

よくある挙動だとコンテキストメニュー開いた後のクリックも普通のクリックと同じ扱いにする(ボタンを押すなど)のですが、どこをクリックしても何かが発動するシーケンサーの場合は話は別なのかなぁと思いました。


// 選択中のノートが無い場合、プレビューを開始しノートIDをリセット
if (event.button === 0) {
startPreview(event, "ADD");
mouseDownNoteId = undefined;
Expand Down
22 changes: 20 additions & 2 deletions src/components/Sing/SequencerNote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,31 @@ const showPitch = computed(() => {
return state.experimentalSetting.showPitchInSongEditor;
});
const contextMenu = ref<InstanceType<typeof ContextMenu>>();
const contextMenuData = ref<[MenuItemButton]>([
const contextMenuData = ref<MenuItemButton[]>([
{
type: "button",
label: "削除",
onClick: async () => {
contextMenu.value?.hide();
store.dispatch("COMMAND_REMOVE_SELECTED_NOTES");
await store.dispatch("COMMAND_REMOVE_SELECTED_NOTES");
},
disableWhenUiLocked: true,
},
{
type: "button",
label: "コピー",
onClick: async () => {
contextMenu.value?.hide();
await store.dispatch("COPY_NOTES_TO_CLIPBOARD");
},
disableWhenUiLocked: true,
},
{
type: "button",
label: "切り取り",
onClick: async () => {
contextMenu.value?.hide();
await store.dispatch("COMMAND_CUT_NOTES_TO_CLIPBOARD");
},
disableWhenUiLocked: true,
},
Expand Down
33 changes: 32 additions & 1 deletion src/components/Talk/MenuBar.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<template>
<BaseMenuBar editor="talk" :file-sub-menu-data="fileSubMenuData" />
<BaseMenuBar
editor="talk"
:file-sub-menu-data="fileSubMenuData"
:edit-sub-menu-data="editSubMenuData"
/>
</template>

<script setup lang="ts">
Expand All @@ -20,6 +24,9 @@ const store = useStore();
const { registerHotkeyWithCleanup } = useHotkeyManager();

const uiLocked = computed(() => store.getters.UI_LOCKED);
const editor = "talk";
const canUndo = computed(() => store.getters.CAN_UNDO(editor));
const canRedo = computed(() => store.getters.CAN_REDO(editor));

const generateAndSaveAllAudio = async () => {
if (!uiLocked.value) {
Expand Down Expand Up @@ -159,4 +166,28 @@ registerHotkeyWithCleanup({
importTextFile();
},
});

// 「編集」メニュー
const editSubMenuData = computed<MenuItemData[]>(() => [
{
type: "button",
label: "元に戻す",
onClick: () => {
if (uiLocked.value) return;
store.dispatch("UNDO", { editor });
},
disableWhenUiLocked: true,
disabled: !canUndo.value,
},
{
type: "button",
label: "やり直す",
onClick: () => {
if (uiLocked.value) return;
store.dispatch("REDO", { editor });
},
disableWhenUiLocked: true,
disabled: !canRedo.value,
},
Copy link
Member

Choose a reason for hiding this comment

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

この2つはsingとtalkで同じなので統一できそうですね!!
実際に「プロジェクト上書き保存」とかはBaseMenuBar内で統一されてるので、書き方を真似れば統一できると思います!

{
type: "button",
label: "プロジェクトを上書き保存",
onClick: async () => {
await saveProject();
},
disableWhenUiLocked: true,
},
{

分からなかったら後でプルリクエスト出させていただきます!

]);
</script>
Loading