Skip to content

Commit

Permalink
✨ Meet LuminareTextField!
Browse files Browse the repository at this point in the history
  • Loading branch information
KrLite committed Dec 18, 2024
1 parent 2944ea3 commit 4f5cae5
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 57 deletions.
69 changes: 39 additions & 30 deletions Sources/Luminare/Components/Auxiliary/LuminareButtonStyles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,52 @@ import SwiftUI

struct AspectRatioModifier: ViewModifier {
@Environment(\.luminareMinHeight) private var minHeight
@Environment(\.luminareCompactButtonAspectRatio) private var aspectRatio
@Environment(\.luminareCompactButtonHasFixedHeight) private
var hasFixedHeight
@Environment(\.luminareAspectRatio) private var aspectRatio

@ViewBuilder func body(content: Content) -> some View {
Group {
if isConstrained {
content
.frame(
minWidth: minWidth, maxWidth: .infinity,
minHeight: minHeight,
maxHeight: hasFixedHeight ? nil : .infinity
)
.aspectRatio(
aspectRatio.aspectRatio,
contentMode: aspectRatio.contentMode
)
} else {
content
.frame(
maxWidth: .infinity, minHeight: minHeight,
maxHeight: .infinity
)
if let aspectRatio {
Group {
if isConstrained {
content
.frame(
minWidth: minWidth, maxWidth: .infinity,
minHeight: minHeight,
maxHeight: hasFixedHeight ? nil : .infinity
)
.aspectRatio(
aspectRatio.aspectRatio,
contentMode: aspectRatio.contentMode
)
} else {
content
.frame(
maxWidth: .infinity, minHeight: minHeight,
maxHeight: .infinity
)
}
}
.fixedSize(
horizontal: aspectRatio.contentMode == .fit,
vertical: hasFixedHeight
)
} else {
content
}
.fixedSize(
horizontal: aspectRatio.contentMode == .fit,
vertical: hasFixedHeight
)
}

private var hasFixedHeight: Bool {
guard let aspectRatio else { return false }
return aspectRatio.hasFixedHeight
}

private var isConstrained: Bool {
aspectRatio.contentMode == .fit || hasFixedHeight
guard let aspectRatio else { return false }
return aspectRatio.contentMode == .fit || hasFixedHeight
}

private var minWidth: CGFloat? {
if hasFixedHeight, let aspectRatio = aspectRatio.aspectRatio {
if hasFixedHeight,
let aspectRatio = aspectRatio?.aspectRatio {
minHeight * aspectRatio
} else {
nil
Expand Down Expand Up @@ -172,7 +181,7 @@ public struct LuminareProminentButtonStyle: ButtonStyle {
///
/// ![LuminareCosmeticButtonStyle](LuminareCosmeticButtonStyle)
public struct LuminareCosmeticButtonStyle: ButtonStyle {
@Environment(\.isEnabled) private var isEnabled: Bool
@Environment(\.isEnabled) private var isEnabled
@Environment(\.luminareAnimationFast) private var animationFast
@Environment(\.luminareMinHeight) private var minHeight
@Environment(\.luminareButtonMaterial) private var material
Expand Down Expand Up @@ -241,7 +250,7 @@ public struct LuminareCosmeticButtonStyle: ButtonStyle {
///
/// ![LuminareCompactButtonStyle](LuminareCompactButtonStyle)
public struct LuminareCompactButtonStyle: ButtonStyle {
@Environment(\.isEnabled) private var isEnabled: Bool
@Environment(\.isEnabled) private var isEnabled
@Environment(\.luminareAnimationFast) private var animationFast
@Environment(\.luminareHorizontalPadding) private var horizontalPadding

Expand Down Expand Up @@ -448,7 +457,7 @@ public struct LuminareBordered: ViewModifier {
/// }
/// }
public struct LuminareHoverable: ViewModifier {
@Environment(\.isEnabled) private var isEnabled: Bool
@Environment(\.isEnabled) private var isEnabled
@Environment(\.luminareAnimationFast) private var animationFast
@Environment(\.luminareHorizontalPadding) private var horizontalPadding

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ struct ColorPickerModalView: View {
colorPicker()
}
}
.luminareCompactButtonAspectRatio(contentMode: .fill)
.luminareAspectRatio(contentMode: .fill)
}

@ViewBuilder private func controls() -> some View {
Expand Down Expand Up @@ -185,7 +185,7 @@ struct ColorPickerModalView: View {
}
}
.buttonStyle(.luminareCompact)
.luminareCompactButtonAspectRatio(contentMode: .fill)
.luminareAspectRatio(contentMode: .fill)
}
}
}
Expand All @@ -200,7 +200,7 @@ struct ColorPickerModalView: View {
} label: {
Image(systemName: "eyedropper.halffull")
}
.luminareCompactButtonAspectRatio(1 / 1, contentMode: .fit)
.luminareAspectRatio(1 / 1, contentMode: .fit)
.buttonStyle(.luminareCompact)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public struct LuminareColorPicker<F>: View
}
.buttonStyle(.luminareCompact)
.luminareHorizontalPadding(0)
.luminareCompactButtonAspectRatio(1 / 1, contentMode: .fit)
.luminareAspectRatio(1 / 1, contentMode: .fit)
.luminareModalWithPredefinedSheetStyle(isPresented: $isColorPickerPresented) {
VStack {
ColorPickerModalView(
Expand Down Expand Up @@ -167,11 +167,11 @@ public struct LuminareColorPicker<F>: View
.luminareModalStyle(.popover)
.luminareModalContentWrapper { view in
view
.luminareCompactButtonAspectRatio(contentMode: .fit)
.luminareAspectRatio(contentMode: .fit)
.monospaced(false)
}
}
.luminareCompactButtonAspectRatio(contentMode: .fill)
.luminareAspectRatio(contentMode: .fill)
.monospaced()
.frame(width: 300)
}
4 changes: 2 additions & 2 deletions Sources/Luminare/Components/LuminareCompactPicker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ private struct PickerPreview<V>: View where V: Hashable & Equatable {
PickerPreview(elements: [40, 41, 42, 43, 44], selection: 42)
.luminareCompactPickerStyle(.segmented)
.padding(2)
.luminareCompactButtonAspectRatio(contentMode: .fit)
.luminareAspectRatio(contentMode: .fit)
}
.luminareCompactButtonAspectRatio(contentMode: .fill)
.luminareAspectRatio(contentMode: .fill)
}
}
117 changes: 117 additions & 0 deletions Sources/Luminare/Components/LuminareTextEditor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//
// LuminareTextEditor.swift
// Luminare
//
// Created by KrLite on 2024/12/18.
//

import SwiftUI

// MARK: - Text Editor

public struct LuminareTextEditor: View {
// MARK: Environments

@Environment(\.isEnabled) private var isEnabled
@Environment(\.font) private var font
@Environment(\.luminareAnimationFast) private var animationFast
@Environment(\.luminareHorizontalPadding) private var horizontalPadding

// MARK: Fields

@Binding private var text: String
@Binding private var selection: Any? // Handle os versions below macOS 15.0

@State private var isHovering: Bool = false

// MARK: Initializers

public init(
text: Binding<String>
) {
self._text = text
self._selection = .constant(nil)
}

@available(macOS 15.0, *)
public init(
text: Binding<String>,
selection: Binding<TextSelection?>
) {
self._text = text
self._selection = Binding {
selection.wrappedValue
} set: { newValue in
if let newValue = newValue as? TextSelection? {
selection.wrappedValue = newValue
} else {
selection.wrappedValue = nil
}
}
}

// MARK: Body

public var body: some View {
ScrollView {
Group {
if #available(macOS 15.0, *) {
TextEditor(text: $text, selection: strongTypedSelection)
.textEditorStyle(.plain)
} else if #available(macOS 14.0, *) {
TextEditor(text: $text)
.textEditorStyle(.plain)
} else {
TextEditor(text: $text)
}
}
.fixedSize(horizontal: false, vertical: true)
.padding(.horizontal, horizontalPadding)
.padding(.vertical, 14)
}
.scrollContentBackground(.hidden)
.font(font ?? .body)
.modifier(LuminareHoverable())
.luminareAspectRatio(unapplying: true)
.luminareHorizontalPadding(0)
}

@available(macOS 15.0, *)
private var strongTypedSelection: Binding<TextSelection?> {
Binding {
if let value = selection as? TextSelection? {
value
} else {
nil
}
} set: { newValue in
selection = newValue
}
}
}

// MARK: - Preview

@available(macOS 15.0, *)
#Preview(
"LuminareTextEditor",
traits: .sizeThatFitsLayout
) {
@Previewable @State var text = """
Cupidatat ipsum consequat est cupidatat veniam sint et voluptate commodo.
Incididunt elit commodo culpa officia sint quis ullamco qui deserunt consequat.
Cillum et ullamco cupidatat. Dolore id ea culpa labore Lorem commodo esse.
Adipisicing dolore officia exercitation dolor occaecat do esse non occaecat reprehenderit duis magna.
Labore eu culpa tempor adipisicing qui incididunt tempor in nostrud dolore velit magna veniam ex.
Veniam et incididunt amet Lorem sit irure.
Anim duis enim enim velit exercitation.
"""
@Previewable @State var selection: TextSelection? = .none

LuminareTextEditor(text: $text)

LuminareTextEditor(text: $text, selection: $selection)

LuminareTextEditor(text: $text, selection: $selection)
.disabled(true)
}
2 changes: 1 addition & 1 deletion Sources/Luminare/Components/LuminareTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,5 @@ public struct LuminareTextField<F>: View where F: ParseableFormatStyle, F.Format
isFocused = false
}
}
.luminareCompactButtonAspectRatio(contentMode: .fill)
.luminareAspectRatio(contentMode: .fill)
}
8 changes: 6 additions & 2 deletions Sources/Luminare/Utilities/EnvironmentValues.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,17 @@ public extension EnvironmentValues {

// MARK: Button Styles

@Entry var luminareAspectRatio: (
aspectRatio: CGFloat?,
contentMode: ContentMode,
hasFixedHeight: Bool
)? = (nil, .fit, true)

@Entry var luminareButtonMaterial: Material? = nil
@Entry var luminareButtonCornerRadii: RectangleCornerRadii = .init(2)
@Entry var luminareButtonHighlightOnHover: Bool = true

@Entry var luminareCompactButtonCornerRadii: RectangleCornerRadii = .init(8)
@Entry var luminareCompactButtonAspectRatio: (aspectRatio: CGFloat?, contentMode: ContentMode) = (nil, .fit)
@Entry var luminareCompactButtonHasFixedHeight: Bool = true

// MARK: Form Style

Expand Down
40 changes: 28 additions & 12 deletions Sources/Luminare/Utilities/Extensions/View+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@

import SwiftUI

extension View {
@ViewBuilder func applying(@ViewBuilder _ transform: @escaping (Self) -> some View) -> some View {
transform(self)
}
}

public extension View {
/// Adjusts the tint of the view, synchronously changing the `.tint()` modifier and the `\.luminareTint` environment
/// value.
Expand Down Expand Up @@ -238,6 +244,28 @@ public extension View {

// MARK: Button Styles

@ViewBuilder func luminareAspectRatio(unapplying: Bool) -> some View {
if unapplying {
environment(\.luminareAspectRatio, nil)
} else {
luminareAspectRatio()
}
}

@ViewBuilder func luminareAspectRatio(
_ aspectRatio: CGFloat? = nil, contentMode: ContentMode = .fit, hasFixedHeight: Bool = true
) -> some View {
environment(\.luminareAspectRatio, (aspectRatio, contentMode, hasFixedHeight))
}

@ViewBuilder func luminareAspectRatio(
_ aspectRatio: CGSize, contentMode: ContentMode = .fit, hasFixedHeight: Bool = true
) -> some View {
luminareAspectRatio(
aspectRatio.width / aspectRatio.height, contentMode: contentMode, hasFixedHeight: hasFixedHeight
)
}

@ViewBuilder func luminareButtonMaterial(_ material: Material? = nil) -> some View {
environment(\.luminareButtonMaterial, material)
}
Expand All @@ -262,18 +290,6 @@ public extension View {
luminareCompactButtonCornerRadii(.init(radius))
}

@ViewBuilder func luminareCompactButtonAspectRatio(_ aspectRatio: CGFloat? = nil, contentMode: ContentMode) -> some View {
environment(\.luminareCompactButtonAspectRatio, (aspectRatio, contentMode))
}

@ViewBuilder func luminareCompactButtonAspectRatio(_ aspectRatio: CGSize, contentMode: ContentMode) -> some View {
environment(\.luminareCompactButtonAspectRatio, (aspectRatio.width / aspectRatio.height, contentMode))
}

@ViewBuilder func luminareCompactButtonHasFixedHeight(_ hasFixedHeight: Bool = true) -> some View {
environment(\.luminareCompactButtonHasFixedHeight, hasFixedHeight)
}

// MARK: Section

@ViewBuilder func luminareSectionLayout(_ layout: LuminareSectionLayout = .stacked) -> some View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@ import SwiftUI
HStack {
Button("Compact") {}
.buttonStyle(.luminareCompact)
.luminareCompactButtonAspectRatio(contentMode: .fill)
.luminareCompactButtonHasFixedHeight(false)
.luminareAspectRatio(contentMode: .fill, hasFixedHeight: false)
}
.frame(height: 40)
}
Expand Down Expand Up @@ -216,8 +215,7 @@ import SwiftUI
) {
Button("Click Me!") {}
.buttonStyle(.luminareCompact)
.luminareCompactButtonAspectRatio(contentMode: .fill)
.luminareCompactButtonHasFixedHeight(false)
.luminareAspectRatio(contentMode: .fill, hasFixedHeight: false)
.frame(height: 40)
}
#endif
Expand Down

0 comments on commit 4f5cae5

Please sign in to comment.