Menu UI feature common in other widget sets:

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.
This commit is contained in:
2010-12-14 02:38:29 +00:00
parent 17bd906de3
commit 35f431b3d0
6 changed files with 187 additions and 5 deletions

View 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);

View 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! */

View File

@@ -5635,11 +5635,68 @@ 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;
}
}
}
/* here we check return conditions for menus */
if(block->flag & UI_BLOCK_LOOP) {
/* if we click outside the block, verify if we clicked on the

View 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;

View 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 */

View 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) {