Cara Menerapkan Menu Popup Mirip dengan Yang Digunakan di Magit


10

Pertanyaan

Saya ingin membuat antarmuka pengguna dalam bentuk menu popup , menu popup mirip dengan yang digunakan di Magit .

fitur

Definisi Popup

Munculan dalam konteks pertanyaan ini berarti sedikit jendela sementara yang berisi koleksi item menu sehingga pengguna dapat memilih satu dan hanya satu dari item ini.

Posisi di Layar

Munculan diperbolehkan untuk muncul di bagian mana pun dari layar, tetapi diinginkan bahwa itu harus cukup jelas dan karenanya harus muncul di samping jendela yang sedang aktif.

Isi dari Penyangga Popup

Item harus ditampilkan dalam bentuk tabel cantik. Pretty adalah konteks pertanyaan yang berarti menarik secara visual, efek ini dapat paling mudah dicapai dengan meletakkan item menu ke dalam baris lurus, lihat complete--insert-stringmisalnya. Paragraf ini berfungsi untuk klarifikasi tambahan, Anda dapat melakukannya dengan cara Anda sendiri, ini tidak akan membuat jawaban Anda salah.

Pemilihan Item Menu

Pemilihan ini diharapkan dilakukan dengan menekan satu tombol atau, secara opsional dengan mouse (meskipun tidak begitu penting sehingga jawaban yang berisi proposisi yang tidak mendukung mouse adalah legal). Jika Anda mengusulkan solusi yang mendukung mouse, harap perhatikan bahwa pengguna harus dapat memilih item menu dengan cara yang intuitif, yaitu dengan mengklik tombol kiri pada pilihan yang diinginkan.

NB mouse dapat digunakan dalam banyak cara dan cara-cara alternatif untuk menunjukkan pilihan juga disambut.

Eliminasi Popup

Setelah pengguna memilih item menu dengan cara yang dijelaskan di atas, buffer dan dengan demikian jendelanya harus dihilangkan dari pandangan dan juga dimatikan. Jendela yang telah aktif sebelum doa menu popup harus mendapatkan fokus (yaitu, menjadi aktif) lagi.

Nilai dan Argumen yang Dikembalikan

Lebih disukai, konsekuensi tindakan ini harus menghasilkan objek Lisp dikembalikan. Objek Lisp dapat berupa:

  • nil- ini menunjukkan bahwa pengguna telah membatalkan menu popup dengan menekan C-gatau dengan cara lain †.

  • string- string (diizinkan menggunakan simbol) harus string-equal ke salah satu string yang disediakan ke menu popup sebagai koleksi item yang sebenarnya.

Cara alternatif untuk memberi tahu seluruh program mengetahui pilihan pengguna, atau, mungkin, ketidakhadirannya, dapat diterima. Namun, jika tidak jelas bagaimana hal itu dapat dilakukan, saya meminta semua penjawab berimprovisasi dan tidak meminta saya untuk klarifikasi lebih lanjut tentang aspek ini.

Ini semua untuk nilai yang dikembalikan. Adapun parameter input, mereka harus setidaknya menyertakan kumpulan string yang mewakili pilihan yang mungkin (yaitu, item menu).

Jawaban yang Dapat Diterima

Jawaban yang diharapkan dapat berupa:

  • Cuplikan kode yang memadai yang memungkinkan pembaca yang berpendidikan untuk menulis fungsi seperti yang dijelaskan di atas; tidak diharapkan atau perlu untuk menulis seluruh fungsi kerja. Namun, untuk menghindari ketidakpastian (dapatkah sebagian besar kode dihilangkan?), Saya harus mencatat bahwa bagian potongan yang hilang harus dijelaskan dalam komponen jawaban tekstual.

  • Tautan ke perpustakaan yang ada yang mengimplementasikan fungsi serupa. Untuk menghindari ketidakpastian, saya harus mencatat bahwa serupa dalam kasus kami berarti bahwa perpustakaan dapat digunakan untuk membuat popup (lihat definisi di atas) yang memiliki setidaknya 2 atau 3 fitur yang dijelaskan di atas. Jika perpustakaan yang diusulkan berbeda ke titik di mana kondisi yang dinyatakan sebelumnya tidak dapat dipenuhi, masing-masing kasus tersebut akan dinilai secara independen dan akan selalu dicabut jika OP menganggapnya berguna.

  • Deskripsi fungsi Emacs bawaan atau yang pihak ketiga yang dapat digunakan untuk mengimplementasikan fitur apa pun yang dijelaskan di bagian «Fitur», lihat di atas. Untuk ketidakpastian menghindari, silakan negara jelas bagaimana jawaban Anda dapat bermanfaat bagi pembaca masa depan yang ingin menerapkan popup , popup menu mirip dengan yang digunakan di Magit .


† Cara-cara alternatif untuk membatalkan menu popup mungkin termasuk yang berikut (tetapi tidak terbatas pada ini):

  • mengklik di luar jendela menu sembulan;

  • membunuh buffer yang berisi popup tanpa membuat pilihan.

Jawaban:


8

magit-popupmemiliki manual sendiri ! Tetapi kecuali jika Anda benar-benar ingin mengatur argumen dalam popup yang kemudian diteruskan ke tindakan yang dipanggil, Anda mungkin lebih baik menggunakan hydra.

Perhatikan juga bahwa saya tidak bermaksud untuk berkembang lebih jauh magit-popup. Sebaliknya saya akan menulis paket serupa dari awal. Ada banyak kerumitan yang tidak disengaja dalam implementasi saat ini. (Tapi itu tidak berarti itu magit-popupakan hilang begitu saja. Sepertinya tidak akan melihat banyak fitur baru, tetapi jika implementasi saat ini melakukan apa yang Anda inginkan, lalu mengapa tidak menggunakannya?)

Atau karena Anda juga ingin melakukannya "dari awal", lihat read-char-choicedan pergi dari sana. Untuk mengimplementasikan bagian visual, lihat di lvmana merupakan bagian dari repositori hydra.


Untuk pembaca lain: Perhatikan bahwa @tarsius telah mengikuti dan menulis pengganti untuk magit-popup. Paket baru dipanggil transient, dan inilah yang digunakan dalam versi saat ini magit. Lihat magit.vc/manual/transient untuk dokumentasi.
phils

5

Hydra cukup sederhana untuk ditulis:

(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 adalah antarmuka keyboard-centric, dan dalam bentuk dasarnya, itu tidak lebih sulit daripada easy-menu-define(bawaan). Dan itu cukup dapat diperluas jika Anda ingin membuatnya melakukan hal-hal yang lebih kompleks.

Lihat saja antarmuka twitter ini, jendela bagian bawah adalah Hydra khusus, tidak jauh lebih sulit untuk ditulis daripada yang di atas:

hydra-twittering.png

Kode untuk ini tersedia di wiki , bersama dengan banyak contoh lainnya.


Hebat, saya pasti akan menemukan waktu untuk mempelajari lebih lanjut tentang hal ini!
Mark Karpov

1

Setelah beberapa penelitian saya menemukan idiom yang dapat digunakan untuk membuat jendela di bagian bawah (atau memang di mana saja) dari jendela yang sedang aktif. Ini sendiri memiliki efek jendela tambahan sementara:

(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)
     ;; …
     )))

Anda dapat bermain sedikit dengan actionargumen with-current-buffer-windowjika Anda ingin popup muncul di bagian layar yang berbeda.

Fungsi berhenti dijelaskan dalam doc-string with-current-buffer-window, ini dapat digunakan untuk mendapatkan input dari pengguna. Seperti yang disarankan @tarsius, read-char-choiceadalah kandidat yang baik untuk bereksperimen.

Buffer popup itu sendiri dapat dibuat seperti buffer lainnya. Saya masih memikirkan tombol di sana, karena pengguna dapat menggunakan mouse-nya untuk memilih item di menu popup, bukan hanya keyboard. Namun, ini membutuhkan upaya tambahan jika Anda ingin melakukannya dengan baik.

Jika popup Anda sedikit ad-hoc dan Anda tidak akan membutuhkannya lebih dari sekali, Anda bisa lolos dengan solusi ini. Juga, lihatlah completion--insert-strings. Saya menemukan ini cukup berguna sebagai contoh cara menghasilkan cukup banyak item menu, Anda bahkan dapat menggunakannya tanpa diubah jika Anda tidak memerlukan sesuatu yang istimewa.


4
Saya pikir mungkin pertanyaan yang ada di kepala Anda bagus; pertanyaan yang Anda tulis agak tidak jelas, saya tidak melihat bagaimana orang bisa tahu Anda setelah jawaban seperti ini.
npostavs

1

Berikut adalah solusi pihak ke-3, menggunakan Es .

  • Kode Anda muncul jendela dengan kandidat diatur dengan rapi sebagai menu. Untuk caranya, lihat di bawah.

  • Anda mengawali setiap item menu dengan karakter unik. Misalnya, a, b, c ... atau 1, 2, 3 ... Pengguna menekan char untuk memilih item.

  • Anda mengikat beberapa variabel di sekitar panggilan ke completing-read. Anda memberikan daftar item menu Anda. Anda secara opsional menentukan salah satu item sebagai default, dipilih jika pengguna hanya mengklik RET.

    Binding variabel kirim completing-readke:

    • Tampilkan setiap item menu pada baris terpisah
    • Tampilkan menu segera
    • Perbarui segera ketika pengguna menekan kunci
    • Segera kembali, jika input pengguna hanya cocok dengan satu item
    • Tampilkan pilihan default di prompt.
  • Anda dapat membuat item menu semewah yang Anda inginkan (tidak ditampilkan).

    • wajah
    • gambar-gambar
    • penjelasan
    • beberapa baris per item
    • mouse-3 menu popup, untuk melakukan apa saja dengan item tersebut
(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)))

Anda juga dapat membuat menu pilihan ganda , yaitu menu tempat pengguna dapat memilih beberapa item sekaligus (pilih subset dari pilihan yang memungkinkan).


1

Anda mungkin juga tertarik melihat paket itu makey. Ini dimaksudkan untuk menyediakan fungsionalitas yang sama seperti magit-popup, dan itu adalah garpu dari pendahulu magit-popup( magit-key-mode, disebutkan dalam komentar) sejak saat itu belum paket tersedia secara terpisah magit.

Izinkan saya juga menyebutkan discover: itu adalah contoh cara menggunakan makey(dan juga raison d'être). Paket itu dimaksudkan untuk membantu pendatang baru menemukan binding emacs.


Referensi


2
@ Mark saya tidak yakin mengapa Anda menerima jawaban ini. Sama sekali tidak menyebutkan blok bangunan kecil, yang, jika saya ingat dengan benar, adalah apa yang Anda cari. Itu juga tidak benar: makey bukan fork-popup, itu fork-fork-key-mode pendahulunya. Itu mewarisi sebagian besar defisit yang sejak itu ditangani di magit-popup. Jadi, Anda jauh lebih baik menggunakan magit-popup atau hydra (atau menulis sendiri).
tarsius

@tarsius Menilai kebenaran jawaban bisa sulit. Jika Anda tahu itu tidak benar, silakan mengeditnya. Saya melakukan edit, saya masih belum 100% yakin itu benar sekarang. Saya juga tidak menyebutkan "defisit" yang diwarisi karena saya tidak tahu apa itu.
YoungFrog


0

Berdasarkan jawaban @ Drew (Icicles), dengan modifikasi untuk memisahkan implementasi menu dari data menu - sehingga beberapa menu dapat ditentukan, masing-masing dengan milik mereka sendiri: (konten, prompt, default).

Ini secara opsional menggunakan ivy (bila tersedia), yang memiliki fitur yang lebih canggih seperti dapat mengaktifkan item sambil menjaga menu tetap terbuka.

Popup umum.

(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))))))

Contoh gunakan, tetapkan beberapa tindakan ke tombol 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)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.