csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
383 lines
11 KiB
383 lines
11 KiB
// Copyright (c) The Perspex Project. All rights reserved.
|
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
|
|
using System;
|
|
using System.Reactive.Disposables;
|
|
using System.Runtime.InteropServices;
|
|
using Gdk;
|
|
using Perspex.Controls;
|
|
using Perspex.Input.Raw;
|
|
using Perspex.Platform;
|
|
using Perspex.Input;
|
|
using Perspex.Threading;
|
|
using Action = System.Action;
|
|
using WindowEdge = Perspex.Controls.WindowEdge;
|
|
|
|
namespace Perspex.Gtk
|
|
{
|
|
using Gtk = global::Gtk;
|
|
|
|
public class WindowImpl : Gtk.Window, IWindowImpl, IPlatformHandle
|
|
{
|
|
private IInputRoot _inputRoot;
|
|
|
|
private Size _clientSize;
|
|
|
|
private Gtk.IMContext _imContext;
|
|
|
|
private uint _lastKeyEventTimestamp;
|
|
|
|
private static readonly Gdk.Cursor DefaultCursor = new Gdk.Cursor(CursorType.LeftPtr);
|
|
|
|
public WindowImpl()
|
|
: base(Gtk.WindowType.Toplevel)
|
|
{
|
|
DefaultSize = new Gdk.Size(900, 480);
|
|
Init();
|
|
}
|
|
|
|
public WindowImpl(Gtk.WindowType type)
|
|
: base(type)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
private void Init()
|
|
{
|
|
Events = EventMask.PointerMotionMask |
|
|
EventMask.ButtonPressMask |
|
|
EventMask.ButtonReleaseMask;
|
|
_imContext = new Gtk.IMMulticontext();
|
|
_imContext.Commit += ImContext_Commit;
|
|
DoubleBuffered = false;
|
|
Realize();
|
|
}
|
|
|
|
protected override void OnRealized ()
|
|
{
|
|
base.OnRealized ();
|
|
_imContext.ClientWindow = this.GdkWindow;
|
|
}
|
|
|
|
public Size ClientSize
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public Size MaxClientSize
|
|
{
|
|
get
|
|
{
|
|
// TODO: This should take into account things such as taskbar and window border
|
|
// thickness etc.
|
|
return new Size(Screen.Width, Screen.Height);
|
|
}
|
|
}
|
|
|
|
public Perspex.Controls.WindowState WindowState
|
|
{
|
|
get
|
|
{
|
|
switch (GdkWindow.State)
|
|
{
|
|
case Gdk.WindowState.Iconified:
|
|
return Controls.WindowState.Minimized;
|
|
case Gdk.WindowState.Maximized:
|
|
return Controls.WindowState.Maximized;
|
|
default:
|
|
return Controls.WindowState.Normal;
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
switch (value)
|
|
{
|
|
case Controls.WindowState.Minimized:
|
|
GdkWindow.Iconify();
|
|
break;
|
|
case Controls.WindowState.Maximized:
|
|
GdkWindow.Maximize();
|
|
break;
|
|
case Controls.WindowState.Normal:
|
|
GdkWindow.Deiconify();
|
|
GdkWindow.Unmaximize();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
IPlatformHandle ITopLevelImpl.Handle => this;
|
|
|
|
[DllImport("libgdk-win32-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
extern static IntPtr gdk_win32_drawable_get_handle(IntPtr gdkWindow);
|
|
|
|
[DllImport("libgtk-x11-2.0.so.0", CallingConvention = CallingConvention.Cdecl)]
|
|
extern static IntPtr gdk_x11_drawable_get_xid(IntPtr gdkWindow);
|
|
|
|
[DllImport("libgdk-quartz-2.0-0.dylib", CallingConvention = CallingConvention.Cdecl)]
|
|
extern static IntPtr gdk_quartz_window_get_nswindow(IntPtr gdkWindow);
|
|
|
|
IntPtr _nativeWindow;
|
|
|
|
IntPtr GetNativeWindow()
|
|
{
|
|
IntPtr h = GdkWindow.Handle;
|
|
if (_nativeWindow != IntPtr.Zero)
|
|
return _nativeWindow;
|
|
//Try whatever backend that works
|
|
try
|
|
{
|
|
return _nativeWindow = gdk_quartz_window_get_nswindow(h);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
try
|
|
{
|
|
return _nativeWindow = gdk_x11_drawable_get_xid(h);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
return _nativeWindow = gdk_win32_drawable_get_handle(h);
|
|
}
|
|
|
|
|
|
IntPtr IPlatformHandle.Handle => GetNativeWindow();
|
|
public string HandleDescriptor => "HWND";
|
|
|
|
public Action Activated { get; set; }
|
|
|
|
public Action Closed { get; set; }
|
|
|
|
public Action Deactivated { get; set; }
|
|
|
|
public Action<RawInputEventArgs> Input { get; set; }
|
|
|
|
public Action<Rect> Paint { get; set; }
|
|
|
|
public Action<Size> Resized { get; set; }
|
|
|
|
public IPopupImpl CreatePopup()
|
|
{
|
|
return new PopupImpl();
|
|
}
|
|
|
|
public void Invalidate(Rect rect)
|
|
{
|
|
if (base.GdkWindow != null)
|
|
base.GdkWindow.InvalidateRect(
|
|
new Rectangle((int) rect.X, (int) rect.Y, (int) rect.Width, (int) rect.Height), true);
|
|
}
|
|
|
|
public Point PointToClient(Point point)
|
|
{
|
|
int x, y;
|
|
GdkWindow.GetDeskrelativeOrigin(out x, out y);
|
|
|
|
return new Point(point.X - x, point.Y - y);
|
|
}
|
|
|
|
public Point PointToScreen(Point point)
|
|
{
|
|
int x, y;
|
|
GdkWindow.GetDeskrelativeOrigin(out x, out y);
|
|
|
|
return new Point(point.X + x, point.Y + y);
|
|
}
|
|
|
|
public void SetInputRoot(IInputRoot inputRoot)
|
|
{
|
|
_inputRoot = inputRoot;
|
|
}
|
|
|
|
public void SetTitle(string title)
|
|
{
|
|
Title = title;
|
|
}
|
|
|
|
|
|
public void SetCursor(IPlatformHandle cursor)
|
|
{
|
|
GdkWindow.Cursor = cursor != null ? new Gdk.Cursor(cursor.Handle) : DefaultCursor;
|
|
}
|
|
|
|
public void BeginMoveDrag()
|
|
{
|
|
int x, y;
|
|
ModifierType mod;
|
|
Screen.RootWindow.GetPointer(out x, out y, out mod);
|
|
BeginMoveDrag(1, x, y, 0);
|
|
}
|
|
|
|
public void BeginResizeDrag(WindowEdge edge)
|
|
{
|
|
int x, y;
|
|
ModifierType mod;
|
|
Screen.RootWindow.GetPointer(out x, out y, out mod);
|
|
BeginResizeDrag((Gdk.WindowEdge) (int) edge, 1, x, y, 0);
|
|
}
|
|
|
|
public Point Position
|
|
{
|
|
get
|
|
{
|
|
int x, y;
|
|
GetPosition(out x, out y);
|
|
return new Point(x, y);
|
|
}
|
|
set
|
|
{
|
|
Move((int)value.X, (int)value.Y);
|
|
}
|
|
}
|
|
|
|
public IDisposable ShowDialog()
|
|
{
|
|
Modal = true;
|
|
Show();
|
|
|
|
return Disposable.Empty;
|
|
}
|
|
|
|
public void SetSystemDecorations(bool enabled) => Decorated = enabled;
|
|
|
|
void ITopLevelImpl.Activate()
|
|
{
|
|
Activate();
|
|
}
|
|
|
|
private static InputModifiers GetModifierKeys(ModifierType state)
|
|
{
|
|
var rv = InputModifiers.None;
|
|
if (state.HasFlag(ModifierType.ControlMask))
|
|
rv |= InputModifiers.Control;
|
|
if (state.HasFlag(ModifierType.ShiftMask))
|
|
rv |= InputModifiers.Shift;
|
|
if (state.HasFlag(ModifierType.Mod1Mask))
|
|
rv |= InputModifiers.Control;
|
|
if(state.HasFlag(ModifierType.Button1Mask))
|
|
rv |= InputModifiers.LeftMouseButton;
|
|
if (state.HasFlag(ModifierType.Button2Mask))
|
|
rv |= InputModifiers.RightMouseButton;
|
|
if (state.HasFlag(ModifierType.Button3Mask))
|
|
rv |= InputModifiers.MiddleMouseButton;
|
|
return rv;
|
|
}
|
|
|
|
protected override bool OnButtonPressEvent(EventButton evnt)
|
|
{
|
|
|
|
var e = new RawMouseEventArgs(
|
|
GtkMouseDevice.Instance,
|
|
evnt.Time,
|
|
_inputRoot,
|
|
evnt.Button == 1
|
|
? RawMouseEventType.LeftButtonDown
|
|
: evnt.Button == 3 ? RawMouseEventType.RightButtonDown : RawMouseEventType.MiddleButtonDown,
|
|
new Point(evnt.X, evnt.Y), GetModifierKeys(evnt.State));
|
|
Input(e);
|
|
return true;
|
|
}
|
|
|
|
protected override bool OnScrollEvent(EventScroll evnt)
|
|
{
|
|
double step = 1;
|
|
var delta = new Vector();
|
|
if (evnt.Direction == ScrollDirection.Down)
|
|
delta = new Vector(0, -step);
|
|
else if (evnt.Direction == ScrollDirection.Up)
|
|
delta = new Vector(0, step);
|
|
else if (evnt.Direction == ScrollDirection.Right)
|
|
delta = new Vector(-step, 0);
|
|
if (evnt.Direction == ScrollDirection.Left)
|
|
delta = new Vector(step, 0);
|
|
var e = new RawMouseWheelEventArgs(GtkMouseDevice.Instance, evnt.Time, _inputRoot, new Point(evnt.X, evnt.Y), delta, GetModifierKeys(evnt.State));
|
|
Input(e);
|
|
return base.OnScrollEvent(evnt);
|
|
}
|
|
|
|
protected override bool OnButtonReleaseEvent(EventButton evnt)
|
|
{
|
|
var e = new RawMouseEventArgs(
|
|
GtkMouseDevice.Instance,
|
|
evnt.Time,
|
|
_inputRoot,
|
|
evnt.Button == 1
|
|
? RawMouseEventType.LeftButtonUp
|
|
: evnt.Button == 3 ? RawMouseEventType.RightButtonUp : RawMouseEventType.MiddleButtonUp,
|
|
new Point(evnt.X, evnt.Y), GetModifierKeys(evnt.State));
|
|
Input(e);
|
|
return true;
|
|
}
|
|
|
|
protected override bool OnConfigureEvent(EventConfigure evnt)
|
|
{
|
|
var newSize = new Size(evnt.Width, evnt.Height);
|
|
|
|
if (newSize != _clientSize)
|
|
{
|
|
Resized(newSize);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected override void OnDestroyed()
|
|
{
|
|
Closed();
|
|
}
|
|
|
|
private bool ProcessKeyEvent(EventKey evnt)
|
|
{
|
|
_lastKeyEventTimestamp = evnt.Time;
|
|
if (_imContext.FilterKeypress(evnt))
|
|
return true;
|
|
var e = new RawKeyEventArgs(
|
|
GtkKeyboardDevice.Instance,
|
|
evnt.Time,
|
|
evnt.Type == EventType.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
|
|
GtkKeyboardDevice.ConvertKey(evnt.Key), GetModifierKeys(evnt.State));
|
|
Input(e);
|
|
return true;
|
|
}
|
|
|
|
protected override bool OnKeyPressEvent(EventKey evnt) => ProcessKeyEvent(evnt);
|
|
|
|
protected override bool OnKeyReleaseEvent(EventKey evnt) => ProcessKeyEvent(evnt);
|
|
|
|
private void ImContext_Commit(object o, Gtk.CommitArgs args)
|
|
{
|
|
Input(new RawTextInputEventArgs(GtkKeyboardDevice.Instance, _lastKeyEventTimestamp, args.Str));
|
|
}
|
|
|
|
protected override bool OnExposeEvent(EventExpose evnt)
|
|
{
|
|
Paint(evnt.Area.ToPerspex());
|
|
return true;
|
|
}
|
|
|
|
protected override void OnFocusActivated()
|
|
{
|
|
Activated();
|
|
}
|
|
|
|
protected override bool OnMotionNotifyEvent(EventMotion evnt)
|
|
{
|
|
var position = new Point(evnt.X, evnt.Y);
|
|
|
|
GtkMouseDevice.Instance.SetClientPosition(position);
|
|
|
|
var e = new RawMouseEventArgs(
|
|
GtkMouseDevice.Instance,
|
|
evnt.Time,
|
|
_inputRoot,
|
|
RawMouseEventType.Move,
|
|
position, GetModifierKeys(evnt.State));
|
|
Input(e);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|