Skip to content
Merged
75 changes: 75 additions & 0 deletions Example/Shared/View/SliderExample.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// SliderExample.swift
// Shared

#if OPENSWIFTUI
import OpenSwiftUI
#else
import SwiftUI
#endif

struct SliderExample: View {
@State private var speed: Double
@State private var isEditing = false

init(speed: Double = 50) {
_speed = State(initialValue: speed)
}

var body: some View {
VStack(spacing: 16) {
Slider(
value: $speed,
in: 0...100,
onEditingChanged: { isEditing = $0 }
)
Text("\(speed, specifier: "%.0f")")
.foregroundColor(isEditing ? .red : .blue)
}
.padding()
}
}

struct SteppedSliderExample: View {
@State private var speed = 85.0
@State private var isEditing = false

var body: some View {
VStack(spacing: 16) {
Slider(
value: $speed,
in: 0...100,
step: 5
) {
Text("Speed")
} minimumValueLabel: {
Text("0")
} maximumValueLabel: {
Text("100")
} onEditingChanged: {
isEditing = $0
}
Text("\(speed, specifier: "%.0f")")
.foregroundColor(isEditing ? .red : .blue)
}
.padding()
}
}

#Preview("Continuous") {
SliderExample()
}

#Preview("Stepped with labels") {
SteppedSliderExample()
}

#Preview("Tint and disabled") {
VStack {
SteppedSliderExample()
.tint(.purple)
SteppedSliderExample()
.tint(.purple)
.disabled(true)
}
}
39 changes: 39 additions & 0 deletions Example/Tests/OpenSwiftUIUITests/View/SliderUITests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// SliderUITests.swift
// OpenSwiftUIUITests

import SnapshotTesting
import Testing
@testable import TestingHost

@MainActor
@Suite(.snapshots(record: .never, diffTool: diffTool))
struct SliderUITests {
@Test(arguments: [0.0, 50.0, 100.0])
func continuousValues(speed: Double) {
openSwiftUIAssertSnapshot(
of: SliderExample(speed: speed),
size: .init(width: 320, height: 160),
named: "\(Int(speed))"
)
}

@Test
func stepAndValueLabels() {
openSwiftUIAssertSnapshot(
of: SteppedSliderExample(),
size: .init(width: 320, height: 160)
)
}

@Test(arguments: [false, true])
func tintedAndDisabled(isDisabled: Bool) {
openSwiftUIAssertSnapshot(
of: SteppedSliderExample()
.tint(.purple)
.disabled(isDisabled),
size: .init(width: 320, height: 160),
named: isDisabled ? "disabled" : "enabled"
)
}
}
4 changes: 4 additions & 0 deletions Sources/COpenSwiftUI/Shims/AppKit/AppKit_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ typedef OPENSWIFTUI_ENUM(NSInteger, NSViewVibrantBlendingStyle) {
@property (nullable, strong) NSFont *font;
@end

@interface NSSliderCell (OpenSwiftUI_SPI)
- (NSInteger)closestTickMarkIndexToValue:(double)value;
@end

@interface NSMenu (OpenSwiftUI_SPI)
+ (void)_setAlwaysCallDelegateBeforeSidebandUpdaters_openswiftui_safe_wrapper:(BOOL)value OPENSWIFTUI_SWIFT_NAME(_setAlwaysCallDelegateBeforeSidebandUpdaters(_:));
+ (void)_setAlwaysInstallWindowTabItems_openswiftui_safe_wrapper:(BOOL)value OPENSWIFTUI_SWIFT_NAME(_setAlwaysInstallWindowTabItems(_:));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// AccessibilityAdjustableAction.swift
// OpenSwiftUI
//
// Audited for 6.5.4
// Status: Complete

public import OpenSwiftUICore
import OpenSwiftUI_SPI
#if os(macOS)
import AppKit
#endif

// MARK: - AccessibilityAdjustmentDirection

/// A directional indicator you use when making an accessibility adjustment.
@available(OpenSwiftUI_v1_0, *)
public enum AccessibilityAdjustmentDirection: Sendable {
case increment
case decrement
}

// MARK: - View + AccessibilityAdjustableAction

@available(OpenSwiftUI_v1_0, *)
extension View {
/// Adds an accessibility adjustable action to the view. Actions allow
/// assistive technologies, such as the VoiceOver, to interact with the
/// view by invoking the action.
///
/// For example, this is how an adjustable action to navigate
/// through pages could be added to a view.
///
/// var body: some View {
/// PageControl()
/// .accessibilityAdjustableAction { direction in
/// switch direction {
/// case .increment:
/// // Go to next page
/// case .decrement:
/// // Go to previous page
/// }
/// }
/// }
///
nonisolated public func accessibilityAdjustableAction(
_ handler: @escaping (AccessibilityAdjustmentDirection) -> Void
) -> ModifiedContent<Self, AccessibilityAttachmentModifier> {
accessibilityAction(AccessibilityAdjustableAction(continuous: nil), handler)
}
}

@available(OpenSwiftUI_v1_0, *)
extension ModifiedContent where Modifier == AccessibilityAttachmentModifier {
/// Adds an accessibility adjustable action to the view. Actions allow
/// assistive technologies, such as the VoiceOver, to interact with the
/// view by invoking the action.
///
/// For example, this is how an adjustable action to navigate
/// through pages could be added to a view.
///
/// var body: some View {
/// PageControl()
/// .accessibilityAdjustableAction { direction in
/// switch direction {
/// case .increment:
/// // Go to next page
/// case .decrement:
/// // Go to previous page
/// }
/// }
/// }
///
nonisolated public func accessibilityAdjustableAction(
_ handler: @escaping (AccessibilityAdjustmentDirection) -> Void
) -> ModifiedContent<Content, Modifier> {
accessibilityAction(AccessibilityAdjustableAction(continuous: nil), handler)
}
}

// MARK: - AccessibilityAdjustableAction

struct AccessibilityAdjustableAction: AccessibilityAction {
typealias Value = AccessibilityAdjustmentDirection

let continuous: Bool?

func impliedRole(for traits: AccessibilityTraitStorage) -> AccessibilityRole? {
#if os(macOS)
AccessibilityRole(resolved: .init(
role: continuous.map { $0 ? .slider : .incrementor },
subrole: nil,
traits: .adjustable
))
#else
AccessibilityRole(resolved: .init(
traits: .adjustable,
automationType: continuous.map { $0 ? .slider : .stepper }
))
#endif
}

func matches<Other>(_ other: Other) -> Bool where Other: AccessibilityAction {
other is AccessibilityAdjustableAction
}
}
Loading
Loading