KMLineWell.swift 35 KB

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