module Termtab.UI.Types (
AppState (..),
DisplayMode (..),
InputMode (..),
ResourceName (..),
AppEvent (..),
initAppState,
moveBeatRight,
moveBeatLeft,
moveMeasureForward,
moveMeasureBack,
moveStringUp,
moveStringDown,
goToStart,
goToEnd,
goToMeasureStart,
goToMeasureEnd,
goToFirstString,
goToLastString,
cycleDisplayMode,
zoomIn,
zoomOut,
currentTrack,
currentMeasure,
currentMeasureBeatCount,
atLastBeat,
atLastMeasure,
atFirstBeat,
atFirstMeasure,
isMeasureEmpty,
isTrailingEmpty,
beatsForTrackMeasure,
stringCountForTrack,
measureCount,
) where
import Brick.BChan (BChan)
import Codec.Picture (PixelRGBA8)
import Data.Map.Strict qualified as Map
import Termtab.Audio.PlaybackThread (PlaybackEnv)
import Termtab.Audio.Types (PlaybackStatus (..))
import Termtab.Graphics.Detect (GraphicsProtocol (..))
import Termtab.Graphics.Font (GlyphFont)
import Termtab.Graphics.TermColor (defaultInk)
import Termtab.Types
data DisplayMode
= TabOnly
| NotationOnly
| TabAndNotation
deriving (Int -> DisplayMode -> ShowS
[DisplayMode] -> ShowS
DisplayMode -> String
(Int -> DisplayMode -> ShowS)
-> (DisplayMode -> String)
-> ([DisplayMode] -> ShowS)
-> Show DisplayMode
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DisplayMode -> ShowS
showsPrec :: Int -> DisplayMode -> ShowS
$cshow :: DisplayMode -> String
show :: DisplayMode -> String
$cshowList :: [DisplayMode] -> ShowS
showList :: [DisplayMode] -> ShowS
Show, DisplayMode -> DisplayMode -> Bool
(DisplayMode -> DisplayMode -> Bool)
-> (DisplayMode -> DisplayMode -> Bool) -> Eq DisplayMode
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: DisplayMode -> DisplayMode -> Bool
== :: DisplayMode -> DisplayMode -> Bool
$c/= :: DisplayMode -> DisplayMode -> Bool
/= :: DisplayMode -> DisplayMode -> Bool
Eq)
data InputMode
= NormalMode
| CommandMode String
| GoToMode
| VisualMode
| [String]
deriving (Int -> InputMode -> ShowS
[InputMode] -> ShowS
InputMode -> String
(Int -> InputMode -> ShowS)
-> (InputMode -> String)
-> ([InputMode] -> ShowS)
-> Show InputMode
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> InputMode -> ShowS
showsPrec :: Int -> InputMode -> ShowS
$cshow :: InputMode -> String
show :: InputMode -> String
$cshowList :: [InputMode] -> ShowS
showList :: [InputMode] -> ShowS
Show, InputMode -> InputMode -> Bool
(InputMode -> InputMode -> Bool)
-> (InputMode -> InputMode -> Bool) -> Eq InputMode
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: InputMode -> InputMode -> Bool
== :: InputMode -> InputMode -> Bool
$c/= :: InputMode -> InputMode -> Bool
/= :: InputMode -> InputMode -> Bool
Eq)
data ResourceName
= TrackViewport TrackIndex
| StatusBarResource
| CommandLineResource
|
NotationRegion TrackIndex
deriving (Int -> ResourceName -> ShowS
[ResourceName] -> ShowS
ResourceName -> String
(Int -> ResourceName -> ShowS)
-> (ResourceName -> String)
-> ([ResourceName] -> ShowS)
-> Show ResourceName
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ResourceName -> ShowS
showsPrec :: Int -> ResourceName -> ShowS
$cshow :: ResourceName -> String
show :: ResourceName -> String
$cshowList :: [ResourceName] -> ShowS
showList :: [ResourceName] -> ShowS
Show, ResourceName -> ResourceName -> Bool
(ResourceName -> ResourceName -> Bool)
-> (ResourceName -> ResourceName -> Bool) -> Eq ResourceName
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ResourceName -> ResourceName -> Bool
== :: ResourceName -> ResourceName -> Bool
$c/= :: ResourceName -> ResourceName -> Bool
/= :: ResourceName -> ResourceName -> Bool
Eq, Eq ResourceName
Eq ResourceName =>
(ResourceName -> ResourceName -> Ordering)
-> (ResourceName -> ResourceName -> Bool)
-> (ResourceName -> ResourceName -> Bool)
-> (ResourceName -> ResourceName -> Bool)
-> (ResourceName -> ResourceName -> Bool)
-> (ResourceName -> ResourceName -> ResourceName)
-> (ResourceName -> ResourceName -> ResourceName)
-> Ord ResourceName
ResourceName -> ResourceName -> Bool
ResourceName -> ResourceName -> Ordering
ResourceName -> ResourceName -> ResourceName
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: ResourceName -> ResourceName -> Ordering
compare :: ResourceName -> ResourceName -> Ordering
$c< :: ResourceName -> ResourceName -> Bool
< :: ResourceName -> ResourceName -> Bool
$c<= :: ResourceName -> ResourceName -> Bool
<= :: ResourceName -> ResourceName -> Bool
$c> :: ResourceName -> ResourceName -> Bool
> :: ResourceName -> ResourceName -> Bool
$c>= :: ResourceName -> ResourceName -> Bool
>= :: ResourceName -> ResourceName -> Bool
$cmax :: ResourceName -> ResourceName -> ResourceName
max :: ResourceName -> ResourceName -> ResourceName
$cmin :: ResourceName -> ResourceName -> ResourceName
min :: ResourceName -> ResourceName -> ResourceName
Ord)
data AppEvent
= PlaybackTick
|
GraphicsRefresh
deriving (Int -> AppEvent -> ShowS
[AppEvent] -> ShowS
AppEvent -> String
(Int -> AppEvent -> ShowS)
-> (AppEvent -> String) -> ([AppEvent] -> ShowS) -> Show AppEvent
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> AppEvent -> ShowS
showsPrec :: Int -> AppEvent -> ShowS
$cshow :: AppEvent -> String
show :: AppEvent -> String
$cshowList :: [AppEvent] -> ShowS
showList :: [AppEvent] -> ShowS
Show, AppEvent -> AppEvent -> Bool
(AppEvent -> AppEvent -> Bool)
-> (AppEvent -> AppEvent -> Bool) -> Eq AppEvent
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: AppEvent -> AppEvent -> Bool
== :: AppEvent -> AppEvent -> Bool
$c/= :: AppEvent -> AppEvent -> Bool
/= :: AppEvent -> AppEvent -> Bool
Eq)
data AppState = AppState
{ AppState -> Song
asSong :: Song
, AppState -> Maybe String
asFilePath :: Maybe FilePath
, AppState -> MeasureIndex
asCurrentMeasure :: MeasureIndex
, AppState -> BeatIndex
asCurrentBeat :: BeatIndex
, AppState -> TrackIndex
asCurrentTrack :: TrackIndex
, AppState -> StringIndex
asCurrentString :: StringIndex
, AppState -> InputMode
asInputMode :: InputMode
, AppState -> Map TrackIndex DisplayMode
asDisplayModes :: Map.Map TrackIndex DisplayMode
, AppState -> Int
asZoom :: Int
, AppState -> Maybe String
asMessage :: Maybe String
, AppState -> PlaybackStatus
asPlaybackStatus :: PlaybackStatus
, AppState -> Maybe MeasureIndex
asPlayheadMeasure :: Maybe MeasureIndex
, AppState -> Maybe BeatIndex
asPlayheadBeat :: Maybe BeatIndex
, AppState -> Maybe PlaybackEnv
asPlaybackEnv :: Maybe PlaybackEnv
, AppState -> BChan AppEvent
asBChan :: BChan AppEvent
, AppState -> [Song]
asUndoStack :: [Song]
, AppState -> [Song]
asRedoStack :: [Song]
, AppState -> Maybe FretNumber
asFretEntry :: Maybe FretNumber
, AppState -> Maybe (MeasureIndex, BeatIndex)
asSelectionStart :: Maybe (MeasureIndex, BeatIndex)
,
AppState -> GraphicsProtocol
asProtocol :: GraphicsProtocol
, AppState -> Maybe GlyphFont
asGlyphFont :: Maybe GlyphFont
, AppState -> PixelRGBA8
asInkColor :: PixelRGBA8
,
AppState -> Map TrackIndex String
asNotationSigs :: Map.Map TrackIndex String
}
initAppState :: Maybe FilePath -> Song -> BChan AppEvent -> AppState
initAppState :: Maybe String -> Song -> BChan AppEvent -> AppState
initAppState Maybe String
mPath Song
song BChan AppEvent
bChan =
AppState
{ asSong :: Song
asSong = Song
song
, asFilePath :: Maybe String
asFilePath = Maybe String
mPath
, asCurrentMeasure :: MeasureIndex
asCurrentMeasure = Int -> MeasureIndex
MeasureIndex Int
0
, asCurrentBeat :: BeatIndex
asCurrentBeat = Int -> BeatIndex
BeatIndex Int
0
, asCurrentTrack :: TrackIndex
asCurrentTrack = Int -> TrackIndex
TrackIndex Int
0
, asCurrentString :: StringIndex
asCurrentString = Int -> StringIndex
StringIndex Int
0
, asInputMode :: InputMode
asInputMode = InputMode
NormalMode
, asDisplayModes :: Map TrackIndex DisplayMode
asDisplayModes = [(TrackIndex, DisplayMode)] -> Map TrackIndex DisplayMode
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(TrackIndex, DisplayMode)]
defaultModes
, asZoom :: Int
asZoom = Int
4
, asMessage :: Maybe String
asMessage = Maybe String
forall a. Maybe a
Nothing
, asPlaybackStatus :: PlaybackStatus
asPlaybackStatus = PlaybackStatus
Stopped
, asPlayheadMeasure :: Maybe MeasureIndex
asPlayheadMeasure = Maybe MeasureIndex
forall a. Maybe a
Nothing
, asPlayheadBeat :: Maybe BeatIndex
asPlayheadBeat = Maybe BeatIndex
forall a. Maybe a
Nothing
, asPlaybackEnv :: Maybe PlaybackEnv
asPlaybackEnv = Maybe PlaybackEnv
forall a. Maybe a
Nothing
, asBChan :: BChan AppEvent
asBChan = BChan AppEvent
bChan
, asUndoStack :: [Song]
asUndoStack = []
, asRedoStack :: [Song]
asRedoStack = []
, asFretEntry :: Maybe FretNumber
asFretEntry = Maybe FretNumber
forall a. Maybe a
Nothing
, asSelectionStart :: Maybe (MeasureIndex, BeatIndex)
asSelectionStart = Maybe (MeasureIndex, BeatIndex)
forall a. Maybe a
Nothing
, asProtocol :: GraphicsProtocol
asProtocol = GraphicsProtocol
TextOnly
, asGlyphFont :: Maybe GlyphFont
asGlyphFont = Maybe GlyphFont
forall a. Maybe a
Nothing
, asInkColor :: PixelRGBA8
asInkColor = PixelRGBA8
defaultInk
, asNotationSigs :: Map TrackIndex String
asNotationSigs = Map TrackIndex String
forall k a. Map k a
Map.empty
}
where
defaultModes :: [(TrackIndex, DisplayMode)]
defaultModes =
[ (Int -> TrackIndex
TrackIndex Int
i, Instrument -> DisplayMode
defaultModeFor (Track -> Instrument
trackInstrument Track
t))
| (Int
i, Track
t) <- [Int] -> [Track] -> [(Int, Track)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
0 ..] (Song -> [Track]
songTracks Song
song)
]
defaultModeFor :: Instrument -> DisplayMode
defaultModeFor (Guitar [Pitch]
_ Int
_) = DisplayMode
TabAndNotation
defaultModeFor (Bass [Pitch]
_ Int
_) = DisplayMode
TabAndNotation
defaultModeFor (Standard MidiProgram
_) = DisplayMode
NotationOnly
currentTrack :: AppState -> Maybe Track
currentTrack :: AppState -> Maybe Track
currentTrack AppState
st =
let TrackIndex Int
i = AppState -> TrackIndex
asCurrentTrack AppState
st
in case Int -> [Track] -> [Track]
forall a. Int -> [a] -> [a]
drop Int
i (Song -> [Track]
songTracks (AppState -> Song
asSong AppState
st)) of
(Track
t : [Track]
_) -> Track -> Maybe Track
forall a. a -> Maybe a
Just Track
t
[] -> Maybe Track
forall a. Maybe a
Nothing
currentMeasure :: AppState -> Maybe Measure
currentMeasure :: AppState -> Maybe Measure
currentMeasure AppState
st =
let MeasureIndex Int
i = AppState -> MeasureIndex
asCurrentMeasure AppState
st
in case Int -> [Measure] -> [Measure]
forall a. Int -> [a] -> [a]
drop Int
i (Song -> [Measure]
songMeasures (AppState -> Song
asSong AppState
st)) of
(Measure
m : [Measure]
_) -> Measure -> Maybe Measure
forall a. a -> Maybe a
Just Measure
m
[] -> Maybe Measure
forall a. Maybe a
Nothing
beatsForTrackMeasure :: Track -> MeasureIndex -> [Beat]
beatsForTrackMeasure :: Track -> MeasureIndex -> [Beat]
beatsForTrackMeasure Track
track MeasureIndex
mi =
[Beat] -> ([Beat] -> [Beat]) -> Maybe [Beat] -> [Beat]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] [Beat] -> [Beat]
forall a. a -> a
id (MeasureIndex -> Map MeasureIndex [Beat] -> Maybe [Beat]
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup MeasureIndex
mi (Track -> Map MeasureIndex [Beat]
trackBeats Track
track))
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
0
measureCount :: AppState -> Int
measureCount :: AppState -> Int
measureCount AppState
st = [Measure] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (Song -> [Measure]
songMeasures (AppState -> Song
asSong AppState
st))
isMeasureEmpty :: MeasureIndex -> AppState -> Bool
isMeasureEmpty :: MeasureIndex -> AppState -> Bool
isMeasureEmpty MeasureIndex
mi AppState
st =
(Track -> Bool) -> [Track] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Track -> Bool
trackEmpty (Song -> [Track]
songTracks (AppState -> Song
asSong AppState
st))
where
trackEmpty :: Track -> Bool
trackEmpty Track
track =
let beats :: [Beat]
beats = Track -> MeasureIndex -> [Beat]
beatsForTrackMeasure Track
track MeasureIndex
mi
in (Beat -> Bool) -> [Beat] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Beat -> Bool
beatEmpty [Beat]
beats
beatEmpty :: Beat -> Bool
beatEmpty Beat
beat = [Note] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (Beat -> [Note]
beatNotes Beat
beat)
isTrailingEmpty :: MeasureIndex -> AppState -> Bool
isTrailingEmpty :: MeasureIndex -> AppState -> Bool
isTrailingEmpty (MeasureIndex Int
mi) AppState
st =
let total :: Int
total = AppState -> Int
measureCount AppState
st
in (Int -> Bool) -> [Int] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (\Int
i -> MeasureIndex -> AppState -> Bool
isMeasureEmpty (Int -> MeasureIndex
MeasureIndex Int
i) AppState
st) [Int
mi .. Int
total Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]
currentMeasureBeatCount :: AppState -> Int
currentMeasureBeatCount :: AppState -> Int
currentMeasureBeatCount AppState
st =
let timeSigBeats :: Int
timeSigBeats = case AppState -> Maybe Measure
currentMeasure AppState
st of
Just Measure
m -> let TimeSignature Int
num Int
_ = Measure -> TimeSignature
timeSignature Measure
m in Int
num
Maybe Measure
Nothing -> Int
4
beats :: [Beat]
beats = case AppState -> Maybe Track
currentTrack AppState
st of
Just Track
t -> Track -> MeasureIndex -> [Beat]
beatsForTrackMeasure Track
t (AppState -> MeasureIndex
asCurrentMeasure AppState
st)
Maybe Track
Nothing -> []
actualCount :: Int
actualCount = [Beat] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Beat]
beats
usedUnits :: Int
usedUnits = [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 (Duration -> Int
durationWeightForNav (Duration -> Int) -> (Beat -> Duration) -> Beat -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Beat -> Duration
beatDuration) [Beat]
beats)
totalUnits :: Int
totalUnits = Int
timeSigBeats Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
4
remainingUnits :: Int
remainingUnits = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int
totalUnits Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
usedUnits)
emptySlots :: Int
emptySlots = Int
remainingUnits Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
4
in Int
actualCount Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
emptySlots
durationWeightForNav :: Duration -> Int
durationWeightForNav :: Duration -> Int
durationWeightForNav Duration
Whole = Int
16
durationWeightForNav Duration
Half = Int
8
durationWeightForNav Duration
Quarter = Int
4
durationWeightForNav Duration
Eighth = Int
2
durationWeightForNav Duration
Sixteenth = Int
1
durationWeightForNav Duration
Thirty2nd = Int
1
durationWeightForNav (Dotted Duration
d) = Duration -> Int
durationWeightForNav Duration
d Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Duration -> Int
durationWeightForNav Duration
d Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2
durationWeightForNav (Triplet Duration
d) = Duration -> Int
durationWeightForNav 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
atLastBeat :: AppState -> Bool
atLastBeat :: AppState -> Bool
atLastBeat AppState
st =
let BeatIndex Int
b = AppState -> BeatIndex
asCurrentBeat AppState
st
in Int
b Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= AppState -> Int
currentMeasureBeatCount AppState
st Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
atLastMeasure :: AppState -> Bool
atLastMeasure :: AppState -> Bool
atLastMeasure AppState
st =
let MeasureIndex Int
m = AppState -> MeasureIndex
asCurrentMeasure AppState
st
in Int
m Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= AppState -> Int
measureCount AppState
st Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
moveBeatRight :: AppState -> AppState
moveBeatRight :: AppState -> AppState
moveBeatRight AppState
st
| AppState -> Bool
atLastBeat AppState
st Bool -> Bool -> Bool
&& Bool -> Bool
not (AppState -> Bool
atLastMeasure AppState
st) =
AppState
st{asCurrentMeasure = let MeasureIndex m = asCurrentMeasure st in MeasureIndex (m + 1), asCurrentBeat = BeatIndex 0}
| AppState -> Bool
atLastBeat AppState
st = AppState
st
| Bool
otherwise =
let BeatIndex Int
b = AppState -> BeatIndex
asCurrentBeat AppState
st
in AppState
st{asCurrentBeat = BeatIndex (b + 1)}
atFirstBeat :: AppState -> Bool
atFirstBeat :: AppState -> Bool
atFirstBeat AppState
st = AppState -> BeatIndex
asCurrentBeat AppState
st BeatIndex -> BeatIndex -> Bool
forall a. Eq a => a -> a -> Bool
== Int -> BeatIndex
BeatIndex Int
0
atFirstMeasure :: AppState -> Bool
atFirstMeasure :: AppState -> Bool
atFirstMeasure AppState
st = AppState -> MeasureIndex
asCurrentMeasure AppState
st MeasureIndex -> MeasureIndex -> Bool
forall a. Eq a => a -> a -> Bool
== Int -> MeasureIndex
MeasureIndex Int
0
moveBeatLeft :: AppState -> AppState
moveBeatLeft :: AppState -> AppState
moveBeatLeft AppState
st
| AppState -> Bool
atFirstBeat AppState
st Bool -> Bool -> Bool
&& Bool -> Bool
not (AppState -> Bool
atFirstMeasure AppState
st) =
let MeasureIndex Int
m = AppState -> MeasureIndex
asCurrentMeasure AppState
st
prevM :: MeasureIndex
prevM = Int -> MeasureIndex
MeasureIndex (Int
m Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
prevSt :: AppState
prevSt = AppState
st{asCurrentMeasure = prevM}
prevBeatCount :: Int
prevBeatCount = AppState -> Int
currentMeasureBeatCount AppState
prevSt
in AppState
st{asCurrentMeasure = prevM, asCurrentBeat = BeatIndex (prevBeatCount - 1)}
| AppState -> Bool
atFirstBeat AppState
st = AppState
st
| Bool
otherwise =
let BeatIndex Int
b = AppState -> BeatIndex
asCurrentBeat AppState
st
in AppState
st{asCurrentBeat = BeatIndex (b - 1)}
moveMeasureForward :: AppState -> AppState
moveMeasureForward :: AppState -> AppState
moveMeasureForward AppState
st =
let MeasureIndex Int
m = AppState -> MeasureIndex
asCurrentMeasure AppState
st
maxM :: Int
maxM = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (AppState -> Int
measureCount AppState
st Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
in AppState
st
{ asCurrentMeasure = MeasureIndex (min (m + 1) maxM)
, asCurrentBeat = BeatIndex 0
}
moveMeasureBack :: AppState -> AppState
moveMeasureBack :: AppState -> AppState
moveMeasureBack AppState
st =
let MeasureIndex Int
m = AppState -> MeasureIndex
asCurrentMeasure AppState
st
in AppState
st
{ asCurrentMeasure = MeasureIndex (max 0 (m - 1))
, asCurrentBeat = BeatIndex 0
}
moveStringUp :: AppState -> AppState
moveStringUp :: AppState -> AppState
moveStringUp AppState
st =
let StringIndex Int
s = AppState -> StringIndex
asCurrentString AppState
st
in AppState
st{asCurrentString = StringIndex (max 0 (s - 1))}
moveStringDown :: AppState -> AppState
moveStringDown :: AppState -> AppState
moveStringDown AppState
st =
let StringIndex Int
s = AppState -> StringIndex
asCurrentString AppState
st
maxS :: Int
maxS = case AppState -> Maybe Track
currentTrack AppState
st of
Maybe Track
Nothing -> Int
0
Just Track
t -> Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Track -> Int
stringCountForTrack Track
t Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
in AppState
st{asCurrentString = StringIndex (min (s + 1) maxS)}
goToStart :: AppState -> AppState
goToStart :: AppState -> AppState
goToStart AppState
st =
AppState
st
{ asCurrentMeasure = MeasureIndex 0
, asCurrentBeat = BeatIndex 0
}
goToEnd :: AppState -> AppState
goToEnd :: AppState -> AppState
goToEnd AppState
st =
let lastM :: Int
lastM = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (AppState -> Int
measureCount AppState
st Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
measures :: [Measure]
measures = Song -> [Measure]
songMeasures (AppState -> Song
asSong AppState
st)
lastBeat :: Int
lastBeat = case Int -> [Measure] -> [Measure]
forall a. Int -> [a] -> [a]
drop Int
lastM [Measure]
measures of
(Measure
m : [Measure]
_) -> let TimeSignature Int
num Int
_ = Measure -> TimeSignature
timeSignature Measure
m in Int
num Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
[] -> Int
0
in AppState
st
{ asCurrentMeasure = MeasureIndex lastM
, asCurrentBeat = BeatIndex lastBeat
}
goToMeasureStart :: AppState -> AppState
goToMeasureStart :: AppState -> AppState
goToMeasureStart AppState
st = AppState
st{asCurrentBeat = BeatIndex 0}
goToMeasureEnd :: AppState -> AppState
goToMeasureEnd :: AppState -> AppState
goToMeasureEnd AppState
st =
let lastBeat :: Int
lastBeat = case AppState -> Maybe Measure
currentMeasure AppState
st of
Just 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
1
Maybe Measure
Nothing -> Int
0
in AppState
st{asCurrentBeat = BeatIndex lastBeat}
goToFirstString :: AppState -> AppState
goToFirstString :: AppState -> AppState
goToFirstString AppState
st = AppState
st{asCurrentString = StringIndex 0}
goToLastString :: AppState -> AppState
goToLastString :: AppState -> AppState
goToLastString AppState
st =
let maxS :: Int
maxS = case AppState -> Maybe Track
currentTrack AppState
st of
Maybe Track
Nothing -> Int
0
Just Track
t -> Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Track -> Int
stringCountForTrack Track
t Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
in AppState
st{asCurrentString = StringIndex maxS}
cycleDisplayMode :: AppState -> AppState
cycleDisplayMode :: AppState -> AppState
cycleDisplayMode AppState
st =
let tIdx :: TrackIndex
tIdx = AppState -> TrackIndex
asCurrentTrack AppState
st
current :: DisplayMode
current = DisplayMode
-> TrackIndex -> Map TrackIndex DisplayMode -> DisplayMode
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault DisplayMode
TabOnly TrackIndex
tIdx (AppState -> Map TrackIndex DisplayMode
asDisplayModes AppState
st)
next :: DisplayMode
next = case DisplayMode
current of
DisplayMode
TabOnly -> DisplayMode
NotationOnly
DisplayMode
NotationOnly -> DisplayMode
TabAndNotation
DisplayMode
TabAndNotation -> DisplayMode
TabOnly
in AppState
st{asDisplayModes = Map.insert tIdx next (asDisplayModes st)}
zoomIn :: AppState -> AppState
zoomIn :: AppState -> AppState
zoomIn AppState
st = AppState
st{asZoom = min 12 (asZoom st + 1)}
zoomOut :: AppState -> AppState
zoomOut :: AppState -> AppState
zoomOut AppState
st = AppState
st{asZoom = max 2 (asZoom st - 1)}