Add special handling for non-spacing marks and control codes U+0000…U+0020.
This commit is contained in:
@@ -11,6 +11,7 @@ namespace UnicodeCharacterInspector
|
||||
internal sealed class CharacterInfoViewModel : BindableObject
|
||||
{
|
||||
private string character;
|
||||
private string displayText;
|
||||
private int codePoint;
|
||||
private UnicodeCharInfo characterInfo = UnicodeInfo.GetCharInfo(0);
|
||||
|
||||
@@ -49,6 +50,7 @@ namespace UnicodeCharacterInspector
|
||||
}
|
||||
|
||||
NotifyPropertyChanged();
|
||||
UpdateDisplayText();
|
||||
NotifyPropertyChanged(nameof(CodePoint));
|
||||
NotifyPropertyChanged(nameof(Name));
|
||||
NotifyPropertyChanged(nameof(OldName));
|
||||
@@ -77,6 +79,18 @@ namespace UnicodeCharacterInspector
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDisplayText()
|
||||
{
|
||||
string oldValue = displayText;
|
||||
|
||||
displayText = character != null ? UnicodeInfo.GetDisplayText(characterInfo) : null;
|
||||
|
||||
if (displayText != oldValue)
|
||||
NotifyPropertyChanged(nameof(DisplayText));
|
||||
}
|
||||
|
||||
public string DisplayText { get { return displayText; } }
|
||||
|
||||
public int? CodePoint
|
||||
{
|
||||
get { return character != null ? codePoint : null as int?; }
|
||||
|
||||
@@ -12,7 +12,21 @@ namespace UnicodeCharacterInspector
|
||||
{
|
||||
internal sealed class CharacterInspectorViewModel : BindableObject
|
||||
{
|
||||
private class CharacterCollection : INotifyCollectionChanged, IList<string>
|
||||
public sealed class CharacterViewModel
|
||||
{
|
||||
public int CodePoint { get; }
|
||||
public string Character { get; }
|
||||
public string DisplayText { get; }
|
||||
|
||||
public CharacterViewModel(int codePoint)
|
||||
{
|
||||
CodePoint = codePoint;
|
||||
Character = char.ConvertFromUtf32(codePoint);
|
||||
DisplayText = UnicodeInfo.GetDisplayText(codePoint);
|
||||
}
|
||||
}
|
||||
|
||||
private class CharacterCollection : INotifyCollectionChanged, IList<CharacterViewModel>
|
||||
{
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged
|
||||
{
|
||||
@@ -27,17 +41,17 @@ namespace UnicodeCharacterInspector
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public string this[int index]
|
||||
public CharacterViewModel this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index >= owner.characterCount) throw new IndexOutOfRangeException();
|
||||
|
||||
return owner.characters[index];
|
||||
return new CharacterViewModel(owner.codePoints[index]);
|
||||
}
|
||||
}
|
||||
|
||||
string IList<string>.this[int index]
|
||||
CharacterViewModel IList<CharacterViewModel>.this[int index]
|
||||
{
|
||||
get { return this[index]; }
|
||||
set { throw new NotSupportedException(); }
|
||||
@@ -45,28 +59,38 @@ namespace UnicodeCharacterInspector
|
||||
|
||||
public int Count { get { return owner.characterCount; } }
|
||||
|
||||
bool ICollection<string>.IsReadOnly { get { return true; } }
|
||||
bool ICollection<CharacterViewModel>.IsReadOnly { get { return true; } }
|
||||
|
||||
void ICollection<string>.Add(string item) { throw new NotSupportedException(); }
|
||||
void IList<string>.Insert(int index, string item) { throw new NotSupportedException(); }
|
||||
void ICollection<CharacterViewModel>.Add(CharacterViewModel item) { throw new NotSupportedException(); }
|
||||
void IList<CharacterViewModel>.Insert(int index, CharacterViewModel item) { throw new NotSupportedException(); }
|
||||
|
||||
bool ICollection<string>.Remove(string item) { throw new NotSupportedException(); }
|
||||
void IList<string>.RemoveAt(int index) { throw new NotSupportedException(); }
|
||||
bool ICollection<CharacterViewModel>.Remove(CharacterViewModel item) { throw new NotSupportedException(); }
|
||||
void IList<CharacterViewModel>.RemoveAt(int index) { throw new NotSupportedException(); }
|
||||
|
||||
void ICollection<string>.Clear() { throw new NotSupportedException(); }
|
||||
void ICollection<CharacterViewModel>.Clear() { throw new NotSupportedException(); }
|
||||
|
||||
public int IndexOf(string item) { return Array.IndexOf(owner.characters, item); }
|
||||
public bool Contains(string item) { return Array.IndexOf(owner.characters, item) >= 0; }
|
||||
public int IndexOf(CharacterViewModel item) { return Array.IndexOf(owner.codePoints, item); }
|
||||
public bool Contains(CharacterViewModel item) { return Array.IndexOf(owner.codePoints, item) >= 0; }
|
||||
|
||||
public void CopyTo(string[] array, int arrayIndex) { owner.characters.CopyTo(array, arrayIndex); }
|
||||
public void CopyTo(CharacterViewModel[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null) throw new ArgumentNullException("array");
|
||||
if (arrayIndex < 0) throw new ArgumentOutOfRangeException("arrayIndex");
|
||||
if (array.Length < arrayIndex + owner.codePoints.Length) throw new ArgumentException();
|
||||
|
||||
public IEnumerator<string> GetEnumerator()
|
||||
for (int i = 0, j = arrayIndex; i < owner.codePoints.Length; ++i, ++j)
|
||||
{
|
||||
array[j] = new CharacterViewModel(owner.codePoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<CharacterViewModel> GetEnumerator()
|
||||
{
|
||||
int length = owner.characterCount;
|
||||
var array = owner.characters;
|
||||
var array = owner.codePoints;
|
||||
|
||||
for (int i = 0; i < length; ++i)
|
||||
yield return array[i];
|
||||
yield return new CharacterViewModel(array[i]);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
|
||||
@@ -76,7 +100,7 @@ namespace UnicodeCharacterInspector
|
||||
|
||||
private string text;
|
||||
private int selectedCharacterIndex = -1;
|
||||
private string[] characters = new string[1024];
|
||||
private int[] codePoints = new int[1024];
|
||||
private int characterCount;
|
||||
private CharacterInfoViewModel selectedCharacterInfo = new CharacterInfoViewModel();
|
||||
private readonly CharacterCollection characterCollection;
|
||||
@@ -121,18 +145,9 @@ namespace UnicodeCharacterInspector
|
||||
{
|
||||
foreach (int codePoint in value.AsCodePointEnumerable())
|
||||
{
|
||||
if (index >= characters.Length) Array.Resize(ref characters, characters.Length * 2);
|
||||
if (index >= codePoints.Length) Array.Resize(ref codePoints, codePoints.Length * 2);
|
||||
|
||||
// We don't replace pre-existing elements if they already have the correct value.
|
||||
// This should help a bit with GC, by making new string elements as short-lived as possible.
|
||||
string oldCodePointValue = characters[index];
|
||||
|
||||
if (oldCodePointValue == null || char.ConvertToUtf32(oldCodePointValue, 0) != codePoint)
|
||||
{
|
||||
characters[index] = char.ConvertFromUtf32(codePoint);
|
||||
}
|
||||
|
||||
++index;
|
||||
codePoints[index++] = codePoint;
|
||||
}
|
||||
}
|
||||
characterCount = index;
|
||||
@@ -145,21 +160,21 @@ namespace UnicodeCharacterInspector
|
||||
NotifyCollectionChanged(NotifyCollectionChangedAction.Reset);
|
||||
if (!keepSelectedIndex)
|
||||
{
|
||||
NotifyPropertyChanged("SelectedCharacterIndex");
|
||||
NotifyPropertyChanged("SelectedCharacter");
|
||||
NotifyPropertyChanged(nameof(SelectedCharacterIndex));
|
||||
NotifyPropertyChanged(nameof(SelectedCharacter));
|
||||
selectedCharacterInfo.Character = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<string> Characters { get { return characterCollection; } }
|
||||
public ICollection<CharacterViewModel> Characters { get { return characterCollection; } }
|
||||
|
||||
public int SelectedCharacterIndex
|
||||
{
|
||||
get { return selectedCharacterIndex; }
|
||||
set
|
||||
{
|
||||
if (selectedCharacterIndex < -1 || selectedCharacterIndex >= characters.Length)
|
||||
if (selectedCharacterIndex < -1 || selectedCharacterIndex >= codePoints.Length)
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
|
||||
if (value != selectedCharacterIndex)
|
||||
@@ -171,15 +186,18 @@ namespace UnicodeCharacterInspector
|
||||
string newSelectedCharacter = SelectedCharacter;
|
||||
|
||||
NotifyPropertyChanged();
|
||||
if (newSelectedCharacter != oldSelectedCharacter) NotifyPropertyChanged("SelectedCharacter");
|
||||
selectedCharacterInfo.Character = newSelectedCharacter;
|
||||
if (newSelectedCharacter != oldSelectedCharacter)
|
||||
{
|
||||
selectedCharacterInfo.Character = newSelectedCharacter;
|
||||
NotifyPropertyChanged(nameof(SelectedCharacter));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedCharacter
|
||||
{
|
||||
get { return selectedCharacterIndex >= 0 ? characters[selectedCharacterIndex] : null; }
|
||||
get { return selectedCharacterIndex >= 0 ? char.ConvertFromUtf32(codePoints[selectedCharacterIndex]) : null; }
|
||||
}
|
||||
|
||||
public CharacterInfoViewModel SelectedCharacterInfo
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
<Window.DataContext>
|
||||
<local:CharacterInspectorViewModel />
|
||||
</Window.DataContext>
|
||||
<Window.Resources>
|
||||
<local:StringToUtf32Converter x:Key="StringToUtf32Converter" />
|
||||
</Window.Resources>
|
||||
<Grid Margin="10">
|
||||
<Grid.Resources>
|
||||
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource ResourceKey={x:Type Label}}">
|
||||
@@ -40,15 +37,15 @@
|
||||
<ColumnDefinition Width="120" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Grid.ColumnSpan="2" MaxLines="1" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<TextBox Grid.ColumnSpan="2" ScrollViewer.VerticalScrollBarVisibility="Auto" MinLines="1" MaxLines="5" AcceptsReturn="True" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<ListBox Grid.Row="1" Grid.RowSpan="3" ItemsSource="{Binding Characters}" SelectedIndex="{Binding SelectedCharacterIndex}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock><Run Text="{Binding Mode=OneWay}" /> <Run Text="{Binding Mode=OneWay, Converter={StaticResource StringToUtf32Converter}, StringFormat=U+{0:X4}}" Foreground="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" FontStyle="Italic" /></TextBlock>
|
||||
<TextBlock><Run Text="{Binding DisplayText, Mode=OneWay}" /> <Run Text="{Binding CodePoint, Mode=OneWay, StringFormat=U+{0:X4}}" Foreground="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" FontStyle="Italic" /></TextBlock>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" TextOptions.TextFormattingMode="Ideal" TextOptions.TextRenderingMode="Grayscale" Text="{Binding SelectedCharacterInfo.Character}" FontSize="96" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" TextOptions.TextFormattingMode="Ideal" TextOptions.TextRenderingMode="Grayscale" Text="{Binding SelectedCharacterInfo.DisplayText}" FontSize="96" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
<ScrollViewer Grid.Row="2" Grid.Column="1" VerticalScrollBarVisibility="Auto">
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace UnicodeCharacterInspector
|
||||
{
|
||||
internal class StringToUtf32Converter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
string text = value as string;
|
||||
|
||||
if (!string.IsNullOrEmpty(text)) return char.ConvertToUtf32(text, 0);
|
||||
else return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,6 @@
|
||||
<Compile Include="CharacterInfoViewModel.cs" />
|
||||
<Compile Include="CharacterInspectorViewModel.cs" />
|
||||
<Compile Include="ZeroToVisibilityConverter.cs" />
|
||||
<Compile Include="StringToUtf32Converter.cs" />
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
|
||||
Reference in New Issue
Block a user