KMLineWell.swift 34 KB

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