Skip to content

Commit

Permalink
Add controlSize/controlProminence modifiers (#431)
Browse files Browse the repository at this point in the history
  • Loading branch information
carson-katri committed Jul 19, 2021
1 parent 6792dbb commit 22ea230
Show file tree
Hide file tree
Showing 24 changed files with 799 additions and 171 deletions.
85 changes: 16 additions & 69 deletions Sources/TokamakCore/Styles/ButtonStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,88 +14,35 @@
//
// Created by Gene Z. Ragan on 07/22/2020.

public struct ButtonStyleConfiguration {
public struct Label: _PrimitiveView {
let content: AnyView
}

public let label: Label
public let isPressed: Bool
}

public struct DefaultButtonStyle: ButtonStyle {
public init() {}
public func makeBody(configuration: ButtonStyleConfiguration) -> some View {
configuration.label
}
}

/// This is a helper type that works around absence of "package private" access control in Swift
public struct _ButtonStyleConfigurationProxy {
public struct Label {
public typealias Subject = ButtonStyleConfiguration.Label
public let subject: Subject

public init(_ subject: Subject) { self.subject = subject }

public var content: AnyView { subject.content }
}

public typealias Subject = ButtonStyleConfiguration
public let subject: Subject

public init(label: AnyView, isPressed: Bool) {
subject = .init(label: .init(content: label), isPressed: isPressed)
}

public var label: ButtonStyleConfiguration.Label { subject.label }
}

public protocol ButtonStyle {
associatedtype Body: View

@ViewBuilder
func makeBody(configuration: Self.Configuration) -> Self.Body

typealias Configuration = ButtonStyleConfiguration
}

public struct _AnyButtonStyle: ButtonStyle {
public typealias Body = AnyView

private let bodyClosure: (ButtonStyleConfiguration) -> AnyView
public let type: Any.Type

public init<S: ButtonStyle>(_ style: S) {
type = S.self
bodyClosure = { configuration in
AnyView(style.makeBody(configuration: configuration))
}
public struct ButtonStyleConfiguration {
public struct Label: View {
public let body: AnyView
}

public func makeBody(configuration: ButtonStyleConfiguration) -> AnyView {
bodyClosure(configuration)
}
public let role: ButtonRole?
public let label: ButtonStyleConfiguration.Label
public let isPressed: Bool
}

public enum _ButtonStyleKey: EnvironmentKey {
public static var defaultValue: _AnyButtonStyle {
_AnyButtonStyle(DefaultButtonStyle())
}
}
struct AnyButtonStyle: ButtonStyle {
let bodyClosure: (ButtonStyleConfiguration) -> AnyView
let type: Any.Type

extension EnvironmentValues {
var buttonStyle: _AnyButtonStyle {
get {
self[_ButtonStyleKey.self]
}
set {
self[_ButtonStyleKey.self] = newValue
init<S: ButtonStyle>(_ style: S) {
type = S.self
bodyClosure = {
AnyView(style.makeBody(configuration: $0))
}
}
}

public extension View {
func buttonStyle<S>(_ style: S) -> some View where S: ButtonStyle {
environment(\.buttonStyle, _AnyButtonStyle(style))
func makeBody(configuration: Configuration) -> some View {
bodyClosure(configuration)
}
}
104 changes: 104 additions & 0 deletions Sources/TokamakCore/Styles/ControlGroupStyle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2018-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/21.
//

import Foundation

public protocol ControlGroupStyle {
associatedtype Body: View
@ViewBuilder
func makeBody(configuration: Self.Configuration) -> Self.Body
typealias Configuration = ControlGroupStyleConfiguration
}

public struct ControlGroupStyleConfiguration {
public struct Content: View {
public let body: AnyView
}

public let content: ControlGroupStyleConfiguration.Content
}

public struct AutomaticControlGroupStyle: ControlGroupStyle {
public init() {}

public func makeBody(configuration: Self.Configuration) -> some View {
Picker(
selection: .constant(AnyHashable?.none),
label: EmptyView()
) {
if let parentView = configuration.content.body.view as? ParentView {
ForEach(Array(parentView.children.enumerated()), id: \.offset) {
$0.element
}
} else {
configuration.content
}
}
.pickerStyle(SegmentedPickerStyle())
}
}

public struct NavigationControlGroupStyle: ControlGroupStyle {
public init() {}

public func makeBody(configuration: Self.Configuration) -> some View {
HStack {
configuration.content
}
}
}

public struct _AnyControlGroupStyle: ControlGroupStyle {
public typealias Body = AnyView

private let bodyClosure: (ControlGroupStyleConfiguration) -> AnyView
public let type: Any.Type

public init<S: ControlGroupStyle>(_ style: S) {
type = S.self
bodyClosure = { configuration in
AnyView(style.makeBody(configuration: configuration))
}
}

public func makeBody(configuration: ControlGroupStyleConfiguration) -> AnyView {
bodyClosure(configuration)
}
}

extension EnvironmentValues {
private enum ControlGroupStyleKey: EnvironmentKey {
static let defaultValue = _AnyControlGroupStyle(AutomaticControlGroupStyle())
}

var controlGroupStyle: _AnyControlGroupStyle {
get {
self[ControlGroupStyleKey.self]
}
set {
self[ControlGroupStyleKey.self] = newValue
}
}
}

public extension View {
func controlGroupStyle<S>(
_ style: S
) -> some View where S: ControlGroupStyle {
environment(\.controlGroupStyle, .init(style))
}
}
133 changes: 133 additions & 0 deletions Sources/TokamakCore/Styles/PrimitiveButtonStyle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright 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/21.
//

public protocol PrimitiveButtonStyle {
associatedtype Body: View
@ViewBuilder
func makeBody(configuration: Self.Configuration) -> Self.Body
typealias Configuration = PrimitiveButtonStyleConfiguration
}

public struct PrimitiveButtonStyleConfiguration {
public struct Label: View {
public let body: AnyView
}

public let role: ButtonRole?
public let label: PrimitiveButtonStyleConfiguration.Label

let action: () -> ()
public func trigger() { action() }
}

public struct DefaultButtonStyle: PrimitiveButtonStyle {
public init() {}

public func makeBody(configuration: Configuration) -> some View {
BorderedButtonStyle().makeBody(configuration: configuration)
}
}

public struct PlainButtonStyle: ButtonStyle {
public init() {}

public func makeBody(configuration: Configuration) -> some View {
configuration.label
.foregroundColor(configuration.isPressed ? .secondary : .primary)
}
}

public struct BorderedButtonStyle: PrimitiveButtonStyle {
public init() {}

public func makeBody(configuration: Configuration) -> some View {
_PrimitiveButtonStyleBody(style: self, configuration: configuration) {
configuration.label
}
}
}

public struct BorderlessButtonStyle: ButtonStyle {
public init() {}

public func makeBody(configuration: Configuration) -> some View {
configuration.label
.foregroundColor(configuration.isPressed ? .primary : .secondary)
}
}

public struct LinkButtonStyle: ButtonStyle {
public init() {}

public func makeBody(configuration: Configuration) -> some View {
configuration.label.body
.foregroundColor(
configuration
.isPressed ? Color(red: 128 / 255, green: 192 / 255, blue: 240 / 255) : .blue
)
}
}

struct AnyPrimitiveButtonStyle: PrimitiveButtonStyle {
let bodyClosure: (PrimitiveButtonStyleConfiguration) -> AnyView
let type: Any.Type

init<S: PrimitiveButtonStyle>(_ style: S) {
type = S.self
bodyClosure = {
AnyView(style.makeBody(configuration: $0))
}
}

func makeBody(configuration: Self.Configuration) -> AnyView {
bodyClosure(configuration)
}
}

extension EnvironmentValues {
enum ButtonStyleKey: EnvironmentKey {
enum ButtonStyleKeyValue {
case primitiveButtonStyle(AnyPrimitiveButtonStyle)
case buttonStyle(AnyButtonStyle)
}

public static let defaultValue: ButtonStyleKeyValue = .primitiveButtonStyle(
.init(DefaultButtonStyle())
)
}

var buttonStyle: ButtonStyleKey.ButtonStyleKeyValue {
get {
self[ButtonStyleKey.self]
}
set {
self[ButtonStyleKey.self] = newValue
}
}
}

public extension View {
func buttonStyle<S>(
_ style: S
) -> some View where S: PrimitiveButtonStyle {
environment(\.buttonStyle, .primitiveButtonStyle(.init(style)))
}

func buttonStyle<S>(_ style: S) -> some View where S: ButtonStyle {
environment(\.buttonStyle, .buttonStyle(.init(style)))
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020 Tokamak contributors
// Copyright 2018-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.
Expand All @@ -12,13 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Created by Gene Z. Ragan on 07/22/2020.
// Created by Carson Katri on 7/12/21.
//

import TokamakCore
public struct ButtonRole: Equatable {
public static let destructive = ButtonRole(rawValue: 0)
public static let cancel = ButtonRole(rawValue: 1)

extension ButtonStyleConfiguration.Label: DOMPrimitive {
var renderedBody: AnyView {
_ButtonStyleConfigurationProxy.Label(self).content
private let rawValue: Int
private init(rawValue: Int) {
self.rawValue = rawValue
}
}
Loading

0 comments on commit 22ea230

Please sign in to comment.