Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Sources/TouchpadInputApp/Views/SettingsPanel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ struct SettingsPanel: View {
value: String(format: "%.2f", session.pressureFloor)) {
Slider(value: $session.pressureFloor, in: 0.05...0.50, step: 0.05)
}
settingRow(label: "Force-press threshold",
value: String(format: "%.2f", session.forcePressThreshold)) {
Slider(value: $session.forcePressThreshold, in: 0.70...1.00, step: 0.05)
}
settingRow(label: "Min contact size",
value: String(format: "%.2f", session.minContactSize)) {
Slider(value: $session.minContactSize, in: 0.0...1.0, step: 0.05)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ public struct CharacterEmitter: CharacterResolver {
// MARK: CharacterResolver

public func character(forZoneID id: String, pressure: Float,
modifiers: Set<AnyModifierKind>, pressureFloor: Float) -> Character? {
modifiers: Set<AnyModifierKind>, pressureFloor: Float,
forcePressThreshold: Float = 0.95) -> Character? {
guard pressure >= pressureFloor else { return nil }
guard let zone = grid.zones.first(where: { String($0.character) == id }) else { return nil }
if modifiers.contains(.shift) {
return Character(String(zone.character).uppercased())
}
return resolve(zone: zone, pressure: pressure)
return resolve(zone: zone, pressure: pressure, forcePressThreshold: forcePressThreshold)
}

// MARK: Legacy coordinate-based API (kept for direct-use tests)
Expand All @@ -27,13 +28,13 @@ public struct CharacterEmitter: CharacterResolver {
pressure: Float, pressureFloor: Float = 0.30) -> Character? {
guard pressure >= pressureFloor else { return nil }
guard let zone = grid.zone(at: x, y: y) else { return nil }
return resolve(zone: zone, pressure: pressure)
return resolve(zone: zone, pressure: pressure, forcePressThreshold: 0.95)
}

// MARK: Private

private func resolve(zone: KeyZone, pressure: Float) -> Character {
if pressure >= 0.95 {
private func resolve(zone: KeyZone, pressure: Float, forcePressThreshold: Float) -> Character {
if pressure >= forcePressThreshold {
return zone.altCharacter ?? Character(String(zone.character).uppercased())
} else {
return zone.character
Expand Down
3 changes: 2 additions & 1 deletion Sources/TouchpadInputCore/Protocols/CharacterResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
public protocol CharacterResolver: Sendable {
/// Returns the character for the given zone and pressure level, or nil if no character should be emitted.
func character(forZoneID id: String, pressure: Float,
modifiers: Set<AnyModifierKind>, pressureFloor: Float) -> Character?
modifiers: Set<AnyModifierKind>, pressureFloor: Float,
forcePressThreshold: Float) -> Character?
}
4 changes: 3 additions & 1 deletion Sources/TouchpadInputCore/Session/TouchInputSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public final class TouchInputSession: ObservableObject, @preconcurrency TouchEve
// MARK: Stability settings

@Published public var pressureFloor: Float = 0.30
@Published public var forcePressThreshold: Float = 0.95
@Published public var minContactSize: Float = 0.0
@Published public var zoneCooldownMs: Double = 80.0

Expand Down Expand Up @@ -308,7 +309,8 @@ public final class TouchInputSession: ObservableObject, @preconcurrency TouchEve
forZoneID: zoneID,
pressure: effectivePressure,
modifiers: heldModifiers,
pressureFloor: pressureFloor
pressureFloor: pressureFloor,
forcePressThreshold: forcePressThreshold
) {
outputBuffer.append(ch)
externalOutputTarget?.emit(character: ch)
Expand Down
Loading