summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-12-15 12:38:21 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-12-15 12:38:21 +0200
commit2619a97a74eb1b758263b7eca6e3968e9b05888b (patch)
tree5c86ea022253df9382810f9886ea42d22a9e2dcb
parentefcd356c447094089a147ba3cfffd4c433a81e24 (diff)
macOS: Newlines in native menus
Other formatting besides line breaks is ignored for now, although attributed strings could be used here.
-rw-r--r--src/gmdocument.c6
-rw-r--r--src/gmutil.h6
-rw-r--r--src/macos.m44
-rw-r--r--src/ui/text.c6
-rw-r--r--src/ui/text.h4
5 files changed, 54 insertions, 12 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c
index 56b2f06d..bec89ca0 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -1899,9 +1899,9 @@ void setUrl_GmDocument(iGmDocument *d, const iString *url) {
1899 } 1899 }
1900} 1900}
1901 1901
1902static int replaceRegExp_String(iString *d, const iRegExp *regexp, const char *replacement, 1902int replaceRegExp_String(iString *d, const iRegExp *regexp, const char *replacement,
1903 void (*matchHandler)(void *, const iRegExpMatch *), 1903 void (*matchHandler)(void *, const iRegExpMatch *),
1904 void *context) { 1904 void *context) {
1905 iRegExpMatch m; 1905 iRegExpMatch m;
1906 iString result; 1906 iString result;
1907 int numMatches = 0; 1907 int numMatches = 0;
diff --git a/src/gmutil.h b/src/gmutil.h
index 35a7ee0d..6d337eeb 100644
--- a/src/gmutil.h
+++ b/src/gmutil.h
@@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
27 27
28iDeclareType(GmError) 28iDeclareType(GmError)
29iDeclareType(RegExp) 29iDeclareType(RegExp)
30iDeclareType(RegExpMatch)
30iDeclareType(Url) 31iDeclareType(Url)
31 32
32/* Response status codes. */ 33/* Response status codes. */
@@ -145,3 +146,8 @@ const iString * findContainerArchive_Path (const iString *path);
145 146
146 147
147const iString * feedEntryOpenCommand_String (const iString *url, int newTab); /* checks fragment */ 148const iString * feedEntryOpenCommand_String (const iString *url, int newTab); /* checks fragment */
149
150/* TODO: Consider adding this to the_Foundation. */
151int replaceRegExp_String (iString *, const iRegExp *regexp, const char *replacement,
152 void (*matchHandler)(void *, const iRegExpMatch *),
153 void *context);
diff --git a/src/macos.m b/src/macos.m
index cfbca488..5fd76bb9 100644
--- a/src/macos.m
+++ b/src/macos.m
@@ -110,7 +110,7 @@ static void ignoreImmediateKeyDownEvents_(void) {
110- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier 110- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier
111 title:(NSString *)title 111 title:(NSString *)title
112 command:(NSString *)cmd { 112 command:(NSString *)cmd {
113 [super initWithIdentifier:identifier]; 113 self = [super initWithIdentifier:identifier];
114 self.view = [NSButton buttonWithTitle:title target:self action:@selector(buttonPressed)]; 114 self.view = [NSButton buttonWithTitle:title target:self action:@selector(buttonPressed)];
115 command = cmd; 115 command = cmd;
116 return self; 116 return self;
@@ -120,7 +120,7 @@ static void ignoreImmediateKeyDownEvents_(void) {
120 image:(NSImage *)image 120 image:(NSImage *)image
121 widget:(iWidget *)widget 121 widget:(iWidget *)widget
122 command:(NSString *)cmd { 122 command:(NSString *)cmd {
123 [super initWithIdentifier:identifier]; 123 self = [super initWithIdentifier:identifier];
124 self.view = [NSButton buttonWithImage:image target:self action:@selector(buttonPressed)]; 124 self.view = [NSButton buttonWithImage:image target:self action:@selector(buttonPressed)];
125 command = cmd; 125 command = cmd;
126 return self; 126 return self;
@@ -163,12 +163,13 @@ static void ignoreImmediateKeyDownEvents_(void) {
163@implementation MenuCommands 163@implementation MenuCommands
164 164
165- (id)init { 165- (id)init {
166 self = [super init];
166 commands = [[NSMutableDictionary<NSString *, NSString *> alloc] init]; 167 commands = [[NSMutableDictionary<NSString *, NSString *> alloc] init];
167 source = NULL; 168 source = NULL;
168 return self; 169 return self;
169} 170}
170 171
171- (void)setCommand:(NSString *)command forMenuItem:(NSMenuItem *)menuItem { 172- (void)setCommand:(NSString * __nonnull)command forMenuItem:(NSMenuItem * __nonnull)menuItem {
172 [commands setObject:command forKey:[menuItem title]]; 173 [commands setObject:command forKey:[menuItem title]];
173} 174}
174 175
@@ -220,7 +221,7 @@ static void ignoreImmediateKeyDownEvents_(void) {
220@implementation MyDelegate 221@implementation MyDelegate
221 222
222- (id)initWithSDLDelegate:(NSObject<NSApplicationDelegate> *)sdl { 223- (id)initWithSDLDelegate:(NSObject<NSApplicationDelegate> *)sdl {
223 [super init]; 224 self = [super init];
224 currentAppearanceName = nil; 225 currentAppearanceName = nil;
225 menuCommands = [[MenuCommands alloc] init]; 226 menuCommands = [[MenuCommands alloc] init];
226 touchBarVariant = default_TouchBarVariant; 227 touchBarVariant = default_TouchBarVariant;
@@ -541,6 +542,29 @@ enum iColorId removeColorEscapes_String(iString *d) {
541 return color; 542 return color;
542} 543}
543 544
545static NSString *cleanString_(const iString *ansiEscapedText) {
546 iString mod;
547 initCopy_String(&mod, ansiEscapedText);
548 iRegExp *ansi = makeAnsiEscapePattern_Text();
549 replaceRegExp_String(&mod, ansi, "", NULL, NULL);
550 iRelease(ansi);
551 NSString *clean = [NSString stringWithUTF8String:cstr_String(&mod)];
552 deinit_String(&mod);
553 return clean;
554}
555
556#if 0
557static NSAttributedString *makeAttributedString_(const iString *ansiEscapedText) {
558 iString mod;
559 initCopy_String(&mod, ansiEscapedText);
560 NSData *data = [NSData dataWithBytesNoCopy:data_Block(&mod.chars) length:size_String(&mod)];
561 NSAttributedString *as = [[NSAttributedString alloc] initWithHTML:data
562 documentAttributes:nil];
563 deinit_String(&mod);
564 return as;
565}
566#endif
567
544/* returns the selected item, if any */ 568/* returns the selected item, if any */
545static NSMenuItem *makeMenuItems_(NSMenu *menu, MenuCommands *commands, const iMenuItem *items, size_t n) { 569static NSMenuItem *makeMenuItems_(NSMenu *menu, MenuCommands *commands, const iMenuItem *items, size_t n) {
546 NSMenuItem *selectedItem = nil; 570 NSMenuItem *selectedItem = nil;
@@ -557,7 +581,7 @@ static NSMenuItem *makeMenuItems_(NSMenu *menu, MenuCommands *commands, const iM
557 isChecked = iTrue; 581 isChecked = iTrue;
558 label += 3; 582 label += 3;
559 } 583 }
560 else if (startsWith_CStr(label, "///")) { 584 else if (startsWith_CStr(label, "///") || startsWith_CStr(label, "```")) {
561 isDisabled = iTrue; 585 isDisabled = iTrue;
562 label += 3; 586 label += 3;
563 } 587 }
@@ -567,9 +591,13 @@ static NSMenuItem *makeMenuItems_(NSMenu *menu, MenuCommands *commands, const iM
567 if (removeColorEscapes_String(&itemTitle) == uiTextCaution_ColorId) { 591 if (removeColorEscapes_String(&itemTitle) == uiTextCaution_ColorId) {
568// prependCStr_String(&itemTitle, "\u26a0\ufe0f "); 592// prependCStr_String(&itemTitle, "\u26a0\ufe0f ");
569 } 593 }
570 NSMenuItem *item = [menu addItemWithTitle:[NSString stringWithUTF8String:cstr_String(&itemTitle)] 594 NSMenuItem *item = [[NSMenuItem alloc] init];
571 action:(hasCommand ? @selector(postMenuItemCommand:) : nil) 595 /* Use attributed string to allow newlines. */
572 keyEquivalent:@""]; 596 NSAttributedString *title = [[NSAttributedString alloc] initWithString:cleanString_(&itemTitle)];
597 item.attributedTitle = title;
598 [title release];
599 item.action = (hasCommand ? @selector(postMenuItemCommand:) : nil);
600 [menu addItem:item];
573 deinit_String(&itemTitle); 601 deinit_String(&itemTitle);
574 [item setTarget:commands]; 602 [item setTarget:commands];
575 if (isChecked) { 603 if (isChecked) {
diff --git a/src/ui/text.c b/src/ui/text.c
index 7367e6c0..f3d945e4 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -390,12 +390,16 @@ static void deinitCache_Text_(iText *d) {
390 SDL_DestroyTexture(d->cache); 390 SDL_DestroyTexture(d->cache);
391} 391}
392 392
393iRegExp *makeAnsiEscapePattern_Text(void) {
394 return new_RegExp("[[()][?]?([0-9;AB]*?)([ABCDEFGHJKSTfhilmn])", 0);
395}
396
393void init_Text(iText *d, SDL_Renderer *render) { 397void init_Text(iText *d, SDL_Renderer *render) {
394 iText *oldActive = activeText_; 398 iText *oldActive = activeText_;
395 activeText_ = d; 399 activeText_ = d;
396 init_Array(&d->fonts, sizeof(iFont)); 400 init_Array(&d->fonts, sizeof(iFont));
397 d->contentFontSize = contentScale_Text_; 401 d->contentFontSize = contentScale_Text_;
398 d->ansiEscape = new_RegExp("[[()][?]?([0-9;AB]*?)([ABCDEFGHJKSTfhilmn])", 0); 402 d->ansiEscape = makeAnsiEscapePattern_Text();
399 d->baseFontId = -1; 403 d->baseFontId = -1;
400 d->baseFgColorId = -1; 404 d->baseFgColorId = -1;
401 d->missingGlyphs = iFalse; 405 d->missingGlyphs = iFalse;
diff --git a/src/ui/text.h b/src/ui/text.h
index b7934855..cb29adad 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -29,6 +29,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 29
30#include "fontpack.h" 30#include "fontpack.h"
31 31
32iDeclareType(RegExp)
33
32/* Content sizes: regular (1x) -> medium (1.2x) -> big (1.33x) -> large (1.67x) -> huge (2x) */ 34/* Content sizes: regular (1x) -> medium (1.2x) -> big (1.33x) -> large (1.67x) -> huge (2x) */
33 35
34#define FONT_ID(name, style, size) ((name) + ((style) * max_FontSize) + (size)) 36#define FONT_ID(name, style, size) ((name) + ((style) * max_FontSize) + (size))
@@ -230,6 +232,8 @@ enum iTextBlockMode { quadrants_TextBlockMode, shading_TextBlockMode };
230iString * renderBlockChars_Text (const iBlock *fontData, int height, enum iTextBlockMode, 232iString * renderBlockChars_Text (const iBlock *fontData, int height, enum iTextBlockMode,
231 const iString *text); 233 const iString *text);
232 234
235iRegExp * makeAnsiEscapePattern_Text (void);
236
233/*-----------------------------------------------------------------------------------------------*/ 237/*-----------------------------------------------------------------------------------------------*/
234 238
235iDeclareType(TextBuf) 239iDeclareType(TextBuf)