module Termtab.UI.Widgets.Tablature (renderTablature, cursorAttr, playheadAttr, selectionAttr, barLineAttr, stringLabelAttr, findVisibleRange, beatColumnWidth) where
import Brick hiding (zoom)
import Data.Map.Strict qualified as Map
import Termtab.Types
import Termtab.UI.Types
cursorAttr :: AttrName
cursorAttr :: AttrName
cursorAttr = String -> AttrName
attrName String
"cursor"
playheadAttr :: AttrName
playheadAttr :: AttrName
playheadAttr = String -> AttrName
attrName String
"playhead"
selectionAttr :: AttrName
selectionAttr :: AttrName
selectionAttr = String -> AttrName
attrName String
"selection"
barLineAttr :: AttrName
barLineAttr :: AttrName
barLineAttr = String -> AttrName
attrName String
"barLine"
stringLabelAttr :: AttrName
stringLabelAttr :: AttrName
stringLabelAttr = String -> AttrName
attrName String
"stringLabel"
line :: Char
line :: Char
line = Char
'─'
bar :: String
bar :: String
bar = String
"│"
renderTablature :: AppState -> TrackIndex -> Widget ResourceName
renderTablature :: AppState -> TrackIndex -> Widget ResourceName
renderTablature AppState
st TrackIndex
tIdx = Size
-> Size
-> RenderM ResourceName (Result ResourceName)
-> Widget ResourceName
forall n. Size -> Size -> RenderM n (Result n) -> Widget n
Widget Size
Greedy Size
Fixed (RenderM ResourceName (Result ResourceName) -> Widget ResourceName)
-> RenderM ResourceName (Result ResourceName)
-> Widget ResourceName
forall a b. (a -> b) -> a -> b
$ do
ctx <- RenderM ResourceName (Context ResourceName)
forall n. RenderM n (Context n)
getContext
let w = Context ResourceName -> Int
forall n. Context n -> Int
availWidth Context ResourceName
ctx
track = TrackIndex -> Song -> Track
getTrack TrackIndex
tIdx (AppState -> Song
asSong AppState
st)
nStrings = Track -> Int
stringCountForTrack' Track
track
labelWidth = Int
1
contentWidth = Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
labelWidth
measures = Song -> [Measure]
songMeasures (AppState -> Song
asSong AppState
st)
zoom = AppState -> Int
asZoom AppState
st
MeasureIndex cursorM = asCurrentMeasure st
visibleMeasures = Track
-> [Measure]
-> Int
-> Int
-> Int
-> [(MeasureIndex, [Beat], Measure)]
findVisibleRange Track
track [Measure]
measures Int
zoom Int
contentWidth Int
cursorM
isFocused = TrackIndex
tIdx TrackIndex -> TrackIndex -> Bool
forall a. Eq a => a -> a -> Bool
== AppState -> TrackIndex
asCurrentTrack AppState
st
labels = Int -> [String]
tabLabels Int
nStrings
render $
vBox $
[renderMeasureNumberRow labelWidth zoom visibleMeasures]
++ [ renderStringLine st isFocused labelWidth zoom visibleMeasures (StringIndex si) (labels !! si)
| si <- reverse [0 .. nStrings - 1]
]
getTrack :: TrackIndex -> Song -> Track
getTrack :: TrackIndex -> Song -> Track
getTrack (TrackIndex Int
i) Song
song =
case Int -> [Track] -> [Track]
forall a. Int -> [a] -> [a]
drop Int
i (Song -> [Track]
songTracks Song
song) of
(Track
t : [Track]
_) -> Track
t
[] -> Text
-> Instrument -> MidiChannel -> Map MeasureIndex [Beat] -> Track
Track Text
"?" (MidiProgram -> Instrument
Standard (Int -> MidiProgram
MidiProgram Int
0)) (Int -> MidiChannel
MidiChannel Int
0) Map MeasureIndex [Beat]
forall k a. Map k a
Map.empty
stringCountForTrack' :: Track -> Int
stringCountForTrack' :: Track -> Int
stringCountForTrack' Track
track = case Track -> Instrument
trackInstrument Track
track of
Guitar [Pitch]
_ Int
n -> Int
n
Bass [Pitch]
_ Int
n -> Int
n
Standard MidiProgram
_ -> Int
1
tabLabels :: Int -> [String]
tabLabels :: Int -> [String]
tabLabels Int
n
| Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
3 =
Int -> String -> [String]
forall a. Int -> a -> [a]
replicate (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
3) [Char
line] [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
"B", String
"A", String
"T"]
| Bool
otherwise = Int -> String -> [String]
forall a. Int -> a -> [a]
replicate Int
n [Char
line]
renderMeasureNumberRow :: Int -> Int -> [(MeasureIndex, [Beat], Measure)] -> Widget ResourceName
renderMeasureNumberRow :: Int
-> Int -> [(MeasureIndex, [Beat], Measure)] -> Widget ResourceName
renderMeasureNumberRow Int
labelWidth Int
zoom [(MeasureIndex, [Beat], Measure)]
visibleMeasures =
[Widget ResourceName] -> Widget ResourceName
forall n. [Widget n] -> Widget n
hBox ([Widget ResourceName] -> Widget ResourceName)
-> [Widget ResourceName] -> Widget ResourceName
forall a b. (a -> b) -> a -> b
$
[String -> Widget ResourceName
forall n. String -> Widget n
str (Int -> Char -> String
forall a. Int -> a -> [a]
replicate Int
labelWidth Char
' ')]
[Widget ResourceName]
-> [Widget ResourceName] -> [Widget ResourceName]
forall a. [a] -> [a] -> [a]
++ ((MeasureIndex, [Beat], Measure) -> Widget ResourceName)
-> [(MeasureIndex, [Beat], Measure)] -> [Widget ResourceName]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> (MeasureIndex, [Beat], Measure) -> Widget ResourceName
renderMeasureNumber Int
zoom) [(MeasureIndex, [Beat], Measure)]
visibleMeasures
renderMeasureNumber :: Int -> (MeasureIndex, [Beat], Measure) -> Widget ResourceName
renderMeasureNumber :: Int -> (MeasureIndex, [Beat], Measure) -> Widget ResourceName
renderMeasureNumber Int
zoom (MeasureIndex Int
mi, [Beat]
beats, Measure
measure) =
let TimeSignature Int
num Int
_ = Measure -> TimeSignature
timeSignature Measure
measure
totalCols :: Int
totalCols = Int
num Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
zoom
usedCols :: Int
usedCols = [Int] -> Int
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Beat -> Int) -> [Beat] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Duration -> Int
beatColumnWidth Int
zoom (Duration -> Int) -> (Beat -> Duration) -> Beat -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Beat -> Duration
beatDuration) [Beat]
beats)
remainingCols :: Int
remainingCols = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int
totalCols Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
usedCols)
emptySlotCount :: Int
emptySlotCount = if Int
zoom Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 then Int
remainingCols Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
zoom else Int
0
actualTotalCols :: Int
actualTotalCols = Int
usedCols Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
emptySlotCount Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
zoom
mNum :: String
mNum = Int -> String
forall a. Show a => a -> String
show (Int
mi Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
padded :: String
padded = String
mNum String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int
actualTotalCols Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
- String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
mNum)) Char
' '
in String -> Widget ResourceName
forall n. String -> Widget n
str String
padded
beatColumnWidth :: Int -> Duration -> Int
beatColumnWidth :: Int -> Duration -> Int
beatColumnWidth Int
zoom Duration
dur = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
3 (Int
zoom Int -> Int -> Int
forall a. Num a => a -> a -> a
* Duration -> Int
durationWeight Duration
dur Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
4)
durationWeight :: Duration -> Int
durationWeight :: Duration -> Int
durationWeight Duration
Whole = Int
16
durationWeight Duration
Half = Int
8
durationWeight Duration
Quarter = Int
4
durationWeight Duration
Eighth = Int
2
durationWeight Duration
Sixteenth = Int
1
durationWeight Duration
Thirty2nd = Int
1
durationWeight (Dotted Duration
d) = Duration -> Int
durationWeight Duration
d Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Duration -> Int
durationWeight Duration
d Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2
durationWeight (Triplet Duration
d) = Duration -> Int
durationWeight Duration
d Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2 Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
3
measureWidthFromTimeSig :: Int -> Measure -> Int
measureWidthFromTimeSig :: Int -> Measure -> Int
measureWidthFromTimeSig Int
zoom Measure
m =
let TimeSignature Int
num Int
_ = Measure -> TimeSignature
timeSignature Measure
m
in Int
num Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
zoom Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
findVisibleRange :: Track -> [Measure] -> Int -> Int -> Int -> [(MeasureIndex, [Beat], Measure)]
findVisibleRange :: Track
-> [Measure]
-> Int
-> Int
-> Int
-> [(MeasureIndex, [Beat], Measure)]
findVisibleRange Track
track [Measure]
measures Int
zoom Int
availW Int
cursorM =
let candidates :: [(Int, [(MeasureIndex, [Beat], Measure)])]
candidates =
[ (Int
startM, [(MeasureIndex, [Beat], Measure)]
fitted)
| Int
startM <- [Int
0 .. Int
cursorM]
, let fitted :: [(MeasureIndex, [Beat], Measure)]
fitted = Track
-> [Measure]
-> Int
-> Int
-> Int
-> [(MeasureIndex, [Beat], Measure)]
fitMeasures Track
track [Measure]
measures Int
zoom Int
availW Int
startM
, ((MeasureIndex, [Beat], Measure) -> Bool)
-> [(MeasureIndex, [Beat], Measure)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (\(MeasureIndex Int
mi, [Beat]
_, Measure
_) -> Int
mi Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
cursorM) [(MeasureIndex, [Beat], Measure)]
fitted
]
in case [(Int, [(MeasureIndex, [Beat], Measure)])]
candidates of
[] -> Track
-> [Measure]
-> Int
-> Int
-> Int
-> [(MeasureIndex, [Beat], Measure)]
fitMeasures Track
track [Measure]
measures Int
zoom Int
availW Int
cursorM
((Int
_, [(MeasureIndex, [Beat], Measure)]
fitted) : [(Int, [(MeasureIndex, [Beat], Measure)])]
_) -> [(MeasureIndex, [Beat], Measure)]
fitted
fitMeasures :: Track -> [Measure] -> Int -> Int -> Int -> [(MeasureIndex, [Beat], Measure)]
fitMeasures :: Track
-> [Measure]
-> Int
-> Int
-> Int
-> [(MeasureIndex, [Beat], Measure)]
fitMeasures Track
track [Measure]
measures Int
zoom Int
availW Int
startM = Int -> Int -> [(MeasureIndex, [Beat], Measure)]
go Int
startM Int
availW
where
totalM :: Int
totalM = [Measure] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Measure]
measures
go :: Int -> Int -> [(MeasureIndex, [Beat], Measure)]
go Int
mi Int
remaining
| Int
mi Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
totalM = []
| Bool
otherwise =
let measure :: Measure
measure = [Measure]
measures [Measure] -> Int -> Measure
forall a. HasCallStack => [a] -> Int -> a
!! Int
mi
beats :: [Beat]
beats = Track -> MeasureIndex -> [Beat]
beatsForTrackMeasure Track
track (Int -> MeasureIndex
MeasureIndex Int
mi)
mWidth :: Int
mWidth = Int -> Measure -> Int
measureWidthFromTimeSig Int
zoom Measure
measure
in if Int
mWidth Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
remaining Bool -> Bool -> Bool
&& Int
mi Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
startM
then []
else (Int -> MeasureIndex
MeasureIndex Int
mi, [Beat]
beats, Measure
measure) (MeasureIndex, [Beat], Measure)
-> [(MeasureIndex, [Beat], Measure)]
-> [(MeasureIndex, [Beat], Measure)]
forall a. a -> [a] -> [a]
: Int -> Int -> [(MeasureIndex, [Beat], Measure)]
go (Int
mi Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Int
remaining Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
mWidth)
renderStringLine ::
AppState ->
Bool ->
Int ->
Int ->
[(MeasureIndex, [Beat], Measure)] ->
StringIndex ->
String ->
Widget ResourceName
renderStringLine :: AppState
-> Bool
-> Int
-> Int
-> [(MeasureIndex, [Beat], Measure)]
-> StringIndex
-> String
-> Widget ResourceName
renderStringLine AppState
st Bool
isFocused Int
_labelWidth Int
zoom [(MeasureIndex, [Beat], Measure)]
visibleMeasures StringIndex
sIdx String
label =
[Widget ResourceName] -> Widget ResourceName
forall n. [Widget n] -> Widget n
hBox ([Widget ResourceName] -> Widget ResourceName)
-> [Widget ResourceName] -> Widget ResourceName
forall a b. (a -> b) -> a -> b
$
[AttrName -> Widget ResourceName -> Widget ResourceName
forall n. AttrName -> Widget n -> Widget n
withAttr AttrName
stringLabelAttr (String -> Widget ResourceName
forall n. String -> Widget n
str String
label)]
[Widget ResourceName]
-> [Widget ResourceName] -> [Widget ResourceName]
forall a. [a] -> [a] -> [a]
++ ((MeasureIndex, [Beat], Measure) -> [Widget ResourceName])
-> [(MeasureIndex, [Beat], Measure)] -> [Widget ResourceName]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (AppState
-> Bool
-> Int
-> StringIndex
-> (MeasureIndex, [Beat], Measure)
-> [Widget ResourceName]
renderMeasureOnString AppState
st Bool
isFocused Int
zoom StringIndex
sIdx) [(MeasureIndex, [Beat], Measure)]
visibleMeasures
renderMeasureOnString ::
AppState ->
Bool ->
Int ->
StringIndex ->
(MeasureIndex, [Beat], Measure) ->
[Widget ResourceName]
renderMeasureOnString :: AppState
-> Bool
-> Int
-> StringIndex
-> (MeasureIndex, [Beat], Measure)
-> [Widget ResourceName]
renderMeasureOnString AppState
st Bool
isFocused Int
zoom StringIndex
sIdx (MeasureIndex
mi, [Beat]
beats, Measure
measure) =
let TimeSignature Int
num Int
_ = Measure -> TimeSignature
timeSignature Measure
measure
totalCols :: Int
totalCols = Int
num Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
zoom
usedCols :: Int
usedCols = [Int] -> Int
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Beat -> Int) -> [Beat] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Duration -> Int
beatColumnWidth Int
zoom (Duration -> Int) -> (Beat -> Duration) -> Beat -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Beat -> Duration
beatDuration) [Beat]
beats)
remainingCols :: Int
remainingCols = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int
totalCols Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
usedCols)
emptySlotWidth :: Int
emptySlotWidth = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
3 Int
zoom
emptySlotCount :: Int
emptySlotCount = if Int
emptySlotWidth Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 then Int
remainingCols Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
emptySlotWidth else Int
0
beatWidgets :: [Widget ResourceName]
beatWidgets =
[ AppState
-> Bool
-> Int
-> StringIndex
-> MeasureIndex
-> BeatIndex
-> [Beat]
-> Widget ResourceName
renderBeatCell AppState
st Bool
isFocused Int
zoom StringIndex
sIdx MeasureIndex
mi (Int -> BeatIndex
BeatIndex Int
bi) [Beat]
beats
| Int
bi <- [Int
0 .. [Beat] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Beat]
beats Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]
]
emptyWidgets :: [Widget ResourceName]
emptyWidgets =
[ AppState
-> Bool
-> StringIndex
-> MeasureIndex
-> BeatIndex
-> Int
-> Widget ResourceName
renderEmptyCell AppState
st Bool
isFocused StringIndex
sIdx MeasureIndex
mi (Int -> BeatIndex
BeatIndex Int
bi) Int
emptySlotWidth
| Int
bi <- [[Beat] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Beat]
beats .. [Beat] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Beat]
beats Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
emptySlotCount Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]
]
barLine :: Widget n
barLine = AttrName -> Widget n -> Widget n
forall n. AttrName -> Widget n -> Widget n
withAttr AttrName
barLineAttr (String -> Widget n
forall n. String -> Widget n
str String
bar)
in [Widget ResourceName]
beatWidgets [Widget ResourceName]
-> [Widget ResourceName] -> [Widget ResourceName]
forall a. [a] -> [a] -> [a]
++ [Widget ResourceName]
emptyWidgets [Widget ResourceName]
-> [Widget ResourceName] -> [Widget ResourceName]
forall a. [a] -> [a] -> [a]
++ [Widget ResourceName
forall {n}. Widget n
barLine]
renderEmptyCell ::
AppState ->
Bool ->
StringIndex ->
MeasureIndex ->
BeatIndex ->
Int ->
Widget ResourceName
renderEmptyCell :: AppState
-> Bool
-> StringIndex
-> MeasureIndex
-> BeatIndex
-> Int
-> Widget ResourceName
renderEmptyCell AppState
st Bool
isFocused StringIndex
sIdx MeasureIndex
mi BeatIndex
bi Int
colWidth =
let cell :: String
cell = Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 Int
colWidth) Char
line
isCursor :: Bool
isCursor =
Bool
isFocused
Bool -> Bool -> Bool
&& MeasureIndex
mi MeasureIndex -> MeasureIndex -> Bool
forall a. Eq a => a -> a -> Bool
== AppState -> MeasureIndex
asCurrentMeasure AppState
st
Bool -> Bool -> Bool
&& BeatIndex
bi BeatIndex -> BeatIndex -> Bool
forall a. Eq a => a -> a -> Bool
== AppState -> BeatIndex
asCurrentBeat AppState
st
Bool -> Bool -> Bool
&& StringIndex
sIdx StringIndex -> StringIndex -> Bool
forall a. Eq a => a -> a -> Bool
== AppState -> StringIndex
asCurrentString AppState
st
attr :: AttrName
attr
| Bool
isCursor = AttrName
cursorAttr
| Bool
otherwise = AttrName
forall a. Monoid a => a
mempty
in AttrName -> Widget ResourceName -> Widget ResourceName
forall n. AttrName -> Widget n -> Widget n
withAttr AttrName
attr (String -> Widget ResourceName
forall n. String -> Widget n
str String
cell)
renderBeatCell ::
AppState ->
Bool ->
Int ->
StringIndex ->
MeasureIndex ->
BeatIndex ->
[Beat] ->
Widget ResourceName
renderBeatCell :: AppState
-> Bool
-> Int
-> StringIndex
-> MeasureIndex
-> BeatIndex
-> [Beat]
-> Widget ResourceName
renderBeatCell AppState
st Bool
isFocused Int
zoom StringIndex
sIdx MeasureIndex
mi BeatIndex
bi [Beat]
beats =
let BeatIndex Int
bIdx = BeatIndex
bi
mBeat :: Maybe Beat
mBeat = case Int -> [Beat] -> [Beat]
forall a. Int -> [a] -> [a]
drop Int
bIdx [Beat]
beats of
(Beat
beat : [Beat]
_) -> Beat -> Maybe Beat
forall a. a -> Maybe a
Just Beat
beat
[] -> Maybe Beat
forall a. Maybe a
Nothing
colWidth :: Int
colWidth = case Maybe Beat
mBeat of
Just Beat
beat -> Int -> Duration -> Int
beatColumnWidth Int
zoom (Beat -> Duration
beatDuration Beat
beat)
Maybe Beat
Nothing -> Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
3 Int
zoom
fretText :: Maybe Int
fretText = case Maybe Beat
mBeat of
Just Beat
beat -> StringIndex -> Beat -> Maybe Int
fretOnString StringIndex
sIdx Beat
beat
Maybe Beat
Nothing -> Maybe Int
forall a. Maybe a
Nothing
cell :: String
cell = case Maybe Int
fretText of
Just Int
f -> Int -> String -> String
fretOnLine Int
colWidth (Int -> String
forall a. Show a => a -> String
show Int
f)
Maybe Int
Nothing -> Int -> Char -> String
forall a. Int -> a -> [a]
replicate Int
colWidth Char
line
isCursor :: Bool
isCursor =
Bool
isFocused
Bool -> Bool -> Bool
&& MeasureIndex
mi MeasureIndex -> MeasureIndex -> Bool
forall a. Eq a => a -> a -> Bool
== AppState -> MeasureIndex
asCurrentMeasure AppState
st
Bool -> Bool -> Bool
&& BeatIndex
bi BeatIndex -> BeatIndex -> Bool
forall a. Eq a => a -> a -> Bool
== AppState -> BeatIndex
asCurrentBeat AppState
st
Bool -> Bool -> Bool
&& StringIndex
sIdx StringIndex -> StringIndex -> Bool
forall a. Eq a => a -> a -> Bool
== AppState -> StringIndex
asCurrentString AppState
st
isPlayhead :: Bool
isPlayhead =
AppState -> Maybe MeasureIndex
asPlayheadMeasure AppState
st Maybe MeasureIndex -> Maybe MeasureIndex -> Bool
forall a. Eq a => a -> a -> Bool
== MeasureIndex -> Maybe MeasureIndex
forall a. a -> Maybe a
Just MeasureIndex
mi
Bool -> Bool -> Bool
&& AppState -> Maybe BeatIndex
asPlayheadBeat AppState
st Maybe BeatIndex -> Maybe BeatIndex -> Bool
forall a. Eq a => a -> a -> Bool
== BeatIndex -> Maybe BeatIndex
forall a. a -> Maybe a
Just BeatIndex
bi
isSelected :: Bool
isSelected = case AppState -> Maybe (MeasureIndex, BeatIndex)
asSelectionStart AppState
st of
Just (MeasureIndex
selMi, BeatIndex
selBi) ->
MeasureIndex
mi MeasureIndex -> MeasureIndex -> Bool
forall a. Eq a => a -> a -> Bool
== MeasureIndex
selMi
Bool -> Bool -> Bool
&& MeasureIndex
mi MeasureIndex -> MeasureIndex -> Bool
forall a. Eq a => a -> a -> Bool
== AppState -> MeasureIndex
asCurrentMeasure AppState
st
Bool -> Bool -> Bool
&& let BeatIndex Int
s = BeatIndex -> BeatIndex -> BeatIndex
forall a. Ord a => a -> a -> a
min BeatIndex
selBi (AppState -> BeatIndex
asCurrentBeat AppState
st)
BeatIndex Int
e = BeatIndex -> BeatIndex -> BeatIndex
forall a. Ord a => a -> a -> a
max BeatIndex
selBi (AppState -> BeatIndex
asCurrentBeat AppState
st)
BeatIndex Int
b = BeatIndex
bi
in Int
b Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
s Bool -> Bool -> Bool
&& Int
b Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
e
Maybe (MeasureIndex, BeatIndex)
Nothing -> Bool
False
attr :: AttrName
attr
| Bool
isCursor = AttrName
cursorAttr
| Bool
isSelected = AttrName
selectionAttr
| Bool
isPlayhead = AttrName
playheadAttr
| Bool
otherwise = AttrName
forall a. Monoid a => a
mempty
in AttrName -> Widget ResourceName -> Widget ResourceName
forall n. AttrName -> Widget n -> Widget n
withAttr AttrName
attr (String -> Widget ResourceName
forall n. String -> Widget n
str String
cell)
fretOnLine :: Int -> String -> String
fretOnLine :: Int -> String -> String
fretOnLine Int
w String
s
| Int
len Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
w = Int -> String -> String
forall a. Int -> [a] -> [a]
take Int
w String
s
| Bool
otherwise =
let remaining :: Int
remaining = Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
len
padLeft :: Int
padLeft = Int
remaining Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2
padRight :: Int
padRight = Int
remaining Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
padLeft
in Int -> Char -> String
forall a. Int -> a -> [a]
replicate Int
padLeft Char
line String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
s String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> Char -> String
forall a. Int -> a -> [a]
replicate Int
padRight Char
line
where
len :: Int
len = String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
s
fretOnString :: StringIndex -> Beat -> Maybe Int
fretOnString :: StringIndex -> Beat -> Maybe Int
fretOnString StringIndex
sIdx Beat
beat =
case (Note -> Bool) -> [Note] -> [Note]
forall a. (a -> Bool) -> [a] -> [a]
filter (StringIndex -> Note -> Bool
matchesString StringIndex
sIdx) (Beat -> [Note]
beatNotes Beat
beat) of
(Note
n : [Note]
_) -> case Note -> Maybe FretNumber
noteFret Note
n of
Just (FretNumber Int
f) -> Int -> Maybe Int
forall a. a -> Maybe a
Just Int
f
Maybe FretNumber
Nothing -> Maybe Int
forall a. Maybe a
Nothing
[] -> Maybe Int
forall a. Maybe a
Nothing
matchesString :: StringIndex -> Note -> Bool
matchesString :: StringIndex -> Note -> Bool
matchesString StringIndex
sIdx Note
n = Note -> Maybe StringIndex
noteString Note
n Maybe StringIndex -> Maybe StringIndex -> Bool
forall a. Eq a => a -> a -> Bool
== StringIndex -> Maybe StringIndex
forall a. a -> Maybe a
Just StringIndex
sIdx