Skip to content

Commit

Permalink
refactor: テーマ周りのコードを整理し、設定を取得する汎用関数に合流させたり、テーマ変更関数を切り出したりした (#2284)
Browse files Browse the repository at this point in the history
* テーマ周りのコードをリファクタリング

* いらないメモを削除

* いらない型

* ピッチ線にも反映

* presetKey?: PresetKey;を戻す

* 関数名の変更
  • Loading branch information
Hiroshiba authored Oct 12, 2024
1 parent 4dbc9a2 commit 4bb13ca
Show file tree
Hide file tree
Showing 13 changed files with 114 additions and 121 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ Storybook を使ってコンポーネントを開発することができます
npm run storybook
```

main ブランチの Storybook は Chromatic から確認できます。
<https://main--667d9c007418420dbb5b0f75.chromatic.com/>

### ブラウザ版の実行(開発中)

別途音声合成エンジンを起動し、以下を実行して表示された localhost へアクセスします。
Expand All @@ -96,7 +99,8 @@ npm run storybook
npm run browser:serve
```

また、main ブランチのビルド結果がこちらにデプロイされています <https://voicevox-browser-dev.netlify.app/>
また、main ブランチのビルド結果がこちらにデプロイされています。
<https://voicevox-browser-dev.netlify.app/>
今はローカル PC 上で音声合成エンジンを起動する必要があります。

## ビルド
Expand Down
23 changes: 3 additions & 20 deletions src/backend/browser/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,32 +295,15 @@ export const api: Sandbox = {
// TODO: Impl
return;
},
async theme(newData?: string) {
if (newData != undefined) {
await this.setSetting("currentTheme", newData);
return;
}
async getAvailableThemes() {
// NOTE: Electron版では起動時にテーマ情報が必要なので、
// この実装とは違って起動時に読み込んだキャッシュを返すだけになっている。
return Promise.all(
// FIXME: themeファイルのいい感じのパスの設定
["/themes/default.json", "/themes/dark.json"].map((url) =>
fetch(url).then((res) => res.json()),
fetch(url).then((res) => res.json() as Promise<ThemeConf>),
),
)
.then((v) => ({
currentTheme: "Default",
availableThemes: v,
}))
.then((v) =>
this.getSetting("currentTheme").then(
(currentTheme) =>
({
...v,
currentTheme,
}) as { currentTheme: string; availableThemes: ThemeConf[] },
),
);
);
},
vuexReady() {
// NOTE: 何もしなくて良さそう
Expand Down
15 changes: 4 additions & 11 deletions src/backend/electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,10 @@ registerIpcMainHandle<IpcMainHandle>({
}
},

GET_AVAILABLE_THEMES: () => {
return themes;
},

OPEN_LOG_DIRECTORY: () => {
void shell.openPath(app.getPath("logs"));
},
Expand Down Expand Up @@ -791,17 +795,6 @@ registerIpcMainHandle<IpcMainHandle>({
return configManager.get("hotkeySettings");
},

THEME: (_, { newData }) => {
if (newData != undefined) {
configManager.set("currentTheme", newData);
return;
}
return {
currentTheme: configManager.get("currentTheme"),
availableThemes: themes,
};
},

ON_VUEX_READY: () => {
win.show();
},
Expand Down
4 changes: 2 additions & 2 deletions src/backend/electron/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,8 @@ const api: Sandbox = {
void ipcRendererInvokeProxy.SET_NATIVE_THEME(source);
},

theme: (newData) => {
return ipcRendererInvokeProxy.THEME({ newData });
getAvailableThemes: () => {
return ipcRendererInvokeProxy.GET_AVAILABLE_THEMES();
},

vuexReady: () => {
Expand Down
29 changes: 21 additions & 8 deletions src/components/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
</template>

<script setup lang="ts">
import { watch, onMounted, ref, computed, toRaw } from "vue";
import { watch, onMounted, ref, computed, toRaw, watchEffect } from "vue";
import { useGtm } from "@gtm-support/vue-gtm";
import { TooltipProvider } from "radix-vue";
import TalkEditor from "@/components/Talk/TalkEditor.vue";
Expand All @@ -36,6 +36,7 @@ import AllDialog from "@/components/Dialog/AllDialog.vue";
import MenuBar from "@/components/Menu/MenuBar/MenuBar.vue";
import { useMenuBarData as useTalkMenuBarData } from "@/components/Talk/menuBarData";
import { useMenuBarData as useSingMenuBarData } from "@/components/Sing/menuBarData";
import { setFontToCss, setThemeToCss } from "@/domain/dom";
import { ExhaustiveError } from "@/type/utility";
const store = useStore();
Expand Down Expand Up @@ -66,13 +67,9 @@ watch(
);
// フォントの制御用パラメータを変更する
watch(
() => store.state.editorFont,
(editorFont) => {
document.body.setAttribute("data-editor-font", editorFont);
},
{ immediate: true },
);
watchEffect(() => {
setFontToCss(store.state.editorFont);
});
// エディタの切り替えを監視してショートカットキーの設定を変更する
watch(
Expand All @@ -84,6 +81,22 @@ watch(
},
);
// テーマの変更を監視してCSS変数を変更する
watchEffect(() => {
const theme = store.state.availableThemes.find((value) => {
return value.name == store.state.currentTheme;
});
if (theme == undefined) {
// NOTE: Vuexが初期化されていない場合はまだテーマが読み込まれていないので無視
if (store.state.isVuexReady) {
throw Error(`Theme not found: ${store.state.currentTheme}`);
} else {
return;
}
}
setThemeToCss(theme);
});
// ソフトウェアを初期化
const { hotkeyManager } = useHotkeyManager();
const isEnginesReady = ref(false);
Expand Down
6 changes: 3 additions & 3 deletions src/components/Dialog/SettingDialog/SettingDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -569,14 +569,14 @@ const undoableTrackOperations = computed({
// 外観
const currentThemeNameComputed = computed({
get: () => store.state.themeSetting.currentTheme,
get: () => store.state.currentTheme,
set: (currentTheme: string) => {
void store.dispatch("SET_THEME_SETTING", { currentTheme: currentTheme });
void store.dispatch("SET_CURRENT_THEME_SETTING", { currentTheme });
},
});
const availableThemeNameComputed = computed(() => {
return [...store.state.themeSetting.availableThemes]
return [...store.state.availableThemes]
.sort((a, b) => a.order - b.order)
.map((theme) => {
return { label: theme.displayName, value: theme.name };
Expand Down
2 changes: 1 addition & 1 deletion src/components/Sing/SequencerPitch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const props = defineProps<{
const { warn, error } = createLogger("SequencerPitch");
const store = useStore();
const tpqn = computed(() => store.state.tpqn);
const isDark = computed(() => store.state.themeSetting.currentTheme === "Dark");
const isDark = computed(() => store.state.currentTheme === "Dark");
const tempos = computed(() => [store.state.tempos[0]]);
const pitchEditData = computed(() => {
return store.getters.SELECTED_TRACK.pitchEditData;
Expand Down
42 changes: 42 additions & 0 deletions src/domain/dom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { colors, Dark, setCssVar } from "quasar";
import { EditorFontType, ThemeColorType, ThemeConf } from "@/type/preload";

/** テーマの設定をCSSへ反映する */
export function setThemeToCss(theme: ThemeConf) {
for (const key in theme.colors) {
const color = theme.colors[key as ThemeColorType];
const { r, g, b } = colors.hexToRgb(color);
document.documentElement.style.setProperty(`--color-${key}`, color);
document.documentElement.style.setProperty(
`--color-${key}-rgb`,
`${r}, ${g}, ${b}`,
);
}
const mixColors: ThemeColorType[][] = [
["primary", "background"],
["warning", "background"],
];
for (const [color1, color2] of mixColors) {
const color1Rgb = colors.hexToRgb(theme.colors[color1]);
const color2Rgb = colors.hexToRgb(theme.colors[color2]);
const r = Math.trunc((color1Rgb.r + color2Rgb.r) / 2);
const g = Math.trunc((color1Rgb.g + color2Rgb.g) / 2);
const b = Math.trunc((color1Rgb.b + color2Rgb.b) / 2);
const propertyName = `--color-mix-${color1}-${color2}-rgb`;
const cssColor = `${r}, ${g}, ${b}`;
document.documentElement.style.setProperty(propertyName, cssColor);
}
Dark.set(theme.isDark);
setCssVar("primary", theme.colors["primary"]);
setCssVar("warning", theme.colors["warning"]);

document.documentElement.setAttribute(
"is-dark-theme",
theme.isDark ? "true" : "false",
);
}

/** フォントを設定する */
export function setFontToCss(font: EditorFontType) {
document.body.setAttribute("data-editor-font", font);
}
75 changes: 14 additions & 61 deletions src/store/setting.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { Dark, setCssVar, colors } from "quasar";
import { SettingStoreState, SettingStoreTypes } from "./type";
import { createDotNotationUILockAction as createUILockAction } from "./ui";
import { createDotNotationPartialStore as createPartialStore } from "./vuex";
import {
HotkeySettingType,
SavingSetting,
ExperimentalSettingType,
ThemeColorType,
ThemeConf,
ToolbarSettingType,
EngineId,
ConfirmedTips,
Expand All @@ -33,10 +30,8 @@ export const settingStoreState: SettingStoreState = {
engineIds: [],
engineInfos: {},
engineManifests: {},
themeSetting: {
currentTheme: "Default",
availableThemes: [],
},
currentTheme: "Default",
availableThemes: [],
editorFont: "default",
showTextLineNumber: false,
showAddAudioItemButton: true,
Expand Down Expand Up @@ -83,16 +78,12 @@ export const settingStore = createPartialStore<SettingStoreTypes>({
});
});

const theme = await window.backend.theme();
if (theme) {
mutations.SET_THEME_SETTING({
currentTheme: theme.currentTheme,
themes: theme.availableThemes,
});
void actions.SET_THEME_SETTING({
currentTheme: theme.currentTheme,
});
}
mutations.SET_AVAILABLE_THEMES({
themes: await window.backend.getAvailableThemes(),
});
void actions.SET_CURRENT_THEME_SETTING({
currentTheme: await window.backend.getSetting("currentTheme"),
});

void actions.SET_ACCEPT_RETRIEVE_TELEMETRY({
acceptRetrieveTelemetry: await window.backend.getSetting(
Expand Down Expand Up @@ -230,61 +221,23 @@ export const settingStore = createPartialStore<SettingStoreTypes>({
},
},

SET_THEME_SETTING: {
mutation(
state,
{ currentTheme, themes }: { currentTheme: string; themes?: ThemeConf[] },
) {
if (themes) {
state.themeSetting.availableThemes = themes;
}
state.themeSetting.currentTheme = currentTheme;
SET_CURRENT_THEME_SETTING: {
mutation(state, { currentTheme }: { currentTheme: string }) {
state.currentTheme = currentTheme;
},
action({ state, mutations }, { currentTheme }: { currentTheme: string }) {
void window.backend.theme(currentTheme);
const theme = state.themeSetting.availableThemes.find((value) => {
void window.backend.setSetting("currentTheme", currentTheme);
const theme = state.availableThemes.find((value) => {
return value.name == currentTheme;
});

if (theme == undefined) {
throw Error("Theme not found");
}

for (const key in theme.colors) {
const color = theme.colors[key as ThemeColorType];
const { r, g, b } = colors.hexToRgb(color);
document.documentElement.style.setProperty(`--color-${key}`, color);
document.documentElement.style.setProperty(
`--color-${key}-rgb`,
`${r}, ${g}, ${b}`,
);
}
const mixColors: ThemeColorType[][] = [
["primary", "background"],
["warning", "background"],
];
for (const [color1, color2] of mixColors) {
const color1Rgb = colors.hexToRgb(theme.colors[color1]);
const color2Rgb = colors.hexToRgb(theme.colors[color2]);
const r = Math.trunc((color1Rgb.r + color2Rgb.r) / 2);
const g = Math.trunc((color1Rgb.g + color2Rgb.g) / 2);
const b = Math.trunc((color1Rgb.b + color2Rgb.b) / 2);
const propertyName = `--color-mix-${color1}-${color2}-rgb`;
const cssColor = `${r}, ${g}, ${b}`;
document.documentElement.style.setProperty(propertyName, cssColor);
}
Dark.set(theme.isDark);
setCssVar("primary", theme.colors["primary"]);
setCssVar("warning", theme.colors["warning"]);

document.documentElement.setAttribute(
"is-dark-theme",
theme.isDark ? "true" : "false",
);

window.backend.setNativeTheme(theme.isDark ? "dark" : "light");

mutations.SET_THEME_SETTING({
mutations.SET_CURRENT_THEME_SETTING({
currentTheme: currentTheme,
});
},
Expand Down
12 changes: 8 additions & 4 deletions src/store/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import {
MoraDataType,
SavingSetting,
ThemeConf,
ThemeSetting,
ExperimentalSettingType,
ToolbarSettingType,
UpdateInfo,
Expand Down Expand Up @@ -1782,7 +1781,8 @@ export type SettingStoreState = {
engineIds: EngineId[];
engineInfos: Record<EngineId, EngineInfo>;
engineManifests: Record<EngineId, EngineManifest>;
themeSetting: ThemeSetting;
currentTheme: string;
availableThemes: ThemeConf[];
acceptTerms: AcceptTermsStatus;
acceptRetrieveTelemetry: AcceptRetrieveTelemetryStatus;
experimentalSetting: ExperimentalSettingType;
Expand Down Expand Up @@ -1823,8 +1823,8 @@ export type SettingStoreTypes = {
action(payload: KeyValuePayload<RootMiscSettingType>): void;
};

SET_THEME_SETTING: {
mutation: { currentTheme: string; themes?: ThemeConf[] };
SET_CURRENT_THEME_SETTING: {
mutation: { currentTheme: string };
action(payload: { currentTheme: string }): void;
};

Expand Down Expand Up @@ -2016,6 +2016,10 @@ export type UiStoreTypes = {
action(payload: { activePointScrollMode: ActivePointScrollMode }): void;
};

SET_AVAILABLE_THEMES: {
mutation: { themes: ThemeConf[] };
};

DETECT_UNMAXIMIZED: {
mutation: undefined;
action(): void;
Expand Down
6 changes: 6 additions & 0 deletions src/store/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,12 @@ export const uiStore = createPartialStore<UiStoreTypes>({
},
},

SET_AVAILABLE_THEMES: {
mutation(state, { themes }) {
state.availableThemes = themes;
},
},

DETECT_UNMAXIMIZED: {
mutation(state) {
state.isMaximized = false;
Expand Down
Loading

0 comments on commit 4bb13ca

Please sign in to comment.