KMLineWell.swift 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  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. var keepOn = true
  242. var _event: NSEvent = event
  243. while (keepOn) {
  244. // theEvent = [[self window] nextEventMatchingMask: NSLeftMouseUpMask | NSLeftMouseDraggedMask];
  245. if let data = self.window?.nextEvent(matching: [.leftMouseUp, .leftMouseDragged]) {
  246. _event = data
  247. switch (_event.type) {
  248. case .leftMouseDragged:
  249. // {
  250. // NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
  251. // [NSNumber numberWithDouble:lineWidth], , [NSNumber numberWithInteger:style], , dashPattern, , nil];
  252. var dict = [SKLineWellLineWidthKey : NSNumber(value: self.lineWidth),
  253. SKLineWellStyleKey : NSNumber(value: self.style.rawValue),
  254. SKLineWellDashPatternKey : self.dashPattern ?? []]
  255. if self.displayStyle == .line {
  256. // if ([self displayStyle] == SKLineWellDisplayStyleLine) {
  257. dict[SKLineWellStartLineStyleKey] = NSNumber(value: self.startLineStyle.rawValue)
  258. dict[SKLineWellEndLineStyleKey] = NSNumber(value: self.endLineStyle.rawValue)
  259. }
  260. let pboard = NSPasteboard(name: .drag)
  261. pboard.clearContents()
  262. pboard.setPropertyList(dict, forType: NSPasteboard.PasteboardType(rawValue: SKPasteboardTypeLineStyle))
  263. let bounds = self.bounds
  264. /*beginDraggingSessionWithItems:event:source: instead*/
  265. // [self dragImage:[self dragImage] at:bounds.origin offset:NSZeroSize event:theEvent pasteboard:pboard source:self slideBack:YES];
  266. keepOn = false
  267. break;
  268. // }
  269. case .leftMouseUp:
  270. if (self.isActive) {
  271. self._deactivate()
  272. } else {
  273. // [self activate:(modifiers & NSShiftKeyMask) == 0];
  274. self._activate(modifiers.contains(.shift) == false)
  275. }
  276. keepOn = false
  277. break;
  278. default:
  279. break;
  280. }
  281. }
  282. }
  283. }
  284. }
  285. override func performClick(_ sender: Any?) {
  286. if (self.isEnabled) {
  287. if (self.isActive) {
  288. self._deactivate()
  289. } else {
  290. self._activate(true)
  291. }
  292. }
  293. }
  294. override var target: AnyObject? {
  295. get {
  296. return self._pri_target
  297. }
  298. set {
  299. self._pri_target = newValue
  300. }
  301. }
  302. override var action: Selector? {
  303. get {
  304. return self._pri_action
  305. }
  306. set {
  307. self._pri_action = newValue
  308. }
  309. }
  310. func takeValue(for key: String, from object: AnyObject) {
  311. if let inspector = object as? KMLineInspector {
  312. if key == Self.widthKey {
  313. self.lineWidth = inspector.lineWidth
  314. } else if key == Self.styleKey {
  315. self.style = CPDFBorderStyle(rawValue: inspector.style) ?? .solid
  316. } else if key == Self.startLineStyleKey {
  317. self.startLineStyle = CPDFLineStyle(rawValue: inspector.startLineStyle) ?? .none
  318. } else if key == Self.endLineStyleKey {
  319. self.endLineStyle = CPDFLineStyle(rawValue: inspector.endLineStyle) ?? .none
  320. } else if key == Self.dashPatternKey {
  321. self.dashPattern = inspector.dashPattern as NSArray
  322. }
  323. }
  324. }
  325. // - (void)takeValueForKey:(NSString *)key from:(id)object {
  326. // [self setValue:[object valueForKey:key] forKey:key];
  327. // NSDictionary *info = [self infoForBinding:key];
  328. // [[info objectForKey:NSObservedObjectKey] setValue:[self valueForKey:key] forKeyPath:[info objectForKey:NSObservedKeyPathKey]];
  329. // }
  330. /*
  331. + (void)initialize {
  332. // SKINITIALIZE;
  333. [self exposeBinding:SKLineWellLineWidthKey];
  334. [self exposeBinding:SKLineWellStyleKey];
  335. [self exposeBinding:SKLineWellDashPatternKey];
  336. [self exposeBinding:SKLineWellStartLineStyleKey];
  337. [self exposeBinding:SKLineWellEndLineStyleKey];
  338. }
  339. - (Class)valueClassForBinding:(NSString *)binding {
  340. if ([binding isEqualToString:SKLineWellDashPatternKey])
  341. return [NSArray class];
  342. else
  343. return [NSNumber class];
  344. }
  345. - (void)dealloc {
  346. // SKENSURE_MAIN_THREAD(
  347. [self unbind:SKLineWellLineWidthKey];
  348. [self unbind:SKLineWellStyleKey];
  349. [self unbind:SKLineWellDashPatternKey];
  350. [self unbind:SKLineWellStartLineStyleKey];
  351. [self unbind:SKLineWellEndLineStyleKey];
  352. // );
  353. [[NSNotificationCenter defaultCenter] removeObserver:self];
  354. // SKDESTROY(dashPattern);
  355. // [super dealloc];
  356. }
  357. #pragma mark Accessors
  358. - (SEL)action { return action; }
  359. - (void)setAction:(SEL)newAction { action = newAction; }
  360. - (id)target { return target; }
  361. - (void)setTarget:(id)newTarget { target = newTarget; }
  362. - (void)setNilValueForKey:(NSString *)key {
  363. if ([key isEqualToString:SKLineWellLineWidthKey] || [key isEqualToString:SKLineWellStyleKey] ||
  364. [key isEqualToString:SKLineWellStartLineStyleKey] || [key isEqualToString:SKLineWellEndLineStyleKey]) {
  365. [self setValue:[NSNumber numberWithInteger:0] forKey:key];
  366. } else {
  367. [super setNilValueForKey:key];
  368. }
  369. }
  370. #pragma mark Notification handlers
  371. #pragma mark NSDraggingSource protocol
  372. - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
  373. return NSDragOperationGeneric;
  374. }
  375. #pragma mark NSDraggingDestination protocol
  376. - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
  377. if ([self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] canReadItemWithDataConformingToTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]]) {
  378. [self setHighlighted:YES];
  379. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  380. [self setNeedsDisplay:YES];
  381. return NSDragOperationEvery;
  382. } else
  383. return NSDragOperationNone;
  384. }
  385. - (void)draggingExited:(id <NSDraggingInfo>)sender {
  386. if ([self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] canReadItemWithDataConformingToTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]]) {
  387. [self setHighlighted:NO];
  388. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  389. [self setNeedsDisplay:YES];
  390. }
  391. }
  392. - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender {
  393. return [self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] canReadItemWithDataConformingToTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]];
  394. }
  395. - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender{
  396. NSPasteboard *pboard = [sender draggingPasteboard];
  397. [pboard types];
  398. NSDictionary *dict = [pboard propertyListForType:SKPasteboardTypeLineStyle];
  399. if ([dict objectForKey:SKLineWellLineWidthKey])
  400. [self takeValueForKey:SKLineWellLineWidthKey from:dict];
  401. if ([dict objectForKey:SKLineWellStyleKey])
  402. [self takeValueForKey:SKLineWellStyleKey from:dict];
  403. [self takeValueForKey:SKLineWellDashPatternKey from:dict];
  404. if ([self displayStyle] == SKLineWellDisplayStyleLine) {
  405. if ([dict objectForKey:SKLineWellStartLineStyleKey])
  406. [self takeValueForKey:SKLineWellStartLineStyleKey from:dict];
  407. if ([dict objectForKey:SKLineWellEndLineStyleKey])
  408. [self takeValueForKey:SKLineWellEndLineStyleKey from:dict];
  409. }
  410. [self sendAction:[self action] to:[self target]];
  411. [self setHighlighted:NO];
  412. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  413. [self setNeedsDisplay:YES];
  414. return dict != nil;
  415. }
  416. #pragma mark Accessibility
  417. - (NSArray *)accessibilityAttributeNames {
  418. static NSArray *attributes = nil;
  419. if (attributes == nil) {
  420. attributes = [[NSArray alloc] initWithObjects:
  421. NSAccessibilityRoleAttribute,
  422. NSAccessibilityRoleDescriptionAttribute,
  423. NSAccessibilityValueAttribute,
  424. NSAccessibilityTitleAttribute,
  425. NSAccessibilityHelpAttribute,
  426. NSAccessibilityFocusedAttribute,
  427. NSAccessibilityParentAttribute,
  428. NSAccessibilityWindowAttribute,
  429. NSAccessibilityTopLevelUIElementAttribute,
  430. NSAccessibilityPositionAttribute,
  431. NSAccessibilitySizeAttribute,
  432. nil];
  433. }
  434. return attributes;
  435. }
  436. - (id)accessibilityAttributeValue:(NSString *)attribute {
  437. if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
  438. return NSAccessibilityCheckBoxRole;
  439. } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
  440. return NSAccessibilityRoleDescription(NSAccessibilityCheckBoxRole, nil);
  441. } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
  442. return [NSNumber numberWithInteger:[self isActive]];
  443. } else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) {
  444. return [NSString stringWithFormat:@"%@ %ld", NSLocalizedString(@"line width", @"Accessibility description"), (long)[self lineWidth]];
  445. } else if ([attribute isEqualToString:NSAccessibilityHelpAttribute]) {
  446. return [self toolTip];
  447. } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
  448. // Just check if the app thinks we're focused.
  449. id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute];
  450. return [NSNumber numberWithBool:[focusedElement isEqual:self]];
  451. } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
  452. return NSAccessibilityUnignoredAncestor([self superview]);
  453. } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
  454. // We're in the same window as our parent.
  455. return [NSAccessibilityUnignoredAncestor([self superview]) accessibilityAttributeValue:NSAccessibilityWindowAttribute];
  456. } else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
  457. // We're in the same top level element as our parent.
  458. return [NSAccessibilityUnignoredAncestor([self superview]) accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
  459. } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
  460. return [NSValue valueWithPoint:[[self window] convertBaseToScreen:[self convertPoint:[self bounds].origin toView:nil]]];
  461. } else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
  462. return [NSValue valueWithSize:[self convertSize:[self bounds].size toView:nil]];
  463. } else {
  464. return nil;
  465. }
  466. }
  467. - (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
  468. if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
  469. return [self canActivate];
  470. } else {
  471. return NO;
  472. }
  473. }
  474. - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
  475. if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
  476. [[self window] makeFirstResponder:self];
  477. }
  478. }
  479. // actions
  480. - (NSArray *)accessibilityActionNames {
  481. return [NSArray arrayWithObject:NSAccessibilityPressAction];
  482. }
  483. - (NSString *)accessibilityActionDescription:(NSString *)anAction {
  484. return NSAccessibilityActionDescription(anAction);
  485. }
  486. - (void)accessibilityPerformAction:(NSString *)anAction {
  487. if ([anAction isEqualToString:NSAccessibilityPressAction])
  488. [self performClick:self];
  489. }
  490. // misc
  491. - (BOOL)accessibilityIsIgnored {
  492. return NO;
  493. }
  494. - (id)accessibilityHitTest:(NSPoint)point {
  495. return NSAccessibilityUnignoredAncestor(self);
  496. }
  497. - (id)accessibilityFocusedUIElement {
  498. return NSAccessibilityUnignoredAncestor(self);
  499. }
  500. @end
  501. */
  502. }
  503. // MARK: - Private Methods
  504. extension KMLineWell {
  505. private func _commonInit() {
  506. __lwFlags.canActivate = true
  507. __lwFlags.existsActiveLineWell = 0
  508. self.isEnabled = true
  509. // [self registerForDraggedTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]];
  510. }
  511. private func _deactivate() {
  512. if self.isActive {
  513. NotificationCenter.default.removeObserver(self)
  514. __lwFlags.active = false
  515. self.setKeyboardFocusRingNeedsDisplay(self.bounds)
  516. self.needsDisplay = true
  517. }
  518. }
  519. private func _changedValue(forKey key: String) {
  520. if (self.isActive) {
  521. // [[SKLineInspector sharedLineInspector] setValue:[self valueForKey:key] forKey:key];
  522. }
  523. self.needsDisplay = true
  524. }
  525. private func _dragImage() -> NSImage? {
  526. let bounds = self.bounds
  527. let path = self.lineWidth > 0.0 ? self._path() : nil
  528. // CGFloat scale = [self backingScale];
  529. let scale = 1.0
  530. let image = NSImage.bitmapImage(with: bounds.size, scale: scale) { rect in
  531. KMContextSetAlpha(NSGraphicsContext.current?.cgContext, 0.7)
  532. NSColor.darkGray.setFill()
  533. _ = NSRect.fill(NSInsetRect(rect, 1.0, 1.0))
  534. NSColor.controlBackgroundColor.setFill()
  535. _ = NSRect.fill(NSInsetRect(rect, 2.0, 2.0))
  536. NSColor.black.setStroke()
  537. path?.stroke()
  538. }
  539. return image
  540. }
  541. private func _path() -> NSBezierPath {
  542. let path = NSBezierPath()
  543. let bounds = self.bounds
  544. if self.displayStyle == .line {
  545. let offset = 0.5 * lineWidth - floor(0.5 * lineWidth)
  546. let startPoint = NSMakePoint(NSMinX(bounds) + ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset)
  547. let endPoint = NSMakePoint(NSMaxX(bounds) - ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset)
  548. switch (self.startLineStyle) {
  549. case .none:
  550. break
  551. case .square:
  552. path.appendRect(NSMakeRect(startPoint.x - 1.5 * lineWidth, startPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth))
  553. break
  554. case .circle:
  555. path.appendOval(in: NSMakeRect(startPoint.x - 1.5 * lineWidth, startPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth))
  556. break
  557. case .diamond:
  558. path.move(to: NSMakePoint(startPoint.x - 1.5 * lineWidth, startPoint.y))
  559. path.line(to: NSMakePoint(startPoint.x, startPoint.y + 1.5 * lineWidth))
  560. path.line(to: NSMakePoint(startPoint.x + 1.5 * lineWidth, startPoint.y))
  561. path.line(to: NSMakePoint(startPoint.x, startPoint.y - 1.5 * lineWidth))
  562. path.close()
  563. break
  564. case .openArrow:
  565. path.move(to: NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y - 1.5 * lineWidth))
  566. path.line(to: NSMakePoint(startPoint.x, startPoint.y))
  567. path.line(to: NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y + 1.5 * lineWidth))
  568. break
  569. case .closedArrow:
  570. path.move(to: NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y - 1.5 * lineWidth))
  571. path.line(to: NSMakePoint(startPoint.x, startPoint.y))
  572. path.line(to: NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y + 1.5 * lineWidth))
  573. path.close()
  574. break
  575. @unknown default:
  576. fatalError()
  577. }
  578. path.move(to: startPoint)
  579. path.line(to: endPoint)
  580. switch (self.endLineStyle) {
  581. case .none:
  582. break
  583. case .square:
  584. path.appendRect(NSMakeRect(endPoint.x - 1.5 * lineWidth, endPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth))
  585. break
  586. case .circle:
  587. path.appendOval(in: NSMakeRect(endPoint.x - 1.5 * lineWidth, endPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth))
  588. break
  589. case .diamond:
  590. path.move(to: NSMakePoint(endPoint.x + 1.5 * lineWidth, endPoint.y))
  591. path.line(to: NSMakePoint(endPoint.x, endPoint.y + 1.5 * lineWidth))
  592. path.line(to: NSMakePoint(endPoint.x - 1.5 * lineWidth, endPoint.y))
  593. path.line(to: NSMakePoint(endPoint.x, endPoint.y - 1.5 * lineWidth))
  594. path.close()
  595. break
  596. case .openArrow:
  597. path.move(to: NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y + 1.5 * lineWidth))
  598. path.line(to: NSMakePoint(endPoint.x, endPoint.y))
  599. path.line(to: NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y - 1.5 * lineWidth))
  600. break
  601. case .closedArrow:
  602. path.move(to: NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y + 1.5 * lineWidth))
  603. path.line(to: NSMakePoint(endPoint.x, endPoint.y))
  604. path.line(to: NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y - 1.5 * lineWidth))
  605. path.close()
  606. break
  607. @unknown default:
  608. fatalError()
  609. }
  610. } else if self.displayStyle == .simpleLine {
  611. let offset = 0.5 * lineWidth - floor(0.5 * lineWidth)
  612. path.move(to: NSMakePoint(NSMinX(bounds) + ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset))
  613. path.line(to: NSMakePoint(NSMaxX(bounds) - ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset))
  614. } else if self.displayStyle == .rectangle {
  615. let inset = 7.0 + 0.5 * lineWidth
  616. path.appendRect(NSInsetRect(bounds, inset, inset))
  617. } else {
  618. let inset = 7.0 + 0.5 * lineWidth
  619. path.appendOval(in: NSInsetRect(bounds, inset, inset))
  620. }
  621. path.lineWidth = self.lineWidth
  622. if self.style == .dashed {
  623. path.setLineDash(self.dashPattern as? [CGFloat], count: self.dashPattern?.count ?? 0, phase: 0)
  624. }
  625. return path
  626. }
  627. private func _activate(_ exclusive: Bool) {
  628. if (self.canActivate) {
  629. let nc = NotificationCenter.default
  630. let inspector = KMLineInspector.shared
  631. _ = inspector.window
  632. __lwFlags.existsActiveLineWell = 0
  633. nc.post(name: NSNotification.Name(rawValue: SKLineWellWillBecomeActiveNotification), object: self, userInfo: [EXCLUSIVE_KEY : NSNumber(value: exclusive)])
  634. if (__lwFlags.existsActiveLineWell != 0) {
  635. self.takeValue(for: SKLineWellLineWidthKey, from: inspector)
  636. self.takeValue(for: SKLineWellDashPatternKey, from: inspector)
  637. self.takeValue(for: SKLineWellStyleKey, from: inspector)
  638. if (self.displayStyle == .line) {
  639. self.takeValue(for: SKLineWellStartLineStyleKey, from: inspector)
  640. self.takeValue(for: SKLineWellEndLineStyleKey, from: inspector)
  641. }
  642. } else {
  643. inspector.lineWidth = self.lineWidth
  644. inspector.dashPattern = self.dashPattern as? [CGFloat] ?? []
  645. inspector.style = self.style.rawValue
  646. if (self.displayStyle == .line) {
  647. inspector.startLineStyle = self.startLineStyle.rawValue
  648. inspector.endLineStyle = self.endLineStyle.rawValue
  649. }
  650. }
  651. inspector.window?.orderFront(self)
  652. nc.addObserver(self, selector: #selector(_lineWellWillBecomeActive), name: NSNotification.Name(SKLineWellWillBecomeActiveNotification), object: nil)
  653. nc.addObserver(self, selector: #selector(_lineInspectorWindowWillClose), name: NSWindow.willCloseNotification, object: inspector.window)
  654. nc.addObserver(self, selector: #selector(_lineInspectorLineAttributeChanged), name: KMLineInspector.lineAttributeDidChangeNotification, object: inspector)
  655. __lwFlags.active = true
  656. self.setKeyboardFocusRingNeedsDisplay(self.bounds)
  657. self.needsDisplay = true
  658. }
  659. }
  660. @objc private func _lineWellWillBecomeActive(_ notification: NSNotification) {
  661. let sender = notification.object
  662. if (self.isEqual(to: sender) == false && self.isActive) {
  663. if let data = notification.userInfo?[EXCLUSIVE_KEY] as? NSNumber, data.boolValue {
  664. self._deactivate()
  665. } else {
  666. (sender as? KMLineWell)?._existsActiveLineWell()
  667. }
  668. }
  669. }
  670. @objc private func _lineInspectorWindowWillClose(_ notification: Notification) {
  671. self._deactivate()
  672. }
  673. @objc private func _lineInspectorLineAttributeChanged(_ notification: Notification) {
  674. guard let inspector = notification.object as? KMLineInspector else {
  675. return
  676. }
  677. let action = inspector.currentLineChangeAction
  678. var key: String?
  679. if action == .lineWidth {
  680. key = Self.widthKey
  681. } else if action == .style {
  682. key = Self.styleKey
  683. } else if action == .dashPattern {
  684. key = Self.dashPatternKey
  685. } else if action == .startLineStyle {
  686. key = Self.startLineStyleKey
  687. } else if action == .endLineStyle {
  688. key = Self.endLineStyleKey
  689. }
  690. if let _key = key {
  691. self.takeValue(for: _key, from: inspector)
  692. self.sendAction(self._pri_action, to: self._pri_target)
  693. }
  694. }
  695. private func _existsActiveLineWell() {
  696. __lwFlags.existsActiveLineWell = 1
  697. }
  698. }