# Annotation marker sample The Annotation sample provides a marker that can be used as a bookmark for your timeline. ![Annotation](images/smpl_annotation.png) Here are the options available on an annotation: Field | Description --- | --- Title | The annotation's title. This will be displayed as a tooltip, when hovering the mouse on the annotation. Color | The annotation's color in the Timeline window. Show line overlay | Use this option to show a vertical line that spans the full height of the Timeline window. ## Custom marker workflow example This example will demonstrate how to: * create a custom marker; * customize a marker with `MarkerEditor`; * use a custom USS style to draw a marker; * add additional commands with `Actions`; ### 1. Create an annotation marker A marker is an item that can be added to a Timeline Asset and is used to represent a point in time. Markers also have a specialization, just like clips (Activation clip, Audio clip, Animation clip, etc). In order to add a new type of marker, all we need to do is to create a class that inherits the `Marker` class: ``` c# public class AnnotationMarker : UnityEngine.Timeline.Marker {} ``` This custom marker can now be added to any track or on the timeline marker area: ![New marker added to a timeline](images/smpl_annotation_newMarker.png) We can add a title, description and color to the annotation: ``` c# public class AnnotationMarker : Marker { public string title; public Color color; public string description; public bool showLineOverlay; } ``` The annotation marker itself is now complete. But the customization work is not done yet. Timeline offers many customization abilities. ## 2. Customize the marker's appearance A marker's appearance can be customized using a USS style or with `MarkerEditor`. Both paths have their advantages and drawbacks. ### Custom USS style A marker can use a USS style to specify its appearance. For more information on how to create custom USS styles, see [how to define custom USS styles](uss_styles.md). The [CustomStyle](xref:UnityEngine.Timeline.CustomStyleAttribute) attribute can be used to specify a style for a given marker: ``` c# [CustomStyle("AnnotationStyle")] public class AnnotationMarker : Marker { //... } ``` `AnnotationStyle` is defined in a USS stylesheet and will be used when a marker is displayed on screen: ![Custom style](images/smpl_annotation_customStyle.png) USS styles are useful if the desired appearance is simple (i.e. when only using a texture icon). For more complex stuff (i.e. dynamically changing a marker's color), a `MarkerEditor` will be needed. ### Custom editor `MarkerEditor` can be used to augment the capabilities of a marker in the editor. It works like a custom [Inspector](https://docs.unity3d.com/ScriptReference/CustomEditor.html); the [CustomTimelineEditor attribute](xref:UnityEditor.Timeline.CustomTimelineEditorAttribute) is used to tell Timeline that a [MarkerEditor](xref:UnityEditor.Timeline.MarkerEditor) class should be associated to a given marker. ``` c# [CustomTimelineEditor(typeof(AnnotationMarker))] public class AnnotationMarkerEditor : MarkerEditor { //... } ``` #### Marker information `MarkerEditor` lets us provide information about the marker by overriding the [GetMarkerOptions](xref:UnityEditor.Timeline.MarkerEditor#UnityEditor_Timeline_MarkerEditor_GetMarkerOptions_UnityEngine_Timeline_IMarker_) method. ``` c# public override MarkerDrawOptions GetMarkerOptions(IMarker marker) { var annotation = marker as AnnotationMarker; if (annotation != null) { return new MarkerDrawOptions { tooltip = annotation.title }; } return base.GetMarkerOptions(marker); } ``` Here the tooltip of an `Annotation` has been set to use the annotation's `title` variable. ![Marker tooltip](images/smpl_annotation_markerTooltip.png) [MarkerDrawOptions](xref:UnityEditor.Timeline.MarkerDrawOptions) can also set the error text on a marker, which can be useful if a variable has been incorrectly set and needs attention. #### Overlay An overlay can be drawn on top of a marker by overriding the [DrawOverlay](xref:UnityEditor.Timeline.MarkerEditor#UnityEditor_Timeline_MarkerEditor_DrawOverlay_UnityEngine_Timeline_IMarker_UnityEditor_Timeline_MarkerUIStates_UnityEditor_Timeline_MarkerOverlayRegion_) method: ``` c# public override void DrawOverlay(IMarker marker, MarkerUIStates uiState, MarkerOverlayRegion region) { var annotation = marker as AnnotationMarker; if (annotation != null) { //Draw overlay code... } } ``` An overlay is drawn on top of the marker; the [USS style](uss_styles.md) is drawn first and `DrawOverlay` is called afterwards. For an `Annotation`, we can use `DrawOverlay` to change the color of the marker and to draw a line that spans the full Timeline window's height. To do this, we can use the information given in `region`. Along with the visible time range, [MarkerOverlayRegion](xref:UnityEditor.Timeline.MarkerOverlayRegion) provides two rectangles that can be used to know where to draw: * `markerRegion` `markerRegion` is the rectangle that encompasses the marker. This is useful to draw something directly on the marker itself. For `Annotation`, this rectangle is used to draw the color overlay. ![marker region](images/smpl_annotation_markerOverlayRegion.png) * `timelineRegion` `timelineRegion` is the rectangle that encompasses the clips and markers region of the timeline window. This is useful to draw something out of the marker's region, like the `Annotation`'s line overlay. ![timeline region](images/smpl_annotation_timelineOverlayRegion.png) ``` c# const float k_LineOverlayWidth = 6.0f; float markerRegionCenter = markerRegion.xMin + (markerRegion.width - k_LineOverlayWidth) / 2.0f; Rect lineRect = new Rect(markerRegionCenter, timelineRegion.y, k_LineOverlayWidth, timelineRegion.height); ``` ## 3. Create custom Actions ### Timeline Action Actions can be used to add new menu entries in Timeline's context menus. For an Annotation, we want to add a menu item available in all context menus to create an `Annotation` with the clipboard's contents. To do this, a [TimelineAction](xref:UnityEditor.Timeline.Actions.TimelineAction) is needed, along with the [MenuEntry attribute](xref:UnityEditor.Timeline.Actions.MenuEntryAttribute). ``` c# [MenuEntry("Create Annotation from clipboard contents")] public class CreateAnnotationFromClipboardContents : TimelineAction { //... } ``` `MenuEntry` lets Timeline know that this action can be added in context menus. Classes inheriting from `TimelineAction` need to override two methods: `Execute` and `Validate`. #### Validate `Validate` is used to specify that the action's prerequisites are fulfilled. In the case of `CreateAnnotationFromClipboardContents`, the action is only valid if there actually is contents in the clipboard. `ActionValidity` is used to describe the validity state of an action: ``` c# public override ActionValidity Validate(ActionContext context) { if (!markers.All(marker => marker is AnnotationMarker)) return ActionValidity.NotApplicable; string buffer = EditorGUIUtility.systemCopyBuffer; return buffer.Length == 0 ? ActionValidity.Invalid : ActionValidity.Valid; } ``` * `ActionValidity.Valid` : The action can be executed. * `ActionValidity.Invalid` : The action cannot be executed given the current context and will appear grayed out in context menus. * `ActionValidity.NotApplicable` : The action does not apply to the current context and will not show up in menus. #### Execute `Execute` should run the code necessary to execute the action's purpose. ``` c# public override bool Execute(ActionContext context) { string buffer = EditorGUIUtility.systemCopyBuffer; TrackAsset track = context.tracks.FirstOrDefault(); if (buffer.Length != 0) { // Create the new annotation and add it to the track //... return true; } return false; } ``` The return value should specify if the execution succeeded or not. ### Marker Action It is also possible to write custom actions that apply only to markers, instead of all Timeline items. This is the purpose of the `MarkerEditor` class. It works just like `TimelineAction`, except that action applies to a list of markers. A shortcut can also be assigned to an action. A static method with the `TimelineShortcut` attribute is needed. `Invoker` can be used to easily execute a given action: ``` c# [TimelineShortcut("Replace annotation description with clipboard", KeyCode.G)] public static void InvokeAction() { Invoker.InvokeWithSelectedMarkers(); } ``` ## Notes ## Runtime considerations `AnnotationMarker` is available at runtime; it can be queried using, for example, `TrackAsset.GetMarkers()`. However, `AnnotationMarkerEditor` and custom actions are not available at runtime, since it depends on classes that are not part of the runtime assembly.