|
|
@ -14,14 +14,17 @@ namespace Avalonia.Input |
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Represents a mouse device.
|
|
|
/// Represents a mouse device.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
public class MouseDevice : IMouseDevice |
|
|
public class MouseDevice : IMouseDevice, IPointer |
|
|
{ |
|
|
{ |
|
|
private int _clickCount; |
|
|
private int _clickCount; |
|
|
private Rect _lastClickRect; |
|
|
private Rect _lastClickRect; |
|
|
private ulong _lastClickTime; |
|
|
private ulong _lastClickTime; |
|
|
private IInputElement _captured; |
|
|
private IInputElement _captured; |
|
|
private IDisposable _capturedSubscription; |
|
|
private IDisposable _capturedSubscription; |
|
|
|
|
|
|
|
|
|
|
|
PointerType IPointer.Type => PointerType.Mouse; |
|
|
|
|
|
bool IPointer.IsPrimary => true; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Gets the control that is currently capturing by the mouse, if any.
|
|
|
/// Gets the control that is currently capturing by the mouse, if any.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
@ -117,6 +120,18 @@ namespace Avalonia.Input |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int ButtonCount(PointerPointProperties props) |
|
|
|
|
|
{ |
|
|
|
|
|
var rv = 0; |
|
|
|
|
|
if (props.IsLeftButtonPressed) |
|
|
|
|
|
rv++; |
|
|
|
|
|
if (props.IsMiddleButtonPressed) |
|
|
|
|
|
rv++; |
|
|
|
|
|
if (props.IsRightButtonPressed) |
|
|
|
|
|
rv++; |
|
|
|
|
|
return rv; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
private void ProcessRawEvent(RawMouseEventArgs e) |
|
|
private void ProcessRawEvent(RawMouseEventArgs e) |
|
|
{ |
|
|
{ |
|
|
Contract.Requires<ArgumentNullException>(e != null); |
|
|
Contract.Requires<ArgumentNullException>(e != null); |
|
|
@ -124,7 +139,7 @@ namespace Avalonia.Input |
|
|
var mouse = (IMouseDevice)e.Device; |
|
|
var mouse = (IMouseDevice)e.Device; |
|
|
|
|
|
|
|
|
Position = e.Root.PointToScreen(e.Position); |
|
|
Position = e.Root.PointToScreen(e.Position); |
|
|
|
|
|
var props = CreateProperties(e); |
|
|
switch (e.Type) |
|
|
switch (e.Type) |
|
|
{ |
|
|
{ |
|
|
case RawMouseEventType.LeaveWindow: |
|
|
case RawMouseEventType.LeaveWindow: |
|
|
@ -133,26 +148,25 @@ namespace Avalonia.Input |
|
|
case RawMouseEventType.LeftButtonDown: |
|
|
case RawMouseEventType.LeftButtonDown: |
|
|
case RawMouseEventType.RightButtonDown: |
|
|
case RawMouseEventType.RightButtonDown: |
|
|
case RawMouseEventType.MiddleButtonDown: |
|
|
case RawMouseEventType.MiddleButtonDown: |
|
|
e.Handled = MouseDown(mouse, e.Timestamp, e.Root, e.Position, |
|
|
if (ButtonCount(props) > 1) |
|
|
e.Type == RawMouseEventType.LeftButtonDown |
|
|
e.Handled = MouseMove(mouse, e.Root, e.Position, props, e.InputModifiers); |
|
|
? MouseButton.Left |
|
|
else |
|
|
: e.Type == RawMouseEventType.RightButtonDown ? MouseButton.Right : MouseButton.Middle, |
|
|
e.Handled = MouseDown(mouse, e.Timestamp, e.Root, e.Position, |
|
|
e.InputModifiers); |
|
|
props, e.InputModifiers); |
|
|
break; |
|
|
break; |
|
|
case RawMouseEventType.LeftButtonUp: |
|
|
case RawMouseEventType.LeftButtonUp: |
|
|
case RawMouseEventType.RightButtonUp: |
|
|
case RawMouseEventType.RightButtonUp: |
|
|
case RawMouseEventType.MiddleButtonUp: |
|
|
case RawMouseEventType.MiddleButtonUp: |
|
|
e.Handled = MouseUp(mouse, e.Root, e.Position, |
|
|
if (ButtonCount(props) != 0) |
|
|
e.Type == RawMouseEventType.LeftButtonUp |
|
|
e.Handled = MouseMove(mouse, e.Root, e.Position, props, e.InputModifiers); |
|
|
? MouseButton.Left |
|
|
else |
|
|
: e.Type == RawMouseEventType.RightButtonUp ? MouseButton.Right : MouseButton.Middle, |
|
|
e.Handled = MouseUp(mouse, e.Root, e.Position, props, e.InputModifiers); |
|
|
e.InputModifiers); |
|
|
|
|
|
break; |
|
|
break; |
|
|
case RawMouseEventType.Move: |
|
|
case RawMouseEventType.Move: |
|
|
e.Handled = MouseMove(mouse, e.Root, e.Position, e.InputModifiers); |
|
|
e.Handled = MouseMove(mouse, e.Root, e.Position, props, e.InputModifiers); |
|
|
break; |
|
|
break; |
|
|
case RawMouseEventType.Wheel: |
|
|
case RawMouseEventType.Wheel: |
|
|
e.Handled = MouseWheel(mouse, e.Root, e.Position, ((RawMouseWheelEventArgs)e).Delta, e.InputModifiers); |
|
|
e.Handled = MouseWheel(mouse, e.Root, e.Position, props, ((RawMouseWheelEventArgs)e).Delta, e.InputModifiers); |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -165,7 +179,30 @@ namespace Avalonia.Input |
|
|
ClearPointerOver(this, root, inputModifiers); |
|
|
ClearPointerOver(this, root, inputModifiers); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private bool MouseDown(IMouseDevice device, ulong timestamp, IInputElement root, Point p, MouseButton button, InputModifiers inputModifiers) |
|
|
|
|
|
|
|
|
PointerPointProperties CreateProperties(RawMouseEventArgs args) |
|
|
|
|
|
{ |
|
|
|
|
|
var rv = new PointerPointProperties(args.InputModifiers); |
|
|
|
|
|
|
|
|
|
|
|
if (args.Type == RawMouseEventType.LeftButtonDown) |
|
|
|
|
|
rv.IsLeftButtonPressed = true; |
|
|
|
|
|
if (args.Type == RawMouseEventType.MiddleButtonDown) |
|
|
|
|
|
rv.IsMiddleButtonPressed = true; |
|
|
|
|
|
if (args.Type == RawMouseEventType.RightButtonDown) |
|
|
|
|
|
rv.IsRightButtonPressed = true; |
|
|
|
|
|
if (args.Type == RawMouseEventType.LeftButtonUp) |
|
|
|
|
|
rv.IsLeftButtonPressed = false; |
|
|
|
|
|
if (args.Type == RawMouseEventType.MiddleButtonUp) |
|
|
|
|
|
rv.IsMiddleButtonPressed = false; |
|
|
|
|
|
if (args.Type == RawMouseEventType.RightButtonDown) |
|
|
|
|
|
rv.IsRightButtonPressed = false; |
|
|
|
|
|
return rv; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private MouseButton _lastMouseDownButton; |
|
|
|
|
|
private bool MouseDown(IMouseDevice device, ulong timestamp, IInputElement root, Point p, |
|
|
|
|
|
PointerPointProperties properties, |
|
|
|
|
|
InputModifiers inputModifiers) |
|
|
{ |
|
|
{ |
|
|
Contract.Requires<ArgumentNullException>(device != null); |
|
|
Contract.Requires<ArgumentNullException>(device != null); |
|
|
Contract.Requires<ArgumentNullException>(root != null); |
|
|
Contract.Requires<ArgumentNullException>(root != null); |
|
|
@ -190,16 +227,8 @@ namespace Avalonia.Input |
|
|
_lastClickTime = timestamp; |
|
|
_lastClickTime = timestamp; |
|
|
_lastClickRect = new Rect(p, new Size()) |
|
|
_lastClickRect = new Rect(p, new Size()) |
|
|
.Inflate(new Thickness(settings.DoubleClickSize.Width / 2, settings.DoubleClickSize.Height / 2)); |
|
|
.Inflate(new Thickness(settings.DoubleClickSize.Width / 2, settings.DoubleClickSize.Height / 2)); |
|
|
|
|
|
_lastMouseDownButton = properties.GetObsoleteMouseButton(); |
|
|
var e = new PointerPressedEventArgs |
|
|
var e = new PointerPressedEventArgs(source, this, root, p, properties, inputModifiers, _clickCount); |
|
|
{ |
|
|
|
|
|
Device = this, |
|
|
|
|
|
RoutedEvent = InputElement.PointerPressedEvent, |
|
|
|
|
|
Source = source, |
|
|
|
|
|
ClickCount = _clickCount, |
|
|
|
|
|
MouseButton = button, |
|
|
|
|
|
InputModifiers = inputModifiers |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
source.RaiseEvent(e); |
|
|
source.RaiseEvent(e); |
|
|
return e.Handled; |
|
|
return e.Handled; |
|
|
@ -209,7 +238,8 @@ namespace Avalonia.Input |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private bool MouseMove(IMouseDevice device, IInputRoot root, Point p, InputModifiers inputModifiers) |
|
|
private bool MouseMove(IMouseDevice device, IInputRoot root, Point p, PointerPointProperties properties, |
|
|
|
|
|
InputModifiers inputModifiers) |
|
|
{ |
|
|
{ |
|
|
Contract.Requires<ArgumentNullException>(device != null); |
|
|
Contract.Requires<ArgumentNullException>(device != null); |
|
|
Contract.Requires<ArgumentNullException>(root != null); |
|
|
Contract.Requires<ArgumentNullException>(root != null); |
|
|
@ -226,19 +256,15 @@ namespace Avalonia.Input |
|
|
source = Captured; |
|
|
source = Captured; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
var e = new PointerEventArgs |
|
|
var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, this, root, |
|
|
{ |
|
|
p, properties, inputModifiers); |
|
|
Device = this, |
|
|
|
|
|
RoutedEvent = InputElement.PointerMovedEvent, |
|
|
|
|
|
Source = source, |
|
|
|
|
|
InputModifiers = inputModifiers |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
source?.RaiseEvent(e); |
|
|
source?.RaiseEvent(e); |
|
|
return e.Handled; |
|
|
return e.Handled; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private bool MouseUp(IMouseDevice device, IInputRoot root, Point p, MouseButton button, InputModifiers inputModifiers) |
|
|
private bool MouseUp(IMouseDevice device, IInputRoot root, Point p, PointerPointProperties props, |
|
|
|
|
|
InputModifiers inputModifiers) |
|
|
{ |
|
|
{ |
|
|
Contract.Requires<ArgumentNullException>(device != null); |
|
|
Contract.Requires<ArgumentNullException>(device != null); |
|
|
Contract.Requires<ArgumentNullException>(root != null); |
|
|
Contract.Requires<ArgumentNullException>(root != null); |
|
|
@ -248,14 +274,7 @@ namespace Avalonia.Input |
|
|
if (hit != null) |
|
|
if (hit != null) |
|
|
{ |
|
|
{ |
|
|
var source = GetSource(hit); |
|
|
var source = GetSource(hit); |
|
|
var e = new PointerReleasedEventArgs |
|
|
var e = new PointerReleasedEventArgs(source, this, root, p, props, inputModifiers, _lastMouseDownButton); |
|
|
{ |
|
|
|
|
|
Device = this, |
|
|
|
|
|
RoutedEvent = InputElement.PointerReleasedEvent, |
|
|
|
|
|
Source = source, |
|
|
|
|
|
MouseButton = button, |
|
|
|
|
|
InputModifiers = inputModifiers |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
source?.RaiseEvent(e); |
|
|
source?.RaiseEvent(e); |
|
|
return e.Handled; |
|
|
return e.Handled; |
|
|
@ -264,7 +283,9 @@ namespace Avalonia.Input |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private bool MouseWheel(IMouseDevice device, IInputRoot root, Point p, Vector delta, InputModifiers inputModifiers) |
|
|
private bool MouseWheel(IMouseDevice device, IInputRoot root, Point p, |
|
|
|
|
|
PointerPointProperties props, |
|
|
|
|
|
Vector delta, InputModifiers inputModifiers) |
|
|
{ |
|
|
{ |
|
|
Contract.Requires<ArgumentNullException>(device != null); |
|
|
Contract.Requires<ArgumentNullException>(device != null); |
|
|
Contract.Requires<ArgumentNullException>(root != null); |
|
|
Contract.Requires<ArgumentNullException>(root != null); |
|
|
@ -274,14 +295,7 @@ namespace Avalonia.Input |
|
|
if (hit != null) |
|
|
if (hit != null) |
|
|
{ |
|
|
{ |
|
|
var source = GetSource(hit); |
|
|
var source = GetSource(hit); |
|
|
var e = new PointerWheelEventArgs |
|
|
var e = new PointerWheelEventArgs(source, this, root, p, props, inputModifiers, delta); |
|
|
{ |
|
|
|
|
|
Device = this, |
|
|
|
|
|
RoutedEvent = InputElement.PointerWheelChangedEvent, |
|
|
|
|
|
Source = source, |
|
|
|
|
|
Delta = delta, |
|
|
|
|
|
InputModifiers = inputModifiers |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
source?.RaiseEvent(e); |
|
|
source?.RaiseEvent(e); |
|
|
return e.Handled; |
|
|
return e.Handled; |
|
|
@ -306,18 +320,19 @@ namespace Avalonia.Input |
|
|
return Captured ?? root.InputHitTest(p); |
|
|
return Captured ?? root.InputHitTest(p); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
PointerEventArgs CreateSimpleEvent(RoutedEvent ev, IInteractive source, InputModifiers inputModifiers) |
|
|
|
|
|
{ |
|
|
|
|
|
return new PointerEventArgs(ev, source, this, null, default, |
|
|
|
|
|
new PointerPointProperties(inputModifiers), inputModifiers); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
private void ClearPointerOver(IPointerDevice device, IInputRoot root, InputModifiers inputModifiers) |
|
|
private void ClearPointerOver(IPointerDevice device, IInputRoot root, InputModifiers inputModifiers) |
|
|
{ |
|
|
{ |
|
|
Contract.Requires<ArgumentNullException>(device != null); |
|
|
Contract.Requires<ArgumentNullException>(device != null); |
|
|
Contract.Requires<ArgumentNullException>(root != null); |
|
|
Contract.Requires<ArgumentNullException>(root != null); |
|
|
|
|
|
|
|
|
var element = root.PointerOverElement; |
|
|
var element = root.PointerOverElement; |
|
|
var e = new PointerEventArgs |
|
|
var e = CreateSimpleEvent(InputElement.PointerLeaveEvent, element, inputModifiers); |
|
|
{ |
|
|
|
|
|
RoutedEvent = InputElement.PointerLeaveEvent, |
|
|
|
|
|
Device = device, |
|
|
|
|
|
InputModifiers = inputModifiers |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
if (element!=null && !element.IsAttachedToVisualTree) |
|
|
if (element!=null && !element.IsAttachedToVisualTree) |
|
|
{ |
|
|
{ |
|
|
@ -384,7 +399,6 @@ namespace Avalonia.Input |
|
|
|
|
|
|
|
|
IInputElement branch = null; |
|
|
IInputElement branch = null; |
|
|
|
|
|
|
|
|
var e = new PointerEventArgs { Device = device, InputModifiers = inputModifiers }; |
|
|
|
|
|
var el = element; |
|
|
var el = element; |
|
|
|
|
|
|
|
|
while (el != null) |
|
|
while (el != null) |
|
|
@ -398,8 +412,8 @@ namespace Avalonia.Input |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
el = root.PointerOverElement; |
|
|
el = root.PointerOverElement; |
|
|
e.RoutedEvent = InputElement.PointerLeaveEvent; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var e = CreateSimpleEvent(InputElement.PointerLeaveEvent, el, inputModifiers); |
|
|
if (el!=null && branch!=null && !el.IsAttachedToVisualTree) |
|
|
if (el!=null && branch!=null && !el.IsAttachedToVisualTree) |
|
|
{ |
|
|
{ |
|
|
ClearChildrenPointerOver(e,branch,false); |
|
|
ClearChildrenPointerOver(e,branch,false); |
|
|
|