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

[ソング] Improve: エンジン起動前の画面を良い感じにする #1790

Merged
merged 19 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
126 changes: 126 additions & 0 deletions src/components/EngineStartupOverlay.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<template>
<!-- TODO: 複数エンジン対応 -->
<!-- TODO: allEngineStateが "ERROR" のときエラーになったエンジンを探してトーストで案内 -->
<div v-if="allEngineState === 'FAILED_STARTING'" class="waiting-engine">
<div>エンジンの起動に失敗しました。エンジンの再起動をお試しください。</div>
</div>
<div
v-else-if="
!props.isCompletedInitialStartup || allEngineState === 'STARTING'
"
class="waiting-engine"
>
<div>
<q-spinner color="primary" size="2.5rem" />
<div class="q-mt-xs">
{{
allEngineState === "STARTING"
? "エンジン起動中・・・"
: "データ準備中・・・"
}}
</div>

<template v-if="isEngineWaitingLong">
<q-separator spaced />
エンジン起動に時間がかかっています。<br />
<q-btn
v-if="isMultipleEngine"
outline
:disable="reloadingLocked"
@click="reloadAppWithMultiEngineOffMode"
>
マルチエンジンをオフにして再読み込みする</q-btn
>
<q-btn v-else outline @click="openQa">Q&Aを見る</q-btn>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch } from "vue";
import { useStore } from "@/store";
import { EngineState } from "@/store/type";

const store = useStore();
const props =
defineProps<{
isCompletedInitialStartup: boolean;
}>();

const reloadingLocked = computed(() => store.state.reloadingLock);
const isMultipleEngine = computed(() => store.state.engineIds.length > 1);

// エンジン待機
// TODO: 個別のエンジンの状態をUIで確認できるようにする
const allEngineState = computed(() => {
const engineStates = store.state.engineStates;

let lastEngineState: EngineState | undefined = undefined;

// 登録されているすべてのエンジンについて状態を確認する
for (const engineId of store.state.engineIds) {
const engineState: EngineState | undefined = engineStates[engineId];
if (engineState == undefined)
throw new Error(`No such engineState set: engineId == ${engineId}`);

// FIXME: 1つでも接続テストに成功していないエンジンがあれば、暫定的に起動中とする
if (engineState === "STARTING") {
return engineState;
}

lastEngineState = engineState;
}

return lastEngineState; // FIXME: 暫定的に1つのエンジンの状態を返す
});

const isEngineWaitingLong = ref<boolean>(false);
let engineTimer: number | undefined = undefined;
watch(allEngineState, (newEngineState) => {
if (engineTimer != undefined) {
clearTimeout(engineTimer);
engineTimer = undefined;
}
if (newEngineState === "STARTING") {
isEngineWaitingLong.value = false;
engineTimer = window.setTimeout(() => {
isEngineWaitingLong.value = true;
}, 30000);
} else {
isEngineWaitingLong.value = false;
}
});

const reloadAppWithMultiEngineOffMode = () => {
store.dispatch("CHECK_EDITED_AND_NOT_SAVE", {
closeOrReload: "reload",
isMultiEngineOffMode: true,
});
};

const openQa = () => {
window.open("https://voicevox.hiroshiba.jp/qa/", "_blank");
};
</script>

<style scoped lang="scss">
@use '@/styles/colors' as colors;
@use '@/styles/variables' as vars;

.waiting-engine {
background-color: rgba(colors.$display-rgb, 0.15);
position: absolute;
inset: 0;
z-index: 10;
display: flex;
text-align: center;
align-items: center;
justify-content: center;
> div {
color: colors.$display;
background: colors.$surface;
border-radius: 6px;
padding: 14px;
}
}
</style>
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<template>
<q-btn flat class="q-pa-none">
<slot></slot>
<q-btn flat class="q-pa-none" :disable="uiLocked">
<selected-character
:show-skeleton="showSkeleton"
:selected-character-info="selectedCharacterInfo"
:selected-singer="selectedSinger"
/>
<q-menu
class="character-menu"
transition-show="none"
Expand Down Expand Up @@ -134,15 +138,22 @@
</q-btn>
</template>

<script lang="ts">
export default {
name: "CharacterMenuButton",
};
</script>
<script setup lang="ts">
import { computed, ref } from "vue";
import { debounce } from "quasar";
import SelectedCharacter from "./SelectedCharacter.vue";
import { useStore } from "@/store";
import { base64ImageToUri } from "@/helpers/imageHelper";
import { SpeakerId, StyleId } from "@/type/preload";
import { getStyleDescription } from "@/sing/viewHelper";

const store = useStore();
const uiLocked = computed(() => store.getters.UI_LOCKED);

const userOrderedCharacterInfos = computed(() => {
return store.getters.USER_ORDERED_CHARACTER_INFOS("singerLike");
Expand All @@ -160,6 +171,7 @@ const reassignSubMenuOpen = debounce((idx: number) => {
arr[idx] = true;
subMenuOpenFlags.value = arr;
}, 100);
const showSkeleton = computed(() => selectedCharacterInfo.value == undefined);

const changeStyleId = (speakerUuid: SpeakerId, styleId: StyleId) => {
const engineId = store.state.engineIds.find((_engineId) =>
Expand Down Expand Up @@ -205,6 +217,10 @@ const selectedCharacterInfo = computed(() => {
return store.getters.CHARACTER_INFO(singer.engineId, singer.styleId);
});

const selectedSinger = computed(() => {
return store.getters.SELECTED_TRACK.singer;
});

const selectedSpeakerUuid = computed(() => {
return selectedCharacterInfo.value?.metas.speakerUuid;
});
Expand Down Expand Up @@ -235,20 +251,6 @@ const engineIcons = computed(() =>
@use '@/styles/variables' as vars;
@use '@/styles/colors' as colors;

.character-name {
position: absolute;
top: 0px;
left: 0px;
padding: 1px 24px 1px 8px;
background-image: linear-gradient(
90deg,
rgba(colors.$background-rgb, 0.5) 0%,
rgba(colors.$background-rgb, 0.5) 75%,
transparent 100%
);
overflow-wrap: anywhere;
}

.character-menu {
.q-item {
color: colors.$display;
Expand All @@ -272,5 +274,18 @@ const engineIcons = computed(() =>
bottom: -6px;
right: -6px;
}
.character-name {
position: absolute;
top: 0px;
left: 0px;
padding: 1px 24px 1px 8px;
background-image: linear-gradient(
90deg,
rgba(colors.$background-rgb, 0.5) 0%,
rgba(colors.$background-rgb, 0.5) 75%,
transparent 100%
);
overflow-wrap: anywhere;
}
}
</style>
138 changes: 138 additions & 0 deletions src/components/Sing/CharacterMenuButton/SelectedCharacter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<template>
<div v-if="props.showSkeleton" class="selected-character">
<q-skeleton class="character-avatar" type="QAvatar" size="52px" />
<div class="character-info">
<q-skeleton
class="character-name skeleton"
type="rect"
width="65px"
height="15px"
/>
<q-skeleton
class="character-style"
type="rect"
width="110px"
height="12px"
/>
</div>
</div>
<div v-else class="selected-character">
<q-avatar
v-if="selectedStyleIconPath"
class="character-avatar"
size="3.5rem"
>
<img :src="selectedStyleIconPath" class="character-avatar-icon" />
</q-avatar>
<div class="character-info">
<div class="character-name">
{{ selectedCharacterName }}
</div>
<div class="character-style">
{{ selectedCharacterStyleDescription }}
</div>
</div>
<q-icon
name="arrow_drop_down"
size="sm"
class="character-menu-dropdown-icon"
/>
</div>
</template>

<script setup lang="ts">
import { computed } from "vue";
import { Singer } from "@/store/type";
import { CharacterInfo } from "@/type/preload";
import { getStyleDescription } from "@/sing/viewHelper";

const props =
defineProps<{
showSkeleton: boolean;
selectedCharacterInfo: CharacterInfo | undefined;
selectedSinger: Singer | undefined;
}>();

const selectedCharacterName = computed(() => {
return props.selectedCharacterInfo?.metas.speakerName;
});
const selectedCharacterStyleDescription = computed(() => {
const style = props.selectedCharacterInfo?.metas.styles.find((style) => {
return (
style.styleId === props.selectedSinger?.styleId &&
style.engineId === props.selectedSinger?.engineId
);
});
return style != undefined ? getStyleDescription(style) : "";
});
const selectedStyleIconPath = computed(() => {
if (!props.selectedCharacterInfo || !props.selectedSinger) {
return;
}
const styles = props.selectedCharacterInfo.metas.styles;
return styles?.find((style) => {
return (
style.styleId === props.selectedSinger?.styleId &&
style.engineId === props.selectedSinger?.engineId
);
})?.iconPath;
});
</script>

<style scoped lang="scss">
@use '@/styles/variables' as vars;
@use '@/styles/colors' as colors;

.selected-character {
align-items: center;
display: flex;
padding: 0.25rem 0.5rem 0.25rem 0.25rem;
position: relative;

.character-menu-toggle {
align-items: center;
display: flex;
padding: 0.25rem 0.5rem 0.25rem 0.25rem;
position: relative;
}
.character-avatar-icon {
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
display: block;
height: 100%;
object-fit: cover;
width: 100%;
}

.character-info {
align-items: start;
display: flex;
flex-direction: column;
margin-left: 0.5rem;
text-align: left;
justify-content: center;
white-space: nowrap;
}
.character-name {
font-size: 0.875rem;
font-weight: bold;
line-height: 1rem;
padding-top: 0.5rem;

&.skeleton {
margin-top: 0.4rem;
margin-bottom: 0.2rem;
}
}

.character-style {
color: rgba(colors.$display-rgb, 0.6);
font-size: 0.75rem;
font-weight: bold;
line-height: 1rem;
}
Comment on lines +120 to +125
Copy link
Member

Choose a reason for hiding this comment

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

@romot-co boldになってちょっと見た目変わったので共有です!
全体の調和でデザイン的な課題があればまた調整お願いできると・・・!!


.character-menu-dropdown-icon {
color: rgba(colors.$display-rgb, 0.8);
margin-left: 0.25rem;
}
}
</style>
3 changes: 3 additions & 0 deletions src/components/Sing/SingEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<menu-bar />
<tool-bar />
<div class="sing-main">
<engine-startup-overlay :is-completed-initial-startup="isEnginesReady" />
<div v-if="nowAudioExporting" class="exporting-dialog">
<div>
<q-spinner color="primary" size="2.5rem" />
Expand Down Expand Up @@ -34,6 +35,7 @@ import {
DEFAULT_BPM,
DEFAULT_TPQN,
} from "@/sing/storeHelper";
import EngineStartupOverlay from "@/components/EngineStartupOverlay.vue";
import { useStore } from "@/store";

const props = withDefaults(
Expand Down Expand Up @@ -121,6 +123,7 @@ const unwatchIsEnginesReady = watch(
.sing-main {
display: flex;
overflow: hidden;
position: relative;
}

.exporting-dialog {
Expand Down
Loading
Loading