Match SwiftUI Color (#177)

This commit is contained in:
Carson Katri 2020-07-13 11:27:55 -04:00 committed by GitHub
parent 97406f1383
commit 2b3010a631
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 178 additions and 10 deletions

View File

@ -15,9 +15,13 @@
// Created by Max Desiatov on 16/10/2018.
//
public struct Color: Equatable {
public enum Space {
public struct Color: Hashable, Equatable {
// FIXME: This is not injected.
@Environment(\.accentColor) static var envAccentColor: Color?
public enum RGBColorSpace {
case sRGB
case sRGBLinear
case displayP3
}
@ -25,13 +29,13 @@ public struct Color: Equatable {
public let green: Double
public let blue: Double
public let opacity: Double
public let space: Space
public let space: RGBColorSpace
public init(_ colorSpace: Space = .sRGB,
public init(_ colorSpace: RGBColorSpace = .sRGB,
red: Double,
green: Double,
blue: Double,
opacity: Double) {
opacity: Double = 1) {
self.red = red
self.green = green
self.blue = blue
@ -39,11 +43,50 @@ public struct Color: Equatable {
space = colorSpace
}
public static var white = Color(red: 1.0, green: 1.0, blue: 1.0, opacity: 1.0)
public static var black = Color(red: 0.0, green: 0.0, blue: 0.0, opacity: 1.0)
public static var red = Color(red: 1.0, green: 0.0, blue: 0.0, opacity: 1.0)
public static var green = Color(red: 0.0, green: 1.0, blue: 0.0, opacity: 1.0)
public static var blue = Color(red: 0.0, green: 0.0, blue: 1.0, opacity: 1.0)
public init(_ colorSpace: RGBColorSpace = .sRGB,
white: Double,
opacity: Double = 1) {
red = white
green = white
blue = white
self.opacity = opacity
space = colorSpace
}
// Source for the formula:
// https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB_alternative
public init(hue: Double,
saturation: Double,
brightness: Double,
opacity: Double = 1) {
let a = saturation * min(brightness / 2, 1 - (brightness / 2))
let f: (Int) -> Double = { n in
let k = Double((n + Int(hue * 12)) % 12)
return brightness - (a * max(-1, min(k - 3, 9 - k, 1)))
}
red = f(0)
green = f(8)
blue = f(4)
self.opacity = opacity
space = .sRGB
}
}
extension Color {
public static let clear: Self = .init(red: 0, green: 0, blue: 0, opacity: 0)
public static let black: Self = .init(white: 0)
public static let white: Self = .init(white: 1)
public static let gray: Self = .init(white: 0.6)
public static let red: Self = .init(red: 1.00, green: 0.27, blue: 0.23)
public static let green: Self = .init(red: 0.20, green: 0.84, blue: 0.29)
public static let blue: Self = .init(red: 0.04, green: 0.52, blue: 1.00)
public static let orange: Self = .init(red: 1.00, green: 0.62, blue: 0.04)
public static let yellow: Self = .init(red: 1.00, green: 0.84, blue: 0.04)
public static let pink: Self = .init(red: 1.00, green: 0.22, blue: 0.37)
public static let purple: Self = .init(red: 0.75, green: 0.36, blue: 0.95)
// FIXME: Switch to use colorScheme
public static let primary: Self = .black
public static let secondary: Self = .gray
}
extension Color: ExpressibleByIntegerLiteral {
@ -84,3 +127,30 @@ extension Color: View {
_ShapeView(shape: Rectangle(), style: self)
}
}
struct AccentColorKey: EnvironmentKey {
static let defaultValue: Color? = nil
}
public extension EnvironmentValues {
var accentColor: Color? {
get {
self[AccentColorKey.self]
}
set {
self[AccentColorKey.self] = newValue
}
}
}
extension View {
public func accentColor(_ accentColor: Color?) -> some View {
environment(\.accentColor, accentColor)
}
}
extension Color {
public static var accentColor: Self {
envAccentColor ?? .blue
}
}

View File

@ -0,0 +1,90 @@
// Copyright 2019-2020 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Created by Carson Katri on 7/12/20.
//
#if canImport(SwiftUI)
import SwiftUI
#else
import TokamakDOM
#endif
public struct ColorDemo: View {
var color: Color {
guard let v0d = Double(v0),
let v1d = Double(v1),
let v2d = Double(v2) else {
return .white
}
switch colorForm {
case .rgb:
return Color(red: v0d, green: v1d, blue: v2d)
case .hsb:
return Color(hue: v0d, saturation: v1d, brightness: v2d)
}
}
enum ColorForm: String {
case rgb, hsb
}
@State private var colorForm: ColorForm = .hsb
@State private var v0: String = "0.9"
@State private var v1: String = "1"
@State private var v2: String = "0.5"
let colors: [Color] = [
.clear,
.black,
.white,
.gray,
.red,
.green,
.blue,
.orange,
.yellow,
.pink,
.purple,
.primary,
.secondary,
]
public var body: some View {
VStack {
Button("Input \(colorForm.rawValue.uppercased())") {
colorForm = colorForm == .rgb ? .hsb : .rgb
}
TextField(colorForm == .rgb ? "Red" : "Hue", text: $v0)
TextField(colorForm == .rgb ? "Green" : "Saturation", text: $v1)
TextField(colorForm == .rgb ? "Blue" : "Brightness", text: $v2)
Text("\(v0) \(v1) \(v2)")
.bold()
.padding()
.background(color)
Text("Accent Color: \(Color.accentColor.description)")
.bold()
.padding()
.background(Color.accentColor)
ForEach(colors, id: \.self) {
Text($0.description)
.font(.caption)
.bold()
.padding()
.background($0)
}
}
}
}

View File

@ -56,6 +56,8 @@ struct TokamakDemoView: View {
if #available(OSX 10.16, *) {
OutlineGroupDemo()
}
ColorDemo()
.padding()
}
}
}

View File

@ -27,6 +27,8 @@
85ED18B624AD42D70085DFA0 /* NSAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85ED189424AD41B90085DFA0 /* NSAppDelegate.swift */; };
B51F215024B920B400CF2583 /* PathDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51F214F24B920B400CF2583 /* PathDemo.swift */; };
B51F215124B920B400CF2583 /* PathDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51F214F24B920B400CF2583 /* PathDemo.swift */; };
B56F22E024BC89FD001738DF /* ColorDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56F22DF24BC89FD001738DF /* ColorDemo.swift */; };
B56F22E124BC89FD001738DF /* ColorDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56F22DF24BC89FD001738DF /* ColorDemo.swift */; };
D1B4229024B3B9BB00682F74 /* ListDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4228E24B3B9BB00682F74 /* ListDemo.swift */; };
D1B4229124B3B9BB00682F74 /* ListDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4228E24B3B9BB00682F74 /* ListDemo.swift */; };
D1B4229224B3B9BB00682F74 /* OutlineGroupDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4228F24B3B9BB00682F74 /* OutlineGroupDemo.swift */; };
@ -50,6 +52,7 @@
85ED18BD24AD46340085DFA0 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
85ED18BF24AD464B0085DFA0 /* iOS Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "iOS Info.plist"; sourceTree = "<group>"; };
B51F214F24B920B400CF2583 /* PathDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PathDemo.swift; sourceTree = "<group>"; };
B56F22DF24BC89FD001738DF /* ColorDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = ColorDemo.swift; sourceTree = "<group>"; tabWidth = 2; };
D1B4228E24B3B9BB00682F74 /* ListDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListDemo.swift; sourceTree = "<group>"; };
D1B4228F24B3B9BB00682F74 /* OutlineGroupDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutlineGroupDemo.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -108,6 +111,7 @@
85ED189F24AD425E0085DFA0 /* TextFieldDemo.swift */,
85ED18A024AD425E0085DFA0 /* EnvironmentDemo.swift */,
B51F214F24B920B400CF2583 /* PathDemo.swift */,
B56F22DF24BC89FD001738DF /* ColorDemo.swift */,
);
name = TokamakDemo;
path = ../Sources/TokamakDemo;
@ -214,6 +218,7 @@
files = (
85ED186A24AD38F20085DFA0 /* UIAppDelegate.swift in Sources */,
D1B4229224B3B9BB00682F74 /* OutlineGroupDemo.swift in Sources */,
B56F22E024BC89FD001738DF /* ColorDemo.swift in Sources */,
B51F215024B920B400CF2583 /* PathDemo.swift in Sources */,
85ED18AF24AD425E0085DFA0 /* EnvironmentDemo.swift in Sources */,
85ED18A324AD425E0085DFA0 /* SpacerDemo.swift in Sources */,
@ -232,6 +237,7 @@
files = (
85ED18AA24AD425E0085DFA0 /* TokamakDemo.swift in Sources */,
D1B4229324B3B9BB00682F74 /* OutlineGroupDemo.swift in Sources */,
B56F22E124BC89FD001738DF /* ColorDemo.swift in Sources */,
B51F215124B920B400CF2583 /* PathDemo.swift in Sources */,
85ED18A424AD425E0085DFA0 /* SpacerDemo.swift in Sources */,
85ED18B024AD425E0085DFA0 /* EnvironmentDemo.swift in Sources */,