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
+
+
+
+
+ Parameters
+ Type
+ Summary
+
+
+
+
+ args all
+ AlphaTab.Model.Beat
+
+ Beat on which the mouse was pressed.
+
+
+
+ originalEvent jQuery
+ MouseEvent
+
+ 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
+
+
+
+
+ Parameters
+ Type
+ Summary
+
+
+
+
+ args all
+ AlphaTab.Model.Beat
+
+ Beat on which the mouse was hovered over during mouse down.
+
+
+
+ originalEvent jQuery
+ MouseEvent
+
+ 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
+
+
+
+
+ Parameters
+ Type
+ Summary
+
+
+
+
+ args all
+ AlphaTab.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 jQuery
+ MouseEvent
+
+ 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
+
+
+
+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: