43
demo/Ursa.Demo/Pages/IPv4BoxDemo.axaml
Normal file
43
demo/Ursa.Demo/Pages/IPv4BoxDemo.axaml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<UserControl
|
||||||
|
x:Class="Ursa.Demo.Pages.IPv4BoxDemo"
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:pages="clr-namespace:Ursa.Demo.Pages"
|
||||||
|
xmlns:u="https://irihi.tech/ursa"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<Design.DataContext>
|
||||||
|
<pages:IPv4DemoViewMode />
|
||||||
|
</Design.DataContext>
|
||||||
|
<StackPanel HorizontalAlignment="Left">
|
||||||
|
<ToggleButton
|
||||||
|
Name="format"
|
||||||
|
Margin="0,0,0,50"
|
||||||
|
Content="Show Leading Zeroes" />
|
||||||
|
<TextBlock Classes="" Text="Normal" />
|
||||||
|
<u:IPv4Box
|
||||||
|
Name="box"
|
||||||
|
Width="200"
|
||||||
|
ShowLeadingZero="{Binding #format.IsChecked}" />
|
||||||
|
|
||||||
|
<TextBlock Text="IP: " />
|
||||||
|
<TextBlock Text="{Binding #box.IPAddress}" />
|
||||||
|
<TextBlock Classes="" Text="Fast input" />
|
||||||
|
<u:IPv4Box
|
||||||
|
Width="200"
|
||||||
|
InputMode="Fast"
|
||||||
|
ShowLeadingZero="{Binding #format.IsChecked}" />
|
||||||
|
|
||||||
|
<TextBlock Classes="" Text="Binding From Source" />
|
||||||
|
<RepeatButton Command="{Binding ChangeAddress}" Content="Random" />
|
||||||
|
<u:IPv4Box
|
||||||
|
Width="200"
|
||||||
|
IPAddress="{Binding Address}"
|
||||||
|
ShowLeadingZero="{Binding #format.IsChecked}" />
|
||||||
|
<TextBlock Classes="" Text="Disabled" />
|
||||||
|
<u:IPv4Box Width="200" IsEnabled="False" />
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
29
demo/Ursa.Demo/Pages/IPv4BoxDemo.axaml.cs
Normal file
29
demo/Ursa.Demo/Pages/IPv4BoxDemo.axaml.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace Ursa.Demo.Pages;
|
||||||
|
|
||||||
|
public partial class IPv4BoxDemo : UserControl
|
||||||
|
{
|
||||||
|
public IPv4BoxDemo()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = new IPv4DemoViewMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class IPv4DemoViewMode: ObservableObject
|
||||||
|
{
|
||||||
|
[ObservableProperty]
|
||||||
|
private IPAddress? _address;
|
||||||
|
|
||||||
|
public void ChangeAddress()
|
||||||
|
{
|
||||||
|
long l = Random.Shared.NextInt64(0x00000000FFFFFFFF);
|
||||||
|
Address = new IPAddress(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview5" />
|
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview5" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
|
||||||
<PackageReference Include="Semi.Avalonia" Version="0.1.0-preview5.1" />
|
<PackageReference Include="Semi.Avalonia" Version="0.1.0-preview5.2" />
|
||||||
<PackageReference Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
<PackageReference Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,9 @@
|
|||||||
<TabItem Header="Banner">
|
<TabItem Header="Banner">
|
||||||
<pages:BannerDemo />
|
<pages:BannerDemo />
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
<TabItem Header="IPv4Box">
|
||||||
|
<pages:IPv4BoxDemo />
|
||||||
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|||||||
127
src/Ursa.Themes.Semi/Controls/IPv4Box.axaml
Normal file
127
src/Ursa.Themes.Semi/Controls/IPv4Box.axaml
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
<ResourceDictionary
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:u="https://irihi.tech/ursa">
|
||||||
|
<!-- Add Resources Here -->
|
||||||
|
<Design.PreviewWith>
|
||||||
|
<StackPanel Margin="20">
|
||||||
|
<TextBlock Text="Hello World" />
|
||||||
|
</StackPanel>
|
||||||
|
</Design.PreviewWith>
|
||||||
|
<MenuFlyout x:Key="IPv4BoxMenuFlyout">
|
||||||
|
<MenuItem
|
||||||
|
x:Name="TextBoxContextFlyoutCutItem"
|
||||||
|
Command="{Binding $parent[u:IPv4Box].Cut}"
|
||||||
|
Header="Cut"
|
||||||
|
InputGesture="{x:Static u:IPv4Box.CutKeyGesture}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="TextBoxContextFlyoutCopyItem"
|
||||||
|
Command="{Binding $parent[u:IPv4Box].Copy}"
|
||||||
|
Header="Copy"
|
||||||
|
InputGesture="{x:Static u:IPv4Box.CopyKeyGesture}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="TextBoxContextFlyoutPasteItem"
|
||||||
|
Command="{Binding $parent[u:IPv4Box].Paste}"
|
||||||
|
Header="Paste"
|
||||||
|
InputGesture="{x:Static u:IPv4Box.PasteKeyGesture}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="TextBoxContextFlyoutClearItem"
|
||||||
|
Command="{Binding $parent[u:IPv4Box].Clear}"
|
||||||
|
Header="Clear" />
|
||||||
|
</MenuFlyout>
|
||||||
|
<ControlTheme x:Key="{x:Type u:IPv4Box}" TargetType="{x:Type u:IPv4Box}">
|
||||||
|
<Setter Property="u:IPv4Box.Focusable" Value="True" />
|
||||||
|
<Setter Property="u:IPv4Box.ShowLeadingZero" Value="True" />
|
||||||
|
<Setter Property="u:IPv4Box.TextAlignment" Value="Center" />
|
||||||
|
<Setter Property="u:IPv4Box.HorizontalAlignment" Value="Left" />
|
||||||
|
<Setter Property="u:IPv4Box.CornerRadius" Value="{DynamicResource IPv4BoxCornerRadius}" />
|
||||||
|
<Setter Property="u:IPv4Box.Background" Value="{DynamicResource IPv4BoxBackground}" />
|
||||||
|
<Setter Property="u:IPv4Box.MinHeight" Value="{DynamicResource IPv4BoxDefaultMinHeight}" />
|
||||||
|
<Setter Property="u:IPv4Box.BorderThickness" Value="{DynamicResource IPv4BoxBorderThickness}" />
|
||||||
|
<Setter Property="u:IPv4Box.SelectionBrush" Value="{DynamicResource IPv4BoxSelectionBrush}" />
|
||||||
|
<Setter Property="u:IPv4Box.SelectionForegroundBrush" Value="{DynamicResource IPv4BoxSelectionForeground}" />
|
||||||
|
<Setter Property="u:IPv4Box.CaretBrush" Value="{DynamicResource IPv4BoxCaretBrush}" />
|
||||||
|
<Setter Property="u:IPv4Box.ContextFlyout" Value="{DynamicResource IPv4BoxMenuFlyout}" />
|
||||||
|
<Setter Property="u:IPv4Box.Template">
|
||||||
|
<ControlTemplate TargetType="u:IPv4Box">
|
||||||
|
<Border
|
||||||
|
Name="PART_Border"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<Grid Width="{TemplateBinding Width}" ColumnDefinitions="1*, Auto, 1*, Auto, 1*, Auto, 1*">
|
||||||
|
<TextPresenter
|
||||||
|
Name="{x:Static u:IPv4Box.PART_FirstTextPresenter}"
|
||||||
|
Grid.Column="0"
|
||||||
|
MinWidth="8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
CaretBrush="{TemplateBinding CaretBrush}"
|
||||||
|
Cursor="IBeam"
|
||||||
|
SelectionBrush="{TemplateBinding SelectionBrush}"
|
||||||
|
SelectionForegroundBrush="{TemplateBinding SelectionForegroundBrush}"
|
||||||
|
TextAlignment="{TemplateBinding TextAlignment}" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,4"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Focusable="False"
|
||||||
|
Text="." />
|
||||||
|
<TextPresenter
|
||||||
|
Name="{x:Static u:IPv4Box.PART_SecondTextPresenter}"
|
||||||
|
Grid.Column="2"
|
||||||
|
MinWidth="8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
CaretBrush="{TemplateBinding CaretBrush}"
|
||||||
|
Cursor="IBeam"
|
||||||
|
SelectionBrush="{TemplateBinding SelectionBrush}"
|
||||||
|
SelectionForegroundBrush="{TemplateBinding SelectionForegroundBrush}"
|
||||||
|
TextAlignment="{TemplateBinding TextAlignment}" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="3"
|
||||||
|
Margin="0,4"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="." />
|
||||||
|
<TextPresenter
|
||||||
|
Name="{x:Static u:IPv4Box.PART_ThirdTextPresenter}"
|
||||||
|
Grid.Column="4"
|
||||||
|
MinWidth="8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
CaretBrush="{TemplateBinding CaretBrush}"
|
||||||
|
Cursor="IBeam"
|
||||||
|
SelectionBrush="{TemplateBinding SelectionBrush}"
|
||||||
|
SelectionForegroundBrush="{TemplateBinding SelectionForegroundBrush}"
|
||||||
|
TextAlignment="{TemplateBinding TextAlignment}" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="5"
|
||||||
|
Margin="0,4"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="." />
|
||||||
|
<TextPresenter
|
||||||
|
Name="{x:Static u:IPv4Box.PART_FourthTextPresenter}"
|
||||||
|
Grid.Column="6"
|
||||||
|
MinWidth="8"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
CaretBrush="{TemplateBinding CaretBrush}"
|
||||||
|
Cursor="IBeam"
|
||||||
|
SelectionBrush="{TemplateBinding SelectionBrush}"
|
||||||
|
SelectionForegroundBrush="{TemplateBinding SelectionForegroundBrush}"
|
||||||
|
TextAlignment="{TemplateBinding TextAlignment}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
<Style Selector="^:pointerover /template/ Border#PART_Border">
|
||||||
|
<Setter Property="Border.Background" Value="{DynamicResource IPv4BoxPointeroverBackground}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pressed /template/ Border#PART_Border">
|
||||||
|
<Setter Property="Border.Background" Value="{DynamicResource IPv4BoxPressedBackground}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:focus-within">
|
||||||
|
<Setter Property="Border.BorderBrush" Value="{DynamicResource IPv4BoxFocusBorderBrush}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:disabled">
|
||||||
|
<Setter Property="Border.Background" Value="{DynamicResource IPv4BoxDisabledBackground}" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -3,5 +3,6 @@
|
|||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceInclude Source="Badge.axaml" />
|
<ResourceInclude Source="Badge.axaml" />
|
||||||
<ResourceInclude Source="Banner.axaml" />
|
<ResourceInclude Source="Banner.axaml" />
|
||||||
|
<ResourceInclude Source="IPv4Box.axaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
12
src/Ursa.Themes.Semi/Themes/Dark/IPv4Box.axaml
Normal file
12
src/Ursa.Themes.Semi/Themes/Dark/IPv4Box.axaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<!-- Add Resources Here -->
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxBackground" Opacity="0.12" Color="White" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxPointeroverBackground" Opacity="0.16" Color="White" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxPressedBackground" Opacity="0.2" Color="White" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxBorderBrush" Color="Transparent" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxDisabledBackground" Opacity="0.04" Color="#E6E8EA" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxFocusBorderBrush" Color="#FF54A9FF" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxSelectionBrush" Color="#FF54A9FF" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxSelectionForeground" Color="White" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxCaretBrush" Color="White" />
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -3,5 +3,6 @@
|
|||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceInclude Source="Badge.axaml" />
|
<ResourceInclude Source="Badge.axaml" />
|
||||||
<ResourceInclude Source="Banner.axaml" />
|
<ResourceInclude Source="Banner.axaml" />
|
||||||
|
<ResourceInclude Source="IPv4Box.axaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
12
src/Ursa.Themes.Semi/Themes/Light/IPv4Box.axaml
Normal file
12
src/Ursa.Themes.Semi/Themes/Light/IPv4Box.axaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<!-- Add Resources Here -->
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxBackground" Opacity="0.05" Color="#FF2E3238" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxPointeroverBackground" Opacity="0.09" Color="#FF2E3238" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxPressedBackground" Opacity="0.13" Color="#FF2E3238" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxBorderBrush" Color="Transparent" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxDisabledBackground" Opacity="0.02" Color="#2E3238" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxFocusBorderBrush" Color="#FF0077FA" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxSelectionBrush" Color="#FF0077FA" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxSelectionForeground" Color="White" />
|
||||||
|
<SolidColorBrush x:Key="IPv4BoxCaretBrush" Color="Black" />
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -3,5 +3,6 @@
|
|||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceInclude Source="Badge.axaml" />
|
<ResourceInclude Source="Badge.axaml" />
|
||||||
<ResourceInclude Source="Banner.axaml" />
|
<ResourceInclude Source="Banner.axaml" />
|
||||||
|
<ResourceInclude Source="IPv4Box.axaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
8
src/Ursa.Themes.Semi/Themes/Shared/IPv4Box.axaml
Normal file
8
src/Ursa.Themes.Semi/Themes/Shared/IPv4Box.axaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<!-- Add Resources Here -->
|
||||||
|
<x:Double x:Key="IPv4BoxDefaultMinHeight">32</x:Double>
|
||||||
|
<x:Double x:Key="IPv4BoxSmallMinHeight">24</x:Double>
|
||||||
|
<x:Double x:Key="IPv4BoxLargeMinHeight">40</x:Double>
|
||||||
|
<Thickness x:Key="IPv4BoxBorderThickness">1</Thickness>
|
||||||
|
<CornerRadius x:Key="IPv4BoxCornerRadius">3</CornerRadius>
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -3,5 +3,6 @@
|
|||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceInclude Source="Badge.axaml" />
|
<ResourceInclude Source="Badge.axaml" />
|
||||||
<ResourceInclude Source="Banner.axaml" />
|
<ResourceInclude Source="Banner.axaml" />
|
||||||
|
<ResourceInclude Source="IPv4Box.axaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
580
src/Ursa/Controls/IPv4Box.cs
Normal file
580
src/Ursa/Controls/IPv4Box.cs
Normal file
@@ -0,0 +1,580 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Net;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Metadata;
|
||||||
|
using Avalonia.Controls.Presenters;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Input.Platform;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Media.TextFormatting;
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
public enum IPv4BoxInputMode
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
// In fast mode, automatically move to next session after 3 digits input.
|
||||||
|
Fast,
|
||||||
|
}
|
||||||
|
|
||||||
|
[TemplatePart(PART_FirstTextPresenter, typeof(TextPresenter))]
|
||||||
|
[TemplatePart(PART_SecondTextPresenter, typeof(TextPresenter))]
|
||||||
|
[TemplatePart(PART_ThirdTextPresenter, typeof(TextPresenter))]
|
||||||
|
[TemplatePart(PART_FourthTextPresenter, typeof(TextPresenter))]
|
||||||
|
public class IPv4Box: TemplatedControl
|
||||||
|
{
|
||||||
|
public const string PART_FirstTextPresenter = "PART_FirstTextPresenter";
|
||||||
|
public const string PART_SecondTextPresenter = "PART_SecondTextPresenter";
|
||||||
|
public const string PART_ThirdTextPresenter = "PART_ThirdTextPresenter";
|
||||||
|
public const string PART_FourthTextPresenter = "PART_FourthTextPresenter";
|
||||||
|
private TextPresenter? _firstText;
|
||||||
|
private TextPresenter? _secondText;
|
||||||
|
private TextPresenter? _thirdText;
|
||||||
|
private TextPresenter? _fourthText;
|
||||||
|
private byte? _firstByte;
|
||||||
|
private byte? _secondByte;
|
||||||
|
private byte? _thirdByte;
|
||||||
|
private byte? _fourthByte;
|
||||||
|
private readonly TextPresenter?[] _presenters = new TextPresenter?[4];
|
||||||
|
private TextPresenter? _currentActivePresenter;
|
||||||
|
|
||||||
|
public static readonly StyledProperty<IPAddress?> IPAddressProperty = AvaloniaProperty.Register<IPv4Box, IPAddress?>(
|
||||||
|
nameof(IPAddress));
|
||||||
|
public IPAddress? IPAddress
|
||||||
|
{
|
||||||
|
get => GetValue(IPAddressProperty);
|
||||||
|
set => SetValue(IPAddressProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<TextAlignment> TextAlignmentProperty =
|
||||||
|
TextBox.TextAlignmentProperty.AddOwner<IPv4Box>();
|
||||||
|
public TextAlignment TextAlignment
|
||||||
|
{
|
||||||
|
get => GetValue(TextAlignmentProperty);
|
||||||
|
set => SetValue(TextAlignmentProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<IBrush?> SelectionBrushProperty =
|
||||||
|
TextBox.SelectionBrushProperty.AddOwner<IPv4Box>();
|
||||||
|
public IBrush? SelectionBrush
|
||||||
|
{
|
||||||
|
get => GetValue(SelectionBrushProperty);
|
||||||
|
set => SetValue(SelectionBrushProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<IBrush?> SelectionForegroundBrushProperty =
|
||||||
|
TextBox.SelectionForegroundBrushProperty.AddOwner<IPv4Box>();
|
||||||
|
public IBrush? SelectionForegroundBrush
|
||||||
|
{
|
||||||
|
get => GetValue(SelectionForegroundBrushProperty);
|
||||||
|
set => SetValue(SelectionForegroundBrushProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<IBrush?> CaretBrushProperty = TextBox.CaretBrushProperty.AddOwner<IPv4Box>();
|
||||||
|
public IBrush? CaretBrush
|
||||||
|
{
|
||||||
|
get => GetValue(CaretBrushProperty);
|
||||||
|
set => SetValue(CaretBrushProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> ShowLeadingZeroProperty = AvaloniaProperty.Register<IPv4Box, bool>(
|
||||||
|
nameof(ShowLeadingZero));
|
||||||
|
public bool ShowLeadingZero
|
||||||
|
{
|
||||||
|
get => GetValue(ShowLeadingZeroProperty);
|
||||||
|
set => SetValue(ShowLeadingZeroProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<IPv4BoxInputMode> InputModeProperty = AvaloniaProperty.Register<IPv4Box, IPv4BoxInputMode>(
|
||||||
|
nameof(InputMode));
|
||||||
|
public IPv4BoxInputMode InputMode
|
||||||
|
{
|
||||||
|
get => GetValue(InputModeProperty);
|
||||||
|
set => SetValue(InputModeProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static IPv4Box()
|
||||||
|
{
|
||||||
|
ShowLeadingZeroProperty.Changed.AddClassHandler<IPv4Box>((o, e) => o.OnFormatChange(e));
|
||||||
|
IPAddressProperty.Changed.AddClassHandler<IPv4Box>((o, e) => o.OnIPChanged(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Overrides
|
||||||
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnApplyTemplate(e);
|
||||||
|
_firstText = e.NameScope.Find<TextPresenter>(PART_FirstTextPresenter);
|
||||||
|
_secondText = e.NameScope.Find<TextPresenter>(PART_SecondTextPresenter);
|
||||||
|
_thirdText = e.NameScope.Find<TextPresenter>(PART_ThirdTextPresenter);
|
||||||
|
_fourthText = e.NameScope.Find<TextPresenter>(PART_FourthTextPresenter);
|
||||||
|
_presenters[0] = _firstText;
|
||||||
|
_presenters[1] = _secondText;
|
||||||
|
_presenters[2] = _thirdText;
|
||||||
|
_presenters[3] = _fourthText;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnKeyDown(KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (_currentActivePresenter is null) return;
|
||||||
|
var keymap = AvaloniaLocator.Current.GetRequiredService<PlatformHotkeyConfiguration>();
|
||||||
|
bool Match(List<KeyGesture> gestures) => gestures.Any(g => g.Matches(e));
|
||||||
|
if (e.Key == Key.Enter)
|
||||||
|
{
|
||||||
|
ParseBytes(ShowLeadingZero);
|
||||||
|
SetIPAddressInternal();
|
||||||
|
base.OnKeyDown(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Match(keymap.SelectAll))
|
||||||
|
{
|
||||||
|
_currentActivePresenter.SelectionStart = 0;
|
||||||
|
_currentActivePresenter.SelectionEnd = _currentActivePresenter.Text?.Length ?? 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (Match(keymap.Copy))
|
||||||
|
{
|
||||||
|
Copy();
|
||||||
|
}
|
||||||
|
else if (Match(keymap.Paste))
|
||||||
|
{
|
||||||
|
Paste();
|
||||||
|
}
|
||||||
|
if (e.Key == Key.Tab)
|
||||||
|
{
|
||||||
|
_currentActivePresenter?.HideCaret();
|
||||||
|
_currentActivePresenter.ClearSelection();
|
||||||
|
if (Equals(_currentActivePresenter, _fourthText))
|
||||||
|
{
|
||||||
|
base.OnKeyDown(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MoveToNextPresenter(_currentActivePresenter, true);
|
||||||
|
_currentActivePresenter?.ShowCaret();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.OemPeriod || e.Key == Key.Decimal)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_currentActivePresenter.Text))
|
||||||
|
{
|
||||||
|
base.OnKeyDown(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_currentActivePresenter?.HideCaret();
|
||||||
|
_currentActivePresenter.ClearSelection();
|
||||||
|
if (Equals(_currentActivePresenter, _fourthText))
|
||||||
|
{
|
||||||
|
base.OnKeyDown(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MoveToNextPresenter(_currentActivePresenter, false);
|
||||||
|
_currentActivePresenter?.ShowCaret();
|
||||||
|
_currentActivePresenter.MoveCaretToStart();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.Back)
|
||||||
|
{
|
||||||
|
DeleteImplementation(_currentActivePresenter);
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.Right )
|
||||||
|
{
|
||||||
|
OnPressRightKey();
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.Left)
|
||||||
|
{
|
||||||
|
OnPressLeftKey();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
base.OnKeyDown(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnTextInput(TextInputEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Handled) return;
|
||||||
|
string? s = e.Text;
|
||||||
|
if (string.IsNullOrEmpty(s)) return;
|
||||||
|
if (!char.IsNumber(s![0])) return;
|
||||||
|
if (_currentActivePresenter != null)
|
||||||
|
{
|
||||||
|
int index = _currentActivePresenter.CaretIndex;
|
||||||
|
string? oldText = _currentActivePresenter.Text;
|
||||||
|
if (oldText is null)
|
||||||
|
{
|
||||||
|
_currentActivePresenter.Text = s;
|
||||||
|
_currentActivePresenter.MoveCaretHorizontal();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_currentActivePresenter.DeleteSelection();
|
||||||
|
_currentActivePresenter.ClearSelection();
|
||||||
|
oldText = _currentActivePresenter.Text;
|
||||||
|
|
||||||
|
string newText = string.IsNullOrEmpty(oldText)
|
||||||
|
? s
|
||||||
|
: oldText?.Substring(0, index) + s + oldText?.Substring(Math.Min(index, oldText.Length));
|
||||||
|
if (newText.Length > 3)
|
||||||
|
{
|
||||||
|
newText = newText.Substring(0, 3);
|
||||||
|
}
|
||||||
|
_currentActivePresenter.Text = newText;
|
||||||
|
_currentActivePresenter.MoveCaretHorizontal();
|
||||||
|
if (_currentActivePresenter.CaretIndex == 3 && InputMode == IPv4BoxInputMode.Fast)
|
||||||
|
{
|
||||||
|
_currentActivePresenter.HideCaret();
|
||||||
|
bool success = MoveToNextPresenter(_currentActivePresenter, true);
|
||||||
|
_currentActivePresenter.ShowCaret();
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
_currentActivePresenter.SelectAll();
|
||||||
|
_currentActivePresenter.MoveCaretToStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
Point position = e.GetPosition(_firstText);
|
||||||
|
foreach (var presenter in _presenters)
|
||||||
|
{
|
||||||
|
if (presenter?.Bounds.Contains(position)??false)
|
||||||
|
{
|
||||||
|
if (e.ClickCount == 1)
|
||||||
|
{
|
||||||
|
presenter.ShowCaret();
|
||||||
|
_currentActivePresenter = presenter;
|
||||||
|
var caretPosition = position.WithX(position.X - presenter.Bounds.X);
|
||||||
|
presenter.MoveCaretToPoint(caretPosition);
|
||||||
|
}
|
||||||
|
else if (e.ClickCount == 2)
|
||||||
|
{
|
||||||
|
presenter.SelectAll();
|
||||||
|
presenter.MoveCaretToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
presenter?.HideCaret();
|
||||||
|
presenter.ClearSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug.WriteLine(_currentActivePresenter?.Name);
|
||||||
|
base.OnPointerPressed(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLostFocus(RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
foreach (var pre in _presenters)
|
||||||
|
{
|
||||||
|
pre?.HideCaret();
|
||||||
|
pre.ClearSelection();
|
||||||
|
}
|
||||||
|
_currentActivePresenter = null;
|
||||||
|
ParseBytes(ShowLeadingZero);
|
||||||
|
SetIPAddressInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnGotFocus(GotFocusEventArgs e)
|
||||||
|
{
|
||||||
|
_currentActivePresenter = _firstText;
|
||||||
|
if (_currentActivePresenter is null)
|
||||||
|
{
|
||||||
|
base.OnGotFocus(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_currentActivePresenter.ShowCaret();
|
||||||
|
_currentActivePresenter.MoveCaretToStart();
|
||||||
|
base.OnGotFocus(e);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private void OnFormatChange(AvaloniaPropertyChangedEventArgs arg)
|
||||||
|
{
|
||||||
|
bool showLeadingZero = arg.GetNewValue<bool>();
|
||||||
|
ParseBytes(showLeadingZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnIPChanged(AvaloniaPropertyChangedEventArgs arg)
|
||||||
|
{
|
||||||
|
IPAddress? address = arg.GetNewValue<IPAddress?>();
|
||||||
|
if (address is null)
|
||||||
|
{
|
||||||
|
foreach (var presenter in _presenters)
|
||||||
|
{
|
||||||
|
if(presenter!=null) presenter.Text = string.Empty;
|
||||||
|
}
|
||||||
|
ParseBytes(ShowLeadingZero);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var sections = address.ToString().Split('.');
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
var presenter = _presenters[i];
|
||||||
|
if (presenter != null)
|
||||||
|
{
|
||||||
|
presenter.Text = sections[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseBytes(ShowLeadingZero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseBytes(bool showLeadingZero)
|
||||||
|
{
|
||||||
|
string format = showLeadingZero ? "D3" : "";
|
||||||
|
if (string.IsNullOrEmpty(_firstText?.Text) && string.IsNullOrEmpty(_secondText?.Text) && string.IsNullOrEmpty(_thirdText?.Text) && string.IsNullOrEmpty(_fourthText?.Text))
|
||||||
|
{
|
||||||
|
_firstByte = null;
|
||||||
|
_secondByte = null;
|
||||||
|
_thirdByte = null;
|
||||||
|
_fourthByte = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_firstByte = byte.TryParse(_firstText?.Text, out byte b1) ? b1 : (byte)0;
|
||||||
|
_secondByte = byte.TryParse(_secondText?.Text, out byte b2) ? b2 : (byte)0;
|
||||||
|
_thirdByte = byte.TryParse(_thirdText?.Text, out byte b3) ? b3 : (byte)0;
|
||||||
|
_fourthByte = byte.TryParse(_fourthText?.Text, out byte b4) ? b4 : (byte)0;
|
||||||
|
if (_firstText != null) _firstText.Text = _firstByte?.ToString(format);
|
||||||
|
if (_secondText != null) _secondText.Text = _secondByte?.ToString(format);
|
||||||
|
if (_thirdText != null) _thirdText.Text = _thirdByte?.ToString(format);
|
||||||
|
if (_fourthText != null) _fourthText.Text = _fourthByte?.ToString(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private bool MoveToNextPresenter(TextPresenter? presenter, bool selectAllAfterMove)
|
||||||
|
{
|
||||||
|
if (presenter is null) return false;
|
||||||
|
if (Equals(presenter, _fourthText)) return false;
|
||||||
|
presenter.ClearSelection();
|
||||||
|
if (Equals(presenter, _firstText)) _currentActivePresenter = _secondText;
|
||||||
|
else if (Equals(presenter, _secondText)) _currentActivePresenter = _thirdText;
|
||||||
|
else if (Equals(presenter, _thirdText)) _currentActivePresenter = _fourthText;
|
||||||
|
if(selectAllAfterMove) _currentActivePresenter.SelectAll();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MoveToPreviousTextPresenter(TextPresenter? presenter)
|
||||||
|
{
|
||||||
|
if (presenter is null) return false;
|
||||||
|
if (Equals(presenter, _firstText)) return false;
|
||||||
|
presenter.ClearSelection();
|
||||||
|
if (Equals(presenter, _fourthText)) _currentActivePresenter = _thirdText;
|
||||||
|
else if (Equals(presenter, _thirdText)) _currentActivePresenter = _secondText;
|
||||||
|
else if (Equals(presenter, _secondText)) _currentActivePresenter = _firstText;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
foreach (var presenter in _presenters)
|
||||||
|
{
|
||||||
|
if (presenter != null) presenter.Text = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_firstByte = null;
|
||||||
|
_secondByte = null;
|
||||||
|
_thirdByte = null;
|
||||||
|
_fourthByte = null;
|
||||||
|
IPAddress = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetIPAddressInternal()
|
||||||
|
{
|
||||||
|
if (_firstByte is null && _secondByte is null && _thirdByte is null && _fourthByte is null)
|
||||||
|
{
|
||||||
|
IPAddress = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long address = 0;
|
||||||
|
address += _firstByte??0;
|
||||||
|
address += (_secondByte??0) << 8;
|
||||||
|
address += (_thirdByte??0) << 16;
|
||||||
|
address += ((long?)_fourthByte ?? 0) << 24;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IPAddress = new IPAddress(address);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
IPAddress = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteImplementation(TextPresenter? presenter)
|
||||||
|
{
|
||||||
|
if(presenter is null) return;
|
||||||
|
var oldText = presenter.Text;
|
||||||
|
if (presenter.SelectionStart != presenter.SelectionEnd)
|
||||||
|
{
|
||||||
|
presenter.DeleteSelection();
|
||||||
|
presenter.ClearSelection();
|
||||||
|
}
|
||||||
|
else if (string.IsNullOrWhiteSpace(oldText) || presenter.CaretIndex == 0)
|
||||||
|
{
|
||||||
|
presenter.HideCaret();
|
||||||
|
MoveToPreviousTextPresenter(presenter);
|
||||||
|
if (_currentActivePresenter != null)
|
||||||
|
{
|
||||||
|
_currentActivePresenter.ShowCaret();
|
||||||
|
_currentActivePresenter.MoveCaretToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int index = presenter.CaretIndex;
|
||||||
|
string newText = oldText?.Substring(0, index - 1) + oldText?.Substring(Math.Min(index, oldText.Length));
|
||||||
|
presenter.MoveCaretHorizontal(LogicalDirection.Backward);
|
||||||
|
presenter.Text = newText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPressRightKey()
|
||||||
|
{
|
||||||
|
if (_currentActivePresenter is null) return;
|
||||||
|
if (_currentActivePresenter.IsTextSelected())
|
||||||
|
{
|
||||||
|
int end = _currentActivePresenter.SelectionEnd;
|
||||||
|
_currentActivePresenter.ClearSelection();
|
||||||
|
_currentActivePresenter.MoveCaretToTextPosition(end);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_currentActivePresenter.CaretIndex >= _currentActivePresenter.Text?.Length)
|
||||||
|
{
|
||||||
|
_currentActivePresenter.HideCaret();
|
||||||
|
bool success = MoveToNextPresenter(_currentActivePresenter, false);
|
||||||
|
_currentActivePresenter.ClearSelection();
|
||||||
|
_currentActivePresenter.ShowCaret();
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
_currentActivePresenter.MoveCaretToStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_currentActivePresenter.ClearSelection();
|
||||||
|
_currentActivePresenter.CaretIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPressLeftKey()
|
||||||
|
{
|
||||||
|
if (_currentActivePresenter is null) return;
|
||||||
|
if (_currentActivePresenter.IsTextSelected())
|
||||||
|
{
|
||||||
|
int start = _currentActivePresenter.SelectionStart;
|
||||||
|
_currentActivePresenter.ClearSelection();
|
||||||
|
_currentActivePresenter.MoveCaretToTextPosition(start);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_currentActivePresenter.CaretIndex == 0)
|
||||||
|
{
|
||||||
|
_currentActivePresenter.HideCaret();
|
||||||
|
bool success = MoveToPreviousTextPresenter(_currentActivePresenter);
|
||||||
|
_currentActivePresenter.ClearSelection();
|
||||||
|
_currentActivePresenter.ShowCaret();
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
_currentActivePresenter.MoveCaretToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_currentActivePresenter.ClearSelection();
|
||||||
|
_currentActivePresenter.CaretIndex--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Copy()
|
||||||
|
{
|
||||||
|
string s = string.Join(".", _firstText?.Text, _secondText?.Text, _thirdText?.Text, _fourthText?.Text);
|
||||||
|
IClipboard? clipboard = AvaloniaLocator.Current.GetService<IClipboard>();
|
||||||
|
clipboard?.SetTextAsync(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static KeyGesture? CopyKeyGesture { get; } = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>()?.Copy.FirstOrDefault();
|
||||||
|
public static KeyGesture? PasteKeyGesture { get; } = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>()?.Paste.FirstOrDefault();
|
||||||
|
public static KeyGesture? CutKeyGesture { get; } = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>()?.Cut.FirstOrDefault();
|
||||||
|
|
||||||
|
public async void Paste()
|
||||||
|
{
|
||||||
|
IClipboard? clipboard = AvaloniaLocator.Current.GetService<IClipboard>();
|
||||||
|
if (clipboard is null) return;
|
||||||
|
string s = await clipboard.GetTextAsync();
|
||||||
|
if (IPAddress.TryParse(s, out var address))
|
||||||
|
{
|
||||||
|
IPAddress = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Cut()
|
||||||
|
{
|
||||||
|
IClipboard? clipboard = AvaloniaLocator.Current.GetService<IClipboard>();
|
||||||
|
if(clipboard is null) return;
|
||||||
|
string s = string.Join(".", _firstText?.Text, _secondText?.Text, _thirdText?.Text, _fourthText?.Text);
|
||||||
|
await clipboard.SetTextAsync(s);
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TextPresenterHelper
|
||||||
|
{
|
||||||
|
public static void MoveCaretToStart(this TextPresenter? presenter)
|
||||||
|
{
|
||||||
|
if (presenter is null) return;
|
||||||
|
presenter.MoveCaretToTextPosition(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MoveCaretToEnd(this TextPresenter? presenter)
|
||||||
|
{
|
||||||
|
if(presenter is null) return;
|
||||||
|
presenter.MoveCaretToTextPosition(presenter.Text?.Length ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ClearSelection(this TextPresenter? presenter)
|
||||||
|
{
|
||||||
|
if (presenter is null) return;
|
||||||
|
presenter.SelectionStart = 0;
|
||||||
|
presenter.SelectionEnd = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SelectAll(this TextPresenter? presenter)
|
||||||
|
{
|
||||||
|
if(presenter is null) return;
|
||||||
|
if(presenter.Text is null) return;
|
||||||
|
presenter.SelectionStart = 0;
|
||||||
|
presenter.SelectionEnd = presenter.Text.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DeleteSelection(this TextPresenter? presenter)
|
||||||
|
{
|
||||||
|
if (presenter is null) return;
|
||||||
|
int selectionStart = presenter.SelectionStart;
|
||||||
|
int selectionEnd = presenter.SelectionEnd;
|
||||||
|
if (selectionStart != selectionEnd)
|
||||||
|
{
|
||||||
|
var start = Math.Min(selectionStart, selectionEnd);
|
||||||
|
var end = Math.Max(selectionStart, selectionEnd);
|
||||||
|
var text = presenter.Text;
|
||||||
|
|
||||||
|
string newText = text is null
|
||||||
|
? string.Empty
|
||||||
|
: text.Substring(0, start) + text.Substring(Math.Min(end, text.Length));
|
||||||
|
presenter.Text = newText;
|
||||||
|
presenter.MoveCaretToTextPosition(start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsTextSelected(this TextPresenter? presenter)
|
||||||
|
{
|
||||||
|
if (presenter is null) return false;
|
||||||
|
return presenter.SelectionStart != presenter.SelectionEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,4 +12,8 @@
|
|||||||
<PackageReference Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
<PackageReference Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="TypeConverters" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user