Magit'te Kullanılana Benzer Açılır Menü Nasıl Uygulanır?


10

Soru

Magit'te kullanılana benzer açılır menü , açılır menü şeklinde kullanıcı arayüzü oluşturmak istiyorum .

Özellikleri

Popup'un tanımı

Bu soru bağlamında açılır pencere, menü öğelerinin koleksiyonunu içeren çok az geçici pencere anlamına gelir, böylece kullanıcı bu öğelerden birini ve yalnızca birini seçebilir.

Ekrandaki Konumu

Pop-up'ın ekranın herhangi bir bölümünde görünmesine izin verilir, ancak oldukça açık olması ve bu nedenle o anda etkin pencerenin yanında görünmesi istenir.

Açılır Arabellek İçeriği

Öğeler güzel tablo şeklinde gösterilmelidir. Oldukça görsel olarak çekici soru araçlarının bağlamdır, bu etki en kolay düz sıralar halinde menü öğelerini koyarak elde edilebilir, bakınız complete--insert-stringörneğin. Bu paragraf ek açıklama sağlar, kendi yolunuzla yapabilirsiniz, bu cevabınızı yanlış yapmaz.

Menü Öğesinin Seçimi

Seçimin tek bir tuşa basarak veya isteğe bağlı olarak bir fare ile yapılması beklenir (bu kadar önemli olmasa da, fareyi desteklemeyen önermeleri içeren cevaplar yasaldır). Fareyi destekleyen bir çözüm öneriyorsanız, kullanıcının sezgisel bir şekilde bir menü öğesini seçebilmesi gerektiğini, yani istenen seçeneğe sol tıklayarak tıklayabileceğini unutmayın.

NB fare birçok şekilde kullanılabilir ve bir seçeneği belirtmek için alternatif yollar da memnuniyetle karşılanmaktadır.

Popupun Ortadan Kaldırılması

Kullanıcı yukarıda açıklanan şekilde bir menü öğesi seçtikten sonra, tampon ve dolayısıyla penceresi görünümden çıkarılmalı ve öldürülmelidir. Açılır menünün çağrılmasından önce aktif olan pencere tekrar odaklanmalı (yani aktif hale gelmelidir).

Döndürülen Değer ve Bağımsız Değişkenler

Tercihen, eylemlerin bu sonucu döndürülen bir Lisp nesnesiyle sonuçlanmalıdır. Lisp nesnesi şunlardan biri olabilir:

  • nil- Bu, kullanıcının pop-up menüsünü tuşuna basarak C-gveya başka bir şekilde † iptal ettiğini gösterir .

  • string- string (bir sembol kullanılmasına izin verilir) string-equal , gerçek öğelerin toplanması olarak açılır menüye verilen dizelerden biri olmalıdır .

Programın geri kalanının kullanıcının seçimini veya muhtemelen yokluğunu bilmesini sağlamanın alternatif yolları kabul edilebilir. Ancak, başka nasıl yapılabileceği açık değilse, tüm cevaplayanlardan doğaçlama yapmasını ve bu yönün daha fazla açıklanmasını istemiyorum.

Tüm bunlar döndürülen değer içindir. Giriş parametrelerine gelince, en azından olası seçenekleri temsil eden dizeleri (yani menü öğelerini) toplamalıdırlar.

Kabul Edilebilir Yanıtlar

Beklenen cevap aşağıdaki şekillerde olabilir:

  • Eğitimli okuyucunun yukarıda anlatıldığı gibi bir işlev yazmasını sağlayan yeterli kod snippet'i; tüm çalışma fonksiyonunun yazılması beklenmez veya gerekli değildir. Ancak, belirsizliği önlemek için (kodun önemli bölümleri atlanabilir mi?), Snippet'in eksik bölümlerinin cevabın metinsel bileşeninde tanımlanması gerektiğini belirtmeliyim.

  • Benzer işlevselliği uygulayan mevcut kitaplığa bir bağlantı. Belirsizliği önlemek için, bizim durumumuzda benzer olanın, kütüphanenin yukarıda açıklanan en az 2 veya 3 özelliğe sahip açılır pencere (yukarıdaki tanıma bakın) oluşturmak için kullanılabileceği anlamına geldiğini belirtmeliyim . Önerilen kütüphane, daha önce belirtilen koşulun karşılanamayacağı noktadan farklıysa, her bir vaka bağımsız olarak değerlendirilecek ve OP'nin yararlı bulması halinde her zaman kaldırılacaktır.

  • «Özellikler» bölümünde açıklanan herhangi bir özelliği uygulamak için kullanılabilen yerleşik Emacs işlevlerinin veya üçüncü taraf işlevlerin açıklaması, yukarıya bakın. Belirsizliği önlemek için, lütfen yanıtınızın Magit'te kullanılana benzer pop- up , pop-up menüsü uygulamak isteyen okuyucular için nasıl yararlı olabileceğini açıkça belirtin .


† Açılır menüyü iptal etmenin alternatif yolları aşağıdakileri içerebilir (ancak bunlarla sınırlı değildir):

  • açılır menü penceresinin dışına tıklamak;

  • seçim yapmadan pop-up içeren tamponun öldürülmesi.

Yanıtlar:


8

magit-popup's var kendi kitabını ! Ancak aslında açılır pencerede çağrılan eyleme geçirilen bağımsız değişkenler ayarlamak istemiyorsanız, hydrabunun yerine muhtemelen daha iyi durumda olursunuz .

Ayrıca daha fazla geliştirmek istemediğimi de unutmayın magit-popup. Bunun yerine sıfırdan benzer bir paket yazacağım. Mevcut uygulamada çok fazla tesadüfi karmaşıklık var. (Ancak bu magit-popupsadece ortadan kaybolacağı anlamına gelmez . Muhtemelen pek çok yeni özellik görmeyecektir, ancak mevcut uygulama istediğinizi yaparsa neden kullanmıyorsunuz?)

Ya da siz de "sıfırdan" yapmak istediğiniz için, bir göz atın read-char-choiceve oradan gidin. Görsel kısmı uygulamak için lvhidra deposunun bir parçası olan bir göz atın .


Diğer okuyucular için: @tarsius'un bunun yerine geçtiğini ve yazdığını unutmayın magit-popup. Yeni paket denir transientve şu anki sürümlerinde kullanılan budur magit. Belgeler için magit.vc/manual/transient adresine bakın .
phils

6

Hidraların yazılması oldukça basittir:

(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 klavye merkezli bir arayüzdür ve temel formunda easy-menu-define(yerleşik) olmaktan daha zor değildir . Ve daha karmaşık şeyler yapmak istiyorsanız oldukça genişletilebilir.

Sadece bu twittering arayüzüne bakın, alt pencere özel bir Hydra'dır, yukarıdakinden çok daha zor değil:

hydra-twittering.png

Bunun kodu , çok daha fazla örnekle birlikte wiki'de mevcuttur .


Harika, kesinlikle bu şey hakkında daha fazla bilgi edinmek için zaman bulacağım!
Mark Karpov

1

Bazı araştırmalardan sonra, o anda etkin olan pencerenin altında (veya aslında herhangi bir yerde) bir pencere oluşturmak için kullanılabilecek bir deyim buldum. Bunun kendisinin geçici, yardımcı pencere etkisi vardır:

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

Açılır pencerenin ekranın farklı bölümlerinde görünmesini istiyorsanız, actionargümanla biraz oynayabilirsiniz with-current-buffer-window.

Quit işlevi doc-string içinde tanımlanmıştır with-current-buffer-window, kullanıcıdan girdi almak için kullanılabilir. @Tarsius'un önerdiği gibi, read-char-choicedeney için iyi bir adaydır.

Pop-up tamponunun kendisi de diğer tamponlar gibi üretilebilir. Ben hala düğmeleri düşünüyorum, çünkü kullanıcı sadece klavye değil, pop-up menüsünde bir öğe seçmek için faresini kullanabilirsiniz. Ancak, bunu iyi yapmak istiyorsanız ek çaba gerektirir.

Açılır pencereniz biraz geçici ise ve bir kereden fazla ihtiyacınız yoksa, bu çözümden kurtulabilirsiniz. Ayrıca, bir göz atın completion--insert-strings. Menü öğelerinin güzel satırlarının nasıl oluşturulacağının bir örneği olarak oldukça yararlı buldum, özel bir şeye ihtiyacınız yoksa bunu değiştirmeden kullanabilirsiniz.


4
Bence belki de kafanızdaki soru iyidir; yazdığınız soru oldukça belirsiz, böyle bir cevaptan sonra kimsenin sizi nasıl tanıyabileceğini görmüyorum.
npostavs

1

İşte buz sarkıtları kullanan bir üçüncü taraf çözümü .

  • Kodunuz, adayların bir menü olarak düzenlenmiş olduğu bir pencere açar. Nasıl yapılacağı için aşağıya bakın.

  • Her menü öğesine benzersiz bir karakter ekler. Örneğin, a, b, c ... veya 1, 2, 3 ... Kullanıcı öğeyi seçmek için karaktere vurur.

  • Çağrısının etrafında birkaç değişken bağlarsınız completing-read. Menü öğelerinizin bir listesini iletirsiniz. İsteğe bağlı olarak kullanıcı isabet ederse seçilen öğelerden birini varsayılan olarak belirtirsiniz RET.

    Değişken bağlamalar completing-readşunları söyler :

    • Her menü öğesini ayrı bir satırda göster
    • Menüyü hemen göster
    • Bir kullanıcı bir tuşa bastığında hemen güncelleyin
    • Kullanıcı girişi yalnızca bir öğeyle eşleşirse hemen geri dönün
    • Komut isteminde varsayılan seçimi gösterin.
  • Menü öğelerini istediğiniz kadar süslü yapabilirsiniz (gösterilmemiştir).

    • yüzleri
    • Görüntüler
    • ek açıklamalar
    • öğe başına birden fazla satır
    • mouse-3 açılır menü, öğeyle ilgili her şeyi yapma
(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)))

Ayrıca gerçekten çoktan seçmeli menüler , yani bir kullanıcının aynı anda birden fazla öğe seçebileceği menüler de oluşturabilirsiniz (olası seçeneklerin bir alt kümesini seçin).


1

Pakete bakmak da ilginizi çekebilir makey. Aynı işlevsellik sağlamaya yöneliktir magit-popupve henüz ayrı olarak temin edilebilen bir paket olmadığı zamandan öncekinin magit-popup( magit-key-modeyorumda belirtildiği gibi) bir çatalıdır magit.

Şunu da söyleyeyim discover: bu nasıl kullanılacağına bir örnek makey(ve ayrıca varoluş nedeni). Bu paket yeni gelenlerin emacs bağlarını keşfetmesine yardımcı olmak içindir.


Referanslar


2
@ Mark Bu cevabı neden kabul ettiğinden emin değilim. Doğru hatırlamıyorsam, peşinde olduğun şey olan küçük yapı taşlarından hiç bahsetmiyor. Ayrıca doğru değil: makey magit-popup çatalı değil, selefi magit-key modunun bir çatalı. O zamandan beri magit-popup'da ele alınan açıkların çoğunu devralır. Yani magit-popup veya hydra kullanmak (veya kendi yazmak) çok daha iyi.
tarsius

@tarsius Bir cevabın doğruluğunu değerlendirmek zor olabilir. Doğru olmadığını biliyorsanız, düzenlemekten çekinmeyin. Bir düzenleme yaptım, hala doğru olduğundan% 100 emin değilim. Ayrıca miras aldığı "açıklardan" bahsetmedim çünkü ne olduklarını bilmiyorum.
YoungFrog


0

@ Drew'un cevabına (buz sarkıtları) dayanarak, menü uygulamasını menü verilerinden ayırmak için değişiklikler yapıldı - böylece her birinin kendine ait birden fazla menü tanımlanabilir: (içerik, bilgi istemi, varsayılan).

Bu isteğe bağlı olarak, menüyü açık tutarken öğeleri etkinleştirmek gibi daha gelişmiş özelliklere sahip sarmaşık (varsa) kullanır.

Genel açılır pencere.

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

Örnek kullanım, f12 tuşuna bazı eylemler atayın:

(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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.