diff --git a/.gitignore b/.gitignore index 741767d15c..f5786b4683 100644 --- a/.gitignore +++ b/.gitignore @@ -151,3 +151,8 @@ $RECYCLE.BIN/ # Mac crap .DS_Store + +################# +## Monodevelop +################# +*.userprefs diff --git a/Cairo/Perspex.Cairo/CairoExtensions.cs b/Cairo/Perspex.Cairo/CairoExtensions.cs new file mode 100644 index 0000000000..51a78ab6e8 --- /dev/null +++ b/Cairo/Perspex.Cairo/CairoExtensions.cs @@ -0,0 +1,29 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Cairo +{ + using Cairo = global::Cairo; + + public static class CairoExtensions + { + public static Cairo.Matrix ToCairo(this Matrix m) + { + return new Cairo.Matrix(m.M11, m.M12, m.M21, m.M22, m.OffsetX, m.OffsetY); + } + + public static Cairo.PointD ToCairo(this Point p) + { + return new Cairo.PointD(p.X, p.Y); + } + + public static Cairo.Rectangle ToCairo(this Rect rect) + { + return new Cairo.Rectangle(rect.X, rect.Y, rect.Width, rect.Height); + } + } +} + diff --git a/Cairo/Perspex.Cairo/CairoPlatform.cs b/Cairo/Perspex.Cairo/CairoPlatform.cs new file mode 100644 index 0000000000..d9e04a91f5 --- /dev/null +++ b/Cairo/Perspex.Cairo/CairoPlatform.cs @@ -0,0 +1,81 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Cairo +{ + using System; + using global::Cairo; + using Perspex.Cairo.Media; + using Perspex.Cairo.Media.Imaging; + using Perspex.Platform; + using Perspex.Threading; + using Splat; + + public class CairoPlatform : IPlatformRenderInterface + { + private static CairoPlatform instance = new CairoPlatform(); + + private static TextService textService = new TextService(); + + public static void Initialize() + { + var locator = Locator.CurrentMutable; + locator.Register(() => instance, typeof(IPlatformRenderInterface)); + locator.Register(() => textService, typeof(ITextService)); + } + + public ITextService TextService + { + get { return textService; } + } + + public IBitmapImpl CreateBitmap(int width, int height) + { + throw new NotImplementedException(); + //return new BitmapImpl(imagingFactory, width, height); + } + + public IRenderer CreateRenderer(IPlatformHandle handle, double width, double height) + { + if (textService.Context == null) + { + textService.Context = GetPangoContext(handle); + } + + return new Renderer(handle, width, height); + } + + public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height) + { + throw new NotImplementedException(); + } + + public IStreamGeometryImpl CreateStreamGeometry() + { + return new StreamGeometryImpl(); + } + + public IBitmapImpl LoadBitmap(string fileName) + { + ImageSurface result = new ImageSurface(fileName); + return new BitmapImpl(result); + } + + private Pango.Context GetPangoContext(IPlatformHandle handle) + { + switch (handle.HandleDescriptor) + { + case "GtkWindow": + var window = GLib.Object.GetObject(handle.Handle) as Gtk.Window; + return window.PangoContext; + default: + throw new NotSupportedException(string.Format( + "Don't know how to get a Pango Context from a '{0}'.", + handle.HandleDescriptor)); + } + } + } +} diff --git a/Cairo/Perspex.Cairo/Media/DrawingContext.cs b/Cairo/Perspex.Cairo/Media/DrawingContext.cs new file mode 100644 index 0000000000..b44cb0dbed --- /dev/null +++ b/Cairo/Perspex.Cairo/Media/DrawingContext.cs @@ -0,0 +1,218 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2013 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- +using Perspex.Cairo.Media.Imaging; + +namespace Perspex.Cairo.Media +{ + using System; + using System.Reactive.Disposables; + using Perspex.Media; + using Perspex.Platform; + using Splat; + using Cairo = global::Cairo; + using IBitmap = Perspex.Media.Imaging.IBitmap; + + /// + /// Draws using Direct2D1. + /// + public class DrawingContext : IDrawingContext, IDisposable + { + /// + /// The cairo context. + /// + private Cairo.Context context; + + /// + /// The cairo surface. + /// + private Cairo.Surface surface; + + /// + /// The text service. + /// + private TextService textService; + + /// + /// Initializes a new instance of the class. + /// + /// The target surface. + public DrawingContext(Cairo.Surface surface) + { + this.surface = surface; + this.context = new Cairo.Context(surface); + this.textService = Locator.Current.GetService() as TextService; + this.CurrentTransform = Matrix.Identity; + } + + /// + /// Initializes a new instance of the class. + /// + /// The GDK drawable. + public DrawingContext(Gdk.Drawable drawable) + { + this.Drawable = drawable; + this.context = Gdk.CairoHelper.Create(drawable); + this.textService = Locator.Current.GetService() as TextService; + this.CurrentTransform = Matrix.Identity; + } + + public Matrix CurrentTransform + { + get; + private set; + } + + public Gdk.Drawable Drawable + { + get; + private set; + } + + /// + /// Ends a draw operation. + /// + public void Dispose() + { + this.context.Dispose(); + + if (this.surface != null) + { + this.surface.Dispose(); + } + } + + public void DrawImage(IBitmap bitmap, double opacity, Rect sourceRect, Rect destRect) + { + var impl = bitmap.PlatformImpl as BitmapImpl; + var size = new Size(impl.PixelWidth, impl.PixelHeight); + var scaleX = destRect.Size.Width / sourceRect.Size.Width; + var scaleY = destRect.Size.Height / sourceRect.Size.Height; + + this.context.Save(); + this.context.Scale(scaleX, scaleY); + this.context.SetSourceSurface(impl.Surface, (int)sourceRect.X, (int)sourceRect.Y); + this.context.Rectangle(destRect.ToCairo()); + this.context.Fill(); + this.context.Restore(); + } + + /// + /// Draws a line. + /// + /// The stroke pen. + /// The first point of the line. + /// The second point of the line. + public void DrawLine(Pen pen, Perspex.Point p1, Perspex.Point p2) + { + this.SetBrush(pen.Brush); + this.context.LineWidth = pen.Thickness; + this.context.MoveTo(p1.ToCairo()); + this.context.LineTo(p2.ToCairo()); + this.context.Stroke(); + } + + /// + /// Draws a geometry. + /// + /// The fill brush. + /// The stroke pen. + /// The geometry. + public void DrawGeometry(Perspex.Media.Brush brush, Perspex.Media.Pen pen, Perspex.Media.Geometry geometry) + { + // TODO: Implement + } + + /// + /// Draws the outline of a rectangle. + /// + /// The pen. + /// The rectangle bounds. + public void DrawRectange(Pen pen, Rect rect) + { + this.SetPen(pen); + this.context.Rectangle(rect.ToCairo()); + this.context.Stroke(); + } + + /// + /// Draws text. + /// + /// The foreground brush. + /// The output rectangle. + /// The text. + public void DrawText(Perspex.Media.Brush foreground, Rect rect, FormattedText text) + { + var layout = this.textService.CreateLayout(text); + this.SetBrush(foreground); + this.context.MoveTo(rect.X, rect.Y); + Pango.CairoHelper.ShowLayout(this.context, layout); + } + + /// + /// Draws a filled rectangle. + /// + /// The brush. + /// The rectangle bounds. + public void FillRectange(Perspex.Media.Brush brush, Rect rect) + { + this.SetBrush(brush); + this.context.Rectangle(rect.ToCairo()); + this.context.Fill(); + } + + /// + /// Pushes a clip rectange. + /// + /// The clip rectangle. + /// A disposable used to undo the clip rectangle. + public IDisposable PushClip(Rect clip) + { + this.context.Save(); + this.context.Rectangle(clip.ToCairo()); + this.context.Clip(); + + return Disposable.Create(() => this.context.Restore()); + } + + /// + /// Pushes a matrix transformation. + /// + /// The matrix + /// A disposable used to undo the transformation. + public IDisposable PushTransform(Matrix matrix) + { + this.context.Save(); + this.context.Transform(matrix.ToCairo()); + this.CurrentTransform *= matrix; + + return Disposable.Create(() => + { + this.context.Restore(); + this.CurrentTransform *= matrix.Invert(); + }); + } + + private void SetBrush(Brush brush) + { + var solid = brush as SolidColorBrush; + + if (solid != null) + { + this.context.SetSourceRGBA( + solid.Color.R / 255.0, + solid.Color.G / 255.0, + solid.Color.B / 255.0, + solid.Color.A / 255.0); + } + } + + private void SetPen(Pen pen) + { + this.SetBrush(pen.Brush); + this.context.LineWidth = pen.Thickness; + } + } +} diff --git a/Cairo/Perspex.Cairo/Media/Imaging/BitmapImpl.cs b/Cairo/Perspex.Cairo/Media/Imaging/BitmapImpl.cs new file mode 100644 index 0000000000..ca36fd7c4d --- /dev/null +++ b/Cairo/Perspex.Cairo/Media/Imaging/BitmapImpl.cs @@ -0,0 +1,42 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Cairo.Media.Imaging +{ + using System; + using Perspex.Platform; + using Cairo = global::Cairo; + + public class BitmapImpl : IBitmapImpl + { + + public BitmapImpl(Cairo.ImageSurface surface) + { + this.Surface = surface; + } + + public int PixelWidth + { + get { return this.Surface.Width; } + } + + public int PixelHeight + { + get { return this.Surface.Height; } + } + + public Cairo.ImageSurface Surface + { + get; + private set; + } + + public void Save(string fileName) + { + throw new NotImplementedException(); + } + } +} diff --git a/Cairo/Perspex.Cairo/Media/StreamGeometryContextImpl.cs b/Cairo/Perspex.Cairo/Media/StreamGeometryContextImpl.cs new file mode 100644 index 0000000000..99e1d4e46b --- /dev/null +++ b/Cairo/Perspex.Cairo/Media/StreamGeometryContextImpl.cs @@ -0,0 +1,38 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2013 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Cairo.Media +{ + using Perspex.Platform; + + public class StreamGeometryContextImpl : IStreamGeometryContextImpl + { + public StreamGeometryContextImpl() + { + // TODO: Implement + } + + public void BeginFigure(Point startPoint, bool isFilled) + { + // TODO: Implement + } + + public void LineTo(Point point) + { + // TODO: Implement + } + + public void EndFigure(bool isClosed) + { + // TODO: Implement + } + + public void Dispose() + { + // TODO: Implement + } + } +} diff --git a/Cairo/Perspex.Cairo/Media/StreamGeometryImpl.cs b/Cairo/Perspex.Cairo/Media/StreamGeometryImpl.cs new file mode 100644 index 0000000000..dec4d03727 --- /dev/null +++ b/Cairo/Perspex.Cairo/Media/StreamGeometryImpl.cs @@ -0,0 +1,38 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Cairo.Media +{ + using System; + using Perspex.Media; + using Perspex.Platform; + using Splat; + + public class StreamGeometryImpl : IStreamGeometryImpl + { + public StreamGeometryImpl() + { + // TODO: Implement + } + + public Rect Bounds + { + get { return new Rect(); } + } + + public Rect GetRenderBounds(double strokeThickness) + { + // TODO: Implement + return new Rect(); + } + + public IStreamGeometryContextImpl Open() + { + // TODO: Implement + return new StreamGeometryContextImpl(); + } + } +} diff --git a/Cairo/Perspex.Cairo/Media/TextService.cs b/Cairo/Perspex.Cairo/Media/TextService.cs new file mode 100644 index 0000000000..f37ae9ecf2 --- /dev/null +++ b/Cairo/Perspex.Cairo/Media/TextService.cs @@ -0,0 +1,92 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Cairo.Media +{ + using System; + using System.Linq; + using System.Runtime.InteropServices; + using Perspex.Media; + using Perspex.Platform; + + public class TextService : ITextService + { + /// + /// Gets the pango context to be used by the service. + /// + /// > + /// There seems to be no way in GtkSharp to create a new Pango Context, so this has to + /// be injected by CairoPlatform the first time a renderer is created. + /// + public Pango.Context Context + { + get; + internal set; + } + + public Pango.Layout CreateLayout(FormattedText text) + { + var layout = new Pango.Layout(this.Context) + { + FontDescription = new Pango.FontDescription + { + Family = text.FontFamilyName, + Size = Pango.Units.FromDouble(text.FontSize), + Style = (Pango.Style)text.FontStyle, + } + }; + + layout.SetText(text.Text); + + return layout; + } + + public int GetCaretIndex(FormattedText text, Point point, Size constraint) + { + var layout = this.CreateLayout(text); + int result; + int trailing; + return layout.XyToIndex( + Pango.Units.FromDouble(point.X), + Pango.Units.FromDouble(point.Y), + out result, + out trailing) ? result : text.Text.Length; + } + + public Point GetCaretPosition(FormattedText text, int caretIndex, Size constraint) + { + // TODO: Rather than have this and GetLineHeights, might be best to just return + // the rect if that's also possible in Direct2D backend. + var layout = this.CreateLayout(text); + var rect = layout.IndexToPos(caretIndex); + return new Point(Pango.Units.ToDouble(rect.X), Pango.Units.ToDouble(rect.Y)); + } + + public double[] GetLineHeights(FormattedText text, Size constraint) + { + var layout = this.CreateLayout(text); + var lines = layout.Lines; + return lines.Select(x => + { + var inkRect = new Pango.Rectangle(); + var logicalRect = new Pango.Rectangle(); + x.GetExtents(ref inkRect, ref logicalRect); + return (double)logicalRect.Height; + }).ToArray(); + } + + public Size Measure(FormattedText text, Size constraint) + { + var layout = this.CreateLayout(text); + + Pango.Rectangle inkRect; + Pango.Rectangle logicalRect; + layout.GetExtents(out inkRect, out logicalRect); + + return new Size(Pango.Units.ToDouble(logicalRect.Width), Pango.Units.ToDouble(logicalRect.Height)); + } + } +} diff --git a/Cairo/Perspex.Cairo/Perspex.Cairo.csproj b/Cairo/Perspex.Cairo/Perspex.Cairo.csproj new file mode 100644 index 0000000000..3a75223eaa --- /dev/null +++ b/Cairo/Perspex.Cairo/Perspex.Cairo.csproj @@ -0,0 +1,98 @@ + + + + + Debug + AnyCPU + {FB05AC90-89BA-4F2F-A924-F37875FB547C} + Library + Properties + Perspex.Cairo + Perspex.Cairo + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + gtk-sharp-2.0 + + + glib-sharp-2.0 + + + gtk-sharp-2.0 + + + gtk-sharp-2.0 + + + ..\..\packages\Splat.1.5.1\lib\Net45\Splat.dll + + + + + + + + + + + ..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + + + ..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + + + gtk-sharp-2.0 + + + + + + + + + + + + + + + + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} + Perspex.Base + + + {EB582467-6ABB-43A1-B052-E981BA910E3A} + Perspex.SceneGraph + + + + + + + + \ No newline at end of file diff --git a/Cairo/Perspex.Cairo/Properties/AssemblyInfo.cs b/Cairo/Perspex.Cairo/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..c143e5e311 --- /dev/null +++ b/Cairo/Perspex.Cairo/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Perspex.Cairo")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Perspex.Cairo")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f999ba8b-64e7-40cc-98a4-003f1852d2a3")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Cairo/Perspex.Cairo/Renderer.cs b/Cairo/Perspex.Cairo/Renderer.cs new file mode 100644 index 0000000000..75115fda69 --- /dev/null +++ b/Cairo/Perspex.Cairo/Renderer.cs @@ -0,0 +1,112 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2013 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Cairo +{ + using System; + using System.Runtime.InteropServices; + using global::Cairo; + using Perspex.Cairo.Media; + using Perspex.Platform; + using Matrix = Perspex.Matrix; + + /// + /// A cairo renderer. + /// + public class Renderer : IRenderer + { + /// + /// Initializes a new instance of the class. + /// + /// The window handle. + /// The width of the window. + /// The height of the window. + public Renderer(IPlatformHandle handle, double width, double height) + { + } + + /// + /// Renders the specified visual. + /// + /// The visual to render. + /// A handle to the drawable. + public void Render(IVisual visual, IPlatformHandle handle) + { + using (DrawingContext context = CreateContext(handle)) + { + this.Render(visual, context); + } + } + + /// + /// Resizes the renderer. + /// + /// The new width. + /// The new height. + public void Resize(int width, int height) + { + // Don't need to do anything here. + } + + [DllImport("user32.dll")] + private static extern IntPtr GetDC(IntPtr hWnd); + + /// + /// Creates a cairo surface that targets a platform-specific resource. + /// + /// The platform-specific handle. + /// A surface. + private static DrawingContext CreateContext(IPlatformHandle handle) + { + switch (handle.HandleDescriptor) + { + case "HWND": + return new DrawingContext(new Win32Surface(GetDC(handle.Handle))); + case "HDC": + return new DrawingContext(new Win32Surface(handle.Handle)); + case "GdkWindow": + return new DrawingContext(new Gdk.Window(handle.Handle)); + default: + throw new NotSupportedException(string.Format( + "Don't know how to create a Cairo renderer from a '{0}' handle", + handle.HandleDescriptor)); + } + } + + /// + /// Renders the specified visual. + /// + /// The visual to render. + /// The drawing context. + private void Render(IVisual visual, DrawingContext context) + { + if (visual.IsVisible && visual.Opacity > 0) + { + Matrix transform = Matrix.Identity; + + if (visual.RenderTransform != null) + { + Matrix current = context.CurrentTransform; + Matrix offset = Matrix.Translation(visual.TransformOrigin.ToPixels(visual.Bounds.Size)); + transform = -current * -offset * visual.RenderTransform.Value * offset * current; + } + + transform *= Matrix.Translation(visual.Bounds.Position); + + using (context.PushClip(visual.Bounds)) + using (context.PushTransform(transform)) + { + visual.Render(context); + + foreach (var child in visual.VisualChildren) + { + this.Render(child, context); + } + } + } + } + } +} diff --git a/Cairo/Perspex.Cairo/packages.config b/Cairo/Perspex.Cairo/packages.config new file mode 100644 index 0000000000..0ab7b56b8d --- /dev/null +++ b/Cairo/Perspex.Cairo/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Docs/mono-build.md b/Docs/mono-build.md new file mode 100644 index 0000000000..b6921863b9 --- /dev/null +++ b/Docs/mono-build.md @@ -0,0 +1,15 @@ +Installing Portable Class Libraries +=================================== + +Microsoft's PCLs are here in an installer that needs to be run on a Windows +machine: + + http://www.microsoft.com/en-us/download/details.aspx?id=40727 + +But someone has made available a .zip here: + + https://www.dropbox.com/s/sf5fclzf2d1spfn/PortableReferenceAssemblies.zip?dl=0 + +Copy them to: + + /usr/lib/mono/xbuild-frameworks/.NETPortable diff --git a/Gtk/Perspex.Gtk/GtkExtensions.cs b/Gtk/Perspex.Gtk/GtkExtensions.cs new file mode 100644 index 0000000000..c8bd6df652 --- /dev/null +++ b/Gtk/Perspex.Gtk/GtkExtensions.cs @@ -0,0 +1,18 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Gtk +{ + using Gtk = global::Gtk; + + public static class GtkExtensions + { + public static Rect ToPerspex(this Gdk.Rectangle rect) + { + return new Rect(rect.Left, rect.Top, rect.Right, rect.Bottom); + } + } +} diff --git a/Gtk/Perspex.Gtk/GtkPlatform.cs b/Gtk/Perspex.Gtk/GtkPlatform.cs new file mode 100644 index 0000000000..70c0342ea0 --- /dev/null +++ b/Gtk/Perspex.Gtk/GtkPlatform.cs @@ -0,0 +1,55 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Gtk +{ + using System; + using System.Reactive.Disposables; + using Perspex.Input; + using Perspex.Platform; + using Splat; + using Gtk = global::Gtk; + + public class GtkPlatform : IPlatformThreadingInterface + { + private static GtkPlatform instance = new GtkPlatform (); + + public GtkPlatform () + { + Gtk.Application.Init(); + } + + public static void Initialize() + { + var locator = Locator.CurrentMutable; + locator.Register(() => new WindowImpl(), typeof(IWindowImpl)); + locator.Register(() => GtkKeyboardDevice.Instance, typeof(IKeyboardDevice)); + locator.Register(() => instance, typeof(IPlatformThreadingInterface)); + } + + public void ProcessMessage () + { + Gtk.Application.RunIteration(); + } + + public IDisposable StartTimer (TimeSpan interval, Action tick) + { + var result = true; + var handle = GLib.Timeout.Add((uint)interval.TotalMilliseconds, () => + { + tick(); + return result; + }); + + return Disposable.Create(() => result = false); + } + + public void Wake () + { + } + } +} + diff --git a/Gtk/Perspex.Gtk/Input/GtkKeyboardDevice.cs b/Gtk/Perspex.Gtk/Input/GtkKeyboardDevice.cs new file mode 100644 index 0000000000..fb42e5e817 --- /dev/null +++ b/Gtk/Perspex.Gtk/Input/GtkKeyboardDevice.cs @@ -0,0 +1,59 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Gtk +{ + using System; + using Perspex.Input; + + public class GtkKeyboardDevice : KeyboardDevice + { + private static GtkKeyboardDevice instance; + + static GtkKeyboardDevice() + { + instance = new GtkKeyboardDevice(); + } + + private GtkKeyboardDevice() + { + } + + public static GtkKeyboardDevice Instance + { + get { return instance; } + } + + public override ModifierKeys Modifiers + { + get { throw new System.NotImplementedException(); } + } + + public static Perspex.Input.Key ConvertKey(Gdk.Key key) + { + // TODO: Don't use reflection for this! My eyes!!! + if (key == Gdk.Key.BackSpace) + { + return Perspex.Input.Key.Back; + } + else + { + var s = Enum.GetName(typeof(Gdk.Key), key); + Perspex.Input.Key result; + + if (Enum.TryParse(s, true, out result)) + { + return result; + } + else + { + return Perspex.Input.Key.None; + } + } + } + } +} + diff --git a/Gtk/Perspex.Gtk/Input/GtkMouseDevice.cs b/Gtk/Perspex.Gtk/Input/GtkMouseDevice.cs new file mode 100644 index 0000000000..e0a7ccaaec --- /dev/null +++ b/Gtk/Perspex.Gtk/Input/GtkMouseDevice.cs @@ -0,0 +1,41 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- +namespace Perspex.Gtk +{ + using Perspex.Input; + + public class GtkMouseDevice : MouseDevice + { + private static GtkMouseDevice instance; + + private Point clientPosition; + + static GtkMouseDevice() + { + instance = new GtkMouseDevice(); + } + + private GtkMouseDevice() + { + } + + public static GtkMouseDevice Instance + { + get { return instance; } + } + + internal void SetClientPosition(Point p) + { + this.clientPosition = p; + } + + protected override Point GetClientPosition() + { + return this.clientPosition; + } + } +} + diff --git a/Gtk/Perspex.Gtk/MyClass.cs b/Gtk/Perspex.Gtk/MyClass.cs new file mode 100644 index 0000000000..ef330fa832 --- /dev/null +++ b/Gtk/Perspex.Gtk/MyClass.cs @@ -0,0 +1,12 @@ +using System; + +namespace Perspex.Gtk +{ + public class MyClass + { + public MyClass () + { + } + } +} + diff --git a/Gtk/Perspex.Gtk/Perspex.Gtk.csproj b/Gtk/Perspex.Gtk/Perspex.Gtk.csproj new file mode 100644 index 0000000000..a63a6510b9 --- /dev/null +++ b/Gtk/Perspex.Gtk/Perspex.Gtk.csproj @@ -0,0 +1,102 @@ + + + + Debug + AnyCPU + {54F237D5-A70A-4752-9656-0C70B1A7B047} + Library + Perspex.Gtk + Perspex.Gtk + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + full + true + bin\Release + prompt + 4 + false + + + + + ..\..\packages\Splat.1.5.1\lib\Net45\Splat.dll + + + gtk-sharp-2.0 + + + gtk-sharp-2.0 + + + gtk-sharp-2.0 + + + ..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + + + ..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + + + ..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + + + glib-sharp-2.0 + + + + + + + + + + + + + + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} + Perspex.Base + + + {62024B2D-53EB-4638-B26B-85EEAA54866E} + Perspex.Input + + + {EB582467-6ABB-43A1-B052-E981BA910E3A} + Perspex.SceneGraph + + + {42472427-4774-4C81-8AFF-9F27B8E31721} + Perspex.Layout + + + {D2221C82-4A25-4583-9B43-D791E3F6820C} + Perspex.Controls + + + {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} + Perspex.Styling + + + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} + Perspex.Interactivity + + + + + + + + + \ No newline at end of file diff --git a/Gtk/Perspex.Gtk/Properties/AssemblyInfo.cs b/Gtk/Perspex.Gtk/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..97680abc0e --- /dev/null +++ b/Gtk/Perspex.Gtk/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle ("Perspex.Gtk")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("steven")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion ("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/Gtk/Perspex.Gtk/WindowImpl.cs b/Gtk/Perspex.Gtk/WindowImpl.cs new file mode 100644 index 0000000000..9a78fc78fb --- /dev/null +++ b/Gtk/Perspex.Gtk/WindowImpl.cs @@ -0,0 +1,150 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Gtk +{ + using System; + using Perspex.Controls; + using Perspex.Input.Raw; + using Perspex.Platform; + using Gtk = global::Gtk; + + public class WindowImpl : Gtk.Window, IWindowImpl + { + private Window owner; + + private IPlatformHandle windowHandle; + + private Size clientSize; + + public WindowImpl() + : base(Gtk.WindowType.Toplevel) + { + this.DefaultSize = new Gdk.Size(640, 480); + this.Events = Gdk.EventMask.PointerMotionMask | + Gdk.EventMask.ButtonPressMask | + Gdk.EventMask.ButtonReleaseMask; + this.windowHandle = new PlatformHandle(this.Handle, "GtkWindow"); + } + + public Size ClientSize + { + get { return this.clientSize; } + } + + IPlatformHandle IWindowImpl.Handle + { + get { return this.windowHandle; } + } + + public Action Activated { get; set; } + + public Action Closed { get; set; } + + public Action Input { get; set; } + + public Action Paint { get; set; } + + public Action Resized { get; set; } + + public void Invalidate(Rect rect) + { + this.QueueDraw(); + } + + public void SetOwner(Window window) + { + this.owner = window; + } + + public void SetTitle(string title) + { + this.Title = title; + } + + protected override bool OnButtonPressEvent(Gdk.EventButton evnt) + { + var e = new RawMouseEventArgs( + GtkMouseDevice.Instance, + this.owner, + RawMouseEventType.LeftButtonDown, + new Point(evnt.X, evnt.Y)); + this.Input(e); + return true; + } + + protected override bool OnButtonReleaseEvent(Gdk.EventButton evnt) + { + var e = new RawMouseEventArgs( + GtkMouseDevice.Instance, + this.owner, + RawMouseEventType.LeftButtonUp, + new Point(evnt.X, evnt.Y)); + this.Input(e); + return true; + } + + protected override bool OnConfigureEvent(Gdk.EventConfigure evnt) + { + var newSize = new Size(evnt.Width, evnt.Height); + + if (newSize != this.clientSize) + { + this.clientSize = newSize; + this.Resized(clientSize); + } + + return true; + } + + protected override void OnDestroyed() + { + this.Closed(); + } + + protected override bool OnKeyPressEvent(Gdk.EventKey evnt) + { + var e = new RawKeyEventArgs( + GtkKeyboardDevice.Instance, + RawKeyEventType.KeyDown, + GtkKeyboardDevice.ConvertKey(evnt.Key), + new string((char)Gdk.Keyval.ToUnicode((uint)evnt.Key), 1)); + this.Input(e); + return true; + } + + protected override bool OnExposeEvent(Gdk.EventExpose evnt) + { + this.Paint(evnt.Area.ToPerspex(), GetHandle(evnt.Window)); + return true; + } + + protected override void OnFocusActivated() + { + this.Activated(); + } + + protected override bool OnMotionNotifyEvent(Gdk.EventMotion evnt) + { + var position = new Point(evnt.X, evnt.Y); + + GtkMouseDevice.Instance.SetClientPosition(position); + + var e = new RawMouseEventArgs( + GtkMouseDevice.Instance, + this.owner, + RawMouseEventType.Move, + position); + this.Input(e); + return true; + } + + private IPlatformHandle GetHandle(Gdk.Window window) + { + return new PlatformHandle(window.Handle, "GdkWindow"); + } + } +} \ No newline at end of file diff --git a/Gtk/Perspex.Gtk/packages.config b/Gtk/Perspex.Gtk/packages.config new file mode 100644 index 0000000000..4dbe72e8fe --- /dev/null +++ b/Gtk/Perspex.Gtk/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Perspex-Linux.userprefs b/Perspex-Linux.userprefs new file mode 100644 index 0000000000..b4525f70d2 --- /dev/null +++ b/Perspex-Linux.userprefs @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Perspex-Mono.sln b/Perspex-Mono.sln new file mode 100644 index 0000000000..56696bab38 --- /dev/null +++ b/Perspex-Mono.sln @@ -0,0 +1,105 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Base", "Perspex.Base\Perspex.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.SceneGraph", "Perspex.SceneGraph\Perspex.SceneGraph.csproj", "{EB582467-6ABB-43A1-B052-E981BA910E3A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Layout", "Perspex.Layout\Perspex.Layout.csproj", "{42472427-4774-4C81-8AFF-9F27B8E31721}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Input", "Perspex.Input\Perspex.Input.csproj", "{62024B2D-53EB-4638-B26B-85EEAA54866E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Interactivity", "Perspex.Interactivity\Perspex.Interactivity.csproj", "{6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Controls", "Perspex.Controls\Perspex.Controls.csproj", "{D2221C82-4A25-4583-9B43-D791E3F6820C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Styling", "Perspex.Styling\Perspex.Styling.csproj", "{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Themes.Default", "Perspex.Themes.Default\Perspex.Themes.Default.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Application", "Perspex.Application\Perspex.Application.csproj", "{799A7BB5-3C2C-48B6-85A7-406A12C420DA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Diagnostics", "Perspex.Diagnostics\Perspex.Diagnostics.csproj", "{7062AE20-5DCC-4442-9645-8195BDECE63E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cairo", "Cairo", "{1D577B69-F23B-4C4F-8605-97704DAC75A9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Cairo", "Cairo\Perspex.Cairo\Perspex.Cairo.csproj", "{FB05AC90-89BA-4F2F-A924-F37875FB547C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gtk", "Gtk", "{B8DA33FB-7ED2-4F4C-9647-D49492B1D07C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Gtk", "Gtk\Perspex.Gtk\Perspex.Gtk.csproj", "{54F237D5-A70A-4752-9656-0C70B1A7B047}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication-Mono", "TestApplication\TestApplication-Mono.csproj", "{E3A1060B-50D0-44E8-88B6-F44EF2E5BD72}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|Any CPU.Build.0 = Release|Any CPU + {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|Any CPU.Build.0 = Release|Any CPU + {54F237D5-A70A-4752-9656-0C70B1A7B047}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54F237D5-A70A-4752-9656-0C70B1A7B047}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54F237D5-A70A-4752-9656-0C70B1A7B047}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54F237D5-A70A-4752-9656-0C70B1A7B047}.Release|Any CPU.Build.0 = Release|Any CPU + {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|Any CPU.Build.0 = Release|Any CPU + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|Any CPU.Build.0 = Release|Any CPU + {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|Any CPU.Build.0 = Release|Any CPU + {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|Any CPU.Build.0 = Release|Any CPU + {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|Any CPU.Build.0 = Release|Any CPU + {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|Any CPU.Build.0 = Release|Any CPU + {E3A1060B-50D0-44E8-88B6-F44EF2E5BD72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3A1060B-50D0-44E8-88B6-F44EF2E5BD72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3A1060B-50D0-44E8-88B6-F44EF2E5BD72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3A1060B-50D0-44E8-88B6-F44EF2E5BD72}.Release|Any CPU.Build.0 = Release|Any CPU + {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|Any CPU.Build.0 = Release|Any CPU + {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|Any CPU.Build.0 = Release|Any CPU + {FB05AC90-89BA-4F2F-A924-F37875FB547C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB05AC90-89BA-4F2F-A924-F37875FB547C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB05AC90-89BA-4F2F-A924-F37875FB547C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB05AC90-89BA-4F2F-A924-F37875FB547C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {FB05AC90-89BA-4F2F-A924-F37875FB547C} = {1D577B69-F23B-4C4F-8605-97704DAC75A9} + {54F237D5-A70A-4752-9656-0C70B1A7B047} = {B8DA33FB-7ED2-4F4C-9647-D49492B1D07C} + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = TestApplication\TestApplication-Mono.csproj + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Perspex-Mono.userprefs b/Perspex-Mono.userprefs new file mode 100644 index 0000000000..c6bbcfe9f8 --- /dev/null +++ b/Perspex-Mono.userprefs @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Perspex.Application/Perspex.Application.csproj b/Perspex.Application/Perspex.Application.csproj index 915072ae6c..bd85add2e1 100644 --- a/Perspex.Application/Perspex.Application.csproj +++ b/Perspex.Application/Perspex.Application.csproj @@ -1,5 +1,5 @@  - + 11.0 @@ -37,31 +37,31 @@ - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base - {d2221c82-4a25-4583-9b43-d791e3f6820c} + {D2221C82-4A25-4583-9B43-D791E3F6820C} Perspex.Controls - {62024b2d-53eb-4638-b26b-85eeaa54866e} + {62024B2D-53EB-4638-B26B-85EEAA54866E} Perspex.Input - {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b} + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} Perspex.Interactivity - {42472427-4774-4c81-8aff-9f27b8e31721} + {42472427-4774-4C81-8AFF-9F27B8E31721} Perspex.Layout - {eb582467-6abb-43a1-b052-e981ba910e3a} + {EB582467-6ABB-43A1-B052-E981BA910E3A} Perspex.SceneGraph - {f1baa01a-f176-4c6a-b39d-5b40bb1b148f} + {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} Perspex.Styling diff --git a/Perspex.Base/Perspex.Base.csproj b/Perspex.Base/Perspex.Base.csproj index 0b224ac578..e560962bbd 100644 --- a/Perspex.Base/Perspex.Base.csproj +++ b/Perspex.Base/Perspex.Base.csproj @@ -1,5 +1,5 @@  - + 11.0 @@ -45,7 +45,9 @@ + + diff --git a/Perspex.Base/Platform/IPlatformHandle.cs b/Perspex.Base/Platform/IPlatformHandle.cs new file mode 100644 index 0000000000..f0144dc990 --- /dev/null +++ b/Perspex.Base/Platform/IPlatformHandle.cs @@ -0,0 +1,26 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Platform +{ + using System; + + /// + /// Represents a platform-specific handle. + /// + public interface IPlatformHandle + { + /// + /// Gets the handle. + /// + IntPtr Handle { get; } + + /// + /// Gets an optional string that describes what represents. + /// + string HandleDescriptor { get; } + } +} diff --git a/Perspex.Base/Platform/IPlatformThreadingInterface.cs b/Perspex.Base/Platform/IPlatformThreadingInterface.cs index 1b752ef297..8b72ee6a8e 100644 --- a/Perspex.Base/Platform/IPlatformThreadingInterface.cs +++ b/Perspex.Base/Platform/IPlatformThreadingInterface.cs @@ -11,6 +11,9 @@ namespace Perspex.Platform using System.Threading.Tasks; using Perspex.Threading; + /// + /// Provides platform-specific services relating to threading. + /// public interface IPlatformThreadingInterface { /// @@ -22,9 +25,9 @@ namespace Perspex.Platform /// Starts a timer. /// /// The interval. - /// The action to call on each tick. + /// The action to call on each tick. /// An used to stop the timer. - IDisposable StartTimer(TimeSpan interval, Action internalTick); + IDisposable StartTimer(TimeSpan interval, Action tick); /// /// Sends a message that causes to exit. diff --git a/Perspex.Base/Platform/PlatformHandle.cs b/Perspex.Base/Platform/PlatformHandle.cs new file mode 100644 index 0000000000..b5d2c0daf6 --- /dev/null +++ b/Perspex.Base/Platform/PlatformHandle.cs @@ -0,0 +1,23 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Platform +{ + using System; + + public class PlatformHandle : IPlatformHandle + { + public PlatformHandle(IntPtr handle, string descriptor) + { + this.Handle = handle; + this.HandleDescriptor = descriptor; + } + + public IntPtr Handle { get; private set; } + + public string HandleDescriptor { get; private set; } + } +} diff --git a/Perspex.Base/Threading/MainLoop.cs b/Perspex.Base/Threading/MainLoop.cs index 0e1fe67ef2..88afba36a6 100644 --- a/Perspex.Base/Threading/MainLoop.cs +++ b/Perspex.Base/Threading/MainLoop.cs @@ -1,5 +1,5 @@ // ----------------------------------------------------------------------- -// +// // Copyright 2014 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- @@ -33,11 +33,11 @@ namespace Perspex.Win32.Threading Job job; // TODO: Dispatch windows messages in preference to lower priority jobs. - while (queue.Count > 0) + while (this.queue.Count > 0) { lock (this.queue) { - job = queue.Dequeue(); + job = this.queue.Dequeue(); } try @@ -59,7 +59,7 @@ namespace Perspex.Win32.Threading { var job = new Job(action); - lock (queue) + lock (this.queue) { this.queue.Add(job, priority); } diff --git a/Perspex.Controls/Grid.cs b/Perspex.Controls/Grid.cs index 650a323d57..613ce9fcf5 100644 --- a/Perspex.Controls/Grid.cs +++ b/Perspex.Controls/Grid.cs @@ -605,10 +605,10 @@ namespace Perspex.Controls // we need to count the number of stars instead. for (int i = start; i <= end; i++) { - double segmentSize = desiredSize ? matrix[i, i].DesiredSize : matrix[i, i].OfferedSize; + double segmentSize = desiredSize ? (matrix[i, i].DesiredSize) : matrix[i, i].OfferedSize; if (segmentSize < matrix[i, i].Max) { - count += type == GridUnitType.Star ? matrix[i, i].Stars : 1; + count += type == GridUnitType.Star ? (matrix[i, i].Stars) : 1; } } @@ -620,7 +620,7 @@ namespace Perspex.Controls for (int i = start; i <= end; i++) { - double segmentSize = desiredSize ? matrix[i, i].DesiredSize : matrix[i, i].OfferedSize; + double segmentSize = desiredSize ? (matrix[i, i].DesiredSize) : matrix[i, i].OfferedSize; if (!(matrix[i, i].Type == type && segmentSize < matrix[i, i].Max)) { @@ -628,7 +628,7 @@ namespace Perspex.Controls } double newsize = segmentSize; - newsize += contribution * (type == GridUnitType.Star ? matrix[i, i].Stars : 1); + newsize += contribution * (type == GridUnitType.Star ? (matrix[i, i].Stars) : 1); newsize = Math.Min(newsize, matrix[i, i].Max); assigned |= newsize > segmentSize; size -= newsize - segmentSize; diff --git a/Perspex.Controls/Perspex.Controls.csproj b/Perspex.Controls/Perspex.Controls.csproj index a4d49af8ba..221f2731c3 100644 --- a/Perspex.Controls/Perspex.Controls.csproj +++ b/Perspex.Controls/Perspex.Controls.csproj @@ -1,5 +1,5 @@  - + 11.0 @@ -41,6 +41,7 @@ + @@ -69,6 +70,7 @@ + @@ -114,27 +116,27 @@ - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base - {62024b2d-53eb-4638-b26b-85eeaa54866e} + {62024B2D-53EB-4638-B26B-85EEAA54866E} Perspex.Input - {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b} + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} Perspex.Interactivity - {42472427-4774-4c81-8aff-9f27b8e31721} + {42472427-4774-4C81-8AFF-9F27B8E31721} Perspex.Layout - {eb582467-6abb-43a1-b052-e981ba910e3a} + {EB582467-6ABB-43A1-B052-E981BA910E3A} Perspex.SceneGraph - {f1baa01a-f176-4c6a-b39d-5b40bb1b148f} + {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} Perspex.Styling diff --git a/Perspex.Controls/Platform/IWindowImpl.cs b/Perspex.Controls/Platform/IWindowImpl.cs new file mode 100644 index 0000000000..5b4d5a6b72 --- /dev/null +++ b/Perspex.Controls/Platform/IWindowImpl.cs @@ -0,0 +1,37 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Platform +{ + using System; + using Perspex.Controls; + using Perspex.Input.Raw; + + public interface IWindowImpl + { + Size ClientSize { get; } + + IPlatformHandle Handle { get; } + + Action Activated { get; set; } + + Action Closed { get; set; } + + Action Input { get; set; } + + Action Paint { get; set; } + + Action Resized { get; set; } + + void Invalidate(Rect rect); + + void SetTitle(string title); + + void SetOwner(Window window); + + void Show(); + } +} diff --git a/Perspex.Controls/Window.cs b/Perspex.Controls/Window.cs new file mode 100644 index 0000000000..890d1e2260 --- /dev/null +++ b/Perspex.Controls/Window.cs @@ -0,0 +1,177 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Controls +{ + using System; + using System.Reactive.Linq; + using Perspex.Input; + using Perspex.Input.Raw; + using Perspex.Layout; + using Perspex.Media; + using Perspex.Platform; + using Perspex.Rendering; + using Perspex.Styling; + using Perspex.Threading; + using Splat; + + public class Window : ContentControl, ILayoutRoot, IRenderRoot, ICloseable + { + public static readonly PerspexProperty ClientSizeProperty = + PerspexProperty.Register("ClientSize"); + + public static readonly PerspexProperty TitleProperty = + PerspexProperty.Register("Title", "Window"); + + private IWindowImpl impl; + + private Dispatcher dispatcher; + + private IRenderer renderer; + + private IInputManager inputManager; + + public event EventHandler Activated; + + public event EventHandler Closed; + + static Window() + { + BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); + } + + public Window() + { + IPlatformRenderInterface renderInterface = Locator.Current.GetService(); + + this.impl = Locator.Current.GetService(); + this.inputManager = Locator.Current.GetService(); + + if (this.impl == null) + { + throw new InvalidOperationException( + "Could not create window implementation: maybe no windowing subsystem was initialized?"); + } + + if (this.inputManager == null) + { + throw new InvalidOperationException( + "Could not create input manager: maybe Application.RegisterServices() wasn't called?"); + } + + this.impl.SetOwner(this); + this.impl.Activated = this.HandleActivated; + this.impl.Closed = this.HandleClosed; + this.impl.Input = this.HandleInput; + this.impl.Paint = this.HandlePaint; + this.impl.Resized = this.HandleResized; + + Size clientSize = this.ClientSize = this.impl.ClientSize; + this.dispatcher = Dispatcher.UIThread; + this.renderer = renderInterface.CreateRenderer(this.impl.Handle, clientSize.Width, clientSize.Height); + + this.LayoutManager = new LayoutManager(this); + this.LayoutManager.LayoutNeeded.Subscribe(_ => this.HandleLayoutNeeded()); + + this.RenderManager = new RenderManager(); + this.RenderManager.RenderNeeded.Subscribe(_ => this.HandleRenderNeeded()); + + this.GetObservable(TitleProperty).Subscribe(s => this.impl.SetTitle(s)); + + IStyler styler = Locator.Current.GetService(); + styler.ApplyStyles(this); + } + + public Size ClientSize + { + get { return this.GetValue(ClientSizeProperty); } + set { this.SetValue(ClientSizeProperty, value); } + } + + public string Title + { + get { return this.GetValue(TitleProperty); } + set { this.SetValue(TitleProperty, value); } + } + + public ILayoutManager LayoutManager + { + get; + private set; + } + + public IRenderManager RenderManager + { + get; + private set; + } + + public void Show() + { + this.impl.Show(); + this.LayoutPass(); + } + + private void HandleActivated() + { + if (this.Activated != null) + { + this.Activated(this, EventArgs.Empty); + } + } + + private void HandleClosed() + { + if (this.Closed != null) + { + this.Closed(this, EventArgs.Empty); + } + } + + private void HandleInput(RawInputEventArgs e) + { + this.inputManager.Process(e); + } + + private void HandleLayoutNeeded() + { + this.dispatcher.InvokeAsync(this.LayoutPass, DispatcherPriority.Render); + } + + private void HandleRenderNeeded() + { + this.dispatcher.InvokeAsync(this.RenderVisualTree, DispatcherPriority.Render); + } + + private void HandlePaint(Rect rect, IPlatformHandle handle) + { + this.renderer.Render(this, handle); + this.RenderManager.RenderFinished(); + } + + private void HandleResized(Size size) + { + this.ClientSize = size; + this.renderer.Resize((int)size.Width, (int)size.Height); + this.LayoutManager.ExecuteLayoutPass(); + this.impl.Invalidate(new Rect(this.ClientSize)); + } + + private void LayoutPass() + { + this.LayoutManager.ExecuteLayoutPass(); + this.impl.Invalidate(new Rect(this.ClientSize)); + } + + private void RenderVisualTree() + { + if (!this.LayoutManager.LayoutQueued) + { + this.impl.Invalidate(new Rect(this.ClientSize)); + } + } + } +} diff --git a/Perspex.Diagnostics/Perspex.Diagnostics.csproj b/Perspex.Diagnostics/Perspex.Diagnostics.csproj index d452d20867..91e89ecee9 100644 --- a/Perspex.Diagnostics/Perspex.Diagnostics.csproj +++ b/Perspex.Diagnostics/Perspex.Diagnostics.csproj @@ -1,5 +1,5 @@  - + 11.0 @@ -36,31 +36,31 @@ - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base - {d2221c82-4a25-4583-9b43-d791e3f6820c} + {D2221C82-4A25-4583-9B43-D791E3F6820C} Perspex.Controls - {62024b2d-53eb-4638-b26b-85eeaa54866e} + {62024B2D-53EB-4638-B26B-85EEAA54866E} Perspex.Input - {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b} + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} Perspex.Interactivity - {42472427-4774-4c81-8aff-9f27b8e31721} + {42472427-4774-4C81-8AFF-9F27B8E31721} Perspex.Layout - {eb582467-6abb-43a1-b052-e981ba910e3a} + {EB582467-6ABB-43A1-B052-E981BA910E3A} Perspex.SceneGraph - {f1baa01a-f176-4c6a-b39d-5b40bb1b148f} + {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} Perspex.Styling diff --git a/Perspex.Input/Perspex.Input.csproj b/Perspex.Input/Perspex.Input.csproj index d8a4fc02ec..ec3798df40 100644 --- a/Perspex.Input/Perspex.Input.csproj +++ b/Perspex.Input/Perspex.Input.csproj @@ -1,5 +1,5 @@  - + 11.0 @@ -36,19 +36,19 @@ - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base - {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b} + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} Perspex.Interactivity - {42472427-4774-4c81-8aff-9f27b8e31721} + {42472427-4774-4C81-8AFF-9F27B8E31721} Perspex.Layout - {eb582467-6abb-43a1-b052-e981ba910e3a} + {EB582467-6ABB-43A1-B052-E981BA910E3A} Perspex.SceneGraph @@ -66,6 +66,7 @@ + diff --git a/Perspex.Input/Raw/RawInputEventArgs.cs b/Perspex.Input/Raw/RawInputEventArgs.cs index 0e9a08c6fe..6c4266818e 100644 --- a/Perspex.Input/Raw/RawInputEventArgs.cs +++ b/Perspex.Input/Raw/RawInputEventArgs.cs @@ -7,11 +7,6 @@ namespace Perspex.Input.Raw { using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using Perspex.Layout; public class RawInputEventArgs : EventArgs { diff --git a/Perspex.Input/Raw/RawMouseEventArgs.cs b/Perspex.Input/Raw/RawMouseEventArgs.cs index 41da9ec88a..c13f41d564 100644 --- a/Perspex.Input/Raw/RawMouseEventArgs.cs +++ b/Perspex.Input/Raw/RawMouseEventArgs.cs @@ -33,6 +33,7 @@ namespace Perspex.Input.Raw this.Type = type; } + //TODO: This should probably be IInputRoot or something. public ILayoutRoot Root { get; private set; } public Point Position { get; private set; } diff --git a/Perspex.Input/Raw/RawSizeEventArgs.cs b/Perspex.Input/Raw/RawSizeEventArgs.cs new file mode 100644 index 0000000000..69f06c6964 --- /dev/null +++ b/Perspex.Input/Raw/RawSizeEventArgs.cs @@ -0,0 +1,25 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2013 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Input.Raw +{ + using System; + + public class RawSizeEventArgs : EventArgs + { + public RawSizeEventArgs(Size size) + { + this.Size = size; + } + + public RawSizeEventArgs(double width, double height) + { + this.Size = new Size(width, height); + } + + public Size Size { get; private set; } + } +} diff --git a/Perspex.Interactivity/Perspex.Interactivity.csproj b/Perspex.Interactivity/Perspex.Interactivity.csproj index 7faac4cede..6f30c8ed23 100644 --- a/Perspex.Interactivity/Perspex.Interactivity.csproj +++ b/Perspex.Interactivity/Perspex.Interactivity.csproj @@ -1,5 +1,5 @@  - + 11.0 @@ -36,15 +36,15 @@ - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base - {42472427-4774-4c81-8aff-9f27b8e31721} + {42472427-4774-4C81-8AFF-9F27B8E31721} Perspex.Layout - {eb582467-6abb-43a1-b052-e981ba910e3a} + {EB582467-6ABB-43A1-B052-E981BA910E3A} Perspex.SceneGraph diff --git a/Perspex.Layout/Perspex.Layout.csproj b/Perspex.Layout/Perspex.Layout.csproj index 920966b61c..af8d421bd3 100644 --- a/Perspex.Layout/Perspex.Layout.csproj +++ b/Perspex.Layout/Perspex.Layout.csproj @@ -1,5 +1,5 @@  - + 11.0 @@ -36,11 +36,11 @@ - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base - {eb582467-6abb-43a1-b052-e981ba910e3a} + {EB582467-6ABB-43A1-B052-E981BA910E3A} Perspex.SceneGraph diff --git a/Perspex.SceneGraph.UnitTests/Perspex.SceneGraph.UnitTests.csproj b/Perspex.SceneGraph.UnitTests/Perspex.SceneGraph.UnitTests.csproj index e0592ce2b8..faec5d7788 100644 --- a/Perspex.SceneGraph.UnitTests/Perspex.SceneGraph.UnitTests.csproj +++ b/Perspex.SceneGraph.UnitTests/Perspex.SceneGraph.UnitTests.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU diff --git a/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/Perspex.SceneGraph/Perspex.SceneGraph.csproj index 71306a04be..d34d8b968f 100644 --- a/Perspex.SceneGraph/Perspex.SceneGraph.csproj +++ b/Perspex.SceneGraph/Perspex.SceneGraph.csproj @@ -1,5 +1,5 @@  - + 11.0 @@ -36,7 +36,7 @@ - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base diff --git a/Perspex.SceneGraph/Platform/IPlatformRenderInterface.cs b/Perspex.SceneGraph/Platform/IPlatformRenderInterface.cs index a5bb3be06c..b5ade8f65e 100644 --- a/Perspex.SceneGraph/Platform/IPlatformRenderInterface.cs +++ b/Perspex.SceneGraph/Platform/IPlatformRenderInterface.cs @@ -17,7 +17,7 @@ namespace Perspex.Platform IStreamGeometryImpl CreateStreamGeometry(); - IRenderer CreateRenderer(IntPtr handle, double width, double height); + IRenderer CreateRenderer(IPlatformHandle handle, double width, double height); IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height); diff --git a/Perspex.SceneGraph/Platform/IRenderer.cs b/Perspex.SceneGraph/Platform/IRenderer.cs index e251fe0f6e..0d7dc136e2 100644 --- a/Perspex.SceneGraph/Platform/IRenderer.cs +++ b/Perspex.SceneGraph/Platform/IRenderer.cs @@ -14,7 +14,8 @@ namespace Perspex.Platform /// Renders the specified visual. /// /// The visual to render. - void Render(IVisual visual); + /// An optional platform-specific handle. + void Render(IVisual visual, IPlatformHandle handle); /// /// Resizes the rendered viewport. diff --git a/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj b/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj index 35f9b9561a..5dbe204925 100644 --- a/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj +++ b/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU diff --git a/Perspex.Styling/Perspex.Styling.csproj b/Perspex.Styling/Perspex.Styling.csproj index 3e4442f46a..d87a4cddce 100644 --- a/Perspex.Styling/Perspex.Styling.csproj +++ b/Perspex.Styling/Perspex.Styling.csproj @@ -1,5 +1,5 @@  - + 11.0 @@ -75,11 +75,11 @@ - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base - {eb582467-6abb-43a1-b052-e981ba910e3a} + {EB582467-6ABB-43A1-B052-E981BA910E3A} Perspex.SceneGraph diff --git a/Perspex.Themes.Default/DefaultTheme.cs b/Perspex.Themes.Default/DefaultTheme.cs index 0d4e986785..0a184873ab 100644 --- a/Perspex.Themes.Default/DefaultTheme.cs +++ b/Perspex.Themes.Default/DefaultTheme.cs @@ -25,6 +25,7 @@ namespace Perspex.Themes.Default this.Add(new TextBoxStyle()); this.Add(new TreeViewStyle()); this.Add(new TreeViewItemStyle()); + this.Add(new WindowStyle()); } } } diff --git a/Perspex.Themes.Default/Perspex.Themes.Default.csproj b/Perspex.Themes.Default/Perspex.Themes.Default.csproj index 643059bcba..abb1b7a2b0 100644 --- a/Perspex.Themes.Default/Perspex.Themes.Default.csproj +++ b/Perspex.Themes.Default/Perspex.Themes.Default.csproj @@ -1,5 +1,5 @@  - + 11.0 @@ -37,35 +37,36 @@ - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base - {d2221c82-4a25-4583-9b43-d791e3f6820c} + {D2221C82-4A25-4583-9B43-D791E3F6820C} Perspex.Controls - {62024b2d-53eb-4638-b26b-85eeaa54866e} + {62024B2D-53EB-4638-B26B-85EEAA54866E} Perspex.Input - {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b} + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} Perspex.Interactivity - {42472427-4774-4c81-8aff-9f27b8e31721} + {42472427-4774-4C81-8AFF-9F27B8E31721} Perspex.Layout - {eb582467-6abb-43a1-b052-e981ba910e3a} + {EB582467-6ABB-43A1-B052-E981BA910E3A} Perspex.SceneGraph - {f1baa01a-f176-4c6a-b39d-5b40bb1b148f} + {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} Perspex.Styling + diff --git a/Perspex.Themes.Default/WindowStyle.cs b/Perspex.Themes.Default/WindowStyle.cs new file mode 100644 index 0000000000..479fc5f19c --- /dev/null +++ b/Perspex.Themes.Default/WindowStyle.cs @@ -0,0 +1,43 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Themes.Default +{ + using System.Linq; + using Perspex.Controls; + using Perspex.Controls.Presenters; + using Perspex.Media; + using Perspex.Styling; + + public class WindowStyle : Styles + { + public WindowStyle() + { + this.AddRange(new[] + { + new Style(x => x.OfType()) + { + Setters = new[] + { + new Setter(Window.TemplateProperty, ControlTemplate.Create(this.Template)), + }, + }, + }); + } + + private Control Template(Window control) + { + return new Border + { + [~Border.BackgroundProperty] = control[~Window.BackgroundProperty], + Content = new ContentPresenter + { + [~ContentPresenter.ContentProperty] = control[~Window.ContentProperty], + } + }; + } + } +} diff --git a/Perspex.UnitTests/Perspex.UnitTests.csproj b/Perspex.UnitTests/Perspex.UnitTests.csproj index a13658aaa7..90956ea27e 100644 --- a/Perspex.UnitTests/Perspex.UnitTests.csproj +++ b/Perspex.UnitTests/Perspex.UnitTests.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -70,10 +70,10 @@ - + diff --git a/Perspex.sln b/Perspex.sln index ca73eb44ec..87589151a6 100644 --- a/Perspex.sln +++ b/Perspex.sln @@ -41,6 +41,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Application", "Pers EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Diagnostics", "Perspex.Diagnostics\Perspex.Diagnostics.csproj", "{7062AE20-5DCC-4442-9645-8195BDECE63E}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cairo", "Cairo", "{1D577B69-F23B-4C4F-8605-97704DAC75A9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Cairo", "Cairo\Perspex.Cairo\Perspex.Cairo.csproj", "{FB05AC90-89BA-4F2F-A924-F37875FB547C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gtk", "Gtk", "{554AF661-FE23-4647-930E-48B617E68F9D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Gtk", "Gtk\Perspex.Gtk\Perspex.Gtk.csproj", "{54F237D5-A70A-4752-9656-0C70B1A7B047}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication-Mono", "TestApplication\TestApplication-Mono.csproj", "{B6FDF644-8EE5-4A6B-8ECC-C2C2E4DAF116}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -119,6 +129,18 @@ Global {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|Any CPU.Build.0 = Debug|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|Any CPU.ActiveCfg = Release|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|Any CPU.Build.0 = Release|Any CPU + {FB05AC90-89BA-4F2F-A924-F37875FB547C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB05AC90-89BA-4F2F-A924-F37875FB547C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB05AC90-89BA-4F2F-A924-F37875FB547C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB05AC90-89BA-4F2F-A924-F37875FB547C}.Release|Any CPU.Build.0 = Release|Any CPU + {54F237D5-A70A-4752-9656-0C70B1A7B047}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54F237D5-A70A-4752-9656-0C70B1A7B047}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54F237D5-A70A-4752-9656-0C70B1A7B047}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54F237D5-A70A-4752-9656-0C70B1A7B047}.Release|Any CPU.Build.0 = Release|Any CPU + {B6FDF644-8EE5-4A6B-8ECC-C2C2E4DAF116}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6FDF644-8EE5-4A6B-8ECC-C2C2E4DAF116}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6FDF644-8EE5-4A6B-8ECC-C2C2E4DAF116}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6FDF644-8EE5-4A6B-8ECC-C2C2E4DAF116}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -127,5 +149,7 @@ Global {811A76CF-1CF6-440F-963B-BBE31BD72A82} = {B39A8919-9F95-48FE-AD7B-76E08B509888} {3E908F67-5543-4879-A1DC-08EACE79B3CD} = {B39A8919-9F95-48FE-AD7B-76E08B509888} {DABFD304-D6A4-4752-8123-C2CCF7AC7831} = {B39A8919-9F95-48FE-AD7B-76E08B509888} + {FB05AC90-89BA-4F2F-A924-F37875FB547C} = {1D577B69-F23B-4C4F-8605-97704DAC75A9} + {54F237D5-A70A-4752-9656-0C70B1A7B047} = {554AF661-FE23-4647-930E-48B617E68F9D} EndGlobalSection EndGlobal diff --git a/TestApplication/App.cs b/TestApplication/App.cs index f9357754a0..40ec041c58 100644 --- a/TestApplication/App.cs +++ b/TestApplication/App.cs @@ -1,9 +1,19 @@ namespace TestApplication { using Perspex; - using Perspex.Direct2D1; - using Perspex.Themes.Default; + using Perspex.Themes.Default; + +#if PERSPEX_CAIRO + using Perspex.Cairo; +#else + using Perspex.Direct2D1; +#endif + +#if PERSPEX_GTK + using Perspex.Gtk; +#else using Perspex.Win32; +#endif public class App : Application { @@ -11,8 +21,17 @@ : base(new DefaultTheme()) { this.RegisterServices(); +#if PERSPEX_CAIRO + CairoPlatform.Initialize(); +#else Direct2D1Platform.Initialize(); - Win32Platform.Initialize(); +#endif + +#if PERSPEX_GTK + GtkPlatform.Initialize(); +#else + Win32Platform.Initialize(); +#endif } } } diff --git a/TestApplication/Program.cs b/TestApplication/Program.cs index 34720898e5..04177b9e60 100644 --- a/TestApplication/Program.cs +++ b/TestApplication/Program.cs @@ -6,7 +6,11 @@ using Perspex.Diagnostics; using Perspex.Layout; using Perspex.Media; using Perspex.Media.Imaging; +#if PERSPEX_GTK +using Perspex.Gtk; +#else using Perspex.Win32; +#endif using Splat; namespace TestApplication diff --git a/TestApplication/TestApplication-Mono.csproj b/TestApplication/TestApplication-Mono.csproj new file mode 100644 index 0000000000..b9f08f0d00 --- /dev/null +++ b/TestApplication/TestApplication-Mono.csproj @@ -0,0 +1,147 @@ + + + + + Debug + AnyCPU + {B6FDF644-8EE5-4A6B-8ECC-C2C2E4DAF116} + WinExe + Properties + TestApplication + TestApplication + v4.5 + 512 + caf18def + + + true + full + false + bin\Debug-Mono\ + TRACE;DEBUG;PERSPEX_CAIRO;PERSPEX_GTK + prompt + 4 + true + + + AnyCPU + pdbonly + true + bin\Release-Mono\ + TRACE + prompt + 4 + + + + + + + ..\packages\Rx-Xaml.2.2.5\lib\net45\System.Reactive.Windows.Threading.dll + + + + + + + + + + ..\packages\reactiveui-core.6.2.1.1\lib\Net45\ReactiveUI.dll + + + ..\packages\Splat.1.5.1\lib\Net45\Splat.dll + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + + + + + + + + + + + + + + {FB05AC90-89BA-4F2F-A924-F37875FB547C} + Perspex.Cairo + + + {799A7BB5-3C2C-48B6-85A7-406A12C420DA} + Perspex.Application + + + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} + Perspex.Base + + + {D2221C82-4A25-4583-9B43-D791E3F6820C} + Perspex.Controls + + + {7062AE20-5DCC-4442-9645-8195BDECE63E} + Perspex.Diagnostics + + + {62024B2D-53EB-4638-B26B-85EEAA54866E} + Perspex.Input + + + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} + Perspex.Interactivity + + + {42472427-4774-4C81-8AFF-9F27B8E31721} + Perspex.Layout + + + {EB582467-6ABB-43A1-B052-E981BA910E3A} + Perspex.SceneGraph + + + {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} + Perspex.Styling + + + {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F} + Perspex.Themes.Default + + + {54F237D5-A70A-4752-9656-0C70B1A7B047} + Perspex.Gtk + + + + + PreserveNewest + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/TestApplication/TestApplication.csproj b/TestApplication/TestApplication.csproj index b759b1b84e..76648a7188 100644 --- a/TestApplication/TestApplication.csproj +++ b/TestApplication/TestApplication.csproj @@ -1,5 +1,5 @@  - + Debug @@ -19,9 +19,10 @@ full false bin\Debug\ - DEBUG;TRACE + TRACE;DEBUG prompt 4 + true AnyCPU @@ -32,51 +33,12 @@ prompt 4 - - - - - False - ..\packages\reactiveui-core.6.2.1.1\lib\Net45\ReactiveUI.dll - - - False - $(SharpDXPackageBinDir)\SharpDX.dll - - - False - $(SharpDXPackageBinDir)\SharpDX.Direct2D1.dll - - - False - $(SharpDXPackageBinDir)\SharpDX.DXGI.dll - - - False - ..\packages\Splat.1.5.1\lib\Net45\Splat.dll - - - False - ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll - - - False - ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll - - - False - ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll - - - False - ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll - - ..\packages\Rx-XAML.2.2.5\lib\net45\System.Reactive.Windows.Threading.dll + ..\packages\Rx-Xaml.2.2.5\lib\net45\System.Reactive.Windows.Threading.dll @@ -85,6 +47,24 @@ + + ..\packages\reactiveui-core.6.2.1.1\lib\Net45\ReactiveUI.dll + + + ..\packages\Splat.1.5.1\lib\Net45\Splat.dll + + + ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + + + ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + + + ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + + + ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + @@ -97,49 +77,49 @@ - {799a7bb5-3c2c-48b6-85a7-406a12c420da} + {799A7BB5-3C2C-48B6-85A7-406A12C420DA} Perspex.Application - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base - {d2221c82-4a25-4583-9b43-d791e3f6820c} + {D2221C82-4A25-4583-9B43-D791E3F6820C} Perspex.Controls - {7062ae20-5dcc-4442-9645-8195bdece63e} + {7062AE20-5DCC-4442-9645-8195BDECE63E} Perspex.Diagnostics - - {3e908f67-5543-4879-a1dc-08eace79b3cd} - Perspex.Direct2D1 - - {62024b2d-53eb-4638-b26b-85eeaa54866e} + {62024B2D-53EB-4638-B26B-85EEAA54866E} Perspex.Input - {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b} + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} Perspex.Interactivity - {42472427-4774-4c81-8aff-9f27b8e31721} + {42472427-4774-4C81-8AFF-9F27B8E31721} Perspex.Layout - {eb582467-6abb-43a1-b052-e981ba910e3a} + {EB582467-6ABB-43A1-B052-E981BA910E3A} Perspex.SceneGraph - {f1baa01a-f176-4c6a-b39d-5b40bb1b148f} + {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} Perspex.Styling - {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} + {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F} Perspex.Themes.Default + + {3e908f67-5543-4879-a1dc-08eace79b3cd} + Perspex.Direct2D1 + {811a76cf-1cf6-440f-963b-bbe31bd72a82} Perspex.Win32 diff --git a/Windows/Perspex.Direct2D1.RenderTests/Perspex.Direct2D1.RenderTests.csproj b/Windows/Perspex.Direct2D1.RenderTests/Perspex.Direct2D1.RenderTests.csproj index 1eebdb3a00..bace0f0688 100644 --- a/Windows/Perspex.Direct2D1.RenderTests/Perspex.Direct2D1.RenderTests.csproj +++ b/Windows/Perspex.Direct2D1.RenderTests/Perspex.Direct2D1.RenderTests.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU diff --git a/Windows/Perspex.Direct2D1/Direct2D1Platform.cs b/Windows/Perspex.Direct2D1/Direct2D1Platform.cs index f748c48a6b..9199583e6b 100644 --- a/Windows/Perspex.Direct2D1/Direct2D1Platform.cs +++ b/Windows/Perspex.Direct2D1/Direct2D1Platform.cs @@ -1,5 +1,5 @@ // ----------------------------------------------------------------------- -// +// // Copyright 2014 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- @@ -44,9 +44,18 @@ namespace Perspex.Direct2D1 return new BitmapImpl(imagingFactory, width, height); } - public IRenderer CreateRenderer(IntPtr handle, double width, double height) + public IRenderer CreateRenderer(IPlatformHandle handle, double width, double height) { - return new Renderer(handle, width, height); + if (handle.HandleDescriptor == "HWND") + { + return new Renderer(handle.Handle, width, height); + } + else + { + throw new NotSupportedException(string.Format( + "Don't know how to create a Direct2D1 renderer from a '{0}' handle", + handle.HandleDescriptor)); + } } public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height) diff --git a/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs b/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs index cc7ef83380..45c7c9ef86 100644 --- a/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs +++ b/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs @@ -35,7 +35,7 @@ namespace Perspex.Direct2D1.Media public void Render(IVisual visual) { Renderer renderer = new Renderer(this.target); - renderer.Render(visual); + renderer.Render(visual, null); } } } diff --git a/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj b/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj index 1077b46351..07e4bb1800 100644 --- a/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj +++ b/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj @@ -1,5 +1,5 @@  - + Debug @@ -43,31 +43,27 @@ False $(SharpDXPackageBinDir)\SharpDX.DXGI.dll - - False - ..\..\packages\Splat.1.5.1\lib\Net45\Splat.dll - - - False - ..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll - - - False - ..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll - - - False - ..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll - + + ..\..\packages\Splat.1.5.1\lib\Net45\Splat.dll + + + ..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll + + + ..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll + + + ..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll + @@ -88,11 +84,11 @@ - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base - {eb582467-6abb-43a1-b052-e981ba910e3a} + {EB582467-6ABB-43A1-B052-E981BA910E3A} Perspex.SceneGraph diff --git a/Windows/Perspex.Direct2D1/Renderer.cs b/Windows/Perspex.Direct2D1/Renderer.cs index 3454f87941..41b26588a7 100644 --- a/Windows/Perspex.Direct2D1/Renderer.cs +++ b/Windows/Perspex.Direct2D1/Renderer.cs @@ -18,9 +18,6 @@ namespace Perspex.Direct2D1 using Matrix = Perspex.Matrix; using Point = Perspex.Point; - /// - /// Renders a . - /// public class Renderer : IRenderer { /// @@ -88,7 +85,8 @@ namespace Perspex.Direct2D1 /// Renders the specified visual. /// /// The visual to render. - public void Render(IVisual visual) + /// Unused. + public void Render(IVisual visual, IPlatformHandle handle) { using (DrawingContext context = new DrawingContext(this.renderTarget, this.DirectWriteFactory)) { diff --git a/Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs b/Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs index df190b0a9d..c5bee8c6a8 100644 --- a/Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs +++ b/Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs @@ -7,6 +7,7 @@ namespace Perspex.Win32.Input { using System.Text; + using Perspex.Controls; using Perspex.Input; using Perspex.Win32.Interop; diff --git a/Windows/Perspex.Win32/Input/WindowsMouseDevice.cs b/Windows/Perspex.Win32/Input/WindowsMouseDevice.cs index ade33bbd01..abf36d40a3 100644 --- a/Windows/Perspex.Win32/Input/WindowsMouseDevice.cs +++ b/Windows/Perspex.Win32/Input/WindowsMouseDevice.cs @@ -19,7 +19,7 @@ namespace Perspex.Win32.Input get { return instance; } } - public Window CurrentWindow + public WindowImpl CurrentWindow { get; set; @@ -35,7 +35,7 @@ namespace Perspex.Win32.Input { UnmanagedMethods.POINT p; UnmanagedMethods.GetCursorPos(out p); - UnmanagedMethods.ScreenToClient(this.CurrentWindow.Handle, ref p); + UnmanagedMethods.ScreenToClient(this.CurrentWindow.Handle.Handle, ref p); return new Point(p.X, p.Y); } } diff --git a/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs b/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs index 0f828526a0..001fef6489 100644 --- a/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs +++ b/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs @@ -42,6 +42,18 @@ namespace Perspex.Win32.Interop IDC_HELP = 32651 } + [StructLayout(LayoutKind.Sequential)] + public struct PAINTSTRUCT + { + public IntPtr hdc; + public bool fErase; + public RECT rcPaint; + public bool fRestore; + public bool fIncUpdate; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] rgbReserved; + } + [Flags] public enum SetWindowPosFlags : uint { @@ -350,6 +362,9 @@ namespace Perspex.Win32.Interop WM_DISPATCH_WORK_ITEM = WM_USER, } + [DllImport("user32.dll")] + public static extern IntPtr BeginPaint(IntPtr hwnd, out PAINTSTRUCT lpPaint); + [DllImport("user32.dll")] public static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint); @@ -383,6 +398,12 @@ namespace Perspex.Win32.Interop [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect); + [DllImport("user32.dll")] + public static extern bool GetUpdateRect(IntPtr hwnd, out RECT lpRect, bool bErase); + + [DllImport("user32.dll")] + public static extern bool InvalidateRect(IntPtr hWnd, ref RECT lpRect, bool bErase); + [DllImport("user32.dll")] public static extern bool KillTimer(IntPtr hWnd, IntPtr uIDEvent); diff --git a/Windows/Perspex.Win32/Perspex.Win32.csproj b/Windows/Perspex.Win32/Perspex.Win32.csproj index 34820c5520..b21e8ea241 100644 --- a/Windows/Perspex.Win32/Perspex.Win32.csproj +++ b/Windows/Perspex.Win32/Perspex.Win32.csproj @@ -1,5 +1,5 @@  - + Debug @@ -30,42 +30,37 @@ 4 - - False - ..\..\packages\Splat.1.5.1\lib\Net45\Splat.dll - - - False + + + + + + + + ..\..\packages\Splat.1.5.1\lib\Net45\Splat.dll + + ..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll - - False + ..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll - - False + ..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll - - False + ..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll - - - - - - - + @@ -75,35 +70,35 @@ - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} Perspex.Base - {d2221c82-4a25-4583-9b43-d791e3f6820c} + {D2221C82-4A25-4583-9B43-D791E3F6820C} Perspex.Controls - {7062ae20-5dcc-4442-9645-8195bdece63e} + {7062AE20-5DCC-4442-9645-8195BDECE63E} Perspex.Diagnostics - {62024b2d-53eb-4638-b26b-85eeaa54866e} + {62024B2D-53EB-4638-B26B-85EEAA54866E} Perspex.Input - {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b} + {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} Perspex.Interactivity - {42472427-4774-4c81-8aff-9f27b8e31721} + {42472427-4774-4C81-8AFF-9F27B8E31721} Perspex.Layout - {eb582467-6abb-43a1-b052-e981ba910e3a} + {EB582467-6ABB-43A1-B052-E981BA910E3A} Perspex.SceneGraph - {f1baa01a-f176-4c6a-b39d-5b40bb1b148f} + {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} Perspex.Styling diff --git a/Windows/Perspex.Win32/Win32Platform.cs b/Windows/Perspex.Win32/Win32Platform.cs index a0de44a705..34ea286e19 100644 --- a/Windows/Perspex.Win32/Win32Platform.cs +++ b/Windows/Perspex.Win32/Win32Platform.cs @@ -7,6 +7,7 @@ namespace Perspex.Win32 { using System; + using System.Collections.Generic; using System.Reactive.Disposables; using System.Threading; using System.Threading.Tasks; @@ -21,9 +22,12 @@ namespace Perspex.Win32 { private static Win32Platform instance = new Win32Platform(); + private List delegates = new List(); + public static void Initialize() { var locator = Locator.CurrentMutable; + locator.Register(() => new WindowImpl(), typeof(IWindowImpl)); locator.Register(() => WindowsKeyboardDevice.Instance, typeof(IKeyboardDevice)); locator.Register(() => instance, typeof(IPlatformThreadingInterface)); } @@ -47,8 +51,12 @@ namespace Perspex.Win32 (uint)interval.TotalMilliseconds, timerDelegate); + // Prevent timerDelegate being garbage collected. + this.delegates.Add(timerDelegate); + return Disposable.Create(() => { + this.delegates.Remove(timerDelegate); UnmanagedMethods.KillTimer(IntPtr.Zero, handle); }); } diff --git a/Windows/Perspex.Win32/Window.cs b/Windows/Perspex.Win32/Window.cs deleted file mode 100644 index f97479b273..0000000000 --- a/Windows/Perspex.Win32/Window.cs +++ /dev/null @@ -1,293 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright 2014 MIT Licence. See licence.md for more information. -// -// ----------------------------------------------------------------------- - -namespace Perspex.Win32 -{ - using System; - using System.ComponentModel; - using System.Diagnostics.CodeAnalysis; - using System.Reactive.Linq; - using System.Runtime.InteropServices; - using Perspex.Controls; - using Perspex.Controls.Presenters; - using Perspex.Diagnostics; - using Perspex.Input; - using Perspex.Input.Raw; - using Perspex.Layout; - using Perspex.Platform; - using Perspex.Rendering; - using Perspex.Threading; - using Perspex.Win32.Input; - using Perspex.Win32.Interop; - using Splat; - - public class Window : ContentControl, ILayoutRoot, IRenderRoot, ICloseable - { - public static readonly PerspexProperty TitleProperty = PerspexProperty.Register("Title"); - - private UnmanagedMethods.WndProc wndProcDelegate; - - private string className; - - private Dispatcher dispatcher; - - private IRenderer renderer; - - private IInputManager inputManager; - - public Window() - { - IPlatformRenderInterface factory = Locator.Current.GetService(); - - this.CreateWindow(); - Size clientSize = this.ClientSize; - this.dispatcher = Dispatcher.UIThread; - this.LayoutManager = new LayoutManager(this); - this.RenderManager = new RenderManager(); - this.renderer = factory.CreateRenderer(this.Handle, (int)clientSize.Width, (int)clientSize.Height); - this.inputManager = Locator.Current.GetService(); - this.Template = ControlTemplate.Create(this.DefaultTemplate); - - this.LayoutManager.LayoutNeeded.Subscribe(x => - { - this.dispatcher.InvokeAsync( - () => - { - this.LayoutManager.ExecuteLayoutPass(); - this.renderer.Render(this); - this.RenderManager.RenderFinished(); - }, - DispatcherPriority.Render); - }); - - this.GetObservable(TitleProperty).Subscribe(s => UnmanagedMethods.SetWindowText(Handle, s)); - - this.RenderManager.RenderNeeded - .Where(_ => !this.LayoutManager.LayoutQueued) - .Subscribe(x => - { - this.dispatcher.InvokeAsync( - () => - { - if (!this.LayoutManager.LayoutQueued) - { - this.renderer.Render(this); - this.RenderManager.RenderFinished(); - } - }, - DispatcherPriority.Render); - }); - } - - public event EventHandler Activated; - - public event EventHandler Closed; - - public string Title - { - get { return this.GetValue(TitleProperty); } - set { this.SetValue(TitleProperty, value); } - } - - public Size ClientSize - { - get - { - UnmanagedMethods.RECT rect; - UnmanagedMethods.GetClientRect(this.Handle, out rect); - return new Size(rect.right, rect.bottom); - } - } - - public IntPtr Handle - { - get; - private set; - } - - public ILayoutManager LayoutManager - { - get; - private set; - } - - public IRenderManager RenderManager - { - get; - private set; - } - - public void Show() - { - UnmanagedMethods.ShowWindow(this.Handle, 1); - } - - protected override void OnPreviewKeyDown(KeyEventArgs e) - { - if (e.Key == Key.F12) - { - Window window = new Window - { - Content = new DevTools - { - Root = this, - }, - }; - - window.Show(); - } - } - - private Control DefaultTemplate(Window c) - { - Border border = new Border(); - border.Background = new Perspex.Media.SolidColorBrush(0xffffffff); - ContentPresenter contentPresenter = new ContentPresenter(); - contentPresenter.Bind( - ContentPresenter.ContentProperty, - this.GetObservable(Window.ContentProperty), - BindingPriority.Style); - border.Content = contentPresenter; - return border; - } - - private void CreateWindow() - { - // Ensure that the delegate doesn't get garbage collected by storing it as a field. - this.wndProcDelegate = new UnmanagedMethods.WndProc(this.WndProc); - - this.className = Guid.NewGuid().ToString(); - - UnmanagedMethods.WNDCLASSEX wndClassEx = new UnmanagedMethods.WNDCLASSEX - { - cbSize = Marshal.SizeOf(typeof(UnmanagedMethods.WNDCLASSEX)), - style = 0, - lpfnWndProc = this.wndProcDelegate, - hInstance = Marshal.GetHINSTANCE(this.GetType().Module), - hCursor = UnmanagedMethods.LoadCursor(IntPtr.Zero, (int)UnmanagedMethods.Cursor.IDC_ARROW), - hbrBackground = (IntPtr)5, - lpszClassName = this.className, - }; - - System.Diagnostics.Debug.WriteLine("Registered class " + this.className); - - ushort atom = UnmanagedMethods.RegisterClassEx(ref wndClassEx); - - if (atom == 0) - { - throw new Win32Exception(); - } - - this.Handle = UnmanagedMethods.CreateWindowEx( - 0, - atom, - null, - (int)UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW, - UnmanagedMethods.CW_USEDEFAULT, - UnmanagedMethods.CW_USEDEFAULT, - UnmanagedMethods.CW_USEDEFAULT, - UnmanagedMethods.CW_USEDEFAULT, - IntPtr.Zero, - IntPtr.Zero, - IntPtr.Zero, - IntPtr.Zero); - - if (this.Handle == IntPtr.Zero) - { - throw new Win32Exception(); - } - } - - private void OnActivated() - { - WindowsKeyboardDevice.Instance.WindowActivated(this); - - if (this.Activated != null) - { - this.Activated(this, EventArgs.Empty); - } - } - - private void OnResized(int width, int height) - { - this.renderer.Resize(width, height); - this.LayoutManager.ExecuteLayoutPass(); - this.renderer.Render(this); - this.RenderManager.RenderFinished(); - } - - private void OnClosed() - { - if (this.Closed != null) - { - this.Closed(this, EventArgs.Empty); - } - } - - [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")] - private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) - { - RawInputEventArgs e = null; - - WindowsMouseDevice.Instance.CurrentWindow = this; - - switch ((UnmanagedMethods.WindowsMessage)msg) - { - case UnmanagedMethods.WindowsMessage.WM_ACTIVATE: - this.OnActivated(); - break; - - case UnmanagedMethods.WindowsMessage.WM_DESTROY: - this.OnClosed(); - break; - - case UnmanagedMethods.WindowsMessage.WM_KEYDOWN: - WindowsKeyboardDevice.Instance.UpdateKeyStates(); - e = new RawKeyEventArgs( - WindowsKeyboardDevice.Instance, - RawKeyEventType.KeyDown, - KeyInterop.KeyFromVirtualKey((int)wParam), - WindowsKeyboardDevice.Instance.StringFromVirtualKey((uint)wParam)); - break; - - case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN: - e = new RawMouseEventArgs( - WindowsMouseDevice.Instance, - this, - RawMouseEventType.LeftButtonDown, - new Point((uint)lParam & 0xffff, (uint)lParam >> 16)); - break; - - case UnmanagedMethods.WindowsMessage.WM_LBUTTONUP: - e = new RawMouseEventArgs( - WindowsMouseDevice.Instance, - this, - RawMouseEventType.LeftButtonUp, - new Point((uint)lParam & 0xffff, (uint)lParam >> 16)); - break; - - case UnmanagedMethods.WindowsMessage.WM_MOUSEMOVE: - e = new RawMouseEventArgs( - WindowsMouseDevice.Instance, - this, - RawMouseEventType.Move, - new Point((uint)lParam & 0xffff, (uint)lParam >> 16)); - break; - - case UnmanagedMethods.WindowsMessage.WM_SIZE: - this.OnResized((int)lParam & 0xffff, (int)lParam >> 16); - return IntPtr.Zero; - } - - if (e != null) - { - this.inputManager.Process(e); - } - - return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam); - } - } -} diff --git a/Windows/Perspex.Win32/WindowImpl.cs b/Windows/Perspex.Win32/WindowImpl.cs new file mode 100644 index 0000000000..7065d9f760 --- /dev/null +++ b/Windows/Perspex.Win32/WindowImpl.cs @@ -0,0 +1,212 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Win32 +{ + using System; + using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.InteropServices; + using Perspex.Controls; + using Perspex.Input.Raw; + using Perspex.Platform; + using Perspex.Win32.Input; + using Perspex.Win32.Interop; + + public class WindowImpl : IWindowImpl + { + private UnmanagedMethods.WndProc wndProcDelegate; + + private string className; + + private IntPtr hwnd; + + private Window owner; + + public WindowImpl() + { + this.CreateWindow(); + } + + public Action Activated { get; set; } + + public Action Closed { get; set; } + + public Action Input { get; set; } + + public Action Paint { get; set; } + + public Action Resized { get; set; } + + public Size ClientSize + { + get + { + UnmanagedMethods.RECT rect; + UnmanagedMethods.GetClientRect(this.hwnd, out rect); + return new Size(rect.right, rect.bottom); + } + } + + public IPlatformHandle Handle + { + get; + private set; + } + + public void Invalidate(Rect rect) + { + this.Paint(rect, this.Handle); + //var r = new UnmanagedMethods.RECT + //{ + // left = (int)rect.X, + // top = (int)rect.Y, + // right = (int)rect.Right, + // bottom = (int)rect.Bottom, + //}; + + //UnmanagedMethods.InvalidateRect(this.hwnd, ref r, false); + } + + public void SetOwner(Window owner) + { + this.owner = owner; + } + + public void SetTitle(string title) + { + UnmanagedMethods.SetWindowText(this.hwnd, title); + } + + public void Show() + { + UnmanagedMethods.ShowWindow(this.hwnd, 1); + } + + private void CreateWindow() + { + // Ensure that the delegate doesn't get garbage collected by storing it as a field. + this.wndProcDelegate = new UnmanagedMethods.WndProc(this.WndProc); + + this.className = Guid.NewGuid().ToString(); + + UnmanagedMethods.WNDCLASSEX wndClassEx = new UnmanagedMethods.WNDCLASSEX + { + cbSize = Marshal.SizeOf(typeof(UnmanagedMethods.WNDCLASSEX)), + style = 0, + lpfnWndProc = this.wndProcDelegate, + hInstance = Marshal.GetHINSTANCE(this.GetType().Module), + hCursor = UnmanagedMethods.LoadCursor(IntPtr.Zero, (int)UnmanagedMethods.Cursor.IDC_ARROW), + hbrBackground = (IntPtr)5, + lpszClassName = this.className, + }; + + System.Diagnostics.Debug.WriteLine("Registered class " + this.className); + + ushort atom = UnmanagedMethods.RegisterClassEx(ref wndClassEx); + + if (atom == 0) + { + throw new Win32Exception(); + } + + this.hwnd = UnmanagedMethods.CreateWindowEx( + 0, + atom, + null, + (int)UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW, + UnmanagedMethods.CW_USEDEFAULT, + UnmanagedMethods.CW_USEDEFAULT, + UnmanagedMethods.CW_USEDEFAULT, + UnmanagedMethods.CW_USEDEFAULT, + IntPtr.Zero, + IntPtr.Zero, + IntPtr.Zero, + IntPtr.Zero); + + if (this.hwnd == IntPtr.Zero) + { + throw new Win32Exception(); + } + + this.Handle = new PlatformHandle(this.hwnd, "HWND"); + } + + [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")] + private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) + { + RawInputEventArgs e = null; + + WindowsMouseDevice.Instance.CurrentWindow = this; + + switch ((UnmanagedMethods.WindowsMessage)msg) + { + case UnmanagedMethods.WindowsMessage.WM_ACTIVATE: + this.Activated(); + break; + + case UnmanagedMethods.WindowsMessage.WM_DESTROY: + this.Closed(); + break; + + case UnmanagedMethods.WindowsMessage.WM_KEYDOWN: + WindowsKeyboardDevice.Instance.UpdateKeyStates(); + e = new RawKeyEventArgs( + WindowsKeyboardDevice.Instance, + RawKeyEventType.KeyDown, + KeyInterop.KeyFromVirtualKey((int)wParam), + WindowsKeyboardDevice.Instance.StringFromVirtualKey((uint)wParam)); + break; + + case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN: + e = new RawMouseEventArgs( + WindowsMouseDevice.Instance, + this.owner, + RawMouseEventType.LeftButtonDown, + new Point((uint)lParam & 0xffff, (uint)lParam >> 16)); + break; + + case UnmanagedMethods.WindowsMessage.WM_LBUTTONUP: + e = new RawMouseEventArgs( + WindowsMouseDevice.Instance, + this.owner, + RawMouseEventType.LeftButtonUp, + new Point((uint)lParam & 0xffff, (uint)lParam >> 16)); + break; + + case UnmanagedMethods.WindowsMessage.WM_MOUSEMOVE: + e = new RawMouseEventArgs( + WindowsMouseDevice.Instance, + this.owner, + RawMouseEventType.Move, + new Point((uint)lParam & 0xffff, (uint)lParam >> 16)); + break; + + // TODO: For some reason WM_PAINT getting called continuously - investigate. + + //case UnmanagedMethods.WindowsMessage.WM_PAINT: + // UnmanagedMethods.RECT r; + // UnmanagedMethods.GetUpdateRect(this.hwnd, out r, false); + // this.Paint(new Rect(r.left, r.top, r.right - r.left, r.bottom - r.top)); + // return IntPtr.Zero; + + case UnmanagedMethods.WindowsMessage.WM_SIZE: + if (this.Resized != null) + { + this.Resized(new Size((int)lParam & 0xffff, (int)lParam >> 16)); + } + return IntPtr.Zero; + } + + if (e != null && this.Input != null) + { + this.Input(e); + } + + return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam); + } + } +}