Browse Source

Implemented cursors. Ceterum censeo StyleCopum esse delendum.

pull/120/head
Nikita Tsukanov 11 years ago
parent
commit
7f72c7763b
  1. 77
      src/Gtk/Perspex.Gtk/CursorFactory.cs
  2. 1
      src/Gtk/Perspex.Gtk/GtkPlatform.cs
  3. 1
      src/Gtk/Perspex.Gtk/Perspex.Gtk.csproj
  4. 8
      src/Gtk/Perspex.Gtk/WindowImpl.cs
  5. 6
      src/Perspex.Controls/Platform/ITopLevelImpl.cs
  6. 1
      src/Perspex.Controls/TextBox.cs
  7. 4
      src/Perspex.Controls/TopLevel.cs
  8. 58
      src/Perspex.Input/Cursors.cs
  9. 7
      src/Perspex.Input/IInputElement.cs
  10. 15
      src/Perspex.Input/InputElement.cs
  11. 2
      src/Perspex.Input/Perspex.Input.csproj
  12. 14
      src/Perspex.Input/Platform/IStandardCursorFactory.cs
  13. 61
      src/Windows/Perspex.Win32/CursorFactory.cs
  14. 25
      src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs
  15. 2
      src/Windows/Perspex.Win32/Perspex.Win32.csproj
  16. 14
      src/Windows/Perspex.Win32/PlatformConstants.cs
  17. 1
      src/Windows/Perspex.Win32/Win32Platform.cs
  18. 14
      src/Windows/Perspex.Win32/WindowImpl.cs

77
src/Gtk/Perspex.Gtk/CursorFactory.cs

@ -0,0 +1,77 @@
namespace Perspex.Gtk
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Gdk;
using Gtk = global::Gtk;
using Perspex.Input;
using Perspex.Platform;
class CursorFactory : IStandardCursorFactory
{
public static CursorFactory Instance { get; } = new CursorFactory();
private CursorFactory()
{
}
private static readonly Dictionary<StandardCursorType, object> CursorTypeMapping = new Dictionary
<StandardCursorType, object>
{
{ StandardCursorType.AppStarting, CursorType.Watch },
{ StandardCursorType.Arrow, CursorType.LeftPtr },
{ StandardCursorType.Cross, CursorType.Cross },
{ StandardCursorType.Hand, CursorType.Hand1 },
{ StandardCursorType.Ibeam, CursorType.Xterm },
{ StandardCursorType.No, Gtk.Stock.Cancel},
{ StandardCursorType.SizeAll, CursorType.Sizing },
//{ StandardCursorType.SizeNorthEastSouthWest, 32643 },
{ StandardCursorType.SizeNorthSouth, CursorType.SbVDoubleArrow},
//{ StandardCursorType.SizeNorthWestSouthEast, 32642 },
{ StandardCursorType.SizeWestEast, CursorType.SbHDoubleArrow },
{ StandardCursorType.UpArrow, CursorType.BasedArrowUp },
{ StandardCursorType.Wait, CursorType.Watch },
{ StandardCursorType.Help, Gtk.Stock.Help }
};
private static readonly Dictionary<StandardCursorType, IPlatformHandle> Cache =
new Dictionary<StandardCursorType, IPlatformHandle>();
Gdk.Cursor GetCursor(object desc)
{
Gdk.Cursor rv;
var name = desc as string;
if (name != null)
{
var theme = Gtk.IconTheme.Default;
var icon = theme.LoadIcon(name, 32, default(Gtk.IconLookupFlags));
rv = icon == null ? new Gdk.Cursor(CursorType.XCursor) : new Gdk.Cursor(Gdk.Display.Default, icon, 0, 0);
}
else
{
rv = new Gdk.Cursor((CursorType) desc);
}
rv.Owned = false;
return rv;
}
public IPlatformHandle GetCursor(StandardCursorType cursorType)
{
IPlatformHandle rv;
if (!Cache.TryGetValue(cursorType, out rv))
{
Cache[cursorType] =
rv =
new PlatformHandle(
GetCursor(CursorTypeMapping[cursorType]).Handle,
"GTKCURSOR");
}
return rv;
}
}
}

1
src/Gtk/Perspex.Gtk/GtkPlatform.cs

@ -43,6 +43,7 @@ namespace Perspex.Gtk
locator.Register(() => new WindowImpl(), typeof(IWindowImpl));
locator.Register(() => new PopupImpl(), typeof(IPopupImpl));
locator.Register(() => new ClipboardImpl(), typeof (IClipboard));
locator.Register(() => CursorFactory.Instance, typeof(IStandardCursorFactory));
locator.Register(() => GtkKeyboardDevice.Instance, typeof(IKeyboardDevice));
locator.Register(() => instance, typeof(IPlatformSettings));
locator.Register(() => instance, typeof(IPlatformThreadingInterface));

1
src/Gtk/Perspex.Gtk/Perspex.Gtk.csproj

@ -60,6 +60,7 @@
<ItemGroup>
<Compile Include="AssetLoader.cs" />
<Compile Include="ClipboardImpl.cs" />
<Compile Include="CursorFactory.cs" />
<Compile Include="GtkExtensions.cs" />
<Compile Include="PopupImpl.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

8
src/Gtk/Perspex.Gtk/WindowImpl.cs

@ -29,6 +29,8 @@ namespace Perspex.Gtk
private uint lastKeyEventTimestamp;
private static readonly Gdk.Cursor DefaultCursor = new Gdk.Cursor(CursorType.LeftPtr);
public WindowImpl()
: base(Gtk.WindowType.Toplevel)
{
@ -105,6 +107,12 @@ namespace Perspex.Gtk
this.Title = title;
}
public void SetCursor(IPlatformHandle cursor)
{
GdkWindow.Cursor = cursor != null ? new Gdk.Cursor(cursor.Handle) : DefaultCursor;
}
public IDisposable ShowDialog()
{
this.Modal = true;

6
src/Perspex.Controls/Platform/ITopLevelImpl.cs

@ -36,5 +36,11 @@ namespace Perspex.Platform
void SetOwner(TopLevel owner);
Point PointToScreen(Point point);
/// <summary>
/// Sets the cursor associated with the window.
/// </summary>
/// <param name="cursor">The cursor. Use null for default cursor</param>
void SetCursor(IPlatformHandle cursor);
}
}

1
src/Perspex.Controls/TextBox.cs

@ -115,6 +115,7 @@ namespace Perspex.Controls
protected override void OnTemplateApplied()
{
this.presenter = this.GetTemplateChild<TextPresenter>("textPresenter");
this.presenter.Cursor = new Cursor(StandardCursorType.Ibeam);
}
protected override void OnGotFocus(GotFocusEventArgs e)

4
src/Perspex.Controls/TopLevel.cs

@ -162,6 +162,10 @@ namespace Perspex.Controls
styler?.ApplyStyles(this);
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => this.PlatformImpl.ClientSize = x);
this.GetObservable(TopLevel.PointerOverElementProperty)
.Select(
x => (x as InputElement)?.GetObservable(InputElement.CursorProperty) ?? Observable.Empty<Cursor>())
.Switch().Subscribe(cursor => this.PlatformImpl.SetCursor(cursor?.PlatformCursor));
}
/// <summary>

58
src/Perspex.Input/Cursors.cs

@ -0,0 +1,58 @@
namespace Perspex.Input
{
using Splat;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Perspex.Platform;
/*
=========================================================================================
NOTE: Cursors are NOT disposable and are cached in platform implementation.
To support loading custom cursors some measures about that should be taken beforehand
=========================================================================================
*/
public enum StandardCursorType
{
Arrow,
Ibeam,
Wait,
Cross,
UpArrow,
SizeWestEast,
SizeNorthSouth,
SizeAll,
No,
Hand,
AppStarting,
Help
// Not available in GTK directly, see http://www.pixelbeat.org/programming/x_cursors/
// We might enable them later, preferably, by loading pixmax direclty from theme with fallback image
// SizeNorthWestSouthEast,
// SizeNorthEastSouthWest,
}
public class Cursor
{
public static Cursor Default = new Cursor(StandardCursorType.Arrow);
internal Cursor(IPlatformHandle platformCursor)
{
this.PlatformCursor = platformCursor;
}
public Cursor(StandardCursorType cursorType)
: this(
((IStandardCursorFactory) Locator.Current.GetService(typeof(IStandardCursorFactory))).GetCursor(
cursorType))
{
}
public IPlatformHandle PlatformCursor { get; }
}
}

7
src/Perspex.Input/IInputElement.cs

@ -4,6 +4,8 @@
// </copyright>
// -----------------------------------------------------------------------
using System.Diagnostics.Contracts;
namespace Perspex.Input
{
using System;
@ -79,6 +81,11 @@ namespace Perspex.Input
/// </summary>
bool IsEnabled { get; }
/// <summary>
/// Gets or sets assicitated mouse cursor.
/// </summary>
Cursor Cursor { get; }
/// <summary>
/// Gets a value indicating whether the control is effectively enabled for user interaction.
/// </summary>

15
src/Perspex.Input/InputElement.cs

@ -35,6 +35,12 @@ namespace Perspex.Input
public static readonly PerspexProperty<bool> IsEnabledCoreProperty =
PerspexProperty.Register<InputElement, bool>("IsEnabledCore", true);
/// <summary>
/// Gets or sets associated mouse cursor.
/// </summary>
public static readonly PerspexProperty<Cursor> CursorProperty =
PerspexProperty.Register<InputElement, Cursor>("Cursor", null, true);
/// <summary>
/// Defines the <see cref="IsFocused"/> property.
/// </summary>
@ -270,6 +276,15 @@ namespace Perspex.Input
set { this.SetValue(IsEnabledProperty, value); }
}
/// <summary>
/// Gets or sets associated mouse cursor.
/// </summary>
public Cursor Cursor
{
get { return this.GetValue(CursorProperty); }
set { this.SetValue(CursorProperty, value); }
}
/// <summary>
/// Gets or sets a value indicating whether the control is focused.
/// </summary>

2
src/Perspex.Input/Perspex.Input.csproj

@ -64,9 +64,11 @@
<Compile Include="..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Cursors.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="AccessKeyHandler.cs" />
<Compile Include="Platform\IClipboard.cs" />
<Compile Include="Platform\IStandardCursorFactory.cs" />
<Compile Include="Raw\RawTextInputEventArgs.cs" />
<Compile Include="NavigationMethod.cs" />
<Compile Include="IInputRoot.cs" />

14
src/Perspex.Input/Platform/IStandardCursorFactory.cs

@ -0,0 +1,14 @@
namespace Perspex.Platform
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Perspex.Input;
public interface IStandardCursorFactory
{
IPlatformHandle GetCursor(StandardCursorType cursorType);
}
}

61
src/Windows/Perspex.Win32/CursorFactory.cs

@ -0,0 +1,61 @@
using Perspex.Win32.Interop;
namespace Perspex.Win32
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Perspex.Input;
using Perspex.Platform;
class CursorFactory : IStandardCursorFactory
{
public static CursorFactory Instance { get; } = new CursorFactory();
private CursorFactory()
{
}
private static readonly Dictionary<StandardCursorType, int> CursorTypeMapping = new Dictionary
<StandardCursorType, int>
{
{ StandardCursorType.AppStarting, 32650 },
{ StandardCursorType.Arrow, 32512 },
{ StandardCursorType.Cross, 32515 },
{ StandardCursorType.Hand, 32649 },
{ StandardCursorType.Help, 32651 },
{ StandardCursorType.Ibeam, 32513 },
{ StandardCursorType.No, 32648 },
{ StandardCursorType.SizeAll, 32646 },
// { StandardCursorType.SizeNorthEastSouthWest, 32643 },
{ StandardCursorType.SizeNorthSouth, 32645 },
// { StandardCursorType.SizeNorthWestSouthEast, 32642 },
{ StandardCursorType.SizeWestEast, 32644 },
{ StandardCursorType.UpArrow, 32516 },
{ StandardCursorType.Wait, 32514 }
};
private static readonly Dictionary<StandardCursorType, IPlatformHandle> Cache =
new Dictionary<StandardCursorType, IPlatformHandle>();
public IPlatformHandle GetCursor(StandardCursorType cursorType)
{
IPlatformHandle rv;
if (!Cache.TryGetValue(cursorType, out rv))
{
Cache[cursorType] =
rv =
new PlatformHandle(
UnmanagedMethods.LoadCursor(IntPtr.Zero, new IntPtr(CursorTypeMapping[cursorType])),
PlatformConstants.CursorHandleType);
}
return rv;
}
}
}

25
src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs

@ -572,7 +572,9 @@ namespace Perspex.Win32.Interop
public static extern bool KillTimer(IntPtr hWnd, IntPtr uIDEvent);
[DllImport("user32.dll")]
public static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
public static extern IntPtr LoadCursor(IntPtr hInstance, IntPtr lpCursorName);
[DllImport("user32.dll")]
public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
@ -627,6 +629,27 @@ namespace Perspex.Win32.Interop
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool SetWindowText(IntPtr hwnd, string lpString);
public enum ClassLongIndex : int
{
GCL_HCURSOR = -12,
GCL_HICON = -14
}
[DllImport("user32.dll", EntryPoint = "SetClassLongPtr")]
private static extern IntPtr SetClassLong64(IntPtr hWnd, ClassLongIndex nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetClassLong")]
private static extern IntPtr SetClassLong32(IntPtr hWnd, ClassLongIndex nIndex, IntPtr dwNewLong);
public static IntPtr SetClassLong(IntPtr hWnd, ClassLongIndex nIndex, IntPtr dwNewLong)
{
if (IntPtr.Size == 4)
{
return SetClassLong32(hWnd, nIndex, dwNewLong);
}
return SetClassLong64(hWnd, nIndex, dwNewLong);
}
[DllImport("user32.dll", SetLastError = true)]
public static extern bool OpenClipboard(IntPtr hWndOwner);

2
src/Windows/Perspex.Win32/Perspex.Win32.csproj

@ -67,10 +67,12 @@
</Compile>
<Compile Include="AssetLoader.cs" />
<Compile Include="ClipboardImpl.cs" />
<Compile Include="CursorFactory.cs" />
<Compile Include="Input\KeyInterop.cs" />
<Compile Include="Input\WindowsKeyboardDevice.cs" />
<Compile Include="Input\WindowsMouseDevice.cs" />
<Compile Include="Interop\UnmanagedMethods.cs" />
<Compile Include="PlatformConstants.cs" />
<Compile Include="PopupImpl.cs" />
<Compile Include="WindowImpl.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

14
src/Windows/Perspex.Win32/PlatformConstants.cs

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Perspex.Win32
{
public static class PlatformConstants
{
public const string WindowHandleType = "HWND";
public const string CursorHandleType = "HCURSOR";
}
}

1
src/Windows/Perspex.Win32/Win32Platform.cs

@ -57,6 +57,7 @@ namespace Perspex.Win32
locator.Register(() => new ClipboardImpl(), typeof(IClipboard));
locator.Register(() => WindowsKeyboardDevice.Instance, typeof(IKeyboardDevice));
locator.Register(() => WindowsMouseDevice.Instance, typeof(IMouseDevice));
locator.Register(() => CursorFactory.Instance, typeof(IStandardCursorFactory));
locator.Register(() => instance, typeof(IPlatformSettings));
locator.Register(() => instance, typeof(IPlatformThreadingInterface));
locator.RegisterConstant(new AssetLoader(), typeof(IAssetLoader));

14
src/Windows/Perspex.Win32/WindowImpl.cs

@ -26,6 +26,9 @@ namespace Perspex.Win32
{
private static List<WindowImpl> instances = new List<WindowImpl>();
private static readonly IntPtr DefaultCursor = UnmanagedMethods.LoadCursor(
IntPtr.Zero, new IntPtr((int) UnmanagedMethods.Cursor.IDC_ARROW));
private UnmanagedMethods.WndProc wndProcDelegate;
private string className;
@ -185,6 +188,12 @@ namespace Perspex.Win32
});
}
public void SetCursor(IPlatformHandle cursor)
{
UnmanagedMethods.SetClassLong(this.hwnd, UnmanagedMethods.ClassLongIndex.GCL_HCURSOR,
cursor?.Handle ?? DefaultCursor);
}
protected virtual IntPtr CreateWindowOverride(ushort atom)
{
return UnmanagedMethods.CreateWindowEx(
@ -244,6 +253,7 @@ namespace Perspex.Win32
case UnmanagedMethods.WindowsMessage.WM_DESTROY:
if (this.Closed != null)
{
UnmanagedMethods.UnregisterClass(this.className, Marshal.GetHINSTANCE(this.GetType().Module));
this.Closed();
}
@ -384,7 +394,7 @@ namespace Perspex.Win32
style = 0,
lpfnWndProc = this.wndProcDelegate,
hInstance = Marshal.GetHINSTANCE(this.GetType().Module),
hCursor = UnmanagedMethods.LoadCursor(IntPtr.Zero, (int)UnmanagedMethods.Cursor.IDC_ARROW),
hCursor = DefaultCursor,
hbrBackground = (IntPtr)5,
lpszClassName = this.className,
};
@ -403,7 +413,7 @@ namespace Perspex.Win32
throw new Win32Exception();
}
this.Handle = new PlatformHandle(this.hwnd, "HWND");
this.Handle = new PlatformHandle(this.hwnd, PlatformConstants.WindowHandleType);
}
private Point ScreenToClient(uint x, uint y)

Loading…
Cancel
Save