From f14288f73a2633947a22f2749dbeef3fe84aa37d Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Thu, 27 Mar 2014 11:12:29 +0000 Subject: [PATCH 01/25] Added some convenience functions to NSTextField subclass to enable adding of hyper links to existing control content. --- NSTextFieldHyperlinks/AppDelegate.h | 3 +- NSTextFieldHyperlinks/AppDelegate.m | 14 +- NSTextFieldHyperlinks/HyperlinkTextField.h | 29 + NSTextFieldHyperlinks/HyperlinkTextField.m | 48 + NSTextFieldHyperlinks/en.lproj/MainMenu.xib | 4095 ++++--------------- 5 files changed, 799 insertions(+), 3390 deletions(-) diff --git a/NSTextFieldHyperlinks/AppDelegate.h b/NSTextFieldHyperlinks/AppDelegate.h index 28d38b8..2b9b233 100644 --- a/NSTextFieldHyperlinks/AppDelegate.h +++ b/NSTextFieldHyperlinks/AppDelegate.h @@ -33,5 +33,6 @@ @property (assign) IBOutlet NSWindow *window; @property (weak) IBOutlet HyperlinkTextField *hyperlinkTextField; - +@property (weak) IBOutlet HyperlinkTextField *hyperlinkTextField2; +@property (weak) IBOutlet HyperlinkTextField *hyperlinkTextField3; @end diff --git a/NSTextFieldHyperlinks/AppDelegate.m b/NSTextFieldHyperlinks/AppDelegate.m index 46aa4f9..39082c9 100644 --- a/NSTextFieldHyperlinks/AppDelegate.m +++ b/NSTextFieldHyperlinks/AppDelegate.m @@ -37,17 +37,21 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification // Create hyperlink NSString *linkName = @"blog"; NSURL *url = [NSURL URLWithString:@"http://toomasvahter.wordpress.com"]; - NSMutableAttributedString *hyperlinkString = [[NSMutableAttributedString alloc] initWithString:linkName]; - [hyperlinkString beginEditing]; - [hyperlinkString addAttribute:NSLinkAttributeName value:url range:NSMakeRange(0, [hyperlinkString length])]; - [hyperlinkString addAttribute:NSForegroundColorAttributeName value:[NSColor blueColor] range:NSMakeRange(0, [hyperlinkString length])]; - [hyperlinkString endEditing]; + NSAttributedString *hyperlinkString = [self.hyperlinkTextField hyperlink:linkName toURL:url]; [resultString appendAttributedString:hyperlinkString]; NSString *plainString = @". Some pretty interesting posts."; [resultString appendAttributedString:[[NSAttributedString alloc] initWithString:plainString]]; [self.hyperlinkTextField setAttributedStringValue:resultString]; + + // Update existing textfield substring with hyperlink + self.hyperlinkTextField2.linkColor = [NSColor redColor]; + [self.hyperlinkTextField2 updateSubstring:@"blog" withHyperLinkToURL:url]; + + // Replace existing textfield content key with hyperlink + self.hyperlinkTextField3.linkColor = [NSColor greenColor]; + [self.hyperlinkTextField3 replaceSubstring:@"$key" withHyperLink:@"blog" toURL:url]; } @end diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.h b/NSTextFieldHyperlinks/HyperlinkTextField.h index 2cd5e24..3270192 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.h +++ b/NSTextFieldHyperlinks/HyperlinkTextField.h @@ -28,4 +28,33 @@ #import @interface HyperlinkTextField : NSTextField + +/*! + + Link color + + */ +@property (strong) NSColor *linkColor; + +/*! + + Update control substring with hyperlink to given URL + + */ +- (void)updateSubstring:(NSString *)linktext withHyperLinkToURL:(NSURL *)linkURL; + +/*! + + Update control substring with hyperlink to given URL + + */ +- (void)replaceSubstring:(NSString *)linkKey withHyperLink:(NSString *)linktext toURL:(NSURL *)linkURL; + +/*! + + Make hyperlink attributed string to given URL + + */ +- (NSAttributedString *)hyperlink:(NSString *)linktext toURL:(NSURL *)linkURL; + @end diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 424a7d6..6d41110 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -44,6 +44,7 @@ - (void)_hyperlinkTextFieldInit { [self setEditable:NO]; [self setSelectable:NO]; + _linkColor = [NSColor blueColor]; } @@ -150,4 +151,51 @@ - (void)mouseUp:(NSEvent *)theEvent } } +#pragma mark - +#pragma mark Substring value updating + +- (void)updateSubstring:(NSString *)linktext withHyperLinkToURL:(NSURL *)linkURL +{ + [self replaceSubstring:linktext withHyperLink:linktext toURL:linkURL]; +} + +- (void)replaceSubstring:(NSString *)linkKey withHyperLink:(NSString *)linktext toURL:(NSURL *)linkURL +{ + + // get substring + NSString *sourceString = self.stringValue; + NSRange linkrange = [sourceString rangeOfString:linkKey]; + if (linkrange.location == NSNotFound) { + return; + } + + // build the hyper link + NSAttributedString *hyperlinkString = [self hyperlink:linktext toURL:linkURL]; + + // get link prefix and suffix strings + NSString *linkPrefix = [sourceString substringToIndex:linkrange.location]; + NSString *linkSuffix = [sourceString substringFromIndex:linkrange.location + linkrange.length]; + + // build new attributed string containg the hyperlink + NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:linkPrefix]; + [attrString appendAttributedString:hyperlinkString]; + [attrString appendAttributedString:[[NSAttributedString alloc] initWithString:linkSuffix]]; + + // update the control attributed string value + [self setAttributedStringValue:attrString]; +} + +#pragma mark - +#pragma mark Link building + +- (NSAttributedString *)hyperlink:(NSString *)linktext toURL:(NSURL *)linkURL +{ + NSMutableAttributedString *hyperlinkString = [[NSMutableAttributedString alloc] initWithString:linktext]; + [hyperlinkString beginEditing]; + [hyperlinkString addAttribute:NSLinkAttributeName value:linkURL range:NSMakeRange(0, [hyperlinkString length])]; + [hyperlinkString addAttribute:NSForegroundColorAttributeName value:self.linkColor range:NSMakeRange(0, [hyperlinkString length])]; + [hyperlinkString endEditing]; + + return hyperlinkString; +} @end diff --git a/NSTextFieldHyperlinks/en.lproj/MainMenu.xib b/NSTextFieldHyperlinks/en.lproj/MainMenu.xib index 76073c3..08d6efb 100644 --- a/NSTextFieldHyperlinks/en.lproj/MainMenu.xib +++ b/NSTextFieldHyperlinks/en.lproj/MainMenu.xib @@ -1,3384 +1,711 @@ - - - - 1080 - 12C3006 - 2844 - 1187.34 - 625.00 - - com.apple.InterfaceBuilder.CocoaPlugin - 2844 - - - IBNSLayoutConstraint - NSCustomObject - NSMenu - NSMenuItem - NSTextField - NSTextFieldCell - NSView - NSWindowTemplate - - - com.apple.InterfaceBuilder.CocoaPlugin - - - PluginDependencyRecalculationVersion - - - - - NSApplication - - - FirstResponder - - - NSApplication - - - AMainMenu - - - - NSTextFieldHyperlinks - - 1048576 - 2147483647 - - NSImage - NSMenuCheckmark - - - NSImage - NSMenuMixedState - - submenuAction: - - NSTextFieldHyperlinks - - - - About NSTextFieldHyperlinks - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Preferences… - , - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Services - - 1048576 - 2147483647 - - - submenuAction: - - Services - - _NSServicesMenu - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Hide NSTextFieldHyperlinks - h - 1048576 - 2147483647 - - - - - - Hide Others - h - 1572864 - 2147483647 - - - - - - Show All - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Quit NSTextFieldHyperlinks - q - 1048576 - 2147483647 - - - - - _NSAppleMenu - - - - - File - - 1048576 - 2147483647 - - - submenuAction: - - File - - - - New - n - 1048576 - 2147483647 - - - - - - Open… - o - 1048576 - 2147483647 - - - - - - Open Recent - - 1048576 - 2147483647 - - - submenuAction: - - Open Recent - - - - Clear Menu - - 1048576 - 2147483647 - - - - - _NSRecentDocumentsMenu - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Close - w - 1048576 - 2147483647 - - - - - - Save… - s - 1048576 - 2147483647 - - - - - - Revert to Saved - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Page Setup... - P - 1179648 - 2147483647 - - - - - - - Print… - p - 1048576 - 2147483647 - - - - - - - - - Edit - - 1048576 - 2147483647 - - - submenuAction: - - Edit - - - - Undo - z - 1048576 - 2147483647 - - - - - - Redo - Z - 1179648 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Cut - x - 1048576 - 2147483647 - - - - - - Copy - c - 1048576 - 2147483647 - - - - - - Paste - v - 1048576 - 2147483647 - - - - - - Paste and Match Style - V - 1572864 - 2147483647 - - - - - - Delete - - 1048576 - 2147483647 - - - - - - Select All - a - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Find - - 1048576 - 2147483647 - - - submenuAction: - - Find - - - - Find… - f - 1048576 - 2147483647 - - - 1 - - - - Find and Replace… - f - 1572864 - 2147483647 - - - 12 - - - - Find Next - g - 1048576 - 2147483647 - - - 2 - - - - Find Previous - G - 1179648 - 2147483647 - - - 3 - - - - Use Selection for Find - e - 1048576 - 2147483647 - - - 7 - - - - Jump to Selection - j - 1048576 - 2147483647 - - - - - - - - - Spelling and Grammar - - 1048576 - 2147483647 - - - submenuAction: - - Spelling and Grammar - - - - Show Spelling and Grammar - : - 1048576 - 2147483647 - - - - - - Check Document Now - ; - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Check Spelling While Typing - - 1048576 - 2147483647 - - - - - - Check Grammar With Spelling - - 1048576 - 2147483647 - - - - - - Correct Spelling Automatically - - 2147483647 - - - - - - - - - Substitutions - - 1048576 - 2147483647 - - - submenuAction: - - Substitutions - - - - Show Substitutions - - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Smart Copy/Paste - f - 1048576 - 2147483647 - - - 1 - - - - Smart Quotes - g - 1048576 - 2147483647 - - - 2 - - - - Smart Dashes - - 2147483647 - - - - - - Smart Links - G - 1179648 - 2147483647 - - - 3 - - - - Text Replacement - - 2147483647 - - - - - - - - - Transformations - - 2147483647 - - - submenuAction: - - Transformations - - - - Make Upper Case - - 2147483647 - - - - - - Make Lower Case - - 2147483647 - - - - - - Capitalize - - 2147483647 - - - - - - - - - Speech - - 1048576 - 2147483647 - - - submenuAction: - - Speech - - - - Start Speaking - - 1048576 - 2147483647 - - - - - - Stop Speaking - - 1048576 - 2147483647 - - - - - - - - - - - - Format - - 2147483647 - - - submenuAction: - - Format - - - - Font - - 2147483647 - - - submenuAction: - - Font - - - - Show Fonts - t - 1048576 - 2147483647 - - - - - - Bold - b - 1048576 - 2147483647 - - - 2 - - - - Italic - i - 1048576 - 2147483647 - - - 1 - - - - Underline - u - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Bigger - + - 1048576 - 2147483647 - - - 3 - - - - Smaller - - - 1048576 - 2147483647 - - - 4 - - - - YES - YES - - - 2147483647 - - - - - - Kern - - 2147483647 - - - submenuAction: - - Kern - - - - Use Default - - 2147483647 - - - - - - Use None - - 2147483647 - - - - - - Tighten - - 2147483647 - - - - - - Loosen - - 2147483647 - - - - - - - - - Ligatures - - 2147483647 - - - submenuAction: - - Ligatures - - - - Use Default - - 2147483647 - - - - - - Use None - - 2147483647 - - - - - - Use All - - 2147483647 - - - - - - - - - Baseline - - 2147483647 - - - submenuAction: - - Baseline - - - - Use Default - - 2147483647 - - - - - - Superscript - - 2147483647 - - - - - - Subscript - - 2147483647 - - - - - - Raise - - 2147483647 - - - - - - Lower - - 2147483647 - - - - - - - - - YES - YES - - - 2147483647 - - - - - - Show Colors - C - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Copy Style - c - 1572864 - 2147483647 - - - - - - Paste Style - v - 1572864 - 2147483647 - - - - - _NSFontMenu - - - - - Text - - 2147483647 - - - submenuAction: - - Text - - - - Align Left - { - 1048576 - 2147483647 - - - - - - Center - | - 1048576 - 2147483647 - - - - - - Justify - - 2147483647 - - - - - - Align Right - } - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Writing Direction - - 2147483647 - - - submenuAction: - - Writing Direction - - - - YES - Paragraph - - 2147483647 - - - - - - CURlZmF1bHQ - - 2147483647 - - - - - - CUxlZnQgdG8gUmlnaHQ - - 2147483647 - - - - - - CVJpZ2h0IHRvIExlZnQ - - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - YES - Selection - - 2147483647 - - - - - - CURlZmF1bHQ - - 2147483647 - - - - - - CUxlZnQgdG8gUmlnaHQ - - 2147483647 - - - - - - CVJpZ2h0IHRvIExlZnQ - - 2147483647 - - - - - - - - - YES - YES - - - 2147483647 - - - - - - Show Ruler - - 2147483647 - - - - - - Copy Ruler - c - 1310720 - 2147483647 - - - - - - Paste Ruler - v - 1310720 - 2147483647 - - - - - - - - - - - - View - - 1048576 - 2147483647 - - - submenuAction: - - View - - - - Show Toolbar - t - 1572864 - 2147483647 - - - - - - Customize Toolbar… - - 1048576 - 2147483647 - - - - - - - - - Window - - 1048576 - 2147483647 - - - submenuAction: - - Window - - - - Minimize - m - 1048576 - 2147483647 - - - - - - Zoom - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Bring All to Front - - 1048576 - 2147483647 - - - - - _NSWindowsMenu - - - - - Help - - 2147483647 - - - submenuAction: - - Help - - - - NSTextFieldHyperlinks Help - ? - 1048576 - 2147483647 - - - - - _NSHelpMenu - - - - _NSMainMenu - - - 15 - 2 - {{335, 390}, {244, 57}} - 1954021376 - NSTextFieldHyperlinks - NSWindow - - - - - 256 - - - - 258 - {{17, 20}, {210, 17}} - - - _NS:1535 - YES - - 67108928 - 272632064 - Label - - LucidaGrande - 13 - 1044 - - _NS:1535 - - - 6 - System - controlColor - - 3 - MC42NjY2NjY2NjY3AA - - - - 6 - System - controlTextColor - - 3 - MAA - - - - NO - - - {244, 57} - - - - - {{0, 0}, {1440, 878}} - {10000000000000, 10000000000000} - YES - - - AppDelegate - - - NSFontManager - - - - - - - terminate: - - - - 449 - - - - orderFrontStandardAboutPanel: - - - - 142 - - - - delegate - - - - 495 - - - - performMiniaturize: - - - - 37 - - - - arrangeInFront: - - - - 39 - - - - print: - - - - 86 - - - - runPageLayout: - - - - 87 - - - - clearRecentDocuments: - - - - 127 - - - - performClose: - - - - 193 - - - - toggleContinuousSpellChecking: - - - - 222 - - - - undo: - - - - 223 - - - - copy: - - - - 224 - - - - checkSpelling: - - - - 225 - - - - paste: - - - - 226 - - - - stopSpeaking: - - - - 227 - - - - cut: - - - - 228 - - - - showGuessPanel: - - - - 230 - - - - redo: - - - - 231 - - - - selectAll: - - - - 232 - - - - startSpeaking: - - - - 233 - - - - delete: - - - - 235 - - - - performZoom: - - - - 240 - - - - performFindPanelAction: - - - - 241 - - - - centerSelectionInVisibleArea: - - - - 245 - - - - toggleGrammarChecking: - - - - 347 - - - - toggleSmartInsertDelete: - - - - 355 - - - - toggleAutomaticQuoteSubstitution: - - - - 356 - - - - toggleAutomaticLinkDetection: - - - - 357 - - - - saveDocument: - - - - 362 - - - - revertDocumentToSaved: - - - - 364 - - - - runToolbarCustomizationPalette: - - - - 365 - - - - toggleToolbarShown: - - - - 366 - - - - hide: - - - - 367 - - - - hideOtherApplications: - - - - 368 - - - - unhideAllApplications: - - - - 370 - - - - newDocument: - - - - 373 - - - - openDocument: - - - - 374 - - - - raiseBaseline: - - - - 426 - - - - lowerBaseline: - - - - 427 - - - - copyFont: - - - - 428 - - - - subscript: - - - - 429 - - - - superscript: - - - - 430 - - - - tightenKerning: - - - - 431 - - - - underline: - - - - 432 - - - - orderFrontColorPanel: - - - - 433 - - - - useAllLigatures: - - - - 434 - - - - loosenKerning: - - - - 435 - - - - pasteFont: - - - - 436 - - - - unscript: - - - - 437 - - - - useStandardKerning: - - - - 438 - - - - useStandardLigatures: - - - - 439 - - - - turnOffLigatures: - - - - 440 - - - - turnOffKerning: - - - - 441 - - - - toggleAutomaticSpellingCorrection: - - - - 456 - - - - orderFrontSubstitutionsPanel: - - - - 458 - - - - toggleAutomaticDashSubstitution: - - - - 461 - - - - toggleAutomaticTextReplacement: - - - - 463 - - - - uppercaseWord: - - - - 464 - - - - capitalizeWord: - - - - 467 - - - - lowercaseWord: - - - - 468 - - - - pasteAsPlainText: - - - - 486 - - - - performFindPanelAction: - - - - 487 - - - - performFindPanelAction: - - - - 488 - - - - performFindPanelAction: - - - - 489 - - - - showHelp: - - - - 493 - - - - alignCenter: - - - - 518 - - - - pasteRuler: - - - - 519 - - - - toggleRuler: - - - - 520 - - - - alignRight: - - - - 521 - - - - copyRuler: - - - - 522 - - - - alignJustified: - - - - 523 - - - - alignLeft: - - - - 524 - - - - makeBaseWritingDirectionNatural: - - - - 525 - - - - makeBaseWritingDirectionLeftToRight: - - - - 526 - - - - makeBaseWritingDirectionRightToLeft: - - - - 527 - - - - makeTextWritingDirectionNatural: - - - - 528 - - - - makeTextWritingDirectionLeftToRight: - - - - 529 - - - - makeTextWritingDirectionRightToLeft: - - - - 530 - - - - performFindPanelAction: - - - - 535 - - - - addFontTrait: - - - - 421 - - - - addFontTrait: - - - - 422 - - - - modifyFont: - - - - 423 - - - - orderFrontFontPanel: - - - - 424 - - - - modifyFont: - - - - 425 - - - - window - - - - 532 - - - - hyperlinkTextField - - - - 541 - - - - - - 0 - - - - - - -2 - - - File's Owner - - - -1 - - - First Responder - - - -3 - - - Application - - - 29 - - - - - - - - - - - - - - 19 - - - - - - - - 56 - - - - - - - - 217 - - - - - - - - 83 - - - - - - - - 81 - - - - - - - - - - - - - - - - - 75 - - - - - 78 - - - - - 72 - - - - - 82 - - - - - 124 - - - - - - - - 77 - - - - - 73 - - - - - 79 - - - - - 112 - - - - - 74 - - - - - 125 - - - - - - - - 126 - - - - - 205 - - - - - - - - - - - - - - - - - - - - - - 202 - - - - - 198 - - - - - 207 - - - - - 214 - - - - - 199 - - - - - 203 - - - - - 197 - - - - - 206 - - - - - 215 - - - - - 218 - - - - - - - - 216 - - - - - - - - 200 - - - - - - - - - - - - - 219 - - - - - 201 - - - - - 204 - - - - - 220 - - - - - - - - - - - - - 213 - - - - - 210 - - - - - 221 - - - - - 208 - - - - - 209 - - - - - 57 - - - - - - - - - - - - - - - - - - 58 - - - - - 134 - - - - - 150 - - - - - 136 - - - - - 144 - - - - - 129 - - - - - 143 - - - - - 236 - - - - - 131 - - - - - - - - 149 - - - - - 145 - - - - - 130 - - - - - 24 - - - - - - - - - - - 92 - - - - - 5 - - - - - 239 - - - - - 23 - - - - - 295 - - - - - - - - 296 - - - - - - - - - 297 - - - - - 298 - - - - - 211 - - - - - - - - 212 - - - - - - - - - 195 - - - - - 196 - - - - - 346 - - - - - 348 - - - - - - - - 349 - - - - - - - - - - - - - - 350 - - - - - 351 - - - - - 354 - - - - - 371 - - - - - - - - 372 - - - - - - 4 - 0 - - 4 - 1 - - 20 - - 1000 - - 9 - 40 - 3 - - - - 6 - 0 - - 6 - 1 - - 20 - - 1000 - - 8 - 29 - 3 - - - - 5 - 0 - - 5 - 1 - - 20 - - 1000 - - 8 - 29 - 3 - - - - 3 - 0 - - 3 - 1 - - 20 - - 1000 - - 9 - 40 - 3 - - - - - - 375 - - - - - - - - 376 - - - - - - - - - 377 - - - - - - - - 388 - - - - - - - - - - - - - - - - - - - - - - - 389 - - - - - 390 - - - - - 391 - - - - - 392 - - - - - 393 - - - - - 394 - - - - - 395 - - - - - 396 - - - - - 397 - - - - - - - - 398 - - - - - - - - 399 - - - - - - - - 400 - - - - - 401 - - - - - 402 - - - - - 403 - - - - - 404 - - - - - 405 - - - - - - - - - - - - 406 - - - - - 407 - - - - - 408 - - - - - 409 - - - - - 410 - - - - - 411 - - - - - - - - - - 412 - - - - - 413 - - - - - 414 - - - - - 415 - - - - - - - - - - - 416 - - - - - 417 - - - - - 418 - - - - - 419 - - - - - 420 - - - - - 450 - - - - - - - - 451 - - - - - - - - - - 452 - - - - - 453 - - - - - 454 - - - - - 457 - - - - - 459 - - - - - 460 - - - - - 462 - - - - - 465 - - - - - 466 - - - - - 485 - - - - - 490 - - - - - - - - 491 - - - - - - - - 492 - - - - - 494 - - - - - 496 - - - - - - - - 497 - - - - - - - - - - - - - - - - - 498 - - - - - 499 - - - - - 500 - - - - - 501 - - - - - 502 - - - - - 503 - - - - - - - - 504 - - - - - 505 - - - - - 506 - - - - - 507 - - - - - 508 - - - - - - - - - - - - - - - - 509 - - - - - 510 - - - - - 511 - - - - - 512 - - - - - 513 - - - - - 514 - - - - - 515 - - - - - 516 - - - - - 517 - - - - - 534 - - - - - 536 - - - - - - - - 537 - - - - - 545 - - - - - 546 - - - - - 547 - - - - - 548 - - - - - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{380, 496}, {480, 360}} - - - - - - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - HyperlinkTextField - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - - - 548 - - - - - AppDelegate - NSObject - - HyperlinkTextField - NSWindow - - - - hyperlinkTextField - HyperlinkTextField - - - window - NSWindow - - - - IBProjectSource - ./Classes/AppDelegate.h - - - - HyperlinkTextField - NSTextField - - IBProjectSource - ./Classes/HyperlinkTextField.h - - - - NSLayoutConstraint - NSObject - - IBProjectSource - ./Classes/NSLayoutConstraint.h - - - - - 0 - IBCocoaFramework - YES - 3 - - {11, 11} - {10, 3} - - YES - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a630f5aac9b5fa0e4d094335ebce787d14b27727 Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Fri, 14 Aug 2015 21:32:08 +0100 Subject: [PATCH 02/25] Added some NSString and NSAttributedString convenience methods. --- NSTextFieldHyperlinks/HyperlinkTextField.h | 17 ++++++ NSTextFieldHyperlinks/HyperlinkTextField.m | 64 ++++++++++++++++++---- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.h b/NSTextFieldHyperlinks/HyperlinkTextField.h index 3270192..7464bde 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.h +++ b/NSTextFieldHyperlinks/HyperlinkTextField.h @@ -58,3 +58,20 @@ - (NSAttributedString *)hyperlink:(NSString *)linktext toURL:(NSURL *)linkURL; @end + +@interface NSString (HyperTextField) +- (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor *)linkColor; +@end + +@interface NSAttributedString (HyperTextField) + +- (NSAttributedString *)htf_replaceSubstringWithHyperLink:(NSString *)linktext + toURL:(NSURL *)linkURL + linkColor:(NSColor *)linkColor; + +- (NSAttributedString *)htf_replaceSubstring:(NSString *)linkKey + withHyperLink:(NSString *)linktext + toURL:(NSURL *)linkURL + linkColor:(NSColor *)linkColor; +@end + diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 6d41110..4c26680 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -161,7 +161,6 @@ - (void)updateSubstring:(NSString *)linktext withHyperLinkToURL:(NSURL *)linkURL - (void)replaceSubstring:(NSString *)linkKey withHyperLink:(NSString *)linktext toURL:(NSURL *)linkURL { - // get substring NSString *sourceString = self.stringValue; NSRange linkrange = [sourceString rangeOfString:linkKey]; @@ -169,17 +168,10 @@ - (void)replaceSubstring:(NSString *)linkKey withHyperLink:(NSString *)linktext return; } - // build the hyper link - NSAttributedString *hyperlinkString = [self hyperlink:linktext toURL:linkURL]; - - // get link prefix and suffix strings - NSString *linkPrefix = [sourceString substringToIndex:linkrange.location]; - NSString *linkSuffix = [sourceString substringFromIndex:linkrange.location + linkrange.length]; // build new attributed string containg the hyperlink - NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:linkPrefix]; - [attrString appendAttributedString:hyperlinkString]; - [attrString appendAttributedString:[[NSAttributedString alloc] initWithString:linkSuffix]]; + NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:sourceString]; + attrString = [attrString htf_replaceSubstring:linkKey withHyperLink:linktext toURL:linkURL linkColor:self.linkColor]; // update the control attributed string value [self setAttributedStringValue:attrString]; @@ -190,12 +182,60 @@ - (void)replaceSubstring:(NSString *)linkKey withHyperLink:(NSString *)linktext - (NSAttributedString *)hyperlink:(NSString *)linktext toURL:(NSURL *)linkURL { - NSMutableAttributedString *hyperlinkString = [[NSMutableAttributedString alloc] initWithString:linktext]; + NSAttributedString *hyperlinkString = [linktext htf_hyperlinkToURL:linkURL linkColor:self.linkColor]; + + return hyperlinkString; +} +@end + + +@implementation NSString (HyperTextField) + +- (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor *)linkColor +{ + NSMutableAttributedString *hyperlinkString = [[NSMutableAttributedString alloc] initWithString:self]; [hyperlinkString beginEditing]; [hyperlinkString addAttribute:NSLinkAttributeName value:linkURL range:NSMakeRange(0, [hyperlinkString length])]; - [hyperlinkString addAttribute:NSForegroundColorAttributeName value:self.linkColor range:NSMakeRange(0, [hyperlinkString length])]; + [hyperlinkString addAttribute:NSForegroundColorAttributeName value:linkColor range:NSMakeRange(0, [hyperlinkString length])]; [hyperlinkString endEditing]; return hyperlinkString; } @end + +@implementation NSAttributedString (HyperTextField) + +- (NSAttributedString *)htf_replaceSubstringWithHyperLink:(NSString *)linktext + toURL:(NSURL *)linkURL + linkColor:(NSColor *)linkColor +{ + return [self htf_replaceSubstring:linktext withHyperLink:linktext toURL:linkURL linkColor:linkColor]; +} + +- (NSAttributedString *)htf_replaceSubstring:(NSString *)linkKey + withHyperLink:(NSString *)linktext + toURL:(NSURL *)linkURL + linkColor:(NSColor *)linkColor +{ + // get substring + NSString *sourceString = self.string; + NSRange linkrange = [sourceString rangeOfString:linkKey]; + if (linkrange.location == NSNotFound) { + return nil; + } + + // build the hyper link + NSAttributedString *hyperlinkString = [linktext htf_hyperlinkToURL:linkURL linkColor:linkColor]; + + // get link prefix and suffix strings + NSString *linkPrefix = [sourceString substringToIndex:linkrange.location]; + NSString *linkSuffix = [sourceString substringFromIndex:linkrange.location + linkrange.length]; + + // build new attributed string containg the hyperlink + NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:linkPrefix]; + [attrString appendAttributedString:hyperlinkString]; + [attrString appendAttributedString:[[NSAttributedString alloc] initWithString:linkSuffix]]; + + return attrString; +} +@end From 4af46dd120d165b6336fa7fd3af5603da354d211 Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Mon, 31 Aug 2015 17:21:37 +0100 Subject: [PATCH 03/25] Support multiple hyperlinks within a single textfield. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 4c26680..d0dced1 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -223,18 +223,19 @@ - (NSAttributedString *)htf_replaceSubstring:(NSString *)linkKey if (linkrange.location == NSNotFound) { return nil; } + CGFloat linkEndLocation = linkrange.location + linkrange.length; // build the hyper link NSAttributedString *hyperlinkString = [linktext htf_hyperlinkToURL:linkURL linkColor:linkColor]; // get link prefix and suffix strings - NSString *linkPrefix = [sourceString substringToIndex:linkrange.location]; - NSString *linkSuffix = [sourceString substringFromIndex:linkrange.location + linkrange.length]; + NSAttributedString *linkPrefix = [self attributedSubstringFromRange:NSMakeRange(0, linkrange.location)]; + NSAttributedString *linkSuffix = [self attributedSubstringFromRange:NSMakeRange(linkEndLocation, sourceString.length - linkEndLocation)]; // build new attributed string containg the hyperlink - NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:linkPrefix]; + NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithAttributedString:linkPrefix]; [attrString appendAttributedString:hyperlinkString]; - [attrString appendAttributedString:[[NSAttributedString alloc] initWithString:linkSuffix]]; + [attrString appendAttributedString:linkSuffix]; return attrString; } From a739f2a95bda8535144752898de176c720d5ff42 Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Fri, 22 Jan 2016 13:39:06 +0000 Subject: [PATCH 04/25] Return copy of receiver rather than nil if link key not found. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index d0dced1..90a077e 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -221,7 +221,7 @@ - (NSAttributedString *)htf_replaceSubstring:(NSString *)linkKey NSString *sourceString = self.string; NSRange linkrange = [sourceString rangeOfString:linkKey]; if (linkrange.location == NSNotFound) { - return nil; + return self; } CGFloat linkEndLocation = linkrange.location + linkrange.length; From 69b59a5f07a7d7c4635d805e3b9ce75b0ce25f1f Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Wed, 2 Mar 2016 10:16:46 +0000 Subject: [PATCH 05/25] Added convenience stringValue setter method. --- NSTextFieldHyperlinks/HyperlinkTextField.h | 12 ++++++++++++ NSTextFieldHyperlinks/HyperlinkTextField.m | 17 +++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.h b/NSTextFieldHyperlinks/HyperlinkTextField.h index 7464bde..3ed2211 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.h +++ b/NSTextFieldHyperlinks/HyperlinkTextField.h @@ -27,6 +27,11 @@ #import +// link option keys +extern NSString * HTLinkOption; +extern NSString * HTUrlOption; +extern NSString * HTColorOption; + @interface HyperlinkTextField : NSTextField /*! @@ -36,6 +41,13 @@ */ @property (strong) NSColor *linkColor; +/*! + + Set control string with link options to define hyperlink. + + */ +- (void)setStringValue:(NSString *)stringValue linkOptions:(NSArray *>*)options; + /*! Update control substring with hyperlink to given URL diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 90a077e..6bd89d4 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -25,6 +25,10 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +NSString * HTLinkOption = @"link"; +NSString * HTUrlOption = @"url"; +NSString * HTColorOption = @"color"; + #import "HyperlinkTextField.h" @interface HyperlinkTextField () @@ -186,6 +190,19 @@ - (NSAttributedString *)hyperlink:(NSString *)linktext toURL:(NSURL *)linkURL return hyperlinkString; } + +- (void)setStringValue:(nonnull NSString *)stringValue linkOptions:(nonnull NSArray *>*)options +{ + NSAttributedString *hyperlinkText = [[NSAttributedString alloc] initWithString:stringValue]; + for (NSDictionary *option in options) { + hyperlinkText = [hyperlinkText htf_replaceSubstringWithHyperLink:option[HTLinkOption] + toURL:option[HTUrlOption] + linkColor:option[HTColorOption]]; + } + + self.attributedStringValue = hyperlinkText; +} + @end From 7cb980c60c38ff57ec5054c5ec2484b6ea71766a Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Tue, 19 Apr 2016 09:28:17 +0100 Subject: [PATCH 06/25] Asset the URLS as it is all to easy to pass in string values in when composing from a dictionary representation. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 6bd89d4..76de7a1 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -210,6 +210,9 @@ @implementation NSString (HyperTextField) - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor *)linkColor { + // contract + NSAssert([linkURL isKindOfClass:[NSURL class]], @"invalid class"); + NSMutableAttributedString *hyperlinkString = [[NSMutableAttributedString alloc] initWithString:self]; [hyperlinkString beginEditing]; [hyperlinkString addAttribute:NSLinkAttributeName value:linkURL range:NSMakeRange(0, [hyperlinkString length])]; From a2f57f5c33c7bad58795486fa3c5e8ae1f61c05c Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Thu, 17 Nov 2016 13:34:02 +0000 Subject: [PATCH 07/25] Default to native hyperlink support. The custom hyperlink implementation works tolerably well for short strings but once we have wrapped text the computed cursor rects can be inaccurate. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 62 +++++++++++++++++----- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 76de7a1..0d3ea80 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -31,6 +31,8 @@ #import "HyperlinkTextField.h" +static BOOL m_useNativeHyperlinkImplementation = YES; + @interface HyperlinkTextField () @property (nonatomic, readonly) NSArray *hyperlinkInfos; @property (nonatomic, readonly) NSTextView *textView; @@ -44,18 +46,43 @@ - (void)_resetHyperlinkCursorRects; @implementation HyperlinkTextField +/* + + The non native implementation attempts to improve on the native implementation. + + The main failing of the default implementation is that it uses the selection cursor (the hand cursor appears if the field is selected). + + The non native implementation works tolerably well in some cases but can fail to compute the correct link cursor rect for longer strings. + + TODO: in order to improve on the non native performance we could display -textView in a popup window nad look for layout issues. + + */ ++ (void)setUseNativeHyperlinkImplementation:(BOOL)value +{ + m_useNativeHyperlinkImplementation = value; +} + ++ (BOOL)useNativeHyperlinkImplementation +{ + return m_useNativeHyperlinkImplementation; +} + - (void)_hyperlinkTextFieldInit { [self setEditable:NO]; - [self setSelectable:NO]; _linkColor = [NSColor blueColor]; + + if (m_useNativeHyperlinkImplementation) { + // An NSTextView based implementation might give a better result but would require a good deal of refactoring. + // see https://developer.apple.com/library/content/qa/qa1487/_index.html + [self setAllowsEditingTextAttributes: YES]; + [self setSelectable: YES]; + } } - - (id)initWithFrame:(NSRect)frame { - if ((self = [super initWithFrame:frame])) - { + if ((self = [super initWithFrame:frame])) { [self _hyperlinkTextFieldInit]; } @@ -65,8 +92,7 @@ - (id)initWithFrame:(NSRect)frame - (id)initWithCoder:(NSCoder *)coder { - if ((self = [super initWithCoder:coder])) - { + if ((self = [super initWithCoder:coder])) { [self _hyperlinkTextFieldInit]; } @@ -77,14 +103,15 @@ - (id)initWithCoder:(NSCoder *)coder - (void)resetCursorRects { [super resetCursorRects]; - [self _resetHyperlinkCursorRects]; + if (!m_useNativeHyperlinkImplementation) { + [self _resetHyperlinkCursorRects]; + } } - (void)_resetHyperlinkCursorRects { - for (NSDictionary *info in self.hyperlinkInfos) - { + for (NSDictionary *info in self.hyperlinkInfos) { [self addCursorRect:[[info objectForKey:kHyperlinkInfoRectKey] rectValue] cursor:[NSCursor pointingHandCursor]]; } } @@ -102,6 +129,7 @@ - (NSArray *)hyperlinkInfos { if (value) { + [textView.layoutManager ensureLayoutForTextContainer:textView.textContainer]; NSUInteger rectCount = 0; NSRectArray rectArray = [textView.layoutManager rectArrayForCharacterRange:range withinSelectedCharacterRange:range inTextContainer:textView.textContainer rectCount:&rectCount]; for (NSUInteger i = 0; i < rectCount; i++) @@ -121,8 +149,9 @@ - (NSTextView *)textView NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedStringValue]; NSFont *font = [attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL]; - if (!font) + if (!font) { [attributedString addAttribute:NSFontAttributeName value:self.font range:NSMakeRange(0, [attributedString length])]; + } NSRect textViewFrame = [self.cell titleRectForBounds:self.bounds]; NSTextView *textView = [[NSTextView alloc] initWithFrame:textViewFrame]; @@ -137,6 +166,11 @@ - (NSTextView *)textView - (void)mouseUp:(NSEvent *)theEvent { + if (m_useNativeHyperlinkImplementation) { + [super mouseUp:theEvent]; + return; + } + NSTextView *textView = self.textView; NSPoint localPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil]; NSUInteger index = [textView.layoutManager characterIndexForPoint:localPoint inTextContainer:textView.textContainer fractionOfDistanceBetweenInsertionPoints:NULL]; @@ -172,9 +206,10 @@ - (void)replaceSubstring:(NSString *)linkKey withHyperLink:(NSString *)linktext return; } + NSFont *font = self.font; // build new attributed string containg the hyperlink - NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:sourceString]; + NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:sourceString attributes:@{NSFontAttributeName : font}]; attrString = [attrString htf_replaceSubstring:linkKey withHyperLink:linktext toURL:linkURL linkColor:self.linkColor]; // update the control attributed string value @@ -210,6 +245,8 @@ @implementation NSString (HyperTextField) - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor *)linkColor { + NSFont *font = [NSFont controlContentFontOfSize:[NSFont systemFontSize]]; + // contract NSAssert([linkURL isKindOfClass:[NSURL class]], @"invalid class"); @@ -217,6 +254,7 @@ - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor * [hyperlinkString beginEditing]; [hyperlinkString addAttribute:NSLinkAttributeName value:linkURL range:NSMakeRange(0, [hyperlinkString length])]; [hyperlinkString addAttribute:NSForegroundColorAttributeName value:linkColor range:NSMakeRange(0, [hyperlinkString length])]; + [hyperlinkString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [hyperlinkString length])]; [hyperlinkString endEditing]; return hyperlinkString; @@ -252,7 +290,7 @@ - (NSAttributedString *)htf_replaceSubstring:(NSString *)linkKey NSAttributedString *linkPrefix = [self attributedSubstringFromRange:NSMakeRange(0, linkrange.location)]; NSAttributedString *linkSuffix = [self attributedSubstringFromRange:NSMakeRange(linkEndLocation, sourceString.length - linkEndLocation)]; - // build new attributed string containg the hyperlink + // build new attributed string containing the hyperlink NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithAttributedString:linkPrefix]; [attrString appendAttributedString:hyperlinkString]; [attrString appendAttributedString:linkSuffix]; From 31387faa98f28bd23e06f8c9ef5fe6d4f3b13d6b Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Thu, 17 Nov 2016 14:11:42 +0000 Subject: [PATCH 08/25] Comment updates. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 0d3ea80..7eeda6e 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -54,7 +54,8 @@ The main failing of the default implementation is that it uses the selection cur The non native implementation works tolerably well in some cases but can fail to compute the correct link cursor rect for longer strings. - TODO: in order to improve on the non native performance we could display -textView in a popup window nad look for layout issues. + TODO: in order to improve on the non native performance we could display -textView in a popup window and look for layout issues. I tried various + methods of overriding the current cursor but none were flicker free. */ + (void)setUseNativeHyperlinkImplementation:(BOOL)value @@ -108,10 +109,10 @@ - (void)resetCursorRects } } - - (void)_resetHyperlinkCursorRects { for (NSDictionary *info in self.hyperlinkInfos) { + // TODO: use tracking areas instead [self addCursorRect:[[info objectForKey:kHyperlinkInfoRectKey] rectValue] cursor:[NSCursor pointingHandCursor]]; } } From bf2af2cfae3f185ea45172fb2ef9de2b8db3224d Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Thu, 17 Nov 2016 16:37:37 +0000 Subject: [PATCH 09/25] Apply control font if none found when setting attributed string. If this is not done the control font can change when the control is selected - probably because of the field editor. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 7eeda6e..e4a53ed 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -161,6 +161,17 @@ - (NSTextView *)textView return textView; } +- (void)setAttributedStringValue:(NSAttributedString *)attributedString +{ + NSFont *font = [attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL]; + + if (!font) { + NSMutableAttributedString *s = [attributedString mutableCopy]; + [s addAttribute:NSFontAttributeName value:self.font range:NSMakeRange(0, [attributedString length])]; + attributedString = s; + } + [super setAttributedStringValue:attributedString]; +} #pragma mark - #pragma mark Mouse Events From 9af3120c3bf47effd5782a96a72ebf9c5f6e1399 Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Wed, 28 Dec 2016 14:30:16 +0000 Subject: [PATCH 10/25] Added convenience method. --- NSTextFieldHyperlinks/HyperlinkTextField.h | 7 +++++++ NSTextFieldHyperlinks/HyperlinkTextField.m | 15 +++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.h b/NSTextFieldHyperlinks/HyperlinkTextField.h index 3ed2211..7ca5486 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.h +++ b/NSTextFieldHyperlinks/HyperlinkTextField.h @@ -41,6 +41,13 @@ extern NSString * HTColorOption; */ @property (strong) NSColor *linkColor; +/*! + + Update control attributed string with link options to define hyperlink. + + */ +- (void)updateAttributedStringValueWithLinkOptions:(NSArray *>*)options; + /*! Set control string with link options to define hyperlink. diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index e4a53ed..d146889 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -238,15 +238,26 @@ - (NSAttributedString *)hyperlink:(NSString *)linktext toURL:(NSURL *)linkURL return hyperlinkString; } +- (void)updateAttributedStringValueWithLinkOptions:(NSArray *>*)options +{ + NSAttributedString *hyperlinkText = self.attributedStringValue; + for (NSDictionary *option in options) { + hyperlinkText = [hyperlinkText htf_replaceSubstringWithHyperLink:option[HTLinkOption] + toURL:option[HTUrlOption] + linkColor:option[HTColorOption]]; + } + self.attributedStringValue = hyperlinkText; +} + - (void)setStringValue:(nonnull NSString *)stringValue linkOptions:(nonnull NSArray *>*)options { - NSAttributedString *hyperlinkText = [[NSAttributedString alloc] initWithString:stringValue]; + NSFont *font = [NSFont controlContentFontOfSize:[NSFont systemFontSize]]; + NSAttributedString *hyperlinkText = [[NSAttributedString alloc] initWithString:stringValue attributes:@{NSFontAttributeName : font}]; for (NSDictionary *option in options) { hyperlinkText = [hyperlinkText htf_replaceSubstringWithHyperLink:option[HTLinkOption] toURL:option[HTUrlOption] linkColor:option[HTColorOption]]; } - self.attributedStringValue = hyperlinkText; } From 93cf21a17ba094779d4301c06adbb27c8034e515 Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Wed, 28 Dec 2016 15:13:18 +0000 Subject: [PATCH 11/25] Fix text redraw issue when using bindings. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 34 ++++++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index d146889..41d8b7b 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -143,7 +143,6 @@ - (NSArray *)hyperlinkInfos return [hyperlinkInfos count] ? hyperlinkInfos : nil; } - - (NSTextView *)textView { // Font used for displaying and frame calculations must match @@ -173,6 +172,36 @@ - (void)setAttributedStringValue:(NSAttributedString *)attributedString [super setAttributedStringValue:attributedString]; } +- (void)setStringValue:(NSString *)stringValue +{ + NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:stringValue attributes:@{NSFontAttributeName : self.font}]; + + [super setAttributedStringValue:attrString]; +} + +- (void)setObjectValue:(id)objectValue +{ + // when binding and using the native implementation the text rendering changes if the text is clicked and + // the attributed text does not have a font attribute defined. hence we enforce this. + if ([objectValue isKindOfClass:[NSString class]]) { + objectValue = [[NSAttributedString alloc] initWithString:objectValue attributes:@{NSFontAttributeName : self.font}]; + } + else if ([objectValue isKindOfClass:[NSAttributedString class]]) { + NSAttributedString *attrString = objectValue; + if (attrString.length > 0) { + NSFont *existingFont = [attrString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL]; + + if (!existingFont) { + NSMutableAttributedString *s = [attrString mutableCopy]; + [s addAttribute:NSFontAttributeName value:self.font range:NSMakeRange(0, [attrString length])]; + objectValue = s; + } + } + } + + [super setObjectValue:objectValue]; +} + #pragma mark - #pragma mark Mouse Events @@ -251,8 +280,7 @@ - (void)updateAttributedStringValueWithLinkOptions:(NSArray *>*)options { - NSFont *font = [NSFont controlContentFontOfSize:[NSFont systemFontSize]]; - NSAttributedString *hyperlinkText = [[NSAttributedString alloc] initWithString:stringValue attributes:@{NSFontAttributeName : font}]; + NSAttributedString *hyperlinkText = [[NSAttributedString alloc] initWithString:stringValue attributes:@{NSFontAttributeName : self.font}]; for (NSDictionary *option in options) { hyperlinkText = [hyperlinkText htf_replaceSubstringWithHyperLink:option[HTLinkOption] toURL:option[HTUrlOption] From 5d39f5bc71fd3e01883f26a05a3e5ed0989caa38 Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Thu, 5 Jan 2017 15:27:22 +0000 Subject: [PATCH 12/25] Try and turn hyperlink underlining off. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 1 + 1 file changed, 1 insertion(+) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 41d8b7b..1ee08a7 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -306,6 +306,7 @@ - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor * [hyperlinkString addAttribute:NSLinkAttributeName value:linkURL range:NSMakeRange(0, [hyperlinkString length])]; [hyperlinkString addAttribute:NSForegroundColorAttributeName value:linkColor range:NSMakeRange(0, [hyperlinkString length])]; [hyperlinkString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [hyperlinkString length])]; + [hyperlinkString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleNone) range:NSMakeRange(0, [hyperlinkString length])]; [hyperlinkString endEditing]; return hyperlinkString; From 2f1c2c8505e88ff2407cb0ece4a9599f92a544d9 Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Sun, 19 Feb 2017 11:56:37 +0000 Subject: [PATCH 13/25] Comment update --- NSTextFieldHyperlinks/HyperlinkTextField.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 1ee08a7..56a82d9 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -57,6 +57,11 @@ The main failing of the default implementation is that it uses the selection cur TODO: in order to improve on the non native performance we could display -textView in a popup window and look for layout issues. I tried various methods of overriding the current cursor but none were flicker free. + on 10.12 the default link style is enforced + http://stackoverflow.com/questions/39926951/color-attribute-is-ignored-in-nsattributedstring-with-nslinkattributename + + A proper solution involves using an NSTextView subclass as oppposed to NSTextField. + */ + (void)setUseNativeHyperlinkImplementation:(BOOL)value { From 0ca0d456526235f3f106f1f1db44bb58e90e308a Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Mon, 27 Mar 2017 12:11:08 +0100 Subject: [PATCH 14/25] Added convenience method for text with multiple hyperlinks. --- NSTextFieldHyperlinks/HyperlinkTextField.h | 1 + NSTextFieldHyperlinks/HyperlinkTextField.m | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.h b/NSTextFieldHyperlinks/HyperlinkTextField.h index 7ca5486..c03e1d9 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.h +++ b/NSTextFieldHyperlinks/HyperlinkTextField.h @@ -80,6 +80,7 @@ extern NSString * HTColorOption; @interface NSString (HyperTextField) - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor *)linkColor; +- (NSAttributedString *)htf_hyperlinkWithAttributes:(NSDictionary *)attributes linkOptions:(NSArray *>*)options; @end @interface NSAttributedString (HyperTextField) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 56a82d9..5317c52 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -283,14 +283,10 @@ - (void)updateAttributedStringValueWithLinkOptions:(NSArray *>*)options +- (void)setStringValue:(NSString *)stringValue linkOptions:(NSArray *>*)options { - NSAttributedString *hyperlinkText = [[NSAttributedString alloc] initWithString:stringValue attributes:@{NSFontAttributeName : self.font}]; - for (NSDictionary *option in options) { - hyperlinkText = [hyperlinkText htf_replaceSubstringWithHyperLink:option[HTLinkOption] - toURL:option[HTUrlOption] - linkColor:option[HTColorOption]]; - } + NSDictionary *attributes = @{NSFontAttributeName : self.font}; + NSAttributedString *hyperlinkText = [stringValue htf_hyperlinkWithAttributes:attributes linkOptions:options]; self.attributedStringValue = hyperlinkText; } @@ -316,6 +312,18 @@ - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor * return hyperlinkString; } + +- (NSAttributedString *)htf_hyperlinkWithAttributes:(NSDictionary *)attributes linkOptions:(NSArray *>*)options +{ + NSAttributedString *hyperlinkText = [[NSAttributedString alloc] initWithString:self attributes:attributes]; + for (NSDictionary *option in options) { + hyperlinkText = [hyperlinkText htf_replaceSubstringWithHyperLink:option[HTLinkOption] + toURL:option[HTUrlOption] + linkColor:option[HTColorOption]]; + } + return hyperlinkText; +} + @end @implementation NSAttributedString (HyperTextField) From 914dd73fa8d0c4de7d82d157dc3806a1e4791ef8 Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Fri, 7 Apr 2017 15:42:52 +0100 Subject: [PATCH 15/25] Added convenience method. --- NSTextFieldHyperlinks/HyperlinkTextField.h | 7 ++++ NSTextFieldHyperlinks/HyperlinkTextField.m | 42 ++++++++++++---------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.h b/NSTextFieldHyperlinks/HyperlinkTextField.h index c03e1d9..72f63ae 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.h +++ b/NSTextFieldHyperlinks/HyperlinkTextField.h @@ -55,6 +55,13 @@ extern NSString * HTColorOption; */ - (void)setStringValue:(NSString *)stringValue linkOptions:(NSArray *>*)options; +/*! + + Set control string with attributes and link options to define hyperlink. + + */ +- (void)setStringValue:(NSString *)stringValue attributes:(NSDictionary *)attributes linkOptions:(NSArray *>*)options; + /*! Update control substring with hyperlink to given URL diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 5317c52..b6e649e 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -165,6 +165,14 @@ - (NSTextView *)textView return textView; } +- (void)setStringValue:(NSString *)stringValue +{ + NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:stringValue attributes:@{NSFontAttributeName : self.font}]; + + [super setAttributedStringValue:attrString]; +} + + - (void)setAttributedStringValue:(NSAttributedString *)attributedString { NSFont *font = [attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL]; @@ -177,11 +185,16 @@ - (void)setAttributedStringValue:(NSAttributedString *)attributedString [super setAttributedStringValue:attributedString]; } -- (void)setStringValue:(NSString *)stringValue +- (void)setStringValue:(NSString *)stringValue linkOptions:(NSArray *>*)options { - NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:stringValue attributes:@{NSFontAttributeName : self.font}]; - - [super setAttributedStringValue:attrString]; + NSDictionary *attributes = @{NSFontAttributeName : self.font}; + [self setStringValue:stringValue attributes:attributes linkOptions:options]; +} + +- (void)setStringValue:(NSString *)stringValue attributes:(NSDictionary *)attributes linkOptions:(NSArray *>*)options +{ + NSAttributedString *hyperlinkText = [stringValue htf_hyperlinkWithAttributes:attributes linkOptions:options]; + self.attributedStringValue = hyperlinkText; } - (void)setObjectValue:(id)objectValue @@ -262,16 +275,6 @@ - (void)replaceSubstring:(NSString *)linkKey withHyperLink:(NSString *)linktext [self setAttributedStringValue:attrString]; } -#pragma mark - -#pragma mark Link building - -- (NSAttributedString *)hyperlink:(NSString *)linktext toURL:(NSURL *)linkURL -{ - NSAttributedString *hyperlinkString = [linktext htf_hyperlinkToURL:linkURL linkColor:self.linkColor]; - - return hyperlinkString; -} - - (void)updateAttributedStringValueWithLinkOptions:(NSArray *>*)options { NSAttributedString *hyperlinkText = self.attributedStringValue; @@ -283,11 +286,14 @@ - (void)updateAttributedStringValueWithLinkOptions:(NSArray *>*)options +#pragma mark - +#pragma mark Link building + +- (NSAttributedString *)hyperlink:(NSString *)linktext toURL:(NSURL *)linkURL { - NSDictionary *attributes = @{NSFontAttributeName : self.font}; - NSAttributedString *hyperlinkText = [stringValue htf_hyperlinkWithAttributes:attributes linkOptions:options]; - self.attributedStringValue = hyperlinkText; + NSAttributedString *hyperlinkString = [linktext htf_hyperlinkToURL:linkURL linkColor:self.linkColor]; + + return hyperlinkString; } @end From 41ef4c68b8fa69381fc11e0083f4471ca1e2ed7a Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Tue, 3 Oct 2017 13:34:44 +0100 Subject: [PATCH 16/25] Improve cursor handling --- NSTextFieldHyperlinks/HyperlinkTextField.h | 2 ++ NSTextFieldHyperlinks/HyperlinkTextField.m | 31 ++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.h b/NSTextFieldHyperlinks/HyperlinkTextField.h index 72f63ae..695c163 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.h +++ b/NSTextFieldHyperlinks/HyperlinkTextField.h @@ -34,6 +34,8 @@ extern NSString * HTColorOption; @interface HyperlinkTextField : NSTextField +@property (strong) NSCursor *cursor; + /*! Link color diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index b6e649e..96d4d6a 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -77,6 +77,7 @@ - (void)_hyperlinkTextFieldInit { [self setEditable:NO]; _linkColor = [NSColor blueColor]; + _cursor = [NSCursor arrowCursor]; if (m_useNativeHyperlinkImplementation) { // An NSTextView based implementation might give a better result but would require a good deal of refactoring. @@ -105,13 +106,25 @@ - (id)initWithCoder:(NSCoder *)coder return self; } - - (void)resetCursorRects { [super resetCursorRects]; if (!m_useNativeHyperlinkImplementation) { [self _resetHyperlinkCursorRects]; } + else { + [self addCursorRect:self.bounds cursor:self.cursor]; + } +} + +- (void)addCursorRect:(NSRect)rect cursor:(NSCursor *)object +{ + if (!m_useNativeHyperlinkImplementation) { + [super addCursorRect:rect cursor:object]; + } + else { + [super addCursorRect:rect cursor:self.cursor]; + } } - (void)_resetHyperlinkCursorRects @@ -223,6 +236,17 @@ - (void)setObjectValue:(id)objectValue #pragma mark - #pragma mark Mouse Events +- (void)mouseMoved:(NSEvent *)theEvent { + if (m_useNativeHyperlinkImplementation) { + // this is not a great solution as there is some cursor flickering but it is perhaps better than using the default I beam + if ([NSCursor currentSystemCursor] != self.cursor) { + [self.cursor set]; + } + return; + } + [super mouseMoved:theEvent]; +} + - (void)mouseUp:(NSEvent *)theEvent { if (m_useNativeHyperlinkImplementation) { @@ -311,9 +335,12 @@ - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor * NSMutableAttributedString *hyperlinkString = [[NSMutableAttributedString alloc] initWithString:self]; [hyperlinkString beginEditing]; [hyperlinkString addAttribute:NSLinkAttributeName value:linkURL range:NSMakeRange(0, [hyperlinkString length])]; + + // can no longer change the link style + // see https://stackoverflow.com/questions/39926951/color-attribute-is-ignored-in-nsattributedstring-with-nslinkattributename [hyperlinkString addAttribute:NSForegroundColorAttributeName value:linkColor range:NSMakeRange(0, [hyperlinkString length])]; - [hyperlinkString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [hyperlinkString length])]; [hyperlinkString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleNone) range:NSMakeRange(0, [hyperlinkString length])]; + [hyperlinkString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [hyperlinkString length])]; [hyperlinkString endEditing]; return hyperlinkString; From 6bbc17d1f9c6778fa2e2a51164faa5c14f5f74a6 Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Thu, 22 Feb 2018 11:00:51 +0000 Subject: [PATCH 17/25] Allow empty attributed string value. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 96d4d6a..5e338d4 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -188,7 +188,11 @@ - (void)setStringValue:(NSString *)stringValue - (void)setAttributedStringValue:(NSAttributedString *)attributedString { - NSFont *font = [attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL]; + NSFont *font = nil; + + if (attributedString.length > 0) { + font = [attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL]; + } if (!font) { NSMutableAttributedString *s = [attributedString mutableCopy]; From 4303f0c5e09f5f537ea083d565208d79fd868de7 Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Mon, 16 Apr 2018 16:45:30 +0100 Subject: [PATCH 18/25] Try and dispel link tool tips. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 5e338d4..3873076 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -78,7 +78,7 @@ - (void)_hyperlinkTextFieldInit [self setEditable:NO]; _linkColor = [NSColor blueColor]; _cursor = [NSCursor arrowCursor]; - + if (m_useNativeHyperlinkImplementation) { // An NSTextView based implementation might give a better result but would require a good deal of refactoring. // see https://developer.apple.com/library/content/qa/qa1487/_index.html @@ -340,6 +340,11 @@ - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor * [hyperlinkString beginEditing]; [hyperlinkString addAttribute:NSLinkAttributeName value:linkURL range:NSMakeRange(0, [hyperlinkString length])]; + // in some cases we see tootltips, which may be okay. + // however if are using links for internal app navigation via a custom scheme then it's not so desirable. + // using an empty tooltip seems to banish the tool tip. + [hyperlinkString addAttribute:NSToolTipAttributeName value:@"" range:NSMakeRange(0, [hyperlinkString length])]; + // can no longer change the link style // see https://stackoverflow.com/questions/39926951/color-attribute-is-ignored-in-nsattributedstring-with-nslinkattributename [hyperlinkString addAttribute:NSForegroundColorAttributeName value:linkColor range:NSMakeRange(0, [hyperlinkString length])]; From a5cc13619b5c7eb0ff937eeb35989f088339afe1 Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Sun, 29 Apr 2018 22:31:49 +0100 Subject: [PATCH 19/25] Check for empty link colour. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 3873076..2baa846 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -347,7 +347,9 @@ - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor * // can no longer change the link style // see https://stackoverflow.com/questions/39926951/color-attribute-is-ignored-in-nsattributedstring-with-nslinkattributename - [hyperlinkString addAttribute:NSForegroundColorAttributeName value:linkColor range:NSMakeRange(0, [hyperlinkString length])]; + if (linkColor) { + [hyperlinkString addAttribute:NSForegroundColorAttributeName value:linkColor range:NSMakeRange(0, [hyperlinkString length])]; + } [hyperlinkString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleNone) range:NSMakeRange(0, [hyperlinkString length])]; [hyperlinkString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [hyperlinkString length])]; [hyperlinkString endEditing]; From c6c172811b9d0283cff74ea762884df35fe51a1e Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Tue, 28 Aug 2018 15:09:08 +0100 Subject: [PATCH 20/25] Additional convenience methods --- NSTextFieldHyperlinks/HyperlinkTextField.h | 3 +++ NSTextFieldHyperlinks/HyperlinkTextField.m | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.h b/NSTextFieldHyperlinks/HyperlinkTextField.h index 695c163..5e6fd99 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.h +++ b/NSTextFieldHyperlinks/HyperlinkTextField.h @@ -88,12 +88,15 @@ extern NSString * HTColorOption; @end @interface NSString (HyperTextField) +- (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL; - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor *)linkColor; - (NSAttributedString *)htf_hyperlinkWithAttributes:(NSDictionary *)attributes linkOptions:(NSArray *>*)options; @end @interface NSAttributedString (HyperTextField) +- (NSAttributedString *)htf_replaceSubstringWithHyperLink:(NSString *)linktext toURL:(NSURL *)linkURL; + - (NSAttributedString *)htf_replaceSubstringWithHyperLink:(NSString *)linktext toURL:(NSURL *)linkURL linkColor:(NSColor *)linkColor; diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 2baa846..b77ff34 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -329,6 +329,11 @@ - (NSAttributedString *)hyperlink:(NSString *)linktext toURL:(NSURL *)linkURL @implementation NSString (HyperTextField) +- (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL +{ + return [self htf_hyperlinkToURL:linkURL linkColor:[NSColor blueColor]]; +} + - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor *)linkColor { NSFont *font = [NSFont controlContentFontOfSize:[NSFont systemFontSize]]; @@ -372,6 +377,11 @@ - (NSAttributedString *)htf_hyperlinkWithAttributes:(NSDictionary Date: Thu, 30 Aug 2018 10:16:14 +0100 Subject: [PATCH 21/25] Fix missing return value --- NSTextFieldHyperlinks/HyperlinkTextField.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index b77ff34..0ea2829 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -379,7 +379,7 @@ @implementation NSAttributedString (HyperTextField) - (NSAttributedString *)htf_replaceSubstringWithHyperLink:(NSString *)linktext toURL:(NSURL *)linkURL { - [self htf_replaceSubstring:linktext withHyperLink:linktext toURL:linkURL linkColor:[NSColor blueColor]]; + return [self htf_replaceSubstring:linktext withHyperLink:linktext toURL:linkURL linkColor:[NSColor blueColor]]; } - (NSAttributedString *)htf_replaceSubstringWithHyperLink:(NSString *)linktext From 14a45a7b5ee49e582779c876253e0cbbd73d498a Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Sun, 28 Oct 2018 22:29:35 +0000 Subject: [PATCH 22/25] On macOS 10.14 we seem to need to enforce the text field init in order to create reliable links. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 0ea2829..d163e4f 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -200,6 +200,9 @@ - (void)setAttributedStringValue:(NSAttributedString *)attributedString attributedString = s; } [super setAttributedStringValue:attributedString]; + + // enforcing the field init here seems to be necessary in some cases. + [self _hyperlinkTextFieldInit]; } - (void)setStringValue:(NSString *)stringValue linkOptions:(NSArray *>*)options From 5e33cf6e1beab5fea1a6acebcc35a6d7208aafdb Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Tue, 20 Nov 2018 13:58:53 +0000 Subject: [PATCH 23/25] Add explicit enabling of hyperlinks. --- NSTextFieldHyperlinks/HyperlinkTextField.h | 7 +++++++ NSTextFieldHyperlinks/HyperlinkTextField.m | 19 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.h b/NSTextFieldHyperlinks/HyperlinkTextField.h index 5e6fd99..d064bf5 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.h +++ b/NSTextFieldHyperlinks/HyperlinkTextField.h @@ -36,6 +36,13 @@ extern NSString * HTColorOption; @property (strong) NSCursor *cursor; +/*! + + Explicity enable and disable hyperlinks. + + */ +@property (assign) BOOL hyperlinksEnabled; + /*! Link color diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index d163e4f..506244a 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -82,8 +82,7 @@ - (void)_hyperlinkTextFieldInit if (m_useNativeHyperlinkImplementation) { // An NSTextView based implementation might give a better result but would require a good deal of refactoring. // see https://developer.apple.com/library/content/qa/qa1487/_index.html - [self setAllowsEditingTextAttributes: YES]; - [self setSelectable: YES]; + self.hyperlinksEnabled = YES; } } @@ -240,6 +239,22 @@ - (void)setObjectValue:(id)objectValue [super setObjectValue:objectValue]; } +- (void)setSelectable:(BOOL)selectable +{ + [super setSelectable:selectable]; +} + +- (void)setHyperlinksEnabled:(BOOL)enabled +{ + self.allowsEditingTextAttributes = enabled; + self.selectable = enabled; +} + +- (BOOL)hyperlinksEnabled +{ + return self.allowsEditingTextAttributes && self.selectable; +} + #pragma mark - #pragma mark Mouse Events From 1bf32618088a86a9da9dd7ff57b3348eff187103 Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Sun, 5 Apr 2020 21:10:02 +0100 Subject: [PATCH 24/25] Add convenience method and ensure that font is preserved when adding link. --- NSTextFieldHyperlinks/HyperlinkTextField.h | 1 + NSTextFieldHyperlinks/HyperlinkTextField.m | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.h b/NSTextFieldHyperlinks/HyperlinkTextField.h index d064bf5..73b9131 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.h +++ b/NSTextFieldHyperlinks/HyperlinkTextField.h @@ -97,6 +97,7 @@ extern NSString * HTColorOption; @interface NSString (HyperTextField) - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL; - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor *)linkColor; +- (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor *)linkColor font:(NSFont *)font; - (NSAttributedString *)htf_hyperlinkWithAttributes:(NSDictionary *)attributes linkOptions:(NSArray *>*)options; @end diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index 506244a..b5f5b4c 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -351,14 +351,21 @@ - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL { return [self htf_hyperlinkToURL:linkURL linkColor:[NSColor blueColor]]; } - + - (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor *)linkColor { - NSFont *font = [NSFont controlContentFontOfSize:[NSFont systemFontSize]]; - + return [self htf_hyperlinkToURL:linkURL linkColor:linkColor font:nil]; +} + +- (NSAttributedString *)htf_hyperlinkToURL:(NSURL *)linkURL linkColor:(NSColor *)linkColor font:(NSFont *)font +{ // contract NSAssert([linkURL isKindOfClass:[NSURL class]], @"invalid class"); + if (!font) { + font = [NSFont controlContentFontOfSize:[NSFont systemFontSize]]; + } + NSMutableAttributedString *hyperlinkString = [[NSMutableAttributedString alloc] initWithString:self]; [hyperlinkString beginEditing]; [hyperlinkString addAttribute:NSLinkAttributeName value:linkURL range:NSMakeRange(0, [hyperlinkString length])]; @@ -414,14 +421,20 @@ - (NSAttributedString *)htf_replaceSubstring:(NSString *)linkKey { // get substring NSString *sourceString = self.string; + if (!linkKey || linkKey.length == 0 ) linkKey = sourceString; + if (!linktext || linktext.length == 0 ) linktext = sourceString; NSRange linkrange = [sourceString rangeOfString:linkKey]; if (linkrange.location == NSNotFound) { return self; } CGFloat linkEndLocation = linkrange.location + linkrange.length; + NSFont *font = nil; + id attr = [self attribute:NSFontAttributeName atIndex:linkrange.location effectiveRange:nil]; + if ([attr isKindOfClass:NSFont.class]) font = attr; + // build the hyper link - NSAttributedString *hyperlinkString = [linktext htf_hyperlinkToURL:linkURL linkColor:linkColor]; + NSAttributedString *hyperlinkString = [linktext htf_hyperlinkToURL:linkURL linkColor:linkColor font:font]; // get link prefix and suffix strings NSAttributedString *linkPrefix = [self attributedSubstringFromRange:NSMakeRange(0, linkrange.location)]; From cf35100338b3824909bb8827848bcf67053fcc9e Mon Sep 17 00:00:00 2001 From: Jonathan Mitchell Date: Mon, 2 Nov 2020 21:35:24 +0000 Subject: [PATCH 25/25] Additional support for adding multiple hyperlinks. --- NSTextFieldHyperlinks/HyperlinkTextField.m | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/NSTextFieldHyperlinks/HyperlinkTextField.m b/NSTextFieldHyperlinks/HyperlinkTextField.m index b5f5b4c..8678a35 100644 --- a/NSTextFieldHyperlinks/HyperlinkTextField.m +++ b/NSTextFieldHyperlinks/HyperlinkTextField.m @@ -313,8 +313,15 @@ - (void)replaceSubstring:(NSString *)linkKey withHyperLink:(NSString *)linktext NSFont *font = self.font; - // build new attributed string containg the hyperlink - NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:sourceString attributes:@{NSFontAttributeName : font}]; + // get attributed string + NSAttributedString *attrString = nil; + if (!self.attributedStringValue) { + attrString = [[NSAttributedString alloc] initWithString:sourceString attributes:@{NSFontAttributeName : font}]; + } + else { + attrString = self.attributedStringValue; + } + attrString = [attrString htf_replaceSubstring:linkKey withHyperLink:linktext toURL:linkURL linkColor:self.linkColor]; // update the control attributed string value