Browse Source

Merge branch 'master' into design-properties

pull/1952/head
danwalmsley 8 years ago
committed by GitHub
parent
commit
8137557524
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/Android/Avalonia.Android/AndroidPlatform.cs
  2. 6
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  3. 383
      src/Avalonia.Controls/TextBox.cs
  4. 18
      src/Avalonia.Input/Key.cs
  5. 11
      src/Avalonia.Input/KeyGesture.cs
  6. 98
      src/Avalonia.Input/Platform/PlatformHotkeyConfiguration.cs
  7. 2
      src/Avalonia.Styling/Styling/IStyle.cs
  8. 52
      src/Avalonia.Styling/Styling/Style.cs
  9. 8
      src/Avalonia.Styling/Styling/Styles.cs
  10. 9
      src/Avalonia.Styling/Styling/packages.config
  11. 1
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  12. 2
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  13. 2
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  14. 10
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  15. 1
      src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs
  16. 1
      src/Windows/Avalonia.Win32/Win32Platform.cs
  17. 1
      src/iOS/Avalonia.iOS/iOSPlatform.cs
  18. 75
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
  19. 25
      tests/Avalonia.Styling.UnitTests/StyleTests.cs
  20. 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<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IAssetLoader>().ToConstant(new AssetLoader(app.GetType().Assembly));
SkiaPlatform.Initialize();

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

@ -102,6 +102,12 @@ namespace Avalonia.Controls.Remote.Server
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 bpp = fmt == ProtocolPixelFormat.Rgb565 ? 2 : 4;
var data = new byte[width * height * bpp];

383
src/Avalonia.Controls/TextBox.cs

@ -212,7 +212,10 @@ namespace Avalonia.Controls
{
if (!_ignoreTextChanges)
{
CaretIndex = CoerceCaretIndex(CaretIndex, value?.Length ?? 0);
var caretIndex = CaretIndex;
SelectionStart = CoerceCaretIndex(SelectionStart, value?.Length ?? 0);
SelectionEnd = CoerceCaretIndex(SelectionEnd, value?.Length ?? 0);
CaretIndex = CoerceCaretIndex(caretIndex, value?.Length ?? 0);
if (SetAndRaise(TextProperty, ref _text, value) && !_isUndoingRedoing)
{
@ -365,186 +368,242 @@ namespace Avalonia.Controls
string text = Text ?? string.Empty;
int caretIndex = CaretIndex;
bool movement = false;
bool selection = false;
bool handled = false;
var modifiers = e.Modifiers;
switch (e.Key)
{
case Key.A:
if (modifiers == InputModifiers.Control)
{
SelectAll();
handled = true;
}
break;
case Key.C:
if (modifiers == InputModifiers.Control)
{
if (!IsPasswordBox)
{
Copy();
}
handled = true;
}
break;
var keymap = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>();
case Key.X:
if (modifiers == InputModifiers.Control)
{
if (!IsPasswordBox)
{
Copy();
DeleteSelection();
}
handled = true;
}
break;
bool Match(List<KeyGesture> gestures) => gestures.Any(g => g.Matches(e));
bool DetectSelection() => e.Modifiers.HasFlag(keymap.SelectionModifiers);
case Key.V:
if (modifiers == InputModifiers.Control)
{
Paste();
handled = true;
}
if (Match(keymap.SelectAll))
{
SelectAll();
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:
if (modifiers == InputModifiers.Control)
{
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;
handled = true;
}
else if (Match(keymap.Paste))
{
case Key.Right:
MoveHorizontal(1, modifiers);
movement = true;
break;
Paste();
handled = true;
}
else if (Match(keymap.Undo))
{
case Key.Up:
movement = MoveVertical(-1, modifiers);
break;
try
{
_isUndoingRedoing = true;
_undoRedoHelper.Undo();
}
finally
{
_isUndoingRedoing = false;
}
case Key.Down:
movement = MoveVertical(1, modifiers);
break;
handled = true;
}
else if (Match(keymap.Redo))
{
try
{
_isUndoingRedoing = true;
_undoRedoHelper.Redo();
}
finally
{
_isUndoingRedoing = false;
}
case Key.Home:
MoveHome(modifiers);
movement = true;
break;
handled = true;
}
else if (Match(keymap.MoveCursorToTheStartOfDocument))
{
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:
MoveEnd(modifiers);
movement = true;
break;
case Key.Right:
MoveHorizontal(1, hasWholeWordModifiers);
movement = true;
selection = DetectSelection();
break;
case Key.Back:
if (modifiers == InputModifiers.Control && SelectionStart == SelectionEnd)
{
SetSelectionForControlBackspace(modifiers);
}
case Key.Up:
movement = MoveVertical(-1);
selection = DetectSelection();
break;
if (!DeleteSelection() && CaretIndex > 0)
{
var removedCharacters = 1;
// 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 > 1 &&
text[CaretIndex - 1] == '\n' &&
text[CaretIndex - 2] == '\r')
case Key.Down:
movement = MoveVertical(1);
selection = DetectSelection();
break;
case Key.Back:
if (hasWholeWordModifiers && SelectionStart == SelectionEnd)
{
removedCharacters = 2;
SetSelectionForControlBackspace();
}
SetTextInternal(text.Substring(0, caretIndex - removedCharacters) + text.Substring(caretIndex));
CaretIndex -= removedCharacters;
SelectionStart = SelectionEnd = CaretIndex;
}
handled = true;
break;
if (!DeleteSelection() && CaretIndex > 0)
{
var removedCharacters = 1;
// 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 > 1 &&
text[CaretIndex - 1] == '\n' &&
text[CaretIndex - 2] == '\r')
{
removedCharacters = 2;
}
case Key.Delete:
if (modifiers == InputModifiers.Control && SelectionStart == SelectionEnd)
{
SetSelectionForControlDelete(modifiers);
}
SetTextInternal(text.Substring(0, caretIndex - removedCharacters) +
text.Substring(caretIndex));
CaretIndex -= removedCharacters;
SelectionStart = SelectionEnd = CaretIndex;
}
if (!DeleteSelection() && caretIndex < text.Length)
{
var removedCharacters = 1;
// 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')
handled = true;
break;
case Key.Delete:
if (hasWholeWordModifiers && SelectionStart == SelectionEnd)
{
removedCharacters = 2;
SetSelectionForControlDelete();
}
SetTextInternal(text.Substring(0, caretIndex) + text.Substring(caretIndex + removedCharacters));
}
handled = true;
break;
if (!DeleteSelection() && caretIndex < text.Length)
{
var removedCharacters = 1;
// 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;
}
break;
break;
case Key.Enter:
if (AcceptsReturn)
{
HandleTextInput(NewLine);
handled = true;
}
case Key.Tab:
if (AcceptsTab)
{
HandleTextInput("\t");
handled = true;
}
else
{
base.OnKeyDown(e);
}
break;
break;
case Key.Tab:
if (AcceptsTab)
{
HandleTextInput("\t");
handled = true;
}
else
{
base.OnKeyDown(e);
}
default:
handled = false;
break;
break;
default:
handled = false;
break;
}
}
if (movement && ((modifiers & InputModifiers.Shift) != 0))
if (movement && selection)
{
SelectionEnd = CaretIndex;
}
@ -663,12 +722,12 @@ namespace Avalonia.Controls
return result;
}
private void MoveHorizontal(int direction, InputModifiers modifiers)
private void MoveHorizontal(int direction, bool wholeWord)
{
var text = Text ?? string.Empty;
var caretIndex = CaretIndex;
if ((modifiers & InputModifiers.Control) == 0)
if (!wholeWord)
{
var index = caretIndex + direction;
@ -706,7 +765,7 @@ namespace Avalonia.Controls
}
}
private bool MoveVertical(int count, InputModifiers modifiers)
private bool MoveVertical(int count)
{
var formattedText = _presenter.FormattedText;
var lines = formattedText.GetLines().ToList();
@ -729,12 +788,12 @@ namespace Avalonia.Controls
}
}
private void MoveHome(InputModifiers modifiers)
private void MoveHome(bool document)
{
var text = Text ?? string.Empty;
var caretIndex = CaretIndex;
if ((modifiers & InputModifiers.Control) != 0)
if (document)
{
caretIndex = 0;
}
@ -759,12 +818,12 @@ namespace Avalonia.Controls
CaretIndex = caretIndex;
}
private void MoveEnd(InputModifiers modifiers)
private void MoveEnd(bool document)
{
var text = Text ?? string.Empty;
var caretIndex = CaretIndex;
if ((modifiers & InputModifiers.Control) != 0)
if (document)
{
caretIndex = text.Length;
}
@ -879,17 +938,17 @@ namespace Avalonia.Controls
}
}
private void SetSelectionForControlBackspace(InputModifiers modifiers)
private void SetSelectionForControlBackspace()
{
SelectionStart = CaretIndex;
MoveHorizontal(-1, modifiers);
MoveHorizontal(-1, true);
SelectionEnd = CaretIndex;
}
private void SetSelectionForControlDelete(InputModifiers modifiers)
private void SetSelectionForControlDelete()
{
SelectionStart = CaretIndex;
MoveHorizontal(1, modifiers);
MoveHorizontal(1, true);
SelectionEnd = CaretIndex;
}

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.
/// </summary>
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 KeyGesture()
{
}
public KeyGesture(Key key, InputModifiers modifiers = InputModifiers.None)
{
Key = key;
Modifiers = modifiers;
}
public bool Equals(KeyGesture other)
{
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; }
}
}

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

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

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

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia.Animation;
using Avalonia.Controls;
@ -15,9 +16,12 @@ namespace Avalonia.Styling
/// </summary>
public class Style : AvaloniaObject, IStyle, ISetStyleParent
{
private static Dictionary<IStyleable, List<IDisposable>> _applied =
new Dictionary<IStyleable, List<IDisposable>>();
private static Dictionary<IStyleable, CompositeDisposable> _applied =
new Dictionary<IStyleable, CompositeDisposable>();
private IResourceNode _parent;
private CompositeDisposable _subscriptions;
private IResourceDictionary _resources;
private IList<IAnimation> _animations;
@ -88,6 +92,14 @@ namespace Avalonia.Styling
}
}
private CompositeDisposable Subscriptions
{
get
{
return _subscriptions ?? (_subscriptions = new CompositeDisposable(2));
}
}
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => _parent;
@ -109,7 +121,9 @@ namespace Avalonia.Styling
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)
{
@ -132,17 +146,25 @@ namespace Avalonia.Styling
var sub = setter.Apply(this, control, match.ObservableResult);
subs.Add(sub);
}
controlSubscriptions.Add(subs);
Subscriptions.Add(subs);
}
}
else if (control == container)
{
var subs = GetSubscriptions(control);
var controlSubscriptions = GetSubscriptions(control);
var subs = new CompositeDisposable(Setters.Count);
foreach (var setter in Setters)
{
var sub = setter.Apply(this, control, null);
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.");
}
if (parent == null)
{
Detach();
}
_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));
_applied.Add(control, subscriptions);
}
@ -209,10 +240,7 @@ namespace Avalonia.Styling
{
var subscriptions = _applied[control];
foreach (var subscription in subscriptions)
{
subscription.Dispose();
}
subscriptions.Dispose();
_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/>
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<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IPlatformIconLoader>().ToConstant(new PlatformIconLoader());
if (useGpu)
EglGlPlatformFeature.TryInitialize();

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

@ -126,7 +126,7 @@ namespace Avalonia.Gtk3
if (state.HasFlag(GdkModifierType.ShiftMask))
rv |= InputModifiers.Shift;
if (state.HasFlag(GdkModifierType.Mod1Mask))
rv |= InputModifiers.Control;
rv |= InputModifiers.Alt;
if (state.HasFlag(GdkModifierType.Button1Mask))
rv |= InputModifiers.LeftMouseButton;
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.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.LinuxFramebuffer;
using Avalonia.Platform;
using Avalonia.Rendering;
@ -36,6 +37,7 @@ namespace Avalonia.LinuxFramebuffer
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
.Bind<IPlatformThreadingInterface>().ToConstant(Threading)
.Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IRenderTimer>().ToConstant(Threading);
}

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/>
public bool TryGetResource(string key, out object value) => Loaded.TryGetResource(key, out value);
@ -90,4 +98,4 @@ namespace Avalonia.Markup.Xaml.Styling
_parent = parent;
}
}
}
}

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

@ -37,6 +37,7 @@ namespace Avalonia.MonoMac
.Bind<IClipboard>().ToSingleton<ClipboardImpl>()
.Bind<IRenderLoop>().ToConstant(s_renderLoop)
.Bind<IRenderTimer>().ToConstant(s_renderTimer)
.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration(InputModifiers.Windows))
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
/*.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<ISystemDialogImpl>().ToSingleton<SystemDialogImpl>()
.Bind<IWindowingPlatform>().ToConstant(s_instance)
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IPlatformIconLoader>().ToConstant(s_instance);
Win32GlManager.Initialize();
UseDeferredRendering = deferredRendering;

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

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

75
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@ -8,7 +8,6 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Markup.Data;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.UnitTests;
@ -322,6 +321,71 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void SelectionEnd_Doesnt_Cause_Exception()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
Text = "0123456789"
};
target.SelectionStart = 0;
target.SelectionEnd = 9;
target.Text = "123";
RaiseTextEvent(target, "456");
Assert.True(true);
}
}
[Fact]
public void SelectionStart_Doesnt_Cause_Exception()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
Text = "0123456789"
};
target.SelectionStart = 8;
target.SelectionEnd = 9;
target.Text = "123";
RaiseTextEvent(target, "456");
Assert.True(true);
}
}
[Fact]
public void SelectionStartEnd_Are_Valid_AterTextChange()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
Text = "0123456789"
};
target.SelectionStart = 8;
target.SelectionEnd = 9;
target.Text = "123";
Assert.True(target.SelectionStart <= "123".Length);
Assert.True(target.SelectionEnd <= "123".Length);
}
}
private static TestServices Services => TestServices.MockThreadingInterface.With(
standardCursorFactory: Mock.Of<IStandardCursorFactory>());
@ -351,6 +415,15 @@ namespace Avalonia.Controls.UnitTests
});
}
private void RaiseTextEvent(TextBox textBox, string text)
{
textBox.RaiseEvent(new TextInputEventArgs
{
RoutedEvent = InputElement.TextInputEvent,
Text = text
});
}
private class Class1 : NotifyingBase
{
private int _foo;

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

@ -167,6 +167,31 @@ namespace Avalonia.Styling.UnitTests
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
{
public static readonly StyledProperty<string> FooProperty =

2
tests/Avalonia.UnitTests/UnitTestApplication.cs

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

Loading…
Cancel
Save