mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-07-13 08:12:54 +02:00
feat: add macos runtime
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// CGFloat++.swift
|
||||
// Memola
|
||||
//
|
||||
// Created by Dscyre Scotti on 5/4/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension CGFloat {
|
||||
var float: Float {
|
||||
Float(self)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// Float++.swift
|
||||
// Memola
|
||||
//
|
||||
// Created by Dscyre Scotti on 5/12/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Float {
|
||||
var cgFloat: CGFloat {
|
||||
CGFloat(self)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)]
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user