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

コンテキストメニューのVue化 #1374

Merged
merged 102 commits into from
Jul 22, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
88c4ada
見た目上だけ整えた
thiramisu Jul 3, 2023
7a48ed3
denseが抜けてたのを修正
thiramisu Jul 3, 2023
6c853c2
各アイテム選択時の動作を追加
thiramisu Jul 5, 2023
3b81808
コメントアウト内容修正
thiramisu Jul 5, 2023
32ab2dc
読み更新処理が発火しないバグを修正
thiramisu Jul 5, 2023
92e32eb
コメントアウト修正
thiramisu Jul 5, 2023
742e258
未使用になったelectron/contextMenu.ts関連の記述を削除
thiramisu Jul 5, 2023
d928868
関数名の見直しと共通化
thiramisu Jul 5, 2023
c7a54e3
pasteOnAudioCell関数の一部を別関数に切り分け
thiramisu Jul 6, 2023
a35cc73
コメントアウトのミスの修正
thiramisu Jul 6, 2023
5962c2c
複数行ペースト処理周りのリファクタリング
thiramisu Jul 6, 2023
59701b8
コンテキストメニューにも複数行貼り付けを追加
thiramisu Jul 6, 2023
5bff4f3
コメントアウト消し忘れ
thiramisu Jul 6, 2023
2f0e943
no-focusを追加
thiramisu Jul 6, 2023
b46cb46
リファクタリング
thiramisu Jul 7, 2023
a027074
Merge branch 'main' into context-menu-vue
thiramisu Jul 7, 2023
e8e9645
コンフリクトの解消にし失敗してた箇所の修正
thiramisu Jul 8, 2023
908074c
コンフリクトの解消に失敗していたのを修正
thiramisu Jul 8, 2023
390fd6d
コンテキストメニュー開閉時に読みが更新されてしまうバグを修正
thiramisu Jul 8, 2023
2f1bb26
変数名を見直し
thiramisu Jul 8, 2023
3eb2d15
コンテキストメニューにヘッダー追加
thiramisu Jul 8, 2023
4052464
省略時の見た目を少し改善
thiramisu Jul 8, 2023
2c7c04e
未選択時の挙動を追加
thiramisu Jul 8, 2023
9d0083b
リファクタリング
thiramisu Jul 8, 2023
d161cb6
範囲選択していない時にコピーと切り取りをdisabledに
thiramisu Jul 8, 2023
186580f
型エラーに抗う
thiramisu Jul 8, 2023
36a1725
型エラーを解決
thiramisu Jul 8, 2023
2835030
setActiveAudioKeyからコンテキストメニュー関連の処理を分離
thiramisu Jul 9, 2023
94bead3
裏技の使用をやめる
thiramisu Jul 9, 2023
deb6016
onhideをonbeforehideに
thiramisu Jul 9, 2023
be2a117
コンテキストメニュー関連の処理をbefore-show/before-hide/hideに統一
thiramisu Jul 10, 2023
05618ae
コメントアウト修正
thiramisu Jul 10, 2023
1011150
コメントアウトを改善
thiramisu Jul 10, 2023
e14a25d
変数名を変更
thiramisu Jul 10, 2023
9d7cf53
変数名に合わせてtrue/falseを逆に
thiramisu Jul 10, 2023
25076e8
バグ修正
thiramisu Jul 10, 2023
11f92ee
実行タイミング変更の影響で貼り付けがバグっていたので修正
thiramisu Jul 10, 2023
d6172e5
`willSelectAll`と`cursorPosition`の抹消
thiramisu Jul 11, 2023
c0d873f
フラグ変更処理をContextMenu.vueのへ移動
thiramisu Jul 12, 2023
e36f40f
コメントアウト追加
thiramisu Jul 12, 2023
b77cff0
コードレビューを一部反映
thiramisu Jul 12, 2023
60eeed8
lint的なtype error回避のための記述であることを明確に
thiramisu Jul 12, 2023
f11c768
。付け忘れ
thiramisu Jul 12, 2023
d6a4161
イベントを書く順番を調整
thiramisu Jul 13, 2023
b921081
no-focusを開く時に何を使ったかで切り替える
thiramisu Jul 13, 2023
40ac6af
バグ修正
thiramisu Jul 13, 2023
d6e75b5
throwする意味なかさそうなので反転
thiramisu Jul 14, 2023
3c1e24f
headerを元の使用に戻す
thiramisu Jul 14, 2023
e671d0b
コメントアウト追加とdiff減らし
thiramisu Jul 14, 2023
fa2bf39
コメントアウト更新
thiramisu Jul 14, 2023
af3728f
Merge branch 'VOICEVOX:main' into context-menu-vue
thiramisu Jul 14, 2023
d8e97b1
元からあった選択範囲の残るバグを多少強引に修正
thiramisu Jul 15, 2023
4aa7791
Merge branch 'context-menu-vue' of https://github.com/thiramisu/voice…
thiramisu Jul 15, 2023
c2b25ad
それぞれ条件が抜けていたのを修正
thiramisu Jul 15, 2023
efa05e2
さらに修正
thiramisu Jul 15, 2023
876f56a
さらに修正
thiramisu Jul 15, 2023
bbc2fb9
記述をコンパクトに
thiramisu Jul 15, 2023
0a51a85
そもそも追加無しならblurされないように
thiramisu Jul 15, 2023
da45a19
Update src/helpers/QInputSelectionHelper.ts
thiramisu Jul 17, 2023
0d9453e
Update src/components/ContextMenu.vue
thiramisu Jul 17, 2023
79ddbda
unselectのreturn trueをなくす
thiramisu Jul 17, 2023
5d51d17
Helperの名前を変更
thiramisu Jul 17, 2023
f25125c
empty()の名前をtoEmpty()に
thiramisu Jul 17, 2023
414d6b1
Update src/components/AudioCell.vue
thiramisu Jul 17, 2023
a37729a
Merge branch 'context-menu-vue' of https://github.com/thiramisu/voice…
thiramisu Jul 17, 2023
64349ac
Merge branch 'VOICEVOX:main' into context-menu-vue
thiramisu Jul 17, 2023
89f2c6a
冗長な記述の削除とstartFragmentをsubstringBeforeに
thiramisu Jul 17, 2023
9593ea6
textSplitterを関数内に移動
thiramisu Jul 17, 2023
476f29a
定数を関数内に移動し所属を明確に
thiramisu Jul 17, 2023
c1de919
冗長だった`nextSelectionText`を削除
thiramisu Jul 17, 2023
15bf892
ブラウザ版そもそも不要な宣言を削除して実質的なコンフリクトを解消
thiramisu Jul 17, 2023
baeb910
冗長な記述の変更
thiramisu Jul 18, 2023
80569b9
nativeElの隠蔽
thiramisu Jul 18, 2023
f0cb1d9
onTabKeyUpの全選択処理を削除
thiramisu Jul 18, 2023
11c2486
`unSelect`の名前を`clearInputSelection`に
thiramisu Jul 18, 2023
c3edcfe
関数名にIfNeededをつける
thiramisu Jul 18, 2023
487b8d6
コードレビューを反映
thiramisu Jul 20, 2023
6322b46
Merge branch 'VOICEVOX:main' into context-menu-vue
thiramisu Jul 20, 2023
c3a157f
`no-focus`付与条件の見直し
thiramisu Jul 21, 2023
447092a
mousetrapに抗う
thiramisu Jul 21, 2023
53494fb
元からあった誤字を修正し、ついでに表記をあさせる
thiramisu Jul 21, 2023
c9b6a1f
直しきれていなかったバグを修正
thiramisu Jul 21, 2023
8b79075
`onMounted`を使用
thiramisu Jul 21, 2023
65cc9f6
選択範囲が残らないようにするコードをグローバルに適用
thiramisu Jul 22, 2023
067ce9f
コンテキストメニューを閉じる場合を除外
thiramisu Jul 22, 2023
8a79dd9
コメントアウトを修正
thiramisu Jul 22, 2023
2c626fc
コメントアウト修正
thiramisu Jul 22, 2023
65a3f8a
``willDispatchFocusOrBlur`を`AudioCell.vue`に移動
thiramisu Jul 22, 2023
0ed134b
Mousetrap回避策の見直し
thiramisu Jul 22, 2023
973d7b8
選択範囲が残らないようにするコードをAudioCell内に戻す
thiramisu Jul 22, 2023
645c10a
戻したのに伴って不要になったコードを削除
thiramisu Jul 22, 2023
7b1b72a
コメントアウトのコピペミスを修正
thiramisu Jul 22, 2023
8d5afc8
変数宣言をなるべく使用直前に
thiramisu Jul 22, 2023
9e27cc9
hideすることを明示
thiramisu Jul 22, 2023
e46dba2
Merge branch 'context-menu-vue' of https://github.com/thiramisu/voice…
thiramisu Jul 22, 2023
c7deec7
不要な`<style>`を削除
thiramisu Jul 22, 2023
0b1fa27
コメントアウトを修正
thiramisu Jul 22, 2023
5775cc8
アニメーションをなくす
thiramisu Jul 22, 2023
4d827fa
Merge branch 'context-menu-vue' of https://github.com/thiramisu/voice…
thiramisu Jul 22, 2023
8f60536
`getMenuItemButton`を`readyForContextMenu`内に
thiramisu Jul 22, 2023
e762a04
contextmenuかcontextMenuかの表記揺れを修正
thiramisu Jul 22, 2023
cb11a25
コンフリクト解消
thiramisu Jul 22, 2023
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
5 changes: 0 additions & 5 deletions src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import dayjs from "dayjs";
import windowStateKeeper from "electron-window-state";
import zodToJsonSchema from "zod-to-json-schema";
import { hasSupportedGpu } from "./electron/device";
import { textEditContextMenu } from "./electron/contextMenu";
import {
HotkeySetting,
ThemeConf,
Expand Down Expand Up @@ -736,10 +735,6 @@ ipcMainHandle("SHOW_IMPORT_FILE_DIALOG", (_, { title }) => {
})?.[0];
});

ipcMainHandle("OPEN_TEXT_EDIT_CONTEXT_MENU", () => {
textEditContextMenu.popup({ window: win });
});

ipcMainHandle("IS_AVAILABLE_GPU_MODE", () => {
return hasSupportedGpu(process.platform);
});
Expand Down
159 changes: 121 additions & 38 deletions src/components/AudioCell.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@
:model-value="audioTextBuffer"
:aria-label="`${textLineNumberIndex}行目`"
@update:model-value="setAudioTextBuffer"
@change="willRemove || pushAudioText()"
@blur="pushAudioTextIfNeeded()"
@paste="pasteOnAudioCell"
@focus="setActiveAudioKey()"
@keydown.prevent.up.exact="moveUpCell"
@keydown.prevent.down.exact="moveDownCell"
@mouseup.right="onRightClickTextField"
@keydown.prevent.enter.exact="pushAudioTextIfNeeded()"
:aria-label="`${textLineNumberIndex}行目`"
Copy link
Contributor

Choose a reason for hiding this comment

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

なんか私が入れたやつが怒られてますね

Attribute ":aria-label" should go before "@keydown.prevent.enter.exact"

ほんとに順番入れ替えれば治る問題なんだろうかw

Copy link
Member

@Hiroshiba Hiroshiba Jul 8, 2023

Choose a reason for hiding this comment

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

いったんこちらに関してだけコメントをば 🙇
これはエラーというよりワーニングで、並び順は順不同で複数人で書いてるとごちゃごちゃになっちゃうので、順番を作ってそれを守りましょうという感じの意図のメッセージです!

>
<template #error>
文章が長いと正常に動作しない可能性があります。
Expand All @@ -55,16 +56,19 @@
@click="removeCell"
/>
</template>
<context-menu :menudata="contextMenudata" />
</q-input>
</div>
</template>

<script setup lang="ts">
import { computed, watch, ref } from "vue";
import { computed, watch, ref, nextTick } from "vue";
import { QInput } from "quasar";
import CharacterButton from "./CharacterButton.vue";
import CharacterButton from "@/components/CharacterButton.vue";
import ContextMenu, { ContextMenuItemData } from "@/components/ContextMenu.vue";
import { useStore } from "@/store";
import { AudioKey, Voice } from "@/type/preload";
import { AudioKey, SplitTextWhenPasteType, Voice } from "@/type/preload";
import { QInputSelectionHelper } from "@/helpers/QInputSelectionHelper";

const props =
defineProps<{
Expand Down Expand Up @@ -148,8 +152,8 @@ watch(
}
);

const pushAudioText = async () => {
if (isChangeFlag.value) {
const pushAudioTextIfNeeded = async () => {
if (!willRemove.value && isChangeFlag.value) {
isChangeFlag.value = false;
await store.dispatch("COMMAND_CHANGE_AUDIO_TEXT", {
audioKey: props.audioKey,
Expand All @@ -158,46 +162,56 @@ const pushAudioText = async () => {
}
};

let willSelectAll = false;
// NOTE: コンテキストメニューアイテムのonClick実行後も再フォーカスされるため発火する
const setActiveAudioKey = () => {
if (willSelectAll) {
willSelectAll = false;
textfield.value?.select();
}

store.dispatch("SET_ACTIVE_AUDIO_KEY", { audioKey: props.audioKey });
};
const isEnableSplitText = computed(() => store.state.splitTextWhenPaste);

// コピペしたときに句点と改行で区切る
const textSplitType = computed(() => store.state.splitTextWhenPaste);
const textSplitter: Record<SplitTextWhenPasteType, (text: string) => string[]> =
{
PERIOD_AND_NEW_LINE: (text) =>
text.replaceAll("。", "。\n\r").split(/[\n\r]/),
NEW_LINE: (text) => text.split(/[\n\r]/),
OFF: (text) => [text],
};
const pasteOnAudioCell = async (event: ClipboardEvent) => {
if (event.clipboardData && isEnableSplitText.value !== "OFF") {
let texts: string[] = [];
if (event.clipboardData && textSplitType.value !== "OFF") {
const clipBoardData = event.clipboardData.getData("text/plain");
switch (isEnableSplitText.value) {
case "PERIOD_AND_NEW_LINE":
texts = clipBoardData.replaceAll("。", "。\n\r").split(/[\n\r]/);
break;
case "NEW_LINE":
texts = clipBoardData.split(/[\n\r]/);
break;
}
const texts = textSplitter[textSplitType.value](clipBoardData);

if (texts.length > 1) {
event.preventDefault();
blurCell(); // フォーカスを外して編集中のテキスト内容を確定させる

const prevAudioKey = props.audioKey;
if (audioTextBuffer.value == "") {
const text = texts.shift();
if (text == undefined) return;
setAudioTextBuffer(text);
await pushAudioText();
}

const audioKeys = await store.dispatch("COMMAND_PUT_TEXTS", {
texts,
voice: audioItem.value.voice,
prevAudioKey,
});
if (audioKeys)
emit("focusCell", { audioKey: audioKeys[audioKeys.length - 1] });
await putMultilineText(texts);
}
}
};
const putMultilineText = async (texts: string[]) => {
blurCell(); // フォーカスを外して編集中のテキスト内容を確定させる

const prevAudioKey = props.audioKey;
if (audioTextBuffer.value == "") {
const text = texts.shift();
if (text == undefined) return;
setAudioTextBuffer(text);
await pushAudioTextIfNeeded();
}

const audioKeys = await store.dispatch("COMMAND_PUT_TEXTS", {
texts,
voice: audioItem.value.voice,
prevAudioKey,
});
if (audioKeys)
emit("focusCell", { audioKey: audioKeys[audioKeys.length - 1] });
};

// 行番号を表示するかどうか
const showTextLineNumber = computed(() => store.state.showTextLineNumber);
Expand Down Expand Up @@ -258,9 +272,76 @@ const deleteButtonEnable = computed(() => {
});

// テキスト編集エリアの右クリック
const onRightClickTextField = () => {
store.dispatch("OPEN_TEXT_EDIT_CONTEXT_MENU");
};
// input.valueをスクリプトから変更した場合は@changeが発火しないため、
// @blurと@keydown.prevent.enter.exactに分けている
const contextMenudata = ref<ContextMenuItemData[]>([
{
type: "button",
label: "切り取り",
onClick: async () => {
if (textfieldSelection.isEmpty) return;

const text = textfieldSelection.getAsString();
const start = textfieldSelection.start;
setAudioTextBuffer(textfieldSelection.getReplacedStringTo(""));
await navigator.clipboard.writeText(text);
// FIXME: <input>への反映までにラグがあるのを、
// 裏技的に上の行にawaitを追加して、await後に実行することで回避している。
// await nextTick()を利用すべきだがなぜか意図したとおりに動かない。
textfieldSelection.setCursorPosition(start);
},
disableWhenUiLocked: true,
},
{
type: "button",
label: "コピー",
onClick: () => {
if (textfieldSelection.isEmpty) return;

navigator.clipboard.writeText(textfieldSelection.getAsString());
},
disableWhenUiLocked: true,
},
{
type: "button",
label: "貼り付け",
onClick: async () => {
const text = await navigator.clipboard.readText();

// 複数行貼り付け
if (textSplitType.value !== "OFF") {
const texts = textSplitter[textSplitType.value](text);
if (texts.length > 1) {
await putMultilineText(texts);
return;
}
}

const beforeLength = textfieldSelection.nativeEl.value.length;
const end = textfieldSelection.end ?? 0;
setAudioTextBuffer(textfieldSelection.getReplacedStringTo(text, true));
await nextTick();
textfieldSelection.setCursorPosition(
// 自動的に削除される改行などの文字数を念のため考慮している
end + textfieldSelection.nativeEl.value.length - beforeLength
);
},
disableWhenUiLocked: true,
},
{ type: "separator" },
{
type: "button",
label: "全選択",
onClick: async () => {
// コンテキストメニューを閉じた場合、多分Quasarの内部処理として
// コンテキストメニューを開いた時点の選択範囲が復元(再選択)される模様。
// その後に選択範囲を変更しないと反映されないため、フラグを立てて後から処理する。
willSelectAll = true;
},
disableWhenUiLocked: true,
},
]);
// TODO: (MAY)コンテキストメニューを開いたときに選択範囲が外れる現象の原因調査・修正

const blurCell = (event?: KeyboardEvent) => {
if (event?.isComposing) {
Expand All @@ -274,6 +355,8 @@ const blurCell = (event?: KeyboardEvent) => {
// フォーカス
const textfield = ref<QInput>();

const textfieldSelection = new QInputSelectionHelper(textfield);

// 複数エンジン
const isMultipleEngine = computed(() => store.state.engineIds.length > 1);
</script>
Expand Down
32 changes: 32 additions & 0 deletions src/components/ContextMenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<template>
<q-menu touch-position context-menu no-focus>
<q-list dense>
<menu-item
v-for="(menu, index) of menudata"
:key="index"
:menudata="menu"
:disable="
uiLocked && menu.type !== 'separator' && menu.disableWhenUiLocked
"
></menu-item>
</q-list>
</q-menu>
</template>

<script setup lang="ts">
import { computed } from "vue";
import MenuItem from "@/components/MenuItem.vue";
import { MenuItemButton, MenuItemSeparator } from "@/components/MenuBar.vue";
import { useStore } from "@/store";

defineProps<{
menudata: ContextMenuItemData[];
}>();

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

export type ContextMenuItemData = MenuItemSeparator | MenuItemButton;
</script>

<style lang="scss" scoped></style>
9 changes: 0 additions & 9 deletions src/electron/contextMenu.ts

This file was deleted.

4 changes: 0 additions & 4 deletions src/electron/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,6 @@ const api: Sandbox = {
return await ipcRendererInvoke("READ_FILE", { filePath });
},

openTextEditContextMenu: () => {
return ipcRendererInvoke("OPEN_TEXT_EDIT_CONTEXT_MENU");
},

isAvailableGPUMode: () => {
return ipcRendererInvoke("IS_AVAILABLE_GPU_MODE");
},
Expand Down
70 changes: 70 additions & 0 deletions src/helpers/QInputSelectionHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { QInput } from "quasar";
import { Ref } from "vue";

/**
* QInput の選択範囲への操作を簡単にできるようにするクラス
*/
export class QInputSelectionHelper {
private _nativeEl: HTMLInputElement | undefined = undefined;

constructor(private textfield: Ref<QInput | undefined>) {}

// this.start が number | null なので null も受け付ける
setCursorPosition(index: number | null) {
if (index === null) return;

this.nativeEl.selectionStart = this.nativeEl.selectionEnd = index;
}

getReplacedStringTo(string: string, allowInsertOnly = false) {
if (!allowInsertOnly && this.isEmpty) {
return this.nativeEl.value;
}

const start = this.nativeEl.selectionStart ?? 0;
const end = this.nativeEl.selectionEnd ?? 0;

return `${this.nativeEl.value.substring(
0,
start
)}${string}${this.nativeEl.value.substring(end)}`;
}

getAsString() {
return this.nativeEl.value.substring(
this.nativeEl.selectionStart ?? 0,
this.nativeEl.selectionEnd ?? 0
);
}

empty() {
this.nativeEl.selectionEnd = this.nativeEl.selectionStart;
}

get nativeEl() {
return this._nativeEl ?? this.getNativeEl();
}

get start() {
return this.nativeEl.selectionStart;
}

get end() {
return this.nativeEl.selectionEnd;
}

get isEmpty() {
const start = this.nativeEl.selectionStart;
const end = this.nativeEl.selectionEnd;
return start === null || end === null || start === end;
}

private getNativeEl() {
const nativeEl = this.textfield.value?.nativeEl;
if (!(nativeEl instanceof HTMLInputElement)) {
throw new Error("nativeElの取得に失敗しました。");
}
this._nativeEl = nativeEl;
return nativeEl;
}
}
6 changes: 0 additions & 6 deletions src/store/audio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1897,12 +1897,6 @@ export const audioStore = createPartialStore<AudioStoreTypes>({
},
},

OPEN_TEXT_EDIT_CONTEXT_MENU: {
action() {
window.electron.openTextEditContextMenu();
},
},

CHECK_FILE_EXISTS: {
action(_, { file }: { file: string }) {
return window.electron.checkFileExists(file);
Expand Down
4 changes: 0 additions & 4 deletions src/store/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,10 +470,6 @@ export type AudioStoreTypes = {
action(): void;
};

OPEN_TEXT_EDIT_CONTEXT_MENU: {
action(): void;
};

CHECK_FILE_EXISTS: {
action(payload: { file: string }): Promise<boolean>;
};
Expand Down
Loading