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

辞書の単語追加・編集UIの単語・読み入力欄で右クリックメニューを使えるようにする #747

Closed
aoirint opened this issue Mar 10, 2022 · 9 comments · Fixed by #2156

Comments

@aoirint
Copy link
Member

aoirint commented Mar 10, 2022

内容

辞書の単語追加・編集UIの単語・読み入力欄では、右クリックメニューがないようでした(Ctrl+X/C/Vのショートカットキーはそれぞれ動作しました)。

読み上げテキストの入力欄と同じような右クリックメニューがあるとよさそうです。

image

image

Pros 良くなる点

マウスでコピペ操作ができる

Cons 悪くなる点

実現方法

VOICEVOXのバージョン

0.11.3

その他

@Hiroshiba
Copy link
Member

Issue作成ありがとうございます!
右クリックコンテキストメニューを開くようになったのはこちらのPRなので参考になるかも

@Hiroshiba
Copy link
Member

Hiroshiba commented Oct 29, 2023

こちらのissueですが、コンテキストメニューは #1374 でやり方が相当変わって、Vueの中で完結するようになったので結構タスクとして簡単になっていると思います。
今だったらこの辺りのコードが参考になるかなと思います!
https://github.com/VOICEVOX/voicevox/pull/1374/files#diff-444f263f72d4db11fe82c672d5c232eb4c29d29dbc1ffd20e279d586b1b2c180R371-R379

@jdkfx
Copy link
Contributor

jdkfx commented Jun 27, 2024

こちら取り組みます!

@jdkfx
Copy link
Contributor

jdkfx commented Jun 27, 2024

実装の参考にしている/src/components/Talk/AudioCell.vueにおいて、ref<QInput>();textFieldのみなのですが、今回実装しようとしている/src/components/Dialog/DictionaryManageDialog.vueにおいては、ref<QInput>();の値がsurfaceInputyomiInputの二つがあります。

この二つにおいて切り取りやコピー、貼り付けを行う際に、surfaceInputyomiInputのそれぞれに切り取りやコピー、貼り付けの処理を実装すべきなのかどうか悩んでいます。
しかし、この場合同じような処理を二つもかいてしまうのはコードの量が大きくなってしまうため、可読性が低くなるなど考えられます。

surfaceInputyomiInputを以下のAudioCell.vueにおけるtextFieldSelectionのように二つを一つにまとめて書くことで読みやすく、コードの量も減らせるのではないかとも考えています。
その場合、surfaceInputをコピーしたつもりがyomiInputをコピーしていたといった感じで、どちらの値を処理の中で利用しているのか分からなくなるということも考えられます。

よって、どのように実装を進めていいのか分からないため、何かしらのアドバイスをいただけますと嬉しいです。

// AudioCell.vue

const textField = ref<QInput>();
const textFieldSelection = new SelectionHelperForQInput(textField);

// 省略

onClick: async () => {
      contextMenu.value?.hide();
      if (textFieldSelection.isEmpty) return;

      const text = textFieldSelection.getAsString();
      const start = textFieldSelection.selectionStart;
      setAudioTextBuffer(textFieldSelection.getReplacedStringTo(""));
      await nextTick();
      navigator.clipboard.writeText(text);
      textFieldSelection.setCursorPosition(start);
    },
<!-- DictionaryManageDialog.vue -->

<div class="row q-pl-md q-mt-md">
              <div class="text-h6">単語</div>
              <QInput
                ref="surfaceInput"
                v-model="surface"
                class="word-input"
                dense
                :disable="uiLocked"
                @blur="setSurface(surface)"
                @keydown.enter="yomiFocus"
              >
                <ContextMenu
                  ref="contextMenu"
                  :header="contextMenuHeader"
                  :menudata="contextMenudata"
                />
              </QInput>
            </div>
            <div class="row q-pl-md q-pt-sm">
              <div class="text-h6">読み</div>
              <QInput
                ref="yomiInput"
                v-model="yomi"
                class="word-input q-pb-none"
                dense
                :error="!isOnlyHiraOrKana"
                :disable="uiLocked"
                @blur="setYomi(yomi)"
                @keydown.enter="setYomiWhenEnter"
              >
                <template #error>
                  読みに使える文字はひらがなとカタカナのみです。
                </template>
                <ContextMenu
                  ref="contextMenu"
                  :header="contextMenuHeader"
                  :menudata="contextMenudata"
                />
              </QInput>
            </div>

@jdkfx
Copy link
Contributor

jdkfx commented Jun 27, 2024

https://ja.vuejs.org/guide/reusability/composables
これが使えそうとの助言をいただいたので、読み込んで実装できそうでしたらこれを使用します。

@Hiroshiba
Copy link
Member

挑戦ありがとうございます!!

おっしゃるとおり、コンポーザブルを使えば可能かもです!
既存のAudioCell.vue内のコードもそのコンポーザブルを使う形にできると良さそうですが、ざっとコード読んだ感じ相当しんどそうですね。。。。。。。。

もっと簡単な方法として、OS標準の右クリックメニューを使うというのもあります!
このissueが建てられた頃(release-0.11ブランチ)のコードはそうなっていて、electron側に設定を用意しないとなのですが、かなり簡単です。

右クリックしたときのvue側の処理はこのあたり

// テキスト編集エリアの右クリック
const onRightClickTextField = () => {
store.dispatch("OPEN_TEXT_EDIT_CONTEXT_MENU");
};

electron側の処理はこのあたり

voicevox/src/background.ts

Lines 808 to 811 in ded825f

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

以前真っ向からtextFieldに戦いを挑んだのですが、かーーーーーなり難しいので、いつでも引き返して良いと思います!!
少なくともこのissueを解決するのはelectronの機能使う方がめちゃくちゃおすすめです。
ぜひ、需要もあるけど実装も大変な別のところで @jdkfx さんの力をお借りしたいです・・・!!

@jdkfx
Copy link
Contributor

jdkfx commented Jul 3, 2024

この二つにおいて切り取りやコピー、貼り付けを行う際に、surfaceInputとyomiInputのそれぞれに切り取りやコピー、貼り付けの処理を実装すべきなのかどうか悩んでいます。

もしかしたら自分が難しく考えてただけかもしれないです。
単純にどちらのInput要素にfocus(もしくはselect)しているかを判定してあげればいいのではないかと考えました。
そのうえで、focus(もしくはselect)している値に対してAudioCell.vueにおけるコンテキストメニューの処理を同じように書いていけば実装できるかなと...

<QInput @focus="handleFocus('surface')">
  <ContextMenu/>
</QInput>

// ...

<QInput @focus="handleFocus('yomi')">
  <ContextMenu/>
</QInput>

// ...

// surface と yomi のどちらを選択しているか
const focusInputElement = ref("");
const handleFocus = (inputName: string) => {
  focusInputElement.value = inputName;
};

@Hiroshiba
Copy link
Member

Hiroshiba commented Jul 4, 2024

@jdkfx 良いですね!!

別の発想として、コンポーザブルを用意してあげて、得たhandle関数をQInputに渡すという手もあると思います!

このままでは動かないかもですが、疑似コードこんな雰囲気です。

// コンポーザブル側

export function useRightContextMenu(qInputRef: Ref<QInput>) {
  const handleFocus = (inputName: string) => {
    qInputRef.value = inputName;
  }

  // もっといろいろ定義する

  return { handleFocus };
}
// 使う側、script

const surfaceElementRef = ref("");
const { surfaceHandleFocus: handleFocus } = useRightContextMenu(surfaceElementRef);

const yomiElementRef = ref("");
const { yomiHandleFocus: handleFocus } = useRightContextMenu(yomiElementRef);
<!-- 使う側、template -->

<template>
  <QInput @focus="surfaceHandleFocus" ref="surfaceElementRef">
    <ContextMenu/>
  </QInput>

  <QInput @focus="yomiHandleFocus" ref="yomiElementRef">
    <ContextMenu/>
  </QInput>
</template>

こうしておけば他の箇所でテキスト入力したくなった場合も、useRightContextMenuをimportすればすぐ使えるという感じです!

@jdkfx
Copy link
Contributor

jdkfx commented Jul 29, 2024

こちらのIssue、なかなか難しいなと感じておりまして、ヘルプをいただけましたら幸いです!
Draft PRをこちらに出しておりますので、どなたか「ここをこうしてみたら?」というような改善点の指摘や、アドバイスなどお願いいたします!

#2156

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants