Skip to content

Commit

Permalink
Show a popup with chord diagram when tapping a chord
Browse files Browse the repository at this point in the history
  • Loading branch information
Desbeers committed Aug 29, 2023
1 parent 668afe2 commit 73ea1db
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 156 deletions.
14 changes: 14 additions & 0 deletions Chord Provider.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
objects = {

/* Begin PBXBuildFile section */
D701F8792A9CB92F007DACF3 /* ChordDiagramView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D701F8782A9CB92F007DACF3 /* ChordDiagramView.swift */; };
D701F87A2A9CB92F007DACF3 /* ChordDiagramView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D701F8782A9CB92F007DACF3 /* ChordDiagramView.swift */; };
D701F87B2A9CBA3D007DACF3 /* Aliasses.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F13FA02A6460B7002D875D /* Aliasses.swift */; };
D701F87D2A9CF806007DACF3 /* Song+Chord+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = D701F87C2A9CF806007DACF3 /* Song+Chord+Status.swift */; };
D701F87E2A9CF806007DACF3 /* Song+Chord+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = D701F87C2A9CF806007DACF3 /* Song+Chord+Status.swift */; };
D7A9B7C12A93BE5E007FD0A3 /* SwiftlyChordUtilities in Frameworks */ = {isa = PBXBuildFile; productRef = D7A9B7C02A93BE5E007FD0A3 /* SwiftlyChordUtilities */; };
D7A9B7C32A93BE8C007FD0A3 /* SwiftlyChordUtilities in Frameworks */ = {isa = PBXBuildFile; productRef = D7A9B7C22A93BE8C007FD0A3 /* SwiftlyChordUtilities */; };
D7AB1E812A98C284009BC9FB /* DocumentKit in Frameworks */ = {isa = PBXBuildFile; platformFilter = ios; productRef = D7AB1E802A98C284009BC9FB /* DocumentKit */; };
Expand Down Expand Up @@ -100,6 +105,8 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
D701F8782A9CB92F007DACF3 /* ChordDiagramView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChordDiagramView.swift; sourceTree = "<group>"; };
D701F87C2A9CF806007DACF3 /* Song+Chord+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Song+Chord+Status.swift"; sourceTree = "<group>"; };
D7AB1E822A98E155009BC9FB /* OnboardingView (iPadOS).swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnboardingView (iPadOS).swift"; sourceTree = "<group>"; };
D7BFC7632A9A522E005E2A3E /* Song+Style.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Song+Style.swift"; sourceTree = "<group>"; };
D7D39C4E2A9A13DA004EAE5F /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -220,6 +227,7 @@
D7F13F8B2A6460B7002D875D /* Song+Section+Line+Grid.swift */,
D7F13F852A6460B7002D875D /* Song+Section+Line.swift */,
D7F13F872A6460B7002D875D /* Song+Chord.swift */,
D701F87C2A9CF806007DACF3 /* Song+Chord+Status.swift */,
D7F13F882A6460B7002D875D /* Song+Render.swift */,
D7BFC7632A9A522E005E2A3E /* Song+Style.swift */,
);
Expand Down Expand Up @@ -305,6 +313,7 @@
D7F13FAD2A6460B7002D875D /* ToolbarView.swift */,
D7F13FB82A6460B7002D875D /* SongView.swift */,
D7F13FB72A6460B7002D875D /* ChordsView.swift */,
D701F8782A9CB92F007DACF3 /* ChordDiagramView.swift */,
D7F13FBB2A6460B7002D875D /* AudioPlayerView.swift */,
D7F13FB62A6460B7002D875D /* PrintSongView.swift */,
D7F13FAC2A6460B7002D875D /* ShareSongView.swift */,
Expand Down Expand Up @@ -503,6 +512,7 @@
D7F13FE22A6460B7002D875D /* MainView.swift in Sources */,
D7F13FD72A6460B7002D875D /* ChordPro+Directive.swift in Sources */,
D7F13FC62A6460B7002D875D /* WindowManagement (macOS).swift in Sources */,
D701F8792A9CB92F007DACF3 /* ChordDiagramView.swift in Sources */,
D7F13FDF2A6460B7002D875D /* EditorView.swift in Sources */,
D7D39C4F2A9A13DA004EAE5F /* Status.swift in Sources */,
D7F13FE62A6460B7002D875D /* ContentView.swift in Sources */,
Expand All @@ -527,6 +537,7 @@
D7D39C512A9A19A1004EAE5F /* WelcomeView.swift in Sources */,
D7F13FE52A6460B7002D875D /* SongView.swift in Sources */,
D7F13FC32A6460B7002D875D /* ExportSong+render.swift in Sources */,
D701F87D2A9CF806007DACF3 /* Song+Chord+Status.swift in Sources */,
D7F13FCD2A6460B7002D875D /* ChordProviderApp.swift in Sources */,
D7F13FD42A6460B7002D875D /* ChordPro+Directive+extension.swift in Sources */,
D7F13FC42A6460B7002D875D /* ExportDocument.swift in Sources */,
Expand All @@ -548,14 +559,17 @@
D7F140162A64647D002D875D /* Song+Section+Line+Grid.swift in Sources */,
D7F140112A64647D002D875D /* Song+Chord.swift in Sources */,
D7F1400E2A64646E002D875D /* ChordPro+Directive+extension.swift in Sources */,
D701F87A2A9CB92F007DACF3 /* ChordDiagramView.swift in Sources */,
D7F140142A64647D002D875D /* Song+Section.swift in Sources */,
D7F140102A64646E002D875D /* ChordPro+Environment.swift in Sources */,
D7F1400D2A64646E002D875D /* ChordPro+Directive.swift in Sources */,
D7F1400C2A64646E002D875D /* ChordPro+Regex.swift in Sources */,
D7BFC7652A9A522E005E2A3E /* Song+Style.swift in Sources */,
D7F140152A64647D002D875D /* Song+Section+Line+Part.swift in Sources */,
D7F140122A64647D002D875D /* Song+Render.swift in Sources */,
D701F87B2A9CBA3D007DACF3 /* Aliasses.swift in Sources */,
D7F1400B2A646439002D875D /* PreviewProvider.swift in Sources */,
D701F87E2A9CF806007DACF3 /* Song+Chord+Status.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
41 changes: 29 additions & 12 deletions Chord Provider/ChordProParser/ChordPro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,16 @@ enum ChordPro {
private static func processDefine(text: String, song: inout Song) {
if let match = text.wholeMatch(of: defineRegex) {
let key = match.1
/// Remove standard chords with the same key if there is one in the chords list
song.chords = song.chords.filter { !($0.name == key && $0.isCustom == false) }
/// Parse the define
if let chord = define(from: text) {
song.chords.append(Song.Chord(name: key, chordPosition: chord, isCustom: true))
let status: Song.Chord.Status = song.transpose == 0 ? .custom : .customTransposed
/// Update a standard chord with the same key if there is one in the chords list
if let index = song.chords.firstIndex(where: { $0.name == key && ($0.status == .standard || $0.status == .transposed) }) {
song.chords[index].chordPosition = chord
song.chords[index].status = status
} else {
song.chords.append(Song.Chord(name: key, chordPosition: chord, status: status))
}
}
}
}
Expand Down Expand Up @@ -303,7 +308,7 @@ enum ChordPro {
if let chord {
let result = processChord(chord: String(chord), song: &song)
/// Add it as chord
grid.parts.append(Song.Section.Line.Part(id: partID, chord: result))
grid.parts.append(Song.Section.Line.Part(id: partID, chord: result.id))
partID += 1
}

Expand Down Expand Up @@ -344,7 +349,7 @@ enum ChordPro {
let (_, chord, lyric) = match.output
var part = Song.Section.Line.Part(id: partID)
if let chord {
part.chord = processChord(chord: String(chord), song: &song)
part.chord = processChord(chord: String(chord), song: &song).id
part.text = " "
/// Because it has a chord; it should be at least a verse
if currentSection.type == .none {
Expand Down Expand Up @@ -373,10 +378,10 @@ enum ChordPro {
/// - chord: The `chord` as String
/// - song: The `Song`
/// - Returns: The processed `chord` as String
private static func processChord(chord: String, song: inout Song) -> String {
private static func processChord(chord: String, song: inout Song) -> Song.Chord {
/// Check if this chord is aready parsed
if let match = song.chords.first(where: { $0.name == chord }) {
return match.display
if let match = song.chords.last(where: { $0.name == chord }) {
return match
}
/// Try to find the chord in SwiftyChords and append to the used chord list from this song
let result = findRootAndQuality(chord: chord)
Expand All @@ -393,12 +398,24 @@ enum ChordPro {
.filter { $0.key == rootValue && $0.suffix == quality }
.sorted { $0.baseFret < $1.baseFret }
if let baseChord = chords.first {
let songChord = Song.Chord(name: chord, chordPosition: baseChord, isCustom: false)
let status: Song.Chord.Status = song.transpose == 0 ? .standard : .transposed
let songChord = Song.Chord(name: chord, chordPosition: baseChord, status: status)
song.chords.append(songChord)
return songChord.display
return songChord
}
}
/// Return the chord because it is possible to have a custom defined chard after the usage of that chord.
return chord
/// Return the chord because it is possible to have a custom defined chord after the usage of that chord.
let chordPosition = ChordPosition(
frets: [0],
fingers: [0],
baseFret: 0,
barres: [0],
midi: [0],
key: .c,
suffix: .major
)
let songChord = Song.Chord(name: chord, chordPosition: chordPosition, status: .unknown)
song.chords.append(songChord)
return songChord
}
}
66 changes: 37 additions & 29 deletions Chord Provider/Export/ExportSong+render.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,39 +39,49 @@ extension ExportSong {
@MainActor
static func renderChords(song: Song) -> CGImage? {
/// Size of the chord diagram
var frame: CGRect {
var width = Int((pageWidth * 2.0) / Double(song.chords.count))
width = width > Int(pageWidth / 4.0) ? Int(pageWidth / 4.0) : width
let height = Int(Double(width) * 1.5)
return CGRect(x: 0, y: 0, width: width, height: height)
}
// var frame: CGRect {
// var width = Int((pageWidth * 2.0) / Double(song.chords.count))
// width = width > Int(pageWidth / 4.0) ? Int(pageWidth / 4.0) : width
// let height = Int(Double(width) * 1.5)
// return CGRect(x: 0, y: 0, width: width, height: height)
// }
var frame = CGRect(x: 0, y: 0, width: 60, height: 80)
/// Render the chords
let renderer = ImageRenderer(
content:
HStack {
LazyVGrid(
columns: [GridItem(.adaptive(minimum: frame.width ))],
alignment: .center,
spacing: 4,
pinnedViews: [.sectionHeaders, .sectionFooters]
) {
ForEach(song.chords.sorted(using: KeyPathComparator(\.name))) { chord in
VStack {
Text(chord.display)
.foregroundColor(Color("AccentColor"))
.font(.caption)
let layer = chord.chordPosition.chordLayer(
rect: frame,
showFingers: false,
chordName: .init(show: false)
)
if let image = layer.image() {
Image(swiftImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: frame.width / 3)
}
}
ChordDiagramView(chord: chord, frame: frame)
//.minimumScaleFactor(0.1)
//.frame(maxWidth: frame.width / 10)
// VStack {
// Text(chord.display)
// .foregroundColor(Color.gray)
// .font(.title)
// let layer = chord.chordPosition.chordLayer(
// rect: frame,
// showFingers: false,
// chordName: .init(show: false)
// )
// if let image = layer.image() {
// Image(swiftImage: image)
// .resizable()
// .aspectRatio(contentMode: .fit)
// .frame(width: frame.width)
// }
// }
}
}
.padding()
.frame(width: pageWidth, alignment: .center)
.preferredColorScheme(.light)
.background(.white)
.accentColor(.gray)
)
renderer.scale = rendererScale
return renderer.cgImage
Expand All @@ -86,18 +96,16 @@ extension ExportSong {
var part: CGImage?
for section in song.sections {
switch section.type {
case .verse:
part = renderPart(view: Song.Render.VerseView(section: section))
case .verse, .bridge:
part = renderPart(view: Song.Render.VerseView(section: section, chords: song.chords))
case .chorus:
part = renderPart(view: Song.Render.ChorusView(section: section))
case .bridge:
part = renderPart(view: Song.Render.VerseView(section: section))
part = renderPart(view: Song.Render.ChorusView(section: section, chords: song.chords))
case .repeatChorus:
part = renderPart(view: Song.Render.RepeatChorusView(section: section))
case .tab:
part = renderPart(view: Song.Render.TabView(section: section))
case .grid:
part = renderPart(view: Song.Render.GridView(section: section))
part = renderPart(view: Song.Render.GridView(section: section, chords: song.chords))
case .comment:
part = renderPart(view: Song.Render.CommentView(section: section))
default:
Expand Down
25 changes: 25 additions & 0 deletions Chord Provider/SongModel/Song+Chord+Status.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// Song+Chord+Status.swift
// Chord Provider
//
// © 2023 Nick Berendsen
//

import Foundation

extension Song.Chord {

/// The status of a chord
enum Status: String {
/// A standard chord from the SwiftyChords database
case standard
/// A transposed chord
case transposed
/// A custom defined chord
case custom
/// A custom defined chord that is transposed
case customTransposed
/// An uknown chord
case unknown
}
}
24 changes: 15 additions & 9 deletions Chord Provider/SongModel/Song+Chord.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,38 @@ extension Song {
var id = UUID()
/// The name of the chord
var name: String
/// The ChordPosition defenition
/// The ChordPosition definition
var chordPosition: ChordPosition
/// Bool if the chord is custom or from the SwiftyChords database
var isCustom: Bool
/// The status of the chord
var status: Status
/// Display name for the chord
var display: String {
if isCustom {
switch status {
case .standard, .transposed:
return chordPosition.key.display.symbol + chordPosition.suffix.display.symbolized
case .custom, .customTransposed:
/// Try to find it in the SwiftyChords database
let chord = findRootAndQuality(chord: name)
if let root = chord.root, let quality = chord.quality {
return "\(root.display.symbol)\(quality.display.symbolized)*"
}
return "\(name)*"
} else {
return chordPosition.key.display.symbol + chordPosition.suffix.display.symbolized
case .unknown:
return "\(name)*"
}
}

/// The chord postions are different if this is a custom chord. So return the custom chord or the default standard chord positions.
/// - Returns: all chord postions for this chord
func getChordPostions() -> [ChordPosition] {
var chordPositions = [ChordPosition]()
if isCustom {
chordPositions.append(chordPosition)
} else {
switch status {
case .standard, .transposed:
chordPositions = SwiftyChords.Chords.guitar.matching(key: chordPosition.key).matching(suffix: chordPosition.suffix)
case .custom, .customTransposed:
chordPositions.append(chordPosition)
case .unknown:
break
}
return chordPositions
}
Expand Down
Loading

0 comments on commit 73ea1db

Please sign in to comment.