288 lines
12 KiB
Swift
288 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>()
|
|
@ObservedObject var showTitle: ShowTitle
|
|
|
|
|
|
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(showTitle.chapVisible) { $0.foregroundColor(mainTextColor) }
|
|
.if(!showTitle.chapVisible) { $0.foregroundColor(secondBackgroundColor) }
|
|
|
|
.font(Font.custom("AveriaSerifLibre-Regular", size: fontSize))
|
|
.background(secondBackgroundColor)
|
|
// .background(mainBackgroundColor)
|
|
.onTapGesture {
|
|
showTitle.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
|
|
}
|
|
}
|
|
|
|
class ShowTitle: NSObject, ObservableObject {
|
|
@Published var chapVisible = false
|
|
}
|
|
|
|
struct Pane: View {
|
|
@ObservedObject var paneConnector: PaneConnector
|
|
@StateObject var showTitle = ShowTitle()
|
|
|
|
// @State var chapVisible = false
|
|
|
|
@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,
|
|
showTitle: showTitle
|
|
)
|
|
.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
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|