// // 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 @State var highlights = Set() let intraWordSpacing = CGFloat(1.6) var body: some View { var segSplit = seg.body.components(separatedBy: ";;") let decoder = JSONDecoder() var retView = 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(20) 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 @State var dragOffset = CGFloat() @State var refresh: Bool = false @Query(SegDenormRequest(book: "bible.mark")) private var segs: [SegDenorm] @Environment(\.appDatabase) private var appDatabase // var handleVisibilityChanged: (String, VisibilityChange, VisibilityTracker) -> Void 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!) .id("\(seg.id)") .offset(x: -dragOffset) .padding(EdgeInsets(top: 10, leading: 20, bottom: 40, 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, refresh: $refresh, 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 // } if paneConnector.setScrollOffset != nil { DispatchQueue.main.async { 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) { // 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!]! self.paneConnector.currentId = tracker.sortedViewIDs[0] self.paneConnector.currentOffset = tracker.visibleViews[currentId!]! } }