Browse Source

Initial implementation of dropdown.

The popup appears in the wrong place, has placeholder content and steals
the main window focus, but it appears!
pull/39/head
Steven Kirk 12 years ago
parent
commit
06bf45ace3
  1. 1
      Perspex.Controls/DropDown.cs
  2. 3
      Perspex.Controls/Perspex.Controls.csproj
  3. 24
      Perspex.Controls/Platform/IPopupImpl.cs
  4. 34
      Perspex.Controls/Platform/ITopLevelImpl.cs
  5. 26
      Perspex.Controls/Platform/IWindowImpl.cs
  6. 24
      Perspex.Controls/Popup.cs
  7. 41
      Perspex.Controls/PopupRoot.cs
  8. 210
      Perspex.Controls/TopLevel.cs
  9. 171
      Perspex.Controls/Window.cs
  10. 5
      Perspex.Layout/LayoutManager.cs
  11. 1
      Perspex.Themes.Default/DefaultTheme.cs
  12. 5
      Perspex.Themes.Default/DropDownStyle.cs
  13. 1
      Perspex.Themes.Default/Perspex.Themes.Default.csproj
  14. 46
      Perspex.Themes.Default/PopupRootStyle.cs
  15. 55
      Windows/Perspex.Win32/Interop/UnmanagedMethods.cs
  16. 1
      Windows/Perspex.Win32/Perspex.Win32.csproj
  17. 41
      Windows/Perspex.Win32/PopupImpl.cs
  18. 1
      Windows/Perspex.Win32/Win32Platform.cs
  19. 108
      Windows/Perspex.Win32/WindowImpl.cs

1
Perspex.Controls/DropDown.cs

@ -6,6 +6,7 @@
namespace Perspex.Controls
{
using System;
using Perspex.Controls.Primitives;
using Perspex.Layout;

3
Perspex.Controls/Perspex.Controls.csproj

@ -39,6 +39,7 @@
<Compile Include="DropDown.cs" />
<Compile Include="IContentControl.cs" />
<Compile Include="Platform\IPopupImpl.cs" />
<Compile Include="Platform\ITopLevelImpl.cs" />
<Compile Include="Popup.cs" />
<Compile Include="RadioButton.cs" />
<Compile Include="CheckBox.cs" />
@ -83,6 +84,8 @@
<Compile Include="Shapes\Ellipse.cs" />
<Compile Include="TextWrapping.cs" />
<Compile Include="Utils\StringUtils.cs" />
<Compile Include="TopLevel.cs" />
<Compile Include="PopupRoot.cs" />
<Compile Include="Window.cs" />
<Compile Include="RowDefinition.cs" />
<Compile Include="RowDefinitions.cs" />

24
Perspex.Controls/Platform/IPopupImpl.cs

@ -10,28 +10,10 @@ namespace Perspex.Platform
using Perspex.Controls;
using Perspex.Input.Raw;
public interface IPopupImpl : IDisposable
public interface IPopupImpl : ITopLevelImpl
{
//Size ClientSize { get; }
void Show();
//IPlatformHandle Handle { get; }
//Action Activated { get; set; }
//Action Closed { get; set; }
//Action<RawInputEventArgs> Input { get; set; }
//Action<Rect, IPlatformHandle> Paint { get; set; }
//Action<Size> Resized { get; set; }
//void Invalidate(Rect rect);
//void SetTitle(string title);
//void SetOwner(Window window);
//void Show();
void Hide();
}
}

34
Perspex.Controls/Platform/ITopLevelImpl.cs

@ -0,0 +1,34 @@
// -----------------------------------------------------------------------
// <copyright file="IWindowImpl.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Platform
{
using System;
using Perspex.Controls;
using Perspex.Input;
using Perspex.Input.Raw;
public interface ITopLevelImpl : IDisposable
{
Size ClientSize { get; set; }
IPlatformHandle Handle { get; }
Action Activated { get; set; }
Action Closed { get; set; }
Action<RawInputEventArgs> Input { get; set; }
Action<Rect, IPlatformHandle> Paint { get; set; }
Action<Size> Resized { get; set; }
void Invalidate(Rect rect);
void SetOwner(TopLevel owner);
}
}

26
Perspex.Controls/Platform/IWindowImpl.cs

@ -6,34 +6,14 @@
namespace Perspex.Platform
{
using System;
using Perspex.Controls;
using Perspex.Input.Raw;
public interface IWindowImpl
public interface IWindowImpl : ITopLevelImpl
{
Size ClientSize { get; }
IPlatformHandle Handle { get; }
Action Activated { get; set; }
Action Closed { get; set; }
Action<RawInputEventArgs> Input { get; set; }
Action<Rect, IPlatformHandle> Paint { get; set; }
Action<Size> Resized { get; set; }
IPopupImpl CreatePopup();
void Invalidate(Rect rect);
void SetTitle(string title);
void SetOwner(Window window);
void Show();
void Hide();
}
}

24
Perspex.Controls/Popup.cs

@ -10,12 +10,15 @@ namespace Perspex.Controls
using Perspex.Platform;
using Perspex.Rendering;
public class Popup : ContentControl
public class Popup : Control
{
public static readonly PerspexProperty<Control> ChildProperty =
PerspexProperty.Register<Popup, Control>("Child");
public static readonly PerspexProperty<bool> IsOpenProperty =
PerspexProperty.Register<Popup, bool>("IsOpen");
private IPopupImpl impl;
private PopupRoot root;
private Window window;
@ -39,6 +42,12 @@ namespace Perspex.Controls
});
}
public Control Child
{
get { return this.GetValue(ChildProperty); }
set { this.SetValue(ChildProperty, value); }
}
public bool IsOpen
{
get { return this.GetValue(IsOpenProperty); }
@ -47,17 +56,20 @@ namespace Perspex.Controls
public void Open()
{
if (this.impl == null)
if (this.root == null)
{
this.impl = this.window.CreatePopup();
this.root = new PopupRoot();
this.root[~PopupRoot.ContentProperty] = this[~ChildProperty];
}
this.root.Show();
}
public void Close()
{
if (this.impl != null)
if (this.root != null)
{
this.impl.Dispose();
this.root.Hide();
}
}

41
Perspex.Controls/PopupRoot.cs

@ -0,0 +1,41 @@
// -----------------------------------------------------------------------
// <copyright file="PopupRoot.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls
{
using Perspex.Media;
using Perspex.Platform;
using Splat;
public class PopupRoot : TopLevel
{
static PopupRoot()
{
BackgroundProperty.OverrideDefaultValue(typeof(PopupRoot), Brushes.White);
}
public PopupRoot()
: base(Locator.Current.GetService<IPopupImpl>())
{
}
public new IPopupImpl PlatformImpl
{
get { return (IPopupImpl)base.PlatformImpl; }
}
public void Hide()
{
this.PlatformImpl.Hide();
}
public void Show()
{
this.PlatformImpl.Show();
this.ExecuteLayoutPass();
}
}
}

210
Perspex.Controls/TopLevel.cs

@ -0,0 +1,210 @@
// -----------------------------------------------------------------------
// <copyright file="TopLevel.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls
{
using System;
using System.Reactive.Disposables;
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 abstract class TopLevel : ContentControl, ILayoutRoot, IRenderRoot, ICloseable, IFocusScope
{
public static readonly PerspexProperty<Size> ClientSizeProperty =
PerspexProperty.Register<TopLevel, Size>("ClientSize");
private Dispatcher dispatcher;
private IRenderManager renderManager;
private IRenderer renderer;
private IInputManager inputManager;
private bool autoSizing;
static TopLevel()
{
TopLevel.AffectsMeasure(TopLevel.ClientSizeProperty);
}
public TopLevel(ITopLevelImpl impl)
{
IPlatformRenderInterface renderInterface = Locator.Current.GetService<IPlatformRenderInterface>();
this.PlatformImpl = impl;
this.inputManager = Locator.Current.GetService<IInputManager>();
this.LayoutManager = Locator.Current.GetService<ILayoutManager>();
this.renderManager = Locator.Current.GetService<IRenderManager>();
if (renderInterface == null)
{
throw new InvalidOperationException(
"Could not create an interface to the rendering subsystem: maybe no rendering subsystem was initialized?");
}
if (this.PlatformImpl == 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?");
}
if (this.LayoutManager == null)
{
throw new InvalidOperationException(
"Could not create layout manager: maybe Application.RegisterServices() wasn't called?");
}
if (this.renderManager == null)
{
throw new InvalidOperationException(
"Could not create render manager: maybe Application.RegisterServices() wasn't called?");
}
this.PlatformImpl.SetOwner(this);
this.PlatformImpl.Activated = this.HandleActivated;
this.PlatformImpl.Closed = this.HandleClosed;
this.PlatformImpl.Input = this.HandleInput;
this.PlatformImpl.Paint = this.HandlePaint;
this.PlatformImpl.Resized = this.HandleResized;
Size clientSize = this.ClientSize = this.PlatformImpl.ClientSize;
this.dispatcher = Dispatcher.UIThread;
this.renderer = renderInterface.CreateRenderer(this.PlatformImpl.Handle, clientSize.Width, clientSize.Height);
this.LayoutManager.Root = this;
this.LayoutManager.LayoutNeeded.Subscribe(_ => this.HandleLayoutNeeded());
this.renderManager.RenderNeeded.Subscribe(_ => this.HandleRenderNeeded());
IStyler styler = Locator.Current.GetService<IStyler>();
styler.ApplyStyles(this);
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => this.PlatformImpl.ClientSize = x);
}
public event EventHandler Activated;
public event EventHandler Closed;
public Size ClientSize
{
get { return this.GetValue(ClientSizeProperty); }
set { this.SetValue(ClientSizeProperty, value); }
}
public ILayoutManager LayoutManager
{
get;
private set;
}
public ITopLevelImpl PlatformImpl
{
get;
private set;
}
IRenderer IRenderRoot.Renderer
{
get { return this.renderer; }
}
IRenderManager IRenderRoot.RenderManager
{
get { return this.renderManager; }
}
protected IDisposable BeginAutoSizing()
{
this.autoSizing = true;
return Disposable.Create(() => this.autoSizing = false);
}
protected void ExecuteLayoutPass()
{
this.LayoutManager.ExecuteLayoutPass();
using (this.BeginAutoSizing())
{
this.ClientSize = new Size(
double.IsNaN(this.Width) ? this.DesiredSize.Value.Width : this.ClientSize.Width,
double.IsNaN(this.Height) ? this.DesiredSize.Value.Height : this.ClientSize.Height);
}
this.PlatformImpl.Invalidate(new Rect(this.ClientSize));
}
private void HandleActivated()
{
if (this.Activated != null)
{
this.Activated(this, EventArgs.Empty);
}
FocusManager.Instance.SetFocusScope(this);
}
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.ExecuteLayoutPass, DispatcherPriority.Render);
}
private void HandleRenderNeeded()
{
this.dispatcher.InvokeAsync(
() => this.PlatformImpl.Invalidate(new Rect(this.ClientSize)),
DispatcherPriority.Render);
}
private void HandlePaint(Rect rect, IPlatformHandle handle)
{
this.renderer.Render(this, handle);
this.renderManager.RenderFinished();
}
private void HandleResized(Size clientSize)
{
if (!this.autoSizing)
{
this.Width = clientSize.Width;
this.Height = clientSize.Height;
}
this.ClientSize = clientSize;
this.renderer.Resize((int)clientSize.Width, (int)clientSize.Height);
this.LayoutManager.ExecuteLayoutPass();
this.PlatformImpl.Invalidate(new Rect(clientSize));
}
}
}

171
Perspex.Controls/Window.cs

@ -6,110 +6,29 @@
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, IFocusScope
public class Window : TopLevel
{
public static readonly PerspexProperty<Size> ClientSizeProperty =
PerspexProperty.Register<Window, Size>("ClientSize");
public static readonly PerspexProperty<string> TitleProperty =
public static readonly PerspexProperty<string> TitleProperty =
PerspexProperty.Register<Window, string>("Title", "Window");
private IWindowImpl impl;
private Dispatcher dispatcher;
private IRenderManager renderManager;
private IRenderer renderer;
private IInputManager inputManager;
static Window()
{
BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
Window.AffectsMeasure(Window.ClientSizeProperty);
}
public Window()
: base(Locator.Current.GetService<IWindowImpl>())
{
IPlatformRenderInterface renderInterface = Locator.Current.GetService<IPlatformRenderInterface>();
this.impl = Locator.Current.GetService<IWindowImpl>();
this.inputManager = Locator.Current.GetService<IInputManager>();
this.LayoutManager = Locator.Current.GetService<ILayoutManager>();
this.renderManager = Locator.Current.GetService<IRenderManager>();
if (renderInterface == null)
{
throw new InvalidOperationException(
"Could not create an interface to the rendering subsystem: maybe no rendering subsystem was initialized?");
}
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?");
}
if (this.LayoutManager == null)
{
throw new InvalidOperationException(
"Could not create layout manager: maybe Application.RegisterServices() wasn't called?");
}
if (this.renderManager == null)
{
throw new InvalidOperationException(
"Could not create render 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.Root = this;
this.LayoutManager.LayoutNeeded.Subscribe(_ => this.HandleLayoutNeeded());
this.renderManager.RenderNeeded.Subscribe(_ => this.HandleRenderNeeded());
this.GetObservable(TitleProperty).Subscribe(s => this.impl.SetTitle(s));
IStyler styler = Locator.Current.GetService<IStyler>();
styler.ApplyStyles(this);
}
public event EventHandler Activated;
public event EventHandler Closed;
public Size ClientSize
public new IWindowImpl PlatformImpl
{
get { return this.GetValue(ClientSizeProperty); }
set { this.SetValue(ClientSizeProperty, value); }
get { return (IWindowImpl)base.PlatformImpl; }
}
public string Title
@ -118,86 +37,22 @@ namespace Perspex.Controls
set { this.SetValue(TitleProperty, value); }
}
public ILayoutManager LayoutManager
{
get;
private set;
}
IRenderer IRenderRoot.Renderer
public void Hide()
{
get { return this.renderer; }
}
IRenderManager IRenderRoot.RenderManager
{
get { return this.renderManager; }
}
public void Show()
{
this.impl.Show();
this.LayoutPass();
}
internal IPopupImpl CreatePopup()
{
return this.impl.CreatePopup();
}
private void HandleActivated()
{
if (this.Activated != null)
using (this.BeginAutoSizing())
{
this.Activated(this, EventArgs.Empty);
this.PlatformImpl.Hide();
}
FocusManager.Instance.SetFocusScope(this);
}
private void HandleClosed()
public void Show()
{
if (this.Closed != null)
this.ExecuteLayoutPass();
using (this.BeginAutoSizing())
{
this.Closed(this, EventArgs.Empty);
this.PlatformImpl.Show();
}
}
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.impl.Invalidate(new Rect(this.ClientSize)),
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));
}
}
}

5
Perspex.Layout/LayoutManager.cs

@ -164,7 +164,10 @@ namespace Perspex.Layout
if (!this.Root.IsMeasureValid)
{
this.Root.Measure(this.Root.ClientSize);
var size = new Size(
double.IsNaN(this.Root.Width) ? double.PositiveInfinity : this.Root.Width,
double.IsNaN(this.Root.Height) ? double.PositiveInfinity : this.Root.Height);
this.Root.Measure(size);
}
foreach (var item in measure)

1
Perspex.Themes.Default/DefaultTheme.cs

@ -20,6 +20,7 @@ namespace Perspex.Themes.Default
this.Add(new ItemsControlStyle());
this.Add(new ListBoxStyle());
this.Add(new ListBoxItemStyle());
this.Add(new PopupRootStyle());
this.Add(new RadioButtonStyle());
this.Add(new ScrollBarStyle());
this.Add(new ScrollViewerStyle());

5
Perspex.Themes.Default/DropDownStyle.cs

@ -79,6 +79,11 @@ namespace Perspex.Themes.Default
[~~ToggleButton.IsCheckedProperty] = control[~~DropDown.IsDropDownOpenProperty],
[Grid.ColumnProperty] = 1,
},
new Popup
{
Child = new TextBlock { Text = "Hello World" },
[~Popup.IsOpenProperty] = control[~DropDown.IsDropDownOpenProperty],
}
},
},
};

1
Perspex.Themes.Default/Perspex.Themes.Default.csproj

@ -75,6 +75,7 @@
<Compile Include="ToggleButtonStyle.cs" />
<Compile Include="ListBoxStyle.cs" />
<Compile Include="ListBoxItemStyle.cs" />
<Compile Include="PopupRootStyle.cs" />
<Compile Include="WindowStyle.cs" />
<Compile Include="ScrollViewerStyle.cs" />
<Compile Include="ScrollBarStyle.cs" />

46
Perspex.Themes.Default/PopupRootStyle.cs

@ -0,0 +1,46 @@
// -----------------------------------------------------------------------
// <copyright file="PopupRootStyle.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Themes.Default
{
using System.Linq;
using Perspex.Controls;
using Perspex.Controls.Presenters;
using Perspex.Media;
using Perspex.Styling;
public class PopupRootStyle : Styles
{
public PopupRootStyle()
{
this.AddRange(new[]
{
new Style(x => x.OfType<PopupRoot>())
{
Setters = new[]
{
new Setter(PopupRoot.TemplateProperty, ControlTemplate.Create<PopupRoot>(this.Template)),
new Setter(PopupRoot.FontFamilyProperty, "Segoe UI"),
new Setter(PopupRoot.FontSizeProperty, 12.0),
},
},
});
}
private Control Template(PopupRoot control)
{
return new Border
{
[~Border.BackgroundProperty] = control[~PopupRoot.BackgroundProperty],
Content = new ContentPresenter
{
Id = "contentPresenter",
[~ContentPresenter.ContentProperty] = control[~PopupRoot.ContentProperty],
}
};
}
}
}

55
Windows/Perspex.Win32/Interop/UnmanagedMethods.cs

@ -64,6 +64,23 @@ namespace Perspex.Win32.Interop
SWP_RESIZE = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER
}
public enum ShowWindowCommand
{
Hide = 0,
Normal = 1,
ShowMinimized = 2,
Maximize = 3,
ShowMaximized = 3,
ShowNoActivate = 4,
Show = 5,
Minimize = 6,
ShowMinNoActive = 7,
ShowNA = 8,
Restore = 9,
ShowDefault = 10,
ForceMinimize = 11
}
public enum SystemMetric
{
SM_CXSCREEN = 0, // 0x00
@ -451,12 +468,30 @@ namespace Perspex.Win32.Interop
WM_DISPATCH_WORK_ITEM = WM_USER,
}
[DllImport("user32.dll", SetLastError = true)]
public static extern Boolean AdjustWindowRectEx(ref RECT lpRect, uint dwStyle, bool bMenu, uint dwExStyle);
[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);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr CreateWindowEx(
int dwExStyle,
uint lpClassName,
string lpWindowName,
uint dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
[DllImport("user32.dll")]
public static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
@ -496,6 +531,9 @@ namespace Perspex.Win32.Interop
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(SystemMetric smIndex);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
@ -536,7 +574,7 @@ namespace Perspex.Win32.Interop
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SetWindowPosFlags uFlags);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommand nCmdShow);
[DllImport("user32.dll")]
public static extern int ToUnicode(
@ -557,21 +595,6 @@ namespace Perspex.Win32.Interop
[DllImport("user32.dll")]
public static extern bool UnregisterClass(string lpClassName, IntPtr hInstance);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr CreateWindowEx(
int dwExStyle,
uint lpClassName,
string lpWindowName,
int dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool SetWindowText(IntPtr hwnd, string lpString);

1
Windows/Perspex.Win32/Perspex.Win32.csproj

@ -60,6 +60,7 @@
<Compile Include="Input\WindowsKeyboardDevice.cs" />
<Compile Include="Input\WindowsMouseDevice.cs" />
<Compile Include="Interop\UnmanagedMethods.cs" />
<Compile Include="PopupImpl.cs" />
<Compile Include="WindowImpl.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Win32Platform.cs" />

41
Windows/Perspex.Win32/PopupImpl.cs

@ -0,0 +1,41 @@
// -----------------------------------------------------------------------
// <copyright file="WindowImpl.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Win32
{
using System;
using Perspex.Platform;
using Perspex.Win32.Interop;
public class PopupImpl : WindowImpl, IPopupImpl
{
protected override IntPtr CreateWindowOverride(ushort atom)
{
UnmanagedMethods.WindowStyles style =
UnmanagedMethods.WindowStyles.WS_POPUP |
UnmanagedMethods.WindowStyles.WS_CLIPSIBLINGS;
UnmanagedMethods.WindowStyles exStyle =
UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW |
UnmanagedMethods.WindowStyles.WS_EX_TOPMOST |
UnmanagedMethods.WindowStyles.WS_EX_NOACTIVATE;
return UnmanagedMethods.CreateWindowEx(
(int)exStyle,
atom,
null,
(uint)style,
UnmanagedMethods.CW_USEDEFAULT,
UnmanagedMethods.CW_USEDEFAULT,
UnmanagedMethods.CW_USEDEFAULT,
UnmanagedMethods.CW_USEDEFAULT,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero);
}
}
}

1
Windows/Perspex.Win32/Win32Platform.cs

@ -51,6 +51,7 @@ namespace Perspex.Win32
public static void Initialize()
{
var locator = Locator.CurrentMutable;
locator.Register(() => new PopupImpl(), typeof(IPopupImpl));
locator.Register(() => new WindowImpl(), typeof(IWindowImpl));
locator.Register(() => WindowsKeyboardDevice.Instance, typeof(IKeyboardDevice));
locator.Register(() => instance, typeof(IPlatformSettings));

108
Windows/Perspex.Win32/WindowImpl.cs

@ -24,7 +24,7 @@ namespace Perspex.Win32
private IntPtr hwnd;
private Window owner;
private TopLevel owner;
private bool trackingMouse;
@ -51,6 +51,30 @@ namespace Perspex.Win32
UnmanagedMethods.GetClientRect(this.hwnd, out rect);
return new Size(rect.right, rect.bottom);
}
set
{
if (value != this.ClientSize)
{
var style = UnmanagedMethods.GetWindowLong(this.hwnd, -16);
var exStyle = UnmanagedMethods.GetWindowLong(this.hwnd, -20);
var padding = new UnmanagedMethods.RECT();
if (UnmanagedMethods.AdjustWindowRectEx(ref padding, style, false, exStyle))
{
UnmanagedMethods.SetWindowPos(
this.hwnd,
IntPtr.Zero,
0,
0,
-padding.left + padding.right + (int)value.Width,
-padding.top + padding.bottom + (int)value.Height,
UnmanagedMethods.SetWindowPosFlags.SWP_RESIZE);
}
System.Diagnostics.Debug.WriteLine("ClientSize = " + value);
}
}
}
public IPlatformHandle Handle
@ -61,7 +85,17 @@ namespace Perspex.Win32
public IPopupImpl CreatePopup()
{
throw new NotImplementedException();
return new PopupImpl();
}
public void Dispose()
{
UnmanagedMethods.DestroyWindow(this.hwnd);
}
public void Hide()
{
UnmanagedMethods.ShowWindow(this.hwnd, UnmanagedMethods.ShowWindowCommand.Hide);
}
public void Invalidate(Rect rect)
@ -77,7 +111,7 @@ namespace Perspex.Win32
UnmanagedMethods.InvalidateRect(this.hwnd, ref r, false);
}
public void SetOwner(Window owner)
public void SetOwner(TopLevel owner)
{
this.owner = owner;
}
@ -89,7 +123,24 @@ namespace Perspex.Win32
public void Show()
{
UnmanagedMethods.ShowWindow(this.hwnd, 1);
UnmanagedMethods.ShowWindow(this.hwnd, UnmanagedMethods.ShowWindowCommand.Normal);
}
protected virtual IntPtr CreateWindowOverride(ushort atom)
{
return 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);
}
private void CreateWindow()
@ -117,19 +168,7 @@ namespace Perspex.Win32
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);
this.hwnd = this.CreateWindowOverride(atom);
if (this.hwnd == IntPtr.Zero)
{
@ -159,11 +198,19 @@ namespace Perspex.Win32
switch ((UnmanagedMethods.WindowsMessage)msg)
{
case UnmanagedMethods.WindowsMessage.WM_ACTIVATE:
this.Activated();
if (this.Activated != null)
{
this.Activated();
}
return IntPtr.Zero;
case UnmanagedMethods.WindowsMessage.WM_DESTROY:
this.Closed();
if (this.Closed != null)
{
this.Closed();
}
return IntPtr.Zero;
case UnmanagedMethods.WindowsMessage.WM_KEYDOWN:
@ -236,22 +283,25 @@ namespace Perspex.Win32
break;
case UnmanagedMethods.WindowsMessage.WM_PAINT:
UnmanagedMethods.PAINTSTRUCT ps;
if (UnmanagedMethods.BeginPaint(this.hwnd, out ps) != IntPtr.Zero)
if (this.Paint != null)
{
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), this.Handle);
UnmanagedMethods.EndPaint(this.hwnd, ref ps);
}
UnmanagedMethods.PAINTSTRUCT ps;
if (UnmanagedMethods.BeginPaint(this.hwnd, out ps) != IntPtr.Zero)
{
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), this.Handle);
UnmanagedMethods.EndPaint(this.hwnd, ref ps);
}
}
return IntPtr.Zero;
case UnmanagedMethods.WindowsMessage.WM_SIZE:
if (this.Resized != null)
{
this.Resized(new Size((int)lParam & 0xffff, (int)lParam >> 16));
{
var clientSize = new Size((int)lParam & 0xffff, (int)lParam >> 16);
this.Resized(clientSize);
}
return IntPtr.Zero;

Loading…
Cancel
Save