Browse Source

Added mouse clicks/events

Not sure the styling system is going to work at this point...
pull/4/head
grokys 12 years ago
parent
commit
22859b2795
  1. 54
      Perspex.Windows/Window.cs
  2. 13
      Perspex/Controls/Button.cs
  3. 38
      Perspex/Controls/Control.cs
  4. 14
      Perspex/Input/MouseEventArgs.cs
  5. 106
      Perspex/Interactive.cs
  6. 5
      Perspex/Perspex.csproj
  7. 87
      Perspex/RoutedEvent.cs
  8. 36
      Perspex/RoutedEventArgs.cs
  9. 10
      Perspex/Themes/Default/ButtonStyle.cs
  10. 65
      Perspex/VisualExtensions.cs

54
Perspex.Windows/Window.cs

@ -9,8 +9,10 @@ namespace Perspex.Windows
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using Perspex.Controls;
using Perspex.Input;
using Perspex.Layout;
using Perspex.Windows.Interop;
using Perspex.Windows.Threading;
@ -132,6 +134,26 @@ namespace Perspex.Windows
}
}
private void MouseDown(Visual visual, Point p)
{
Visual hit = visual.GetVisualAt(p);
if (hit != null)
{
Interactive source = (hit as Interactive) ?? hit.GetVisualAncestor<Interactive>();
if (source != null)
{
source.RaiseEvent(new MouseEventArgs
{
RoutedEvent = Control.MouseLeftButtonDownEvent,
OriginalSource = source,
Source = source,
});
}
}
}
private void MouseMove(Visual visual, Point p)
{
Control control = visual as Control;
@ -147,6 +169,26 @@ namespace Perspex.Windows
}
}
private void MouseUp(Visual visual, Point p)
{
Visual hit = visual.GetVisualAt(p);
if (hit != null)
{
Interactive source = (hit as Interactive) ?? hit.GetVisualAncestor<Interactive>();
if (source != null)
{
source.RaiseEvent(new MouseEventArgs
{
RoutedEvent = Control.MouseLeftButtonUpEvent,
OriginalSource = source,
Source = source,
});
}
}
}
[SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")]
private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
@ -164,13 +206,13 @@ namespace Perspex.Windows
//// KeyInterop.KeyFromVirtualKey((int)wParam)));
//// break;
////case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN:
//// InputManager.Current.ProcessInput(new RawMouseEventArgs(mouse, RawMouseEventType.LeftButtonDown));
//// break;
case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN:
this.MouseDown(this, new Point((uint)lParam & 0xffff, (uint)lParam >> 16));
break;
////case UnmanagedMethods.WindowsMessage.WM_LBUTTONUP:
//// InputManager.Current.ProcessInput(new RawMouseEventArgs(mouse, RawMouseEventType.LeftButtonUp));
//// break;
case UnmanagedMethods.WindowsMessage.WM_LBUTTONUP:
this.MouseUp(this, new Point((uint)lParam & 0xffff, (uint)lParam >> 16));
break;
case UnmanagedMethods.WindowsMessage.WM_MOUSEMOVE:
this.MouseMove(this, new Point((uint)lParam & 0xffff, (uint)lParam >> 16));

13
Perspex/Controls/Button.cs

@ -10,6 +10,19 @@ namespace Perspex.Controls
public class Button : ContentControl
{
public Button()
{
this.GetObservable(MouseLeftButtonDownEvent).Subscribe(e =>
{
this.Classes.Add(":pressed");
});
this.GetObservable(MouseLeftButtonUpEvent).Subscribe(e =>
{
this.Classes.Remove(":pressed");
});
}
protected override Visual DefaultTemplate()
{
Border border = new Border();

38
Perspex/Controls/Control.cs

@ -11,6 +11,7 @@ namespace Perspex.Controls
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Reactive.Linq;
using Perspex.Input;
using Perspex.Layout;
using Perspex.Media;
@ -30,7 +31,7 @@ namespace Perspex.Controls
Bottom,
}
public abstract class Control : Visual, ILayoutable
public abstract class Control : Interactive, ILayoutable
{
public static readonly ReadOnlyPerspexProperty<Control> ParentProperty =
new ReadOnlyPerspexProperty<Control>(ParentPropertyRW);
@ -59,6 +60,12 @@ namespace Perspex.Controls
public static readonly PerspexProperty<VerticalAlignment> VerticalAlignmentProperty =
PerspexProperty.Register<Control, VerticalAlignment>("VerticalAlignment");
public static readonly RoutedEvent<MouseEventArgs> MouseLeftButtonDownEvent =
RoutedEvent.Register<Control, MouseEventArgs>("MouseLeftButtonDown", RoutingStrategy.Bubble);
public static readonly RoutedEvent<MouseEventArgs> MouseLeftButtonUpEvent =
RoutedEvent.Register<Control, MouseEventArgs>("MouseLeftButtonUp", RoutingStrategy.Bubble);
internal static readonly PerspexProperty<Control> ParentPropertyRW =
PerspexProperty.Register<Control, Control>("Parent");
@ -85,6 +92,35 @@ namespace Perspex.Controls
this.GetObservable(BackgroundProperty).Skip(1).Subscribe(_ => this.InvalidateMeasure());
}
public event EventHandler<MouseEventArgs> MouseLeftButtonDown
{
add
{
Contract.Requires<NullReferenceException>(value != null);
this.AddHandler(MouseLeftButtonDownEvent, value);
}
remove
{
Contract.Requires<NullReferenceException>(value != null);
this.RemoveHandler(MouseLeftButtonDownEvent, value);
}
}
public event EventHandler<MouseEventArgs> MouseLeftButtonUp
{
add
{
Contract.Requires<NullReferenceException>(value != null);
this.AddHandler(MouseLeftButtonUpEvent, value);
}
remove
{
Contract.Requires<NullReferenceException>(value != null);
this.RemoveHandler(MouseLeftButtonUpEvent, value);
}
}
public Brush Background
{
get { return this.GetValue(BackgroundProperty); }

14
Perspex/Input/MouseEventArgs.cs

@ -0,0 +1,14 @@
// -----------------------------------------------------------------------
// <copyright file="MouseEventArgs.cs" company="Steven Kirk">
// Copyright 2013 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Input
{
using System;
public class MouseEventArgs : RoutedEventArgs
{
}
}

106
Perspex/Interactive.cs

@ -0,0 +1,106 @@
// -----------------------------------------------------------------------
// <copyright file="Interactive.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex
{
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Reactive;
using System.Reactive.Linq;
public class Interactive : Visual
{
private Dictionary<RoutedEvent, List<Delegate>> eventHandlers = new Dictionary<RoutedEvent, List<Delegate>>();
public void AddHandler(RoutedEvent routedEvent, Delegate handler)
{
Contract.Requires<NullReferenceException>(routedEvent != null);
Contract.Requires<NullReferenceException>(handler != null);
List<Delegate> delegates;
if (!this.eventHandlers.TryGetValue(routedEvent, out delegates))
{
delegates = new List<Delegate>();
this.eventHandlers.Add(routedEvent, delegates);
}
delegates.Add(handler);
}
public IObservable<EventPattern<T>> GetObservable<T>(RoutedEvent<T> routedEvent) where T : RoutedEventArgs
{
Contract.Requires<NullReferenceException>(routedEvent != null);
return Observable.FromEventPattern<T>(
handler => this.AddHandler(routedEvent, handler),
handler => this.RemoveHandler(routedEvent, handler));
}
public void RemoveHandler(RoutedEvent routedEvent, Delegate handler)
{
Contract.Requires<NullReferenceException>(routedEvent != null);
Contract.Requires<NullReferenceException>(handler != null);
List<Delegate> delegates;
if (this.eventHandlers.TryGetValue(routedEvent, out delegates))
{
delegates.Remove(handler);
}
}
public void RaiseEvent(RoutedEventArgs e)
{
Contract.Requires<NullReferenceException>(e != null);
if (e.RoutedEvent != null)
{
switch (e.RoutedEvent.RoutingStrategy)
{
case RoutingStrategy.Bubble:
this.BubbleEvent(e);
break;
case RoutingStrategy.Direct:
this.RaiseEventImpl(e);
break;
default:
throw new NotImplementedException();
}
}
}
private void BubbleEvent(RoutedEventArgs e)
{
Contract.Requires<NullReferenceException>(e != null);
Interactive target = this;
while (target != null)
{
target.RaiseEventImpl(e);
target = target.GetVisualAncestor<Interactive>();
}
}
private void RaiseEventImpl(RoutedEventArgs e)
{
Contract.Requires<NullReferenceException>(e != null);
List<Delegate> delegates;
if (this.eventHandlers.TryGetValue(e.RoutedEvent, out delegates))
{
foreach (Delegate handler in delegates)
{
// TODO: Implement the Handled stuff.
handler.DynamicInvoke(this, e);
}
}
}
}
}

5
Perspex/Perspex.csproj

@ -76,6 +76,8 @@
<Compile Include="Controls\ContentControl.cs" />
<Compile Include="Controls\Control.cs" />
<Compile Include="Controls\TextBlock.cs" />
<Compile Include="Input\MouseEventArgs.cs" />
<Compile Include="Interactive.cs" />
<Compile Include="IBindingDescription.cs" />
<Compile Include="IStyle.cs" />
<Compile Include="Layout\ILayoutable.cs" />
@ -90,6 +92,7 @@
<Compile Include="Media\Matrix.cs" />
<Compile Include="Media\Pen.cs" />
<Compile Include="Media\SolidColorBrush.cs" />
<Compile Include="RoutedEvent.cs" />
<Compile Include="PerspexList.cs" />
<Compile Include="PerspexObject.cs" />
<Compile Include="PerspexProperty.cs" />
@ -97,6 +100,7 @@
<Compile Include="Point.cs" />
<Compile Include="ReadOnlyPerspexProperty.cs" />
<Compile Include="Match.cs" />
<Compile Include="RoutedEventArgs.cs" />
<Compile Include="Selectors.cs" />
<Compile Include="ServiceLocator.cs" />
<Compile Include="Setter.cs" />
@ -110,6 +114,7 @@
<Compile Include="Thickness.cs" />
<Compile Include="Visual.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VisualExtensions.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Splat">

87
Perspex/RoutedEvent.cs

@ -0,0 +1,87 @@
// -----------------------------------------------------------------------
// <copyright file="RoutedEvent.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex
{
using System;
using System.Diagnostics.Contracts;
using System.Reflection;
public enum RoutingStrategy
{
Tunnel,
Bubble,
Direct,
}
public class RoutedEvent
{
public RoutedEvent(
string name,
RoutingStrategy routingStrategy,
Type eventArgsType,
Type ownerType)
{
Contract.Requires<NullReferenceException>(name != null);
Contract.Requires<NullReferenceException>(eventArgsType != null);
Contract.Requires<NullReferenceException>(ownerType != null);
Contract.Requires<InvalidCastException>(typeof(RoutedEventArgs).GetTypeInfo().IsAssignableFrom(eventArgsType.GetTypeInfo()));
Contract.Requires<InvalidCastException>(typeof(Interactive).GetTypeInfo().IsAssignableFrom(ownerType.GetTypeInfo()));
this.EventArgsType = eventArgsType;
this.Name = name;
this.OwnerType = ownerType;
this.RoutingStrategy = routingStrategy;
}
public Type EventArgsType
{
get;
private set;
}
public string Name
{
get;
private set;
}
public Type OwnerType
{
get;
private set;
}
public RoutingStrategy RoutingStrategy
{
get;
private set;
}
public static RoutedEvent<TEventArgs> Register<TOwner, TEventArgs>(
string name,
RoutingStrategy routingStrategy)
where TOwner : Interactive
where TEventArgs : RoutedEventArgs
{
Contract.Requires<NullReferenceException>(name != null);
return new RoutedEvent<TEventArgs>(name, routingStrategy, typeof(TOwner));
}
}
public class RoutedEvent<TEventArgs> : RoutedEvent
where TEventArgs : RoutedEventArgs
{
public RoutedEvent(string name, RoutingStrategy routingStrategy, Type ownerType)
: base(name, routingStrategy, typeof(TEventArgs), ownerType)
{
Contract.Requires<NullReferenceException>(name != null);
Contract.Requires<NullReferenceException>(ownerType != null);
Contract.Requires<InvalidCastException>(typeof(Interactive).GetTypeInfo().IsAssignableFrom(ownerType.GetTypeInfo()));
}
}
}

36
Perspex/RoutedEventArgs.cs

@ -0,0 +1,36 @@
// -----------------------------------------------------------------------
// <copyright file="RoutedEventArgs.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex
{
using System;
public class RoutedEventArgs : EventArgs
{
public RoutedEventArgs()
{
}
public RoutedEventArgs(RoutedEvent routedEvent)
{
this.RoutedEvent = routedEvent;
}
public RoutedEventArgs(RoutedEvent routedEvent, object source)
{
this.RoutedEvent = routedEvent;
this.Source = source;
}
public bool Handled { get; set; }
public object OriginalSource { get; set; }
public RoutedEvent RoutedEvent { get; set; }
public object Source { get; set; }
}
}

10
Perspex/Themes/Default/ButtonStyle.cs

@ -31,7 +31,15 @@ namespace Perspex.Themes.Default
new Setter (Button.BackgroundProperty, new SolidColorBrush(0xffbee6fd)),
new Setter (Button.BorderBrushProperty, new SolidColorBrush(0xff3c7fb1)),
},
}
},
new Style(x => x.Select<Button>().Class(":pressed"))
{
Setters = new[]
{
new Setter (Button.BackgroundProperty, new SolidColorBrush(0xffc4e5f6)),
new Setter (Button.BorderBrushProperty, new SolidColorBrush(0xff2c628b)),
},
},
});
}
}

65
Perspex/VisualExtensions.cs

@ -0,0 +1,65 @@
// -----------------------------------------------------------------------
// <copyright file="VisualExtensions.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex
{
using System;
using System.Diagnostics.Contracts;
using System.Linq;
public static class VisualExtensions
{
public static T GetVisualAncestor<T>(this Visual visual) where T : Visual
{
Contract.Requires<NullReferenceException>(visual != null);
visual = visual.VisualParent;
while (visual != null)
{
if (visual is T)
{
return (T)visual;
}
else
{
visual = visual.VisualParent;
}
}
return null;
}
public static Visual GetVisualAt(this Visual visual, Point p)
{
Contract.Requires<NullReferenceException>(visual != null);
if (visual.Bounds.Contains(p))
{
p -= visual.Bounds.Position;
if (visual.VisualChildren.Any())
{
foreach (Visual child in visual.VisualChildren)
{
Visual hit = child.GetVisualAt(p);
if (hit != null)
{
return hit;
}
}
}
else
{
return visual;
}
}
return null;
}
}
}
Loading…
Cancel
Save