diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-15 12:38:21 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-15 12:38:21 +0200 |
commit | 2619a97a74eb1b758263b7eca6e3968e9b05888b (patch) | |
tree | 5c86ea022253df9382810f9886ea42d22a9e2dcb /src | |
parent | efcd356c447094089a147ba3cfffd4c433a81e24 (diff) |
macOS: Newlines in native menus
Other formatting besides line breaks is ignored for now, although attributed strings could be used here.
Diffstat (limited to 'src')
-rw-r--r-- | src/gmdocument.c | 6 | ||||
-rw-r--r-- | src/gmutil.h | 6 | ||||
-rw-r--r-- | src/macos.m | 44 | ||||
-rw-r--r-- | src/ui/text.c | 6 | ||||
-rw-r--r-- | src/ui/text.h | 4 |
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 | ||
1902 | static int replaceRegExp_String(iString *d, const iRegExp *regexp, const char *replacement, | 1902 | int 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 | ||
28 | iDeclareType(GmError) | 28 | iDeclareType(GmError) |
29 | iDeclareType(RegExp) | 29 | iDeclareType(RegExp) |
30 | iDeclareType(RegExpMatch) | ||
30 | iDeclareType(Url) | 31 | iDeclareType(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 | ||
147 | const iString * feedEntryOpenCommand_String (const iString *url, int newTab); /* checks fragment */ | 148 | const iString * feedEntryOpenCommand_String (const iString *url, int newTab); /* checks fragment */ |
149 | |||
150 | /* TODO: Consider adding this to the_Foundation. */ | ||
151 | int 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 | ||
545 | static 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 | ||
557 | static 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 */ |
545 | static NSMenuItem *makeMenuItems_(NSMenu *menu, MenuCommands *commands, const iMenuItem *items, size_t n) { | 569 | static 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 | ||
393 | iRegExp *makeAnsiEscapePattern_Text(void) { | ||
394 | return new_RegExp("[[()][?]?([0-9;AB]*?)([ABCDEFGHJKSTfhilmn])", 0); | ||
395 | } | ||
396 | |||
393 | void init_Text(iText *d, SDL_Renderer *render) { | 397 | void 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 | ||
32 | iDeclareType(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 }; | |||
230 | iString * renderBlockChars_Text (const iBlock *fontData, int height, enum iTextBlockMode, | 232 | iString * renderBlockChars_Text (const iBlock *fontData, int height, enum iTextBlockMode, |
231 | const iString *text); | 233 | const iString *text); |
232 | 234 | ||
235 | iRegExp * makeAnsiEscapePattern_Text (void); | ||
236 | |||
233 | /*-----------------------------------------------------------------------------------------------*/ | 237 | /*-----------------------------------------------------------------------------------------------*/ |
234 | 238 | ||
235 | iDeclareType(TextBuf) | 239 | iDeclareType(TextBuf) |