Compare commits

...

9 Commits
undo ... master

Author SHA1 Message Date
Saint b8764b5577 swiping a bit better 2024-06-24 00:53:15 -04:00
Saint 3af58e417f stable scrolling and drag 2024-06-23 19:36:21 -04:00
saint 09ffa5f8e2 added acts 2024-06-12 13:05:04 -04:00
saint 495b052db0 Show title pane working with showing chapter
switched to observedobj from binding as it works better
2024-06-12 12:27:12 -04:00
saint ac45ae5b20 chap number view prelim 2024-06-12 12:19:30 -04:00
saint c04469ef19 swipe with drag offset works now 2024-06-09 10:32:59 -04:00
saint a8648b8508 swiping seems to work fine, need to fix dragoffset now 2024-06-09 10:25:08 -04:00
saint 56b32c39ab working better still needs tweaks 2024-06-08 19:41:44 -04:00
saint f23cff87ab working better 2024-06-08 18:53:23 -04:00
7 changed files with 280 additions and 95 deletions

View File

@ -14,46 +14,63 @@ 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 = WrappingHStack(alignment: .leading, horizontalSpacing: 0) {
ForEach(0 ..< segSplit.count, id: \.self) { segIndex in
var retView = VStack {
Text("\(seg.chap)")
.frame(width: width)
.padding(.vertical, 10)
let verse = try! decoder.decode(Verse.self, from: segSplit[segIndex].data(using: .utf8)!)
let arrayOfText = verse.body.components(separatedBy: " ")
.if(showTitle.chapVisible) { $0.foregroundColor(mainTextColor) }
.if(!showTitle.chapVisible) { $0.foregroundColor(secondBackgroundColor) }
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("")
.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(" ")
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))
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)) }
}
.if(self.highlights.contains(verse.verse)) { $0.background(Color(hex: highlightColor)) }
}
}
Text(arrayOfText[index])
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))
@ -69,9 +86,10 @@ struct SegRow: View {
self.highlights.insert(verse.verse)
}
}
}
.frame(height: 16) // intra line spacing
.padding(.vertical, 0)
}
.frame(height: 16) // intra line spacing
.padding(.vertical, 0)
}
}
}
@ -79,21 +97,31 @@ struct SegRow: View {
}
}
class ShowTitle: NSObject, ObservableObject {
@Published var chapVisible = false
}
struct Pane: View {
@ObservedObject var paneConnector: PaneConnector
@ObservedObject var scrollDelegate: ScrollDelegate
@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 {
@ -104,10 +132,13 @@ struct Pane: View {
LazyVStack {
ForEach(segs) { seg in
SegRow(seg: seg,
ribbonId: selectedRibbon[0].id!)
ribbonId: selectedRibbon[0].id!,
width: width,
showTitle: showTitle
)
.id("\(seg.id)")
.offset(x: -dragOffset)
.padding(EdgeInsets(top: 10, leading: 20, bottom: 40, trailing: 20))
.padding(EdgeInsets(top: 10, leading: 20, bottom: 20, trailing: 20))
.trackVisibility(id: "\(seg.id)")
}
}
@ -173,12 +204,11 @@ struct Pane: View {
let reactive = self.refresh
}
// if self.paneConnector != nil {
// let reactive = self.paneConnector
// }
scrollView.canCancelContentTouches = false
scrollView.delegate = scrollDelegate
DispatchQueue.main.async {
if paneConnector.setScrollOffset != nil {
DispatchQueue.main.async {
if paneConnector.setScrollOffset != nil {
scrollView.contentOffset.y = scrollView.contentOffset.y + paneConnector.setScrollOffset!
paneConnector.setScrollOffset = nil

View File

@ -11,6 +11,7 @@ struct SegDenorm: Identifiable, Equatable {
var body: String
// var lineIds: [Int64]
var book: String
var chap: Int64
}
extension SegDenorm {
@ -27,7 +28,7 @@ extension SegDenorm: Codable, FetchableRecord, MutablePersistableRecord {
// static let id = Column(CodingKeys.id)
// static let book = Column(CodingKeys.book)
// }
// /// Updates a player id after it has been inserted in the database.
// mutating func didInsert(_ inserted: InsertionSuccess) {
// id = inserted.rowID

View File

@ -43,7 +43,7 @@ struct SegDenormRequest: Queryable {
var book = ret[0].book
var sql = """
select seg_id as id, seg.book as book, group_concat(line.body, ';;') as body from \
select seg_id as id, line.chap as chap, seg.book as book, group_concat(line.body, ';;') as body from \
(select * from seg where seg.book = '\(book)') as seg \
join (select * from line where line.book = '\(book)') as line \
on seg.line_id = line.line_id group by seg.seg_id

View File

@ -42,6 +42,7 @@
85942EFE29B11C0B00307621 /* john_export.json in Resources */ = {isa = PBXBuildFile; fileRef = 85942EFC29B11C0A00307621 /* john_export.json */; };
85942EFF29B11C0B00307621 /* mark_export.json in Resources */ = {isa = PBXBuildFile; fileRef = 85942EFD29B11C0B00307621 /* mark_export.json */; };
8594ED982BF6845F001213F2 /* HexColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8594ED972BF6845F001213F2 /* HexColor.swift */; };
85AAAF572C1A0BC700FCB723 /* acts_export.json in Resources */ = {isa = PBXBuildFile; fileRef = 85AAAF562C1A0BC700FCB723 /* acts_export.json */; };
85E00E7C29F34D2D00FF9E78 /* ScrollState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E00E7B29F34D2D00FF9E78 /* ScrollState.swift */; };
85E00E7E29F34D3700FF9E78 /* ScrollStateRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E00E7D29F34D3700FF9E78 /* ScrollStateRequest.swift */; };
85F01DF82978787800F317B4 /* AveriaSerifLibre-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85F01DF72978787800F317B4 /* AveriaSerifLibre-Regular.ttf */; };
@ -83,6 +84,7 @@
85942EFC29B11C0A00307621 /* john_export.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = john_export.json; sourceTree = "<group>"; };
85942EFD29B11C0B00307621 /* mark_export.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = mark_export.json; sourceTree = "<group>"; };
8594ED972BF6845F001213F2 /* HexColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HexColor.swift; sourceTree = "<group>"; };
85AAAF562C1A0BC700FCB723 /* acts_export.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = acts_export.json; sourceTree = "<group>"; };
85E00E7B29F34D2D00FF9E78 /* ScrollState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollState.swift; sourceTree = "<group>"; };
85E00E7D29F34D3700FF9E78 /* ScrollStateRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollStateRequest.swift; sourceTree = "<group>"; };
85F01DF72978787800F317B4 /* AveriaSerifLibre-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AveriaSerifLibre-Regular.ttf"; sourceTree = "<group>"; };
@ -187,6 +189,7 @@
isa = PBXGroup;
children = (
85942EFC29B11C0A00307621 /* john_export.json */,
85AAAF562C1A0BC700FCB723 /* acts_export.json */,
85942EFD29B11C0B00307621 /* mark_export.json */,
);
path = json;
@ -283,6 +286,7 @@
85F01DFB2978790400F317B4 /* xe-Dogma-Bold.ttf in Resources */,
85F01DF82978787800F317B4 /* AveriaSerifLibre-Regular.ttf in Resources */,
8514D5BC299EFB780054F185 /* store.db in Resources */,
85AAAF572C1A0BC700FCB723 /* acts_export.json in Resources */,
85942EFF29B11C0B00307621 /* mark_export.json in Resources */,
85942EFE29B11C0B00307621 /* john_export.json in Resources */,
85431A8D2905F4F600EE0760 /* Assets.xcassets in Resources */,

View File

@ -79,7 +79,7 @@ struct AppDatabase {
}
// change this to nuke/remake the database
try db.create(table: "foo2") { t in
try db.create(table: "foo4") { t in
t.autoIncrementedPrimaryKey("id")
t.column("ribbonId", .integer).notNull()
}
@ -399,6 +399,7 @@ extension AppDatabase {
{
try importJson("john_export.json", db)
try importJson("mark_export.json", db)
try importJson("acts_export.json", db)
_ = try Ribbon(id: 1,
groupId: 1,
pos: 1,
@ -406,7 +407,7 @@ extension AppDatabase {
currentLevel: 0,
minLevel: 0,
maxLevel: 0,
title: "John",
title: "Gospel of John",
book: "bible.john",
scrollId: "1",
scrollOffset: 0).inserted(db)
@ -418,7 +419,7 @@ extension AppDatabase {
currentLevel: 0,
minLevel: 0,
maxLevel: 0,
title: "Gospel of Mark and other dogmatic works",
title: "Gospel according to Mark",
book: "bible.mark",
scrollId: "1",
scrollOffset: 300).inserted(db)
@ -432,8 +433,8 @@ extension AppDatabase {
currentLevel: 2,
minLevel: 0,
maxLevel: 2,
title: "bottom",
book: "bible.john",
title: "Acts",
book: "bible.acts",
scrollId: "1",
scrollOffset: 0).inserted(db)
@ -444,8 +445,8 @@ extension AppDatabase {
currentLevel: 2,
minLevel: 0,
maxLevel: 2,
title: "topp",
book: "bible.john",
title: "Acts",
book: "bible.acts",
scrollId: "1",
scrollOffset: 0).inserted(db)
@ -456,8 +457,8 @@ extension AppDatabase {
currentLevel: 2,
minLevel: 0,
maxLevel: 2,
title: "topp",
book: "bible.john",
title: "Acts",
book: "bible.acts",
scrollId: "1",
scrollOffset: 0).inserted(db)

View File

@ -18,6 +18,11 @@ var currentOffset: CGFloat?
var disableDrop = false
//TODO: move to globals file
let mainBackgroundColor = Color(red: 0.1, green: 0.1, blue: 0.1)
let mainTextColor = Color(UIColor(red: 0.76, green: 0.76, blue: 0.76, alpha: 1.00))
let secondBackgroundColor = Color(red: 0.18, green: 0.18, blue: 0.18)
public extension UserDefaults {
func optionalInt(forKey defaultName: String) -> Int? {
let defaults = self
@ -210,6 +215,7 @@ func makeVerseView(seg: SegDenorm) -> some View {
}
class PaneConnector: NSObject, ObservableObject {
var showOverlay: Bool = false
@Published var refresh: Bool = false
@Published var vertSep = CGFloat(20)
@ -220,9 +226,27 @@ class PaneConnector: NSObject, ObservableObject {
@Published var scrollOffset = CGFloat()
@Published var hasMoved = false
var setScrollOffset: CGFloat?
@Published var disableScroll = false
}
class ScrollDelegate: NSObject, ObservableObject, UIScrollViewDelegate {
@Published var isScrolling = false
func scrollViewWillBeginDragging(_: UIScrollView) {
print("pop started scroll")
isScrolling.toggle()
}
func scrollViewDidEndDragging(_: UIScrollView, willDecelerate _: Bool) {
print("pop ended scroll")
isScrolling.toggle()
}
}
struct ContentView: View {
// this is for the whole view swiping
@State var mainSwipe = CGSize.zero
@ -231,6 +255,7 @@ struct ContentView: View {
@State var selection = 0
@StateObject var paneConnector = PaneConnector()
@StateObject var scrollDelegate = ScrollDelegate()
@State var refresh: Bool = false
@ -243,6 +268,23 @@ struct ContentView: View {
@State var dragOffset = CGFloat(0)
@GestureState var dragGestureActive = CGSize.zero
enum SwipeStartState {
case center
case right
case left
}
enum SwipeStartDir {
case left
case right
}
// @State var curSwipeState: SwipeState = .start
@State var startSwipeState: SwipeStartState?
@State var startSwipeDir: SwipeStartDir?
@Environment(\.appDatabase) private var appDatabase
@Query(RibbonRequest()) private var ribbons: [Ribbon]
@ -251,10 +293,13 @@ struct ContentView: View {
init() {
UITableView.appearance().backgroundColor = UIColor(Color(red: 0.2, green: 0.2, blue: 0.2))
_selectedRibbon = Query(SelectedRibbonRequest())
}
var body: some View {
var fontSize = CGFloat(15)
var scale = 0.65
var height = CGFloat(50)
@ -307,6 +352,7 @@ struct ContentView: View {
// Top pane
Pane(paneConnector: paneConnector,
scrollDelegate: scrollDelegate,
selectedRibbon: selectedRibbon,
width: geometry.size.width - 15,
height: geometry.size.height + 20,
@ -320,7 +366,7 @@ struct ContentView: View {
// .onChanged { gesture in
// paneConnector.vertSep = paneConnector.vertSep - gesture.translation.height
// }
// )
//
// // Bottom pane
// ScrollViewReader { _ in
@ -351,85 +397,187 @@ struct ContentView: View {
}
.offset(x: 20, y: 0)
.offset(x: pulledOut.width)
.offset(x: mainSwipe.width, y: mainSwipe.height)
.gesture(
DragGesture()
.onChanged { gesture in
.offset(x: mainSwipe.width)
.highPriorityGesture(
DragGesture(minimumDistance: 20)
.updating($dragGestureActive) { value, state, _ in
print("pop here")
state = value.translation
}
.onChanged { value in
print("pop changed")
// Calculate the offset
let margin = CGFloat(30)
let rightSwipeLimit = CGFloat(110)
let leftSwipeLimit = CGFloat(-200)
print("mao mainSwipe :\(mainSwipe.width)")
print(pulledOut.width)
var swipeWidth = gesture.translation.width
var swipeActual = gesture.translation.width + pulledOut.width
// swiping right
if swipeWidth > 0 {
if swipeActual > rightSwipeLimit {
swipeWidth = rightSwipeLimit + pow(swipeActual - rightSwipeLimit, 0.8)
var newOffset = value.translation.width
if startSwipeState == nil {
if pulledOut.width == 0 {
startSwipeState = .center
} else if pulledOut.width < 0 {
startSwipeState = .left
} else {
startSwipeState = .right
}
mainSwipe.width = swipeWidth - pulledOut.width
// swiping right
} else {
if swipeActual < leftSwipeLimit {
swipeWidth = leftSwipeLimit - pow(-swipeActual + leftSwipeLimit, 0.8)
}
mainSwipe.width = swipeWidth - pulledOut.width
print("start swipe meow: \(startSwipeState)")
}
if newOffset > 0 {
startSwipeDir = .right
} else {
startSwipeDir = .left
}
// Apply resistance if out of bounds
let maxRight: CGFloat = 140
let finalRight: CGFloat = 110
let rightDiff = maxRight - finalRight
var maxOffsetLeft: CGFloat = -200
if newOffset > maxRight , pulledOut.width == 0 {
// newOffset = maxRight + rubberBandEffect(newOffset)
newOffset = maxRight + rubberBandEffect(newOffset - maxRight)
}
if newOffset > rightDiff, pulledOut.width > 0 {
newOffset = rightDiff + rubberBandEffect(newOffset - rightDiff)
}
if newOffset < maxOffsetLeft, pulledOut.width == 0 {
newOffset = maxOffsetLeft + rubberBandEffect(newOffset)
}
self.mainSwipe.width = newOffset
// dragOffset is what is used to make the text be readable
// with the right pane being visible
// if mainSwipe.width < -margin && pulledOut.width <= 0 {
if mainSwipe.width < -margin && pulledOut.width <= 0 {
if mainSwipe.width < -margin, pulledOut.width <= 0 {
dragOffset = margin + mainSwipe.width + pulledOut.width
}
if mainSwipe.width > 0 && pulledOut.width < 0 {
if mainSwipe.width > 0, pulledOut.width < 0 {
dragOffset = margin + mainSwipe.width + pulledOut.width
}
}
.onEnded { _ in
let pulledOutRight = CGFloat(-200)
let pulledOutLeft = CGFloat(110)
let margin = CGFloat(30)
var setPulledOutWith = CGFloat(0)
var setDragOffset = CGFloat(0)
if mainSwipe.width < 0 && pulledOut.width > 0 {
setPulledOutWith = CGFloat(0)
} else if mainSwipe.width > 0 && pulledOut.width < 0 {
setPulledOutWith = CGFloat(0)
} else if (mainSwipe.width < 0 && pulledOut.width < 0) ||
(mainSwipe.width < 0 && pulledOut.width == 0) {
setPulledOutWith = pulledOutRight
setDragOffset = margin + setPulledOutWith
} else if abs(mainSwipe.width + pulledOut.width) > 30 {
setPulledOutWith = pulledOutLeft
}
withAnimation(.spring(response: 0.2)) {
pulledOut.width = setPulledOutWith
dragOffset = setDragOffset
mainSwipe = .zero
}
onDragEnded()
}
)
)
.onChange(of: scrollDelegate.isScrolling) { value in
print ("pop reset change")
// if scrollDelegate.isScrolling == true {
Task {
mainSwipe.width = 0
}
// }
}
}
}
.background(Color(red: 0.1, green: 0.1, blue: 0.1))
}
func onDragEnded() {
print("pop ended")
paneConnector.disableScroll = false
var finalSwipe = CGFloat(0)
let swipeLeftFinal = CGFloat(-200)
let swipeRightFinal = CGFloat(110)
let margin = CGFloat(30)
var setDragOffset = CGFloat(0)
if startSwipeState == .center {
if startSwipeDir == .right {
finalSwipe = swipeRightFinal
} else {
finalSwipe = swipeLeftFinal
setDragOffset = margin + swipeLeftFinal
}
} else if startSwipeState == .right {
finalSwipe = .zero
if startSwipeDir == .left {
finalSwipe = .zero
} else {
finalSwipe = swipeRightFinal
}
} else if startSwipeState == .left {
if startSwipeDir == .left {
finalSwipe = swipeLeftFinal
setDragOffset = margin + swipeLeftFinal
} else {
finalSwipe = .zero
}
}
startSwipeState = nil
startSwipeDir = nil
print("foo")
// if mainSwipe.width < 0 && pulledOut.width > 0 {
// setPulledOutWith = CGFloat(0)
// } else if mainSwipe.width > 0 && pulledOut.width < 0 {
// setPulledOutWith = CGFloat(0)
// } else if (mainSwipe.width < 0 && pulledOut.width < 0) ||
// (mainSwipe.width < 0 && pulledOut.width == 0) {
// setPulledOutWith = pulledOutRight
// setDragOffset = margin + setPulledOutWith
// } else if abs(mainSwipe.width + pulledOut.width) > 30 {
// setPulledOutWith = pulledOutLeft
// }
withAnimation(.spring(response: 0.2)) {
pulledOut.width = finalSwipe
dragOffset = setDragOffset
mainSwipe = .zero
}
}
// let hackyPinch = MagnificationGesture(minimumScaleDelta: 0.0)
// .onChanged({ delta in
// onDragEnded()
// })
// .onEnded({ delta in
// onDragEnded()
// })
// let hackyRotation = RotationGesture(minimumAngleDelta: Angle(degrees: 0.0))
// .onChanged({ delta in
// onDragEnded()
// })
// .onEnded({ delta in
// onDragEnded()
// })
// let hackyPress = LongPressGesture(minimumDuration: 0.0, maximumDistance: 0.0)
// .onChanged({ _ in
// onDragEnded()
// })
// .onEnded({ delta in
// onDragEnded()
// })
func handleVisibilityChanged2(_: String, change _: VisibilityChange, tracker _: VisibilityTracker<String>) {}
func rubberBandEffect(_ offset: CGFloat) -> CGFloat {
let resistance: CGFloat = 0.05
return 6 * log(offset + 1)
// return resistance * pow(abs(offset), 1) * (offset < 0 ? -1 : 1)
}
private func dragCancelled() {
print("pop dragCancelled")
mainSwipe = .zero
}
}
private let itemFormatter: DateFormatter = {

1
json/acts_export.json Normal file

File diff suppressed because one or more lines are too long