Browse Source

AvalonEdit: fixed 'Home' key and CaretPositioningMode.WordStart over line borders.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4206 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 17 years ago
parent
commit
8c332d9550
  1. 2
      src/AddIns/Misc/SearchAndReplace/Project/Commands/SearchToolbarCommands.cs
  2. 12
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs
  3. 36
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs
  4. 10
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
  5. 23
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs
  6. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/NullSafeCollection.cs
  7. 5
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ObserveAddRemoveCollection.cs
  8. 5
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/TextUtilities.cs
  9. 7
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ThrowUtil.cs
  10. 2
      src/Main/Base/Project/Src/Gui/Workbench/WpfWorkbench.xaml

2
src/AddIns/Misc/SearchAndReplace/Project/Commands/SearchToolbarCommands.cs

@ -35,8 +35,8 @@ namespace SearchAndReplace
void OnKeyPress(object sender, KeyEventArgs e) void OnKeyPress(object sender, KeyEventArgs e)
{ {
if (e.Key == Key.Enter) { if (e.Key == Key.Enter) {
CommitSearch();
e.Handled = true; e.Handled = true;
CommitSearch();
} }
} }

12
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs

@ -5,13 +5,13 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using ICSharpCode.AvalonEdit.Utils;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Media.TextFormatting; using System.Windows.Media.TextFormatting;
using System.Windows.Threading; using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Rendering;
@ -58,7 +58,7 @@ namespace ICSharpCode.AvalonEdit.Editing
/// <summary> /// <summary>
/// Gets/Sets the position of the caret. /// Gets/Sets the position of the caret.
/// Retrieving this property will validate the visual column. /// Retrieving this property will validate the visual column (which can be expensive).
/// Use the <see cref="Location"/> property instead if you don't need the visual column. /// Use the <see cref="Location"/> property instead if you don't need the visual column.
/// </summary> /// </summary>
public TextViewPosition Position { public TextViewPosition Position {
@ -264,7 +264,9 @@ namespace ICSharpCode.AvalonEdit.Editing
// then try backwards // then try backwards
newVisualColumn = visualLine.GetNextCaretPosition(position.VisualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.Normal); newVisualColumn = visualLine.GetNextCaretPosition(position.VisualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.Normal);
} }
if (newVisualColumn >= 0 && newVisualColumn != position.VisualColumn) { if (newVisualColumn < 0)
throw ThrowUtil.NoValidCaretPosition();
if (newVisualColumn != position.VisualColumn) {
int newOffset = visualLine.GetRelativeOffset(newVisualColumn) + firstDocumentLineOffset; int newOffset = visualLine.GetRelativeOffset(newVisualColumn) + firstDocumentLineOffset;
this.Position = new TextViewPosition(textView.Document.GetLocation(newOffset), newVisualColumn); this.Position = new TextViewPosition(textView.Document.GetLocation(newOffset), newVisualColumn);
} }
@ -294,7 +296,9 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
if (textView != null) { if (textView != null) {
VisualLine visualLine = textView.GetOrConstructVisualLine(textView.Document.GetLineByNumber(position.Line)); VisualLine visualLine = textView.GetOrConstructVisualLine(textView.Document.GetLineByNumber(position.Line));
textView.MakeVisible(CalcCaretRectangle(visualLine)); Rect caretRectangle = CalcCaretRectangle(visualLine);
caretRectangle.Inflate(30, 30); // leave at least 30 pixels distance to the view border
textView.MakeVisible(caretRectangle);
} }
} }

36
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs

@ -7,6 +7,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media.TextFormatting; using System.Windows.Media.TextFormatting;
@ -185,9 +186,8 @@ namespace ICSharpCode.AvalonEdit.Editing
static void MoveCaretToStartOfLine(TextArea textArea, VisualLine visualLine) static void MoveCaretToStartOfLine(TextArea textArea, VisualLine visualLine)
{ {
int newVC = visualLine.GetNextCaretPosition(-1, LogicalDirection.Forward, CaretPositioningMode.WordStart); int newVC = visualLine.GetNextCaretPosition(-1, LogicalDirection.Forward, CaretPositioningMode.WordStart);
// in empty lines (whitespace only), jump to the end
if (newVC < 0) if (newVC < 0)
newVC = visualLine.VisualLength; throw ThrowUtil.NoValidCaretPosition();
// when the caret is already at the start of the text, jump to start before whitespace // when the caret is already at the start of the text, jump to start before whitespace
if (newVC == textArea.Caret.VisualColumn) if (newVC == textArea.Caret.VisualColumn)
newVC = 0; newVC = 0;
@ -211,18 +211,40 @@ namespace ICSharpCode.AvalonEdit.Editing
SetCaretPosition(textArea, pos, visualLine.GetRelativeOffset(pos) + visualLine.FirstDocumentLine.Offset); SetCaretPosition(textArea, pos, visualLine.GetRelativeOffset(pos) + visualLine.FirstDocumentLine.Offset);
} else { } else {
// move to start of next line // move to start of next line
SetCaretPosition(textArea, 0, visualLine.LastDocumentLine.Offset + visualLine.LastDocumentLine.TotalLength); DocumentLine nextDocumentLine = visualLine.LastDocumentLine.NextLine;
if (nextDocumentLine != null) {
VisualLine nextLine = textArea.TextView.GetOrConstructVisualLine(nextDocumentLine);
pos = nextLine.GetNextCaretPosition(-1, LogicalDirection.Forward, mode);
if (pos < 0)
throw ThrowUtil.NoValidCaretPosition();
SetCaretPosition(textArea, pos, nextLine.GetRelativeOffset(pos) + nextLine.FirstDocumentLine.Offset);
} else {
// at end of document
Debug.Assert(visualLine.LastDocumentLine.Offset + visualLine.LastDocumentLine.TotalLength == textArea.Document.TextLength);
SetCaretPosition(textArea, -1, textArea.Document.TextLength);
}
} }
} }
static void MoveCaretLeft(TextArea textArea, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode) static void MoveCaretLeft(TextArea textArea, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode)
{ {
int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode); int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode);
if (pos >= 0) { if (pos >= 0) {
SetCaretPosition(textArea, pos, visualLine.GetRelativeOffset(pos) + visualLine.FirstDocumentLine.Offset); SetCaretPosition(textArea, pos, visualLine.GetRelativeOffset(pos) + visualLine.FirstDocumentLine.Offset);
} else if (caretPosition.Line > 1) { } else {
DocumentLine prevLine = textArea.Document.GetLineByNumber(caretPosition.Line - 1); // move to end of previous line
SetCaretPosition(textArea, -1, prevLine.Offset + prevLine.Length); DocumentLine previousDocumentLine = visualLine.FirstDocumentLine.PreviousLine;
if (previousDocumentLine != null) {
VisualLine previousLine = textArea.TextView.GetOrConstructVisualLine(previousDocumentLine);
pos = previousLine.GetNextCaretPosition(previousLine.VisualLength + 1, LogicalDirection.Backward, mode);
if (pos < 0)
throw ThrowUtil.NoValidCaretPosition();
SetCaretPosition(textArea, pos, previousLine.GetRelativeOffset(pos) + previousLine.FirstDocumentLine.Offset);
} else {
// at start of document
Debug.Assert(visualLine.FirstDocumentLine.Offset == 0);
SetCaretPosition(textArea, 0, 0);
}
} }
} }
#endregion #endregion

10
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs

@ -232,16 +232,6 @@ namespace ICSharpCode.AvalonEdit.Editing
if (textArea.Selection.IsEmpty) if (textArea.Selection.IsEmpty)
selectingCommand.Execute(args.Parameter, textArea); selectingCommand.Execute(args.Parameter, textArea);
textArea.RemoveSelectedText(); textArea.RemoveSelectedText();
if (selectingCommand == EditingCommands.SelectLeftByWord) {
// Special case: when Ctrl+Backspace deletes until the start of the line,
// also delete the previous line delimiter.
// This allows deleting lines that consist only of indentation using a single
// press on Ctrl+Backspace.
if (textArea.Caret.Column == 1 && textArea.Caret.VisualColumn == 0 && textArea.Caret.Line > 1) {
DocumentLine previousLine = textArea.Document.GetLineByNumber(textArea.Caret.Line - 1);
textArea.Document.Remove(previousLine.EndOffset, previousLine.DelimiterLength);
}
}
} }
textArea.Caret.BringCaretToView(); textArea.Caret.BringCaretToView();
args.Handled = true; args.Handled = true;

23
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs

@ -334,7 +334,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
if (direction == LogicalDirection.Backward) { if (direction == LogicalDirection.Backward) {
// Search Backwards: // Search Backwards:
// If the last element doesn't handle line borders, return the line end as caret stop // If the last element doesn't handle line borders, return the line end as caret stop
if (visualColumn > this.VisualLength && !elements[elements.Count-1].HandlesLineBorders) { // (we don't check the mode here: we treat a line end as word start, border, etc.
if (visualColumn > this.VisualLength && !elements[elements.Count-1].HandlesLineBorders && HasImplicitStopAtLineEnd(mode)) {
return this.VisualLength; return this.VisualLength;
} }
// skip elements that start after or at visualColumn // skip elements that start after or at visualColumn
@ -350,14 +351,14 @@ namespace ICSharpCode.AvalonEdit.Rendering
if (pos >= 0) if (pos >= 0)
return pos; return pos;
} }
// if we've found nothing, and the first element doesn't handle line borders, // If we've found nothing, and the first element doesn't handle line borders,
// return the line start as caret stop // return the line start as normal caret stop.
if (visualColumn > 0 && !elements[0].HandlesLineBorders) if (visualColumn > 0 && !elements[0].HandlesLineBorders && HasImplicitStopAtLineStart(mode))
return 0; return 0;
} else { } else {
// Search Forwards: // Search Forwards:
// If the first element doesn't handle line borders, return the line start as caret stop // If the first element doesn't handle line borders, return the line start as caret stop
if (visualColumn < 0 && !elements[0].HandlesLineBorders) if (visualColumn < 0 && !elements[0].HandlesLineBorders && HasImplicitStopAtLineStart(mode))
return 0; return 0;
// skip elements that end before or at visualColumn // skip elements that end before or at visualColumn
for (i = 0; i < elements.Count; i++) { for (i = 0; i < elements.Count; i++) {
@ -374,11 +375,21 @@ namespace ICSharpCode.AvalonEdit.Rendering
} }
// if we've found nothing, and the last element doesn't handle line borders, // if we've found nothing, and the last element doesn't handle line borders,
// return the line end as caret stop // return the line end as caret stop
if (visualColumn < this.VisualLength && !elements[elements.Count-1].HandlesLineBorders) if (visualColumn < this.VisualLength && !elements[elements.Count-1].HandlesLineBorders && HasImplicitStopAtLineEnd(mode))
return this.VisualLength; return this.VisualLength;
} }
// we've found nothing, return -1 and let the caret search continue in the next line // we've found nothing, return -1 and let the caret search continue in the next line
return -1; return -1;
} }
static bool HasImplicitStopAtLineStart(CaretPositioningMode mode)
{
return mode == CaretPositioningMode.Normal;
}
static bool HasImplicitStopAtLineEnd(CaretPositioningMode mode)
{
return true;
}
} }
} }

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/NullSafeCollection.cs

@ -13,7 +13,7 @@ namespace ICSharpCode.AvalonEdit.Utils
/// <summary> /// <summary>
/// A collection that cannot contain null values. /// A collection that cannot contain null values.
/// </summary> /// </summary>
public sealed class NullSafeCollection<T> : Collection<T> where T : class public class NullSafeCollection<T> : Collection<T> where T : class
{ {
/// <inheritdoc/> /// <inheritdoc/>
protected override void InsertItem(int index, T item) protected override void InsertItem(int index, T item)

5
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ObserveAddRemoveCollection.cs

@ -12,6 +12,8 @@ namespace ICSharpCode.AvalonEdit.Utils
{ {
/// <summary> /// <summary>
/// A collection where adding and removing items causes a callback. /// A collection where adding and removing items causes a callback.
/// It is valid for the onAdd callback to throw an exception - this will prevent the new item from
/// being added to the collection.
/// </summary> /// </summary>
sealed class ObserveAddRemoveCollection<T> : Collection<T> sealed class ObserveAddRemoveCollection<T> : Collection<T>
{ {
@ -54,7 +56,8 @@ namespace ICSharpCode.AvalonEdit.Utils
if (onAdd != null) if (onAdd != null)
onAdd(item); onAdd(item);
} catch { } catch {
// when adding the new item fails, just remove the old one // When adding the new item fails, just remove the old one
// (we cannot keep the old item since we already successfully called onRemove for it)
base.RemoveAt(index); base.RemoveAt(index);
throw; throw;
} }

5
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/TextUtilities.cs

@ -191,6 +191,11 @@ namespace ICSharpCode.AvalonEdit.Utils
/// <param name="mode">The mode for caret positioning.</param> /// <param name="mode">The mode for caret positioning.</param>
/// <returns>The offset of the next caret position, or -1 if there is no further caret position /// <returns>The offset of the next caret position, or -1 if there is no further caret position
/// in the text source.</returns> /// in the text source.</returns>
/// <remarks>
/// This method is NOT equivalent to the actual caret movement when using VisualLine.GetNextCaretPosition.
/// In real caret movement, there are additional caret stops at line starts and ends. However, this method
/// doesn't know anything about lines: it is often called with a textSource that represents only a single VisualTextElement.
/// </remarks>
public static int GetNextCaretPosition(ITextSource textSource, int offset, LogicalDirection direction, CaretPositioningMode mode) public static int GetNextCaretPosition(ITextSource textSource, int offset, LogicalDirection direction, CaretPositioningMode mode)
{ {
if (textSource == null) if (textSource == null)

7
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ThrowUtil.cs

@ -22,7 +22,7 @@ namespace ICSharpCode.AvalonEdit.Utils
/// Use this method to throw an ArgumentNullException when using parameters for base /// Use this method to throw an ArgumentNullException when using parameters for base
/// constructor calls. /// constructor calls.
/// <code> /// <code>
/// public VisualLineText(string text) : base(ThrowUtil.CheckNull(text, "text").Length) /// public VisualLineText(string text) : base(ThrowUtil.CheckNotNull(text, "text").Length)
/// </code> /// </code>
/// </example> /// </example>
public static T CheckNotNull<T>(T val, string parameterName) where T : class public static T CheckNotNull<T>(T val, string parameterName) where T : class
@ -43,5 +43,10 @@ namespace ICSharpCode.AvalonEdit.Utils
{ {
throw new InvalidOperationException("Document is null"); throw new InvalidOperationException("Document is null");
} }
public static InvalidOperationException NoValidCaretPosition()
{
throw new InvalidOperationException("Could not find a valid caret position in the line");
}
} }
} }

2
src/Main/Base/Project/Src/Gui/Workbench/WpfWorkbench.xaml

@ -9,6 +9,6 @@
<Menu Name="mainMenu" DockPanel.Dock="Top"/> <Menu Name="mainMenu" DockPanel.Dock="Top"/>
<!-- Toolbars will be inserted here (index: 1) --> <!-- Toolbars will be inserted here (index: 1) -->
<!-- Statusbar will be inserted here (index: Count-2) --> <!-- Statusbar will be inserted here (index: Count-2) -->
<ContentControl Name="mainContent"/> <ContentControl Name="mainContent" Focusable="False"/>
</DockPanel> </DockPanel>
</Window> </Window>
Loading…
Cancel
Save