Browse Source

Merge branch 'master' into tab-navigation-fix

pull/1366/head
Steven Kirk 9 years ago
committed by GitHub
parent
commit
25c23c06ee
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      appveyor.yml
  2. 10
      readme.md
  3. 2
      src/Avalonia.Controls/Primitives/Thumb.cs
  4. 65
      src/Avalonia.Controls/TextBox.cs
  5. 47
      src/Avalonia.Controls/Window.cs
  6. 1
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  7. 20
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  8. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs
  9. 9
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  10. 19
      src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
  11. 7
      src/Gtk/Avalonia.Gtk3/SystemDialogs.cs
  12. 6
      src/Shared/SharedAssemblyInfo.cs
  13. 24
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
  14. 22
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
  15. 6
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs
  16. 2
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneTests.cs

2
appveyor.yml

@ -5,8 +5,6 @@ configuration:
environment:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
NUGET_API_KEY:
secure: Xv89dlP2MSBZKhl1nrWSxqcDgCXB0HRhOd4SWQ+jRJ7QoLxQel5mLTipXM++J3G5
NUGET_API_URL: https://www.nuget.org/api/v2/package
MYGET_API_KEY:
secure: OtVfyN3ErqQrDTnWH2HDfJDlCiu/i4/X4wFmK3ZXXP7HmCiXYPSbTjMPwwdOxRaK

10
readme.md

@ -10,7 +10,7 @@
Avalonia is a WPF-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of OSs: Windows (.NET Framework, .NET Core), Linux (GTK), MacOS, Android and iOS.
<b>Avalonia is now in alpha.</b> This means that framework is now at a stage where you can have a play and hopefully create simple applications. There's still a lot missing, and you *will* find bugs, and the API *will* change, but this represents the first time where we've made it somewhat easy to have a play and experiment with the framework.
**Avalonia is currently in beta** which means that the framework is generally usable for writing applications, but there may be some bugs and breaking changes as we continue development.
| Control catalog | Desktop platforms | Mobile platforms |
|---|---|---|
@ -35,16 +35,16 @@ https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master/artifacts
## Documentation
As mentioned above, Avalonia is still in alpha and as such there's not much documentation yet. You can take a look at the [getting started page](http://avaloniaui.net/tutorial/gettingstarted) for an overview of how to get started but probably the best thing to do for now is to already know a little bit about WPF/Silverlight/UWP/XAML and ask questions in our [Gitter room](https://gitter.im/AvaloniaUI/Avalonia).
As mentioned above, Avalonia is still in alpha and as such there's not much documentation yet. You can take a look at the [getting started page](http://avaloniaui.net/guides/quickstart) for an overview of how to get started but probably the best thing to do for now is to already know a little bit about WPF/Silverlight/UWP/XAML and ask questions in our [Gitter room](https://gitter.im/AvaloniaUI/Avalonia).
There's also a high-level [architecture document](http://avaloniaui.net/spec/architecture) that is currently a little bit out of date, and I've also started writing blog posts on Avalonia at http://grokys.github.io/.
There's also a high-level [architecture document](http://avaloniaui.net/architecture/project-structure) that is currently a little bit out of date, and I've also started writing blog posts on Avalonia at http://grokys.github.io/.
Contributions are always welcome!
## Building and Using
See the [build instructions here](http://avaloniaui.net/guidelines/build).
See the [build instructions here](http://avaloniaui.net/contributing/build).
## Contributing
Please read the [contribution guidelines](http://avaloniaui.net/guidelines/contributing) before submitting a pull request.
Please read the [contribution guidelines](http://avaloniaui.net/contributing/contributing) before submitting a pull request.

2
src/Avalonia.Controls/Primitives/Thumb.cs

@ -75,6 +75,7 @@ namespace Avalonia.Controls.Primitives
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
e.Device.Capture(this);
e.Handled = true;
_lastPoint = e.GetPosition(this);
var ev = new VectorEventArgs
@ -91,6 +92,7 @@ namespace Avalonia.Controls.Primitives
if (_lastPoint.HasValue)
{
e.Device.Capture(null);
e.Handled = true;
_lastPoint = null;
var ev = new VectorEventArgs

65
src/Avalonia.Controls/TextBox.cs

@ -98,9 +98,19 @@ namespace Avalonia.Controls
var horizontalScrollBarVisibility = Observable.CombineLatest(
this.GetObservable(AcceptsReturnProperty),
this.GetObservable(TextWrappingProperty),
(acceptsReturn, wrapping) => acceptsReturn && wrapping == TextWrapping.NoWrap ?
ScrollBarVisibility.Auto : ScrollBarVisibility.Disabled);
(acceptsReturn, wrapping) =>
{
if (acceptsReturn)
{
return wrapping == TextWrapping.NoWrap ?
ScrollBarVisibility.Auto :
ScrollBarVisibility.Disabled;
}
else
{
return ScrollBarVisibility.Hidden;
}
});
Bind(
ScrollViewer.HorizontalScrollBarVisibilityProperty,
horizontalScrollBarVisibility,
@ -487,37 +497,34 @@ namespace Avalonia.Controls
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
if (e.Source == _presenter)
{
var point = e.GetPosition(_presenter);
var index = CaretIndex = _presenter.GetCaretIndex(point);
var text = Text;
var point = e.GetPosition(_presenter);
var index = CaretIndex = _presenter.GetCaretIndex(point);
var text = Text;
if (text != null)
if (text != null)
{
switch (e.ClickCount)
{
switch (e.ClickCount)
{
case 1:
SelectionStart = SelectionEnd = index;
break;
case 2:
if (!StringUtils.IsStartOfWord(text, index))
{
SelectionStart = StringUtils.PreviousWord(text, index);
}
case 1:
SelectionStart = SelectionEnd = index;
break;
case 2:
if (!StringUtils.IsStartOfWord(text, index))
{
SelectionStart = StringUtils.PreviousWord(text, index);
}
SelectionEnd = StringUtils.NextWord(text, index);
break;
case 3:
SelectionStart = 0;
SelectionEnd = text.Length;
break;
}
SelectionEnd = StringUtils.NextWord(text, index);
break;
case 3:
SelectionStart = 0;
SelectionEnd = text.Length;
break;
}
e.Device.Capture(_presenter);
e.Handled = true;
}
e.Device.Capture(_presenter);
e.Handled = true;
}
protected override void OnPointerMoved(PointerEventArgs e)

47
src/Avalonia.Controls/Window.cs

@ -19,27 +19,28 @@ namespace Avalonia.Controls
/// <summary>
/// Determines how a <see cref="Window"/> will size itself to fit its content.
/// </summary>
[Flags]
public enum SizeToContent
{
/// <summary>
/// The window will not automatically size itself to fit its content.
/// </summary>
Manual,
Manual = 0,
/// <summary>
/// The window will size itself horizontally to fit its content.
/// </summary>
Width,
Width = 1,
/// <summary>
/// The window will size itself vertically to fit its content.
/// </summary>
Height,
Height = 2,
/// <summary>
/// The window will size itself horizontally and vertically to fit its content.
/// </summary>
WidthAndHeight,
WidthAndHeight = 3,
}
/// <summary>
@ -374,27 +375,31 @@ namespace Avalonia.Controls
{
var sizeToContent = SizeToContent;
var clientSize = ClientSize;
Size constraint;
Size constraint = clientSize;
switch (sizeToContent)
if ((sizeToContent & SizeToContent.Width) != 0)
{
case SizeToContent.Width:
constraint = new Size(double.PositiveInfinity, ClientSize.Height);
break;
case SizeToContent.Height:
constraint = new Size(ClientSize.Width, double.PositiveInfinity);
break;
case SizeToContent.WidthAndHeight:
constraint = Size.Infinity;
break;
case SizeToContent.Manual:
constraint = ClientSize;
break;
default:
throw new InvalidOperationException("Invalid value for SizeToContent.");
constraint = constraint.WithWidth(double.PositiveInfinity);
}
return base.MeasureOverride(constraint);
if ((sizeToContent & SizeToContent.Height) != 0)
{
constraint = constraint.WithHeight(double.PositiveInfinity);
}
var result = base.MeasureOverride(constraint);
if ((sizeToContent & SizeToContent.Width) == 0)
{
result = result.WithWidth(clientSize.Width);
}
if ((sizeToContent & SizeToContent.Height) == 0)
{
result = result.WithHeight(clientSize.Height);
}
return result;
}
protected override void HandleClosed()

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

@ -140,6 +140,7 @@ namespace Avalonia.DesignerSupport.Remote
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (builderMethod == null)
throw Die($"{entryPoint.DeclaringType.FullName} doesn't have a method named {BuilderMethodName}");
Design.IsDesignMode = true;
Log($"Obtaining AppBuilder instance from {builderMethod.DeclaringType.FullName}.{builderMethod.Name}");
var appBuilder = builderMethod.Invoke(null, null);
Log($"Initializing application in design mode");

20
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -28,7 +28,7 @@ namespace Avalonia.Rendering
private readonly ISceneBuilder _sceneBuilder;
private bool _running;
private Scene _scene;
private volatile IRef<Scene> _scene;
private DirtyVisuals _dirty;
private IRef<IRenderTargetBitmapImpl> _overlay;
private bool _updateQueued;
@ -128,7 +128,7 @@ namespace Avalonia.Rendering
UpdateScene();
}
return _scene?.HitTest(p, root, filter) ?? Enumerable.Empty<IVisual>();
return _scene?.Item.HitTest(p, root, filter) ?? Enumerable.Empty<IVisual>();
}
/// <inheritdoc/>
@ -180,7 +180,7 @@ namespace Avalonia.Rendering
internal void UnitTestUpdateScene() => UpdateScene();
internal void UnitTestRender() => Render(_scene);
internal void UnitTestRender() => Render(_scene.Item);
private void Render(Scene scene)
{
@ -381,7 +381,8 @@ namespace Avalonia.Rendering
{
if (_root.IsVisible)
{
var scene = _scene?.Clone() ?? new Scene(_root);
var sceneRef = RefCountable.Create(_scene?.Item.CloneScene() ?? new Scene(_root));
var scene = sceneRef.Item;
if (_dirty == null)
{
@ -396,7 +397,7 @@ namespace Avalonia.Rendering
}
}
var oldScene = Interlocked.Exchange(ref _scene, scene);
var oldScene = Interlocked.Exchange(ref _scene, sceneRef);
oldScene?.Dispose();
_dirty.Clear();
@ -425,10 +426,11 @@ namespace Avalonia.Rendering
_updateQueued = true;
_dispatcher.Post(UpdateScene, DispatcherPriority.Render);
}
Scene scene = null;
Interlocked.Exchange(ref scene, _scene);
Render(scene);
using (var scene = _scene?.Clone())
{
Render(scene?.Item);
}
}
catch { }
finally

2
src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs

@ -93,5 +93,7 @@ namespace Avalonia.Rendering.SceneGraph
/// to hit test children they must be hit tested manually.
/// </remarks>
bool HitTest(Point p);
bool Disposed { get; }
}
}

9
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@ -82,7 +82,7 @@ namespace Avalonia.Rendering.SceneGraph
/// Clones the scene.
/// </summary>
/// <returns>The cloned scene.</returns>
public Scene Clone()
public Scene CloneScene()
{
var index = new Dictionary<IVisual, IVisualNode>();
var root = Clone((VisualNode)Root, null, index);
@ -98,7 +98,10 @@ namespace Avalonia.Rendering.SceneGraph
public void Dispose()
{
Root.Dispose();
foreach (var node in _index.Values)
{
node.Dispose();
}
}
/// <summary>
@ -137,6 +140,8 @@ namespace Avalonia.Rendering.SceneGraph
Contract.Requires<ArgumentNullException>(node != null);
_index.Remove(node.Visual);
node.Dispose();
}
private VisualNode Clone(VisualNode source, IVisualNode parent, Dictionary<IVisual, IVisualNode> index)

19
src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs

@ -113,6 +113,11 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="child">The child to add.</param>
public void AddChild(IVisualNode child)
{
if (child.Disposed)
{
throw new ObjectDisposedException("Visual node for {node.Visual}");
}
EnsureChildrenCreated();
_children.Add(child);
}
@ -135,7 +140,6 @@ namespace Avalonia.Rendering.SceneGraph
{
EnsureChildrenCreated();
_children.Remove(child);
child.Dispose();
}
/// <summary>
@ -145,10 +149,13 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="node">The child to add.</param>
public void ReplaceChild(int index, IVisualNode node)
{
if (node.Disposed)
{
throw new ObjectDisposedException("Visual node for {node.Visual}");
}
EnsureChildrenCreated();
var old = _children[index];
_children[index] = node;
old.Dispose();
}
/// <summary>
@ -330,12 +337,10 @@ namespace Avalonia.Rendering.SceneGraph
}
}
public bool Disposed { get; }
public void Dispose()
{
foreach (var child in Children)
{
child.Dispose();
}
_drawOperationsRefCounter?.Dispose();
}

7
src/Gtk/Avalonia.Gtk3/SystemDialogs.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -79,9 +80,11 @@ namespace Avalonia.Gtk3
public Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
{
return ShowDialog(dialog.Title, ((WindowBaseImpl) parent)?.GtkWidget,
return ShowDialog(dialog.Title, ((WindowBaseImpl)parent)?.GtkWidget,
dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save,
(dialog as OpenFileDialog)?.AllowMultiple ?? false, dialog.InitialFileName);
(dialog as OpenFileDialog)?.AllowMultiple ?? false,
Path.Combine(string.IsNullOrEmpty(dialog.InitialDirectory) ? "" : dialog.InitialDirectory,
string.IsNullOrEmpty(dialog.InitialFileName) ? "" : dialog.InitialFileName));
}
public async Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)

6
src/Shared/SharedAssemblyInfo.cs

@ -14,6 +14,6 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyTrademark("")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: AssemblyVersion("0.5.2")]
[assembly: AssemblyFileVersion("0.5.2")]
[assembly: AssemblyInformationalVersion("0.5.2")]
[assembly: AssemblyVersion("0.6.0")]
[assembly: AssemblyFileVersion("0.6.0")]
[assembly: AssemblyInformationalVersion("0.6.0")]

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

@ -4,10 +4,12 @@
using System;
using System.Reactive.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.UnitTests;
using Moq;
@ -245,6 +247,28 @@ namespace Avalonia.Controls.UnitTests
}
}
[Theory]
[InlineData(new object[] { false, TextWrapping.NoWrap, ScrollBarVisibility.Hidden })]
[InlineData(new object[] { false, TextWrapping.Wrap, ScrollBarVisibility.Hidden })]
[InlineData(new object[] { true, TextWrapping.NoWrap, ScrollBarVisibility.Auto })]
[InlineData(new object[] { true, TextWrapping.Wrap, ScrollBarVisibility.Disabled })]
public void Has_Correct_Horizontal_ScrollBar_Visibility(
bool acceptsReturn,
TextWrapping wrapping,
ScrollBarVisibility expected)
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
AcceptsReturn = acceptsReturn,
TextWrapping = wrapping,
};
Assert.Equal(expected, ScrollViewer.GetHorizontalScrollBarVisibility(target));
}
}
private static TestServices Services => TestServices.MockThreadingInterface.With(
standardCursorFactory: Mock.Of<IStandardCursorFactory>());

22
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

@ -99,7 +99,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Equal(new Rect(10, 20, 160, 240), canvasNode.ClipBounds);
// Initial ClipBounds are correct, make sure they're still correct after updating canvas.
result = result.Clone();
result = result.CloneScene();
Assert.True(sceneBuilder.Update(result, canvas));
canvasNode = result.FindNode(canvas);
@ -197,7 +197,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
canvas.Arrange(new Rect(tree.DesiredSize));
// Initial ClipBounds are correct, make sure they're still correct after updating canvas.
scene = scene.Clone();
scene = scene.CloneScene();
Assert.True(sceneBuilder.Update(scene, canvas));
borderNode = scene.FindNode(border);
@ -309,7 +309,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
var borderNode = scene.FindNode(border);
Assert.Equal(expectedTransform, borderNode.Transform);
scene = scene.Clone();
scene = scene.CloneScene();
Assert.True(sceneBuilder.Update(scene, border));
borderNode = scene.FindNode(border);
@ -354,7 +354,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
border.Background = Brushes.Green;
var result = initial.Clone();
var result = initial.CloneScene();
sceneBuilder.Update(result, border);
var borderNode = (VisualNode)result.Root.Children[0];
@ -402,7 +402,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
sceneBuilder.UpdateAll(initial);
border.Child = decorator;
var result = initial.Clone();
var result = initial.CloneScene();
Assert.True(sceneBuilder.Update(result, decorator));
@ -457,7 +457,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
sceneBuilder.UpdateAll(initial);
border.Child = null;
var result = initial.Clone();
var result = initial.CloneScene();
Assert.True(sceneBuilder.Update(result, decorator));
Assert.False(sceneBuilder.Update(result, canvas));
@ -501,7 +501,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
sceneBuilder.UpdateAll(initial);
border.IsVisible = false;
var result = initial.Clone();
var result = initial.CloneScene();
Assert.True(sceneBuilder.Update(result, border));
Assert.False(sceneBuilder.Update(result, canvas));
@ -552,7 +552,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
decorator.Margin = new Thickness(0, 20, 0, 0);
layout.ExecuteLayoutPass();
scene = scene.Clone();
scene = scene.CloneScene();
sceneBuilder.Update(scene, decorator);
borderNode = scene.FindNode(border);
@ -600,7 +600,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
decorator.Margin = new Thickness(0, 20, 0, 0);
layout.ExecuteLayoutPass();
scene = scene.Clone();
scene = scene.CloneScene();
sceneBuilder.Update(scene, decorator);
@ -640,7 +640,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Equal(new Size(100, 100), scene.Size);
tree.ClientSize = new Size(110, 120);
scene = scene.Clone();
scene = scene.CloneScene();
sceneBuilder.Update(scene, tree);
Assert.Equal(new Size(110, 120), scene.Size);
@ -735,7 +735,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
tree.Child = new Decorator();
using (var result = scene.Clone())
using (var result = scene.CloneScene())
{
sceneBuilder.Update(result, img);
scene.Dispose();

6
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs

@ -62,7 +62,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Empty(scene.Layers.Select(x => x.LayerRoot).Except(new IVisual[] { tree, border }));
animation.OnCompleted();
scene = scene.Clone();
scene = scene.CloneScene();
sceneBuilder.Update(scene, border);
@ -160,7 +160,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Equal(3, scene.Layers.Count);
decorator.Child = null;
scene = scene.Clone();
scene = scene.CloneScene();
sceneBuilder.Update(scene, border);
@ -210,7 +210,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Equal(3, scene.Layers.Count);
border.IsVisible = false;
scene = scene.Clone();
scene = scene.CloneScene();
sceneBuilder.Update(scene, border);

2
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneTests.cs

@ -25,7 +25,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
scene.Layers[tree].Dirty.Add(new Rect(0, 0, 100, 100));
scene.Layers[decorator].Dirty.Add(new Rect(0, 0, 50, 100));
scene = scene.Clone();
scene = scene.CloneScene();
Assert.Equal(2, scene.Layers.Count());
Assert.Empty(scene.Layers[0].Dirty);
Assert.Empty(scene.Layers[1].Dirty);

Loading…
Cancel
Save