From 49d94ebe97affbbe36595ab93c4432956ff6e436 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 16 Apr 2016 22:30:37 +0300 Subject: [PATCH] Revert "Moves Perspex Designer to PerspexVS" This reverts commit 41e572b3a202fb80d3034150bf0d98a908094061. --- Perspex.sln | 27 ++ src/Windows/Perspex.Designer/App.config | 6 + src/Windows/Perspex.Designer/App.xaml | 8 + src/Windows/Perspex.Designer/App.xaml.cs | 28 ++ .../AppHost/HostedAppModel.cs | 100 ++++++ .../AppHost/PerspexAppHost.cs | 297 ++++++++++++++++++ .../Perspex.Designer/AppHost/WindowHost.cs | 143 +++++++++ .../Perspex.Designer/Comm/CommChannel.cs | 137 ++++++++ .../Perspex.Designer/Comm/InitMessage.cs | 18 ++ .../Perspex.Designer/Comm/ProcessHost.cs | 171 ++++++++++ .../Perspex.Designer/Comm/StateMessage.cs | 19 ++ .../Perspex.Designer/Comm/UpdateMetadata.cs | 20 ++ .../Comm/UpdateXamlMessage.cs | 19 ++ .../Comm/WindowCreatedMessage.cs | 19 ++ src/Windows/Perspex.Designer/DemoWindow.xaml | 23 ++ .../Perspex.Designer/DemoWindow.xaml.cs | 34 ++ .../InProcDesigner/InProcDesignerView.xaml | 34 ++ .../InProcDesigner/InProcDesignerView.xaml.cs | 98 ++++++ .../Metadata/PerspexDesignerMetadata.cs | 51 +++ .../Perspex.Designer/Perspex.Designer.csproj | 137 ++++++++ .../Perspex.Designer/PerspexDesigner.xaml | 17 + .../Perspex.Designer/PerspexDesigner.xaml.cs | 131 ++++++++ src/Windows/Perspex.Designer/Program.cs | 67 ++++ .../Properties/AssemblyInfo.cs | 36 +++ src/Windows/Perspex.Designer/README.txt | 3 + src/Windows/Perspex.Designer/Settings.cs | 19 ++ src/Windows/Perspex.Designer/WinApi.cs | 33 ++ src/Windows/Perspex.Designer/packages.config | 4 + .../Perspex.Designer/perspex.designer.snk | Bin 0 -> 596 bytes 29 files changed, 1699 insertions(+) create mode 100644 src/Windows/Perspex.Designer/App.config create mode 100644 src/Windows/Perspex.Designer/App.xaml create mode 100644 src/Windows/Perspex.Designer/App.xaml.cs create mode 100644 src/Windows/Perspex.Designer/AppHost/HostedAppModel.cs create mode 100644 src/Windows/Perspex.Designer/AppHost/PerspexAppHost.cs create mode 100644 src/Windows/Perspex.Designer/AppHost/WindowHost.cs create mode 100644 src/Windows/Perspex.Designer/Comm/CommChannel.cs create mode 100644 src/Windows/Perspex.Designer/Comm/InitMessage.cs create mode 100644 src/Windows/Perspex.Designer/Comm/ProcessHost.cs create mode 100644 src/Windows/Perspex.Designer/Comm/StateMessage.cs create mode 100644 src/Windows/Perspex.Designer/Comm/UpdateMetadata.cs create mode 100644 src/Windows/Perspex.Designer/Comm/UpdateXamlMessage.cs create mode 100644 src/Windows/Perspex.Designer/Comm/WindowCreatedMessage.cs create mode 100644 src/Windows/Perspex.Designer/DemoWindow.xaml create mode 100644 src/Windows/Perspex.Designer/DemoWindow.xaml.cs create mode 100644 src/Windows/Perspex.Designer/InProcDesigner/InProcDesignerView.xaml create mode 100644 src/Windows/Perspex.Designer/InProcDesigner/InProcDesignerView.xaml.cs create mode 100644 src/Windows/Perspex.Designer/Metadata/PerspexDesignerMetadata.cs create mode 100644 src/Windows/Perspex.Designer/Perspex.Designer.csproj create mode 100644 src/Windows/Perspex.Designer/PerspexDesigner.xaml create mode 100644 src/Windows/Perspex.Designer/PerspexDesigner.xaml.cs create mode 100644 src/Windows/Perspex.Designer/Program.cs create mode 100644 src/Windows/Perspex.Designer/Properties/AssemblyInfo.cs create mode 100644 src/Windows/Perspex.Designer/README.txt create mode 100644 src/Windows/Perspex.Designer/Settings.cs create mode 100644 src/Windows/Perspex.Designer/WinApi.cs create mode 100644 src/Windows/Perspex.Designer/packages.config create mode 100644 src/Windows/Perspex.Designer/perspex.designer.snk diff --git a/Perspex.sln b/Perspex.sln index 9cbaca445d..eec6adcbf5 100644 --- a/Perspex.sln +++ b/Perspex.sln @@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Win32", "src\Window EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Direct2D1", "src\Windows\Perspex.Direct2D1\Perspex.Direct2D1.csproj", "{3E908F67-5543-4879-A1DC-08EACE79B3CD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Designer", "src\Windows\Perspex.Designer\Perspex.Designer.csproj", "{EC42600F-049B-43FF-AED1-8314D61B2749}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Input", "src\Perspex.Input\Perspex.Input.csproj", "{62024B2D-53EB-4638-B26B-85EEAA54866E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Interactivity", "src\Perspex.Interactivity\Perspex.Interactivity.csproj", "{6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}" @@ -306,6 +308,30 @@ Global {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhone.Build.0 = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.AppStore|Any CPU.ActiveCfg = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.AppStore|Any CPU.Build.0 = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.AppStore|iPhone.Build.0 = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Debug|iPhone.Build.0 = Debug|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Release|Any CPU.Build.0 = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Release|iPhone.ActiveCfg = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Release|iPhone.Build.0 = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {EC42600F-049B-43FF-AED1-8314D61B2749}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -1356,6 +1382,7 @@ Global GlobalSection(NestedProjects) = preSolution {811A76CF-1CF6-440F-963B-BBE31BD72A82} = {B39A8919-9F95-48FE-AD7B-76E08B509888} {3E908F67-5543-4879-A1DC-08EACE79B3CD} = {B39A8919-9F95-48FE-AD7B-76E08B509888} + {EC42600F-049B-43FF-AED1-8314D61B2749} = {B39A8919-9F95-48FE-AD7B-76E08B509888} {47ECDF59-DEF8-4C53-87B1-2098A3429059} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {5CCB5571-7C30-4E7D-967D-0E2158EBD91F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {76716382-3159-460E-BDA6-C5715CF606D7} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} diff --git a/src/Windows/Perspex.Designer/App.config b/src/Windows/Perspex.Designer/App.config new file mode 100644 index 0000000000..d1428ad712 --- /dev/null +++ b/src/Windows/Perspex.Designer/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Windows/Perspex.Designer/App.xaml b/src/Windows/Perspex.Designer/App.xaml new file mode 100644 index 0000000000..2c853d351e --- /dev/null +++ b/src/Windows/Perspex.Designer/App.xaml @@ -0,0 +1,8 @@ + + + diff --git a/src/Windows/Perspex.Designer/App.xaml.cs b/src/Windows/Perspex.Designer/App.xaml.cs new file mode 100644 index 0000000000..929a440192 --- /dev/null +++ b/src/Windows/Perspex.Designer/App.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Perspex.Designer +{ + /// + /// Interaction logic for App.xaml + /// + internal partial class App + { + public App() + { + InitializeComponent(); + } + } +} diff --git a/src/Windows/Perspex.Designer/AppHost/HostedAppModel.cs b/src/Windows/Perspex.Designer/AppHost/HostedAppModel.cs new file mode 100644 index 0000000000..31fe2beeeb --- /dev/null +++ b/src/Windows/Perspex.Designer/AppHost/HostedAppModel.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media; +using JetBrains.Annotations; + +namespace Perspex.Designer.AppHost +{ + public class HostedAppModel : INotifyPropertyChanged + { + private readonly PerspexAppHost _host; + private IntPtr _nativeWindowHandle; + private string _error; + private string _errorDetails; + + internal HostedAppModel(PerspexAppHost host) + { + _host = host; + Background = Settings.Background; + } + + public IntPtr NativeWindowHandle + { + get { return _nativeWindowHandle; } + set + { + if (value.Equals(_nativeWindowHandle)) return; + _nativeWindowHandle = value; + OnPropertyChanged(); + } + } + + public string Error + { + get { return _error; } + private set + { + if (value == _error) return; + _error = value; + OnPropertyChanged(); + } + } + + public string ErrorDetails + { + get { return _errorDetails; } + private set + { + if (value == _errorDetails) return; + _errorDetails = value; + OnPropertyChanged(); + } + } + + public string Background + { + get { return _background; } + set + { + if (value == _background) return; + _background = value; + OnPropertyChanged(); + } + } + + public IReadOnlyList AvailableScalingFactors => new List() {1, 2, 4, 8}; + + public double CurrentScalingFactor + { + get { return _currentScalingFactor; } + set + { + _currentScalingFactor = value; + _host.Api.SetScalingFactor(value); + } + } + + public void SetError(string error, string details = null) + { + Error = error; + ErrorDetails = details; + } + + double _currentScalingFactor = 1; + private string _color; + private string _background; + + public event PropertyChangedEventHandler PropertyChanged; + + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/src/Windows/Perspex.Designer/AppHost/PerspexAppHost.cs b/src/Windows/Perspex.Designer/AppHost/PerspexAppHost.cs new file mode 100644 index 0000000000..c3ca657baf --- /dev/null +++ b/src/Windows/Perspex.Designer/AppHost/PerspexAppHost.cs @@ -0,0 +1,297 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Windows.Forms; +using System.Windows.Forms.Integration; +using System.Xml; +using Perspex.Designer.Comm; +using Perspex.Designer.InProcDesigner; +using Perspex.Designer.Metadata; +using Timer = System.Windows.Forms.Timer; +using Perspex.DesignerSupport; + +namespace Perspex.Designer.AppHost +{ + class PerspexAppHost + { + private string _appDir; + private readonly CommChannel _comm; + private string _lastXaml; + private string _currentXaml; + private bool _initSuccess; + private readonly HostedAppModel _appModel; + private Control _window; + + public PerspexAppHost(CommChannel channel) + { + _comm = channel; + _appModel = new HostedAppModel(this); + } + + public void Start() + { + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + _comm.OnMessage += Channel_OnMessage; + _comm.Start(); + } + + private void Channel_OnMessage(object obj) + { + var init = obj as InitMessage; + if (init != null) + { + Init(init.TargetExe); + } + var updateXaml = obj as UpdateXamlMessage; + if (updateXaml != null) + _currentXaml = updateXaml.Xaml; + } + + void UpdateState(string state) + { + _comm.SendMessage(new StateMessage(state)); + } + + Type LookupType(params string[] names) + { + var asms = AppDomain.CurrentDomain.GetAssemblies(); + foreach (var asm in asms) + { + foreach (var name in names) + { + var found = asm.GetType(name, false, true); + if (found != null) + return found; + } + } + throw new TypeLoadException("Unable to find any of types: " + string.Join(",", names)); + } + + private MethodInfo LookupStaticMethod(string typeName, string method) + { + var type = LookupType(typeName); + var methods = type.GetMethods(); + return methods.First(m => m.Name == method); + } + + private void Init(string targetExe) + { + var log = new StringBuilder(); + try + { + DoInit(targetExe, log); + } + catch (Exception e) + { + UpdateState("Unable to load Perspex:\n\n" + e + "\n\n" + log); + } + } + + PerspexDesignerMetadata BuildMetadata(List asms, Type xmlNsAttr) + { + var rv = new PerspexDesignerMetadata() + { + + NamespaceAliases = new List(), + Types = new List() + }; + + + foreach (var asm in asms) + { + foreach (dynamic xmlns in asm.GetCustomAttributes().Where(a => a.GetType() == xmlNsAttr)) + { + rv.NamespaceAliases.Add(new MetadataNamespaceAlias + { + Namespace = (string)xmlns.ClrNamespace, + XmlNamespace = (string)xmlns.XmlNamespace + }); + } + + try + { + foreach (var type in asm.GetTypes()) + { + try + { + if (!type.IsPublic || type.IsAbstract) + continue; + var t = new MetadataType() + { + Name = type.Name, + Namespace = type.Namespace, + Properties = new List() + }; + rv.Types.Add(t); + foreach (var prop in type.GetProperties()) + { + if (prop.GetMethod?.IsPublic != true) + continue; + var p = new MetadataProperty() + { + Name = prop.Name, + Type = + prop.PropertyType == typeof (string) || + (prop.PropertyType.IsValueType && + prop.PropertyType.Assembly == typeof (int).Assembly) + ? MetadataPropertyType.BasicType + : prop.PropertyType.IsEnum + ? MetadataPropertyType.Enum + : MetadataPropertyType.MetadataType + + }; + if (p.Type == MetadataPropertyType.Enum) + p.EnumValues = Enum.GetNames(prop.PropertyType); + if (p.Type == MetadataPropertyType.MetadataType) + p.MetadataFullTypeName = prop.PropertyType.Namespace + "." + prop.PropertyType.Name; + t.Properties.Add(p); + } + } + catch + { + // + } + } + } + catch + { + // + } + } + return rv; + } + + void BuildMetadataAndSendMessageAsync(List asms) + { + var xmlNsAttr = LookupType("Perspex.Metadata.XmlnsDefinitionAttribute"); + new Thread(() => + { + _comm.SendMessage(new UpdateMetadataMessage(BuildMetadata(asms, xmlNsAttr))); + }).Start(); + } + + private void DoInit(string targetExe, StringBuilder logger) + { + _appDir = Path.GetFullPath(Path.GetDirectoryName(targetExe)); + Directory.SetCurrentDirectory(_appDir); + Action log = s => + { + UpdateState(s); + logger.AppendLine(s); + }; + log("Loading assemblies from " + _appDir); + var asms = new List(); + foreach(var asm in Directory.GetFiles(_appDir).Where(f=>f.ToLower().EndsWith(".dll")||f.ToLower().EndsWith(".exe"))) + try + { + log("Trying to load " + asm); + asms.Add(Assembly.LoadFrom(asm)); + } + catch (Exception e) + { + logger.AppendLine(e.ToString()); + } + + log("Looking up Perspex types"); + BuildMetadataAndSendMessageAsync(asms); + + log("Initializing built-in designer"); + var dic = new Dictionary(); + Api = new DesignerApi(dic) {OnResize = OnResize, OnWindowCreated = OnWindowCreated}; + LookupStaticMethod("Perspex.DesignerSupport.DesignerAssist", "Init").Invoke(null, new object[] {dic}); + + _window = new Control + { + Controls = + { + new ElementHost() + { + Child = new InProcDesignerView(_appModel), + Dock = DockStyle.Fill + } + } + }; + _window.CreateControl(); + + new Timer {Interval = 200, Enabled = true}.Tick += delegate { XamlUpdater(); }; + _comm.SendMessage(new WindowCreatedMessage(_window.Handle)); + _initSuccess = true; + } + + private void OnWindowCreated(IntPtr hWnd) + { + _appModel.NativeWindowHandle = hWnd; + } + + + public DesignerApi Api { get; set; } + + + bool ValidateXml(string xml) + { + try + { + var rdr = new XmlTextReader(new StringReader(xml)); + while (rdr.Read()) + { + + } + } + catch + { + return false; + } + return true; + } + + private void OnResize() + { + + } + + void XamlUpdater() + { + if (!_initSuccess) + return; + if(_lastXaml == _currentXaml) + return; + _lastXaml = _currentXaml; + + if (!ValidateXml(_currentXaml)) + { + _appModel.SetError("Invalid markup"); + return; + } + try + { + Api.UpdateXaml(_currentXaml); + + _appModel.SetError(null); + } + catch (Exception e) + { + _appModel.SetError("XAML load error", e.ToString()); + } + } + + + + private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + string assemblyPath = Path.Combine(_appDir, new AssemblyName(args.Name).Name + ".dll"); + if (File.Exists(assemblyPath) == false) return null; + Assembly assembly = Assembly.LoadFrom(assemblyPath); + return assembly; + } + } + + static class Helper + { + public static object Prop(this object obj, string name) => obj.GetType().GetProperty(name).GetValue(obj); + + } +} diff --git a/src/Windows/Perspex.Designer/AppHost/WindowHost.cs b/src/Windows/Perspex.Designer/AppHost/WindowHost.cs new file mode 100644 index 0000000000..e598e4cb6e --- /dev/null +++ b/src/Windows/Perspex.Designer/AppHost/WindowHost.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Perspex.Designer.AppHost +{ + class WindowHost : UserControl + { + private readonly bool _supportScroll; + + public WindowHost(bool supportScroll) + { + _supportScroll = supportScroll; + if (_supportScroll) + { + AutoScroll = true; + VerticalScroll.Enabled = true; + HorizontalScroll.Enabled = true; + } + SetStyle(ControlStyles.AllPaintingInWmPaint, true); + Controls.Add(_windowHost); + _windowHost.Anchor = AnchorStyles.None; + if (!supportScroll) + _windowHost.Visible = false; + _timer.Tick += delegate + { + ReloadSettings(); + FixWindow(); + }; + } + + private void ReloadSettings() + { + var bkg = Settings.Background; + var color = System.Drawing.ColorTranslator.FromHtml(bkg); + if (BackColor != color) + BackColor = color; + } + + private readonly Control _windowHost = new Control() {Text = "WindowWrapper"}; + private readonly Timer _timer = new Timer {Enabled = true, Interval = 50}; + private IntPtr _hWnd; + private int _desiredWidth; + private int _desiredHeight; + + private const int WM_HSCROLL = 0x114; + private const int WM_VSCROLL = 0x115; + + protected override void WndProc(ref Message m) + { + if ((m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL) + && (((int) m.WParam & 0xFFFF) == 5)) + { + // Change SB_THUMBTRACK to SB_THUMBPOSITION + m.WParam = (IntPtr) (((int) m.WParam & ~0xFFFF) | 4); + } + base.WndProc(ref m); + } + + protected override void OnPaint(PaintEventArgs e) + { + using (var b = new SolidBrush(BackColor)) + e.Graphics.FillRectangle(b, e.ClipRectangle); + } + + void FixPosition() + { + var newScrollSize = new Size(_desiredWidth, _desiredHeight); + if (AutoScrollMinSize != newScrollSize) + AutoScrollMinSize = newScrollSize; + + var width = Width - AutoScrollMargin.Width; + var height = Height - AutoScrollMargin.Height; + var x = Math.Max(0, (width - _windowHost.Width)/2); + var y = Math.Max(0, (height - _windowHost.Height)/2); + + var newLoc = new Point(x - HorizontalScroll.Value, y - VerticalScroll.Value); + if(_windowHost.Location != newLoc) + _windowHost.Location = newLoc; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _timer.Dispose(); + } + base.Dispose(disposing); + } + + public void SetWindow(IntPtr hWnd) + { + if (_hWnd != IntPtr.Zero) + WinApi.SendMessage(_hWnd, WinApi.WM_CLOSE, IntPtr.Zero, IntPtr.Zero); + _hWnd = hWnd; + if (_hWnd != IntPtr.Zero) + { + WinApi.SetParent(hWnd, _supportScroll ? _windowHost.Handle : Handle); + FixWindow(); + } + } + + void FixWindow() + { + if (_hWnd != IntPtr.Zero) + { + if (_supportScroll) + { + WinApi.RECT rc; + WinApi.GetWindowRect(_hWnd, out rc); + _desiredWidth = rc.Right - rc.Left; + _desiredHeight = rc.Bottom - rc.Top; + var pt = _windowHost.PointToClient(new Point(rc.Left, rc.Top)); + + if ( + !(pt.Y == 0 && pt.X == 0 && _desiredWidth == _windowHost.Width && + _desiredHeight == _windowHost.Height)) + { + _windowHost.Width = _desiredWidth; + _windowHost.Height = _desiredHeight; + WinApi.MoveWindow(_hWnd, 0, 0, _desiredWidth, _desiredHeight, true); + } + FixPosition(); + } + else + { + WinApi.MoveWindow(_hWnd, 0, 0, Width, Height, true); + } + } + } + + protected override void OnResize(EventArgs e) + { + FixWindow(); + base.OnResize(e); + } + } +} diff --git a/src/Windows/Perspex.Designer/Comm/CommChannel.cs b/src/Windows/Perspex.Designer/Comm/CommChannel.cs new file mode 100644 index 0000000000..41d613d4d6 --- /dev/null +++ b/src/Windows/Perspex.Designer/Comm/CommChannel.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Windows.Threading; + +namespace Perspex.Designer.Comm +{ + class CommChannel : IDisposable + { + private readonly BinaryReader _input; + private readonly BinaryWriter _output; + private readonly SynchronizationContext _dispatcher; + readonly TaskCompletionSource _terminating = new TaskCompletionSource(); + private readonly BlockingCollection _outputQueue = new BlockingCollection(); + public event Action OnMessage; + public event Action Disposed; + public event Action Exception; + public CommChannel(Stream input, Stream output) + { + _input = new BinaryReader(input); + _output = new BinaryWriter(output); + _dispatcher = SynchronizationContext.Current; + + } + + public void Start() + { + new Thread(WriterThread) { IsBackground = true }.Start(); + new Thread(ReaderThread) { IsBackground = true }.Start(); + } + + public void SendMessage(object message) + { + var fmt = GetFormatter(); + var ms = new MemoryStream(); + fmt.Serialize(ms, message); + _outputQueue.Add(ms.ToArray()); + } + + void WriterThread() + { + while (!_terminating.Task.IsCompleted) + { + var item = _outputQueue.Take(); + if(_terminating.Task.IsCompleted) + return; + try + { + _output.Write(item.Length); + _output.Write(item); + _output.Flush(); + } + catch (Exception e) + { + Exception?.Invoke(e); + Dispose(); + } + } + } + + private async Task ReadAsyncOrNull(int count) + { + var readTask = Task.Factory.StartNew(() => _input.ReadBytes(count)); + await Task.WhenAny(readTask, _terminating.Task); + if (_terminating.Task.IsCompleted) + return null; + return await readTask; + } + + async void ReaderThread() + { + await Task.Delay(10).ConfigureAwait(false); + var fmt = GetFormatter(); + while (!_terminating.Task.IsCompleted) + { + try + { + var lenb = await ReadAsyncOrNull(4); + if (lenb == null) + return; + var data = await ReadAsyncOrNull(BitConverter.ToInt32(lenb, 0)); + if (data == null) + return; + var message = fmt.Deserialize(new MemoryStream(data)); + _dispatcher.Post(_ => OnMessage?.Invoke(message), null); + } + catch(Exception e) + { + Dispose(); + Exception?.Invoke(e); + return; + } + } + + } + + sealed class BinderFix : SerializationBinder + { + private const string ListNamePrefix = "System.Collections.Generic.List`1[["; + public override Type BindToType(string assemblyName, string typeName) + { + if (typeName.StartsWith(ListNamePrefix)) + { + typeName = typeName.Substring(ListNamePrefix.Length); + typeName = typeName.Substring(0, typeName.IndexOf(",")); + return typeof (List<>).MakeGenericType(BindToType(assemblyName, typeName)); + } + + + return typeof (CommChannel).Assembly.GetType(typeName, false, true); + + } + } + + BinaryFormatter GetFormatter() + { + return new BinaryFormatter() {Binder = new BinderFix()}; + } + + public void Dispose() + { + if(_terminating.Task.IsCompleted) + return; + _terminating.TrySetResult(true); + _outputQueue.Add(null); + Disposed?.Invoke(); + } + } +} diff --git a/src/Windows/Perspex.Designer/Comm/InitMessage.cs b/src/Windows/Perspex.Designer/Comm/InitMessage.cs new file mode 100644 index 0000000000..71a562d9e5 --- /dev/null +++ b/src/Windows/Perspex.Designer/Comm/InitMessage.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Designer.Comm +{ + [Serializable] + class InitMessage : UpdateXamlMessage + { + public string TargetExe { get; private set; } + public InitMessage(string targetExe, string xaml) : base(xaml) + { + TargetExe = targetExe; + } + } +} diff --git a/src/Windows/Perspex.Designer/Comm/ProcessHost.cs b/src/Windows/Perspex.Designer/Comm/ProcessHost.cs new file mode 100644 index 0000000000..203017e51b --- /dev/null +++ b/src/Windows/Perspex.Designer/Comm/ProcessHost.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Threading; +using Perspex.Designer.Metadata; + +namespace Perspex.Designer.Comm +{ + class ProcessHost : INotifyPropertyChanged + { + private CommChannel _comm; + private string _state; + public string State + { + get { return _state; } + set + { + if (_state == value) + return; + _state = value; + OnPropertyChanged(); + } + } + + public event Action MetadataArrived; + + private bool _isAlive; + private readonly SynchronizationContext _dispatcher; + private Process _proc; + + public bool IsAlive + { + get { return _isAlive; } + set + { + if (_isAlive == value) + return; + _isAlive = value; + OnPropertyChanged(); + } + } + + private IntPtr _windowHandle; + public IntPtr WindowHandle + { + get { return _windowHandle; } + set + { + if (_windowHandle == value) + return; + _windowHandle = value; + OnPropertyChanged(); + } + } + + public ProcessHost() + { + _dispatcher = SynchronizationContext.Current; + } + + void OnExited(object sender, EventArgs eventArgs) + { + _proc = null; + _dispatcher.Post(_ => + { + + HandleExited(); + }, null); + } + + void HandleExited() + { + _comm.Dispose(); + IsAlive = false; + WindowHandle = IntPtr.Zero; + State = "Designer process crashed"; + } + + public void Start(string targetExe, string initialXaml) + { + if (_proc != null) + { + _proc.Exited -= OnExited; + try + { + _proc.Kill(); + } + catch { } + HandleExited(); + State = "Restarting..."; + } + + var msg = new InitMessage(Path.GetFullPath(targetExe), initialXaml); + var exe = typeof (ProcessHost).Assembly.GetModules()[0].FullyQualifiedName; + _proc = new Process() + { + StartInfo = new ProcessStartInfo(exe, "--hosted") + { + UseShellExecute = false, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }, + EnableRaisingEvents = true + }; + _proc.Exited += OnExited; + try + { + _proc.Start(); + State = "Launching designer process..."; + _comm = new CommChannel(_proc.StandardOutput.BaseStream, _proc.StandardInput.BaseStream); + _comm.OnMessage += OnMessage; + _comm.Start(); + _comm.SendMessage(msg); + } + catch (Exception e) + { + State = e.ToString(); + HandleExited(); + } + IsAlive = true; + + } + + public void UpdateXaml(string xaml) + { + _comm?.SendMessage(new UpdateXamlMessage(xaml)); + } + + private void OnMessage(object obj) + { + var stateMessage = obj as StateMessage; + if (stateMessage != null) + State = stateMessage.State; + var windowMessage = obj as WindowCreatedMessage; + if (windowMessage != null) + WindowHandle = windowMessage.Handle; + var metadata = obj as UpdateMetadataMessage; + if (metadata != null) + _dispatcher.Post(_ => MetadataArrived?.Invoke(metadata.Metadata), null); + } + + public void Kill() + { + try + { + _proc?.Kill(); + _proc = null; + } + catch + { + // + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/src/Windows/Perspex.Designer/Comm/StateMessage.cs b/src/Windows/Perspex.Designer/Comm/StateMessage.cs new file mode 100644 index 0000000000..1c844bddfa --- /dev/null +++ b/src/Windows/Perspex.Designer/Comm/StateMessage.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Designer.Comm +{ + [Serializable] + class StateMessage + { + public StateMessage(string state) + { + State = state; + } + + public string State { get; private set; } + } +} diff --git a/src/Windows/Perspex.Designer/Comm/UpdateMetadata.cs b/src/Windows/Perspex.Designer/Comm/UpdateMetadata.cs new file mode 100644 index 0000000000..0e1421f154 --- /dev/null +++ b/src/Windows/Perspex.Designer/Comm/UpdateMetadata.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Perspex.Designer.Metadata; + +namespace Perspex.Designer.Comm +{ + [Serializable] + public class UpdateMetadataMessage + { + public UpdateMetadataMessage(PerspexDesignerMetadata metadata) + { + Metadata = metadata; + } + + public PerspexDesignerMetadata Metadata { get; } + } +} diff --git a/src/Windows/Perspex.Designer/Comm/UpdateXamlMessage.cs b/src/Windows/Perspex.Designer/Comm/UpdateXamlMessage.cs new file mode 100644 index 0000000000..eb0fc27dd9 --- /dev/null +++ b/src/Windows/Perspex.Designer/Comm/UpdateXamlMessage.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Designer.Comm +{ + [Serializable] + class UpdateXamlMessage + { + public UpdateXamlMessage(string xaml) + { + Xaml = xaml; + } + + public string Xaml { get; private set; } + } +} diff --git a/src/Windows/Perspex.Designer/Comm/WindowCreatedMessage.cs b/src/Windows/Perspex.Designer/Comm/WindowCreatedMessage.cs new file mode 100644 index 0000000000..e8570cee9f --- /dev/null +++ b/src/Windows/Perspex.Designer/Comm/WindowCreatedMessage.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Designer.Comm +{ + [Serializable] + class WindowCreatedMessage + { + public WindowCreatedMessage(IntPtr handle) + { + Handle = handle; + } + + public IntPtr Handle { get; private set; } + } +} diff --git a/src/Windows/Perspex.Designer/DemoWindow.xaml b/src/Windows/Perspex.Designer/DemoWindow.xaml new file mode 100644 index 0000000000..69cfe8cefe --- /dev/null +++ b/src/Windows/Perspex.Designer/DemoWindow.xaml @@ -0,0 +1,23 @@ + + + + Target Exe: + + + + + + + + + + diff --git a/src/Windows/Perspex.Designer/DemoWindow.xaml.cs b/src/Windows/Perspex.Designer/DemoWindow.xaml.cs new file mode 100644 index 0000000000..102bf4a412 --- /dev/null +++ b/src/Windows/Perspex.Designer/DemoWindow.xaml.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Perspex.Designer +{ + + internal partial class DemoWindow + { + public DemoWindow() + { + InitializeComponent(); + } + + public DemoWindow(string targetExe, string targetPath) : this() + { + TargetExe.Text = targetExe; + if (targetExe != null) + Xaml.Text = File.ReadAllText(targetPath); + } + } +} diff --git a/src/Windows/Perspex.Designer/InProcDesigner/InProcDesignerView.xaml b/src/Windows/Perspex.Designer/InProcDesigner/InProcDesignerView.xaml new file mode 100644 index 0000000000..721cb83e04 --- /dev/null +++ b/src/Windows/Perspex.Designer/InProcDesigner/InProcDesignerView.xaml @@ -0,0 +1,34 @@ + + + + + + + 00% + + + + + + + + + + + (View details) + + + + + + + diff --git a/src/Windows/Perspex.Designer/InProcDesigner/InProcDesignerView.xaml.cs b/src/Windows/Perspex.Designer/InProcDesigner/InProcDesignerView.xaml.cs new file mode 100644 index 0000000000..c936b2faa8 --- /dev/null +++ b/src/Windows/Perspex.Designer/InProcDesigner/InProcDesignerView.xaml.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Forms; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Perspex.Designer.AppHost; +using TextBox = System.Windows.Controls.TextBox; +using UserControl = System.Windows.Controls.UserControl; + +namespace Perspex.Designer.InProcDesigner +{ + /// + /// Interaction logic for InProcDesignerView.xaml + /// + public partial class InProcDesignerView : UserControl + { + private readonly HostedAppModel _appModel; + private readonly WindowHost _host; + + public InProcDesignerView(HostedAppModel appModel) + { + _appModel = appModel; + InitializeComponent(); + DataContext = _appModel; + _appModel.PropertyChanged += ModelPropertyChanged; + WindowHostControl.Child = _host = new WindowHost(true); + HandleVisibility(); + HandleWindow(); + + } + + private void ModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(HostedAppModel.Error) || e.PropertyName == nameof(HostedAppModel.ErrorDetails)) + HandleVisibility(); + if (e.PropertyName == nameof(HostedAppModel.NativeWindowHandle)) + HandleWindow(); + } + + private void HandleWindow() + { + _host.SetWindow(_appModel.NativeWindowHandle); + } + + private void HandleVisibility() + { + ErrorPanel.Visibility = string.IsNullOrEmpty(_appModel.Error) + ? Visibility.Collapsed + : Visibility.Visible; + DetailsBlock.Visibility = string.IsNullOrWhiteSpace(_appModel.ErrorDetails) + ? Visibility.Collapsed + : Visibility.Visible; + } + + + + public InProcDesignerView() + { + + } + + private void Hyperlink_OnClick(object sender, RoutedEventArgs e) + { + var wnd = new Window() + { + Content = new TextBox() + { + IsReadOnly = true, + AcceptsReturn = true, + TextWrapping = TextWrapping.Wrap, + Text = _appModel.ErrorDetails + } + }; + wnd.ShowDialog(); + } + + private void ColorPicker_OnClick(object sender, MouseButtonEventArgs e) + { + var dlg = new ColorDialog() {Color = ColorTranslator.FromHtml(Settings.Background)}; + if (dlg.ShowDialog(_host) == DialogResult.OK) + { + var color = ColorTranslator.ToHtml(dlg.Color); + _appModel.Background = color; + Settings.Background = color; + } + } + } +} diff --git a/src/Windows/Perspex.Designer/Metadata/PerspexDesignerMetadata.cs b/src/Windows/Perspex.Designer/Metadata/PerspexDesignerMetadata.cs new file mode 100644 index 0000000000..653faf01cb --- /dev/null +++ b/src/Windows/Perspex.Designer/Metadata/PerspexDesignerMetadata.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Designer.Metadata +{ + [Serializable] + public class PerspexDesignerMetadata + { + public List Types { get; set; } + public List NamespaceAliases { get; set; } + } + + + [Serializable] + public class MetadataType + { + public string Namespace { get; set; } + public string Name { get; set; } + public List Properties { get; set; } + public string FullName => Namespace + "." + Name; + } + + [Serializable] + public class MetadataNamespaceAlias + { + public string Namespace { get; set; } + public string XmlNamespace { get; set; } + } + + [Serializable] + public class MetadataProperty + { + public string Name { get; set; } + public MetadataPropertyType Type { get; set; } + public string MetadataFullTypeName { get; set; } + public string[] EnumValues { get; set; } + } + + + + [Serializable] + public enum MetadataPropertyType + { + BasicType, + MetadataType, + Enum + } +} diff --git a/src/Windows/Perspex.Designer/Perspex.Designer.csproj b/src/Windows/Perspex.Designer/Perspex.Designer.csproj new file mode 100644 index 0000000000..d90a0a55cb --- /dev/null +++ b/src/Windows/Perspex.Designer/Perspex.Designer.csproj @@ -0,0 +1,137 @@ + + + + + Debug + AnyCPU + {EC42600F-049B-43FF-AED1-8314D61B2749} + WinExe + Properties + Perspex.Designer + Perspex.Designer + v4.5 + 512 + true + + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + perspex.designer.snk + + + + + + + ..\..\..\packages\JetBrains.Annotations.10.0.0\lib\net20\JetBrains.Annotations.dll + True + + + + + + + + + + + + + + + + + + + + AppHost\DesignerApi.cs + + + App.xaml + + + + + UserControl + + + + + + + + + + DemoWindow.xaml + + + InProcDesignerView.xaml + + + + PerspexDesigner.xaml + + + + + + + + + + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + + + + \ No newline at end of file diff --git a/src/Windows/Perspex.Designer/PerspexDesigner.xaml b/src/Windows/Perspex.Designer/PerspexDesigner.xaml new file mode 100644 index 0000000000..96679be499 --- /dev/null +++ b/src/Windows/Perspex.Designer/PerspexDesigner.xaml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/src/Windows/Perspex.Designer/PerspexDesigner.xaml.cs b/src/Windows/Perspex.Designer/PerspexDesigner.xaml.cs new file mode 100644 index 0000000000..1b394a0f7d --- /dev/null +++ b/src/Windows/Perspex.Designer/PerspexDesigner.xaml.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Forms.Integration; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Perspex.Designer.AppHost; +using Perspex.Designer.Comm; +using Perspex.Designer.Metadata; + +namespace Perspex.Designer +{ + /// + /// Interaction logic for PerpexDesigner.xaml + /// + public partial class PerspexDesigner + { + public static readonly DependencyProperty TargetExeProperty = DependencyProperty.Register( + "TargetExe", typeof (string), typeof (PerspexDesigner), new FrameworkPropertyMetadata(TargetExeChanged)); + + private static void TargetExeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((PerspexDesigner) d).RestartProcess(); + } + public string TargetExe + { + get { return (string) GetValue(TargetExeProperty); } + set { SetValue(TargetExeProperty, value); } + } + + public static readonly DependencyProperty XamlProperty = DependencyProperty.Register( + "Xaml", typeof (string), typeof (PerspexDesigner), new FrameworkPropertyMetadata(XamlChanged)); + + private static void XamlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((PerspexDesigner) d).OnXamlChanged(); + } + + public string Xaml + { + get { return (string) GetValue(XamlProperty); } + set { SetValue(XamlProperty, value); } + } + + public PerspexDesignerMetadata Metadata { get; private set; } + + private readonly ProcessHost _host = new ProcessHost(); + + + public PerspexDesigner() + { + InitializeComponent(); + BindingOperations.SetBinding(State, TextBox.TextProperty, + new Binding(nameof(ProcessHost.State)) {Source = _host, Mode = BindingMode.OneWay}); + + _host.PropertyChanged += _host_PropertyChanged; + _host.MetadataArrived += data => Metadata = data; + } + + private void _host_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(ProcessHost.WindowHandle)) + { + if (NativeContainer.Child != null) + { + var child = NativeContainer.Child; + NativeContainer.Child = null; + child.Dispose(); + } + NativeContainer.Child = new WindowHost(false); + var wndHost = ((WindowHost) NativeContainer.Child); + wndHost.SetWindow(_host.WindowHandle); + + + } + } + + + public void KillProcess() + { + _host.Kill(); + } + + bool CheckTargetExeOrSetError() + { + if (string.IsNullOrEmpty(TargetExe)) + { + _host.State = "No target exe found"; + return false; + } + + if (File.Exists(TargetExe ?? "")) + return true; + _host.State = "No target binary found, build your project"; + return false; + } + + public void RestartProcess() + { + KillProcess(); + if(!CheckTargetExeOrSetError()) + return; + if(string.IsNullOrEmpty(Xaml)) + return; + _host.Start(TargetExe, Xaml); + } + + private void OnXamlChanged() + { + if (!CheckTargetExeOrSetError()) + return; + if (!_host.IsAlive) + _host.Start(TargetExe, Xaml); + else + _host.UpdateXaml(Xaml ?? ""); + } + + } +} diff --git a/src/Windows/Perspex.Designer/Program.cs b/src/Windows/Perspex.Designer/Program.cs new file mode 100644 index 0000000000..6e58a3c98b --- /dev/null +++ b/src/Windows/Perspex.Designer/Program.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Windows.Threading; +using Perspex.Designer.AppHost; +using Perspex.Designer.Comm; + +namespace Perspex.Designer +{ + class Program + { + [STAThread] + static void Main(string[] args) + { + if (!args.Contains("--hosted")) + DemoMain(args); + else + HostedMain(); + + + } + + static void HostedMain() + { + //Initialize sync context + SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext()); + var comm = new CommChannel(Console.OpenStandardInput(), Console.OpenStandardOutput()); + Console.SetOut(new NullTextWriter()); + Console.SetError(new NullTextWriter()); + comm.Disposed += () => Process.GetCurrentProcess().Kill(); + comm.SendMessage(new StateMessage("Staying awhile and listening...")); + var service = new PerspexAppHost(comm); + service.Start(); + Application.Run(); + } + + class NullTextWriter : TextWriter + { + public override Encoding Encoding => Encoding.UTF8; + public override void Write(char[] buffer, int index, int count) + { + } + + public override void Write(char value) + { + } + } + + private static void DemoMain(string[] args) + { + SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext()); + var app = new App(); + const string targetExe = "--exe="; + const string xaml = "--xaml="; + + app.Run(new DemoWindow( + args.Where(a => a.StartsWith(targetExe)).Select(a => a.Substring(targetExe.Length)).FirstOrDefault(), + args.Where(a => a.StartsWith(xaml)).Select(a => a.Substring(xaml.Length)).FirstOrDefault())); + } + } +} diff --git a/src/Windows/Perspex.Designer/Properties/AssemblyInfo.cs b/src/Windows/Perspex.Designer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..8a85c81a30 --- /dev/null +++ b/src/Windows/Perspex.Designer/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.Designer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Perspex.Designer")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[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("ec42600f-049b-43ff-aed1-8314d61b2749")] + +// 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/src/Windows/Perspex.Designer/README.txt b/src/Windows/Perspex.Designer/README.txt new file mode 100644 index 0000000000..1dec6c88b0 --- /dev/null +++ b/src/Windows/Perspex.Designer/README.txt @@ -0,0 +1,3 @@ +To load data from XamlTestApplication run with: + +--exe=..\..\..\..\..\samples\XamlTestApplication\bin\Debug\XamlTestApplication.exe --xaml=..\..\..\..\..\samples\XamlTestApplication\Views\MainWindow.xaml \ No newline at end of file diff --git a/src/Windows/Perspex.Designer/Settings.cs b/src/Windows/Perspex.Designer/Settings.cs new file mode 100644 index 0000000000..534dd2d5f1 --- /dev/null +++ b/src/Windows/Perspex.Designer/Settings.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Win32; + +namespace Perspex.Designer +{ + static class Settings + { + static readonly string Root = @"HKEY_CURRENT_USER\Software\PerspexUI\Designer"; + public static string Background + { + get { return Registry.GetValue(Root, "Background", "#f3f3f3")?.ToString() ?? "#f3f3f3"; } + set { Registry.SetValue(Root, "Background", value); } + } + } +} diff --git a/src/Windows/Perspex.Designer/WinApi.cs b/src/Windows/Perspex.Designer/WinApi.cs new file mode 100644 index 0000000000..c751254945 --- /dev/null +++ b/src/Windows/Perspex.Designer/WinApi.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Designer +{ + class WinApi + { + [DllImport("user32.dll", SetLastError = true)] + public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); + [DllImport("user32.dll")] + public static extern bool SetParent(IntPtr hWnd, IntPtr hWndNewParent); + [DllImport("user32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); + + public const uint WM_CLOSE = 0x0010; + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect); + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left; // x position of upper-left corner + public int Top; // y position of upper-left corner + public int Right; // x position of lower-right corner + public int Bottom; // y position of lower-right corner + } + } +} diff --git a/src/Windows/Perspex.Designer/packages.config b/src/Windows/Perspex.Designer/packages.config new file mode 100644 index 0000000000..b0ac21bcd8 --- /dev/null +++ b/src/Windows/Perspex.Designer/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/Windows/Perspex.Designer/perspex.designer.snk b/src/Windows/Perspex.Designer/perspex.designer.snk new file mode 100644 index 0000000000000000000000000000000000000000..68df1b6e887d1e3d085ea63caaecd22a5e44d6e6 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096QQf1ZE9mKcbN5{fX*TdFXyyegi1yTs& z9{>9=d_R;G7Q9oSRUErF*qUH9>OTiuQ^%D`_kb^W+`5EU(YeXWYbQeBo$FeR-$yXp zc`WJ$H!jI6^ftf$yO4jMeyuOX?Dj^i;YhL(UaZz;X*Q+<{-B(_YY=|e;A?%6&GDlB z))-DxhfokFp$Sg)_x|r%p*?D04PaNmYIy`^kzmNTCOI?ALBvjpO)HIXh6hQ>1V&!6 zwtP)2Ru4Y3$K>Hn$ODI>>FLHaL+YEvbig>&?&RuN=O}p6x*Y^$R!hv7{f#oIvKb&a zndJl;yO{xD6+X4-kB0DF4(oeEMya@|8W@-ZvuT?4TY!-H59pw)PjVdE`GdS154Js< zTTw_K>y~&_${8iWk!XVtZi;B0I6K7f=PGB(o5~yEc#+H%T8hL%>D_|YCwOQy_TT_p z+E)tp-Ozm1TS>AlPArZk+Kx5i0=p;W~3K-!=$MFfHB`Pt%@VITiLznJfoeh9`Xh literal 0 HcmV?d00001