feat: add macos runtime

This commit is contained in:
dscyrescotti
2024-07-06 14:20:31 +07:00
parent 8d33b94789
commit 15e96136f1
41 changed files with 367 additions and 114 deletions
+17
View File
@@ -0,0 +1,17 @@
//
// Array++.swift
// Memola
//
// Created by Dscyre Scotti on 5/4/24.
//
import Foundation
extension Array {
mutating func append(_ element: Element, capacity: Int) {
if count >= capacity {
remove(at: 0)
}
append(element)
}
}
@@ -0,0 +1,14 @@
//
// CGAffineTransform++.swift
// Memola
//
// Created by Dscyre Scotti on 5/4/24.
//
import Foundation
extension CGAffineTransform {
static func * (lhs: CGAffineTransform, rhs: CGAffineTransform) -> CGAffineTransform {
return lhs.concatenating(rhs)
}
}
+14
View File
@@ -0,0 +1,14 @@
//
// CGFloat++.swift
// Memola
//
// Created by Dscyre Scotti on 5/4/24.
//
import Foundation
extension CGFloat {
var float: Float {
Float(self)
}
}
+29
View File
@@ -0,0 +1,29 @@
//
// CGPoint++.swift
// Memola
//
// Created by Dscyre Scotti on 5/4/24.
//
import Foundation
extension CGPoint {
func muliply(by factor: CGFloat) -> CGPoint {
CGPoint(x: x * factor, y: y * factor)
}
func distance(to point: CGPoint) -> CGFloat {
let p = pow(x - point.x, 2) + pow(y - point.y, 2)
return sqrt(p)
}
static func middle(p1: CGPoint, p2: CGPoint) -> CGPoint {
return CGPoint(x: (p1.x + p2.x) * 0.5, y: (p1.y + p2.y) * 0.5)
}
func angle(to point: CGPoint) -> CGFloat {
let deltaX = point.x - x
let deltaY = point.y - y
return atan2(deltaY, deltaX)
}
}
+27
View File
@@ -0,0 +1,27 @@
//
// CGRect++.swift
// Memola
//
// Created by Dscyre Scotti on 5/4/24.
//
import Foundation
extension CGRect {
func transform(to rect: CGRect) -> CGAffineTransform {
var t = CGAffineTransform.identity
t = t.translatedBy(x: -self.minX, y: -self.minY)
t = t.translatedBy(x: rect.minX, y: rect.minY)
t = t.scaledBy(x: 1 / self.width, y: 1 / self.height)
t = t.scaledBy(x: rect.width, y: rect.height)
return t
}
func muliply(by factor: CGFloat) -> CGRect {
CGRect(origin: origin.muliply(by: factor), size: size.multiply(by: factor))
}
var box: Box {
Box(minX: minX, minY: minY, maxX: maxX, maxY: maxY)
}
}
+14
View File
@@ -0,0 +1,14 @@
//
// CGSize++.swift
// Memola
//
// Created by Dscyre Scotti on 5/4/24.
//
import Foundation
extension CGSize {
func multiply(by scale: CGFloat) -> CGSize {
CGSize(width: width * scale, height: height * scale)
}
}
@@ -0,0 +1,21 @@
//
// Collection++.swift
// Memola
//
// Created by Dscyre Scotti on 5/4/24.
//
import Foundation
extension Collection {
subscript(safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
extension Collection where Index == Int {
subscript(fromEnd index: Index) -> Element? {
let i = count - (index + 1)
return self[safe: i]
}
}
+65
View File
@@ -0,0 +1,65 @@
//
// Color++.swift
// Memola
//
// Created by Dscyre Scotti on 5/4/24.
//
import SwiftUI
extension Color {
init(color: Platform.Color) {
#if os(macOS)
self = Color(nsColor: color)
#else
self = Color(uiColor: color)
#endif
}
var components: [CGFloat] {
let color = Platform.Color(self)
return color.components
}
static func rgba(from color: [CGFloat]) -> Color {
Color(red: color[0], green: color[1], blue: color[2]).opacity(color[3])
}
}
extension Color {
var hsba: (hue: Double, saturation: Double, brightness: Double, alpha: Double) {
#if os(macOS)
#warning("TODO: need double check")
let nsColor = NSColor(self)
var hue: CGFloat = 0
var saturation: CGFloat = 0
var brightness: CGFloat = 0
var alpha: CGFloat = 0
nsColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
return (hue, saturation, brightness, alpha)
#else
let uiColor = UIColor(self)
var hue: CGFloat = 0
var saturation: CGFloat = 0
var brightness: CGFloat = 0
var alpha: CGFloat = 0
uiColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
return (hue, saturation, brightness, alpha)
#endif
}
}
extension Platform.Color {
var components: [CGFloat] {
#if os(macOS)
#warning("TODO: need double check")
let nsColor: NSColor = self
let ciColor: CIColor = .init(color: nsColor) ?? CIColor(red: 0, green: 0, blue: 0)
return [ciColor.red, ciColor.green, ciColor.blue, ciColor.alpha]
#else
let uiColor: UIColor = self
let ciColor: CIColor = .init(color: uiColor)
return [ciColor.red, ciColor.green, ciColor.blue, ciColor.alpha]
#endif
}
}
+18
View File
@@ -0,0 +1,18 @@
//
// Data++.swift
// Memola
//
// Created by Dscyre Scotti on 6/16/24.
//
import Foundation
extension Data {
func getBookmarkURL() -> URL? {
var isStale = false
guard let bookmarkURL = try? URL(resolvingBookmarkData: self, options: .withoutUI, relativeTo: nil, bookmarkDataIsStale: &isStale) else {
return nil
}
return bookmarkURL
}
}
+32
View File
@@ -0,0 +1,32 @@
//
// Date++.swift
// Memola
//
// Created by Dscyre Scotti on 6/27/24.
//
import Foundation
extension Date {
func getTimeDifference(to date: Date) -> String {
let calendar = Calendar.current
let components = calendar.dateComponents([.minute, .hour, .day, .weekOfYear, .month, .year], from: self, to: date)
if let years = components.year, years > 0 {
return "\(years) year\(years > 1 ? "s" : "") ago"
} else if let months = components.month, months > 0 {
return "\(months) month\(months > 1 ? "s" : "") ago"
} else if let weeks = components.weekOfYear, weeks > 0 {
return "\(weeks) week\(weeks > 1 ? "s" : "") ago"
} else if let days = components.day, days > 0 {
return "\(days) day\(days > 1 ? "s" : "") ago"
} else if let hours = components.hour, hours > 0 {
return "\(hours) hour\(hours > 1 ? "s" : "") ago"
} else if let minutes = components.minute, minutes > 0 {
return "\(minutes) minute\(minutes > 1 ? "s" : "") ago"
} else {
return "just now"
}
}
}
+14
View File
@@ -0,0 +1,14 @@
//
// Float++.swift
// Memola
//
// Created by Dscyre Scotti on 5/12/24.
//
import Foundation
extension Float {
var cgFloat: CGFloat {
CGFloat(self)
}
}
+36
View File
@@ -0,0 +1,36 @@
//
// Image++.swift
// Memola
//
// Created by Dscyre Scotti on 6/15/24.
//
import SwiftUI
import Foundation
extension Image {
init(image: Platform.Image) {
#if os(macOS)
self = Image(nsImage: image)
#else
self = Image(uiImage: image)
#endif
}
}
#if os(iOS)
extension UIImage {
func imageWithUpOrientation() -> UIImage? {
switch imageOrientation {
case .up:
return self
default:
UIGraphicsBeginImageContextWithOptions(size, false, scale)
draw(in: CGRect(origin: .zero, size: size))
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result
}
}
}
#endif
@@ -0,0 +1,14 @@
//
// MTLDevice++.swift
// Memola
//
// Created by Dscyre Scotti on 5/4/24.
//
import MetalKit
extension MTLDevice {
func maximumTextureDimension() -> Int {
supportsFamily(.apple3) ? 16384 : 8192
}
}
@@ -0,0 +1,48 @@
//
// MTLTexture++.swift
// Memola
//
// Created by Dscyre Scotti on 7/4/24.
//
import MetalKit
import Foundation
extension MTLTexture {
private func bytes() -> UnsafeMutableRawPointer {
let width = self.width
let height = self.height
let rowBytes = self.width * 4
let p = malloc(width * height * 4)!
getBytes(p, bytesPerRow: rowBytes, from: MTLRegionMake2D(0, 0, width, height), mipmapLevel: 0)
return p
}
func getImage() -> CGImage? {
let bytes = self.bytes()
let pColorSpace = CGColorSpaceCreateDeviceRGB()
let rawBitmapInfo = CGImageAlphaInfo.noneSkipFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
let bitmapInfo = CGBitmapInfo(rawValue: rawBitmapInfo)
let selftureSize = self.width * self.height * 4
let rowBytes = self.width * 4
if let provider = CGDataProvider(dataInfo: nil, data: bytes, size: selftureSize, releaseData: { _, p, _ in
p.deallocate()
}) {
return CGImage(
width: width,
height: height,
bitsPerComponent: 8,
bitsPerPixel: 32,
bytesPerRow: rowBytes,
space: pColorSpace,
bitmapInfo: bitmapInfo,
provider: provider,
decode: nil,
shouldInterpolate: true,
intent: CGColorRenderingIntent.defaultIntent
)
}
return nil
}
}
@@ -0,0 +1,15 @@
//
// NSManagedObject++.swift
// Memola
//
// Created by Dscyre Scotti on 5/11/24.
//
import CoreData
import Foundation
extension NSManagedObject {
convenience init(_ keyPath: KeyPath<Persistence, NSManagedObjectContext>) {
self.init(context: Persistence.shared[keyPath: keyPath])
}
}
@@ -0,0 +1,16 @@
//
// NSManagedObjectContext++.swift
// Memola
//
// Created by Dscyre Scotti on 5/11/24.
//
import CoreData
extension NSManagedObjectContext {
func saveIfNeeded() throws {
if hasChanges {
try save()
}
}
}
+16
View File
@@ -0,0 +1,16 @@
//
// View++.swift
// Memola
//
// Created by Dscyre Scotti on 5/11/24.
//
import SwiftUI
import CoreData
import Foundation
extension View {
func persistence(_ keyPath: KeyPath<Persistence, NSManagedObjectContext>) -> some View {
environment(\.managedObjectContext, Persistence.shared[keyPath: keyPath])
}
}
@@ -0,0 +1,21 @@
//
// simd_float4x4++.swift
// Memola
//
// Created by Dscyre Scotti on 5/4/24.
//
import MetalKit
import Foundation
extension simd_float4x4 {
init(_ transform: CGAffineTransform) {
let t = CATransform3DMakeAffineTransform(transform)
self = simd_float4x4([
[Float(t.m11), Float(t.m12), Float(t.m13), Float(t.m14)],
[Float(t.m21), Float(t.m22), Float(t.m23), Float(t.m24)],
[Float(t.m31), Float(t.m32), Float(t.m33), Float(t.m34)],
[Float(t.m41), Float(t.m42), Float(t.m43), Float(t.m44)]
])
}
}
+30
View File
@@ -0,0 +1,30 @@
//
// Platform.swift
// Memola
//
// Created by Dscyre Scotti on 7/6/24.
//
import SwiftUI
enum Platform {
#if os(macOS)
typealias View = NSView
typealias Color = NSColor
typealias Image = NSImage
typealias ScrollView = NSScrollView
typealias Application = NSApplication
typealias ViewController = NSViewController
typealias TapGestureRecognizer = NSClickGestureRecognizer
typealias ViewControllerRepresentable = NSViewControllerRepresentable
#else
typealias View = UIView
typealias Color = UIColor
typealias Image = UIImage
typealias ScrollView = UIScrollView
typealias Application = UIApplication
typealias ViewController = UIViewController
typealias TapGestureRecognizer = UITapGestureRecognizer
typealias ViewControllerRepresentable = UIViewControllerRepresentable
#endif
}