KMLineWell.swift 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  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. /*
  116. - (void)lineInspectorLineAttributeChanged:(NSNotification *)notification;
  117. */
  118. var canActivate: Bool {
  119. get {
  120. return __lwFlags.canActivate
  121. }
  122. set {
  123. if __lwFlags.canActivate != newValue {
  124. __lwFlags.canActivate = newValue
  125. if self.isActive && __lwFlags.canActivate == false {
  126. self._deactivate()
  127. }
  128. }
  129. }
  130. }
  131. var isActive: Bool {
  132. get {
  133. return __lwFlags.active
  134. }
  135. }
  136. override var isHighlighted: Bool {
  137. get {
  138. return self.__lwFlags.highlighted
  139. }
  140. set {
  141. if self.__lwFlags.highlighted != newValue {
  142. self.__lwFlags.highlighted = newValue
  143. }
  144. }
  145. }
  146. private var _pri_target: AnyObject?
  147. private var _pri_action: Selector?
  148. deinit {
  149. KMPrint("KMLineWell deinit.")
  150. NotificationCenter.default.removeObserver(self)
  151. }
  152. override init(frame frameRect: NSRect) {
  153. super.init(frame: frameRect)
  154. lineWidth = 1.0
  155. style = .solid
  156. dashPattern = nil;
  157. startLineStyle = .none
  158. endLineStyle = .none
  159. __lwFlags.displayStyle = UInt8(KMLineWellDisplayStyle.line.rawValue)
  160. __lwFlags.active = false
  161. __lwFlags.canActivate = false
  162. __lwFlags.existsActiveLineWell = 0
  163. self.target = nil
  164. self.action = nil
  165. self._commonInit()
  166. }
  167. required init?(coder: NSCoder) {
  168. super.init(coder: coder)
  169. lineWidth = coder.decodeDouble(forKey: SKLineWellLineWidthKey)
  170. style = CPDFBorderStyle(rawValue: coder.decodeInteger(forKey: SKLineWellStyleKey)) ?? .solid
  171. dashPattern = coder.decodeObject(forKey: SKLineWellDashPatternKey) as? NSArray
  172. startLineStyle = CPDFLineStyle(rawValue: coder.decodeInteger(forKey: SKLineWellStartLineStyleKey)) ?? .none
  173. endLineStyle = CPDFLineStyle(rawValue: coder.decodeInteger(forKey: SKLineWellEndLineStyleKey)) ?? .none
  174. __lwFlags.displayStyle = UInt8(coder.decodeInteger(forKey: DISPLAYSTYLE_KEY))
  175. __lwFlags.active = coder.decodeBool(forKey: ACTIVE_KEY)
  176. action = NSSelectorFromString(coder.decodeObject(forKey: ACTION_KEY) as? String ?? "")
  177. target = coder.decodeObject(forKey: TARGET_KEY) as AnyObject?
  178. self._commonInit()
  179. }
  180. override func encode(with coder: NSCoder) {
  181. super.encode(with: coder)
  182. coder.encode(lineWidth, forKey: SKLineWellLineWidthKey)
  183. coder.encode(style.rawValue, forKey: SKLineWellStyleKey)
  184. coder.encode(dashPattern, forKey: SKLineWellDashPatternKey)
  185. coder.encode(startLineStyle.rawValue, forKey: SKLineWellStartLineStyleKey)
  186. coder.encode(endLineStyle.rawValue, forKey: SKLineWellEndLineStyleKey)
  187. coder.encode(__lwFlags.displayStyle, forKey: DISPLAYSTYLE_KEY)
  188. coder.encode(__lwFlags.active, forKey: ACTIVE_KEY)
  189. if let data = self.action {
  190. coder.encode(NSStringFromSelector(data), forKey: ACTION_KEY)
  191. }
  192. coder.encode(target, forKey: TARGET_KEY)
  193. }
  194. override var isOpaque: Bool {
  195. return true
  196. }
  197. override var acceptsFirstResponder: Bool {
  198. return self.canActivate
  199. }
  200. override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
  201. return self.canActivate
  202. }
  203. override func viewWillMove(toWindow newWindow: NSWindow?) {
  204. self._deactivate()
  205. super.viewWillMove(toWindow: newWindow)
  206. }
  207. override func draw(_ dirtyRect: NSRect) {
  208. super.draw(dirtyRect)
  209. let bounds = self.bounds
  210. KMDrawTextFieldBezel(bounds, self)
  211. if (self.isActive) {
  212. NSGraphicsContext.saveGraphicsState()
  213. NSColor.selectedControlColor.setFill()
  214. bounds.fill(using: .plusDarker)
  215. NSGraphicsContext.restoreGraphicsState()
  216. }
  217. if (self.isHighlighted) {
  218. NSGraphicsContext.saveGraphicsState()
  219. NSColor(calibratedWhite: 0, alpha: 0.1).setFill()
  220. self.bounds.frame(withWidth: 1, using: .plusDarker)
  221. NSGraphicsContext.restoreGraphicsState()
  222. }
  223. if (lineWidth > 0.0) {
  224. NSGraphicsContext.saveGraphicsState()
  225. NSBezierPath(rect: NSInsetRect(bounds, 2.0, 2.0)).addClip()
  226. NSColor.black.setStroke()
  227. self._path().stroke()
  228. NSGraphicsContext.restoreGraphicsState()
  229. }
  230. if self.refusesFirstResponder == false && NSApp.isActive && self.window!.isKeyWindow && self.window!.firstResponder == self {
  231. NSGraphicsContext.saveGraphicsState()
  232. NSFocusRingPlacement.only.set()
  233. bounds.fill()
  234. NSGraphicsContext.restoreGraphicsState()
  235. }
  236. }
  237. override func mouseDown(with event: NSEvent) {
  238. if (self.isEnabled) {
  239. self.setKeyboardFocusRingNeedsDisplay(self.bounds)
  240. self.needsDisplay = true
  241. // NSUInteger modifiers = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
  242. var modifiers = event.modifierFlags
  243. modifiers.insert(.deviceIndependentFlagsMask)
  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. - (void)lineInspectorLineAttributeChanged:(NSNotification *)notification {
  375. // SKLineInspector *inspector = [notification object];
  376. // NSString *key = nil;
  377. // switch ([inspector currentLineChangeAction]) {
  378. // case SKLineChangeActionLineWidth:
  379. // key = SKLineWellLineWidthKey;
  380. // break;
  381. // case SKLineChangeActionStyle:
  382. // key = SKLineWellStyleKey;
  383. // break;
  384. // case SKLineChangeActionDashPattern:
  385. // key = SKLineWellDashPatternKey;
  386. // break;
  387. // case SKLineChangeActionStartLineStyle:
  388. // if ([self displayStyle] == SKLineWellDisplayStyleLine)
  389. // key = SKLineWellStartLineStyleKey;
  390. // break;
  391. // case SKLineChangeActionEndLineStyle:
  392. // if ([self displayStyle] == SKLineWellDisplayStyleLine)
  393. // key = SKLineWellEndLineStyleKey;
  394. // break;
  395. // case SKNoLineChangeAction:
  396. // break;
  397. // }
  398. // if (key) {
  399. // [self takeValueForKey:key from:inspector];
  400. // [self sendAction:[self action] to:[self target]];
  401. // }
  402. }
  403. #pragma mark NSDraggingSource protocol
  404. - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
  405. return NSDragOperationGeneric;
  406. }
  407. #pragma mark NSDraggingDestination protocol
  408. - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
  409. if ([self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] canReadItemWithDataConformingToTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]]) {
  410. [self setHighlighted:YES];
  411. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  412. [self setNeedsDisplay:YES];
  413. return NSDragOperationEvery;
  414. } else
  415. return NSDragOperationNone;
  416. }
  417. - (void)draggingExited:(id <NSDraggingInfo>)sender {
  418. if ([self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] canReadItemWithDataConformingToTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]]) {
  419. [self setHighlighted:NO];
  420. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  421. [self setNeedsDisplay:YES];
  422. }
  423. }
  424. - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender {
  425. return [self isEnabled] && [sender draggingSource] != self && [[sender draggingPasteboard] canReadItemWithDataConformingToTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]];
  426. }
  427. - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender{
  428. NSPasteboard *pboard = [sender draggingPasteboard];
  429. [pboard types];
  430. NSDictionary *dict = [pboard propertyListForType:SKPasteboardTypeLineStyle];
  431. if ([dict objectForKey:SKLineWellLineWidthKey])
  432. [self takeValueForKey:SKLineWellLineWidthKey from:dict];
  433. if ([dict objectForKey:SKLineWellStyleKey])
  434. [self takeValueForKey:SKLineWellStyleKey from:dict];
  435. [self takeValueForKey:SKLineWellDashPatternKey from:dict];
  436. if ([self displayStyle] == SKLineWellDisplayStyleLine) {
  437. if ([dict objectForKey:SKLineWellStartLineStyleKey])
  438. [self takeValueForKey:SKLineWellStartLineStyleKey from:dict];
  439. if ([dict objectForKey:SKLineWellEndLineStyleKey])
  440. [self takeValueForKey:SKLineWellEndLineStyleKey from:dict];
  441. }
  442. [self sendAction:[self action] to:[self target]];
  443. [self setHighlighted:NO];
  444. [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
  445. [self setNeedsDisplay:YES];
  446. return dict != nil;
  447. }
  448. #pragma mark Accessibility
  449. - (NSArray *)accessibilityAttributeNames {
  450. static NSArray *attributes = nil;
  451. if (attributes == nil) {
  452. attributes = [[NSArray alloc] initWithObjects:
  453. NSAccessibilityRoleAttribute,
  454. NSAccessibilityRoleDescriptionAttribute,
  455. NSAccessibilityValueAttribute,
  456. NSAccessibilityTitleAttribute,
  457. NSAccessibilityHelpAttribute,
  458. NSAccessibilityFocusedAttribute,
  459. NSAccessibilityParentAttribute,
  460. NSAccessibilityWindowAttribute,
  461. NSAccessibilityTopLevelUIElementAttribute,
  462. NSAccessibilityPositionAttribute,
  463. NSAccessibilitySizeAttribute,
  464. nil];
  465. }
  466. return attributes;
  467. }
  468. - (id)accessibilityAttributeValue:(NSString *)attribute {
  469. if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
  470. return NSAccessibilityCheckBoxRole;
  471. } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
  472. return NSAccessibilityRoleDescription(NSAccessibilityCheckBoxRole, nil);
  473. } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
  474. return [NSNumber numberWithInteger:[self isActive]];
  475. } else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) {
  476. return [NSString stringWithFormat:@"%@ %ld", NSLocalizedString(@"line width", @"Accessibility description"), (long)[self lineWidth]];
  477. } else if ([attribute isEqualToString:NSAccessibilityHelpAttribute]) {
  478. return [self toolTip];
  479. } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
  480. // Just check if the app thinks we're focused.
  481. id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute];
  482. return [NSNumber numberWithBool:[focusedElement isEqual:self]];
  483. } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
  484. return NSAccessibilityUnignoredAncestor([self superview]);
  485. } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
  486. // We're in the same window as our parent.
  487. return [NSAccessibilityUnignoredAncestor([self superview]) accessibilityAttributeValue:NSAccessibilityWindowAttribute];
  488. } else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
  489. // We're in the same top level element as our parent.
  490. return [NSAccessibilityUnignoredAncestor([self superview]) accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
  491. } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
  492. return [NSValue valueWithPoint:[[self window] convertBaseToScreen:[self convertPoint:[self bounds].origin toView:nil]]];
  493. } else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
  494. return [NSValue valueWithSize:[self convertSize:[self bounds].size toView:nil]];
  495. } else {
  496. return nil;
  497. }
  498. }
  499. - (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
  500. if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
  501. return [self canActivate];
  502. } else {
  503. return NO;
  504. }
  505. }
  506. - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
  507. if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
  508. [[self window] makeFirstResponder:self];
  509. }
  510. }
  511. // actions
  512. - (NSArray *)accessibilityActionNames {
  513. return [NSArray arrayWithObject:NSAccessibilityPressAction];
  514. }
  515. - (NSString *)accessibilityActionDescription:(NSString *)anAction {
  516. return NSAccessibilityActionDescription(anAction);
  517. }
  518. - (void)accessibilityPerformAction:(NSString *)anAction {
  519. if ([anAction isEqualToString:NSAccessibilityPressAction])
  520. [self performClick:self];
  521. }
  522. // misc
  523. - (BOOL)accessibilityIsIgnored {
  524. return NO;
  525. }
  526. - (id)accessibilityHitTest:(NSPoint)point {
  527. return NSAccessibilityUnignoredAncestor(self);
  528. }
  529. - (id)accessibilityFocusedUIElement {
  530. return NSAccessibilityUnignoredAncestor(self);
  531. }
  532. @end
  533. */
  534. }
  535. // MARK: - Private Methods
  536. extension KMLineWell {
  537. private func _commonInit() {
  538. __lwFlags.canActivate = true
  539. __lwFlags.existsActiveLineWell = 0
  540. self.isEnabled = true
  541. // [self registerForDraggedTypes:[NSArray arrayWithObjects:SKPasteboardTypeLineStyle, nil]];
  542. }
  543. private func _deactivate() {
  544. if self.isActive {
  545. NotificationCenter.default.removeObserver(self)
  546. __lwFlags.active = false
  547. self.setKeyboardFocusRingNeedsDisplay(self.bounds)
  548. self.needsDisplay = true
  549. }
  550. }
  551. private func _changedValue(forKey key: String) {
  552. if (self.isActive) {
  553. // [[SKLineInspector sharedLineInspector] setValue:[self valueForKey:key] forKey:key];
  554. }
  555. self.needsDisplay = true
  556. }
  557. private func _dragImage() -> NSImage? {
  558. let bounds = self.bounds
  559. let path = self.lineWidth > 0.0 ? self._path() : nil
  560. // CGFloat scale = [self backingScale];
  561. let scale = 1.0
  562. let image = NSImage.bitmapImage(with: bounds.size, scale: scale) { rect in
  563. KMContextSetAlpha(NSGraphicsContext.current?.cgContext, 0.7)
  564. NSColor.darkGray.setFill()
  565. _ = NSRect.fill(NSInsetRect(rect, 1.0, 1.0))
  566. NSColor.controlBackgroundColor.setFill()
  567. _ = NSRect.fill(NSInsetRect(rect, 2.0, 2.0))
  568. NSColor.black.setStroke()
  569. path?.stroke()
  570. }
  571. return image
  572. }
  573. private func _path() -> NSBezierPath {
  574. let path = NSBezierPath()
  575. let bounds = self.bounds
  576. if self.displayStyle == .line {
  577. let offset = 0.5 * lineWidth - floor(0.5 * lineWidth)
  578. let startPoint = NSMakePoint(NSMinX(bounds) + ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset)
  579. let endPoint = NSMakePoint(NSMaxX(bounds) - ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset)
  580. switch (self.startLineStyle) {
  581. case .none:
  582. break
  583. case .square:
  584. path.appendRect(NSMakeRect(startPoint.x - 1.5 * lineWidth, startPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth))
  585. break
  586. case .circle:
  587. path.appendOval(in: NSMakeRect(startPoint.x - 1.5 * lineWidth, startPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth))
  588. break
  589. case .diamond:
  590. path.move(to: NSMakePoint(startPoint.x - 1.5 * lineWidth, startPoint.y))
  591. path.line(to: NSMakePoint(startPoint.x, startPoint.y + 1.5 * lineWidth))
  592. path.line(to: NSMakePoint(startPoint.x + 1.5 * lineWidth, startPoint.y))
  593. path.line(to: NSMakePoint(startPoint.x, startPoint.y - 1.5 * lineWidth))
  594. path.close()
  595. break
  596. case .openArrow:
  597. path.move(to: NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y - 1.5 * lineWidth))
  598. path.line(to: NSMakePoint(startPoint.x, startPoint.y))
  599. path.line(to: NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y + 1.5 * lineWidth))
  600. break
  601. case .closedArrow:
  602. path.move(to: NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y - 1.5 * lineWidth))
  603. path.line(to: NSMakePoint(startPoint.x, startPoint.y))
  604. path.line(to: NSMakePoint(startPoint.x + 3.0 * lineWidth, startPoint.y + 1.5 * lineWidth))
  605. path.close()
  606. break
  607. @unknown default:
  608. fatalError()
  609. }
  610. path.move(to: startPoint)
  611. path.line(to: endPoint)
  612. switch (self.endLineStyle) {
  613. case .none:
  614. break
  615. case .square:
  616. path.appendRect(NSMakeRect(endPoint.x - 1.5 * lineWidth, endPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth))
  617. break
  618. case .circle:
  619. path.appendOval(in: NSMakeRect(endPoint.x - 1.5 * lineWidth, endPoint.y - 1.5 * lineWidth, 3 * lineWidth, 3 * lineWidth))
  620. break
  621. case .diamond:
  622. path.move(to: NSMakePoint(endPoint.x + 1.5 * lineWidth, endPoint.y))
  623. path.line(to: NSMakePoint(endPoint.x, endPoint.y + 1.5 * lineWidth))
  624. path.line(to: NSMakePoint(endPoint.x - 1.5 * lineWidth, endPoint.y))
  625. path.line(to: NSMakePoint(endPoint.x, endPoint.y - 1.5 * lineWidth))
  626. path.close()
  627. break
  628. case .openArrow:
  629. path.move(to: NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y + 1.5 * lineWidth))
  630. path.line(to: NSMakePoint(endPoint.x, endPoint.y))
  631. path.line(to: NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y - 1.5 * lineWidth))
  632. break
  633. case .closedArrow:
  634. path.move(to: NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y + 1.5 * lineWidth))
  635. path.line(to: NSMakePoint(endPoint.x, endPoint.y))
  636. path.line(to: NSMakePoint(endPoint.x - 3.0 * lineWidth, endPoint.y - 1.5 * lineWidth))
  637. path.close()
  638. break
  639. @unknown default:
  640. fatalError()
  641. }
  642. } else if self.displayStyle == .simpleLine {
  643. let offset = 0.5 * lineWidth - floor(0.5 * lineWidth)
  644. path.move(to: NSMakePoint(NSMinX(bounds) + ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset))
  645. path.line(to: NSMakePoint(NSMaxX(bounds) - ceil(0.5 * NSHeight(bounds)), round(NSMidY(bounds)) - offset))
  646. } else if self.displayStyle == .rectangle {
  647. let inset = 7.0 + 0.5 * lineWidth
  648. path.appendRect(NSInsetRect(bounds, inset, inset))
  649. } else {
  650. let inset = 7.0 + 0.5 * lineWidth
  651. path.appendOval(in: NSInsetRect(bounds, inset, inset))
  652. }
  653. path.lineWidth = self.lineWidth
  654. if self.style == .dashed {
  655. path.setLineDash(self.dashPattern as? [CGFloat], count: self.dashPattern?.count ?? 0, phase: 0)
  656. }
  657. return path
  658. }
  659. private func _activate(_ exclusive: Bool) {
  660. if (self.canActivate) {
  661. let nc = NotificationCenter.default
  662. let inspector = KMLineInspector.shared
  663. __lwFlags.existsActiveLineWell = 0
  664. nc.post(name: NSNotification.Name(rawValue: SKLineWellWillBecomeActiveNotification), object: self, userInfo: [EXCLUSIVE_KEY : NSNumber(value: exclusive)])
  665. if (__lwFlags.existsActiveLineWell != 0) {
  666. self.takeValue(for: SKLineWellLineWidthKey, from: inspector)
  667. self.takeValue(for: SKLineWellDashPatternKey, from: inspector)
  668. self.takeValue(for: SKLineWellStyleKey, from: inspector)
  669. if (self.displayStyle == .line) {
  670. self.takeValue(for: SKLineWellStartLineStyleKey, from: inspector)
  671. self.takeValue(for: SKLineWellEndLineStyleKey, from: inspector)
  672. }
  673. } else {
  674. inspector.lineWidth = self.lineWidth
  675. inspector.dashPattern = self.dashPattern as? [CGFloat] ?? []
  676. inspector.style = self.style.rawValue
  677. if (self.displayStyle == .line) {
  678. inspector.startLineStyle = self.startLineStyle.rawValue
  679. inspector.endLineStyle = self.endLineStyle.rawValue
  680. }
  681. }
  682. inspector.window?.orderFront(self)
  683. nc.addObserver(self, selector: #selector(_lineWellWillBecomeActive), name: NSNotification.Name(SKLineWellWillBecomeActiveNotification), object: nil)
  684. nc.addObserver(self, selector: #selector(_lineInspectorWindowWillClose), name: NSWindow.willCloseNotification, object: inspector.window)
  685. nc.addObserver(self, selector: #selector(_lineInspectorLineAttributeChanged), name: KMLineInspector.lineAttributeDidChangeNotification, object: inspector)
  686. __lwFlags.active = true
  687. self.setKeyboardFocusRingNeedsDisplay(self.bounds)
  688. self.needsDisplay = true
  689. }
  690. }
  691. @objc private func _lineWellWillBecomeActive(_ notification: NSNotification) {
  692. let sender = notification.object
  693. if (self.isEqual(to: sender) == false && self.isActive) {
  694. if let data = notification.userInfo?[EXCLUSIVE_KEY] as? NSNumber, data.boolValue {
  695. self._deactivate()
  696. } else {
  697. (sender as? KMLineWell)?._existsActiveLineWell()
  698. }
  699. }
  700. }
  701. @objc private func _lineInspectorWindowWillClose(_ notification: Notification) {
  702. self._deactivate()
  703. }
  704. @objc private func _lineInspectorLineAttributeChanged(_ notification: Notification) {
  705. guard let inspector = notification.object as? KMLineInspector else {
  706. return
  707. }
  708. let action = inspector.currentLineChangeAction
  709. var key: String?
  710. if action == .lineWidth {
  711. key = Self.widthKey
  712. } else if action == .style {
  713. key = Self.styleKey
  714. } else if action == .dashPattern {
  715. key = Self.dashPatternKey
  716. } else if action == .startLineStyle {
  717. key = Self.startLineStyleKey
  718. } else if action == .endLineStyle {
  719. key = Self.endLineStyleKey
  720. }
  721. if let _key = key {
  722. self.takeValue(for: _key, from: inspector)
  723. self.sendAction(self._pri_action, to: self._pri_target)
  724. }
  725. }
  726. private func _existsActiveLineWell() {
  727. __lwFlags.existsActiveLineWell = 1
  728. }
  729. }