Go to QuArK Web Site
Right Mouse Menus
Updated 05 Apr 2018
Upper levels:
QuArK Information Base
3. Advanced customization
3.6. Plug-Ins
3.6.1. Plugin Tutorial

 3.6.1.6. Right Mouse Menus

 [ Prev - Up - Next ] 

Our commands work, but the interface is clunky because we have to select our sides, and then cruise up to the command menu (or remember a hot key, which are in limited supply) to get the effect. A popular strategy to make things easier is to put commands on a `speed menu', or RMB menu, which is brought up by clicking the right mouse button when the cursor is over the object we want to act on.


 Index


 Basics

tiglari - 05 Apr 2018   [ Top ] 

Once again we will be redefining a method (the one that produces the speed menus), but the technique is different again because of the way the relevant classes are set up. Speed menus are handled by methods associated with various kinds of classes descended from EntityManager, defined in the quarkpy file mapentities.py. These classes have no instances, which will lead to a bit of black magic later on. The class for faces is FaceType

What we'll be doing is defining a new menu function for FaceType, which will add our new stuff onto the right mouse menu for faces. What stuff? Well, always, a Tag Side command, but if a side is tagged, we will also want a Glue to Tagged command, and for a final flourish, let's say that if the face we're looking at is the original one, the only option we want is to Clear the tag.

Here is a basic format for adding things to the face menu:

def tagmenu(o, editor, oldfacemenu = quarkpy.mapentities.FaceType.menu.im_func):
    "the new right-mouse for sides"
    menu = oldfacemenu(o, editor)
    <add stuff to menu>
    return menu
quarkpy.mapentities.FaceType.menu = tagmenu
The `im_func' and the end of the `def' line is the black magic; it is needed to refer to a method of a class with no instances.

Now to add stuff to the menu we could in fact just add the menu items we already have, getting something like this:

def tagmenu(o, editor, oldfacemenu = quarkpy.mapentities.FaceType.menu.im_func):
    "the new right-mouse for sides"
    menu = oldfacemenu(o, editor)
    menu = [mentagside, menglueside, menclearside]+menu
    return menu
These menu-building functions take two arguments, the object they're called by RMB-ing on, and the editor. And we pass the older form of the menu function, which this one replaces, as a default parameter.


 Elaborations

tiglari - 05 Apr 2018   [ Top ] 

The above works, except that we've lost the disablers. We could arrange things so that the RMB items used the same disablers as the command items, but the actual conditions are a bit different. For example on the RMB, the selected item is guaranteed to be a single face, since we're putting these items on the face menu. So we can use much simpler disablers and revise our menu function to, say, this:

def tagmenu(o, editor, oldfacemenu = quarkpy.mapentities.FaceType.menu.im_func):
    "the new right-mouse for sides"
    menu = oldfacemenu(o, editor)
    menglueside.state = mentagside.state = mencleartag.state = qmenu.normal
    tagged = gettagged(editor)
    if tagged is None:
        menglueside.state = mencleartag.state = qmenu.disabled
    elif tagged==o:
        mentagside.state = menglueside.state = qmenu.disabled
    menu = [mentagside, menglueside, mencleartag]+menu
    return menu
 maptagside4.py  contains the final version of this stuff so far.

Here we're using menu items and click functions defined outside menu functions, but there is another approach that's possible if you don't want to also use the menu item on the main menu or a toolbar. If we define the click function inside the menu function, we can use the default argument mechanism to pass the editor and the face operated on, so that for example the menu function might start out like this:

def tagmenu(o, editor, oldfacemenu = quarkpy.mapentities.FaceType.menu.im_func):
    "the new right-mouse for sides"
    menu = oldfacemenu(o, editor)
    def tagSideClick(m, face=o, editor=editor):
        editor.tagging = Tagging()
        editor.tagging.tagged = face
        editor.invalidateviews()
This technique is used in `curvemenu' in plugins\mb2curves.py, and elsewhere.

A final point is that the disabler calculations are sometimes quite complex, and produce results that are useful for the actual operation. This happens for example with the texture-wrapping commands in the real maptagside.py. These results can be passed thru to the click function either with the default argument mechanism:

    usefulInfo = complicatedFunction(o, editor)
    def complexClick(m,o=o, editor=editor,usefulInfo=usefulInfo)
        ...
    menuItem = qmenu.item('Complicated Function',complexClick)
    if usefulInfo is None:
        menuItem.state=qmenu.disabled
or else by attaching thing them directly to the menu item:
    ...
    menuItem = qmenu.item('Complicated Function',complexClick)
    menuItem.usefulInfo = complicatedFunction(o, editor)
    if isefulInfo is None:
        menuItem.state=qmenu.disabled
    else:
        menuitem.usefulInfo=usefulInfo
If the latter technique is used, the click function can be defined outside of the menu function definition:
def compexClick(m):
    editor=madeditor()
    if editor is None: return
    usefulInfo = m.usefulInfo
    ...
Even tho the click function only gets one (non-default) argument, the menu item that was clicked on to launch it, this imposes no practical limitation on the amount of information that can be passed thru it, since there's no limit on what can be attached to that argument



Copyright (c) 2022, GNU General Public License by The QuArK (Quake Army Knife) Community - https://quark.sourceforge.io/

 [ Prev - Top - Next ]