Loading ...
Sorry, an error occurred while loading the content.
 

request for comment on how to implement menus

Expand Messages
  • björn
    I am facing a problem with implementing menus in MacVim and would appreciate comments on how to implement them. Partly I bring it up because I will need to
    Message 1 of 6 , Jul 27, 2007
      I am facing a problem with implementing menus in MacVim and would appreciate comments on how to implement them.  Partly I bring it up because I will need to modify Vim to implement these features, and so far I have tried to avoid having to change any code in Vim.  (I've only added some #ifdefs to enable certain features.)  I will try to outline how the menus work in the Carbon port, and then suggest how they should work in MacVim.


      First of all -- key equivalents (i.e. Cmd+key keyboard shortcuts):

      Here's how the Carbon port does it (as far as I can work out): it provides a .gvimrc file which modifies the accelerator text of some standard menu items to some key combo, then in another file ( macmap.vim) it maps these key combos to something (i.e. if you change the text in the menu, it won't actually change the mapping and vice versa).  Then in gui_mac.c it looks at the accelerator text and if it looks like a key combo, e.g. <D-o>, then it uses this to make sure the key equivalent is displayed on the menu item (all other accelerator texts are not displayed).

      I dislike the idea of modifying the menus to change the accelerator text, so my thought was to make a new command in vim which lets you set the key equivalent for a menu item.  This way, the actext can be preserved and since key equivalents are bound to an actual menu item, the key equivalent displayed on the menu item ( e.g. Cmd-O) really executes that same menu item.  Thus both shortcomings of the Carbon menus are circumvented, but it does require adding a new command.  I can't think of any particular objections to this approach...any comments?  Oh, except I would need to add one or two entries 'struct VimMenu' which holds the key equivalent.


      Second -- binding to action messages:

      In Cocoa, each NSMenuItem is "bound" to an action message, i.e. a Objective-C message with void return type and which takes an 'id' (object pointer) as its only argument.  At the moment MacVim binds every menu item to vimMenuAction: (in MMWindowController) and this simply passes the 'tag' (a vimmenu_T pointer) to MMBackend which in turn calls gui_menu_cb().  However, it would be a great deal more flexible if it was possible to create menus in Vim which sent an arbitrary action message when executed.

      For instance, say I want to have a "New Window" menu item which calls the action message newVimWindow: (which starts up a new Vim task).  (There currently _is_ such an item on the window menu, but that is just one big hack, it has to change.)  At the moment this is completely impossible...Vim has no concept (nor should it) of MacVim's "windows" (or frames, the terminology clash here is unfortunate).  Were it possible to bind to an action, it would, and Vim would be none the wiser.  The only problem I see is if somebody use ex_menu to execute a menu item bound to an action...some kind of logic in Vim which deals with this would be necessary.

      Here is how I thought this could be implemented:
      The command to use is still 'menu', but instead of an accelerator text you specify an action name (always the name of the message, terminated with a colon), and you would be forced to bind it to a command, called e.g. menu_action. For instance
        :menu File.New\ Window<Tab>newVimWindow: :menu_action
      I would implement the new command "menu_action" in Vim, which would call something like gui_macvim_menu_action() to deal with it.  Also, when a new menu item is added with gui_mch_add_menu_item() MacVim can scan the actext to check if it is an action (a name terminated with colon) then binds the menu item to the appropriate action.


      Lets see if anybody has any objections to this approach, or if maybe somebody else have thought about how to deal with menus.  Otherwise I guess I'll just go ahead with what I outlined above.


      /Björn

      --~--~---------~--~----~------------~-------~--~----~
      You received this message from the "vim_mac" maillist.
      For more information, visit http://www.vim.org/maillist.php
      -~----------~----~----~----~------~----~------~--~---

    • Bram Moolenaar
      ... I m not quite sure if I understand this. Keep in mind that in Vim menus work just like mappings. They are a GUI way to get a command executed. When you
      Message 2 of 6 , Jul 27, 2007
        Bjorn Winckler wrote:

        > I am facing a problem with implementing menus in MacVim and would appreciate
        > comments on how to implement them. Partly I bring it up because I will need
        > to modify Vim to implement these features, and so far I have tried to avoid
        > having to change any code in Vim. (I've only added some #ifdefs to enable
        > certain features.) I will try to outline how the menus work in the Carbon
        > port, and then suggest how they should work in MacVim.
        >
        >
        > First of all -- key equivalents (i.e. Cmd+key keyboard shortcuts):
        >
        > Here's how the Carbon port does it (as far as I can work out): it provides a
        > .gvimrc file which modifies the accelerator text of some standard menu items
        > to some key combo, then in another file (macmap.vim) it maps these key
        > combos to something (i.e. if you change the text in the menu, it won't
        > actually change the mapping and vice versa). Then in gui_mac.c it looks at
        > the accelerator text and if it looks like a key combo, e.g. <D-o>, then it
        > uses this to make sure the key equivalent is displayed on the menu item (all
        > other accelerator texts are not displayed).
        >
        > I dislike the idea of modifying the menus to change the accelerator text, so
        > my thought was to make a new command in vim which lets you set the key
        > equivalent for a menu item. This way, the actext can be preserved and since
        > key equivalents are bound to an actual menu item, the key equivalent
        > displayed on the menu item (e.g. Cmd-O) really executes that same menu
        > item. Thus both shortcomings of the Carbon menus are circumvented, but it
        > does require adding a new command. I can't think of any particular
        > objections to this approach...any comments? Oh, except I would need to add
        > one or two entries 'struct VimMenu' which holds the key equivalent.

        I'm not quite sure if I understand this. Keep in mind that in Vim menus
        work just like mappings. They are a GUI way to get a command executed.

        When you define a menu, there might as well be a mapping for a key
        sequence that does the same thing. But they are not really bound to
        each other, there can be subtle differences (so long as the user expects
        this).

        On the other hand, we also have menu accelerators. These actually open
        a menu and allow you to select one of the entries. This may be done
        partly outside of Vim, mostly with Alt-key combinations. These are
        directly related to how the GUI works and have nothing to do with key
        mappings. We do have a way to add accelerator keys in the menu text.

        Now, what you show in the text of the menus (at the righthand side) is
        yet another thing. Of course it must make sense, but usually you need
        to update the text if you change a mapping that does the same action.
        Often it's not the whole story, e.g. displaying ":e" while the action
        actually is ":browse confirm e".

        I don't know what you mean with "accelerator text". Is this something
        that actually opens a menu, or is it more like a mapping? In the first
        case it's similar to what happens in the GUI, thus must be somehow done
        in a ":menu" command. In the second case I would not want to change
        commands, just edit the text and add a mapping separately.


        > Second -- binding to action messages:
        >
        > In Cocoa, each NSMenuItem is "bound" to an action message, i.e. a
        > Objective-C message with void return type and which takes an 'id' (object
        > pointer) as its only argument. At the moment MacVim binds every menu item
        > to vimMenuAction: (in MMWindowController) and this simply passes the 'tag'
        > (a vimmenu_T pointer) to MMBackend which in turn calls gui_menu_cb().
        > However, it would be a great deal more flexible if it was possible to create
        > menus in Vim which sent an arbitrary action message when executed.
        >
        > For instance, say I want to have a "New Window" menu item which calls the
        > action message newVimWindow: (which starts up a new Vim task). (There
        > currently _is_ such an item on the window menu, but that is just one big
        > hack, it has to change.) At the moment this is completely impossible...Vim
        > has no concept (nor should it) of MacVim's "windows" (or frames, the
        > terminology clash here is unfortunate). Were it possible to bind to an
        > action, it would, and Vim would be none the wiser. The only problem I see
        > is if somebody use ex_menu to execute a menu item bound to an action...some
        > kind of logic in Vim which deals with this would be necessary.

        The principle is that everything you can do with a menu must also be
        possible with a Vim command. I would not want to add functionality that
        is _only_ available in a menu item.

        Thus if you miss some functionality I would rather add a Vim command for
        it and invoke it from the menu with that command. That obviously also
        avoids the problem of executing a menu item with ":emenu".


        --
        hundred-and-one symptoms of being an internet addict:
        42. Your virtual girlfriend finds a new net sweetheart with a larger bandwidth.

        /// Bram Moolenaar -- Bram@... -- http://www.Moolenaar.net \\\
        /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
        \\\ download, build and distribute -- http://www.A-A-P.org ///
        \\\ help me help AIDS victims -- http://ICCF-Holland.org ///

        --~--~---------~--~----~------------~-------~--~----~
        You received this message from the "vim_mac" maillist.
        For more information, visit http://www.vim.org/maillist.php
        -~----------~----~----~----~------~----~------~--~---
      • Jjgod Jiang
        Hi,2007/7/28, björn : First of all -- key equivalents (i.e. Cmd+key keyboard shortcuts): Here s how the Carbon port does it
        Message 3 of 6 , Jul 27, 2007
          Hi,

          2007/7/28, björn <bjorn.winckler@...>:
          > First of all -- key equivalents (i.e. Cmd+key keyboard shortcuts):
          >
          > Here's how the Carbon port does it (as far as I can work out): it provides a
          > .gvimrc file which modifies the accelerator text of some standard menu items
          > to some key combo, then in another file ( macmap.vim) it maps these key
          > combos to something (i.e. if you change the text in the menu, it won't
          > actually change the mapping and vice versa). Then in gui_mac.c it looks at
          > the accelerator text and if it looks like a key combo, e.g. <D-o>, then it
          > uses this to make sure the key equivalent is displayed on the menu item (all
          > other accelerator texts are not displayed).
          >
          > I dislike the idea of modifying the menus to change the accelerator text, so
          > my thought was to make a new command in vim which lets you set the key
          > equivalent for a menu item. This way, the actext can be preserved and since
          > key equivalents are bound to an actual menu item, the key equivalent
          > displayed on the menu item ( e.g. Cmd-O) really executes that same menu
          > item. Thus both shortcomings of the Carbon menus are circumvented, but it
          > does require adding a new command. I can't think of any particular
          > objections to this approach...any comments? Oh, except I would need to add
          > one or two entries 'struct VimMenu' which holds the key equivalent.

          I personally see the key limitation of Mac OS X menu handling is, you can't
          display arbitrary text on the right side of a menu item. Only key combinations
          like ⌘N, ⌘O, ⇧⌘H can be displayed there, thus gvim for mac have to break
          the tradition on other platforms -- display the actual commands executed when
          that menu item is clicked on the right side of it.

          IMHO, though allow the user to specify a key equivalent like ⌘O will make
          gvim more "mac like", since almost all users of vim are already familiar with
          vim commands, creating new key equivalents will confuse them. So I think
          the current behavior -- display nothing on the right side of a menu item, while
          using actext as tooltip -- is OK.

          - Jiang

          --~--~---------~--~----~------------~-------~--~----~
          You received this message from the "vim_mac" maillist.
          For more information, visit http://www.vim.org/maillist.php
          -~----------~----~----~----~------~----~------~--~---
        • björn
          ... I think I was a bit unclear in my previous post, so in order to understand the problem I will say something about the menu handling in Cocoa. Hopefully
          Message 4 of 6 , Jul 28, 2007
            > I am facing a problem with implementing menus in MacVim and would appreciate
            > comments on how to implement them.  Partly I bring it up because I will need
            > to modify Vim to implement these features, and so far I have tried to avoid
            > having to change any code in Vim.  (I've only added some #ifdefs to enable
            > certain features.)  I will try to outline how the menus work in the Carbon
            > port, and then suggest how they should work in MacVim.
            >
            >
            > First of all -- key equivalents (i.e. Cmd+key keyboard shortcuts):
            >
            > Here's how the Carbon port does it (as far as I can work out): it provides a
            > .gvimrc file which modifies the accelerator text of some standard menu items
            > to some key combo, then in another file (macmap.vim) it maps these key
            > combos to something (i.e. if you change the text in the menu, it won't
            > actually change the mapping and vice versa).  Then in gui_mac.c it looks at
            > the accelerator text and if it looks like a key combo, e.g. <D-o>, then it
            > uses this to make sure the key equivalent is displayed on the menu item (all
            > other accelerator texts are not displayed).
            >
            > I dislike the idea of modifying the menus to change the accelerator text, so
            > my thought was to make a new command in vim which lets you set the key
            > equivalent for a menu item.  This way, the actext can be preserved and since
            > key equivalents are bound to an actual menu item, the key equivalent
            > displayed on the menu item (e.g. Cmd-O) really executes that same menu
            > item.  Thus both shortcomings of the Carbon menus are circumvented, but it
            > does require adding a new command.  I can't think of any particular
            > objections to this approach...any comments?  Oh, except I would need to add
            > one or two entries 'struct VimMenu' which holds the key equivalent.

            I'm not quite sure if I understand this.  Keep in mind that in Vim menus
            work just like mappings.  They are a GUI way to get a command executed.

            When you define a menu, there might as well be a mapping for a key
            sequence that does the same thing.  But they are not really bound to
            each other, there can be subtle differences (so long as the user expects
            this).

            On the other hand, we also have menu accelerators.  These actually open
            a menu and allow you to select one of the entries.  This may be done
            partly outside of Vim, mostly with Alt-key combinations.  These are
            directly related to how the GUI works and have nothing to do with key
            mappings.  We do have a way to add accelerator keys in the menu text.

            Now, what you show in the text of the menus (at the righthand side) is
            yet another thing.  Of course it must make sense, but usually you need
            to update the text if you change a mapping that does the same action.
            Often it's not the whole story, e.g. displaying ":e" while the action
            actually is ":browse confirm e".

            I don't know what you mean with "accelerator text".  Is this something
            that actually opens a menu, or is it more like a mapping?  In the first
            case it's similar to what happens in the GUI, thus must be somehow done
            in a ":menu" command.  In the second case I would not want to change
            commands, just edit the text and add a mapping separately.

            I think I was a bit unclear in my previous post, so in order to understand the problem I will say something about the menu handling in Cocoa.  Hopefully this will clarify the situation.

            1.  The text on that goes on the right hand side of the menu item is handled by Cocoa.  You can only specify which "accelerator key" should go there (specifically, you can specify a printable key, e.g. "o", and modifiers, e.g. "Cmd") and Cocoa will do the drawing.  Hence, it is not possible to display things like ":e".  (I'm just restating what Jiang said in his post here.)

            2.  There are no "menu accelerators", where one letter of the menu item is underlined and pressing Alt+'that letter' brings up a menu and then pressing another letter will select a menu item in that menu.  That is, you cannot navigate through menus with the keyboard (correct me if I'm wrong here, but I don't know of any way to do so).

            3.  Cocoa intercepts key presses, checks if any menu item is "bound" to that key combination (the Cmd key is always part of such a binding).  If Cocoa finds a menu item bound the key combination, it briefly highlights the menu on the menu bar which contains that menu item, and calls the 'action message' of that menu item.  This 'feature' can be disabled by hacking a little, so that key presses go straight to Vim, but this means that the menu won't get highlighted (a minor disturbance, but this is the behaviour you expect from an OS X application, so I would like to preserve it.)


            Now to get back to my proposal.  If there was a command like this in Vim:
              :menukeyequiv File.Open <D-o>
            Then, you could make all the usual bindings (i.e. the usual ones for an OS X app) in a .vim file which gets sourced some time after menu.vim has been sourced (thus also making it possible to internationalize key equivalents).  The implementation of :menukeyequiv itself would simply pass the key combination (e.g. <D-o>) and the appropriate vimmenu_T* ( File.Open) to the GUI, which can then set the key equivalent of the menu item.  Then, if the user presses <D-o>, Cocoa intercepts this event, sees that it is bound to the "Open" menu item under the "File" menu and calls its action message (in this case, vimMenuItemAction:, which tells Vim to execute a menu item with gui_menu_cb()).

            The negative side effect of this is noticed if somebody tries to remap <D-o> with :map, since :map will think that <D-o> is not already bound to something.  When the user then presses <D-o> it is intercepted by the GUI and the <D-o> event never reaches Vim.   Of course, if you make it clear ( e.g. in :help MacVim) what is going on, then this should not be such a big problem.  (The key equivalent would be forced to contain the Cmd modifier because this is expected behaviour.)

            Without something like I outline above, you can of course do what the Carbon port does:  make all the usual (as in "OS X app usual") bindings in a .vim file (<D-o>, <D-c>, etc.) with :map, but this means that the text on the right hand side of all menu items will be empty, and the menu highlighting I mentioned above won't work.  From a OS X point of view this is quite confusing, but it I guess it makes "Vim-sense".

            Since I am trying to make Vim more OS X like I would strongly prefer the way I suggested above (i.e. I want the key equiv text on the right hand side _and_ the highlighting), but I will not do it if it is deemed to clash too much with how Vim works.


            > Second -- binding to action messages:
            >
            > In Cocoa, each NSMenuItem is "bound" to an action message, i.e. a
            > Objective-C message with void return type and which takes an 'id' (object
            > pointer) as its only argument.  At the moment MacVim binds every menu item
            > to vimMenuAction: (in MMWindowController) and this simply passes the 'tag'
            > (a vimmenu_T pointer) to MMBackend which in turn calls gui_menu_cb().
            > However, it would be a great deal more flexible if it was possible to create
            > menus in Vim which sent an arbitrary action message when executed.
            >
            > For instance, say I want to have a "New Window" menu item which calls the
            > action message newVimWindow: (which starts up a new Vim task).  (There
            > currently _is_ such an item on the window menu, but that is just one big
            > hack, it has to change.)  At the moment this is completely impossible...Vim
            > has no concept (nor should it) of MacVim's "windows" (or frames, the
            > terminology clash here is unfortunate).  Were it possible to bind to an
            > action, it would, and Vim would be none the wiser.  The only problem I see
            > is if somebody use ex_menu to execute a menu item bound to an action...some
            > kind of logic in Vim which deals with this would be necessary.

            The principle is that everything you can do with a menu must also be
            possible with a Vim command.  I would not want to add functionality that
            is _only_ available in a menu item.

            Thus if you miss some functionality I would rather add a Vim command for
            it and invoke it from the menu with that command.  That obviously also
            avoids the problem of executing a menu item with ":emenu".

            I agree...this is how it could be done:  I would implement a Vim command called e.g. ':action' which is used like this:
              :action vimNewWindow:
            When executed, ':action' passes the argument (vimNewWindow:, the name of an action message) to the GUI.  Then it would be possible to do this:
              :menu File.New\ Window :action vimNewWindow:

            That seems like a good solution to me...any objections?


            /Björn


            --~--~---------~--~----~------------~-------~--~----~
            You received this message from the "vim_mac" maillist.
            For more information, visit http://www.vim.org/maillist.php
            -~----------~----~----~----~------~----~------~--~---

          • Jjgod Jiang
            Hi, ... But this command only makes sense for Cocoa, I m not sure whether creating a command for only one GUI port is a good idea. - Jiang
            Message 5 of 6 , Jul 28, 2007
              Hi,

              2007/7/28, björn <bjorn.winckler@...>:
              > I agree...this is how it could be done: I would implement a Vim command
              > called e.g. ':action' which is used like this:
              > :action vimNewWindow:
              > When executed, ':action' passes the argument (vimNewWindow:, the name of an
              > action message) to the GUI. Then it would be possible to do this:
              > :menu File.New\ Window :action vimNewWindow:

              But this command only makes sense for Cocoa, I'm not sure whether
              creating a command for only one GUI port is a good idea.

              - Jiang

              --~--~---------~--~----~------------~-------~--~----~
              You received this message from the "vim_mac" maillist.
              For more information, visit http://www.vim.org/maillist.php
              -~----------~----~----~----~------~----~------~--~---
            • Bram Moolenaar
              ... That sounds very good to me. ... Well, if someone wants to map he will have to remove any menu item that captures this key. I don t think this is a
              Message 6 of 6 , Jul 28, 2007
                Bjorn Winckler wrote:

                > I think I was a bit unclear in my previous post, so in order to understand
                > the problem I will say something about the menu handling in Cocoa.
                > Hopefully this will clarify the situation.
                >
                > 1. The text on that goes on the right hand side of the menu item is handled
                > by Cocoa. You can only specify which "accelerator key" should go there
                > (specifically, you can specify a printable key, e.g. "o", and modifiers, e.g.
                > "Cmd") and Cocoa will do the drawing. Hence, it is not possible to display
                > things like ":e". (I'm just restating what Jiang said in his post here.)
                >
                > 2. There are no "menu accelerators", where one letter of the menu item is
                > underlined and pressing Alt+'that letter' brings up a menu and then pressing
                > another letter will select a menu item in that menu. That is, you cannot
                > navigate through menus with the keyboard (correct me if I'm wrong here, but
                > I don't know of any way to do so).
                >
                > 3. Cocoa intercepts key presses, checks if any menu item is "bound" to that
                > key combination (the Cmd key is always part of such a binding). If Cocoa
                > finds a menu item bound the key combination, it briefly highlights the menu
                > on the menu bar which contains that menu item, and calls the 'action
                > message' of that menu item. This 'feature' can be disabled by hacking a
                > little, so that key presses go straight to Vim, but this means that the menu
                > won't get highlighted (a minor disturbance, but this is the behaviour you
                > expect from an OS X application, so I would like to preserve it.)
                >
                >
                > Now to get back to my proposal. If there was a command like this in Vim:
                > :menukeyequiv File.Open <D-o>
                > Then, you could make all the usual bindings (i.e. the usual ones for an OS X
                > app) in a .vim file which gets sourced some time after menu.vim has been
                > sourced (thus also making it possible to internationalize key equivalents).
                > The implementation of :menukeyequiv itself would simply pass the key
                > combination (e.g. <D-o>) and the appropriate vimmenu_T* ( File.Open) to the
                > GUI, which can then set the key equivalent of the menu item. Then, if the
                > user presses <D-o>, Cocoa intercepts this event, sees that it is bound to
                > the "Open" menu item under the "File" menu and calls its action message (in
                > this case, vimMenuItemAction:, which tells Vim to execute a menu item with
                > gui_menu_cb()).

                That sounds very good to me.

                > The negative side effect of this is noticed if somebody tries to remap <D-o>
                > with :map, since :map will think that <D-o> is not already bound to
                > something. When the user then presses <D-o> it is intercepted by the GUI
                > and the <D-o> event never reaches Vim. Of course, if you make it clear (
                > e.g. in :help MacVim) what is going on, then this should not be such a big
                > problem. (The key equivalent would be forced to contain the Cmd modifier
                > because this is expected behaviour.)

                Well, if someone wants to map <D-o> he will have to remove any menu item
                that captures this key. I don't think this is a problem, people are
                aware a key may already be in use. The only problem may be that
                removing the menu can be a bit complicated. Ideally there would be a
                menu command to delete the menu associated with a specific key. But
                having the user find the menu and using the menu name itself would also
                be OK.

                > Without something like I outline above, you can of course do what the Carbon
                > port does: make all the usual (as in "OS X app usual") bindings in a .vim
                > file (<D-o>, <D-c>, etc.) with :map, but this means that the text on the
                > right hand side of all menu items will be empty, and the menu highlighting I
                > mentioned above won't work. From a OS X point of view this is quite
                > confusing, but it I guess it makes "Vim-sense".

                It also means you have to keep key mappings and menu entries in sync,
                everything is there twice.

                > Since I am trying to make Vim more OS X like I would strongly prefer the way
                > I suggested above (i.e. I want the key equiv text on the right hand side
                > _and_ the highlighting), but I will not do it if it is deemed to clash too
                > much with how Vim works.

                Agreed.

                > I agree...this is how it could be done: I would implement a Vim command
                > called e.g. ':action' which is used like this:
                > :action vimNewWindow:
                > When executed, ':action' passes the argument (vimNewWindow:, the name of an
                > action message) to the GUI. Then it would be possible to do this:
                > :menu File.New\ Window :action vimNewWindow:
                >
                > That seems like a good solution to me...any objections?

                Sounds good to me.

                --
                hundred-and-one symptoms of being an internet addict:
                50. The last girl you picked up was only a jpeg.

                /// Bram Moolenaar -- Bram@... -- http://www.Moolenaar.net \\\
                /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
                \\\ download, build and distribute -- http://www.A-A-P.org ///
                \\\ help me help AIDS victims -- http://ICCF-Holland.org ///

                --~--~---------~--~----~------------~-------~--~----~
                You received this message from the "vim_mac" maillist.
                For more information, visit http://www.vim.org/maillist.php
                -~----------~----~----~----~------~----~------~--~---
              Your message has been successfully submitted and would be delivered to recipients shortly.