KMFontWell.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. //
  2. // KMFontWell.swift
  3. // PDF Master
  4. //
  5. // Created by tangchao on 2023/11/7.
  6. //
  7. import Cocoa
  8. class KMFontWellCell: NSButtonCell {
  9. var textColor: NSColor?
  10. var hasTextColor: Bool = false
  11. private let FONTNAME_KEY = "fontName"
  12. private let FONTSIZE_KEY = "fontSize"
  13. private let TEXTCOLOR_KEY = "textColor"
  14. private let HASTEXTCOLOR_KEY = "hasTextColor"
  15. deinit {
  16. KMPrint("KMFontWellCell deinit.")
  17. }
  18. override init(textCell string: String) {
  19. super.init(textCell: string)
  20. self.commonInit()
  21. }
  22. required init(coder: NSCoder) {
  23. super.init(coder: coder)
  24. self.textColor = coder.decodeObject(forKey: TEXTCOLOR_KEY) as? NSColor
  25. self.hasTextColor = coder.decodeBool(forKey: HASTEXTCOLOR_KEY)
  26. self.commonInit()
  27. }
  28. func commonInit() {
  29. if self.textColor == nil {
  30. self.textColor = .black
  31. }
  32. self.bezelStyle = .shadowlessSquare
  33. self.setButtonType(.pushOnPushOff)
  34. self.state = .off
  35. }
  36. override func encode(with coder: NSCoder) {
  37. super.encode(with: coder)
  38. coder.encodeConditionalObject(self.textColor, forKey: TEXTCOLOR_KEY)
  39. coder.encode(self.hasTextColor, forKey: HASTEXTCOLOR_KEY)
  40. }
  41. override func copy(with zone: NSZone? = nil) -> Any {
  42. let cell = super.copy(with: zone) as? KMFontWellCell
  43. cell?.textColor = self.textColor?.copy(with: zone) as? NSColor
  44. cell?.hasTextColor = self.hasTextColor
  45. return cell as Any
  46. }
  47. override func drawBezel(withFrame frame: NSRect, in controlView: NSView) {
  48. KMDrawTextFieldBezel(frame, controlView)
  49. if (self.state == .on) {
  50. NSGraphicsContext.saveGraphicsState()
  51. NSColor.selectedControlColor.setFill()
  52. frame.fill(using: .plusDarker)
  53. NSGraphicsContext.restoreGraphicsState()
  54. }
  55. if (self.isHighlighted) {
  56. NSGraphicsContext.saveGraphicsState()
  57. NSColor(calibratedWhite: 0, alpha: 0.1).setFill()
  58. frame.frame(withWidth: 1, using: .plusDarker)
  59. NSGraphicsContext.restoreGraphicsState()
  60. }
  61. if (self.showsFirstResponder) {
  62. NSGraphicsContext.saveGraphicsState()
  63. NSFocusRingPlacement.only.set()
  64. _ = CGRect.fill(frame)
  65. NSGraphicsContext.restoreGraphicsState()
  66. }
  67. }
  68. override var attributedTitle: NSAttributedString {
  69. set {
  70. super.attributedTitle = newValue
  71. }
  72. get {
  73. if (self.hasTextColor) {
  74. var attrString = super.attributedTitle.mutableCopy() as? NSMutableAttributedString
  75. attrString?.addAttribute(.foregroundColor, value: self.textColor ?? NSColor.black, range: NSMakeRange(0, attrString?.length ?? 0))
  76. if let data = self.textColor?.usingColorSpaceName(.calibratedRGB)?.brightnessComponent, data > 0.8 {
  77. let shade = NSShadow()
  78. shade.shadowColor = .black
  79. shade.shadowBlurRadius = 1
  80. attrString?.addAttribute(.shadow, value: shade, range: NSMakeRange(0, attrString?.length ?? 0))
  81. }
  82. return attrString!
  83. } else {
  84. return super.attributedTitle
  85. }
  86. }
  87. }
  88. }
  89. private let SKFontWellWillBecomeActiveNotification = "SKFontWellWillBecomeActiveNotification"
  90. private let ACTION_KEY = "action"
  91. private let TARGET_KEY = "target"
  92. class KMFontWell: NSButton {
  93. var isActive = false
  94. var fontName: String?
  95. var fontSize: CGFloat = 0
  96. var textColor: NSColor = .black
  97. var hasTextColor = false
  98. /*
  99. @interface SKFontWell : NSButton {
  100. id target;
  101. SEL action;
  102. NSMutableDictionary *bindingInfo;
  103. }
  104. - (void)activate;
  105. - (void)deactivate;
  106. - (void)changeFontFromFontManager:(id)sender;
  107. - (void)changeAttributesFromFontManager:(id)sender;
  108. @end
  109. */
  110. override class var cellClass: AnyClass? {
  111. set {
  112. super.cellClass = newValue
  113. }
  114. get {
  115. return KMFontWellCell.self
  116. }
  117. }
  118. override init(frame frameRect: NSRect) {
  119. super.init(frame: frameRect)
  120. self.commonInit()
  121. }
  122. required init?(coder: NSCoder) {
  123. super.init(coder: coder)
  124. let oldCell = self.cell as? NSButtonCell
  125. if let data = oldCell?.isKind(of: Self.cellClass!), !data {
  126. let newCell = KMFontWellCell(textCell: "")
  127. newCell.alignment = oldCell!.alignment
  128. newCell.isEditable = oldCell!.isEditable
  129. newCell.target = oldCell?.target
  130. newCell.action = oldCell?.action
  131. self.cell = newCell
  132. }
  133. action = NSSelectorFromString(coder.decodeObject(forKey: ACTION_KEY) as? String ?? "")
  134. target = coder.decodeObject(forKey: TARGET_KEY) as AnyObject?
  135. self.commonInit()
  136. }
  137. override func encode(with coder: NSCoder) {
  138. super.encode(with: coder)
  139. coder.encode(NSStringFromSelector(action!), forKey: ACTION_KEY)
  140. coder.encodeConditionalObject(target, forKey: TARGET_KEY)
  141. }
  142. func commonInit() {
  143. if (self.font == nil) {
  144. self.font = .systemFont(ofSize: 0.0)
  145. }
  146. self._fontChanged()
  147. super.action = #selector(_changeActive)
  148. super.target = self
  149. // bindingInfo = [[NSMutableDictionary alloc] init];
  150. // [self registerForDraggedTypes:[NSArray arrayWithObjects:SKNSFontPanelDescriptorsPboardType, SKNSFontPanelFamiliesPboardType, NSPasteboardTypeColor, nil]];
  151. }
  152. override func draw(_ dirtyRect: NSRect) {
  153. super.draw(dirtyRect)
  154. // Drawing code here.
  155. }
  156. /*
  157. #define SKNSFontPanelDescriptorsPboardType @"NSFontPanelDescriptorsPboardType"
  158. #define SKNSFontPanelFamiliesPboardType @"NSFontPanelFamiliesPboardType"
  159. #define SKNSFontCollectionFontDescriptors @"NSFontCollectionFontDescriptors"
  160. #define FONT_KEY @"font"
  161. static char SKFontWellFontNameObservationContext;
  162. static char SKFontWellFontSizeObservationContext;
  163. @interface SKFontWell (SKPrivate)
  164. - (void)changeActive:(id)sender;
  165. - (void)fontChanged;
  166. @end
  167. @implementation SKFontWell
  168. @dynamic isActive, fontName, fontSize, textColor, hasTextColor;
  169. + (void)initialize {
  170. // SKINITIALIZE;
  171. [self exposeBinding:FONTNAME_KEY];
  172. [self exposeBinding:FONTSIZE_KEY];
  173. [self exposeBinding:TEXTCOLOR_KEY];
  174. }
  175. + (NSSet *)keyPathsForValuesAffectingFontName {
  176. return [NSSet setWithObjects:FONT_KEY, nil];
  177. }
  178. + (NSSet *)keyPathsForValuesAffectingFontSize {
  179. return [NSSet setWithObjects:FONT_KEY, nil];
  180. }
  181. - (Class)valueClassForBinding:(NSString *)binding {
  182. if ([binding isEqualToString:FONTNAME_KEY])
  183. return [NSString class];
  184. else if ([binding isEqualToString:FONTNAME_KEY])
  185. return [NSNumber class];
  186. else if ([binding isEqualToString:TEXTCOLOR_KEY])
  187. return [NSColor class];
  188. else
  189. return [super valueClassForBinding:binding];
  190. }
  191. - (void)dealloc {
  192. // SKENSURE_MAIN_THREAD(
  193. [self unbind:FONTNAME_KEY];
  194. [self unbind:FONTSIZE_KEY];
  195. // );
  196. [[NSNotificationCenter defaultCenter] removeObserver:self];
  197. // SKDESTROY(bindingInfo);
  198. // [super dealloc];
  199. }
  200. - (void)viewWillMoveToWindow:(NSWindow *)newWindow {
  201. [self deactivate];
  202. [super viewWillMoveToWindow:newWindow];
  203. }
  204. - (void)fontPickerWillBecomeActive:(NSNotification *)notification {
  205. id sender = [notification object];
  206. if (sender != self && [self isActive]) {
  207. [self deactivate];
  208. }
  209. }
  210. - (void)fontPanelWillClose:(NSNotification *)notification {
  211. [self deactivate];
  212. }
  213. - (void)notifyFontBinding {
  214. NSDictionary *info = [self infoForBinding:FONTNAME_KEY];
  215. [[info objectForKey:NSObservedObjectKey] setValue:[self fontName] forKeyPath:[info objectForKey:NSObservedKeyPathKey]];
  216. info = [self infoForBinding:FONTSIZE_KEY];
  217. [[info objectForKey:NSObservedObjectKey] setValue:[NSNumber numberWithDouble:[self fontSize]] forKeyPath:[info objectForKey:NSObservedKeyPathKey]];
  218. }
  219. - (void)notifyTextColorBinding {
  220. NSDictionary *info = [self infoForBinding:TEXTCOLOR_KEY];
  221. if (info) {
  222. id value = [self textColor];
  223. NSString *transformerName = [[info objectForKey:NSOptionsKey] objectForKey:NSValueTransformerNameBindingOption];
  224. if (transformerName && [transformerName isEqual:[NSNull null]] == NO) {
  225. NSValueTransformer *valueTransformer = [NSValueTransformer valueTransformerForName:transformerName];
  226. value = [valueTransformer reverseTransformedValue:value];
  227. }
  228. [[info objectForKey:NSObservedObjectKey] setValue:value forKeyPath:[info objectForKey:NSObservedKeyPathKey]];
  229. }
  230. }
  231. - (void)changeFontFromFontManager:(id)sender {
  232. if ([self isActive]) {
  233. [self setFont:[sender convertFont:[self font]]];
  234. [self notifyFontBinding];
  235. [self sendAction:[self action] to:[self target]];
  236. }
  237. }
  238. - (void)changeAttributesFromFontManager:(id)sender {
  239. if ([self isActive] && [self hasTextColor]) {
  240. [self setTextColor:[[sender convertAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[self textColor], NSForegroundColorAttributeName, nil]] valueForKey:NSForegroundColorAttributeName]];
  241. [self notifyTextColorBinding];
  242. [self sendAction:[self action] to:[self target]];
  243. }
  244. }
  245. #pragma mark Accessors
  246. - (SEL)action { return action; }
  247. - (void)setAction:(SEL)newAction { action = newAction; }
  248. - (id)target { return target; }
  249. - (void)setTarget:(id)newTarget { target = newTarget; }
  250. - (BOOL)isActive {
  251. return [self state] == NSOnState;
  252. }
  253. - (void)setFont:(NSFont *)newFont {
  254. BOOL didChange = [[self font] isEqual:newFont] == NO;
  255. [super setFont:newFont];
  256. if (didChange)
  257. [self fontChanged];
  258. }
  259. - (NSString *)fontName {
  260. return [[self font] fontName];
  261. }
  262. - (void)setFontName:(NSString *)fontName {
  263. NSFont *newFont = [NSFont fontWithName:fontName size:[[self font] pointSize]];
  264. if (newFont)
  265. [self setFont:newFont];
  266. }
  267. - (CGFloat)fontSize {
  268. return [[self font] pointSize];
  269. }
  270. - (void)setFontSize:(CGFloat)pointSize {
  271. NSFont *newFont = [NSFont fontWithName:[[self font] fontName] size:pointSize];
  272. if (newFont)
  273. [self setFont:newFont];
  274. }
  275. - (NSColor *)textColor {
  276. return [[self cell] textColor];
  277. }
  278. - (void)setTextColor:(NSColor *)newTextColor {
  279. BOOL didChange = [[self textColor] isEqual:newTextColor] == NO;
  280. [[self cell] setTextColor:newTextColor];
  281. if (didChange)
  282. [self setNeedsDisplay:YES];
  283. }
  284. - (BOOL)hasTextColor {
  285. return [[self cell] hasTextColor];
  286. }
  287. - (void)setHasTextColor:(BOOL)newHasTextColor {
  288. if ([self hasTextColor] != newHasTextColor) {
  289. [[self cell] setHasTextColor:newHasTextColor];
  290. [self setNeedsDisplay:YES];
  291. }
  292. }
  293. #pragma mark Binding support
  294. - (void)bind:(NSString *)bindingName toObject:(id)observableController withKeyPath:(NSString *)keyPath options:(NSDictionary *)options {
  295. if ([bindingName isEqualToString:FONTNAME_KEY] || [bindingName isEqualToString:FONTSIZE_KEY]) {
  296. if ([bindingInfo objectForKey:bindingName])
  297. [self unbind:bindingName];
  298. // NSDictionary *bindingsData = [NSDictionary dictionaryWithObjectsAndKeys:observableController, NSObservedObjectKey, [[keyPath copy] autorelease], NSObservedKeyPathKey, [[options copy] autorelease], NSOptionsKey, nil];
  299. // [bindingInfo setObject:bindingsData forKey:bindingName];
  300. void *context = NULL;
  301. if ([bindingName isEqualToString:FONTNAME_KEY])
  302. context = &SKFontWellFontNameObservationContext;
  303. else if ([bindingName isEqualToString:FONTSIZE_KEY])
  304. context = &SKFontWellFontSizeObservationContext;
  305. [observableController addObserver:self forKeyPath:keyPath options:0 context:context];
  306. [self observeValueForKeyPath:keyPath ofObject:observableController change:nil context:context];
  307. } else {
  308. [super bind:bindingName toObject:observableController withKeyPath:keyPath options:options];
  309. }
  310. [self setNeedsDisplay:YES];
  311. }
  312. - (void)unbind:(NSString *)bindingName {
  313. if ([bindingName isEqualToString:FONTNAME_KEY] || [bindingName isEqualToString:FONTSIZE_KEY]) {
  314. NSDictionary *info = [self infoForBinding:bindingName];
  315. [[info objectForKey:NSObservedObjectKey] removeObserver:self forKeyPath:[info objectForKey:NSObservedKeyPathKey]];
  316. [bindingInfo removeObjectForKey:bindingName];
  317. } else {
  318. [super unbind:bindingName];
  319. }
  320. [self setNeedsDisplay:YES];
  321. }
  322. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
  323. NSString *key = nil;
  324. if (context == &SKFontWellFontNameObservationContext)
  325. key = FONTNAME_KEY;
  326. else if (context == &SKFontWellFontSizeObservationContext)
  327. key = FONTSIZE_KEY;
  328. if (key) {
  329. NSDictionary *info = [self infoForBinding:key];
  330. id value = [[info objectForKey:NSObservedObjectKey] valueForKeyPath:[info objectForKey:NSObservedKeyPathKey]];
  331. if (NSIsControllerMarker(value) == NO)
  332. [self setValue:value forKey:key];
  333. } else {
  334. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  335. }
  336. }
  337. - (NSDictionary *)infoForBinding:(NSString *)bindingName {
  338. return [bindingInfo objectForKey:bindingName] ?: [super infoForBinding:bindingName];
  339. }
  340. #pragma mark NSDraggingDestination protocol
  341. - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
  342. if ([self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:SKNSFontPanelDescriptorsPboardType, SKNSFontPanelFamiliesPboardType, ([self hasTextColor] ? NSPasteboardTypeColor : nil), nil]]) {
  343. [[self cell] setHighlighted:YES];
  344. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  345. [self setNeedsDisplay:YES];
  346. return NSDragOperationGeneric;
  347. } else
  348. return NSDragOperationNone;
  349. }
  350. - (void)draggingExited:(id <NSDraggingInfo>)sender {
  351. if ([self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:SKNSFontPanelDescriptorsPboardType, SKNSFontPanelFamiliesPboardType, ([self hasTextColor] ? NSPasteboardTypeColor : nil), nil]]) {
  352. [[self cell] setHighlighted:NO];
  353. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  354. [self setNeedsDisplay:YES];
  355. }
  356. }
  357. - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender {
  358. return [self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:SKNSFontPanelDescriptorsPboardType, SKNSFontPanelFamiliesPboardType, ([self hasTextColor] ? NSPasteboardTypeColor : nil), nil]];
  359. }
  360. - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender{
  361. NSPasteboard *pboard = [sender draggingPasteboard];
  362. NSString *type = [pboard availableTypeFromArray:[NSArray arrayWithObjects:SKNSFontPanelDescriptorsPboardType, SKNSFontPanelFamiliesPboardType, ([self hasTextColor] ? NSPasteboardTypeColor : nil), nil]];
  363. NSFont *droppedFont = nil;
  364. NSColor *droppedColor = nil;
  365. @try {
  366. if ([type isEqualToString:SKNSFontPanelDescriptorsPboardType]) {
  367. NSData *data = [pboard dataForType:type];
  368. NSDictionary *dict = [data isKindOfClass:[NSData class]] ? [NSKeyedUnarchiver unarchiveObjectWithData:data] : nil;
  369. if ([dict isKindOfClass:[NSDictionary class]]) {
  370. NSArray *fontDescriptors = [dict objectForKey:SKNSFontCollectionFontDescriptors];
  371. NSFontDescriptor *fontDescriptor = ([fontDescriptors isKindOfClass:[NSArray class]] && [fontDescriptors count]) ? [fontDescriptors objectAtIndex:0] : nil;
  372. if ([fontDescriptor isKindOfClass:[NSFontDescriptor class]]) {
  373. NSNumber *size = [[fontDescriptor fontAttributes] objectForKey:NSFontSizeAttribute] ?: [dict objectForKey:NSFontSizeAttribute];
  374. CGFloat fontSize = [size respondsToSelector:@selector(doubleValue)] ? [size doubleValue] : [self fontSize];
  375. droppedFont = [NSFont fontWithDescriptor:fontDescriptor size:fontSize];
  376. }
  377. }
  378. } else if ([type isEqualToString:SKNSFontPanelFamiliesPboardType]) {
  379. NSArray *families = [pboard propertyListForType:type];
  380. NSString *family = ([families isKindOfClass:[NSArray class]] && [families count]) ? [families objectAtIndex:0] : nil;
  381. if ([family isKindOfClass:[NSString class]])
  382. droppedFont = [[NSFontManager sharedFontManager] convertFont:[self font] toFamily:family];
  383. } else if ([type isEqualToString:NSPasteboardTypeColor]) {
  384. droppedColor = [NSColor colorFromPasteboard:pboard];
  385. }
  386. }
  387. @catch (id exception) {
  388. NSLog(@"Ignoring exception %@ when dropping on SKFontWell failed", exception);
  389. }
  390. if (droppedFont) {
  391. [self setFont:droppedFont];
  392. [self notifyFontBinding];
  393. [self sendAction:[self action] to:[self target]];
  394. }
  395. if (droppedColor) {
  396. [self setTextColor:droppedColor];
  397. [self notifyTextColorBinding];
  398. [self sendAction:[self action] to:[self target]];
  399. }
  400. [[self cell] setHighlighted:NO];
  401. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  402. [self setNeedsDisplay:YES];
  403. return droppedFont != nil || droppedColor != nil;
  404. }
  405. @end
  406. */
  407. }
  408. // MARK: - Private Methods
  409. extension KMFontWell {
  410. private func _fontChanged() {
  411. if self.isActive {
  412. NSFontManager.shared.setSelectedFont(self.font ?? .systemFont(ofSize: 0), isMultiple: false)
  413. }
  414. self.title = String(format: "%@ %li", self.font?.displayName ?? "", self.fontSize)
  415. self.needsDisplay = true
  416. }
  417. @objc private func _changeActive(_ sender: AnyObject?) {
  418. if (self.isEnabled) {
  419. if (self.isActive) {
  420. self._activate()
  421. } else {
  422. self._deactivate()
  423. }
  424. }
  425. }
  426. private func _activate() {
  427. let nc = NotificationCenter.default
  428. let fm = NSFontManager.shared
  429. nc.post(name: NSNotification.Name(rawValue: SKFontWellWillBecomeActiveNotification), object: self)
  430. fm.setSelectedFont(self.font ?? .systemFont(ofSize: 0), isMultiple: false)
  431. fm.orderFrontFontPanel(self)
  432. // [nc addObserver:self selector:@selector(fontPickerWillBecomeActive:)
  433. // name:SKFontWellWillBecomeActiveNotification object:nil];
  434. // [nc addObserver:self selector:@selector(fontPanelWillClose:)
  435. // name:NSWindowWillCloseNotification object:[fm fontPanel:YES]];
  436. self.state = .on
  437. self.setKeyboardFocusRingNeedsDisplay(self.bounds)
  438. self.needsDisplay = true
  439. }
  440. private func _deactivate() {
  441. NotificationCenter.default.removeObserver(self)
  442. self.state = .off
  443. self.setKeyboardFocusRingNeedsDisplay(self.bounds)
  444. self.needsDisplay = true
  445. }
  446. /*
  447. */
  448. }