Safe Haskell | Safe-Inferred |
---|
The overall UI state is described here. This is an immutable data structure that contains all the tracks, rulers, note data, and so forth. It exports a StateT monad for modification and access.
Since the same block may have >=0 views, and a single track may appear in >=0 blocks, these are stored as IDs rather than directly in their containers. Using explicit references introduces all the usual problems with pointers like invalid references and unreferenced data. The latter is actually a feature (e.g. having a block with no associated view is perfectly normal), but the former is a pain. To ease the pain, IDs should only be created via the monadic create_* interface in this module, even though I'm forced to export their constructors to avoid circular imports. There may still be problems with IDs from one State being applied to a different State (likely an older and newer version of the same State), but I'll deal with that when I get there.
A higher level interface (e.g. Cmd.Create) may ease this by automatically creating objects with automatically generated IDs.
Synopsis
- data State = State {}
- views :: Lens.Lens State (Map Id.ViewId Block.View)
- blocks :: Lens.Lens State (Map Id.BlockId Block.Block)
- tracks :: Lens.Lens State (Map Id.TrackId Track.Track)
- rulers :: Lens.Lens State (Map Id.RulerId Ruler.Ruler)
- config :: Lens.Lens State UiConfig.Config
- empty :: State
- create :: IO State
- clear :: State -> State
- data Track = Track !Id.BlockId !Types.TrackNum
- data Range = Range !(Maybe.Maybe Id.BlockId) !Id.TrackId !TrackTime !TrackTime
- data TrackInfo = TrackInfo {}
- class (Applicative m, Monad m) => M m
- data StateT m a
- type StateId a = StateT Identity a
- get :: M m => m State
- unsafe_put :: M m => State -> m ()
- damage :: M m => Update.UiDamage -> m ()
- get_damage :: M m => m Update.UiDamage
- throw_error :: M m => Error -> m a
- throw :: (CallStack.Stack, M m) => Text -> m a
- run :: Monad m => State -> StateT m a -> m (Either Error (a, State, Update.UiDamage))
- run_id :: State -> StateId a -> Either Error (a, State, Update.UiDamage)
- eval :: State -> StateId a -> Either Error a
- eval_rethrow :: M m => Text -> State -> StateId a -> m a
- exec :: State -> StateId a -> Either Error State
- exec_rethrow :: M m => Text -> State -> StateId a -> m State
- gets :: M m => (State -> a) -> m a
- unsafe_modify :: M m => (State -> State) -> m ()
- put :: M m => State -> m ()
- modify :: M m => (State -> State) -> m ()
- update_all :: M m => m ()
- data Error
- require :: (CallStack.Stack, M m) => Text -> Maybe.Maybe a -> m a
- require_right :: (CallStack.Stack, M m) => (err -> Text) -> Either err a -> m a
- get_namespace :: M m => m Id.Namespace
- set_namespace :: M m => Id.Namespace -> m ()
- get_default :: M m => (UiConfig.Default -> a) -> m a
- modify_default :: M m => (UiConfig.Default -> UiConfig.Default) -> m ()
- get_root_id :: M m => m Id.BlockId
- lookup_root_id :: M m => m (Maybe.Maybe Id.BlockId)
- set_root_id :: M m => Id.BlockId -> m ()
- modify_config :: M m => (UiConfig.Config -> UiConfig.Config) -> m ()
- get_config :: M m => (UiConfig.Config -> a) -> m a
- with_config :: M m => (UiConfig.Config -> UiConfig.Config) -> m a -> m a
- modify_meta :: M m => (UiConfig.Meta -> UiConfig.Meta) -> m ()
- modify_allocation :: M m => ScoreT.Instrument -> (UiConfig.Allocation -> UiConfig.Allocation) -> m ()
- allocation :: ScoreT.Instrument -> Lens.Lens State (Maybe.Maybe UiConfig.Allocation)
- get_view :: M m => Id.ViewId -> m Block.View
- lookup_view :: M m => Id.ViewId -> m (Maybe.Maybe Block.View)
- all_view_ids :: M m => m [Id.ViewId]
- create_view :: M m => Id.Id -> Block.View -> m Id.ViewId
- destroy_view :: M m => Id.ViewId -> m ()
- put_views :: M m => Map Id.ViewId Block.View -> m ()
- set_view_status :: M m => Id.ViewId -> (Int, Text) -> Maybe.Maybe Text -> m ()
- get_zoom :: M m => Id.ViewId -> m Zoom.Zoom
- modify_zoom :: M m => Id.ViewId -> (Zoom.Zoom -> Zoom.Zoom) -> m ()
- set_track_scroll :: M m => Id.ViewId -> Types.Width -> m ()
- set_view_rect :: M m => Id.ViewId -> Rect.Rect -> m ()
- set_view_padding :: M m => Id.ViewId -> Block.Padding -> m ()
- get_selection :: M m => Id.ViewId -> Sel.Num -> m (Maybe.Maybe Sel.Selection)
- set_selection :: M m => Id.ViewId -> Sel.Num -> Maybe.Maybe Sel.Selection -> m ()
- shift_selection :: Bool -> Block.Block -> Types.TrackNum -> Sel.Selection -> Sel.Selection
- skip_unselectable_tracks :: Block.Block -> Types.TrackNum -> Int -> Types.TrackNum
- selectable_tracks :: Block.Block -> [Types.TrackNum]
- get_block :: M m => Id.BlockId -> m Block.Block
- lookup_block :: M m => Id.BlockId -> m (Maybe.Maybe Block.Block)
- all_block_ids :: M m => m [Id.BlockId]
- all_block_track_ids :: M m => m [(Id.BlockId, [Id.TrackId])]
- create_config_block :: M m => Id.Id -> Block.Block -> m Id.BlockId
- create_block :: M m => Id.Id -> Text -> [Block.Track] -> m Id.BlockId
- destroy_block :: M m => Id.BlockId -> m ()
- block_of :: M m => Id.ViewId -> m Block.Block
- block_id_of :: M m => Id.ViewId -> m Id.BlockId
- views_of :: M m => Id.BlockId -> m (Map Id.ViewId Block.View)
- get_block_title :: M m => Id.BlockId -> m Text
- set_block_title :: M m => Id.BlockId -> Text -> m ()
- modify_block_meta :: M m => Id.BlockId -> (Block.Meta -> Block.Meta) -> m ()
- set_integrated_block :: M m => Id.BlockId -> Maybe.Maybe (Id.BlockId, Block.TrackDestinations) -> m ()
- modify_integrated_tracks :: M m => Id.BlockId -> ([(Id.TrackId, Block.TrackDestinations)] -> [(Id.TrackId, Block.TrackDestinations)]) -> m ()
- set_integrated_manual :: M m => Id.BlockId -> Block.SourceKey -> Maybe.Maybe [Block.NoteDestination] -> m ()
- set_edit_box :: M m => Id.BlockId -> Block.Box -> Block.Box -> m ()
- set_play_box :: M m => Id.BlockId -> Color.Color -> m ()
- block_ruler_end :: M m => Id.BlockId -> m TrackTime
- block_event_end :: M m => Id.BlockId -> m TrackTime
- block_end :: M m => Id.BlockId -> m TrackTime
- block_logical_range :: M m => Id.BlockId -> m (TrackTime, TrackTime)
- set_skeleton_config :: M m => Id.BlockId -> Block.Skeleton -> m ()
- has_explicit_skeleton :: M m => Id.BlockId -> m Bool
- get_skeleton :: M m => Id.BlockId -> m Skeleton.Skeleton
- set_skeleton :: M m => Id.BlockId -> Skeleton.Skeleton -> m ()
- modify_skeleton :: M m => Id.BlockId -> (Skeleton.Skeleton -> Skeleton.Skeleton) -> m ()
- toggle_skeleton_edge :: M m => Bool -> Id.BlockId -> Skeleton.Edge -> m Bool
- add_edges :: M m => Id.BlockId -> [Skeleton.Edge] -> m ()
- remove_edges :: M m => Id.BlockId -> [Skeleton.Edge] -> m ()
- splice_skeleton_above :: M m => Id.BlockId -> Types.TrackNum -> Types.TrackNum -> m ()
- splice_skeleton_below :: M m => Id.BlockId -> Types.TrackNum -> Types.TrackNum -> m ()
- insert_track :: M m => Id.BlockId -> Types.TrackNum -> Block.Track -> m ()
- remove_track :: M m => Id.BlockId -> Types.TrackNum -> m ()
- move_track :: M m => Id.BlockId -> Types.TrackNum -> Types.TrackNum -> m ()
- track_count :: M m => Id.BlockId -> m Types.TrackNum
- block_track_at :: M m => Id.BlockId -> Types.TrackNum -> m (Maybe.Maybe Block.Track)
- get_block_track_at :: M m => Id.BlockId -> Types.TrackNum -> m Block.Track
- track_at :: M m => Id.BlockId -> Types.TrackNum -> m (Maybe.Maybe Block.TracklikeId)
- event_track_at :: M m => Id.BlockId -> Types.TrackNum -> m (Maybe.Maybe Id.TrackId)
- get_event_track_at :: M m => Id.BlockId -> Types.TrackNum -> m Id.TrackId
- ruler_track_at :: M m => Id.BlockId -> Types.TrackNum -> m (Maybe.Maybe Id.RulerId)
- block_ruler :: M m => Id.BlockId -> m Id.RulerId
- track_ids_of :: M m => Id.BlockId -> m [Id.TrackId]
- tracknums_of :: M m => Id.BlockId -> m [(Id.TrackId, Types.TrackNum)]
- block_tracknums :: M m => Id.BlockId -> m [(Block.Track, Types.TrackNum)]
- tracknum_of :: M m => Id.BlockId -> Id.TrackId -> m (Maybe.Maybe Types.TrackNum)
- get_tracknum_of :: M m => Id.BlockId -> Id.TrackId -> m Types.TrackNum
- set_track_width :: M m => Id.BlockId -> Types.TrackNum -> Types.Width -> m ()
- set_track_suggested_width :: M m => Id.BlockId -> Types.TrackNum -> Types.Width -> m ()
- track_flags :: M m => Id.BlockId -> Types.TrackNum -> m (Set Block.TrackFlag)
- track_collapsed :: M m => Id.BlockId -> Types.TrackNum -> m Bool
- toggle_track_flag :: M m => Id.BlockId -> Types.TrackNum -> Block.TrackFlag -> m ()
- add_track_flag :: M m => Id.BlockId -> Types.TrackNum -> Block.TrackFlag -> m ()
- remove_track_flag :: M m => Id.BlockId -> Types.TrackNum -> Block.TrackFlag -> m ()
- modify_track_flags :: M m => Id.BlockId -> Types.TrackNum -> (Set Block.TrackFlag -> Set Block.TrackFlag) -> m ()
- set_track_ruler :: M m => Id.BlockId -> Types.TrackNum -> Id.RulerId -> m ()
- merge_track :: M m => Id.BlockId -> Types.TrackNum -> Types.TrackNum -> m ()
- unmerge_track :: M m => Id.BlockId -> Types.TrackNum -> m ()
- set_merged_tracks :: M m => Id.BlockId -> Types.TrackNum -> Set Id.TrackId -> m ()
- track_merged :: M m => Id.BlockId -> Types.TrackNum -> m Bool
- set_ruler_ids :: M m => Id.BlockId -> [Maybe.Maybe Id.RulerId] -> m ()
- replace_ruler_id :: M m => Id.BlockId -> Id.RulerId -> Id.RulerId -> m ()
- set_ruler_id :: M m => Id.BlockId -> Id.RulerId -> m ()
- get_tracklike :: M m => Block.TracklikeId -> m Block.Tracklike
- get_track :: M m => Id.TrackId -> m Track.Track
- lookup_track :: M m => Id.TrackId -> m (Maybe.Maybe Track.Track)
- all_track_ids :: M m => m [Id.TrackId]
- create_track :: M m => Id.Id -> Track.Track -> m Id.TrackId
- destroy_track :: M m => Id.TrackId -> m ()
- get_track_title :: M m => Id.TrackId -> m Text
- set_track_title :: M m => Id.TrackId -> Text -> m ()
- modify_track_title :: M m => Id.TrackId -> (Text -> Text) -> m ()
- set_track_bg :: M m => Id.TrackId -> Color.Color -> m ()
- modify_track_render :: M m => Id.TrackId -> (Track.RenderConfig -> Track.RenderConfig) -> m ()
- set_render_style :: M m => Track.RenderStyle -> Id.TrackId -> m ()
- modify_waveform :: M m => Id.TrackId -> (Bool -> Bool) -> m ()
- blocks_with_track_id :: M m => Id.TrackId -> m [(Id.BlockId, [(Types.TrackNum, Block.TracklikeId)])]
- insert_events :: M m => Id.TrackId -> [Event.Event] -> m ()
- insert_block_events :: M m => Id.BlockId -> Id.TrackId -> [Event.Event] -> m ()
- insert_event :: M m => Id.TrackId -> Event.Event -> m ()
- get_events :: M m => Id.TrackId -> m Events.Events
- modify_events :: M m => Id.TrackId -> (Events.Events -> Events.Events) -> m ()
- modify_events_range :: M m => Id.TrackId -> Events.Range -> (Events.Events -> Events.Events) -> m ()
- modify_events_from :: M m => Id.TrackId -> TrackTime -> (Events.Events -> Events.Events) -> m ()
- modify_some_events :: M m => Id.TrackId -> (Events.Events -> Events.Events) -> m ()
- calculate_damage :: Events.Events -> Events.Events -> Ranges.Ranges TrackTime
- remove_event :: M m => Id.TrackId -> Event.Event -> m ()
- remove_events :: M m => Id.TrackId -> [Event.Event] -> m ()
- remove_events_range :: M m => Id.TrackId -> Events.Range -> m ()
- track_event_end :: M m => Id.TrackId -> m TrackTime
- range_from :: M m => Id.TrackId -> TrackTime -> m Events.Range
- get_ruler :: M m => Id.RulerId -> m Ruler.Ruler
- lookup_ruler :: M m => Id.RulerId -> m (Maybe.Maybe Ruler.Ruler)
- all_ruler_ids :: M m => m [Id.RulerId]
- create_ruler :: M m => Id.Id -> Ruler.Ruler -> m Id.RulerId
- destroy_ruler :: M m => Id.RulerId -> m ()
- modify_ruler :: M m => Id.RulerId -> (Ruler.Ruler -> Either Text Ruler.Ruler) -> m ()
- ruler_of :: M m => Id.BlockId -> m Id.RulerId
- rulers_of :: M m => Id.BlockId -> m [Id.RulerId]
- blocks_with_ruler_id :: M m => Id.RulerId -> m [(Id.BlockId, [(Types.TrackNum, Block.TracklikeId)])]
- no_ruler :: Id.RulerId
- find_tracks :: (Block.TracklikeId -> Bool) -> Map Id.BlockId Block.Block -> [(Id.BlockId, [(Types.TrackNum, Block.TracklikeId)])]
- quick_verify :: Update.UiDamage -> State -> Either String (State, [Text])
- verify :: State -> (State, [Text])
- fix_state :: M m => m [Text]
- read_id :: (CallStack.Stack, Id.Ident a, M m) => Text -> m a
- namespace :: M m => Text -> m Id.Namespace
Documentation
Score state. When you save a score, this is what is saved to disk.
address types
Address a track in a block. This is similar to a TrackId, except it doesn't guarantee that the track is an event track.
A position on a track that can be indicated on the UI. Its Pretty instance emits a string, which if logged or copy-pasted into the REPL, will cause that section of score to be highlighted.
Summary information on a Track.
TrackInfo | |
|
StateT monad
class (Applicative m, Monad m) => M m Source #
Monads implementing this class can call the UI state functions directly.
Instances
Monad m => M (CmdT m) Source # | And to the UI state operations. |
Monad m => M (StateT m) Source # | |
M m => M (Except.ExceptT exc m) Source # | |
Defined in Ui.Ui get :: Except.ExceptT exc m State Source # unsafe_put :: State -> Except.ExceptT exc m () Source # damage :: Update.UiDamage -> Except.ExceptT exc m () Source # get_damage :: Except.ExceptT exc m Update.UiDamage Source # throw_error :: Error -> Except.ExceptT exc m a Source # | |
M m => M (State.StateT state m) Source # | |
Defined in Ui.Ui get :: State.StateT state m State Source # unsafe_put :: State -> State.StateT state m () Source # damage :: Update.UiDamage -> State.StateT state m () Source # get_damage :: State.StateT state m Update.UiDamage Source # throw_error :: Error -> State.StateT state m a Source # |
Instances
MonadTrans StateT Source # | |
Monad m => MonadError Error (StateT m) Source # | |
MonadIO m => MonadIO (StateT m) Source # | |
Monad m => Applicative (StateT m) Source # | |
Functor m => Functor (StateT m) Source # | |
Monad m => Monad (StateT m) Source # | |
Monad m => M (StateT m) Source # | |
Monad m => LogMonad (UiLogT m) Source # | |
unsafe_put :: M m => State -> m () Source #
This directly modifies the state, and can break internal invariants.
put
is slower but safer since it checks those invariants.
damage :: M m => Update.UiDamage -> m () Source #
get_damage :: M m => m Update.UiDamage Source #
throw_error :: M m => Error -> m a Source #
run :: Monad m => State -> StateT m a -> m (Either Error (a, State, Update.UiDamage)) Source #
Run the given StateT with the given initial state, and return a new
state along with updates. Normally updates are produced by diff
,
but for efficiency updates to track data are accumulated when they are
actually made. All the UI needs is a TrackTime range to redraw in, and
redrawing the whole track isn't that expensive.
See the StateStack comment for more.
eval :: State -> StateId a -> Either Error a Source #
A form of run
that returns only the val and automatically runs in
Identity.
unsafe_modify :: M m => (State -> State) -> m () Source #
As with unsafe_put
, this directly modifies the state. modify
is
the safe version.
put :: M m => State -> m () Source #
TODO verify
This updates all tracks because I don't know what you modified in there.
modify :: M m => (State -> State) -> m () Source #
An arbitrary modify. It's unsafe because it doesn't check internal invariants, and inefficient because it damages all tracks. Use more specific modify_* functions, if possible.
update_all :: M m => m () Source #
Emit track updates for all tracks. Use this when events have changed but I don't know which ones, e.g. when loading a file or restoring a previous state.
errors
Abort is used by Cmd, so don't throw it from here. This isn't exactly modular, but ErrorT can't be composed and extensible exceptions are too much bother at the moment.
require :: (CallStack.Stack, M m) => Text -> Maybe.Maybe a -> m a Source #
require_right :: (CallStack.Stack, M m) => (err -> Text) -> Either err a -> m a Source #
config
get_namespace :: M m => m Id.Namespace Source #
set_namespace :: M m => Id.Namespace -> m () Source #
get_default :: M m => (UiConfig.Default -> a) -> m a Source #
modify_default :: M m => (UiConfig.Default -> UiConfig.Default) -> m () Source #
get_root_id :: M m => m Id.BlockId Source #
lookup_root_id :: M m => m (Maybe.Maybe Id.BlockId) Source #
set_root_id :: M m => Id.BlockId -> m () Source #
modify_config :: M m => (UiConfig.Config -> UiConfig.Config) -> m () Source #
Unlike other State fields, you can modify Config freely without worrying about breaking invariants. TODO except allocations have invariants.
get_config :: M m => (UiConfig.Config -> a) -> m a Source #
with_config :: M m => (UiConfig.Config -> UiConfig.Config) -> m a -> m a Source #
Run the action with a modified state, and restore it.
modify_meta :: M m => (UiConfig.Meta -> UiConfig.Meta) -> m () Source #
modify_allocation :: M m => ScoreT.Instrument -> (UiConfig.Allocation -> UiConfig.Allocation) -> m () Source #
allocation :: ScoreT.Instrument -> Lens.Lens State (Maybe.Maybe UiConfig.Allocation) Source #
TODO use this for read only. If used for write it bypasses
UiConfig.allocate
.
view
lookup_view :: M m => Id.ViewId -> m (Maybe.Maybe Block.View) Source #
all_view_ids :: M m => m [Id.ViewId] Source #
All ViewIds, in sorted order.
create_view :: M m => Id.Id -> Block.View -> m Id.ViewId Source #
Create a new view. Block.view_tracks can be left empty, since it will
be replaced by views generated from the the block. If the caller uses the
Block.view
constructor, it won't have to worry about this.
Throw if the ViewId already exists.
destroy_view :: M m => Id.ViewId -> m () Source #
set_view_status :: M m => Id.ViewId -> (Int, Text) -> Maybe.Maybe Text -> m () Source #
Set a status variable on a view.
zoom and track scroll
set_track_scroll :: M m => Id.ViewId -> Types.Width -> m () Source #
set_view_padding :: M m => Id.ViewId -> Block.Padding -> m () Source #
Only ui_update
is supposed to call this, because the UI is
responsible for the padding.
selections
get_selection :: M m => Id.ViewId -> Sel.Num -> m (Maybe.Maybe Sel.Selection) Source #
Get view_id
's selection at selnum
, or Nothing if there is none.
set_selection :: M m => Id.ViewId -> Sel.Num -> Maybe.Maybe Sel.Selection -> m () Source #
Replace any selection on view_id
at selnum
with sel
.
:: Bool | skip unselectable tracks |
-> Block.Block | |
-> Types.TrackNum | |
-> Sel.Selection | |
-> Sel.Selection |
Shift the selection, clipping if it's out of range. While the sel_cur_track won't be on a non-selectable track after this, the selection may still include one.
skip_unselectable_tracks :: Block.Block -> Types.TrackNum -> Int -> Types.TrackNum Source #
Shift a tracknum to another track, skipping unselectable tracks.
selectable_tracks :: Block.Block -> [Types.TrackNum] Source #
Get the tracknums from a block that should be selectable.
block
get_block :: M m => Id.BlockId -> m Block.Block Source #
lookup_block :: M m => Id.BlockId -> m (Maybe.Maybe Block.Block) Source #
all_block_ids :: M m => m [Id.BlockId] Source #
all_block_track_ids :: M m => m [(Id.BlockId, [Id.TrackId])] Source #
Get all blocks along with their tracks.
create_config_block :: M m => Id.Id -> Block.Block -> m Id.BlockId Source #
Make a new block. If it's the first one, it will be set as the root.
This is the low level version, you probably want to use create_block
.
Throw if the BlockId already exists.
create_block :: M m => Id.Id -> Text -> [Block.Track] -> m Id.BlockId Source #
Make a new block with the default Block.Config
.
destroy_block :: M m => Id.BlockId -> m () Source #
Destroy the block and all the views that display it. If the block was the root, it will be be unset. The block's tracks are left intact.
block_id_of :: M m => Id.ViewId -> m Id.BlockId Source #
views_of :: M m => Id.BlockId -> m (Map Id.ViewId Block.View) Source #
Get all views of a given block.
get_block_title :: M m => Id.BlockId -> m Text Source #
set_block_title :: M m => Id.BlockId -> Text -> m () Source #
modify_block_meta :: M m => Id.BlockId -> (Block.Meta -> Block.Meta) -> m () Source #
set_integrated_block :: M m => Id.BlockId -> Maybe.Maybe (Id.BlockId, Block.TrackDestinations) -> m () Source #
Set or clear this block as an integrate destination. The automatic integration system will update it from the given source block.
modify_integrated_tracks :: M m => Id.BlockId -> ([(Id.TrackId, Block.TrackDestinations)] -> [(Id.TrackId, Block.TrackDestinations)]) -> m () Source #
set_integrated_manual :: M m => Id.BlockId -> Block.SourceKey -> Maybe.Maybe [Block.NoteDestination] -> m () Source #
Set or clear the block's manual integration Block.NoteDestination
s.
This just attaches (or removes) the integrate information to the block so
a future integration can use it to merge, and then call this function again.
set_edit_box :: M m => Id.BlockId -> Block.Box -> Block.Box -> m () Source #
set_play_box :: M m => Id.BlockId -> Color.Color -> m () Source #
The play box doesn't use a char, so I leave that out.
block_ruler_end :: M m => Id.BlockId -> m TrackTime Source #
Get the end of the block according to the ruler. This means that if the block has no rulers (e.g. a clipboard block) then block_ruler_end will be 0.
block_event_end :: M m => Id.BlockId -> m TrackTime Source #
Get the end of the block according to the last event of the block.
block_end :: M m => Id.BlockId -> m TrackTime Source #
Get the maximum of ruler end and event end. The end may still be 0 if the block is totally empty.
block_logical_range :: M m => Id.BlockId -> m (TrackTime, TrackTime) Source #
The logical range is defined by Ruler.bounds_of
and is intended to
correspond to the "note" that this block defines.
skeleton
set_skeleton_config :: M m => Id.BlockId -> Block.Skeleton -> m () Source #
has_explicit_skeleton :: M m => Id.BlockId -> m Bool Source #
get_skeleton :: M m => Id.BlockId -> m Skeleton.Skeleton Source #
set_skeleton :: M m => Id.BlockId -> Skeleton.Skeleton -> m () Source #
modify_skeleton :: M m => Id.BlockId -> (Skeleton.Skeleton -> Skeleton.Skeleton) -> m () Source #
:: M m | |
=> Bool | If not true, the child's existing parents will be unlinked. While a track with multiple parents is possible, and is a way to express the same score derived under different conditions, in practice I never do that. |
-> Id.BlockId | |
-> Skeleton.Edge | |
-> m Bool |
Toggle the given edge in the block's skeleton. If a cycle would be created, refuse to add the edge and return False. The edge is in (parent, child) order.
add_edges :: M m => Id.BlockId -> [Skeleton.Edge] -> m () Source #
Add the edges to the skeleton. Throw if they would produce a cycle.
remove_edges :: M m => Id.BlockId -> [Skeleton.Edge] -> m () Source #
splice_skeleton_above :: M m => Id.BlockId -> Types.TrackNum -> Types.TrackNum -> m () Source #
The first tracknum is spliced above the second.
splice_skeleton_below :: M m => Id.BlockId -> Types.TrackNum -> Types.TrackNum -> m () Source #
The first tracknum is spliced below the second.
tracks
insert_track :: M m => Id.BlockId -> Types.TrackNum -> Block.Track -> m () Source #
Insert a track at the given TrackNum. The TrackNum can be out of range to insert a track at the beginning or append it to the end.
This will throw if it's an event track and the block already contains that TrackId. This invariant ensures that a (BlockId, TrackNum) is interchangeable with a TrackId.
remove_track :: M m => Id.BlockId -> Types.TrackNum -> m () Source #
Remove the track at the given tracknum.
move_track :: M m => Id.BlockId -> Types.TrackNum -> Types.TrackNum -> m () Source #
Move a track from one tracknum to another.
tracks by tracknum
track_count :: M m => Id.BlockId -> m Types.TrackNum Source #
Number of tracks in the block. This includes the ruler, so subtract 1 if you want all non-ruler tracks.
block_track_at :: M m => Id.BlockId -> Types.TrackNum -> m (Maybe.Maybe Block.Track) Source #
Get the Track at tracknum
, or Nothing if its out of range.
get_block_track_at :: M m => Id.BlockId -> Types.TrackNum -> m Block.Track Source #
track_at :: M m => Id.BlockId -> Types.TrackNum -> m (Maybe.Maybe Block.TracklikeId) Source #
event_track_at :: M m => Id.BlockId -> Types.TrackNum -> m (Maybe.Maybe Id.TrackId) Source #
Like track_at
, but only for event tracks.
get_event_track_at :: M m => Id.BlockId -> Types.TrackNum -> m Id.TrackId Source #
Like event_track_at
but throws if it's not there or not an event track.
ruler_track_at :: M m => Id.BlockId -> Types.TrackNum -> m (Maybe.Maybe Id.RulerId) Source #
Get the RulerId of an event or ruler track, or Nothing if the tracknum is out of range or doesn't have a ruler.
block_ruler :: M m => Id.BlockId -> m Id.RulerId Source #
0 is the conventional ruler tracknum.
tracks by TrackId
track_ids_of :: M m => Id.BlockId -> m [Id.TrackId] Source #
Get all TrackIds of the given block.
tracknums_of :: M m => Id.BlockId -> m [(Id.TrackId, Types.TrackNum)] Source #
Get all TrackIds of the given block, along with their tracknums.
block_tracknums :: M m => Id.BlockId -> m [(Block.Track, Types.TrackNum)] Source #
Get tracks along with their TrackNums.
tracknum_of :: M m => Id.BlockId -> Id.TrackId -> m (Maybe.Maybe Types.TrackNum) Source #
There can only be one TrackId per block, which allows TrackNums and
TrackIds to be interchangeable. This is enforced by insert_track
.
The inverse is event_track_at
.
get_tracknum_of :: M m => Id.BlockId -> Id.TrackId -> m Types.TrackNum Source #
block track
set_track_width :: M m => Id.BlockId -> Types.TrackNum -> Types.Width -> m () Source #
set_track_suggested_width :: M m => Id.BlockId -> Types.TrackNum -> Types.Width -> m () Source #
track_flags :: M m => Id.BlockId -> Types.TrackNum -> m (Set Block.TrackFlag) Source #
track_collapsed :: M m => Id.BlockId -> Types.TrackNum -> m Bool Source #
toggle_track_flag :: M m => Id.BlockId -> Types.TrackNum -> Block.TrackFlag -> m () Source #
add_track_flag :: M m => Id.BlockId -> Types.TrackNum -> Block.TrackFlag -> m () Source #
remove_track_flag :: M m => Id.BlockId -> Types.TrackNum -> Block.TrackFlag -> m () Source #
modify_track_flags :: M m => Id.BlockId -> Types.TrackNum -> (Set Block.TrackFlag -> Set Block.TrackFlag) -> m () Source #
set_track_ruler :: M m => Id.BlockId -> Types.TrackNum -> Id.RulerId -> m () Source #
merge_track :: M m => Id.BlockId -> Types.TrackNum -> Types.TrackNum -> m () Source #
Merge the from
tracknum into the to
tracknum and collapse from
.
unmerge_track :: M m => Id.BlockId -> Types.TrackNum -> m () Source #
Reverse merge_track
: remove the merged tracks and expand their
occurrances in the given block. "Unmerge" is not a graceful term, but at
least it's obviously the opposite of "merge".
set_merged_tracks :: M m => Id.BlockId -> Types.TrackNum -> Set Id.TrackId -> m () Source #
track_merged :: M m => Id.BlockId -> Types.TrackNum -> m Bool Source #
set_ruler_ids :: M m => Id.BlockId -> [Maybe.Maybe Id.RulerId] -> m () Source #
Set rulers, one per track.
replace_ruler_id :: M m => Id.BlockId -> Id.RulerId -> Id.RulerId -> m () Source #
Replace one RulerId with another on the given block.
It's more convenient to do here than removing and inserting tracks, and easy since there's no "one per block" invariant to maintain with ruler ids.
set_ruler_id :: M m => Id.BlockId -> Id.RulerId -> m () Source #
get_tracklike :: M m => Block.TracklikeId -> m Block.Tracklike Source #
Resolve a TracklikeId to a Tracklike.
track
get_track :: M m => Id.TrackId -> m Track.Track Source #
lookup_track :: M m => Id.TrackId -> m (Maybe.Maybe Track.Track) Source #
all_track_ids :: M m => m [Id.TrackId] Source #
create_track :: M m => Id.Id -> Track.Track -> m Id.TrackId Source #
Insert the given track with the given ID.
Throw if the TrackId already exists.
destroy_track :: M m => Id.TrackId -> m () Source #
Destroy the track and remove it from all the blocks it's in. No-op if the TrackId doesn't exist.
get_track_title :: M m => Id.TrackId -> m Text Source #
set_track_title :: M m => Id.TrackId -> Text -> m () Source #
modify_track_title :: M m => Id.TrackId -> (Text -> Text) -> m () Source #
set_track_bg :: M m => Id.TrackId -> Color.Color -> m () Source #
modify_track_render :: M m => Id.TrackId -> (Track.RenderConfig -> Track.RenderConfig) -> m () Source #
set_render_style :: M m => Track.RenderStyle -> Id.TrackId -> m () Source #
modify_waveform :: M m => Id.TrackId -> (Bool -> Bool) -> m () Source #
blocks_with_track_id :: M m => Id.TrackId -> m [(Id.BlockId, [(Types.TrackNum, Block.TracklikeId)])] Source #
Find track_id
in all the blocks it exists in, and return the track info
for each tracknum at which track_id
lives. Blocks with no matching tracks
won't be returned, so the return track lists will always be non-null.
events
insert_events :: M m => Id.TrackId -> [Event.Event] -> m () Source #
Insert events into track_id as per Events.insert
.
insert_block_events :: M m => Id.BlockId -> Id.TrackId -> [Event.Event] -> m () Source #
Like insert_events
, but clip the events to the end of a block.
This is necessarily block specific, because block duration is defined by its
ruler. Still, you should use this in preference to insert_events
.
This uses block_end
, which means that if events don't already go past the
end of the ruler, they won't after this is called. If they are already
past (e.g. there is no ruler), then they will only be clipped if they move
to later in time. This might be confusing, but it seems generally
convenient to not have to constantly manually trim events when they get
moved past the end of the ruler, but definitely inconvenient for events to
just disappear when there is no ruler.
insert_event :: M m => Id.TrackId -> Event.Event -> m () Source #
get_events :: M m => Id.TrackId -> m Events.Events Source #
modify_events :: M m => Id.TrackId -> (Events.Events -> Events.Events) -> m () Source #
Modify the events on a track, and assume the entire track has been damaged.
modify_events_range :: M m => Id.TrackId -> Events.Range -> (Events.Events -> Events.Events) -> m () Source #
modify_events_from :: M m => Id.TrackId -> TrackTime -> (Events.Events -> Events.Events) -> m () Source #
modify_some_events :: M m => Id.TrackId -> (Events.Events -> Events.Events) -> m () Source #
Just like modify_events
, except that it expects you only modified a few
events, and will only emit damage for the changed parts.
remove_event :: M m => Id.TrackId -> Event.Event -> m () Source #
Remove a single event by start and orientation.
TODO I think remove_events_range
is now just as expressive and can be just
as efficient
remove_events :: M m => Id.TrackId -> [Event.Event] -> m () Source #
Just like mapM_ (remove_event track_id)
but more efficient.
TODO at least I hope, it got sort of complicated.
remove_events_range :: M m => Id.TrackId -> Events.Range -> m () Source #
track_event_end :: M m => Id.TrackId -> m TrackTime Source #
Get the end of the last event of the block.
range_from :: M m => Id.TrackId -> TrackTime -> m Events.Range Source #
ruler
get_ruler :: M m => Id.RulerId -> m Ruler.Ruler Source #
lookup_ruler :: M m => Id.RulerId -> m (Maybe.Maybe Ruler.Ruler) Source #
all_ruler_ids :: M m => m [Id.RulerId] Source #
create_ruler :: M m => Id.Id -> Ruler.Ruler -> m Id.RulerId Source #
Insert the given ruler with the given ID.
Throw if the RulerId already exists.
destroy_ruler :: M m => Id.RulerId -> m () Source #
Destroy the ruler and remove it from all the blocks it's in.
modify_ruler :: M m => Id.RulerId -> (Ruler.Ruler -> Either Text Ruler.Ruler) -> m () Source #
ruler_of :: M m => Id.BlockId -> m Id.RulerId Source #
rulers_of :: M m => Id.BlockId -> m [Id.RulerId] Source #
blocks_with_ruler_id :: M m => Id.RulerId -> m [(Id.BlockId, [(Types.TrackNum, Block.TracklikeId)])] Source #
Just like blocks_with_track_id
except for ruler_id.
no_ruler :: Id.RulerId Source #
Since all TracklikeIds must have a ruler, all States have a special empty ruler that can be used in a "no ruler" situation.
This RulerId is implicitly present in every block. It's not actually in
state_rulers
to avoid it getting renamed or deleted, but get_ruler
will
pretend it exists. As long as everyone that cares about no_ruler (which is
only verify
and get_tracklike
for Ui.Sync) uses get_ruler
then
they won't be confused by tracks that have no_ruler.
util
find_tracks :: (Block.TracklikeId -> Bool) -> Map Id.BlockId Block.Block -> [(Id.BlockId, [(Types.TrackNum, Block.TracklikeId)])] Source #
verify
quick_verify :: Update.UiDamage -> State -> Either String (State, [Text]) Source #
This is like verify
, but less complete. It returns Left if it wants
you to reject the new state entirely.
verify
is better, but more expensive, so I'm reluctant to run it on every
single cmd. If I run verify
before unsafe puts and trust this module to
maintain invariants then I don't need to, but I don't fully trust this
module.
TODO a better approach would be to make sure Sync can't be broken by State.
verify :: State -> (State, [Text]) Source #
Unfortunately there are some invariants to protect within State. They can all be fixed by dropping things, so this will fix them and return a list of warnings.