Menu UI feature common in other widget sets:
authorCampbell Barton <ideasman42@gmail.com>
Tue, 14 Dec 2010 02:38:29 +0000 (02:38 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 14 Dec 2010 02:38:29 +0000 (02:38 +0000)
 Automatically assign menu keys based on name, alternative to pressing number 0-9 on menus items.

 keys are assigned by first giving each menu item the first character of any word, if that fails any key in the name is used.

- active key is shown underlined.
- only ascii keys are assigned currently.
- can run operators, open menu items.
- currently this only works in cases where number buttons were used (UI_BLOCK_NUMSELECT), but could be enabled for file menu, splash etc by removing this check.

source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface.c
source/blender/editors/interface/interface_handlers.c
source/blender/editors/interface/interface_intern.h
source/blender/editors/interface/interface_style.c
source/blender/editors/interface/interface_widgets.c

index ef74052f217c7804e1f547b630692ad76f5b16ef..19e2a86f644a270e0a0a091e3a0700fd7035c15f 100644 (file)
@@ -749,6 +749,8 @@ void uiIDContextProperty(struct bContext *C, struct PointerRNA *ptr, struct Prop
 
 /* Styled text draw */
 void uiStyleFontSet(struct uiFontStyle *fs);
+void uiStyleFontDrawExt(struct uiFontStyle *fs, struct rcti *rect, const char *str,
+       float *r_xofs, float *r_yofs);
 void uiStyleFontDraw(struct uiFontStyle *fs, struct rcti *rect, const char *str);
 void uiStyleFontDrawRotated(struct uiFontStyle *fs, struct rcti *rect, const char *str);
 
index 60928f03ea72913e0db9b4813a4c0c10341f2db9..4e3e9786a5a56e639a6259b531d28cb3fa8d08a8 100644 (file)
@@ -29,6 +29,7 @@
 #include <limits.h>
 #include <math.h>
 #include <string.h>
+#include <ctype.h>
  
 #include "MEM_guardedalloc.h"
 
@@ -585,6 +586,77 @@ int uiButActiveOnly(const bContext *C, uiBlock *block, uiBut *but)
        return 1;
 }
 
+/* assigns automatic keybindings to menu items for fast access
+ * (underline key in menu) */
+static void ui_menu_block_set_keyaccels(uiBlock *block)
+{
+       uiBut *but;
+
+       unsigned int meny_key_mask= 0;
+       unsigned char menu_key;
+       const char *str_pt;
+       int pass;
+       int tot_missing= 0;
+
+       /* only do it before bounding */
+       if(block->minx != block->maxx)
+               return;
+
+       for(pass=0; pass<2; pass++) {
+               /* 2 Passes, on for first letter only, second for any letter if first fails
+                * fun first pass on all buttons so first word chars always get first priority */
+
+               for(but=block->buttons.first; but; but=but->next) {
+                       if(!ELEM4(but->type, BUT, MENU, BLOCK, PULLDOWN) || (but->flag & UI_HIDDEN)) {
+                               /* pass */
+                       }
+                       else if(but->menu_key=='\0') {
+                               if(but->str) {
+                                       for(str_pt= but->str; *str_pt; ) {
+                                               menu_key= tolower(*str_pt);
+                                               if((menu_key >= 'a' && menu_key <= 'z') && !(meny_key_mask & 1<<(menu_key-'a'))) {
+                                                       meny_key_mask |= 1<<(menu_key-'a');
+                                                       break;
+                                               }
+
+                                               if(pass==0) {
+                                                       /* Skip to next delimeter on first pass (be picky) */
+                                                       while(isalpha(*str_pt))
+                                                               str_pt++;
+
+                                                       if(*str_pt)
+                                                               str_pt++;
+                                               }
+                                               else {
+                                                       /* just step over every char second pass and find first usable key */
+                                                       str_pt++;
+                                               }
+                                       }
+
+                                       if(*str_pt) {
+                                               but->menu_key= menu_key;
+                                       }
+                                       else {
+                                               /* run second pass */
+                                               tot_missing++;
+                                       }
+
+                                       /* if all keys have been used just exit, unlikely */
+                                       if(meny_key_mask == (1<<26)-1) {
+                                               return;
+                                       }
+                               }
+                       }
+               }
+
+               /* check if second pass is needed */
+               if(!tot_missing) {
+                       break;
+               }
+       }
+}
+
+
 void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
 {
        uiBut *but;
@@ -658,6 +730,7 @@ void uiEndBlock(const bContext *C, uiBlock *block)
        /* handle pending stuff */
        if(block->layouts.first) uiBlockLayoutResolve(block, NULL, NULL);
        ui_block_do_align(block);
+       if((block->flag & UI_BLOCK_LOOP) && (block->flag & UI_BLOCK_NUMSELECT)) ui_menu_block_set_keyaccels(block); /* could use a different flag to check */
        if(block->flag & UI_BLOCK_LOOP) ui_menu_block_set_keymaps(C, block);
        
        /* after keymaps! */
index 85359267ffdd75c9bfe77e3acbafe24dd94e2d2d..e15696d7e80f9c36775317f37f5e1a3e63f919d1 100644 (file)
@@ -5635,8 +5635,65 @@ int ui_handle_menu_event(bContext *C, wmEvent *event, uiPopupBlockHandle *menu,
 
                                                retval= WM_UI_HANDLER_BREAK;
                                        }
+                                       break;
+
+                               /* Handle keystrokes on menu items */
+                               case AKEY:
+                               case BKEY:
+                               case CKEY:
+                               case DKEY:
+                               case EKEY:
+                               case FKEY:
+                               case GKEY:
+                               case HKEY:
+                               case IKEY:
+                               case JKEY:
+                               case KKEY:
+                               case LKEY:
+                               case MKEY:
+                               case NKEY:
+                               case OKEY:
+                               case PKEY:
+                               case QKEY:
+                               case RKEY:
+                               case SKEY:
+                               case TKEY:
+                               case UKEY:
+                               case VKEY:
+                               case WKEY:
+                               case XKEY:
+                               case YKEY:
+                               case ZKEY:
+                               {
+                                       if(event->val == KM_PRESS) {
+                                               count= 0;
+                                               for(but= block->buttons.first; but; but= but->next) {
 
+                                                       if(but->menu_key==event->type) {
+                                                               if(but->type == BUT) {
+                                                                       /* mainly for operator buttons */
+                                                                       ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE_APPLY);
+                                                               }
+                                                               else if(ELEM(but->type, BLOCK, PULLDOWN)) {
+                                                                       /* open submenus (like right arrow key) */
+                                                                       ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE_OPEN);
+                                                               }
+                                                               else if (but->type == MENU) {
+                                                                       /* activate menu items */
+                                                                       ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE);
+                                                               }
+                                                               else {
+                                                                       printf("Error, but->menu_key type: %d\n", but->type);
+                                                               }
+
+                                                               break;
+                                                       }
+                                               }
+
+                                               retval= WM_UI_HANDLER_BREAK;
+                                       }
                                        break;
+                               }
                        }
                }
                
index a7c03a6b95d8cee3d58a712ce2341074298e0b2d..72f6e72cd5d22fd6e62decc7fce8dac7f2655c25 100644 (file)
@@ -237,9 +237,10 @@ struct uiBut {
        struct IDProperty *opproperties;
        struct PointerRNA *opptr;
        short opcontext;
-       
+       unsigned char menu_key; /* 'a'-'z', always lower case */
+
        /* Draggable data, type is WM_DRAG_... */
-       short dragtype;
+       char dragtype;
        void *dragpoin;
        struct ImBuf *imb;
        float imb_scale;
index ec9ac300ca2b98a1ee73c23aff91988bb1dac487..af0b2189a9950e9c108e306eb612c0f0185b8cdd 100644 (file)
@@ -136,7 +136,9 @@ static uiFont *uifont_to_blfont(int id)
 
 /* *************** draw ************************ */
 
-void uiStyleFontDraw(uiFontStyle *fs, rcti *rect, const char *str)
+
+void uiStyleFontDrawExt(uiFontStyle *fs, rcti *rect, const char *str,
+       float *r_xofs, float *r_yofs)
 {
        float height;
        int xofs=0, yofs;
@@ -171,6 +173,16 @@ void uiStyleFontDraw(uiFontStyle *fs, rcti *rect, const char *str)
                BLF_disable(fs->uifont_id, BLF_SHADOW);
        if (fs->kerning == 1)
                BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
+
+       *r_xofs= xofs;
+       *r_yofs= yofs;
+}
+
+void uiStyleFontDraw(uiFontStyle *fs, rcti *rect, const char *str)
+{
+       float xofs, yofs;
+       uiStyleFontDrawExt(fs, rect, str,
+               &xofs, &yofs);
 }
 
 /* drawn same as above, but at 90 degree angle */
index a3a8e40abe6d2a611e452a30665cb3dc2f6ea3a0..319d51cdf75a01d7c4f660a3c80dbd7e52f20bfb 100644 (file)
@@ -35,6 +35,7 @@
 #include "BLI_math.h"
 #include "BLI_listbase.h"
 #include "BLI_rect.h"
+#include "BLI_string.h"
 
 #include "BKE_context.h"
 #include "BKE_curve.h"
@@ -961,6 +962,9 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
 //     int transopts;
        char *cpoin = NULL;
        
+       /* for underline drawing */
+       float font_xofs, font_yofs;
+
        uiStyleFontSet(fstyle);
        
        if(but->editstr || (but->flag & UI_TEXT_LEFT))
@@ -1038,7 +1042,40 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
        }
        
        glColor3ubv((unsigned char*)wcol->text);
-       uiStyleFontDraw(fstyle, rect, but->drawstr+but->ofs);
+
+       uiStyleFontDrawExt(fstyle, rect, but->drawstr+but->ofs, &font_xofs, &font_yofs);
+
+       if(but->menu_key != '\0') {
+               char fixedbuf[128];
+               char *str;
+
+               BLI_strncpy(fixedbuf, but->drawstr + but->ofs, sizeof(fixedbuf));
+
+               str= strchr(fixedbuf, but->menu_key-32); /* upper case */
+               if(str==NULL)
+                       str= strchr(fixedbuf, but->menu_key);
+
+               if(str) {
+                       int ul_index= -1;
+                       float ul_advance;
+
+                       ul_index= (int)(str - fixedbuf);
+
+                       if (fstyle->kerning == 1) {
+                               BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
+                       }
+
+                       fixedbuf[ul_index]= '\0';
+                       ul_advance= BLF_width(fstyle->uifont_id, fixedbuf);
+
+                       BLF_position(fstyle->uifont_id, rect->xmin+font_xofs + ul_advance, rect->ymin+font_yofs, 0.0f);
+                       BLF_draw(fstyle->uifont_id, "_", 2);
+
+                       if (fstyle->kerning == 1) {
+                               BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
+                       }
+               }
+       }
 
        /* part text right aligned */
        if(cpoin) {