Mirzhan Irkegulov
Mirzhan Irkegulov

Reputation: 18075

Dynamically show/hide menu bar in Emacs

I have menu-bar-open bound on f11 and menu-bar turned off, and because of that, f11 calls tmm-menubar, which is inconvenient and doesn't have mode-specific menu items for some reason (like org and tbl in org-mode). I want it to behave this way: make menu-bar visible, enable user to choose menu item, after that make menu-bar invisible again.

What is the most idiomatic and elegant way to to that?

I thought on writing advices, but Emacs developers usually recommend against it, as it causes problems for debug, and standard Emacs code does not include advices.

I use Emacs 24.1 in GUI.

Upvotes: 5

Views: 4661

Answers (5)

anarcat
anarcat

Reputation: 6197

I have just stumbled upon this myself and found that the Emacs defaults are actually fine by me.

By default, f10 is bound to menu-bar-open and when I turn off the menu bar, it conveniently pops up a context menu. It doesn't show the actual menu bar on top, mind you, but I find this less disruptive because it doesn't "shift" the entire window around.

This doesn't work in terminal mode (emacs -nw) for some reason. There, like you describe, when I type f10 I get the uglier tmm-menubar and no amount of meddling with tty-menu-open-use-tmm fixes that, regardless of what the menu bar documentation says. But that's in terminal mode, and you say you're in GUI mode.

Here's a screenshot of what I see in GUI mode (in the back) and TTY mode (in front):

screenshot of two emacs, one in Pure GTK mode and the other in TTY, showing menus

So I'm not sure what's going on for you, but I've found that just erasing some of my customization helped a lot in this case, as is often the case with my aging Emacs configuration with a moving upstream... I also understand my answer is not exactly what you want

I'm using emacs-pgtk on Debian bookworm with the Emacs 29 backport.

Upvotes: 0

Flux
Flux

Reputation: 10950

I have tested this in GNU Emacs 25.2 and 26.3:

(menu-bar-mode -1)

(advice-add 'menu-bar-open
            :around
            (lambda (orig-fun &rest args)
              (menu-bar-mode 1)
              (apply orig-fun args)
              (menu-bar-mode -1)))

Resulting behaviour (assuming that menu-bar-open is bound to F10, which is the default):

  • The menu bar is not shown by default.
  • If you press F10, the menu bar will be shown.
  • Once you leave the menu bar, the menu bar will be gone until the next time you press F10.

Note that this is more like a hack than a proper solution.

Upvotes: 0

Richard Gomes
Richard Gomes

Reputation: 6114

In Emacs-24 you can simply do this:

(global-set-key [f9] 'toggle-menu-bar-mode-from-frame)
  • Not sure about versions of Emacs older than 24.
  • Just be sure that f9 is really available in your installation.

Upvotes: 9

Lluís
Lluís

Reputation: 21

A small improvement to Greg's answer, which keeps pre-command-hook clean:

(menu-bar-mode -1)

(defun my-menu-bar-open-after ()
  (remove-hook 'pre-command-hook 'my-menu-bar-open-after)
  (when (eq menu-bar-mode 42)
    (menu-bar-mode -1)))

(defun my-menu-bar-open (&rest args)
  (interactive)
  (let ((open menu-bar-mode))
    (unless open
      (menu-bar-mode 1))
    (funcall 'menu-bar-open args)
    (unless open
      (setq menu-bar-mode 42)
      (add-hook 'pre-command-hook 'my-menu-bar-open-after))))

(global-set-key [f10] 'my-menu-bar-open)

Upvotes: 2

Greg E.
Greg E.

Reputation: 2742

If you're running a graphical Emacs session with menu-bar-mode disabled, then C-<mouse-3> should bring up the entire contents of the menu as a popup dialogue box. If you're running Emacs in a terminal, however, this definitely won't work; you haven't specified which is the case, so I'll try not to make assumptions. It's also possible to create custom mouse bindings (optionally, with keyboard modifiers) to the mouse-popup-menubar and/or mouse-popup-menubar-stuff functions, but ultimately that would only enable you to replicate behavior similar to the standard functionality that I've described above.

Due to the somewhat inflexible and global nature of menu-bar-mode (i.e., the fact that it applies across all Emacs frames and provides for relatively little customization via hooks, etc.), I think it would be very difficult to achieve precisely the behavior you desire with vanilla Emacs. It might be possible to write a custom function to temporarily enable menu-bar-mode and then use something like post-command-hook to disable it again after a selection is made, but I'm not certain. I'll try to investigate further if time allows.

Also, you might wish to look into third-party menu-bar packages, (q.v., the Menu Bar section of EmacsWiki).

Edit: I've hacked together a rather kludgy solution that you may find useful...

(add-hook
 'pre-command-hook
 (lambda ()
   (when (eq menu-bar-mode 42)
     (menu-bar-mode -1))))

(defun my-menu-bar-open ()
  (interactive)
  (unless menu-bar-mode
    (menu-bar-mode 1))
  (menu-bar-open)
  (setq menu-bar-mode 42))

I've tested this in a graphical session and it appears to simulate the behavior that you wanted, as long as you don't perform any action that Emacs registers as a command between executing my-menu-bar-open and making your selection (which is basically anything other than navigating the menu itself). The choice of 42 is a magic number (and a Douglas Adams homage) intended to minimize the risk that the hook function would be activated for more typical values of the menu-bar-mode variable. I don't claim that this is in any way elegant, but, in its decidedly ugly way, it does work. If you decide to use this, simply bind my-menu-bar-open to f11 (or whatever you prefer), i.e.:

(global-set-key [f11] 'my-menu-bar-open)

Alternatively, you can probably achieve very similar functionality by using pre-command-hook in an analogous fashion and instead advising menu-bar-open to perform a temporary toggle of menu-bar-mode.

Upvotes: 4

Related Questions