diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index bc52e31d2c..63f500270d 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -44,7 +44,7 @@
Properties\SharedAssemblyInfo.cs
-
+
diff --git a/src/Avalonia.Base/Data/BindingChainNullException.cs b/src/Avalonia.Base/Data/BindingChainException.cs
similarity index 52%
rename from src/Avalonia.Base/Data/BindingChainNullException.cs
rename to src/Avalonia.Base/Data/BindingChainException.cs
index 0e50a36d8a..97b0d3ba8b 100644
--- a/src/Avalonia.Base/Data/BindingChainNullException.cs
+++ b/src/Avalonia.Base/Data/BindingChainException.cs
@@ -10,36 +10,39 @@ namespace Avalonia.Data
/// requested binding expression could not be evaluated because of a null in one of the links
/// of the binding chain.
///
- public class BindingChainNullException : Exception
+ public class BindingChainException : Exception
{
private string _message;
///
- /// Initalizes a new instance of the class.
+ /// Initalizes a new instance of the class.
///
- public BindingChainNullException()
+ public BindingChainException()
{
}
///
- /// Initalizes a new instance of the class.
+ /// Initalizes a new instance of the class.
///
- public BindingChainNullException(string message)
+ /// The error message.
+ public BindingChainException(string message)
{
_message = message;
}
///
- /// Initalizes a new instance of the class.
+ /// Initalizes a new instance of the class.
///
+ /// The error message.
/// The expression.
- ///
- /// The point in the expression at which the null was encountered.
+ ///
+ /// The point in the expression at which the error was encountered.
///
- public BindingChainNullException(string expression, string expressionNullPoint)
+ public BindingChainException(string message, string expression, string errorPoint)
{
+ _message = message;
Expression = expression;
- ExpressionNullPoint = expressionNullPoint;
+ ExpressionErrorPoint = errorPoint;
}
///
@@ -48,37 +51,27 @@ namespace Avalonia.Data
public string Expression { get; protected set; }
///
- /// Gets the point in the expression at which the null was encountered.
+ /// Gets the point in the expression at which the error occured.
///
- public string ExpressionNullPoint { get; protected set; }
+ public string ExpressionErrorPoint { get; protected set; }
///
public override string Message
{
get
{
- if (_message == null)
+ if (Expression != null && ExpressionErrorPoint != null)
{
- _message = BuildMessage();
+ return $"{_message} in expression '{Expression}' at '{ExpressionErrorPoint}'.";
+ }
+ else if (ExpressionErrorPoint != null)
+ {
+ return $"{_message} in expression '{ExpressionErrorPoint}'.";
+ }
+ else
+ {
+ return $"{_message} in expression.";
}
-
- return _message;
- }
- }
-
- private string BuildMessage()
- {
- if (Expression != null && ExpressionNullPoint != null)
- {
- return $"'{ExpressionNullPoint}' is null in expression '{Expression}'.";
- }
- else if (ExpressionNullPoint != null)
- {
- return $"'{ExpressionNullPoint}' is null in expression.";
- }
- else
- {
- return "Null encountered in binding expression.";
}
}
}
diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs
index ed92899cc6..7a271e8615 100644
--- a/src/Avalonia.Controls/TextBox.cs
+++ b/src/Avalonia.Controls/TextBox.cs
@@ -495,10 +495,10 @@ namespace Avalonia.Controls
case 2:
if (!StringUtils.IsStartOfWord(text, index))
{
- SelectionStart = StringUtils.PreviousWord(text, index, false);
+ SelectionStart = StringUtils.PreviousWord(text, index);
}
- SelectionEnd = StringUtils.NextWord(text, index, false);
+ SelectionEnd = StringUtils.NextWord(text, index);
break;
case 3:
SelectionStart = 0;
@@ -547,7 +547,7 @@ namespace Avalonia.Controls
var exceptions = aggregate == null ?
(IEnumerable)new[] { exception } :
aggregate.InnerExceptions;
- var filtered = exceptions.Where(x => !(x is BindingChainNullException)).ToList();
+ var filtered = exceptions.Where(x => !(x is BindingChainException)).ToList();
if (filtered.Count > 0)
{
@@ -638,11 +638,11 @@ namespace Avalonia.Controls
{
if (direction > 0)
{
- CaretIndex += StringUtils.NextWord(text, caretIndex, false) - caretIndex;
+ CaretIndex += StringUtils.NextWord(text, caretIndex) - caretIndex;
}
else
{
- CaretIndex += StringUtils.PreviousWord(text, caretIndex, false) - caretIndex;
+ CaretIndex += StringUtils.PreviousWord(text, caretIndex) - caretIndex;
}
}
}
@@ -718,6 +718,10 @@ namespace Avalonia.Controls
if (pos < text.Length)
{
--pos;
+ if (pos > 0 && Text[pos - 1] == '\r' && Text[pos] == '\n')
+ {
+ --pos;
+ }
}
break;
@@ -820,12 +824,6 @@ namespace Avalonia.Controls
SelectionStart = CaretIndex;
MoveHorizontal(1, modifiers);
SelectionEnd = CaretIndex;
-
- string selection = GetSelection();
- if (selection != " " && selection.EndsWith(" "))
- {
- SelectionEnd = CaretIndex - 1;
- }
}
UndoRedoState UndoRedoHelper.IUndoRedoHost.UndoRedoState
diff --git a/src/Avalonia.Controls/Utils/StringUtils.cs b/src/Avalonia.Controls/Utils/StringUtils.cs
index 8571d663f5..2304866a85 100644
--- a/src/Avalonia.Controls/Utils/StringUtils.cs
+++ b/src/Avalonia.Controls/Utils/StringUtils.cs
@@ -57,7 +57,7 @@ namespace Avalonia.Controls.Utils
}
}
- public static int PreviousWord(string text, int cursor, bool gtkMode)
+ public static int PreviousWord(string text, int cursor)
{
int begin;
int i;
@@ -81,60 +81,21 @@ namespace Avalonia.Controls.Utils
return (cr > 0) ? cr : 0;
}
- if (gtkMode)
- {
- CharClass cc = GetCharClass(text[cursor - 1]);
- begin = lf + 1;
- i = cursor;
-
- // skip over the word, punctuation, or run of whitespace
- while (i > begin && GetCharClass(text[i - 1]) == cc)
- {
- i--;
- }
+ CharClass cc = GetCharClass(text[cursor - 1]);
+ begin = lf + 1;
+ i = cursor;
- // if the cursor was at whitespace, skip back a word too
- if (cc == CharClass.CharClassWhitespace && i > begin)
- {
- cc = GetCharClass(text[i - 1]);
- while (i > begin && GetCharClass(text[i - 1]) == cc)
- {
- i--;
- }
- }
- }
- else
+ // skip over the word, punctuation, or run of whitespace
+ while (i > begin && GetCharClass(text[i - 1]) == cc)
{
- begin = lf + 1;
- i = cursor;
-
- if (cursor < text.Length)
- {
- // skip to the beginning of this word
- while (i > begin && !char.IsWhiteSpace(text[i - 1]))
- {
- i--;
- }
-
- if (i < cursor && IsStartOfWord(text, i))
- {
- return i;
- }
- }
-
- // skip to the start of the lwsp
- while (i > begin && char.IsWhiteSpace(text[i - 1]))
- {
- i--;
- }
-
- if (i > begin)
- {
- i--;
- }
+ i--;
+ }
- // skip to the beginning of the word
- while (i > begin && !IsStartOfWord(text, i))
+ // if the cursor was at whitespace, skip back a word too
+ if (cc == CharClass.CharClassWhitespace && i > begin)
+ {
+ cc = GetCharClass(text[i - 1]);
+ while (i > begin && GetCharClass(text[i - 1]) == cc)
{
i--;
}
@@ -143,7 +104,7 @@ namespace Avalonia.Controls.Utils
return i;
}
- public static int NextWord(string text, int cursor, bool gtkMode)
+ public static int NextWord(string text, int cursor)
{
int i, lf, cr;
@@ -169,50 +130,19 @@ namespace Avalonia.Controls.Utils
return cursor;
}
- if (gtkMode)
- {
- CharClass cc = GetCharClass(text[cursor]);
- i = cursor;
-
- // skip over the word, punctuation, or run of whitespace
- while (i < cr && GetCharClass(text[i]) == cc)
- {
- i++;
- }
+ CharClass cc = GetCharClass(text[cursor]);
+ i = cursor;
- // skip any whitespace after the word/punct
- while (i < cr && char.IsWhiteSpace(text[i]))
- {
- i++;
- }
- }
- else
+ // skip over the word, punctuation, or run of whitespace
+ while (i < cr && GetCharClass(text[i]) == cc)
{
- i = cursor;
-
- // skip any whitespace before the word
- while (i < cr && char.IsWhiteSpace(text[i]))
- {
- i++;
- }
-
- // skip to the end of the current word
- while (i < cr && !char.IsWhiteSpace(text[i]))
- {
- i++;
- }
-
- // skip any whitespace after the word
- while (i < cr && char.IsWhiteSpace(text[i]))
- {
- i++;
- }
+ i++;
+ }
- // find the start of the next word
- while (i < cr && !IsStartOfWord(text, i))
- {
- i++;
- }
+ // skip any whitespace after the word/punct
+ while (i < cr && char.IsWhiteSpace(text[i]))
+ {
+ i++;
}
return i;
diff --git a/src/Avalonia.Input/AccessKeyHandler.cs b/src/Avalonia.Input/AccessKeyHandler.cs
index 7acbc109fc..7baa4103d7 100644
--- a/src/Avalonia.Input/AccessKeyHandler.cs
+++ b/src/Avalonia.Input/AccessKeyHandler.cs
@@ -43,6 +43,16 @@ namespace Avalonia.Input
///
private bool _ignoreAltUp;
+ ///
+ /// Whether the AltKey is down.
+ ///
+ private bool _altIsDown;
+
+ ///
+ /// Element to restore folowing AltKey taking focus.
+ ///
+ private IInputElement _restoreFocusElement;
+
///
/// Gets or sets the window's main menu.
///
@@ -110,8 +120,14 @@ namespace Avalonia.Input
{
if (e.Key == Key.LeftAlt)
{
+ _altIsDown = true;
+
if (MainMenu == null || !MainMenu.IsOpen)
{
+ // TODO: Use FocusScopes to store the current element and restore it when context menu is closed.
+ // Save currently focused input element.
+ _restoreFocusElement = FocusManager.Instance.Current;
+
// When Alt is pressed without a main menu, or with a closed main menu, show
// access key markers in the window (i.e. "_File").
_owner.ShowAccessKeys = _showingAccessKeys = true;
@@ -121,11 +137,18 @@ namespace Avalonia.Input
// If the Alt key is pressed and the main menu is open, close the main menu.
CloseMenu();
_ignoreAltUp = true;
+
+ _restoreFocusElement?.Focus();
+ _restoreFocusElement = null;
}
// We always handle the Alt key.
e.Handled = true;
}
+ else if (_altIsDown)
+ {
+ _ignoreAltUp = true;
+ }
}
///
@@ -179,6 +202,8 @@ namespace Avalonia.Input
switch (e.Key)
{
case Key.LeftAlt:
+ _altIsDown = false;
+
if (_ignoreAltUp)
{
_ignoreAltUp = false;
diff --git a/src/Markup/Avalonia.Markup.Xaml/OmniXAML b/src/Markup/Avalonia.Markup.Xaml/OmniXAML
index b122549406..544af79d21 160000
--- a/src/Markup/Avalonia.Markup.Xaml/OmniXAML
+++ b/src/Markup/Avalonia.Markup.Xaml/OmniXAML
@@ -1 +1 @@
-Subproject commit b122549406107170bbe6e67c0d6a1a4252beef77
+Subproject commit 544af79d218127b4174da4be19896c5ca78eaa5d
diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
index 88c4a6ab18..1c3f453280 100644
--- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
+++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
@@ -42,7 +42,8 @@
Properties\SharedAssemblyInfo.cs
-
+
+
@@ -63,9 +64,9 @@
-
-
-
+
+
+
diff --git a/src/Markup/Avalonia.Markup/Data/ExpressionNode.cs b/src/Markup/Avalonia.Markup/Data/ExpressionNode.cs
index b0957c7187..93f20e4c77 100644
--- a/src/Markup/Avalonia.Markup/Data/ExpressionNode.cs
+++ b/src/Markup/Avalonia.Markup/Data/ExpressionNode.cs
@@ -17,7 +17,6 @@ namespace Avalonia.Markup.Data
private WeakReference _target = UnsetReference;
private IDisposable _valueSubscription;
private IObserver