Skip to content

Instantly share code, notes, and snippets.

@lucaswkuipers
Created October 14, 2024 23:13
Show Gist options
  • Save lucaswkuipers/6f2264afd4a7fe51cdeca5cd5f1f9e6b to your computer and use it in GitHub Desktop.
Save lucaswkuipers/6f2264afd4a7fe51cdeca5cd5f1f9e6b to your computer and use it in GitHub Desktop.
import Cocoa
final class AppDelegate: NSObject, NSApplicationDelegate {
private var statusItem: NSStatusItem!
func applicationDidFinishLaunching(_ aNotification: Notification) {
statusItem = NSStatusBar.system.statusItem(withLength: 450)
let contentView = ContentView()
contentView.translatesAutoresizingMaskIntoConstraints = false
if let button = statusItem.button {
button.addSubview(contentView)
NSLayoutConstraint.activate([
contentView.leadingAnchor.constraint(equalTo: button.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: button.trailingAnchor),
contentView.topAnchor.constraint(equalTo: button.topAnchor),
contentView.bottomAnchor.constraint(equalTo: button.bottomAnchor)
])
}
}
}
final class ContentView: NSView {
var ballPosition: CGPoint = .zero
var ballVelocity: CGPoint = CGPoint(x: 0.5, y: 0.25)
let ballRadius: CGFloat = 8
var timer: Timer?
let leftBorderWidth: CGFloat = 2
let rightBorderWidth: CGFloat = 2
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.wantsLayer = true
setupTimer()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupTimer() {
self.ballPosition = CGPoint(x: self.bounds.width / 2, y: self.bounds.height / 2)
timer = Timer.scheduledTimer(withTimeInterval: 1/120, repeats: true) { [weak self] _ in
self?.updateBall()
}
}
private func updateBall() {
ballPosition.x += ballVelocity.x
ballPosition.y += ballVelocity.y
let boundsWidth = self.bounds.width
let boundsHeight = self.bounds.height
let leftBoundary = leftBorderWidth + ballRadius - 1.5
let rightBoundary = boundsWidth - rightBorderWidth + 1.5
if ballPosition.x <= leftBoundary {
ballPosition.x = leftBoundary
ballVelocity.x *= -1
}
if ballPosition.x + ballRadius >= rightBoundary {
ballPosition.x = rightBoundary - ballRadius
ballVelocity.x *= -1
}
if ballPosition.y >= boundsHeight {
ballPosition.y = boundsHeight
ballVelocity.y *= -1
}
if ballPosition.y <= -0.5 {
ballPosition.y = -0.5
ballVelocity.y *= -1
}
self.needsDisplay = true
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
if let ctx = NSGraphicsContext.current?.cgContext {
ctx.clear(dirtyRect)
}
NSColor.white.setFill()
let leftBorderRect = NSRect(
x: 0,
y: 0,
width: leftBorderWidth,
height: self.bounds.height
)
leftBorderRect.fill()
let rightBorderRect = NSRect(
x: self.bounds.width - rightBorderWidth,
y: 0,
width: rightBorderWidth,
height: self.bounds.height
)
rightBorderRect.fill()
let ballRect = NSRect(
x: ballPosition.x - ballRadius,
y: ballPosition.y - ballRadius,
width: ballRadius * 2,
height: ballRadius * 2
)
let path = NSBezierPath(ovalIn: ballRect)
NSColor.black.setFill()
path.fill()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment