From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Newsgroups: gmane.emacs.bugs Subject: bug#45502: [PATCH] Prettier key bindings in NS menu entries Date: Mon, 28 Dec 2020 15:23:25 +0100 Message-ID: Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.17\)) Content-Type: multipart/mixed; boundary="Apple-Mail=_F8693C92-8DE6-46E3-B196-EFD187D21980" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="31438"; mail-complaints-to="usenet@ciao.gmane.io" To: 45502@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Mon Dec 28 15:24:18 2020 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kttRa-000843-0a for geb-bug-gnu-emacs@m.gmane-mx.org; Mon, 28 Dec 2020 15:24:18 +0100 Original-Received: from localhost ([::1]:36718 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kttRY-0001cR-BY for geb-bug-gnu-emacs@m.gmane-mx.org; Mon, 28 Dec 2020 09:24:16 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:40230) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kttRL-0001cH-1o for bug-gnu-emacs@gnu.org; Mon, 28 Dec 2020 09:24:03 -0500 Original-Received: from debbugs.gnu.org ([209.51.188.43]:51371) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kttRK-0003Cp-Of for bug-gnu-emacs@gnu.org; Mon, 28 Dec 2020 09:24:02 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1kttRK-0008Rg-Js for bug-gnu-emacs@gnu.org; Mon, 28 Dec 2020 09:24:02 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Mon, 28 Dec 2020 14:24:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 45502 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch X-Debbugs-Original-To: bug-gnu-emacs@gnu.org Original-Received: via spool by submit@debbugs.gnu.org id=B.160916541932430 (code B ref -1); Mon, 28 Dec 2020 14:24:02 +0000 Original-Received: (at submit) by debbugs.gnu.org; 28 Dec 2020 14:23:39 +0000 Original-Received: from localhost ([127.0.0.1]:34684 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kttQx-0008R0-1W for submit@debbugs.gnu.org; Mon, 28 Dec 2020 09:23:39 -0500 Original-Received: from lists.gnu.org ([209.51.188.17]:59098) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kttQv-0008Qt-RP for submit@debbugs.gnu.org; Mon, 28 Dec 2020 09:23:38 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:40156) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kttQv-0001bX-L4 for bug-gnu-emacs@gnu.org; Mon, 28 Dec 2020 09:23:37 -0500 Original-Received: from mail212c50.megamailservers.eu ([91.136.10.222]:59906 helo=mail194c50.megamailservers.eu) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kttQs-00030m-OY for bug-gnu-emacs@gnu.org; Mon, 28 Dec 2020 09:23:37 -0500 X-Authenticated-User: mattiase@bredband.net DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=megamailservers.eu; s=maildub; t=1609165408; bh=fU0paIHjuXiqMuDrb3jUBowpt4ocBax5qDEIlmSorxI=; h=From:Subject:Date:To:From; b=QRUDTcGRDOLUYK+5R2T/c0PQMOBfI7rMZpLeoz2Y4M2lUMD5XbrqIY7+TkIniu+V5 NuZzpHki6p02GvzihGNyyLRK7tZbTBQrDNQaaf8xdBjZdC2QikKIqol6+T6rlYo+G4 vP/R7uW/Gku5oCtsBNJfOEQy7lhB0LIxU3/IGhyA= Feedback-ID: mattiase@acm.or Original-Received: from [192.168.0.4] (c188-150-171-71.bredband.comhem.se [188.150.171.71]) (authenticated bits=0) by mail194c50.megamailservers.eu (8.14.9/8.13.1) with ESMTP id 0BSENQJD007881 for ; Mon, 28 Dec 2020 14:23:27 +0000 X-Mailer: Apple Mail (2.3445.104.17) X-CTCH-RefID: str=0001.0A742F2A.5FE9EA60.0028, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0 X-CTCH-VOD: Unknown X-CTCH-Spam: Unknown X-CTCH-Score: 0.000 X-CTCH-Flags: 0 X-CTCH-ScoreCust: 0.000 X-CSC: 0 X-CHA: v=2.3 cv=AfXP4EfG c=1 sm=1 tr=0 a=SF+I6pRkHZhrawxbOkkvaA==:117 a=SF+I6pRkHZhrawxbOkkvaA==:17 a=M51BFTxLslgA:10 a=cgnavh2NdbwcH_dWKUcA:9 a=CjuIK1q_8ugA:10 a=qwvwYxifb6zOxjQCdIIA:9 a=De_Ol2h6w80A:10 X-Origin-Country: SE Received-SPF: softfail client-ip=91.136.10.222; envelope-from=mattiase@acm.org; helo=mail194c50.megamailservers.eu X-Spam_score_int: -11 X-Spam_score: -1.2 X-Spam_bar: - X-Spam_report: (-1.2 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_SOFTFAIL=0.665 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:196868 Archived-At: --Apple-Mail=_F8693C92-8DE6-46E3-B196-EFD187D21980 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii The NS port shows key bindings in a rather cluttered way, with the key = in brackets directly after the menu entry. The Mac port of Emacs is much = neater with the bindings all aligned at a common tab position. We could = do the same, but having done some experiments I actually prefer a = right-alignment of the keys. Proof-of-concept patch attached. The alignment is made by padding with spaces, and then with hair spaces = for extra precision; the result is not perfect but probably better than = what we have now. If I get some time, I might do an experiment with more = precise formatting. --Apple-Mail=_F8693C92-8DE6-46E3-B196-EFD187D21980 Content-Disposition: attachment; filename=right-justify-keys.diff Content-Type: application/octet-stream; x-unix-mode=0644; name="right-justify-keys.diff" Content-Transfer-Encoding: 7bit diff --git a/src/nsmenu.m b/src/nsmenu.m index 3f0cd0c6ed..6c96f5f123 100644 --- a/src/nsmenu.m +++ b/src/nsmenu.m @@ -450,32 +450,14 @@ - (BOOL)performKeyEquivalent: (NSEvent *)theEvent } -/* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '') - into an accelerator string. We are only able to display a single character - for an accelerator, together with an optional modifier combination. (Under - Carbon more control was possible, but in Cocoa multi-char strings passed to - NSMenuItem get ignored. For now we try to display a super-single letter - combo, and return the others as strings to be appended to the item title. - (This is signaled by setting keyEquivModMask to 0 for now.) */ --(NSString *)parseKeyEquiv: (const char *)key +static const char * +skipspc (const char *s) { - const char *tpos = key; - keyEquivModMask = NSEventModifierFlagCommand; - - if (!key || !*key) - return @""; - - while (*tpos == ' ' || *tpos == '(') - tpos++; - if ((*tpos == 's') && (*(tpos+1) == '-')) - { - return [NSString stringWithFormat: @"%c", tpos[2]]; - } - keyEquivModMask = 0; /* signal */ - return [NSString stringWithUTF8String: tpos]; + while (*s == ' ') + s++; + return s; } - - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr { NSMenuItem *item; @@ -488,31 +470,20 @@ - (NSMenuItem *)addItemWithWidgetValue: (void *)wvptr } else { - NSString *title, *keyEq; + NSString *title; title = [NSString stringWithUTF8String: wv->name]; if (title == nil) title = @"< ? >"; /* (get out in the open so we know about it) */ - keyEq = [self parseKeyEquiv: wv->key]; -#ifdef NS_IMPL_COCOA - /* macOS mangles modifier strings longer than one character. */ - if (keyEquivModMask == 0) - { - title = [title stringByAppendingFormat: @" (%@)", keyEq]; - item = [self addItemWithTitle: (NSString *)title - action: @selector (menuDown:) - keyEquivalent: @""]; - } - else - { -#endif - item = [self addItemWithTitle: (NSString *)title - action: @selector (menuDown:) - keyEquivalent: keyEq]; -#ifdef NS_IMPL_COCOA - } -#endif - [item setKeyEquivalentModifierMask: keyEquivModMask]; + /* Cocoa only permits a single key (with modifiers) as + keyEquivalent, so we stick with the standard Emacs notation + for all key bindings for the sake of uniformity. */ + if (wv->key) + title = [title stringByAppendingString: + [NSString stringWithUTF8String: skipspc (wv->key)]]; + item = [self addItemWithTitle: (NSString *)title + action: @selector (menuDown:) + keyEquivalent: @""]; [item setEnabled: wv->enabled]; @@ -557,14 +528,69 @@ -(void)removeAllItems - (void)fillWithWidgetValue: (void *)wvptr { - widget_value *wv = (widget_value *)wvptr; + widget_value *first_wv = (widget_value *)wvptr; + NSFont *menuFont = [NSFont menuFontOfSize:0]; + NSDictionary *attributes = + [NSDictionary dictionaryWithObject:menuFont forKey:NSFontAttributeName]; + NSSize spaceSize = [@" " sizeWithAttributes:attributes]; + CGFloat spaceWidth = spaceSize.width; + const char hair_str[] = "\u200a"; // HAIR SPACE + int hair_bytes = sizeof hair_str - 1; + NSSize hairSize = [[NSString stringWithUTF8String: hair_str] + sizeWithAttributes:attributes]; + CGFloat hairWidth = spaceSize.width; + CGFloat maxNameWidth = 0; + CGFloat maxKeyWidth = 0; + + /* Determine maximum width of menu items. */ + for (widget_value *wv = first_wv; wv != NULL; wv = wv->next) + if (!menu_separator_name_p (wv->name)) + { + NSString *name = [NSString stringWithUTF8String: wv->name]; + NSSize nameSize = [name sizeWithAttributes: attributes]; + CGFloat nameWidth = nameSize.width; + maxNameWidth = MAX(maxNameWidth, nameWidth); + if (wv->key) + { + NSString *key = [NSString stringWithUTF8String: skipspc (wv->key)]; + NSSize keySize = [key sizeWithAttributes: attributes]; + CGFloat keyWidth = keySize.width; + maxKeyWidth = MAX(maxKeyWidth, keyWidth); + } + } + CGFloat maxWidth = maxNameWidth + maxKeyWidth; /* clear existing contents */ [self removeAllItems]; /* add new contents */ - for (; wv != NULL; wv = wv->next) + for (widget_value *wv = first_wv; wv != NULL; wv = wv->next) { + if (wv->key && !menu_separator_name_p (wv->name)) + { + /* Modify name to include padding to right-justify key binding. */ + NSString *name = [NSString stringWithUTF8String: wv->name]; + NSSize nameSize = [name sizeWithAttributes: attributes]; + CGFloat nameWidth = nameSize.width; + NSString *key = [NSString stringWithUTF8String: skipspc (wv->key)]; + NSSize keySize = [key sizeWithAttributes: attributes]; + CGFloat keyWidth = keySize.width; + CGFloat padWidth = maxWidth - (nameWidth + keyWidth); + /* Use plain spaces as far as possible, and hair spaces for + the rest. */ + int extra_spaces = 4; + int nspaces = padWidth / spaceWidth + extra_spaces; + int nhairs = fmod(padWidth, spaceWidth) / hairWidth + 0.5; + int name_len = strlen (wv->name); + Lisp_Object s = make_uninit_string (name_len + nspaces + + nhairs * hair_bytes); + memcpy (SSDATA (s), wv->name, name_len); + memset (SDATA (s) + name_len, ' ', nspaces); + for (int i = 0; i < nhairs; i++) + memcpy (SDATA (s) + name_len + nspaces + i * hair_bytes, hair_str, + hair_bytes); + wv->name = SSDATA (s); + } NSMenuItem *item = [self addItemWithWidgetValue: wv]; if (wv->contents) diff --git a/src/nsterm.h b/src/nsterm.h index b7b4d3b047..c4873b6082 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -515,7 +515,6 @@ #define NS_DRAW_TO_BUFFER 1 @interface EmacsMenu : NSMenu { - unsigned long keyEquivModMask; BOOL needsUpdate; } --Apple-Mail=_F8693C92-8DE6-46E3-B196-EFD187D21980--