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

Add: 1~10番目のキャラクターを選択するホットキーを追加 #2034

Merged
merged 5 commits into from
May 4, 2024
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
30 changes: 10 additions & 20 deletions src/components/CharacterButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@
no-transition
:ratio="1"
:src="
getDefaultStyle(characterInfo.metas.speakerUuid).iconPath
getDefaultStyleWrapper(characterInfo.metas.speakerUuid)
.iconPath
"
/>
<QAvatar
Expand All @@ -92,7 +93,7 @@
<img
:src="
engineIcons[
getDefaultStyle(characterInfo.metas.speakerUuid)
getDefaultStyleWrapper(characterInfo.metas.speakerUuid)
.engineId
]
"
Expand Down Expand Up @@ -199,6 +200,7 @@ import { base64ImageToUri } from "@/helpers/imageHelper";
import { useStore } from "@/store";
import { CharacterInfo, SpeakerId, Voice } from "@/type/preload";
import { formatCharacterStyleName } from "@/store/utility";
import { getDefaultStyle } from "@/domain/talk";

const props = withDefaults(
defineProps<{
Expand Down Expand Up @@ -282,27 +284,15 @@ const engineIcons = computed(() =>
),
);

const getDefaultStyle = (speakerUuid: SpeakerId) => {
// FIXME: 同一キャラが複数エンジンにまたがっているとき、順番が先のエンジンが必ず選択される
const characterInfo = props.characterInfos.find(
(info) => info.metas.speakerUuid === speakerUuid,
const getDefaultStyleWrapper = (speakerUuid: SpeakerId) =>
getDefaultStyle(
speakerUuid,
props.characterInfos,
store.state.defaultStyleIds,
);
const defaultStyleId = store.state.defaultStyleIds.find(
(x) => x.speakerUuid === speakerUuid,
)?.defaultStyleId;

const defaultStyle =
characterInfo?.metas.styles.find(
(style) => style.styleId === defaultStyleId,
) ?? characterInfo?.metas.styles[0]; // デフォルトのスタイルIDが見つからない場合stylesの先頭を選択する

if (defaultStyle == undefined) throw new Error("defaultStyle == undefined");

return defaultStyle;
};

const onSelectSpeaker = (speakerUuid: SpeakerId) => {
const style = getDefaultStyle(speakerUuid);
const style = getDefaultStyleWrapper(speakerUuid);
emit("update:selectedVoice", {
engineId: style.engineId,
speakerId: speakerUuid,
Expand Down
29 changes: 29 additions & 0 deletions src/components/Talk/AudioCell.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ import {
useShiftKey,
useCommandOrControlKey,
} from "@/composables/useModifierKey";
import { getDefaultStyle } from "@/domain/talk";

const props = defineProps<{
audioKey: AudioKey;
Expand Down Expand Up @@ -155,6 +156,10 @@ defineExpose({
removeCell: () => {
removeCell();
},
/** index番目のキャラクターを選ぶ */
selectCharacterAt: (index: number) => {
selectCharacterAt(index);
},
});

const store = useStore();
Expand Down Expand Up @@ -490,6 +495,30 @@ const removeCell = async () => {
}
};

// N番目のキャラクターを選ぶ
const selectCharacterAt = (index: number) => {
if (userOrderedCharacterInfos.value.length < index + 1) {
return;
}
const speakerUuid = userOrderedCharacterInfos.value[index].metas.speakerUuid;
const style = getDefaultStyle(
speakerUuid,
userOrderedCharacterInfos.value,
store.state.defaultStyleIds,
);
const voice = {
engineId: style.engineId,
speakerId: speakerUuid,
styleId: style.styleId,
};
store.dispatch("COMMAND_MULTI_CHANGE_VOICE", {
audioKeys: isMultiSelectEnabled.value
? store.getters.SELECTED_AUDIO_KEYS
: [props.audioKey],
voice,
});
};

// 削除ボタンの有効/無効判定
const enableDeleteButton = computed(() => {
return (
Expand Down
19 changes: 19 additions & 0 deletions src/components/Talk/TalkEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ import {
PresetKey,
SplitterPositionType,
Voice,
HotkeyActionNameType,
actionPostfixSelectNthCharacter,
} from "@/type/preload";
import { useHotkeyManager } from "@/plugins/hotkeyPlugin";
import onetimeWatch from "@/helpers/onetimeWatch";
Expand Down Expand Up @@ -258,12 +260,29 @@ registerHotkeyWithCleanup({
}
},
});
for (let i = 0; i < 10; i++) {
registerHotkeyWithCleanup({
editor: "talk",
enableInTextbox: true,
name: `${i + 1}${actionPostfixSelectNthCharacter}` as HotkeyActionNameType,
callback: () => {
if (!uiLocked.value) {
onCharacterSelectHotkey(i);
}
},
});
}

const removeAudioItem = async () => {
if (activeAudioKey.value == undefined) throw new Error();
audioCellRefs[activeAudioKey.value].removeCell();
};

const onCharacterSelectHotkey = async (selectedCharacterIndex: number) => {
if (activeAudioKey.value == undefined) throw new Error();
audioCellRefs[activeAudioKey.value].selectCharacterAt(selectedCharacterIndex);
};

// view
const DEFAULT_PORTRAIT_PANE_WIDTH = 22; // %
const MIN_PORTRAIT_PANE_WIDTH = 0;
Expand Down
25 changes: 25 additions & 0 deletions src/domain/talk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { CharacterInfo, DefaultStyleId, SpeakerId } from "@/type/preload";

/** 話者に対応するデフォルトスタイルを取得する */
export const getDefaultStyle = (
speakerUuid: SpeakerId,
characterInfos: CharacterInfo[],
defaultStyleIds: DefaultStyleId[],
) => {
// FIXME: 同一キャラが複数エンジンにまたがっているとき、順番が先のエンジンが必ず選択される
const characterInfo = characterInfos.find(
(info) => info.metas.speakerUuid === speakerUuid,
);
const defaultStyleId = defaultStyleIds.find(
(x) => x.speakerUuid === speakerUuid,
)?.defaultStyleId;

const defaultStyle =
characterInfo?.metas.styles.find(
(style) => style.styleId === defaultStyleId,
) ?? characterInfo?.metas.styles[0]; // デフォルトのスタイルIDが見つからない場合stylesの先頭を選択する

if (defaultStyle == undefined) throw new Error("defaultStyle == undefined");

return defaultStyle;
};
21 changes: 21 additions & 0 deletions src/type/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export type VoiceId = z.infer<typeof voiceIdSchema>;
export const VoiceId = (voice: Voice): VoiceId =>
voiceIdSchema.parse(`${voice.engineId}:${voice.speakerId}:${voice.styleId}`);

// 共通のアクション名
export const actionPostfixSelectNthCharacter = "番目のキャラクターを選択";

// ホットキーを追加したときは設定のマイグレーションが必要
export const defaultHotkeySettings: HotkeySettingType[] = [
{
Expand Down Expand Up @@ -176,6 +179,14 @@ export const defaultHotkeySettings: HotkeySettingType[] = [
action: "全セルを選択",
combination: HotkeyCombination(!isMac ? "Ctrl Shift A" : "Meta Shift A"),
},
...Array.from({ length: 10 }, (_, index) => {
const roleKey = index == 9 ? 0 : index + 1;
return {
action:
`${index + 1}${actionPostfixSelectNthCharacter}` as HotkeyActionNameType,
combination: HotkeyCombination((!isMac ? "Ctrl " : "Meta ") + roleKey),
};
}),
];

export const defaultToolbarButtonSetting: ToolbarSettingType = [
Expand Down Expand Up @@ -463,6 +474,16 @@ export const hotkeyActionNameSchema = z.enum([
"すべて選択",
"選択解除",
"全セルを選択",
`1${actionPostfixSelectNthCharacter}`,
`2${actionPostfixSelectNthCharacter}`,
`3${actionPostfixSelectNthCharacter}`,
`4${actionPostfixSelectNthCharacter}`,
`5${actionPostfixSelectNthCharacter}`,
`6${actionPostfixSelectNthCharacter}`,
`7${actionPostfixSelectNthCharacter}`,
`8${actionPostfixSelectNthCharacter}`,
`9${actionPostfixSelectNthCharacter}`,
`10${actionPostfixSelectNthCharacter}`,
]);

export type HotkeyActionNameType = z.infer<typeof hotkeyActionNameSchema>;
Expand Down
Loading