import SwiftUI struct SheetCell: View { @Bindable var model: SheetModel let point: Coordinate @Binding var editing: Coordinate? var select: () -> Void var beginEditing: () -> Void = {} var endEditing: () -> Void var columnWidth: Double? = nil var rowHeight: Double? = nil @Environment(\.colorScheme) private var scheme private func width(_ col: Int) -> Double { columnWidth ?? model.sizes.cols?[String(col)] ?? 110 } private func height(_ row: Int) -> Double { rowHeight ?? model.sizes.rows?[String(row)] ?? 32 } var body: some View { let cell = model.cells[point] let style = model.styles[cell?.f ?? ""] let alignment: Alignment = style?.a == "center" ? .center : style?.a == "right" || (style?.a == nil && cell?.n == true) ? .trailing : .leading return Group { #if os(macOS) if editing == point { SheetCellEditor(text: $model.draft, enabled: model.mayWrite && !model.busy, label: String(format: sheetsMacString("Edit %@"), point.ref), commit: { commit(right: $0) }, cancel: { guard !model.busy else { return } model.discardAll(); endEditing() }) .padding(.horizontal, 5) .frame(width: width(point.c), height: height(point.r)) .background(fill(style)) .overlay { Rectangle().strokeBorder(Color.accentColor, lineWidth: 2) } .accessibilityLabel(String(format: sheetsMacString("Edit %@"), point.ref)) } else { display(cell, style: style, alignment: alignment) } #else display(cell, style: style, alignment: alignment) #endif } } private func commit(right: Bool) { guard !model.busy, model.selection.start == point else { return } Task { await model.commit(moveRight: right) if !model.hasUnconfirmedChanges { endEditing() } } } private func display(_ cell: Cell?, style: CellStyle?, alignment: Alignment) -> some View { Button(action: select) { Text(cell?.display ?? "").font(.system(size: 13, weight: style?.b == true ? .bold : .regular)) .italic(style?.i == true).lineLimit(1).padding(.horizontal, 5) .frame(width: width(point.c), height: height(point.r), alignment: alignment) .foregroundStyle(ink(style)).background(fill(style)) .contentShape(.rect) .overlay { Rectangle().strokeBorder( model.selection.contains(point) ? Color.accentColor : Color.secondary.opacity(0.2), lineWidth: model.selection.contains(point) ? 2 : 0.5) } }.buttonStyle(.plain) .simultaneousGesture(TapGesture(count: 2).onEnded { if model.mayWrite, !model.busy, !model.hasUnconfirmedChanges { model.select(point); beginEditing() } }) .accessibilityLabel("\(point.ref), \(cell?.display ?? "Empty")") .accessibilityValue(style?.lc == true ? "Imported colours have low contrast" : "") .accessibilityAddTraits(model.selection.contains(point) ? .isSelected : []) } private func fill(_ style: CellStyle?) -> Color { if let hex = style?.fx { return Color(hex: hex) } if let h = style?.fh, let c = style?.fc { return .oklch(scheme == .dark ? 0.30 : 0.92, c, h) } return Color.clear } private func ink(_ style: CellStyle?) -> Color { if let hex = style?.k { return Color(hex: hex) } if let h = style?.kh, let c = style?.kc { return .oklch(scheme == .dark ? 0.80 : 0.42, c, h) } return .primary } }