A cross-platform UI framework for .NET
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.
 
 
 

327 lines
11 KiB

// Copyright (c) The Avalonia 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.Collections.Generic;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Gdk;
using Action = System.Action;
using WindowEdge = Avalonia.Controls.WindowEdge;
using GLib;
namespace Avalonia.Gtk
{
using Gtk = global::Gtk;
public abstract class WindowImplBase : IWindowImpl
{
private IInputRoot _inputRoot;
protected Gtk.Widget _window;
private FramebufferManager _framebuffer;
private Gtk.IMContext _imContext;
private uint _lastKeyEventTimestamp;
private static readonly Gdk.Cursor DefaultCursor = new Gdk.Cursor(CursorType.LeftPtr);
protected WindowImplBase(Gtk.Widget window)
{
_window = window;
_framebuffer = new FramebufferManager(this);
Init();
}
void Init()
{
Handle = _window as IPlatformHandle;
_window.Events = EventMask.AllEventsMask;
_imContext = new Gtk.IMMulticontext();
_imContext.Commit += ImContext_Commit;
_window.Realized += OnRealized;
_window.AppPaintable = true;
_window.DoubleBuffered = false;
_window.Realize();
_window.ButtonPressEvent += OnButtonPressEvent;
_window.ButtonReleaseEvent += OnButtonReleaseEvent;
_window.ScrollEvent += OnScrollEvent;
_window.Destroyed += OnDestroyed;
_window.KeyPressEvent += OnKeyPressEvent;
_window.KeyReleaseEvent += OnKeyReleaseEvent;
_window.ExposeEvent += OnExposeEvent;
_window.MotionNotifyEvent += OnMotionNotifyEvent;
}
public IPlatformHandle Handle { get; private set; }
public Gtk.Widget Widget => _window;
public Gdk.Drawable CurrentDrawable { get; private set; }
void OnRealized (object sender, EventArgs eventArgs)
{
_imContext.ClientWindow = _window.GdkWindow;
}
public abstract 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(_window.Screen.Width, _window.Screen.Height);
}
}
public Avalonia.Controls.WindowState WindowState
{
get
{
switch (_window.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:
_window.GdkWindow.Iconify();
break;
case Controls.WindowState.Maximized:
_window.GdkWindow.Maximize();
break;
case Controls.WindowState.Normal:
_window.GdkWindow.Deiconify();
_window.GdkWindow.Unmaximize();
break;
}
}
}
public double Scaling => 1;
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 Action<Point> PositionChanged { get; set; }
public Action<double> ScalingChanged { get; set; }
public IEnumerable<object> Surfaces => new object[]
{
Handle,
new Func<Gdk.Drawable>(() => CurrentDrawable),
_framebuffer
};
public IPopupImpl CreatePopup()
{
return new PopupImpl();
}
public void Invalidate(Rect rect)
{
if (_window?.GdkWindow != null)
_window.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;
_window.GdkWindow.GetDeskrelativeOrigin(out x, out y);
return new Point(point.X - x, point.Y - y);
}
public Point PointToScreen(Point point)
{
int x, y;
_window.GdkWindow.GetDeskrelativeOrigin(out x, out y);
return new Point(point.X + x, point.Y + y);
}
public void SetInputRoot(IInputRoot inputRoot)
{
_inputRoot = inputRoot;
}
public abstract void SetTitle(string title);
public abstract IDisposable ShowDialog();
public abstract void SetSystemDecorations(bool enabled);
public abstract void SetIcon(IWindowIconImpl icon);
public void SetCursor(IPlatformHandle cursor)
{
_window.GdkWindow.Cursor = cursor != null ? new Gdk.Cursor(cursor.Handle) : DefaultCursor;
}
public void Show() => _window.Show();
public void Hide() => _window.Hide();
public abstract void BeginMoveDrag();
public abstract void BeginResizeDrag(WindowEdge edge);
public abstract Point Position { get; set; }
void ITopLevelImpl.Activate()
{
_window.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;
}
void OnButtonPressEvent(object o, Gtk.ButtonPressEventArgs args)
{
var evnt = args.Event;
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);
}
void OnScrollEvent(object o, Gtk.ScrollEventArgs args)
{
var evnt = args.Event;
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);
}
protected void OnButtonReleaseEvent(object o, Gtk.ButtonReleaseEventArgs args)
{
var evnt = args.Event;
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);
}
void OnDestroyed(object sender, EventArgs eventArgs)
{
Closed();
}
private void ProcessKeyEvent(EventKey evnt)
{
_lastKeyEventTimestamp = evnt.Time;
if (_imContext.FilterKeypress(evnt))
return;
var e = new RawKeyEventArgs(
GtkKeyboardDevice.Instance,
evnt.Time,
evnt.Type == EventType.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
Common.KeyTransform.ConvertKey(evnt.Key), GetModifierKeys(evnt.State));
Input(e);
}
[ConnectBefore]
void OnKeyPressEvent(object o, Gtk.KeyPressEventArgs args)
{
args.RetVal = true;
ProcessKeyEvent(args.Event);
}
void OnKeyReleaseEvent(object o, Gtk.KeyReleaseEventArgs args)
{
args.RetVal = true;
ProcessKeyEvent(args.Event);
}
private void ImContext_Commit(object o, Gtk.CommitArgs args)
{
Input(new RawTextInputEventArgs(GtkKeyboardDevice.Instance, _lastKeyEventTimestamp, args.Str));
}
void OnExposeEvent(object o, Gtk.ExposeEventArgs args)
{
CurrentDrawable = args.Event.Window;
Paint(args.Event.Area.ToAvalonia());
CurrentDrawable = null;
args.RetVal = true;
}
void OnMotionNotifyEvent(object o, Gtk.MotionNotifyEventArgs args)
{
var evnt = args.Event;
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);
args.RetVal = true;
}
public void Dispose()
{
_framebuffer.Dispose();
_window.Hide();
_window.Dispose();
_window = null;
}
}
}