Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offset hint #430

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
63 changes: 50 additions & 13 deletions ViMac-Swift/HintView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -18,40 +20,74 @@ 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.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

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) {
Expand Down Expand Up @@ -109,7 +145,8 @@ class HintText: NSTextField {

self.isEditable = false
}



func updateTypedText(typed: String) {
let hintText = self.attributedStringValue.string
let attr = NSMutableAttributedString(string: hintText)
Expand Down
26 changes: 13 additions & 13 deletions ViMac-Swift/Modes/HintModeController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
// 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)
}
}

enum HintAction: String {
Expand Down Expand Up @@ -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)

Expand Down
23 changes: 8 additions & 15 deletions ViMac-Swift/Modes/HintsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,15 @@ 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 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: "")
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)
)
}()

// position hint just below element
let hintOrigin = NSPoint(
x: clickPosition.x - (view.intrinsicContentSize.width / 2),
y: clickPosition.y - (view.intrinsicContentSize.height)
)

if hintOrigin.x.isNaN || hintOrigin.y.isNaN {
return nil
Expand Down