From 0b176024ca5e7766f8fadbeddd026f42ab104606 Mon Sep 17 00:00:00 2001 From: Dean Blackketter Date: Mon, 26 Apr 2021 08:40:19 -0700 Subject: [PATCH 1/5] Hints displayed in center of target area, offset below --- ViMac-Swift/Modes/HintModeController.swift | 26 ++++++++++----------- ViMac-Swift/Modes/HintsViewController.swift | 25 +++++++++----------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/ViMac-Swift/Modes/HintModeController.swift b/ViMac-Swift/Modes/HintModeController.swift index da21de7..3732dc7 100644 --- a/ViMac-Swift/Modes/HintModeController.swift +++ b/ViMac-Swift/Modes/HintModeController.swift @@ -97,6 +97,18 @@ class ContentViewController: NSViewController { struct Hint { let element: Element let text: String + func clickPosition() -> NSPoint { + // hints are shown at the bottom-left for AXLinks (see HintsViewController#renderHint), + // so a click is performed there + if element.role == "AXLink" { + return NSPoint( + // tiny offset in case clicking on the edge of the element does nothing + x: element.frame.origin.x + 5, + y: element.frame.origin.y + element.frame.height - 5 + ) + } + return GeometryUtils.center(element.frame) + } } enum HintAction: String { @@ -316,19 +328,7 @@ class HintModeController: ModeController { } private func performHintAction(_ hint: Hint, action: HintAction) { - let element = hint.element - let clickPosition: NSPoint = { - // hints are shown at the bottom-left for AXLinks (see HintsViewController#renderHint), - // so a click is performed there - if element.role == "AXLink" { - return NSPoint( - // tiny offset in case clicking on the edge of the element does nothing - x: element.frame.origin.x + 5, - y: element.frame.origin.y + element.frame.height - 5 - ) - } - return GeometryUtils.center(element.frame) - }() + let clickPosition = hint.clickPosition() Utils.moveMouse(position: clickPosition) diff --git a/ViMac-Swift/Modes/HintsViewController.swift b/ViMac-Swift/Modes/HintsViewController.swift index c739a2a..b542b7e 100644 --- a/ViMac-Swift/Modes/HintsViewController.swift +++ b/ViMac-Swift/Modes/HintsViewController.swift @@ -70,22 +70,19 @@ class HintsViewController: NSViewController { // are you changing the location where hints are rendered? // make sure to update HintModeController#performHintAction as well func renderHint(_ hint: Hint) -> HintView? { - let view = HintView(associatedElement: hint.element, hintTextSize: CGFloat(textSize), hintText: hint.text, typedHintText: "") guard let elementFrame = self.elementFrame(hint.element) else { return nil } - let hintOrigin: NSPoint = { - // position hint on bottom-left of AXLinks (see #373) - if hint.element.role == "AXLink" { - return elementFrame.origin - } - - // position hint on center of element - let elementCenter = GeometryUtils.center(elementFrame) - return NSPoint( - x: elementCenter.x - (view.intrinsicContentSize.width / 2), - y: elementCenter.y - (view.intrinsicContentSize.height / 2) - ) - }() + let elementCenter = GeometryUtils.center(elementFrame) + + //let clickPosition = hint.clickPosition() + + let view = HintView(associatedElement: hint.element, hintTextSize: CGFloat(textSize), hintText: hint.text, typedHintText: "") + + // position hint just below element + let hintOrigin = NSPoint( + x: elementCenter.x - (view.intrinsicContentSize.width / 2), + y: elementCenter.y - (view.intrinsicContentSize.height * 1.2)// / 2) + ) if hintOrigin.x.isNaN || hintOrigin.y.isNaN { return nil From 092743c88e8bb9f7adac098d445abb94672bf456 Mon Sep 17 00:00:00 2001 From: Dean Blackketter Date: Sat, 1 May 2021 13:10:30 -0700 Subject: [PATCH 2/5] Draw a pointer from the offset hint to the element center --- ViMac-Swift/HintView.swift | 69 +++++++++++++++++---- ViMac-Swift/Modes/HintsViewController.swift | 2 +- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/ViMac-Swift/HintView.swift b/ViMac-Swift/HintView.swift index 24b1264..874abe2 100644 --- a/ViMac-Swift/HintView.swift +++ b/ViMac-Swift/HintView.swift @@ -9,6 +9,8 @@ import Cocoa import AXSwift +let scale: CGFloat = 1.0 // adjust to debug at a larger size + class HintView: NSView { static let borderColor = NSColor.darkGray static let backgroundColor = NSColor(red: 255 / 255, green: 224 / 255, blue: 112 / 255, alpha: 1) @@ -18,40 +20,80 @@ class HintView: NSView { let associatedElement: Element var hintTextView: HintText? - let borderWidth: CGFloat = 1.0 - let cornerRadius: CGFloat = 3.0 + let borderWidth: CGFloat = 1.0 * scale + let cornerRadius: CGFloat = 2.0 * scale required init(associatedElement: Element, hintTextSize: CGFloat, hintText: String, typedHintText: String) { self.associatedElement = associatedElement super.init(frame: .zero) - self.hintTextView = HintText(hintTextSize: hintTextSize, hintText: hintText, typedHintText: typedHintText) + self.hintTextView = HintText(hintTextSize: hintTextSize * scale, hintText: hintText, typedHintText: typedHintText) self.subviews.append(hintTextView!) self.wantsLayer = true - - self.layer?.borderWidth = borderWidth - - self.layer?.backgroundColor = HintView.backgroundColor.cgColor - self.layer?.borderColor = HintView.borderColor.cgColor - self.layer?.cornerRadius = cornerRadius + // self.layer?.borderWidth = borderWidth + +// self.layer?.backgroundColor = NSColor.red.cgColor // HintView.backgroundColor.cgColor +// self.layer?.borderColor = HintView.borderColor.cgColor +// self.layer?.cornerRadius = cornerRadius self.translatesAutoresizingMaskIntoConstraints = false - self.hintTextView!.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true +// self.hintTextView!.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true + self.hintTextView!.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -2 * borderWidth).isActive = true + self.hintTextView!.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true self.widthAnchor.constraint(equalToConstant: width()).isActive = true self.heightAnchor.constraint(equalToConstant: height()).isActive = true } - + + override func draw(_ dirtyRect: NSRect) { + let textWidth: CGFloat = self.hintTextView!.intrinsicContentSize.width + let textHeight: CGFloat = self.hintTextView!.intrinsicContentSize.height + let pointerLength: CGFloat = textHeight / 2 // as a fraction of the text height + let pointerWidth: CGFloat = textHeight / 2 // as a fraction of the text _height_ + + let border = NSBezierPath.init() + + border.lineWidth = borderWidth + border.lineJoinStyle = .miter + + HintView.backgroundColor.setFill() + HintView.borderColor.setStroke() + + border.move(to:NSPoint(x:borderWidth, y:cornerRadius + borderWidth)) + + border.relativeLine(to:NSPoint(x:0,y:textHeight + borderWidth - 2 * cornerRadius)) + border.appendArc(withCenter: NSPoint(x:border.currentPoint.x + cornerRadius, y:border.currentPoint.y), radius: cornerRadius, startAngle: 180.0, endAngle: 90.0, clockwise: true) + + border.relativeLine(to:NSPoint(x:textWidth / 2.0 - cornerRadius - pointerWidth / 2.0, y: 0)) + + border.relativeLine(to:NSPoint(x:pointerWidth/2.0, y: pointerLength)) + border.relativeLine(to:NSPoint(x:pointerWidth/2.0, y: -pointerLength)) + + border.relativeLine(to:NSPoint(x:textWidth / 2.0 - cornerRadius - pointerWidth / 2.0, y: 0)) + + + border.appendArc(withCenter: NSPoint(x:border.currentPoint.x, y:border.currentPoint.y - cornerRadius), radius: cornerRadius, startAngle: 90.0, endAngle: 0.0, clockwise: true) + + border.relativeLine(to:NSPoint(x:0,y:-(textHeight + borderWidth - 2 * cornerRadius))) + border.appendArc(withCenter: NSPoint(x:border.currentPoint.x - cornerRadius, y:border.currentPoint.y), radius: cornerRadius, startAngle: 0, endAngle: 270, clockwise: true) + + border.relativeLine(to:NSPoint(x:-(textWidth - 2 * cornerRadius),y: 0)) + border.appendArc(withCenter: NSPoint(x:border.currentPoint.x, y:border.currentPoint.y + cornerRadius), radius: cornerRadius, startAngle: 270.0, endAngle: 180.0, clockwise: true) + + border.fill() + border.stroke() + } + private func width() -> CGFloat { return self.hintTextView!.intrinsicContentSize.width + 2 * borderWidth } private func height() -> CGFloat { - self.hintTextView!.intrinsicContentSize.height + 2 * borderWidth + self.hintTextView!.intrinsicContentSize.height * (1.5) + 2 * borderWidth } required init?(coder: NSCoder) { @@ -109,7 +151,8 @@ class HintText: NSTextField { self.isEditable = false } - + + func updateTypedText(typed: String) { let hintText = self.attributedStringValue.string let attr = NSMutableAttributedString(string: hintText) diff --git a/ViMac-Swift/Modes/HintsViewController.swift b/ViMac-Swift/Modes/HintsViewController.swift index b542b7e..c64711a 100644 --- a/ViMac-Swift/Modes/HintsViewController.swift +++ b/ViMac-Swift/Modes/HintsViewController.swift @@ -81,7 +81,7 @@ class HintsViewController: NSViewController { // position hint just below element let hintOrigin = NSPoint( x: elementCenter.x - (view.intrinsicContentSize.width / 2), - y: elementCenter.y - (view.intrinsicContentSize.height * 1.2)// / 2) + y: elementCenter.y - (view.intrinsicContentSize.height) ) if hintOrigin.x.isNaN || hintOrigin.y.isNaN { From cea63b59dba562eaaa9cfde1c6ee881eb7f8fc4e Mon Sep 17 00:00:00 2001 From: Dean Blackketter Date: Sat, 1 May 2021 13:25:16 -0700 Subject: [PATCH 3/5] Remove commented out code --- ViMac-Swift/HintView.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ViMac-Swift/HintView.swift b/ViMac-Swift/HintView.swift index 874abe2..bc305d3 100644 --- a/ViMac-Swift/HintView.swift +++ b/ViMac-Swift/HintView.swift @@ -32,15 +32,9 @@ class HintView: NSView { self.wantsLayer = true - // self.layer?.borderWidth = borderWidth - -// self.layer?.backgroundColor = NSColor.red.cgColor // HintView.backgroundColor.cgColor -// self.layer?.borderColor = HintView.borderColor.cgColor -// self.layer?.cornerRadius = cornerRadius self.translatesAutoresizingMaskIntoConstraints = false -// self.hintTextView!.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true self.hintTextView!.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -2 * borderWidth).isActive = true self.hintTextView!.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true From f5105cca27d14ab98c88ac297a2a6c19b63e332e Mon Sep 17 00:00:00 2001 From: Dean Blackketter Date: Wed, 5 May 2021 13:06:28 -0700 Subject: [PATCH 4/5] Move pointer tip back to the click position --- ViMac-Swift/Modes/HintsViewController.swift | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/ViMac-Swift/Modes/HintsViewController.swift b/ViMac-Swift/Modes/HintsViewController.swift index c64711a..997e502 100644 --- a/ViMac-Swift/Modes/HintsViewController.swift +++ b/ViMac-Swift/Modes/HintsViewController.swift @@ -70,18 +70,14 @@ class HintsViewController: NSViewController { // are you changing the location where hints are rendered? // make sure to update HintModeController#performHintAction as well func renderHint(_ hint: Hint) -> HintView? { - guard let elementFrame = self.elementFrame(hint.element) else { return nil } - - let elementCenter = GeometryUtils.center(elementFrame) - - //let clickPosition = hint.clickPosition() + let clickPosition = GeometryUtils.convertAXFrameToGlobal(NSRect(origin: hint.clickPosition(), size: CGSize(width:0,height:0))).origin let view = HintView(associatedElement: hint.element, hintTextSize: CGFloat(textSize), hintText: hint.text, typedHintText: "") // position hint just below element let hintOrigin = NSPoint( - x: elementCenter.x - (view.intrinsicContentSize.width / 2), - y: elementCenter.y - (view.intrinsicContentSize.height) + x: clickPosition.x - (view.intrinsicContentSize.width / 2), + y: clickPosition.y - (view.intrinsicContentSize.height) ) if hintOrigin.x.isNaN || hintOrigin.y.isNaN { From 2499ae4a6cbff7d2b264d1cb8143641155b87508 Mon Sep 17 00:00:00 2001 From: Dean Blackketter Date: Wed, 5 May 2021 16:52:53 -0700 Subject: [PATCH 5/5] Inset the corner by 10 pixels instead of 5 and keep in the lower-left corner --- ViMac-Swift/Modes/HintModeController.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ViMac-Swift/Modes/HintModeController.swift b/ViMac-Swift/Modes/HintModeController.swift index da21de7..b6e9b6a 100644 --- a/ViMac-Swift/Modes/HintModeController.swift +++ b/ViMac-Swift/Modes/HintModeController.swift @@ -322,9 +322,9 @@ class HintModeController: ModeController { // so a click is performed there if element.role == "AXLink" { return NSPoint( - // tiny offset in case clicking on the edge of the element does nothing - x: element.frame.origin.x + 5, - y: element.frame.origin.y + element.frame.height - 5 + // inset from corner by 10 pixels, but no more than halfway + x: element.frame.origin.x + min(element.frame.width / 2, 10), + y: element.frame.origin.y + element.frame.height - min(element.frame.height / 2, 10) ) } return GeometryUtils.center(element.frame)