aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattias EngdegÄrd2020-12-28 15:24:08 +0100
committerMattias EngdegÄrd2020-12-29 17:34:53 +0100
commitb5ada7f9afc157cce2d58ad157841b65b2450fb9 (patch)
tree1c433453238d0dd862122439fa7baa839a363e74 /src
parent7fbcca29b883e68b7a92d4bc706aa0a0bd19b5a4 (diff)
downloademacs-b5ada7f9afc157cce2d58ad157841b65b2450fb9.tar.gz
emacs-b5ada7f9afc157cce2d58ad157841b65b2450fb9.zip
More readable keys in NS menu entries (bug#45502)
Each menu entry now has the key binding in a right-aligned column, as an attempt to improve readability. Previously the keys were given in brackets immediately following the menu string. * src/nsmenu.m ([EmacsMenu parseKeyEquiv:]): Remove. (skipspc): New helper function. ([EmacsMenu addItemWithWidgetValue:]): Add attributes argument. Use attributed title string. Don't special-case Super bindings. ([EmacsMenu fillWithWidgetValue:]): Compute maximum width. Prepare attributes for title.
Diffstat (limited to 'src')
-rw-r--r--src/nsmenu.m105
-rw-r--r--src/nsterm.h5
2 files changed, 61 insertions, 49 deletions
diff --git a/src/nsmenu.m b/src/nsmenu.m
index 201c02bb35d..d5321dcdc6d 100644
--- a/src/nsmenu.m
+++ b/src/nsmenu.m
@@ -457,33 +457,16 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
457} 457}
458 458
459 459
460/* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>') 460static const char *
461 into an accelerator string. We are only able to display a single character 461skipspc (const char *s)
462 for an accelerator, together with an optional modifier combination. (Under
463 Carbon more control was possible, but in Cocoa multi-char strings passed to
464 NSMenuItem get ignored. For now we try to display a super-single letter
465 combo, and return the others as strings to be appended to the item title.
466 (This is signaled by setting keyEquivModMask to 0 for now.) */
467-(NSString *)parseKeyEquiv: (const char *)key
468{ 462{
469 const char *tpos = key; 463 while (*s == ' ')
470 keyEquivModMask = NSEventModifierFlagCommand; 464 s++;
471 465 return s;
472 if (!key || !*key)
473 return @"";
474
475 while (*tpos == ' ' || *tpos == '(')
476 tpos++;
477 if ((*tpos == 's') && (*(tpos+1) == '-'))
478 {
479 return [NSString stringWithFormat: @"%c", tpos[2]];
480 }
481 keyEquivModMask = 0; /* signal */
482 return [NSString stringWithUTF8String: tpos];
483} 466}
484 467
485
486- (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr 468- (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
469 attributes: (NSDictionary *)attributes
487{ 470{
488 NSMenuItem *item; 471 NSMenuItem *item;
489 widget_value *wv = (widget_value *)wvptr; 472 widget_value *wv = (widget_value *)wvptr;
@@ -491,36 +474,32 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
491 if (menu_separator_name_p (wv->name)) 474 if (menu_separator_name_p (wv->name))
492 { 475 {
493 item = [NSMenuItem separatorItem]; 476 item = [NSMenuItem separatorItem];
494 [self addItem: item];
495 } 477 }
496 else 478 else
497 { 479 {
498 NSString *title, *keyEq; 480 NSString *title = [NSString stringWithUTF8String: wv->name];
499 title = [NSString stringWithUTF8String: wv->name];
500 if (title == nil) 481 if (title == nil)
501 title = @"< ? >"; /* (get out in the open so we know about it) */ 482 title = @"< ? >"; /* (get out in the open so we know about it) */
502 483
503 keyEq = [self parseKeyEquiv: wv->key]; 484 item = [[NSMenuItem alloc] init];
504#ifdef NS_IMPL_COCOA 485 if (wv->key)
505 /* macOS mangles modifier strings longer than one character. */
506 if (keyEquivModMask == 0)
507 { 486 {
508 title = [title stringByAppendingFormat: @" (%@)", keyEq]; 487 NSString *key = [NSString stringWithUTF8String: skipspc (wv->key)];
509 item = [self addItemWithTitle: (NSString *)title
510 action: @selector (menuDown:)
511 keyEquivalent: @""];
512 }
513 else
514 {
515#endif
516 item = [self addItemWithTitle: (NSString *)title
517 action: @selector (menuDown:)
518 keyEquivalent: keyEq];
519#ifdef NS_IMPL_COCOA 488#ifdef NS_IMPL_COCOA
520 } 489 /* Cocoa only permits a single key (with modifiers) as
490 keyEquivalent, so we put them in the title string
491 in a tab-separated column. */
492 title = [title stringByAppendingFormat: @"\t%@", key];
493#else
494 [item setKeyEquivalent: key];
521#endif 495#endif
522 [item setKeyEquivalentModifierMask: keyEquivModMask]; 496 }
523 497
498 NSAttributedString *atitle = [[NSAttributedString alloc]
499 initWithString: title
500 attributes: attributes];
501 [item setAction: @selector (menuDown:)];
502 [item setAttributedTitle: atitle];
524 [item setEnabled: wv->enabled]; 503 [item setEnabled: wv->enabled];
525 504
526 /* Draw radio buttons and tickboxes. */ 505 /* Draw radio buttons and tickboxes. */
@@ -533,6 +512,7 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
533 [item setTag: (NSInteger)wv->call_data]; 512 [item setTag: (NSInteger)wv->call_data];
534 } 513 }
535 514
515 [self addItem: item];
536 return item; 516 return item;
537} 517}
538 518
@@ -557,15 +537,48 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
557 537
558- (void)fillWithWidgetValue: (void *)wvptr 538- (void)fillWithWidgetValue: (void *)wvptr
559{ 539{
560 widget_value *wv = (widget_value *)wvptr; 540 widget_value *first_wv = (widget_value *)wvptr;
541 NSFont *menuFont = [NSFont menuFontOfSize:0];
542 NSDictionary *font_attribs = @{NSFontAttributeName: menuFont};
543 CGFloat maxNameWidth = 0;
544 CGFloat maxKeyWidth = 0;
545
546 /* Determine the maximum width of all menu items. */
547 for (widget_value *wv = first_wv; wv != NULL; wv = wv->next)
548 if (!menu_separator_name_p (wv->name))
549 {
550 NSString *name = [NSString stringWithUTF8String: wv->name];
551 NSSize nameSize = [name sizeWithAttributes: font_attribs];
552 maxNameWidth = MAX(maxNameWidth, nameSize.width);
553 if (wv->key)
554 {
555 NSString *key = [NSString stringWithUTF8String: skipspc (wv->key)];
556 NSSize keySize = [key sizeWithAttributes: font_attribs];
557 maxKeyWidth = MAX(maxKeyWidth, keySize.width);
558 }
559 }
560
561 /* Put some space between the names and keys. */
562 CGFloat maxWidth = maxNameWidth + maxKeyWidth + 40;
563
564 /* Set a right-aligned tab stop at the maximum width, so that the
565 key will appear immediately to the left of it. */
566 NSTextTab *tab =
567 [[NSTextTab alloc] initWithTextAlignment: NSTextAlignmentRight
568 location: maxWidth
569 options: @{}];
570 NSMutableParagraphStyle *pstyle = [[NSMutableParagraphStyle alloc] init];
571 [pstyle setTabStops: @[tab]];
572 NSDictionary *attributes = @{NSParagraphStyleAttributeName: pstyle};
561 573
562 /* clear existing contents */ 574 /* clear existing contents */
563 [self removeAllItems]; 575 [self removeAllItems];
564 576
565 /* add new contents */ 577 /* add new contents */
566 for (; wv != NULL; wv = wv->next) 578 for (widget_value *wv = first_wv; wv != NULL; wv = wv->next)
567 { 579 {
568 NSMenuItem *item = [self addItemWithWidgetValue: wv]; 580 NSMenuItem *item = [self addItemWithWidgetValue: wv
581 attributes: attributes];
569 582
570 if (wv->contents) 583 if (wv->contents)
571 { 584 {
diff --git a/src/nsterm.h b/src/nsterm.h
index b7b4d3b047c..f1d5acde2e6 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -515,13 +515,12 @@ typedef id instancetype;
515 515
516@interface EmacsMenu : NSMenu <NSMenuDelegate> 516@interface EmacsMenu : NSMenu <NSMenuDelegate>
517{ 517{
518 unsigned long keyEquivModMask;
519 BOOL needsUpdate; 518 BOOL needsUpdate;
520} 519}
521 520
522- (void)menuNeedsUpdate: (NSMenu *)menu; /* (delegate method) */ 521- (void)menuNeedsUpdate: (NSMenu *)menu; /* (delegate method) */
523- (NSString *)parseKeyEquiv: (const char *)key; 522- (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr
524- (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr; 523 attributes: (NSDictionary *)attributes;
525- (void)fillWithWidgetValue: (void *)wvptr; 524- (void)fillWithWidgetValue: (void *)wvptr;
526- (EmacsMenu *)addSubmenuWithTitle: (const char *)title; 525- (EmacsMenu *)addSubmenuWithTitle: (const char *)title;
527- (void) removeAllItems; 526- (void) removeAllItems;