KMLineWell.swift 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. //
  2. // KMLineWell.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by tangchao on 2023/11/6.
  6. //
  7. import Cocoa
  8. private let SKPasteboardTypeLineStyle = "net.sourceforge.skim-app.pasteboard.line-style"
  9. private let SKLineWellLineWidthKey = "lineWidth"
  10. private let SKLineWellStyleKey = "style"
  11. private let SKLineWellDashPatternKey = "dashPattern"
  12. private let SKLineWellStartLineStyleKey = "startLineStyle"
  13. private let SKLineWellEndLineStyleKey = "endLineStyle"
  14. private let ACTION_KEY = "action"
  15. private let TARGET_KEY = "target"
  16. private let DISPLAYSTYLE_KEY = "lwFlagsdisplayStyle"
  17. private let ACTIVE_KEY = "active"
  18. private let SKLineWellWillBecomeActiveNotification = "SKLineWellWillBecomeActiveNotification"
  19. private let EXCLUSIVE_KEY = "exclusive"
  20. enum KMLineWellDisplayStyle: Int {
  21. case line = 0
  22. case simpleLine
  23. case rectangle
  24. case oval
  25. }
  26. class KMLineWell: NSControl {
  27. public static let widthKey = SKLineWellLineWidthKey
  28. public static let styleKey = SKLineWellStyleKey
  29. public static let dashPatternKey = SKLineWellDashPatternKey
  30. public static let startLineStyleKey = SKLineWellStartLineStyleKey
  31. public static let endLineStyleKey = SKLineWellEndLineStyleKey
  32. private var _lineWidth: CGFloat = 2
  33. var lineWidth: CGFloat {
  34. get {
  35. return self._lineWidth
  36. }
  37. set {
  38. if (abs(self._lineWidth - newValue) > 0.00001) {
  39. self._lineWidth = newValue
  40. self._changedValue(forKey: SKLineWellLineWidthKey)
  41. }
  42. }
  43. }
  44. private var _style: CPDFBorderStyle = .solid
  45. var style: CPDFBorderStyle {
  46. get {
  47. return self._style
  48. }
  49. set {
  50. if (newValue != self._style) {
  51. self._style = newValue
  52. self._changedValue(forKey: SKLineWellStyleKey)
  53. }
  54. }
  55. }
  56. private var _startLineStyle: CPDFLineStyle = .none
  57. var startLineStyle: CPDFLineStyle {
  58. get {
  59. return self._startLineStyle
  60. }
  61. set {
  62. if (newValue != self._startLineStyle) {
  63. self._startLineStyle = newValue
  64. self._changedValue(forKey: SKLineWellStartLineStyleKey)
  65. }
  66. }
  67. }
  68. private var _endLineStyle: CPDFLineStyle = .none
  69. var endLineStyle: CPDFLineStyle {
  70. get {
  71. return self._endLineStyle
  72. }
  73. set {
  74. if (newValue != self._endLineStyle) {
  75. self._endLineStyle = newValue
  76. self._changedValue(forKey: SKLineWellEndLineStyleKey)
  77. }
  78. }
  79. }
  80. private var _dashPattern: NSArray?
  81. var dashPattern: NSArray? {
  82. get {
  83. return self._dashPattern
  84. }
  85. set {
  86. var _pattern: NSArray? = newValue
  87. if (NSIsControllerMarker(_pattern)) {
  88. _pattern = nil
  89. }
  90. if _pattern?.isEqual(to: self._dashPattern) == false && _pattern != nil || self._dashPattern != nil {
  91. self._dashPattern = _pattern
  92. self._changedValue(forKey: SKLineWellDashPatternKey)
  93. }
  94. }
  95. }
  96. var displayStyle: KMLineWellDisplayStyle {
  97. get {
  98. return KMLineWellDisplayStyle(rawValue: Int(__lwFlags.displayStyle)) ?? .line
  99. }
  100. set {
  101. if __lwFlags.displayStyle != newValue.rawValue {
  102. __lwFlags.displayStyle = UInt8(newValue.rawValue)
  103. self.needsDisplay = true
  104. }
  105. }
  106. }
  107. private struct _lwFlags {
  108. var displayStyle: UInt8 = 0
  109. var active = false
  110. var canActivate = false
  111. var existsActiveLineWell: UInt8 = 0
  112. var highlighted: Bool = false
  113. }
  114. private var __lwFlags = _lwFlags()
  115. var canActivate: Bool {
  116. get {
  117. return __lwFlags.canActivate
  118. }
  119. set {
  120. if __lwFlags.canActivate != newValue {
  121. __lwFlags.canActivate = newValue
  122. if self.isActive && __lwFlags.canActivate == false {
  123. self._deactivate()
  124. }
  125. }
  126. }
  127. }
  128. var isActive: Bool {
  129. get {
  130. return __lwFlags.active
  131. }
  132. }
  133. override var isHighlighted: Bool {
  134. get {
  135. return self.__lwFlags.highlighted
  136. }
  137. set {
  138. if self.__lwFlags.highlighted != newValue {
  139. self.__lwFlags.highlighted = newValue
  140. }
  141. }
  142. }
  143. private var _pri_target: AnyObject?
  144. private var _pri_action: Selector?
  145. deinit {
  146. KMPrint("KMLineWell deinit.")
  147. NotificationCenter.default.removeObserver(self)
  148. }
  149. override init(frame frameRect: NSRect) {
  150. super.init(frame: frameRect)
  151. lineWidth = 1.0
  152. style = .solid
  153. dashPattern = nil;
  154. startLineStyle = .none
  155. endLineStyle = .none
  156. __lwFlags.displayStyle = UInt8(KMLineWellDisplayStyle.line.rawValue)
  157. __lwFlags.active = false
  158. __lwFlags.canActivate = false
  159. __lwFlags.existsActiveLineWell = 0
  160. self.target = nil
  161. self.action = nil
  162. self._commonInit()
  163. }
  164. required init?(coder: NSCoder) {
  165. super.init(coder: coder)
  166. lineWidth = coder.decodeDouble(forKey: SKLineWellLineWidthKey)
  167. style = CPDFBorderStyle(rawValue: coder.decodeInteger(forKey: SKLineWellStyleKey)) ?? .solid
  168. dashPattern = coder.decodeObject(forKey: SKLineWellDashPatternKey) as? NSArray
  169. startLineStyle = CPDFLineStyle(rawValue: coder.decodeInteger(forKey: SKLineWellStartLineStyleKey)) ?? .none
  170. endLineStyle = CPDFLineStyle(rawValue: coder.decodeInteger(forKey: SKLineWellEndLineStyleKey)) ?? .none
  171. __lwFlags.displayStyle = UInt8(coder.decodeInteger(forKey: DISPLAYSTYLE_KEY))
  172. __lwFlags.active = coder.decodeBool(forKey: ACTIVE_KEY)
  173. action = NSSelectorFromString(coder.decodeObject(forKey: ACTION_KEY) as? String ?? "")
  174. target = coder.decodeObject(forKey: TARGET_KEY) as AnyObject?
  175. self._commonInit()
  176. }
  177. override func encode(with coder: NSCoder) {
  178. super.encode(with: coder)
  179. coder.encode(lineWidth, forKey: SKLineWellLineWidthKey)
  180. coder.encode(style.rawValue, forKey: SKLineWellStyleKey)
  181. coder.encode(dashPattern, forKey: SKLineWellDashPatternKey)
  182. coder.encode(startLineStyle.rawValue, forKey: SKLineWellStartLineStyleKey)
  183. coder.encode(endLineStyle.rawValue, forKey: SKLineWellEndLineStyleKey)
  184. coder.encode(__lwFlags.displayStyle, forKey: DISPLAYSTYLE_KEY)
  185. coder.encode(__lwFlags.active, forKey: ACTIVE_KEY)
  186. if let data = self.action {
  187. coder.encode(NSStringFromSelector(data), forKey: ACTION_KEY)
  188. }
  189. coder.encode(target, forKey: TARGET_KEY)
  190. }
  191. override var isOpaque: Bool {
  192. return true
  193. }
  194. override var acceptsFirstResponder: Bool {
  195. return self.canActivate
  196. }
  197. override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
  198. return self.canActivate
  199. }
  200. override func viewWillMove(toWindow newWindow: NSWindow?) {
  201. self._deactivate()
  202. super.viewWillMove(toWindow: newWindow)
  203. }
  204. override func draw(_ dirtyRect: NSRect) {
  205. super.draw(dirtyRect)
  206. let bounds = self.bounds
  207. KMDrawTextFieldBezel(bounds, self)
  208. if (self.isActive) {
  209. NSGraphicsContext.saveGraphicsState()
  210. NSColor.selectedControlColor.setFill()
  211. bounds.fill(using: .plusDarker)
  212. NSGraphicsContext.restoreGraphicsState()
  213. }
  214. if (self.isHighlighted) {
  215. NSGraphicsContext.saveGraphicsState()
  216. NSColor(calibratedWhite: 0, alpha: 0.1).setFill()
  217. self.bounds.frame(withWidth: 1, using: .plusDarker)
  218. NSGraphicsContext.restoreGraphicsState()
  219. }
  220. if (lineWidth > 0.0) {
  221. NSGraphicsContext.saveGraphicsState()
  222. NSBezierPath(rect: NSInsetRect(bounds, 2.0, 2.0)).addClip()
  223. NSColor.black.setStroke()
  224. self._path().stroke()
  225. NSGraphicsContext.restoreGraphicsState()
  226. }
  227. if self.refusesFirstResponder == false && NSApp.isActive && self.window!.isKeyWindow && self.window!.firstResponder == self {
  228. NSGraphicsContext.saveGraphicsState()
  229. NSFocusRingPlacement.only.set()
  230. bounds.fill()
  231. NSGraphicsContext.restoreGraphicsState()
  232. }
  233. }
  234. override func mouseDown(with event: NSEvent) {
  235. if (self.isEnabled) {
  236. self.setKeyboardFocusRingNeedsDisplay(self.bounds)
  237. self.needsDisplay = true
  238. // NSUInteger modifiers = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
  239. var modifiers = event.modifierFlags
  240. // modifiers.insert(.deviceIndependentFlagsMask)
  241. if modifiers.contains(.deviceIndependentFlagsMask) {
  242. modifiers.remove(.deviceIndependentFlagsMask)
  243. }
  244. var keepOn = true
  245. var _event: NSEvent = event
  246. while (keepOn) {
  247. // theEvent = [[self window] nextEventMatchingMask: NSLeftMouseUpMask | NSLeftMouseDraggedMask];
  248. if let data = self.window?.nextEvent(matching: [.leftMouseUp, .leftMouseDragged]) {
  249. _event = data
  250. switch (_event.type) {
  251. case .leftMouseDragged:
  252. // {
  253. // NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
  254. // [NSNumber numberWithDouble:lineWidth], , [NSNumber numberWithInteger:style], , dashPattern, , nil];
  255. var dict = [SKLineWellLineWidthKey : NSNumber(value: self.lineWidth),
  256. SKLineWellStyleKey : NSNumber(value: self.style.rawValue),
  257. SKLineWellDashPatternKey : self.dashPattern ?? []]
  258. if self.displayStyle == .line {
  259. // if ([self displayStyle] == SKLineWellDisplayStyleLine) {
  260. dict[SKLineWellStartLineStyleKey] = NSNumber(value: self.startLineStyle.rawValue)
  261. dict[SKLineWellEndLineStyleKey] = NSNumber(value: self.endLineStyle.rawValue)
  262. }
  263. let pboard = NSPasteboard(name: .drag)
  264. pboard.clearContents()
  265. pboard.setPropertyList(dict, forType: NSPasteboard.PasteboardType(rawValue: SKPasteboardTypeLineStyle))
  266. let bounds = self.bounds
  267. /*beginDraggingSessionWithItems:event:source: instead*/
  268. // [self dragImage:[self dragImage] at:bounds.origin offset:NSZeroSize event:theEvent pasteboard:pboard source:self slideBack:YES];
  269. keepOn = false
  270. break
  271. // }
  272. case .leftMouseUp:
  273. if (self.isActive) {
  274. self._deactivate()
  275. } else {
  276. // [self activate:(modifiers & NSShiftKeyMask) == 0];
  277. self._activate(modifiers.contains(.shift) == false)
  278. }
  279. keepOn = false
  280. break
  281. default:
  282. break
  283. }
  284. }
  285. }
  286. }
  287. }
  288. override func performClick(_ sender: Any?) {
  289. if (self.isEnabled) {
  290. if (self.isActive) {
  291. self._deactivate()
  292. } else {
  293. self._activate(true)
  294. }
  295. }
  296. }
  297. override var target: AnyObject? {
  298. get {
  299. return self._pri_target
  300. }
  301. set {
  302. self._pri_target = newValue
  303. }
  304. }
  305. override var action: Selector? {
  306. get {
  307. return self._pri_action
  308. }
  309. set {
  310. self._pri_action = newValue
  311. }
  312. }
  313. func takeValue(for key: String, from object: AnyObject) {
  314. if let inspector = object as? KMLineInspector {
  315. if key == Self.widthKey {
  316. self.lineWidth = inspector.lineWidth
  317. } else if key == Self.styleKey {
  318. self.style = CPDFBorderStyle(rawValue: inspector.style) ?? .solid
  319. } else if key == Self.startLineStyleKey {
  320. self.startLineStyle = CPDFLineStyle(rawValue: inspector.startLineStyle) ?? .none
  321. } else if key == Self.endLineStyleKey {
  322. self.endLineStyle = CPDFLineStyle(rawValue: inspector.endLineStyle) ?? .none
  323. } else if key == Self.dashPatternKey {
  324. self.dashPattern = inspector.dashPattern as NSArray
  325. }
  326. }
  327. }
  328. // - (void)takeValueForKey:(NSString *)key from:(id)object {
  329. // [self setValue:[object valueForKey:key] forKey:key];
  330. // NSDictionary *info = [self infoForBinding:key];
  331. // [[info objectForKey:NSObservedObjectKey] setValue:[self valueForKey:key] forKeyPath:[info objectForKey:NSObservedKeyPathKey]];
  332. // }
  333. /*
  334. + (void)initialize {
  335. // SKINITIALIZE;
  336. [self exposeBinding:SKLineWellLineWidthKey];
  337. [self exposeBinding:SKLineWellStyleKey];
  338. [self exposeBinding:SKLineWellDashPatternKey];
  339. [self exposeBinding:SKLineWellStartLineStyleKey];
  340. [self exposeBinding:SKLineWellEndLineStyleKey];
  341. }
  342. - (Class)valueClassForBinding:(NSString *)binding {
  343. if ([binding isEqualToString:SKLineWellDashPatternKey])
  344. return [NSArray class];
  345. else
  346. return [NSNumber class];
  347. }
  348. - (void)dealloc {
  349. // SKENSURE_MAIN_THREAD(
  350. [self unbind:SKLineWellLineWidthKey];
  351. [self unbind:SKLineWellStyleKey];
  352. [self unbind:SKLineWellDashPatternKey];
  353. [self unbind:SKLineWellStartLineStyleKey];
  354. [self unbind:SKLineWellEndLineStyleKey];
  355. // );
  356. [[NSNotificationCenter defaultCenter] removeObserver:self];
  357. // SKDESTROY(dashPattern);
  358. // [super dealloc];
  359. }
  360. #pragma mark Accessors
  361. - (SEL)action { return action; }
  362. - (void)setAction:(SEL)newAction { action = newAction; }
  363. - (id)target { return target; }
  364. - (void)setTarget:(id)newTarget { target = newTarget; }
  365. - (void)setNilValueForKey:(NSString *)key {
  366. if ([key isEqualToString:SKLineWellLineWidthKey] || [key isEqualToString:SKLineWellStyleKey] ||
  367. [key isEqualToString:SKLineWellStartLineStyleKey] || [key isEqualToString:SKLineWellEndLineStyleKey]) {
  368. [self setValue:[NSNumber numberWithInteger:0] forKey:key];
  369. } else {
  370. [super setNilValueForKey:key];
  371. }
  372. }
  373. #pragma mark Notification handlers
  374. #pragma mark NSDraggingSource protocol
  375. - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
  376. return NSDragOperationGeneric;
  377. }
  378. #pragma mark NSDraggingDestination protocol
  379. - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
  380. if ([self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] canReadItemWithDataConformingToTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]]) {
  381. [self setHighlighted:YES];
  382. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  383. [self setNeedsDisplay:YES];
  384. return NSDragOperationEvery;
  385. } else
  386. return NSDragOperationNone;
  387. }
  388. - (void)draggingExited:(id <NSDraggingInfo>)sender {
  389. if ([self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] canReadItemWithDataConformingToTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]]) {
  390. [self setHighlighted:NO];
  391. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  392. [self setNeedsDisplay:YES];
  393. }
  394. }
  395. - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender {
  396. return [self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] canReadItemWithDataConformingToTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]];
  397. }
  398. - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender{
  399. NSPasteboard *pboard = [sender draggingPasteboard];
  400. [pboard types];
  401. NSDictionary *dict = [pboard propertyListForType:SKPasteboardTypeLineStyle];
  402. if ([dict objectForKey:SKLineWellLineWidthKey])
  403. [self takeValueForKey:SKLineWellLineWidthKey from:dict];
  404. if ([dict objectForKey:SKLineWellStyleKey])
  405. [self takeValueForKey:SKLineWellStyleKey from:dict];
  406. [self takeValueForKey:SKLineWellDashPatternKey from:dict];
  407. if ([self displayStyle] == SKLineWellDisplayStyleLine) {
  408. if ([dict objectForKey:SKLineWellStartLineStyleKey])
  409. [self takeValueForKey:SKLineWellStartLineStyleKey from:dict];
  410. if ([dict objectForKey:SKLineWellEndLineStyleKey])
  411. [self takeValueForKey:SKLineWellEndLineStyleKey from:dict];
  412. }
  413. [self sendAction:[self action] to:[self target]];
  414. [self setHighlighted:NO];
  415. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  416. [self setNeedsDisplay:YES];
  417. return dict != nil;
  418. }
  419. #pragma mark Accessibility
  420. - (NSArray *)accessibilityAttributeNames {
  421. static NSArray *attributes = nil;
  422. if (attributes == nil) {
  423. attributes = [[NSArray alloc] initWithObjects:
  424. NSAccessibilityRoleAttribute,
  425. NSAccessibilityRoleDescriptionAttribute,
  426. NSAccessibilityValueAttribute,
  427. NSAccessibilityTitleAttribute,
  428. NSAccessibilityHelpAttribute,
  429. NSAccessibilityFocusedAttribute,
  430. NSAccessibilityParentAttribute,
  431. NSAccessibilityWindowAttribute,
  432. NSAccessibilityTopLevelUIElementAttribute,
  433. NSAccessibilityPositionAttribute,
  434. NSAccessibilitySizeAttribute,
  435. nil];
  436. }
  437. return attributes;
  438. }
  439. - (id)accessibilityAttributeValue:(NSString *)attribute {
  440. if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
  441. return NSAccessibilityCheckBoxRole;
  442. } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
  443. return NSAccessibilityRoleDescription(NSAccessibilityCheckBoxRole, nil);
  444. } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
  445. return [NSNumber numberWithInteger:[self isActive]];
  446. } else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) {
  447. return [NSString stringWithFormat:@"%@ %ld", NSLocalizedString(@"line width", @"Accessibility description"), (long)[self lineWidth]];
  448. } else if ([attribute isEqualToString:NSAccessibilityHelpAttribute]) {
  449. return [self toolTip];
  450. } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
  451. // Just check if the app thinks we're focused.
  452. id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute];
  453. return [NSNumber numberWithBool:[focusedElement isEqual:self]];
  454. } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
  455. return NSAccessibilityUnignoredAncestor([self superview]);
  456. } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
  457. // We're in the same window as our parent.
  458. return [NSAccessibilityUnignoredAncestor([self superview]) accessibilityAttributeValue:NSAccessibilityWindowAttribute];
  459. } else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
  460. // We're in the same top level element as our parent.
  461. return [NSAccessibilityUnignoredAncestor([self superview]) accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
  462. } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
  463. return [NSValue valueWithPoint:[[self window] convertBaseToScreen:[self convertPoint:[self bounds].origin toView:nil]]];
  464. } else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
  465. return [NSValue valueWithSize:[self convertSize:[self bounds].size toView:nil]];
  466. } else {
  467. return nil;
  468. }
  469. }
  470. - (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
  471. if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
  472. return [self canActivate];
  473. } else {
  474. return NO;
  475. }
  476. }
  477. - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
  478. if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
  479. [[self window] makeFirstResponder:self];
  480. }
  481. }
  482. // actions
  483. - (NSArray *)accessibilityActionNames {
  484. return [NSArray arrayWithObject:NSAccessibilityPressAction];
  485. }
  486. - (NSString *)accessibilityActionDescription:(NSString *)anAction {
  487. return NSAccessibilityActionDescription(anAction);
  488. }
  489. - (void)accessibilityPerformAction:(NSString *)anAction {
  490. if ([anAction isEqualToString:NSAccessibilityPressAction])
  491. [self performClick:self];
  492. }
  493. // misc
  494. - (BOOL)accessibilityIsIgnored {
  495. return NO;
  496. }
  497. - (id)accessibilityHitTest:(NSPoint)point {
  498. return NSAccessibilityUnignoredAncestor(self);
  499. }
  500. - (id)accessibilityFocusedUIElement {
  501. return NSAccessibilityUnignoredAncestor(self);
  502. }
  503. @end
  504. */
  505. }
  506. // MARK: - Private Methods
  507. extension KMLineWell {
  508. private func _commonInit() {
  509. __lwFlags.canActivate = true
  510. __lwFlags.existsActiveLineWell = 0
  511. self.isEnabled = true
  512. // [self registerForDraggedTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]];
  513. }
  514. private func _deactivate() {
  515. if self.isActive {
  516. NotificationCenter.default.removeObserver(self)
  517. __lwFlags.active = false
  518. self.setKeyboardFocusRingNeedsDisplay(self.bounds)
  519. self.needsDisplay = true
  520. }
  521. }
  522. private func _changedValue(forKey key: String) {
  523. if (self.isActive) {
  524. // [[SKLineInspector sharedLineInspector] setValue:[self valueForKey:key] forKey:key];
  525. }
  526. self.needsDisplay = true
  527. }
  528. private func _dragImage() -> NSImage? {
  529. let bounds = self.bounds
  530. let path = self.lineWidth > 0.0 ? self._path() : nil
  531. // CGFloat scale = [self backingScale];
  532. let scale = 1.0
  533. let image = NSImage.bitmapImage(with: bounds.size, scale: scale) { rect in
  534. KMContextSetAlpha(NSGraphicsContext.current?.cgContext, 0.7)
  535. NSColor.darkGray.setFill()
  536. _ = NSRect.fill(NSInsetRect(rect, 1.0, 1.0))
  537. NSColor.controlBackgroundColor.setFill()
  538. _ = NSRect.fill(NSInsetRect(rect, 2.0, 2.0))
  539. NSColor.black.setStroke()
  540. path?.stroke()
  541. }
  542. return image
  543. }
  544. private func _path() -> NSBezierPath {
  545. let path = NSBezierPath()
  546. let bounds = self.bounds
  547. if self.displayStyle == .line {
  548. let offset = 0.5 * lineWidth - floor(0.5 * lineWidth)
  549. let startPoint = NSMakePoint(NSMinX(bounds) + ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset)
  550. let endPoint = NSMakePoint(NSMaxX(bounds) - ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset)
  551. switch (self.startLineStyle) {
  552. case .none:
  553. break
  554. case .square:
  555. path.appendRect(NSMakeRect(startPoint.x - 1.5 * lineWidth, startPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth))
  556. break
  557. case .circle:
  558. path.appendOval(in: NSMakeRect(startPoint.x - 1.5 * lineWidth, startPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth))
  559. break
  560. case .diamond:
  561. path.move(to: NSMakePoint(startPoint.x - 1.5 * lineWidth, startPoint.y))
  562. path.line(to: NSMakePoint(startPoint.x, startPoint.y + 1.5 * lineWidth))
  563. path.line(to: NSMakePoint(startPoint.x + 1.5 * lineWidth, startPoint.y))
  564. path.line(to: NSMakePoint(startPoint.x, startPoint.y - 1.5 * lineWidth))
  565. path.close()
  566. break
  567. case .openArrow:
  568. path.move(to: NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y - 1.5 * lineWidth))
  569. path.line(to: NSMakePoint(startPoint.x, startPoint.y))
  570. path.line(to: NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y + 1.5 * lineWidth))
  571. break
  572. case .closedArrow:
  573. path.move(to: NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y - 1.5 * lineWidth))
  574. path.line(to: NSMakePoint(startPoint.x, startPoint.y))
  575. path.line(to: NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y + 1.5 * lineWidth))
  576. path.close()
  577. break
  578. @unknown default:
  579. fatalError()
  580. }
  581. path.move(to: startPoint)
  582. path.line(to: endPoint)
  583. switch (self.endLineStyle) {
  584. case .none:
  585. break
  586. case .square:
  587. path.appendRect(NSMakeRect(endPoint.x - 1.5 * lineWidth, endPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth))
  588. break
  589. case .circle:
  590. path.appendOval(in: NSMakeRect(endPoint.x - 1.5 * lineWidth, endPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth))
  591. break
  592. case .diamond:
  593. path.move(to: NSMakePoint(endPoint.x + 1.5 * lineWidth, endPoint.y))
  594. path.line(to: NSMakePoint(endPoint.x, endPoint.y + 1.5 * lineWidth))
  595. path.line(to: NSMakePoint(endPoint.x - 1.5 * lineWidth, endPoint.y))
  596. path.line(to: NSMakePoint(endPoint.x, endPoint.y - 1.5 * lineWidth))
  597. path.close()
  598. break
  599. case .openArrow:
  600. path.move(to: NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y + 1.5 * lineWidth))
  601. path.line(to: NSMakePoint(endPoint.x, endPoint.y))
  602. path.line(to: NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y - 1.5 * lineWidth))
  603. break
  604. case .closedArrow:
  605. path.move(to: NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y + 1.5 * lineWidth))
  606. path.line(to: NSMakePoint(endPoint.x, endPoint.y))
  607. path.line(to: NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y - 1.5 * lineWidth))
  608. path.close()
  609. break
  610. @unknown default:
  611. fatalError()
  612. }
  613. } else if self.displayStyle == .simpleLine {
  614. let offset = 0.5 * lineWidth - floor(0.5 * lineWidth)
  615. path.move(to: NSMakePoint(NSMinX(bounds) + ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset))
  616. path.line(to: NSMakePoint(NSMaxX(bounds) - ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset))
  617. } else if self.displayStyle == .rectangle {
  618. let inset = 7.0 + 0.5 * lineWidth
  619. path.appendRect(NSInsetRect(bounds, inset, inset))
  620. } else {
  621. let inset = 7.0 + 0.5 * lineWidth
  622. path.appendOval(in: NSInsetRect(bounds, inset, inset))
  623. }
  624. path.lineWidth = self.lineWidth
  625. if self.style == .dashed {
  626. path.setLineDash(self.dashPattern as? [CGFloat], count: self.dashPattern?.count ?? 0, phase: 0)
  627. }
  628. return path
  629. }
  630. private func _activate(_ exclusive: Bool) {
  631. if (self.canActivate) {
  632. let nc = NotificationCenter.default
  633. let inspector = KMLineInspector.shared
  634. _ = inspector.window
  635. __lwFlags.existsActiveLineWell = 0
  636. nc.post(name: NSNotification.Name(rawValue: SKLineWellWillBecomeActiveNotification), object: self, userInfo: [EXCLUSIVE_KEY : NSNumber(value: exclusive)])
  637. if (__lwFlags.existsActiveLineWell != 0) {
  638. self.takeValue(for: SKLineWellLineWidthKey, from: inspector)
  639. self.takeValue(for: SKLineWellDashPatternKey, from: inspector)
  640. self.takeValue(for: SKLineWellStyleKey, from: inspector)
  641. if (self.displayStyle == .line) {
  642. self.takeValue(for: SKLineWellStartLineStyleKey, from: inspector)
  643. self.takeValue(for: SKLineWellEndLineStyleKey, from: inspector)
  644. }
  645. } else {
  646. inspector.lineWidth = self.lineWidth
  647. inspector.dashPattern = self.dashPattern as? [CGFloat] ?? []
  648. inspector.style = self.style.rawValue
  649. if (self.displayStyle == .line) {
  650. inspector.startLineStyle = self.startLineStyle.rawValue
  651. inspector.endLineStyle = self.endLineStyle.rawValue
  652. }
  653. }
  654. inspector.window?.orderFront(self)
  655. nc.addObserver(self, selector: #selector(_lineWellWillBecomeActive), name: NSNotification.Name(SKLineWellWillBecomeActiveNotification), object: nil)
  656. nc.addObserver(self, selector: #selector(_lineInspectorWindowWillClose), name: NSWindow.willCloseNotification, object: inspector.window)
  657. nc.addObserver(self, selector: #selector(_lineInspectorLineAttributeChanged), name: KMLineInspector.lineAttributeDidChangeNotification, object: inspector)
  658. __lwFlags.active = true
  659. self.setKeyboardFocusRingNeedsDisplay(self.bounds)
  660. self.needsDisplay = true
  661. }
  662. }
  663. @objc private func _lineWellWillBecomeActive(_ notification: NSNotification) {
  664. let sender = notification.object
  665. if (self.isEqual(to: sender) == false && self.isActive) {
  666. if let data = notification.userInfo?[EXCLUSIVE_KEY] as? NSNumber, data.boolValue {
  667. self._deactivate()
  668. } else {
  669. (sender as? KMLineWell)?._existsActiveLineWell()
  670. }
  671. }
  672. }
  673. @objc private func _lineInspectorWindowWillClose(_ notification: Notification) {
  674. self._deactivate()
  675. }
  676. @objc private func _lineInspectorLineAttributeChanged(_ notification: Notification) {
  677. guard let inspector = notification.object as? KMLineInspector else {
  678. return
  679. }
  680. let action = inspector.currentLineChangeAction
  681. var key: String?
  682. if action == .lineWidth {
  683. key = Self.widthKey
  684. } else if action == .style {
  685. key = Self.styleKey
  686. } else if action == .dashPattern {
  687. key = Self.dashPatternKey
  688. } else if action == .startLineStyle {
  689. key = Self.startLineStyleKey
  690. } else if action == .endLineStyle {
  691. key = Self.endLineStyleKey
  692. }
  693. if let _key = key {
  694. self.takeValue(for: _key, from: inspector)
  695. self.sendAction(self._pri_action, to: self._pri_target)
  696. }
  697. }
  698. private func _existsActiveLineWell() {
  699. __lwFlags.existsActiveLineWell = 1
  700. }
  701. }