Browse Source

Merge branch 'master' into master

pull/1968/head
Nikita Tsukanov 8 years ago
committed by GitHub
parent
commit
b8aabe5cd1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/Android/Avalonia.Android/AndroidPlatform.cs
  2. 9
      src/Avalonia.Controls/Application.cs
  3. 26
      src/Avalonia.Controls/Remote/RemoteWidget.cs
  4. 161
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  5. 378
      src/Avalonia.Controls/TextBox.cs
  6. 2
      src/Avalonia.DesignerSupport/DesignWindowLoader.cs
  7. 3
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs
  8. 10
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  9. 4
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  10. 18
      src/Avalonia.Input/Key.cs
  11. 11
      src/Avalonia.Input/KeyGesture.cs
  12. 98
      src/Avalonia.Input/Platform/PlatformHotkeyConfiguration.cs
  13. 6
      src/Avalonia.Remote.Protocol/InputMessages.cs
  14. 7
      src/Avalonia.Remote.Protocol/ViewportMessages.cs
  15. 2
      src/Avalonia.Styling/Styling/IStyle.cs
  16. 52
      src/Avalonia.Styling/Styling/Style.cs
  17. 8
      src/Avalonia.Styling/Styling/Styles.cs
  18. 9
      src/Avalonia.Styling/Styling/packages.config
  19. 1
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  20. 2
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  21. 2
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  22. 10
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  23. 37
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs
  24. 18
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
  25. 10
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  26. 1
      src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs
  27. 1
      src/Windows/Avalonia.Win32/Win32Platform.cs
  28. 1
      src/iOS/Avalonia.iOS/iOSPlatform.cs
  29. 18
      tests/Avalonia.Controls.UnitTests/ApplicationTests.cs
  30. 41
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  31. 25
      tests/Avalonia.Styling.UnitTests/StyleTests.cs
  32. 2
      tests/Avalonia.UnitTests/UnitTestApplication.cs

1
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -54,6 +54,7 @@ namespace Avalonia.Android
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>() .Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>()
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60)) .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<IRenderLoop>().ToConstant(new RenderLoop()) .Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IAssetLoader>().ToConstant(new AssetLoader(app.GetType().Assembly)); .Bind<IAssetLoader>().ToConstant(new AssetLoader(app.GetType().Assembly));
SkiaPlatform.Initialize(); SkiaPlatform.Initialize();

9
src/Avalonia.Controls/Application.cs

@ -122,11 +122,11 @@ namespace Avalonia
if (_resources != null) if (_resources != null)
{ {
hadResources = _resources.Count > 0; hadResources = _resources.Count > 0;
_resources.ResourcesChanged -= ResourcesChanged; _resources.ResourcesChanged -= ThisResourcesChanged;
} }
_resources = value; _resources = value;
_resources.ResourcesChanged += ResourcesChanged; _resources.ResourcesChanged += ThisResourcesChanged;
if (hadResources || _resources.Count > 0) if (hadResources || _resources.Count > 0)
{ {
@ -343,5 +343,10 @@ namespace Avalonia
.Bind<IGlobalClock>().ToConstant(clock) .Bind<IGlobalClock>().ToConstant(clock)
.GetService<IRenderLoop>()?.Add(clock); .GetService<IRenderLoop>()?.Add(clock);
} }
private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, e);
}
} }
} }

26
src/Avalonia.Controls/Remote/RemoteWidget.cs

@ -11,11 +11,19 @@ namespace Avalonia.Controls.Remote
{ {
public class RemoteWidget : Control public class RemoteWidget : Control
{ {
public enum SizingMode
{
Local,
Remote
}
private readonly IAvaloniaRemoteTransportConnection _connection; private readonly IAvaloniaRemoteTransportConnection _connection;
private FrameMessage _lastFrame; private FrameMessage _lastFrame;
private WriteableBitmap _bitmap; private WriteableBitmap _bitmap;
public RemoteWidget(IAvaloniaRemoteTransportConnection connection) public RemoteWidget(IAvaloniaRemoteTransportConnection connection)
{ {
Mode = SizingMode.Local;
_connection = connection; _connection = connection;
_connection.OnMessage += (t, msg) => Dispatcher.UIThread.Post(() => OnMessage(msg)); _connection.OnMessage += (t, msg) => Dispatcher.UIThread.Post(() => OnMessage(msg));
_connection.Send(new ClientSupportedPixelFormatsMessage _connection.Send(new ClientSupportedPixelFormatsMessage
@ -28,6 +36,8 @@ namespace Avalonia.Controls.Remote
}); });
} }
public SizingMode Mode { get; set; }
private void OnMessage(object msg) private void OnMessage(object msg)
{ {
if (msg is FrameMessage frame) if (msg is FrameMessage frame)
@ -44,13 +54,17 @@ namespace Avalonia.Controls.Remote
protected override void ArrangeCore(Rect finalRect) protected override void ArrangeCore(Rect finalRect)
{ {
_connection.Send(new ClientViewportAllocatedMessage if (Mode == SizingMode.Local)
{ {
Width = finalRect.Width, _connection.Send(new ClientViewportAllocatedMessage
Height = finalRect.Height, {
DpiX = 96, Width = finalRect.Width,
DpiY = 96 //TODO: Somehow detect the actual DPI Height = finalRect.Height,
}); DpiX = 10 * 96,
DpiY = 10 * 96 //TODO: Somehow detect the actual DPI
});
}
base.ArrangeCore(finalRect); base.ArrangeCore(finalRect);
} }

161
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@ -4,11 +4,15 @@ using System.Runtime.InteropServices;
using Avalonia.Controls.Embedding.Offscreen; using Avalonia.Controls.Embedding.Offscreen;
using Avalonia.Controls.Platform.Surfaces; using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol;
using Avalonia.Remote.Protocol.Input;
using Avalonia.Remote.Protocol.Viewport; using Avalonia.Remote.Protocol.Viewport;
using Avalonia.Threading; using Avalonia.Threading;
using InputModifiers = Avalonia.Input.InputModifiers;
using Key = Avalonia.Input.Key;
using PixelFormat = Avalonia.Platform.PixelFormat; using PixelFormat = Avalonia.Platform.PixelFormat;
using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat; using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat;
@ -31,6 +35,67 @@ namespace Avalonia.Controls.Remote.Server
{ {
_transport = transport; _transport = transport;
_transport.OnMessage += OnMessage; _transport.OnMessage += OnMessage;
KeyboardDevice = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
}
private static RawMouseEventType GetAvaloniaEventType (Avalonia.Remote.Protocol.Input.MouseButton button, bool pressed)
{
switch (button)
{
case Avalonia.Remote.Protocol.Input.MouseButton.Left:
return pressed ? RawMouseEventType.LeftButtonDown : RawMouseEventType.LeftButtonUp;
case Avalonia.Remote.Protocol.Input.MouseButton.Middle:
return pressed ? RawMouseEventType.MiddleButtonDown : RawMouseEventType.MiddleButtonUp;
case Avalonia.Remote.Protocol.Input.MouseButton.Right:
return pressed ? RawMouseEventType.RightButtonDown : RawMouseEventType.RightButtonUp;
default:
return RawMouseEventType.Move;
}
}
private static InputModifiers GetAvaloniaInputModifiers (Avalonia.Remote.Protocol.Input.InputModifiers[] modifiers)
{
var result = InputModifiers.None;
foreach(var modifier in modifiers)
{
switch (modifier)
{
case Avalonia.Remote.Protocol.Input.InputModifiers.Control:
result |= InputModifiers.Control;
break;
case Avalonia.Remote.Protocol.Input.InputModifiers.Alt:
result |= InputModifiers.Alt;
break;
case Avalonia.Remote.Protocol.Input.InputModifiers.Shift:
result |= InputModifiers.Shift;
break;
case Avalonia.Remote.Protocol.Input.InputModifiers.Windows:
result |= InputModifiers.Windows;
break;
case Avalonia.Remote.Protocol.Input.InputModifiers.LeftMouseButton:
result |= InputModifiers.LeftMouseButton;
break;
case Avalonia.Remote.Protocol.Input.InputModifiers.MiddleMouseButton:
result |= InputModifiers.MiddleMouseButton;
break;
case Avalonia.Remote.Protocol.Input.InputModifiers.RightMouseButton:
result |= InputModifiers.RightMouseButton;
break;
}
}
return result;
} }
protected virtual void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj) protected virtual void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj)
@ -45,6 +110,16 @@ namespace Avalonia.Controls.Remote.Server
} }
Dispatcher.UIThread.Post(RenderIfNeeded); Dispatcher.UIThread.Post(RenderIfNeeded);
} }
if(obj is ClientRenderInfoMessage renderInfo)
{
lock(_lock)
{
_dpi = new Vector(renderInfo.DpiX, renderInfo.DpiY);
_invalidated = true;
}
Dispatcher.UIThread.Post(RenderIfNeeded);
}
if (obj is ClientSupportedPixelFormatsMessage supportedFormats) if (obj is ClientSupportedPixelFormatsMessage supportedFormats)
{ {
lock (_lock) lock (_lock)
@ -82,6 +157,84 @@ namespace Avalonia.Controls.Remote.Server
_pendingAllocation = allocated; _pendingAllocation = allocated;
} }
} }
if(obj is PointerMovedEventMessage pointer)
{
Dispatcher.UIThread.Post(() =>
{
Input?.Invoke(new RawMouseEventArgs(
MouseDevice,
0,
InputRoot,
RawMouseEventType.Move,
new Point(pointer.X, pointer.Y),
GetAvaloniaInputModifiers(pointer.Modifiers)));
}, DispatcherPriority.Input);
}
if(obj is PointerPressedEventMessage pressed)
{
Dispatcher.UIThread.Post(() =>
{
Input?.Invoke(new RawMouseEventArgs(
MouseDevice,
0,
InputRoot,
GetAvaloniaEventType(pressed.Button, true),
new Point(pressed.X, pressed.Y),
GetAvaloniaInputModifiers(pressed.Modifiers)));
}, DispatcherPriority.Input);
}
if (obj is PointerPressedEventMessage released)
{
Dispatcher.UIThread.Post(() =>
{
Input?.Invoke(new RawMouseEventArgs(
MouseDevice,
0,
InputRoot,
GetAvaloniaEventType(released.Button, false),
new Point(released.X, released.Y),
GetAvaloniaInputModifiers(released.Modifiers)));
}, DispatcherPriority.Input);
}
if(obj is ScrollEventMessage scroll)
{
Dispatcher.UIThread.Post(() =>
{
Input?.Invoke(new RawMouseWheelEventArgs(
MouseDevice,
0,
InputRoot,
new Point(scroll.X, scroll.Y),
new Vector(scroll.DeltaX, scroll.DeltaY),
GetAvaloniaInputModifiers(scroll.Modifiers)));
}, DispatcherPriority.Input);
}
if(obj is KeyEventMessage key)
{
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
Dispatcher.UIThread.Post(() =>
{
Input?.Invoke(new RawKeyEventArgs(
KeyboardDevice,
0,
key.IsDown ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
(Key)key.Key,
GetAvaloniaInputModifiers(key.Modifiers)));
}, DispatcherPriority.Input);
}
if(obj is TextInputEventMessage text)
{
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
Dispatcher.UIThread.Post(() =>
{
Input?.Invoke(new RawTextInputEventArgs(
KeyboardDevice,
0,
text.Text));
}, DispatcherPriority.Input);
}
} }
} }
@ -102,6 +255,12 @@ namespace Avalonia.Controls.Remote.Server
FrameMessage RenderFrame(int width, int height, ProtocolPixelFormat? format) FrameMessage RenderFrame(int width, int height, ProtocolPixelFormat? format)
{ {
var scalingX = _dpi.X / 96.0;
var scalingY = _dpi.Y / 96.0;
width = (int)(width * scalingX);
height = (int)(height * scalingY);
var fmt = format ?? ProtocolPixelFormat.Rgba8888; var fmt = format ?? ProtocolPixelFormat.Rgba8888;
var bpp = fmt == ProtocolPixelFormat.Rgb565 ? 2 : 4; var bpp = fmt == ProtocolPixelFormat.Rgb565 ? 2 : 4;
var data = new byte[width * height * bpp]; var data = new byte[width * height * bpp];
@ -169,5 +328,7 @@ namespace Avalonia.Controls.Remote.Server
} }
public override IMouseDevice MouseDevice { get; } = new MouseDevice(); public override IMouseDevice MouseDevice { get; } = new MouseDevice();
public IKeyboardDevice KeyboardDevice { get; }
} }
} }

378
src/Avalonia.Controls/TextBox.cs

@ -368,186 +368,242 @@ namespace Avalonia.Controls
string text = Text ?? string.Empty; string text = Text ?? string.Empty;
int caretIndex = CaretIndex; int caretIndex = CaretIndex;
bool movement = false; bool movement = false;
bool selection = false;
bool handled = false; bool handled = false;
var modifiers = e.Modifiers; var modifiers = e.Modifiers;
switch (e.Key) var keymap = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>();
{
case Key.A:
if (modifiers == InputModifiers.Control)
{
SelectAll();
handled = true;
}
break;
case Key.C:
if (modifiers == InputModifiers.Control)
{
if (!IsPasswordBox)
{
Copy();
}
handled = true;
}
break;
case Key.X: bool Match(List<KeyGesture> gestures) => gestures.Any(g => g.Matches(e));
if (modifiers == InputModifiers.Control) bool DetectSelection() => e.Modifiers.HasFlag(keymap.SelectionModifiers);
{
if (!IsPasswordBox)
{
Copy();
DeleteSelection();
}
handled = true;
}
break;
case Key.V: if (Match(keymap.SelectAll))
if (modifiers == InputModifiers.Control) {
{ SelectAll();
Paste(); handled = true;
handled = true; }
} else if (Match(keymap.Copy))
{
if (!IsPasswordBox)
{
Copy();
}
break; handled = true;
}
else if (Match(keymap.Cut))
{
if (!IsPasswordBox)
{
Copy();
DeleteSelection();
}
case Key.Z: handled = true;
if (modifiers == InputModifiers.Control) }
{ else if (Match(keymap.Paste))
try {
{
_isUndoingRedoing = true;
_undoRedoHelper.Undo();
}
finally
{
_isUndoingRedoing = false;
}
handled = true;
}
break;
case Key.Y:
if (modifiers == InputModifiers.Control)
{
try
{
_isUndoingRedoing = true;
_undoRedoHelper.Redo();
}
finally
{
_isUndoingRedoing = false;
}
handled = true;
}
break;
case Key.Left:
MoveHorizontal(-1, modifiers);
movement = true;
break;
case Key.Right: Paste();
MoveHorizontal(1, modifiers); handled = true;
movement = true; }
break; else if (Match(keymap.Undo))
{
case Key.Up: try
movement = MoveVertical(-1, modifiers); {
break; _isUndoingRedoing = true;
_undoRedoHelper.Undo();
}
finally
{
_isUndoingRedoing = false;
}
case Key.Down: handled = true;
movement = MoveVertical(1, modifiers); }
break; else if (Match(keymap.Redo))
{
try
{
_isUndoingRedoing = true;
_undoRedoHelper.Redo();
}
finally
{
_isUndoingRedoing = false;
}
case Key.Home: handled = true;
MoveHome(modifiers); }
movement = true; else if (Match(keymap.MoveCursorToTheStartOfDocument))
break; {
MoveHome(true);
movement = true;
selection = false;
handled = true;
}
else if (Match(keymap.MoveCursorToTheEndOfDocument))
{
MoveEnd(true);
movement = true;
selection = false;
handled = true;
}
else if (Match(keymap.MoveCursorToTheStartOfLine))
{
MoveHome(false);
movement = true;
selection = false;
handled = true;
}
else if (Match(keymap.MoveCursorToTheEndOfLine))
{
MoveEnd(false);
movement = true;
selection = false;
handled = true;
}
else if (Match(keymap.MoveCursorToTheStartOfDocumentWithSelection))
{
MoveHome(true);
movement = true;
selection = true;
handled = true;
}
else if (Match(keymap.MoveCursorToTheEndOfDocumentWithSelection))
{
MoveEnd(true);
movement = true;
selection = true;
handled = true;
}
else if (Match(keymap.MoveCursorToTheStartOfLineWithSelection))
{
MoveHome(false);
movement = true;
selection = true;
handled = true;
}
else if (Match(keymap.MoveCursorToTheEndOfLineWithSelection))
{
MoveEnd(false);
movement = true;
selection = true;
handled = true;
}
else
{
bool hasWholeWordModifiers = modifiers.HasFlag(keymap.WholeWordTextActionModifiers);
switch (e.Key)
{
case Key.Left:
MoveHorizontal(-1, hasWholeWordModifiers);
movement = true;
selection = DetectSelection();
break;
case Key.End: case Key.Right:
MoveEnd(modifiers); MoveHorizontal(1, hasWholeWordModifiers);
movement = true; movement = true;
break; selection = DetectSelection();
break;
case Key.Back: case Key.Up:
if (modifiers == InputModifiers.Control && SelectionStart == SelectionEnd) movement = MoveVertical(-1);
{ selection = DetectSelection();
SetSelectionForControlBackspace(modifiers); break;
}
if (!DeleteSelection() && CaretIndex > 0) case Key.Down:
{ movement = MoveVertical(1);
var removedCharacters = 1; selection = DetectSelection();
// handle deleting /r/n break;
// you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if
// a /r should also be deleted. case Key.Back:
if (CaretIndex > 1 && if (hasWholeWordModifiers && SelectionStart == SelectionEnd)
text[CaretIndex - 1] == '\n' &&
text[CaretIndex - 2] == '\r')
{ {
removedCharacters = 2; SetSelectionForControlBackspace();
} }
SetTextInternal(text.Substring(0, caretIndex - removedCharacters) + text.Substring(caretIndex)); if (!DeleteSelection() && CaretIndex > 0)
CaretIndex -= removedCharacters; {
SelectionStart = SelectionEnd = CaretIndex; var removedCharacters = 1;
} // handle deleting /r/n
handled = true; // you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if
break; // a /r should also be deleted.
if (CaretIndex > 1 &&
text[CaretIndex - 1] == '\n' &&
text[CaretIndex - 2] == '\r')
{
removedCharacters = 2;
}
case Key.Delete: SetTextInternal(text.Substring(0, caretIndex - removedCharacters) +
if (modifiers == InputModifiers.Control && SelectionStart == SelectionEnd) text.Substring(caretIndex));
{ CaretIndex -= removedCharacters;
SetSelectionForControlDelete(modifiers); SelectionStart = SelectionEnd = CaretIndex;
} }
if (!DeleteSelection() && caretIndex < text.Length) handled = true;
{ break;
var removedCharacters = 1;
// handle deleting /r/n case Key.Delete:
// you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if if (hasWholeWordModifiers && SelectionStart == SelectionEnd)
// a /r should also be deleted.
if (CaretIndex < text.Length - 1 &&
text[caretIndex + 1] == '\n' &&
text[caretIndex] == '\r')
{ {
removedCharacters = 2; SetSelectionForControlDelete();
} }
SetTextInternal(text.Substring(0, caretIndex) + text.Substring(caretIndex + removedCharacters)); if (!DeleteSelection() && caretIndex < text.Length)
} {
handled = true; var removedCharacters = 1;
break; // handle deleting /r/n
// you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if
// a /r should also be deleted.
if (CaretIndex < text.Length - 1 &&
text[caretIndex + 1] == '\n' &&
text[caretIndex] == '\r')
{
removedCharacters = 2;
}
SetTextInternal(text.Substring(0, caretIndex) +
text.Substring(caretIndex + removedCharacters));
}
case Key.Enter:
if (AcceptsReturn)
{
HandleTextInput(NewLine);
handled = true; handled = true;
} break;
break; case Key.Enter:
if (AcceptsReturn)
{
HandleTextInput(NewLine);
handled = true;
}
case Key.Tab: break;
if (AcceptsTab)
{
HandleTextInput("\t");
handled = true;
}
else
{
base.OnKeyDown(e);
}
break; case Key.Tab:
if (AcceptsTab)
{
HandleTextInput("\t");
handled = true;
}
else
{
base.OnKeyDown(e);
}
default: break;
handled = false;
break; default:
handled = false;
break;
}
} }
if (movement && ((modifiers & InputModifiers.Shift) != 0)) if (movement && selection)
{ {
SelectionEnd = CaretIndex; SelectionEnd = CaretIndex;
} }
@ -666,12 +722,12 @@ namespace Avalonia.Controls
return result; return result;
} }
private void MoveHorizontal(int direction, InputModifiers modifiers) private void MoveHorizontal(int direction, bool wholeWord)
{ {
var text = Text ?? string.Empty; var text = Text ?? string.Empty;
var caretIndex = CaretIndex; var caretIndex = CaretIndex;
if ((modifiers & InputModifiers.Control) == 0) if (!wholeWord)
{ {
var index = caretIndex + direction; var index = caretIndex + direction;
@ -709,7 +765,7 @@ namespace Avalonia.Controls
} }
} }
private bool MoveVertical(int count, InputModifiers modifiers) private bool MoveVertical(int count)
{ {
var formattedText = _presenter.FormattedText; var formattedText = _presenter.FormattedText;
var lines = formattedText.GetLines().ToList(); var lines = formattedText.GetLines().ToList();
@ -732,12 +788,12 @@ namespace Avalonia.Controls
} }
} }
private void MoveHome(InputModifiers modifiers) private void MoveHome(bool document)
{ {
var text = Text ?? string.Empty; var text = Text ?? string.Empty;
var caretIndex = CaretIndex; var caretIndex = CaretIndex;
if ((modifiers & InputModifiers.Control) != 0) if (document)
{ {
caretIndex = 0; caretIndex = 0;
} }
@ -762,12 +818,12 @@ namespace Avalonia.Controls
CaretIndex = caretIndex; CaretIndex = caretIndex;
} }
private void MoveEnd(InputModifiers modifiers) private void MoveEnd(bool document)
{ {
var text = Text ?? string.Empty; var text = Text ?? string.Empty;
var caretIndex = CaretIndex; var caretIndex = CaretIndex;
if ((modifiers & InputModifiers.Control) != 0) if (document)
{ {
caretIndex = text.Length; caretIndex = text.Length;
} }
@ -882,17 +938,17 @@ namespace Avalonia.Controls
} }
} }
private void SetSelectionForControlBackspace(InputModifiers modifiers) private void SetSelectionForControlBackspace()
{ {
SelectionStart = CaretIndex; SelectionStart = CaretIndex;
MoveHorizontal(-1, modifiers); MoveHorizontal(-1, true);
SelectionEnd = CaretIndex; SelectionEnd = CaretIndex;
} }
private void SetSelectionForControlDelete(InputModifiers modifiers) private void SetSelectionForControlDelete()
{ {
SelectionStart = CaretIndex; SelectionStart = CaretIndex;
MoveHorizontal(1, modifiers); MoveHorizontal(1, true);
SelectionEnd = CaretIndex; SelectionEnd = CaretIndex;
} }

2
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@ -18,7 +18,7 @@ namespace Avalonia.DesignerSupport
Control control; Control control;
using (PlatformManager.DesignerMode()) using (PlatformManager.DesignerMode())
{ {
var loader = new AvaloniaXamlLoader(); var loader = new AvaloniaXamlLoader() {IsDesignMode = true};
var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)); var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml));

3
src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs

@ -57,7 +57,8 @@ namespace Avalonia.DesignerSupport.Remote
.Bind<IRenderTimer>().ToConstant(threading) .Bind<IRenderTimer>().ToConstant(threading)
.Bind<ISystemDialogImpl>().ToSingleton<SystemDialogsStub>() .Bind<ISystemDialogImpl>().ToSingleton<SystemDialogsStub>()
.Bind<IWindowingPlatform>().ToConstant(instance) .Bind<IWindowingPlatform>().ToConstant(instance)
.Bind<IPlatformIconLoader>().ToSingleton<IconLoaderStub>(); .Bind<IPlatformIconLoader>().ToSingleton<IconLoaderStub>()
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
} }

10
src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs

@ -15,6 +15,8 @@ namespace Avalonia.DesignerSupport.Remote
{ {
private static ClientSupportedPixelFormatsMessage s_supportedPixelFormats; private static ClientSupportedPixelFormatsMessage s_supportedPixelFormats;
private static ClientViewportAllocatedMessage s_viewportAllocatedMessage; private static ClientViewportAllocatedMessage s_viewportAllocatedMessage;
private static ClientRenderInfoMessage s_renderInfoMessage;
private static IAvaloniaRemoteTransportConnection s_transport; private static IAvaloniaRemoteTransportConnection s_transport;
class CommandLineArgs class CommandLineArgs
{ {
@ -161,7 +163,8 @@ namespace Avalonia.DesignerSupport.Remote
PreviewerWindowingPlatform.PreFlightMessages = new List<object> PreviewerWindowingPlatform.PreFlightMessages = new List<object>
{ {
s_supportedPixelFormats, s_supportedPixelFormats,
s_viewportAllocatedMessage s_viewportAllocatedMessage,
s_renderInfoMessage
}; };
} }
@ -173,6 +176,11 @@ namespace Avalonia.DesignerSupport.Remote
s_supportedPixelFormats = formats; s_supportedPixelFormats = formats;
RebuildPreFlight(); RebuildPreFlight();
} }
if (obj is ClientRenderInfoMessage renderInfo)
{
s_renderInfoMessage = renderInfo;
RebuildPreFlight();
}
if (obj is ClientViewportAllocatedMessage viewport) if (obj is ClientViewportAllocatedMessage viewport)
{ {
s_viewportAllocatedMessage = viewport; s_viewportAllocatedMessage = viewport;

4
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Reactive.Disposables; using System.Reactive.Disposables;
@ -20,7 +20,7 @@ namespace Avalonia.DesignerSupport.Remote
public IPlatformHandle Handle { get; } public IPlatformHandle Handle { get; }
public Size MaxClientSize { get; } public Size MaxClientSize { get; }
public Size ClientSize { get; } public Size ClientSize { get; }
public double Scaling { get; } public double Scaling { get; } = 1.0;
public IEnumerable<object> Surfaces { get; } public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; } public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; } public Action<Rect> Paint { get; set; }

18
src/Avalonia.Input/Key.cs

@ -1020,5 +1020,23 @@ namespace Avalonia.Input
/// The key is used with another key to create a single combined character. /// The key is used with another key to create a single combined character.
/// </summary> /// </summary>
DeadCharProcessed = 172, DeadCharProcessed = 172,
/// <summary>
/// OSX Platform-specific Fn+Left key
/// </summary>
FnLeftArrow = 10001,
/// <summary>
/// OSX Platform-specific Fn+Right key
/// </summary>
FnRightArrow = 10002,
/// <summary>
/// OSX Platform-specific Fn+Up key
/// </summary>
FnUpArrow = 10003,
/// <summary>
/// OSX Platform-specific Fn+Down key
/// </summary>
FnDownArrow = 10004,
} }
} }

11
src/Avalonia.Input/KeyGesture.cs

@ -6,6 +6,17 @@ namespace Avalonia.Input
{ {
public sealed class KeyGesture : IEquatable<KeyGesture> public sealed class KeyGesture : IEquatable<KeyGesture>
{ {
public KeyGesture()
{
}
public KeyGesture(Key key, InputModifiers modifiers = InputModifiers.None)
{
Key = key;
Modifiers = modifiers;
}
public bool Equals(KeyGesture other) public bool Equals(KeyGesture other)
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other)) return false;

98
src/Avalonia.Input/Platform/PlatformHotkeyConfiguration.cs

@ -0,0 +1,98 @@
using System.Collections.Generic;
namespace Avalonia.Input.Platform
{
public class PlatformHotkeyConfiguration
{
public PlatformHotkeyConfiguration() : this(InputModifiers.Control)
{
}
public PlatformHotkeyConfiguration(InputModifiers commandModifiers,
InputModifiers selectionModifiers = InputModifiers.Shift,
InputModifiers wholeWordTextActionModifiers = InputModifiers.Control)
{
CommandModifiers = commandModifiers;
SelectionModifiers = selectionModifiers;
WholeWordTextActionModifiers = wholeWordTextActionModifiers;
Copy = new List<KeyGesture>
{
new KeyGesture(Key.C, commandModifiers)
};
Cut = new List<KeyGesture>
{
new KeyGesture(Key.X, commandModifiers)
};
Paste = new List<KeyGesture>
{
new KeyGesture(Key.V, commandModifiers)
};
Undo = new List<KeyGesture>
{
new KeyGesture(Key.Z, commandModifiers)
};
Redo = new List<KeyGesture>
{
new KeyGesture(Key.Y, commandModifiers),
new KeyGesture(Key.Z, commandModifiers | selectionModifiers)
};
SelectAll = new List<KeyGesture>
{
new KeyGesture(Key.A, commandModifiers)
};
MoveCursorToTheStartOfLine = new List<KeyGesture>
{
new KeyGesture(Key.Home)
};
MoveCursorToTheEndOfLine = new List<KeyGesture>
{
new KeyGesture(Key.End)
};
MoveCursorToTheStartOfDocument = new List<KeyGesture>
{
new KeyGesture(Key.Home, commandModifiers)
};
MoveCursorToTheEndOfDocument = new List<KeyGesture>
{
new KeyGesture(Key.End, commandModifiers)
};
MoveCursorToTheStartOfLineWithSelection = new List<KeyGesture>
{
new KeyGesture(Key.Home, selectionModifiers)
};
MoveCursorToTheEndOfLineWithSelection = new List<KeyGesture>
{
new KeyGesture(Key.End, selectionModifiers)
};
MoveCursorToTheStartOfDocumentWithSelection = new List<KeyGesture>
{
new KeyGesture(Key.Home, commandModifiers | selectionModifiers)
};
MoveCursorToTheEndOfDocumentWithSelection = new List<KeyGesture>
{
new KeyGesture(Key.End, commandModifiers | selectionModifiers)
};
}
public InputModifiers CommandModifiers { get; set; }
public InputModifiers WholeWordTextActionModifiers { get; set; }
public InputModifiers SelectionModifiers { get; set; }
public List<KeyGesture> Copy { get; set; }
public List<KeyGesture> Cut { get; set; }
public List<KeyGesture> Paste { get; set; }
public List<KeyGesture> Undo { get; set; }
public List<KeyGesture> Redo { get; set; }
public List<KeyGesture> SelectAll { get; set; }
public List<KeyGesture> MoveCursorToTheStartOfLine { get; set; }
public List<KeyGesture> MoveCursorToTheEndOfLine { get; set; }
public List<KeyGesture> MoveCursorToTheStartOfDocument { get; set; }
public List<KeyGesture> MoveCursorToTheEndOfDocument { get; set; }
public List<KeyGesture> MoveCursorToTheStartOfLineWithSelection { get; set; }
public List<KeyGesture> MoveCursorToTheEndOfLineWithSelection { get; set; }
public List<KeyGesture> MoveCursorToTheStartOfDocumentWithSelection { get; set; }
public List<KeyGesture> MoveCursorToTheEndOfDocumentWithSelection { get; set; }
}
}

6
src/Avalonia.Remote.Protocol/InputMessages.cs

@ -75,4 +75,10 @@ namespace Avalonia.Remote.Protocol.Input
public Key Key { get; set; } public Key Key { get; set; }
} }
[AvaloniaRemoteMessageGuid("C174102E-7405-4594-916F-B10B8248A17D")]
public class TextInputEventMessage : InputEventMessageBase
{
public string Text { get; set; }
}
} }

7
src/Avalonia.Remote.Protocol/ViewportMessages.cs

@ -37,6 +37,13 @@
public PixelFormat[] Formats { get; set; } public PixelFormat[] Formats { get; set; }
} }
[AvaloniaRemoteMessageGuid("7A3c25d3-3652-438D-8EF1-86E942CC96C0")]
public class ClientRenderInfoMessage
{
public double DpiX { get; set; }
public double DpiY { get; set; }
}
[AvaloniaRemoteMessageGuid("68014F8A-289D-4851-8D34-5367EDA7F827")] [AvaloniaRemoteMessageGuid("68014F8A-289D-4851-8D34-5367EDA7F827")]
public class FrameReceivedMessage public class FrameReceivedMessage
{ {

2
src/Avalonia.Styling/Styling/IStyle.cs

@ -18,5 +18,7 @@ namespace Avalonia.Styling
/// The control that contains this style. May be null. /// The control that contains this style. May be null.
/// </param> /// </param>
void Attach(IStyleable control, IStyleHost container); void Attach(IStyleable control, IStyleHost container);
void Detach();
} }
} }

52
src/Avalonia.Styling/Styling/Style.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Controls; using Avalonia.Controls;
@ -15,9 +16,12 @@ namespace Avalonia.Styling
/// </summary> /// </summary>
public class Style : AvaloniaObject, IStyle, ISetStyleParent public class Style : AvaloniaObject, IStyle, ISetStyleParent
{ {
private static Dictionary<IStyleable, List<IDisposable>> _applied = private static Dictionary<IStyleable, CompositeDisposable> _applied =
new Dictionary<IStyleable, List<IDisposable>>(); new Dictionary<IStyleable, CompositeDisposable>();
private IResourceNode _parent; private IResourceNode _parent;
private CompositeDisposable _subscriptions;
private IResourceDictionary _resources; private IResourceDictionary _resources;
private IList<IAnimation> _animations; private IList<IAnimation> _animations;
@ -88,6 +92,14 @@ namespace Avalonia.Styling
} }
} }
private CompositeDisposable Subscriptions
{
get
{
return _subscriptions ?? (_subscriptions = new CompositeDisposable(2));
}
}
/// <inheritdoc/> /// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => _parent; IResourceNode IResourceNode.ResourceParent => _parent;
@ -109,7 +121,9 @@ namespace Avalonia.Styling
if (match.ImmediateResult != false) if (match.ImmediateResult != false)
{ {
var subs = GetSubscriptions(control); var controlSubscriptions = GetSubscriptions(control);
var subs = new CompositeDisposable(Setters.Count + Animations.Count);
if (control is Animatable animatable) if (control is Animatable animatable)
{ {
@ -132,17 +146,25 @@ namespace Avalonia.Styling
var sub = setter.Apply(this, control, match.ObservableResult); var sub = setter.Apply(this, control, match.ObservableResult);
subs.Add(sub); subs.Add(sub);
} }
controlSubscriptions.Add(subs);
Subscriptions.Add(subs);
} }
} }
else if (control == container) else if (control == container)
{ {
var subs = GetSubscriptions(control); var controlSubscriptions = GetSubscriptions(control);
var subs = new CompositeDisposable(Setters.Count);
foreach (var setter in Setters) foreach (var setter in Setters)
{ {
var sub = setter.Apply(this, control, null); var sub = setter.Apply(this, control, null);
subs.Add(sub); subs.Add(sub);
} }
controlSubscriptions.Add(subs);
Subscriptions.Add(subs);
} }
} }
@ -183,16 +205,25 @@ namespace Avalonia.Styling
throw new InvalidOperationException("The Style already has a parent."); throw new InvalidOperationException("The Style already has a parent.");
} }
if (parent == null)
{
Detach();
}
_parent = parent; _parent = parent;
} }
private static List<IDisposable> GetSubscriptions(IStyleable control) public void Detach()
{ {
List<IDisposable> subscriptions; _subscriptions?.Dispose();
_subscriptions = null;
}
if (!_applied.TryGetValue(control, out subscriptions)) private static CompositeDisposable GetSubscriptions(IStyleable control)
{
if (!_applied.TryGetValue(control, out var subscriptions))
{ {
subscriptions = new List<IDisposable>(2); subscriptions = new CompositeDisposable(2);
subscriptions.Add(control.StyleDetach.Subscribe(ControlDetach)); subscriptions.Add(control.StyleDetach.Subscribe(ControlDetach));
_applied.Add(control, subscriptions); _applied.Add(control, subscriptions);
} }
@ -209,10 +240,7 @@ namespace Avalonia.Styling
{ {
var subscriptions = _applied[control]; var subscriptions = _applied[control];
foreach (var subscription in subscriptions) subscriptions.Dispose();
{
subscription.Dispose();
}
_applied.Remove(control); _applied.Remove(control);
} }

8
src/Avalonia.Styling/Styling/Styles.cs

@ -105,6 +105,14 @@ namespace Avalonia.Styling
} }
} }
public void Detach()
{
foreach (IStyle style in this)
{
style.Detach();
}
}
/// <inheritdoc/> /// <inheritdoc/>
public bool TryGetResource(string key, out object value) public bool TryGetResource(string key, out object value)
{ {

9
src/Avalonia.Styling/Styling/packages.config

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Rx-Core" version="2.2.5" targetFramework="portable-net45+win8" userInstalled="true" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="portable-net45+win8" userInstalled="true" />
<package id="Rx-Linq" version="2.2.5" targetFramework="portable-net45+win8" userInstalled="true" />
<package id="Rx-Main" version="2.2.5" targetFramework="portable-net45+win8" userInstalled="true" />
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="portable-net45+win8" userInstalled="true" />
<package id="Splat" version="1.6.2" targetFramework="portable45-net45+win8" userInstalled="true" />
</packages>

1
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@ -81,6 +81,7 @@ namespace Avalonia.Gtk3
.Bind<ISystemDialogImpl>().ToSingleton<SystemDialog>() .Bind<ISystemDialogImpl>().ToSingleton<SystemDialog>()
.Bind<IRenderLoop>().ToConstant(new RenderLoop()) .Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60)) .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IPlatformIconLoader>().ToConstant(new PlatformIconLoader()); .Bind<IPlatformIconLoader>().ToConstant(new PlatformIconLoader());
if (useGpu) if (useGpu)
EglGlPlatformFeature.TryInitialize(); EglGlPlatformFeature.TryInitialize();

2
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@ -126,7 +126,7 @@ namespace Avalonia.Gtk3
if (state.HasFlag(GdkModifierType.ShiftMask)) if (state.HasFlag(GdkModifierType.ShiftMask))
rv |= InputModifiers.Shift; rv |= InputModifiers.Shift;
if (state.HasFlag(GdkModifierType.Mod1Mask)) if (state.HasFlag(GdkModifierType.Mod1Mask))
rv |= InputModifiers.Control; rv |= InputModifiers.Alt;
if (state.HasFlag(GdkModifierType.Button1Mask)) if (state.HasFlag(GdkModifierType.Button1Mask))
rv |= InputModifiers.LeftMouseButton; rv |= InputModifiers.LeftMouseButton;
if (state.HasFlag(GdkModifierType.Button2Mask)) if (state.HasFlag(GdkModifierType.Button2Mask))

2
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@ -5,6 +5,7 @@ using Avalonia.Controls;
using Avalonia.Controls.Embedding; using Avalonia.Controls.Embedding;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.LinuxFramebuffer; using Avalonia.LinuxFramebuffer;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
@ -36,6 +37,7 @@ namespace Avalonia.LinuxFramebuffer
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>() .Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
.Bind<IPlatformThreadingInterface>().ToConstant(Threading) .Bind<IPlatformThreadingInterface>().ToConstant(Threading)
.Bind<IRenderLoop>().ToConstant(new RenderLoop()) .Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IRenderTimer>().ToConstant(Threading); .Bind<IRenderTimer>().ToConstant(Threading);
} }

10
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -21,6 +21,12 @@ namespace Avalonia.Markup.Xaml
{ {
private readonly AvaloniaXamlSchemaContext _context = GetContext(); private readonly AvaloniaXamlSchemaContext _context = GetContext();
public bool IsDesignMode
{
get => _context.IsDesignMode;
set => _context.IsDesignMode = value;
}
private static AvaloniaXamlSchemaContext GetContext() private static AvaloniaXamlSchemaContext GetContext()
{ {
var result = AvaloniaLocator.Current.GetService<AvaloniaXamlSchemaContext>(); var result = AvaloniaLocator.Current.GetService<AvaloniaXamlSchemaContext>();
@ -200,7 +206,7 @@ namespace Avalonia.Markup.Xaml
internal static object LoadFromReader(XamlReader reader, AvaloniaXamlContext context = null, IAmbientProvider parentAmbientProvider = null) internal static object LoadFromReader(XamlReader reader, AvaloniaXamlContext context = null, IAmbientProvider parentAmbientProvider = null)
{ {
var writer = AvaloniaXamlObjectWriter.Create( var writer = AvaloniaXamlObjectWriter.Create(
reader.SchemaContext, (AvaloniaXamlSchemaContext)reader.SchemaContext,
context, context,
parentAmbientProvider); parentAmbientProvider);
@ -234,4 +240,4 @@ namespace Avalonia.Markup.Xaml
public static T Parse<T>(string xaml, Assembly localAssembly = null) public static T Parse<T>(string xaml, Assembly localAssembly = null)
=> (T)Parse(xaml, localAssembly); => (T)Parse(xaml, localAssembly);
} }
} }

37
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs

@ -4,13 +4,27 @@ using Portable.Xaml.ComponentModel;
using System.ComponentModel; using System.ComponentModel;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using Avalonia.Controls;
using Portable.Xaml.Schema;
namespace Avalonia.Markup.Xaml.PortableXaml namespace Avalonia.Markup.Xaml.PortableXaml
{ {
public class AvaloniaXamlObjectWriter : XamlObjectWriter class AvaloniaXamlObjectWriter : XamlObjectWriter
{ {
private static Dictionary<XamlDirective, string> DesignDirectives = new Dictionary<string, string>
{
["DataContext"] = "DataContext",
["DesignWidth"] = "Width", ["DesignHeight"] = "Height", ["PreviewWith"] = "PreviewWith"
}
.ToDictionary(p => new XamlDirective(
new[] {"http://schemas.microsoft.com/expression/blend/2008"}, p.Key,
XamlLanguage.Object, null, AllowedMemberLocations.Attribute), p => p.Value);
private readonly AvaloniaXamlSchemaContext _schemaContext;
public static AvaloniaXamlObjectWriter Create( public static AvaloniaXamlObjectWriter Create(
XamlSchemaContext schemaContext, AvaloniaXamlSchemaContext schemaContext,
AvaloniaXamlContext context, AvaloniaXamlContext context,
IAmbientProvider parentAmbientProvider = null) IAmbientProvider parentAmbientProvider = null)
{ {
@ -34,13 +48,14 @@ namespace Avalonia.Markup.Xaml.PortableXaml
private AvaloniaNameScope _nameScope; private AvaloniaNameScope _nameScope;
private AvaloniaXamlObjectWriter( private AvaloniaXamlObjectWriter(
XamlSchemaContext schemaContext, AvaloniaXamlSchemaContext schemaContext,
XamlObjectWriterSettings settings, XamlObjectWriterSettings settings,
AvaloniaNameScope nameScope, AvaloniaNameScope nameScope,
IAmbientProvider parentAmbientProvider) IAmbientProvider parentAmbientProvider)
: base(schemaContext, settings, parentAmbientProvider) : base(schemaContext, settings, parentAmbientProvider)
{ {
_nameScope = nameScope; _nameScope = nameScope;
_schemaContext = schemaContext;
} }
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
@ -122,6 +137,20 @@ namespace Avalonia.Markup.Xaml.PortableXaml
(value as Avalonia.ISupportInitialize)?.EndInit(); (value as Avalonia.ISupportInitialize)?.EndInit();
} }
public override void WriteStartMember(XamlMember property)
{
foreach(var d in DesignDirectives)
if (property == d.Key && _schemaContext.IsDesignMode)
{
base.WriteStartMember(new XamlMember(d.Value,
typeof(Design).GetMethod("Get" + d.Value, BindingFlags.Static | BindingFlags.Public),
typeof(Design).GetMethod("Set" + d.Value, BindingFlags.Static | BindingFlags.Public),
SchemaContext));
return;
}
base.WriteStartMember(property);
}
private class DelayedValuesHelper private class DelayedValuesHelper
{ {
private int _cnt; private int _cnt;
@ -225,4 +254,4 @@ namespace Avalonia.Markup.Xaml.PortableXaml
} }
} }
} }
} }

18
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs

@ -15,6 +15,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml
{ {
internal class AvaloniaXamlSchemaContext : XamlSchemaContext internal class AvaloniaXamlSchemaContext : XamlSchemaContext
{ {
public bool IsDesignMode { get; set; }
public static AvaloniaXamlSchemaContext Create(IRuntimeTypeProvider typeProvider = null) public static AvaloniaXamlSchemaContext Create(IRuntimeTypeProvider typeProvider = null)
{ {
return new AvaloniaXamlSchemaContext(typeProvider ?? new AvaloniaRuntimeTypeProvider()); return new AvaloniaXamlSchemaContext(typeProvider ?? new AvaloniaRuntimeTypeProvider());
@ -280,5 +281,20 @@ namespace Avalonia.Markup.Xaml.PortableXaml
return $"{MemberType}:{Type.Namespace}:{Type.Name}.{Member}"; return $"{MemberType}:{Type.Namespace}:{Type.Name}.{Member}";
} }
} }
public override bool TryGetCompatibleXamlNamespace(string xamlNamespace, out string compatibleNamespace)
{
//Forces XamlXmlReader to not ignore our namespace in design mode if mc:Ignorable is set
if (IsDesignMode &&
xamlNamespace == "http://schemas.microsoft.com/expression/blend/2008")
{
compatibleNamespace = xamlNamespace;
return true;
}
return base.TryGetCompatibleXamlNamespace(xamlNamespace, out compatibleNamespace);
}
} }
} }

10
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

@ -70,6 +70,14 @@ namespace Avalonia.Markup.Xaml.Styling
} }
} }
public void Detach()
{
if (Source != null)
{
Loaded.Detach();
}
}
/// <inheritdoc/> /// <inheritdoc/>
public bool TryGetResource(string key, out object value) => Loaded.TryGetResource(key, out value); public bool TryGetResource(string key, out object value) => Loaded.TryGetResource(key, out value);
@ -90,4 +98,4 @@ namespace Avalonia.Markup.Xaml.Styling
_parent = parent; _parent = parent;
} }
} }
} }

1
src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs

@ -37,6 +37,7 @@ namespace Avalonia.MonoMac
.Bind<IClipboard>().ToSingleton<ClipboardImpl>() .Bind<IClipboard>().ToSingleton<ClipboardImpl>()
.Bind<IRenderLoop>().ToConstant(s_renderLoop) .Bind<IRenderLoop>().ToConstant(s_renderLoop)
.Bind<IRenderTimer>().ToConstant(s_renderTimer) .Bind<IRenderTimer>().ToConstant(s_renderTimer)
.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration(InputModifiers.Windows))
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance) .Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
/*.Bind<IPlatformDragSource>().ToTransient<DragSource>()*/; /*.Bind<IPlatformDragSource>().ToTransient<DragSource>()*/;
} }

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

@ -89,6 +89,7 @@ namespace Avalonia.Win32
.Bind<IRenderTimer>().ToConstant(new RenderTimer(60)) .Bind<IRenderTimer>().ToConstant(new RenderTimer(60))
.Bind<ISystemDialogImpl>().ToSingleton<SystemDialogImpl>() .Bind<ISystemDialogImpl>().ToSingleton<SystemDialogImpl>()
.Bind<IWindowingPlatform>().ToConstant(s_instance) .Bind<IWindowingPlatform>().ToConstant(s_instance)
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IPlatformIconLoader>().ToConstant(s_instance); .Bind<IPlatformIconLoader>().ToConstant(s_instance);
Win32GlManager.Initialize(); Win32GlManager.Initialize();
UseDeferredRendering = deferredRendering; UseDeferredRendering = deferredRendering;

1
src/iOS/Avalonia.iOS/iOSPlatform.cs

@ -42,6 +42,7 @@ namespace Avalonia.iOS
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>() .Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>()
.Bind<IWindowingPlatform>().ToSingleton<WindowingPlatformImpl>() .Bind<IWindowingPlatform>().ToSingleton<WindowingPlatformImpl>()
.Bind<IRenderTimer>().ToSingleton<DisplayLinkRenderTimer>() .Bind<IRenderTimer>().ToSingleton<DisplayLinkRenderTimer>()
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IRenderLoop>().ToSingleton<RenderLoop>(); .Bind<IRenderLoop>().ToSingleton<RenderLoop>();
} }
} }

18
tests/Avalonia.Controls.UnitTests/ApplicationTests.cs

@ -113,5 +113,21 @@ namespace Avalonia.Controls.UnitTests
Assert.Throws<ArgumentNullException>(() => { Application.Current.Run(null); }); Assert.Throws<ArgumentNullException>(() => { Application.Current.Run(null); });
} }
} }
[Fact]
public void Raises_ResourcesChanged_When_Event_Handler_Added_After_Resources_Has_Been_Accessed()
{
// Test for #1765.
using (UnitTestApplication.Start())
{
var resources = Application.Current.Resources;
var raised = false;
Application.Current.ResourcesChanged += (s, e) => raised = true;
resources["foo"] = "bar";
Assert.True(raised);
}
}
} }
} }

41
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@ -924,6 +924,45 @@ do we need it?")]
} }
} }
[Fact]
public void Design_Mode_Properties_Should_Be_Ignored_At_Runtime_And_Set_In_Design_Mode()
{
using (UnitTestApplication.Start(TestServices.MockWindowingPlatform))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:d='http://schemas.microsoft.com/expression/blend/2008'
xmlns:mc='http://schemas.openxmlformats.org/markup-compatibility/2006'
mc:Ignorable='d'
d:DataContext='data-context'
d:DesignWidth='123'
d:DesignHeight='321'
>
</Window>";
foreach (var designMode in new[] {true, false})
{
var loader = new AvaloniaXamlLoader {IsDesignMode = designMode};
var obj = (Window)loader.Load(xaml);
var context = Design.GetDataContext(obj);
var width = Design.GetWidth(obj);
var height = Design.GetHeight(obj);
if (designMode)
{
Assert.Equal("data-context", context);
Assert.Equal(123, width);
Assert.Equal(321, height);
}
else
{
Assert.False(obj.IsSet(Design.DataContextProperty));
Assert.False(obj.IsSet(Design.WidthProperty));
Assert.False(obj.IsSet(Design.HeightProperty));
}
}
}
}
private class SelectedItemsViewModel : INotifyPropertyChanged private class SelectedItemsViewModel : INotifyPropertyChanged
{ {
public string[] Items { get; set; } public string[] Items { get; set; }
@ -952,4 +991,4 @@ do we need it?")]
public static string GetFoo(AvaloniaObject target) => (string)target.GetValue(FooProperty); public static string GetFoo(AvaloniaObject target) => (string)target.GetValue(FooProperty);
} }
} }

25
tests/Avalonia.Styling.UnitTests/StyleTests.cs

@ -167,6 +167,31 @@ namespace Avalonia.Styling.UnitTests
Assert.Equal(new Thickness(0), border.BorderThickness); Assert.Equal(new Thickness(0), border.BorderThickness);
} }
[Fact]
public void Style_Should_Detach_Setters_When_Detach_Is_Called()
{
Border border;
var style = new Style(x => x.OfType<Border>())
{
Setters = new[]
{
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
};
var root = new TestRoot
{
Child = border = new Border(),
};
style.Attach(border, null);
Assert.Equal(new Thickness(4), border.BorderThickness);
style.Detach();
Assert.Equal(new Thickness(0), border.BorderThickness);
}
private class Class1 : Control private class Class1 : Control
{ {
public static readonly StyledProperty<string> FooProperty = public static readonly StyledProperty<string> FooProperty =

2
tests/Avalonia.UnitTests/UnitTestApplication.cs

@ -11,6 +11,7 @@ using Avalonia.Rendering;
using Avalonia.Threading; using Avalonia.Threading;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Concurrency; using System.Reactive.Concurrency;
using Avalonia.Input.Platform;
namespace Avalonia.UnitTests namespace Avalonia.UnitTests
{ {
@ -58,6 +59,7 @@ namespace Avalonia.UnitTests
.Bind<IStandardCursorFactory>().ToConstant(Services.StandardCursorFactory) .Bind<IStandardCursorFactory>().ToConstant(Services.StandardCursorFactory)
.Bind<IStyler>().ToConstant(Services.Styler) .Bind<IStyler>().ToConstant(Services.Styler)
.Bind<IWindowingPlatform>().ToConstant(Services.WindowingPlatform) .Bind<IWindowingPlatform>().ToConstant(Services.WindowingPlatform)
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IApplicationLifecycle>().ToConstant(this); .Bind<IApplicationLifecycle>().ToConstant(this);
var styles = Services.Theme?.Invoke(); var styles = Services.Theme?.Invoke();

Loading…
Cancel
Save