gloss-ios/Pane.swift

278 lines
12 KiB
Swift

//
// Fenestra.swift
// gloss
//
// Created by Saint on 5/20/24.
//
import GRDB
import GRDBQuery
import Foundation
import SwiftUI
import WrappingHStack
struct SegRow: View {
var seg: SegDenorm
var ribbonId: Int64
var width: CGFloat
@State var highlights = Set<Int>()
@State var chapVisible = false
let intraWordSpacing = CGFloat(1.6)
var body: some View {
var segSplit = seg.body.components(separatedBy: ";;")
let decoder = JSONDecoder()
var retView = VStack {
Text("\(seg.chap)")
.frame(width: width)
.padding(.vertical, 10)
.if(chapVisible) { $0.foregroundColor(mainTextColor) }
.if(!chapVisible) { $0.foregroundColor(secondBackgroundColor) }
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
.background(secondBackgroundColor)
// .background(mainBackgroundColor)
.onTapGesture {
chapVisible.toggle()
}
WrappingHStack(alignment: .leading, horizontalSpacing: 0) {
ForEach(0 ..< segSplit.count, id: \.self) { segIndex in
let verse = try! decoder.decode(Verse.self, from: segSplit[segIndex].data(using: .utf8)!)
let arrayOfText = verse.body.components(separatedBy: " ")
let lineHeight = CGFloat(30)
let fontSize = CGFloat(18)
let highlightColor = "470000"
ForEach(0 ..< arrayOfText.count, id: \.self) { index in
HStack(spacing: 0) {
if index == 0 {
Text("")
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
VStack(spacing: 0) {
ZStack {
Text(" ")
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
Text(String(verse.verse))
.font(Font.custom("AveriaSerifLibre-Regular", size: 10))
.padding(.horizontal, 0)
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
}
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
}
}
Text(arrayOfText[index])
.foregroundColor(Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00)))
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
.padding(.horizontal, intraWordSpacing) // intra word spacing
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
.foregroundColor(Color.white)
.onTapGesture {
Print(arrayOfText[index])
Print(verse.verse)
if self.highlights.contains(verse.verse) {
self.highlights.remove(verse.verse)
} else {
self.highlights.insert(verse.verse)
}
}
}
.frame(height: 16) // intra line spacing
.padding(.vertical, 0)
}
}
}
}
return retView
}
}
struct Pane: View {
@ObservedObject var paneConnector: PaneConnector
@State var selectedRibbon: [Ribbon]
@State var width: CGFloat
@State var height: CGFloat
var dragOffset = CGFloat()
@State var refresh: Bool = false
@Query(SegDenormRequest(book: "bible.mark")) private var segs: [SegDenorm]
@Environment(\.appDatabase) private var appDatabase
var body: some View {
var adjustedHeight = height - paneConnector.vertSep
ZStack {
ScrollViewReader { proxy in
VisibilityTrackingScrollView(action: handleVisibilityChanged) {
LazyVStack {
ForEach(segs) { seg in
SegRow(seg: seg,
ribbonId: selectedRibbon[0].id!,
width: width)
.id("\(seg.id)")
.offset(x: -dragOffset)
.padding(EdgeInsets(top: 10, leading: 20, bottom: 20, trailing: 20))
.trackVisibility(id: "\(seg.id)")
}
}
.background(Color(red: 0.18, green: 0.18, blue: 0.18))
}
.onAppear {
goToRibbon(selectedRibbon: selectedRibbon[0],
destRibbon: selectedRibbon[0],
appDatabase: appDatabase,
paneConnector: paneConnector,
loading: true)
}
.onChange(of: paneConnector.refresh) { _ in
print("inside change")
print("on change")
Task {
DispatchQueue.main.async {
if paneConnector.visibilityTracker == nil {
return
}
let gTracker = paneConnector.visibilityTracker!
Print("scroll Id target: \(paneConnector.scrollId)")
proxy.scrollTo(paneConnector.scrollId, anchor: .top)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
Print(" scroll id target", paneConnector.scrollId)
Print(" current id ", paneConnector.currentId)
Print(gTracker.sortedViewIDs)
if paneConnector.currentId != paneConnector.scrollId {
Print("NO MATCH")
}
Print(" current offset ", gTracker.visibleViews[paneConnector.scrollId])
var curOffset = gTracker.visibleViews[paneConnector.scrollId]
Print(" stats", gTracker.visibleViews)
if curOffset != nil {
paneConnector.setScrollOffset = CGFloat(Int(paneConnector.scrollOffset) - Int(curOffset!))
// setScrollOffset = CGFloat(Int(paneConnector.scrollOffset) - Int(curOffset!))
// setScrollOffset = CGFloat(Int(paneConnector.scrollOffset) - Int(curOffset!))
Print("applying scroll offset \(paneConnector.setScrollOffset)")
// paneConnector = paneConnector.copy() as! PaneConnector
self.refresh.toggle()
} else {
var adjust = (Int(paneConnector.scrollId)! - Int(paneConnector.currentId)!) * 200
Print("adjusting \(adjust)")
paneConnector.setScrollOffset = CGFloat(adjust)
refresh.toggle()
}
}
}
}
}
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in
Print("introspect")
// Weird hack for reactivity
if self.refresh {
let reactive = self.refresh
}
// if self.paneConnector != nil {
// let reactive = self.paneConnector
// }
DispatchQueue.main.async {
if paneConnector.setScrollOffset != nil {
scrollView.contentOffset.y = scrollView.contentOffset.y + paneConnector.setScrollOffset!
paneConnector.setScrollOffset = nil
withAnimation(.easeIn(duration: 0.2)) {
paneConnector.showOverlay = false
self.refresh.toggle()
}
}
}
Print("end introspect")
}
.listStyle(PlainListStyle())
}
.zIndex(1)
.background(Color(red: 0.2, green: 0.2, blue: 0.2))
.frame(width: width, height: adjustedHeight)
if self.paneConnector.showOverlay {
Rectangle()
// .frame(width: width, height: height + 200)
.background(.ultraThinMaterial)
.opacity(0.98)
.offset(y: -50)
.frame(width: width, height: adjustedHeight + 100)
// .blur(radius: 0.8)
// .opacity(1)
.transition(.opacity)
// .frame(width: geometry.size.width - 50)
// .offset(x: pulledOut.width)
// .offset(x: viewState.width, y: viewState.height)
.zIndex(2)
}
}
.frame(width: width, height: adjustedHeight)
}
func handleVisibilityChanged(_: String, change _: VisibilityChange, tracker: VisibilityTracker<String>) {
// var printRate: Int64 = 10
// gTracker = tracker
self.paneConnector.visibilityTracker = tracker
let visibleViews2 = Array(tracker.visibleViews.keys)
if visibleViews2.count == 0 {
return
}
// currentId = tracker.sortedViewIDs[tracker.sortedViewIDs.count - 1]
currentId = tracker.sortedViewIDs[0]
currentOffset = tracker.visibleViews[currentId!]!
if self.paneConnector.currentId != tracker.sortedViewIDs[0] {
self.paneConnector.currentId = tracker.sortedViewIDs[0]
}
self.paneConnector.currentOffset = tracker.visibleViews[currentId!]!
if self.paneConnector.currentId == self.paneConnector.scrollId
&& abs(self.paneConnector.currentOffset - self.paneConnector.scrollOffset) < 10 {
if self.paneConnector.hasMoved {
self.paneConnector.hasMoved = false
}
} else {
if !self.paneConnector.hasMoved {
self.paneConnector.hasMoved = true
}
}
}
}