Magitで使用されるものと同様のポップアップメニューを実装する方法


10

質問

私はの形でユーザーインターフェイスを作成したいポップアップメニューMagitに使用されるものと同様のポップアップメニュー

特徴

ポップアップの定義

この質問のポップアップは、メニュー項目のコレクションを含む小さな一時的なウィンドウを意味し、ユーザーはこれらの項目の1つだけを選択できます。

画面上の位置

ポップアップは画面のどの部分にも表示できますが、非常にわかりやすく、現在アクティブなウィンドウの横に表示されることが望ましいです。

ポップアップバッファの内容

アイテムはきれいな表の形で表示されるべきです。かなりの質問のコンテキストは視覚的に魅力的であることを意味します。この効果は、メニュー項目をまっすぐな列に配置することで最も簡単に実現できます complete--insert-string。この段落は、追加の説明に役立ちます。独自の方法で行うことができます。これにより、答えが不正確になることはありません。

メニュー項目の選択

選択は、単一のキーを押すか、必要に応じてマウスを使用して実行されることが予想されます(それほど重要ではないため、マウスをサポートしない命題を含む回答は合法です)。マウスをサポートするソリューションを提案する場合は、ユーザーが直感的な方法で、つまり目的の選択肢を左ボタンをクリックすることによってメニュー項目を選択できる必要があることに注意してください。

NBマウスはさまざまな方法で使用でき、選択肢を示す別の方法も歓迎します。

ポップアップの排除

ユーザーが上記の方法でメニュー項目を選択すると、バッファーとそのウィンドウが表示から削除され、強制終了されます。ポップアップメニューを呼び出す前にアクティブであったウィンドウは、再びフォーカスされる(つまり、アクティブになる)必要があります。

戻り値と引数

できれば、このアクションの結果として、Lispオブジェクトが返される必要があります。Lispオブジェクトは次のいずれかです。

  • nil—これは、ユーザーがを押すC-gか、その他の方法でポップアップメニューを中止したことを示します†。

  • string—文字列(シンボルの使用が許可されています)はstring-equal 、実際のアイテムのコレクションとしてポップアップメニューに提供される文字列の1つにする必要があります。

プログラムの残りの部分にユーザーの選択、または場合によってはその不在を知らせる別の方法も許容されます。ただし、他にどのように実行できるかが明確でない場合は、すべての回答者に即興で質問し、この側面のさらなる明確化を求めません。

これはすべて戻り値です。入力パラメータについては、可能な選択肢(つまり、メニュー項目)を表す文字列のコレクションを少なくとも含める必要があります。

許容できる回答

予想される回答は次の形式になります。

  • 知識のある読者が上記のような関数を記述できるようにする十分なコードスニペット。作業関数全体を記述することは予期されていません。ただし、不確実性を避けるために(コードのかなりの部分を省略できますか?)、スニペットの欠落部分は回答のテキストコンポーネントで説明する必要があります。

  • 同様の機能を実装する既存のライブラリへのリンク。不確実性を避けるために、私たちの場合と同様に、ライブラリーを使用して、上記の機能を少なくとも2つまたは3つ持つポップアップ(上記の定義を参照)を作成できることに注意してください。提案されたライブラリーが前述の条件を満たせない点と異なる場合、そのようなケースはそれぞれ独立して判断され、OPが有用であると判断した場合は常に賛成されます。

  • «機能»セクションで説明されている機能を実装するために使用できる組み込みEmacs関数またはサードパーティの関数の説明。上記を参照してください。回避の不確実性には、あなたの答えは実装したい、将来の読者のためにどのように活用できるか明確にしてください状態のポップアップMagitに使用されるものと同様のポップアップメニューを


†ポップアップメニューを中止する別の方法には、次のものがあります(これらに限定されません)。

  • ポップアップメニューウィンドウの外側をクリックします。

  • 選択せずにポップアップを含むバッファを強制終了します。

回答:


8

magit-popupそれの持つ独自のマニュアルを!しかし、実際にポップアップで引数を設定して、呼び出されたアクションに渡す必要がない限り、hydra代わりに使用するほうがよいでしょう。

また、今後開発するつもりはありませんmagit-popup。代わりに、ゼロから同様のパッケージを作成します。現在の実装では、偶然にも非常に複雑になっています。(しかし、それはそれmagit-popupが単に消えるという意味ではありません。多くの新機能が表示されない可能性がありますが、現在の実装で期待どおりの結果が得られたら、なぜそれを使用しないのですか?)

または、あなたも「ゼロから」それをやりたいので、見てread-char-choice、そこから行ってください。ビジュアルパーツを実装するにlvは、ヒドラリポジトリの一部であるを見てください。


他の読者の場合:@tarsiusがに代わってその代わりを書いていることに注意してくださいmagit-popup。新しいパッケージはと呼ばれtransient、これがの現在のバージョンで使用されているものですmagit。ドキュメントについては、magit.vc / manual / transientを参照してください。
フィル

5

Hydrasの記述は非常に簡単です。

(defhydra hydra-launcher (:color blue :columns 2)
   "Launch"
   ("h" man "man")
   ("r" (browse-url "http://www.reddit.com/r/emacs/") "reddit")
   ("w" (browse-url "http://www.emacswiki.org/") "emacswiki")
   ("s" shell "shell")
   ("q" nil "cancel"))

(global-set-key (kbd "C-c r") 'hydra-launcher/body)

Hydraはキーボード中心のインターフェースであり、その基本的な形式ではeasy-menu-define(組み込み)よりも難しくありません。そして、もっと複雑なことをさせたい場合、それはかなり拡張可能です。

このツイッターインターフェイスを見てください。下のウィンドウはカスタムHydraであり、上記のものよりも書くのはそれほど難しくありません。

hydra-twittering.png

このためのコードは、より多くの例とともにwikiで入手できます。


すばらしいです。このことについてもっと学ぶ時間を見つけることができます。
Mark Karpov、2015

1

いくつかの調査の後、現在アクティブなウィンドウの下部(または実際にはどこにでも)にウィンドウを作成するために使用できるイディオムを見つけました。これ自体は一時的な補助ウィンドウの効果があります:

(let ((buffer (get-buffer-create "*Name of Buffer*")))
  (with-current-buffer buffer
    (with-current-buffer-window
     ;; buffer or name
     buffer
     ;; action (for `display-buffer')
     (cons 'display-buffer-below-selected
           '((window-height . fit-window-to-buffer)
             (preserve-size . (nil . t))))
     ;; quit-function
     (lambda (window _value)
       (with-selected-window window
         (unwind-protect
             ;; code that gets user input
           (when (window-live-p window)
             (quit-restore-window window 'kill)))))
     ;; Here we generate the popup buffer.
     (setq cursor-type nil)
     ;; …
     )))

画面の別の部分にポップアップを表示する場合は、action引数を指定して少し遊ぶことができwith-current-buffer-windowます。

Quit関数はのdoc-stringに記述されておりwith-current-buffer-window、ユーザーからの入力を取得するために使用できます。@tarsiusが示唆したように、 read-char-choiceは実験に適した候補です。

ポップアップバッファ自体は、他のバッファと同じように生成できます。ユーザーがキーボードだけでなく、マウスを使用してポップアップメニューの項目を選択できるため、私はまだそこにあるボタンについて考えています。ただし、これをうまく実行するには、追加の作業が必要です。

ポップアップが少しアドホックで、一度しか必要ない場合は、このソリューションで回避できます。また、もご覧ください completion--insert-strings。メニュー項目のかなりの行を生成する方法の例として非常に役立つことがわかりました。特別なものが必要ない場合は、そのまま使用することもできます。


4
多分、あなたの頭にある質問は良いと思います。あなたが書き留めた質問はかなり不明確です、私は誰がどのようにあなたがこのような答えの後であなたがそうであったと知っていたかもわかりません。
npostavs 2015

1

Iciclesを使用したサードパーティのソリューションを次に示します。

  • コードにより、候補がメニューとして整然と配置されたウィンドウがポップアップします。方法については、以下を参照してください。

  • 各メニュー項目の前に固有の文字を付けます。たとえば、a、b、c ...または1、2、3 ...ユーザーは、charを押してアイテムを選択します。

  • への呼び出しの周りにいくつかの変数をバインドしcompleting-readます。メニュー項目のリストを渡します。オプションで、ユーザーがを押すだけで選択されるデフォルトの項目の1つを指定しますRET

    変数バインディングは次のことcompleting-readを伝えます。

    • 各メニュー項目を別々の行に表示する
    • メニューをすぐに表示する
    • ユーザーがキーを押したらすぐに更新する
    • ユーザー入力が1つのアイテムのみに一致する場合、すぐに戻る
    • プロンプトにデフォルトの選択肢を表示します。
  • メニュー項目は好きなだけ空けることができます(表示されていません)。

    • 画像
    • 注釈
    • アイテムごとに複数行
    • mouse-3 ポップアップメニュー、アイテムで何でもする
(defvar my-menu '(("a: Alpha It"   . alpha-action)
                  ("b: Beta It"    . beta-action)
                  ("c: Gamma It"   . gamma-action)
                  ("d: Delta It"   . delta-action)
                  ("e: Epsilon It" . epsilon-action)
                  ("f: Zeta It"    . zeta-action)
                  ("g: Eta It"     . eta-action)
                  ("h: Theta It"   . theta-action)
                  ("i: Iota It"    . iota-action)
                  ("j: Kappa It"   . kappa-action)
                  ("k: Lambda It"  . lambda-action)))

(defun my-popup ()
  "Pop up my menu.
User can hit just the first char of a menu item to choose it.
Or s?he can click it with `mouse-1' or `mouse-2' to select it.
Or s?he can hit RET immediately to select the default item."
  (interactive)
  (let* ((icicle-Completions-max-columns               1)
         (icicle-show-Completions-initially-flag       t)
         (icicle-incremental-completion-delay          0.01)
         (icicle-top-level-when-sole-completion-flag   t)
         (icicle-top-level-when-sole-completion-delay  0.01)
         (icicle-default-value                         t)
         (icicle-show-Completions-help-flag            nil)
         (choice  (completing-read "Choose: " my-menu nil t nil nil
                                   "b: Beta It")) ; The default item.
         (action  (cdr (assoc choice my-menu))))

    ;; Here, you would invoke the ACTION: (funcall action).
    ;; This message just shows which ACTION would be invoked.
    (message "ACTION chosen: %S" action) (sit-for 2)))

また、本当に複数の選択肢を持つメニュー、つまりユーザーが同時に複数の項目を選択できるメニュー(可能な選択肢のサブセットを選択する)を作成することもできます。


1

パッケージを見ることにも興味があるかもしれませんmakey。同様の機能を提供するように意図されmagit-popup、それはの前身のフォークであるmagit-popupmagit-key-modeそれは別に、まだ利用可能なパッケージではありませんでしたときからのコメントにmentionned) magit

私も言及させてくださいdiscover:これは使用方法の例ですmakey(そしてその存在理由でもあります)。そのパッケージは、初心者がemacsバインディングを発見するのを助けることを意図しています。


参考文献


2
@Markなぜあなたがこの答えを受け入れたのかはよくわかりません。小さなビルディングブロックについてはまったく触れていません。私が正しく覚えていれば、それはあなたが求めていたものです。また、これは正しくありません。makeyはmagit-popupのフォークではなく、前身のmagit-key-modeのフォークです。それはmagit-popupで対処されてきた赤字のほとんどを継承しています。したがって、あなたはmagit-popupまたはhydraを使用する(または独自に作成する)方がはるかに優れています。
tarsius

@tarsius回答の正しさを判断するのは難しい場合があります。正しくないことがわかっている場合は、自由に編集してください。私は編集を行いましたが、今でもそれが100%正しいかどうかはわかりません。また、それが何であるかわからないので、それが継承する「赤字」については触れませんでした。
YoungFrog、2015


0

@Drewの回答(Icicles)に基づいて、メニューデータからメニュー実装を分離するように変更しました。そのため、複数のメニューを定義でき、それぞれに独自の(コンテンツ、プロンプト、デフォルト)があります。

これは、オプションでivy(使用可能な場合)を使用します。これには、メニューを開いたままアイテムをアクティブ化できるなど、より高度な機能があります。

一般的なポップアップ。

(defun custom-popup (my-prompt default-index my-content)
  "Pop up menu
Takes args: my-prompt, default-index, my-content).
Where the content is any number of (string, function) pairs,
each representing a menu item."
  (cond
    ;; Ivy (optional)
    ((fboundp 'ivy-read)
      (ivy-read my-prompt my-content
        :preselect
        (if (= default-index -1) nil default-index)
        :require-match t
        :action
        (lambda (x)
          (pcase-let ((`(,_text . ,action) x))
            (funcall action)))
        :caller #'custom-popup))
    ;; Fallback to completing read.
    (t
      (let ((choice (completing-read
                     my-prompt my-content nil t nil nil
                     (nth default-index my-content))))
        (pcase-let ((`(,_text . ,action) (assoc choice my-content)))
          (funcall action))))))

使用例、いくつかのアクションをf12キーに割り当てます。

(defvar
  my-global-utility-menu-def
  ;; (content, prompt, default_index)
  '(("Emacs REPL" . ielm)
    ("Spell Check" . ispell-buffer)
    ("Delete Trailing Space In Buffer" . delete-trailing-whitespace)
    ("Emacs Edit Init" . (lambda () (find-file user-init-file))))

(defun my-global-utility-popup ()
  "Pop up my menu. Hit RET immediately to select the default item."
  (interactive)
  (custom-popup my-global-utility-menu-def "Select: ", 1))

(global-set-key (kbd "<f12>") 'my-global-utility-popup)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.