diff --git a/Directory.Build.props b/Directory.Build.props index f39a5396b..78a85242d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,15 +3,15 @@ full true - 0.9.6 - 0.9.6.0 - 0.9.6.0 + 0.9.7 + 0.9.7.0 + 0.9.7.0 Danielku15 CoderLine AlphaTab en alphaTab is a cross platform music notation and guitar tablature rendering library. - Copyright © 2019, Daniel Kuschny and Contributors + Copyright © 2020, Daniel Kuschny and Contributors MPL-2.0 https://www.alphatab.net https://github.com/CoderLine/alphaTab diff --git a/Documentation/input/reference/events/beatmousedown.cshtml b/Documentation/input/reference/events/beatmousedown.cshtml new file mode 100644 index 000000000..107b455e9 --- /dev/null +++ b/Documentation/input/reference/events/beatmousedown.cshtml @@ -0,0 +1,102 @@ +Title: BeatMouseDown +JsName: addBeatMouseDown();removeBeatMouseDown() +DomName: alphaTab.beatMouseDown +Category: Events - Player +Description: This event is fired whenever a the user presses the mouse button on a beat. +ShowInSideBar: false +Since: 0.9.7 +--- + +

Description

+

+ @Html.Raw(Model.String(DocsKeys.Description)) +

+ +

Types

+ + + + + + + + + + +
Action<Beat> .net
function(e) JavaScript
+ +

Parameters

+ + + + + + + + + + + + + + + + + + + + + +
ParametersTypeSummary
args allAlphaTab.Model.Beat + Beat on which the mouse was pressed. +
originalEvent jQueryMouseEvent + The original mouse event that lead to trigger of the beat event. For the DOM event it is stored in the event.originalEvent +
+ +

Example - C#

+ +
+
+var api = new AlphaTabApi(...);
+api.BeatMouseDown += beat => 
+{
+    StartSelectionOnBeat(args);
+};
+
+
+ + +

Example - JavaScript

+ +
+
+var api = new alphaTab.platform.javaScript.AlphaTabApi(document.querySelector('#alphaTab'));
+api.addBeatMouseDown(function(beat) {
+    startSelectionOnBeat(beat);
+});
+
+
+
+ +

Example - jQuery

+ +
+
+$('#alphaTab').on('alphaTab.beatMouseDown', function(e, beat) {
+    originalEvent.preventDefault();
+    startSelectionOnBeat(beat);
+});
+
+
+ +

Example - HTML

+ +
+
+document.querySelector('#alphaTab').addEventListener('alphaTab.beatMouseDown', function(e) {
+    var beat = e.detail;
+    e.originalEvent.preventDefault();
+    startSelectionOnBeat(beat);
+}, false);
+
+
\ No newline at end of file diff --git a/Documentation/input/reference/events/beatmousemove.cshtml b/Documentation/input/reference/events/beatmousemove.cshtml new file mode 100644 index 000000000..4b1087eef --- /dev/null +++ b/Documentation/input/reference/events/beatmousemove.cshtml @@ -0,0 +1,102 @@ +Title: BeatMouseMove +JsName: addBeatMouseMove();removeBeatMouseMove() +DomName: alphaTab.beatMouseMove +Category: Events - Player +Description: This event is fired whenever the user moves the mouse over a beat after the user already pressed the button on a beat. +ShowInSideBar: false +Since: 0.9.7 +--- + +

Description

+

+ @Html.Raw(Model.String(DocsKeys.Description)) +

+ +

Types

+ + + + + + + + + + +
Action<Beat> .net
function(e) JavaScript
+ +

Parameters

+ + + + + + + + + + + + + + + + + + + + + +
ParametersTypeSummary
args allAlphaTab.Model.Beat + Beat on which the mouse was hovered over during mouse down. +
originalEvent jQueryMouseEvent + The original mouse event that lead to trigger of the beat event. For the DOM event it is stored in the event.originalEvent +
+ +

Example - C#

+ +
+
+var api = new AlphaTabApi(...);
+api.BeatMouseMove += beat => 
+{
+    ExpandSelectionToBeat(beat);
+};
+
+
+ + +

Example - JavaScript

+ +
+
+var api = new alphaTab.platform.javaScript.AlphaTabApi(document.querySelector('#alphaTab'));
+api.addBeatMouseMove(function(beat) {
+    expandSelectionToBeat(beat);
+});
+
+
+
+ +

Example - jQuery

+ +
+
+$('#alphaTab').on('alphaTab.beatMouseMove', function(e, beat) {
+    originalEvent.preventDefault();
+    expandSelectionToBeat(beat);
+});
+
+
+ +

Example - HTML

+ +
+
+document.querySelector('#alphaTab').addEventListener('alphaTab.beatMouseMove', function(e) {
+    var beat = e.detail;
+    e.originalEvent.preventDefault();
+    expandSelectionToBeat(beat);
+}, false);
+
+
\ No newline at end of file diff --git a/Documentation/input/reference/events/beatmouseup.cshtml b/Documentation/input/reference/events/beatmouseup.cshtml new file mode 100644 index 000000000..7f9db4241 --- /dev/null +++ b/Documentation/input/reference/events/beatmouseup.cshtml @@ -0,0 +1,106 @@ +Title: BeatMouseUp +JsName: addBeatMouseUp();removeBeatMouseUp() +DomName: alphaTab.beatMouseUp +Category: Events - Player +Description: This event is fired whenever the user releases the mouse after a mouse press on a beat. + +ShowInSideBar: false +Since: 0.9.7 +--- + +

Description

+

+ @Html.Raw(Model.String(DocsKeys.Description)) + This event is fired regardless of whether the mouse was released on a beat. + The parameter is null if the mouse was released somewhere beside the beat. +

+ +

Types

+ + + + + + + + + + +
Action<Beat> .net
function(e) JavaScript
+ +

Parameters

+ + + + + + + + + + + + + + + + + + + + + +
ParametersTypeSummary
args allAlphaTab.Model.Beat + Beat on which the mouse was released over after mouse down. + Might be null if no beat could be found in near location. +
originalEvent jQueryMouseEvent + The original mouse event that lead to trigger of the beat event. For the DOM event it is stored in the event.originalEvent +
+ +

Example - C#

+ +
+
+var api = new AlphaTabApi(...);
+api.BeatMouseUp += beat => 
+{
+    HideSelection(beat);
+};
+
+
+ + +

Example - JavaScript

+ +
+
+var api = new alphaTab.platform.javaScript.AlphaTabApi(document.querySelector('#alphaTab'));
+api.addBeatMouseUp(function(beat) {
+    hideSelection(beat);
+});
+
+
+
+ +

Example - jQuery

+ +
+
+$('#alphaTab').on('alphaTab.beatMouseUp', function(e, beat) {
+    originalEvent.preventDefault();
+    hideSelection(beat);
+});
+
+
+ +

Example - HTML

+ +
+
+document.querySelector('#alphaTab').addEventListener('alphaTab.beatMouseUp', function(e) {
+    var beat = e.detail;
+    e.originalEvent.preventDefault();
+    hideSelection(beat);
+}, false);
+
+
\ No newline at end of file diff --git a/Documentation/input/reference/property/player-enableuserinteraction.cshtml b/Documentation/input/reference/property/player-enableuserinteraction.cshtml new file mode 100644 index 000000000..e33e2c4d3 --- /dev/null +++ b/Documentation/input/reference/property/player-enableuserinteraction.cshtml @@ -0,0 +1,31 @@ +Title: Player.EnableUserInteraction +JsName: player.enableUserInteraction +JsonName: player.enableUserInteraction +DataAttribute: data-player-enableuserinteraction +Category: Player +Description: Gets or sets whether the player should be enabled. +ShowInSideBar: false +Since: 0.9.6 +--- + +

Description

+

+ This setting configures whether alphaTab provides the default user interaction features like selection of the playback range and "seek on click". + By default users can select the desired playback range with the mouse and also jump to individual beats by click. This behavior can be contolled w1ith this setting. +

+ +@Html.Partial("_PropertyDescription", Model) + +

Types

+ + + + + + + +
bool
+ +

Default Value

+ +true \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/ManagedUiFacade.cs b/Source/AlphaTab.CSharp/Platform/CSharp/ManagedUiFacade.cs index 26171c332..e5cc3c00b 100644 --- a/Source/AlphaTab.CSharp/Platform/CSharp/ManagedUiFacade.cs +++ b/Source/AlphaTab.CSharp/Platform/CSharp/ManagedUiFacade.cs @@ -61,7 +61,7 @@ public IAlphaSynth CreateWorkerPlayer() public abstract event Action RootContainerBecameVisible; public abstract void Destroy(); public abstract IContainer CreateCanvasElement(); - public abstract void TriggerEvent(IContainer container, string eventName, object details = null); + public abstract void TriggerEvent(IContainer container, string eventName, object details = null, IMouseEventArgs originalEvent = null); public virtual void InitialRender() { diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/WinFormsUiFacade.cs b/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/WinFormsUiFacade.cs index a93e70996..541baae02 100644 --- a/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/WinFormsUiFacade.cs +++ b/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/WinFormsUiFacade.cs @@ -112,7 +112,7 @@ public override IContainer CreateCanvasElement() return new ControlContainer(_layoutPanel); } - public override void TriggerEvent(IContainer container, string eventName, object details = null) + public override void TriggerEvent(IContainer container, string eventName, object details = null, IMouseEventArgs originalEvent = null) { } diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/WpfUiFacade.cs b/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/WpfUiFacade.cs index e4ea0c4ee..1473204a6 100644 --- a/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/WpfUiFacade.cs +++ b/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/WpfUiFacade.cs @@ -118,7 +118,7 @@ public override IContainer CreateCanvasElement() return new FrameworkElementContainer(canvas); } - public override void TriggerEvent(IContainer container, string eventName, object details = null) + public override void TriggerEvent(IContainer container, string eventName, object details = null, IMouseEventArgs originalEvent = null) { } diff --git a/Source/AlphaTab.JavaScript/UI/BrowserMouseEventArgs.cs b/Source/AlphaTab.JavaScript/UI/BrowserMouseEventArgs.cs index 5c015a873..f77df3154 100644 --- a/Source/AlphaTab.JavaScript/UI/BrowserMouseEventArgs.cs +++ b/Source/AlphaTab.JavaScript/UI/BrowserMouseEventArgs.cs @@ -4,15 +4,16 @@ namespace AlphaTab.UI { internal class BrowserMouseEventArgs : IMouseEventArgs { - private readonly MouseEvent _e; - public bool IsLeftMouseButton => _e.Button == 0; + public MouseEvent MouseEvent { get; } + + public bool IsLeftMouseButton => MouseEvent.Button == 0; public float GetX(IContainer relativeTo) { var relativeToElement = ((HtmlElementContainer)relativeTo).Element; var bounds = relativeToElement.GetBoundingClientRect(); float left = bounds.Left + relativeToElement.OwnerDocument.DefaultView.PageXOffset; - return _e.PageX - left; + return MouseEvent.PageX - left; } public float GetY(IContainer relativeTo) @@ -20,17 +21,17 @@ public float GetY(IContainer relativeTo) var relativeToElement = ((HtmlElementContainer)relativeTo).Element; var bounds = relativeToElement.GetBoundingClientRect(); float top = bounds.Top + relativeToElement.OwnerDocument.DefaultView.PageYOffset; - return _e.PageY - top; + return MouseEvent.PageY - top; } public void PreventDefault() { - _e.PreventDefault(); + MouseEvent.PreventDefault(); } public BrowserMouseEventArgs(MouseEvent e) { - _e = e; + MouseEvent = e; } } } diff --git a/Source/AlphaTab.JavaScript/UI/BrowserUiFacade.cs b/Source/AlphaTab.JavaScript/UI/BrowserUiFacade.cs index 78bda970b..5da1dbf93 100644 --- a/Source/AlphaTab.JavaScript/UI/BrowserUiFacade.cs +++ b/Source/AlphaTab.JavaScript/UI/BrowserUiFacade.cs @@ -246,19 +246,32 @@ public IContainer CreateCanvasElement() return new HtmlElementContainer(canvasElement); } - public void TriggerEvent(IContainer container, string name, object details = null) + public void TriggerEvent(IContainer container, string name, object details = null, IMouseEventArgs originalEvent = null) { var element = ((HtmlElementContainer)container).Element; name = "alphaTab." + name; dynamic e = Browser.Document.CreateEvent("CustomEvent"); + var originalMouseEvent = (originalEvent != null) ? ((BrowserMouseEventArgs)originalEvent).MouseEvent : null; e.initCustomEvent(name, false, false, details); + if (originalMouseEvent != null) + { + e.originalEvent = originalMouseEvent; + } element.DispatchEvent(e); if (Platform.Platform.JsonExists(Browser.Window, "jQuery")) { var jquery = Browser.Window.Member("jQuery"); - jquery(element).trigger(name, details); + + var args = new FastList(); + args.Add(details); + if (originalMouseEvent != null) + { + args.Add(originalMouseEvent); + } + + jquery(element).trigger(name, args.ToArray()); } } diff --git a/Source/AlphaTab/AlphaTabApi.cs b/Source/AlphaTab/AlphaTabApi.cs index 3bec2f0c8..77ce515a7 100644 --- a/Source/AlphaTab/AlphaTabApi.cs +++ b/Source/AlphaTab/AlphaTabApi.cs @@ -917,7 +917,11 @@ private void CursorUpdateTick(int tick, bool stop = false) var beat = cache.FindBeat(tracks, tick); if (beat != null) { - CursorUpdateBeat(beat.CurrentBeat, beat.NextBeat, beat.Duration, stop, beat.BeatsToHighlight); + CursorUpdateBeat(beat.CurrentBeat, + beat.NextBeat, + beat.Duration, + stop, + beat.BeatsToHighlight); } } } @@ -1026,7 +1030,7 @@ private void CursorUpdateBeat( }); } - if (!_selecting && Settings.Player.ScrollMode != ScrollMode.Off) + if (!_beatMouseDown && Settings.Player.ScrollMode != ScrollMode.Off) { //// calculate position of whole music wheet within the scroll parent var scrollElement = UiFacade.GetScrollContainer(); @@ -1040,7 +1044,8 @@ private void CursorUpdateBeat( switch (mode) { case ScrollMode.Continuous: - var y = (int)(elementOffset.Y + barBoundings.RealBounds.Y + Settings.Player.ScrollOffsetY); + var y = (int)(elementOffset.Y + barBoundings.RealBounds.Y + + Settings.Player.ScrollOffsetY); if (y != _lastScroll) { _lastScroll = y; @@ -1115,58 +1120,75 @@ private void OnPlayedBeatChanged(Beat beat) #region Selection - private bool _selecting; + private bool _beatMouseDown; private SelectionInfo _selectionStart; private SelectionInfo _selectionEnd; - private void SetupClickHandling() + + /// + /// This event is fired whenever a the user presses the mouse button on a beat. + /// + public event Action BeatMouseDown; + + + /// + /// This event is fired whenever the user moves the mouse over a beat after + /// the user already pressed the button on a beat. + /// + public event Action BeatMouseMove; + + + /// + /// This event is fired whenever the user releases the mouse after a mouse press + /// on a beat. This event is fired regardless of whether the mouse was released on a beat. + /// The parameter is null if the mouse was released somewhere beside the beat. + /// + public event Action BeatMouseUp; + + private void OnBeatMouseDown(IMouseEventArgs originalEvent, Beat beat) { - CanvasElement.MouseDown += e => + if (Settings.Player.EnablePlayer + && Settings.Player.EnableCursor + && Settings.Player.EnableUserInteraction) { - if (!e.IsLeftMouseButton || !Settings.Player.EnablePlayer || !Settings.Player.EnableCursor) - { - return; - } + _selectionStart = new SelectionInfo(beat); + _selectionEnd = null; + } - e.PreventDefault(); + _beatMouseDown = true; - var relX = e.GetX(CanvasElement); - var relY = e.GetY(CanvasElement); - var beat = Renderer.BoundsLookup.GetBeatAtPos(relX, relY); - if (beat != null) - { - _selectionStart = new SelectionInfo(beat); - _selectionEnd = null; - _selecting = true; - } - }; - - CanvasElement.MouseMove += e => + var handler = BeatMouseDown; + if (handler != null) { - if (!_selecting || !Settings.Player.EnablePlayer || !Settings.Player.EnableCursor) - { - return; - } + handler(beat); + } + UiFacade.TriggerEvent(Container, "beatMouseDown", beat, originalEvent); + } - var relX = e.GetX(CanvasElement); - var relY = e.GetY(CanvasElement); - var beat = Renderer.BoundsLookup.GetBeatAtPos(relX, relY); - if (beat != null && (_selectionEnd == null || _selectionEnd.Beat != beat)) + private void OnBeatMouseMove(IMouseEventArgs originalEvent, Beat beat) + { + if (Settings.Player.EnableUserInteraction) + { + if (_selectionEnd == null || _selectionEnd.Beat != beat) { _selectionEnd = new SelectionInfo(beat); CursorSelectRange(_selectionStart, _selectionEnd); } - }; + } - CanvasElement.MouseUp += e => - { - if (!_selecting || !Settings.Player.EnablePlayer || !Settings.Player.EnableCursor) - { - return; - } - e.PreventDefault(); + var handler = BeatMouseMove; + if (handler != null) + { + handler(beat); + } + UiFacade.TriggerEvent(Container, "beatMouseMove", beat, originalEvent); + } + private void OnBeatMouseUp(IMouseEventArgs originalEvent, Beat beat) + { + if (Settings.Player.EnableUserInteraction) + { // for the selection ensure start < end if (_selectionEnd != null) { @@ -1208,16 +1230,86 @@ private void SetupClickHandling() CursorSelectRange(_selectionStart, _selectionEnd); } } + } + + var handler = BeatMouseUp; + if (handler != null) + { + handler(beat); + } + UiFacade.TriggerEvent(Container, "beatMouseUp", beat, originalEvent); + + _beatMouseDown = false; + } + + + private void SetupClickHandling() + { + CanvasElement.MouseDown += e => + { + if (!e.IsLeftMouseButton) + { + return; + } + + if (Settings.Player.EnableUserInteraction) + { + e.PreventDefault(); + } + + var relX = e.GetX(CanvasElement); + var relY = e.GetY(CanvasElement); + var beat = Renderer.BoundsLookup.GetBeatAtPos(relX, relY); + if (beat != null) + { + OnBeatMouseDown(e, beat); + } + }; + + CanvasElement.MouseMove += e => + { + if (!_beatMouseDown) + { + return; + } - _selecting = false; + var relX = e.GetX(CanvasElement); + var relY = e.GetY(CanvasElement); + var beat = Renderer.BoundsLookup.GetBeatAtPos(relX, relY); + if (beat != null) + { + OnBeatMouseMove(e, beat); + } + }; + + CanvasElement.MouseUp += e => + { + if (!_beatMouseDown) + { + return; + } + + if (Settings.Player.EnableUserInteraction) + { + e.PreventDefault(); + } + + var relX = e.GetX(CanvasElement); + var relY = e.GetY(CanvasElement); + var beat = Renderer.BoundsLookup.GetBeatAtPos(relX, relY); + OnBeatMouseUp(e, beat); }; Renderer.PostRenderFinished += () => { - if (_selectionStart == null || !Settings.Player.EnablePlayer || !Settings.Player.EnableCursor) + if (_selectionStart == null + || !Settings.Player.EnablePlayer + || !Settings.Player.EnableCursor + || !Settings.Player.EnableUserInteraction) { return; } + CursorSelectRange(_selectionStart, _selectionEnd); }; } diff --git a/Source/AlphaTab/Settings.cs b/Source/AlphaTab/Settings.cs index a1aad4b4b..20e90562c 100644 --- a/Source/AlphaTab/Settings.cs +++ b/Source/AlphaTab/Settings.cs @@ -532,6 +532,13 @@ public partial class PlayerSettings [JsonName("enableCursor")] public bool EnableCursor { get; set; } = true; + /// + /// Gets or sets alphaTab should provide user interaction features to + /// select playback ranges and jump to the playback position by click (aka. seeking). + /// + [JsonName("enableUserInteraction")] + public bool EnableUserInteraction { get; set; } = true; + /// /// Gets or sets the X-offset to add when scrolling. /// diff --git a/Source/AlphaTab/UI/IUiFacade.cs b/Source/AlphaTab/UI/IUiFacade.cs index 872595735..1b5b8572f 100644 --- a/Source/AlphaTab/UI/IUiFacade.cs +++ b/Source/AlphaTab/UI/IUiFacade.cs @@ -67,7 +67,8 @@ public interface IUiFacade /// The element on which the event should be triggered. /// The event that should be triggered. /// The object holding the details about the event. - void TriggerEvent(IContainer container, string eventName, object details = null); + /// The original event related to this custom event. + void TriggerEvent(IContainer container, string eventName, object details = null, IMouseEventArgs originalEvent = null); /// /// Tells the UI layer to do the initial rendering. diff --git a/appveyor.yml b/appveyor.yml index f58b5002a..ddd752266 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 0.9.6.{build} +version: 0.9.7.{build} # branches to build branches: only: