feat: move back to textbox, add caret handling on click and focus.
This commit is contained in:
@@ -8,8 +8,9 @@
|
|||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<StackPanel>
|
<StackPanel HorizontalAlignment="Left">
|
||||||
<TextBlock />
|
<TextBlock />
|
||||||
<u:IPv4Box Width="500" />
|
<u:IPv4Box />
|
||||||
|
<u:IPv4Box ShowPort="True" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -3,55 +3,88 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:u="https://irihi.tech/ursa">
|
xmlns:u="https://irihi.tech/ursa">
|
||||||
<!-- Add Resources Here -->
|
<!-- Add Resources Here -->
|
||||||
|
<Design.PreviewWith>
|
||||||
|
<StackPanel Margin="20">
|
||||||
|
<TextBlock Text="Hello World" />
|
||||||
|
</StackPanel>
|
||||||
|
</Design.PreviewWith>
|
||||||
<ControlTheme x:Key="{x:Type u:IPv4Box}" TargetType="{x:Type u:IPv4Box}">
|
<ControlTheme x:Key="{x:Type u:IPv4Box}" TargetType="{x:Type u:IPv4Box}">
|
||||||
|
<Setter Property="u:IPv4Box.Focusable" Value="True" />
|
||||||
|
<Setter Property="u:IPv4Box.HorizontalAlignment" Value="Left" />
|
||||||
<Setter Property="u:IPv4Box.Template">
|
<Setter Property="u:IPv4Box.Template">
|
||||||
<ControlTemplate TargetType="u:IPv4Box">
|
<ControlTemplate TargetType="u:IPv4Box">
|
||||||
<Border
|
<Border
|
||||||
|
Name="PART_Border"
|
||||||
|
MinHeight="30"
|
||||||
|
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||||
|
Background="Transparent"
|
||||||
BorderBrush="Black"
|
BorderBrush="Black"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CornerRadius="3">
|
CornerRadius="3">
|
||||||
<Grid Width="{TemplateBinding Width}" ColumnDefinitions="*, Auto, *, Auto, *, Auto, *, Auto, * ">
|
<Grid Width="{TemplateBinding Width}" ColumnDefinitions="1*, Auto, 1*, Auto, 1*, Auto, 1*, Auto, * ">
|
||||||
<TextBox
|
<TextPresenter
|
||||||
Name="{x:Static u:IPv4Box.PART_FirstTextBox}"
|
Name="{x:Static u:IPv4Box.PART_FirstTextPresenter}"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
BorderThickness="0" />
|
MinWidth="48"
|
||||||
<TextPresenter
|
VerticalAlignment="Center"
|
||||||
|
Cursor="IBeam"
|
||||||
|
Text="123"
|
||||||
|
TextAlignment="Center" />
|
||||||
|
<TextBlock
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
|
Margin="0,4"
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
|
Focusable="False"
|
||||||
Text="." />
|
Text="." />
|
||||||
<TextBox
|
<TextPresenter
|
||||||
Name="{x:Static u:IPv4Box.PART_SecondTextBox}"
|
Name="{x:Static u:IPv4Box.PART_SecondTextPresenter}"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
BorderThickness="0" />
|
MinWidth="48"
|
||||||
<TextPresenter
|
VerticalAlignment="Center"
|
||||||
|
Cursor="IBeam"
|
||||||
|
Text="123" />
|
||||||
|
<TextBlock
|
||||||
Grid.Column="3"
|
Grid.Column="3"
|
||||||
|
Margin="0,4"
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
Text="." />
|
Text="." />
|
||||||
<TextBox
|
|
||||||
Name="{x:Static u:IPv4Box.PART_ThirdTextBox}"
|
|
||||||
Grid.Column="4"
|
|
||||||
BorderThickness="0" />
|
|
||||||
<TextPresenter
|
<TextPresenter
|
||||||
|
Name="{x:Static u:IPv4Box.PART_ThirdTextPresenter}"
|
||||||
|
Grid.Column="4"
|
||||||
|
MinWidth="48"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Cursor="IBeam" />
|
||||||
|
<TextBlock
|
||||||
Grid.Column="5"
|
Grid.Column="5"
|
||||||
|
Margin="0,4"
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
Text="." />
|
Text="." />
|
||||||
<TextBox
|
<TextPresenter
|
||||||
Name="{x:Static u:IPv4Box.PART_FourthTextBox}"
|
Name="{x:Static u:IPv4Box.PART_FourthTextPresenter}"
|
||||||
Grid.Column="6"
|
Grid.Column="6"
|
||||||
BorderThickness="0" />
|
MinWidth="48"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Cursor="IBeam" />
|
||||||
<Rectangle
|
<Rectangle
|
||||||
Grid.Column="7"
|
Grid.Column="7"
|
||||||
Width="1"
|
Width="1"
|
||||||
Margin="0,2"
|
Margin="0,4"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
Fill="Black" />
|
Fill="Black"
|
||||||
<TextBox
|
IsVisible="{TemplateBinding ShowPort}" />
|
||||||
Name="{x:Static u:IPv4Box.PART_PortTextBox}"
|
<TextPresenter
|
||||||
|
Name="{x:Static u:IPv4Box.PART_PortTextPresenter}"
|
||||||
Grid.Column="8"
|
Grid.Column="8"
|
||||||
BorderThickness="0" />
|
MinWidth="30"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Cursor="IBeam"
|
||||||
|
IsVisible="{TemplateBinding ShowPort}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
|
<Style Selector="^:focus-within /template/ Border#PART_Border">
|
||||||
|
<Setter Property="BorderBrush" Value="Red" />
|
||||||
|
</Style>
|
||||||
</ControlTheme>
|
</ControlTheme>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -10,23 +10,30 @@ using Avalonia.Interactivity;
|
|||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
[TemplatePart(PART_FirstTextBox, typeof(TextBox))]
|
[TemplatePart(PART_FirstTextPresenter, typeof(TextPresenter))]
|
||||||
[TemplatePart(PART_SecondTextBox, typeof(TextBox))]
|
[TemplatePart(PART_SecondTextPresenter, typeof(TextPresenter))]
|
||||||
[TemplatePart(PART_ThirdTextBox, typeof(TextBox))]
|
[TemplatePart(PART_ThirdTextPresenter, typeof(TextPresenter))]
|
||||||
[TemplatePart(PART_FourthTextBox, typeof(TextBox))]
|
[TemplatePart(PART_FourthTextPresenter, typeof(TextPresenter))]
|
||||||
[TemplatePart(PART_PortTextBox, typeof(TextBox))]
|
[TemplatePart(PART_PortTextPresenter, typeof(TextPresenter))]
|
||||||
public class IPv4Box: TemplatedControl
|
public class IPv4Box: TemplatedControl
|
||||||
{
|
{
|
||||||
public const string PART_FirstTextBox = "PART_FirstTextPresenter";
|
public const string PART_FirstTextPresenter = "PART_FirstTextPresenter";
|
||||||
public const string PART_SecondTextBox = "PART_SecondTextPresenter";
|
public const string PART_SecondTextPresenter = "PART_SecondTextPresenter";
|
||||||
public const string PART_ThirdTextBox = "PART_ThirdTextPresenter";
|
public const string PART_ThirdTextPresenter = "PART_ThirdTextPresenter";
|
||||||
public const string PART_FourthTextBox = "PART_FourthTextPresenter";
|
public const string PART_FourthTextPresenter = "PART_FourthTextPresenter";
|
||||||
public const string PART_PortTextBox = "PART_PortNumericInput";
|
public const string PART_PortTextPresenter = "PART_PortTextPresenter";
|
||||||
private TextBox? _firstText;
|
private TextPresenter? _firstText;
|
||||||
private TextBox? _secondText;
|
private TextPresenter? _secondText;
|
||||||
private TextBox? _thirdText;
|
private TextPresenter? _thirdText;
|
||||||
private TextBox? _fourthText;
|
private TextPresenter? _fourthText;
|
||||||
private TextBox? _portText;
|
private TextPresenter? _portText;
|
||||||
|
private byte _firstByte;
|
||||||
|
private byte _secondByte;
|
||||||
|
private byte _thridByte;
|
||||||
|
private byte _fourthByte;
|
||||||
|
private int _port;
|
||||||
|
private TextPresenter?[] _presenters = new TextPresenter?[5];
|
||||||
|
private TextPresenter? _currentActivePresenter;
|
||||||
|
|
||||||
public static readonly StyledProperty<bool> ShowPortProperty = AvaloniaProperty.Register<IPv4Box, bool>(
|
public static readonly StyledProperty<bool> ShowPortProperty = AvaloniaProperty.Register<IPv4Box, bool>(
|
||||||
nameof(ShowPort));
|
nameof(ShowPort));
|
||||||
@@ -37,10 +44,10 @@ public class IPv4Box: TemplatedControl
|
|||||||
set => SetValue(ShowPortProperty, value);
|
set => SetValue(ShowPortProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<IPAddress> IPAddressProperty = AvaloniaProperty.Register<IPv4Box, IPAddress>(
|
public static readonly StyledProperty<IPAddress?> IPAddressProperty = AvaloniaProperty.Register<IPv4Box, IPAddress?>(
|
||||||
nameof(IPAddress));
|
nameof(IPAddress));
|
||||||
|
|
||||||
public IPAddress IPAddress
|
public IPAddress? IPAddress
|
||||||
{
|
{
|
||||||
get => GetValue(IPAddressProperty);
|
get => GetValue(IPAddressProperty);
|
||||||
set => SetValue(IPAddressProperty, value);
|
set => SetValue(IPAddressProperty, value);
|
||||||
@@ -55,69 +62,78 @@ public class IPv4Box: TemplatedControl
|
|||||||
set => SetValue(PortProperty, value);
|
set => SetValue(PortProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
_firstText = e.NameScope.Find<TextBox>(PART_FirstTextBox);
|
ClearTextPresenterEvents(_firstText);
|
||||||
_secondText = e.NameScope.Find<TextBox>(PART_SecondTextBox);
|
ClearTextPresenterEvents(_secondText);
|
||||||
_thirdText = e.NameScope.Find<TextBox>(PART_ThirdTextBox);
|
ClearTextPresenterEvents(_thirdText);
|
||||||
_fourthText = e.NameScope.Find<TextBox>(PART_FourthTextBox);
|
ClearTextPresenterEvents(_fourthText);
|
||||||
_portText = e.NameScope.Find<TextBox>(PART_PortTextBox);
|
ClearTextPresenterEvents(_portText);
|
||||||
|
_firstText = e.NameScope.Find<TextPresenter>(PART_FirstTextPresenter);
|
||||||
_firstText.LostFocus += OnTextLostFocus;
|
_secondText = e.NameScope.Find<TextPresenter>(PART_SecondTextPresenter);
|
||||||
|
_thirdText = e.NameScope.Find<TextPresenter>(PART_ThirdTextPresenter);
|
||||||
|
_fourthText = e.NameScope.Find<TextPresenter>(PART_FourthTextPresenter);
|
||||||
|
_portText = e.NameScope.Find<TextPresenter>(PART_PortTextPresenter);
|
||||||
|
_presenters[0] = _portText;
|
||||||
|
_presenters[1] = _firstText;
|
||||||
|
_presenters[2] = _secondText;
|
||||||
|
_presenters[3] = _thirdText;
|
||||||
|
_presenters[4] = _fourthText;
|
||||||
|
RegisterTextPresenterEvents(_firstText);
|
||||||
|
RegisterTextPresenterEvents(_secondText);
|
||||||
|
RegisterTextPresenterEvents(_thirdText);
|
||||||
|
RegisterTextPresenterEvents(_fourthText);
|
||||||
|
RegisterTextPresenterEvents(_portText);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTextKeyDown(object sender, KeyEventArgs args)
|
private void ClearTextPresenterEvents(TextPresenter? presenter)
|
||||||
{
|
{
|
||||||
if (args.Key == Key.Right)
|
if (presenter is null) return;
|
||||||
|
presenter.LostFocus -= OnTextPresenterLostFocus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterTextPresenterEvents(TextPresenter? presenter)
|
||||||
{
|
{
|
||||||
_secondText?.Focus();
|
if(presenter is null) return;
|
||||||
|
presenter.LostFocus += OnTextPresenterLostFocus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTextPresenterLostFocus(object? sender, RoutedEventArgs args)
|
||||||
|
{
|
||||||
|
if (sender is TextPresenter p)
|
||||||
|
{
|
||||||
|
p.HideCaret();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTextLostFocus(object? sender, RoutedEventArgs args)
|
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender?.Equals(_firstText)??false)
|
var source = e.Source;
|
||||||
|
Point position = e.GetPosition(_firstText);
|
||||||
|
foreach (var presenter in _presenters)
|
||||||
{
|
{
|
||||||
Debug.WriteLine("FIRST");
|
if (presenter?.Bounds.Contains(position) ?? false)
|
||||||
|
{
|
||||||
|
presenter?.ShowCaret();
|
||||||
|
_currentActivePresenter = presenter;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
presenter?.HideCaret();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug.WriteLine(_currentActivePresenter?.Name);
|
||||||
|
base.OnPointerPressed(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnKeyDown(KeyEventArgs e)
|
protected override void OnLostFocus(RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Key == Key.Left)
|
_firstText?.HideCaret();
|
||||||
{
|
_secondText?.HideCaret();
|
||||||
if ((_firstText?.IsFocused??false) && _firstText.SelectionStart == 0)
|
_thirdText?.HideCaret();
|
||||||
{
|
_fourthText?.HideCaret();
|
||||||
_secondText?.Focus();
|
_portText?.HideCaret();
|
||||||
}
|
_currentActivePresenter = null;
|
||||||
}
|
|
||||||
else if (e.Key == Key.Right)
|
|
||||||
{
|
|
||||||
if ((_firstText?.IsFocused ?? false) && _firstText?.SelectionEnd == 2)
|
|
||||||
{
|
|
||||||
_firstText.Focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (e.Key == Key.Tab)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (!(e.Key is >= Key.D0 and <= Key.D9 || e.Key is >= Key.NumPad0 and <= Key.NumPad9))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
base.OnKeyDown(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnTextInput(TextInputEventArgs e)
|
|
||||||
{
|
|
||||||
if (_firstText != null)
|
|
||||||
{
|
|
||||||
_firstText.Text = e.Text;
|
|
||||||
}
|
|
||||||
base.OnTextInput(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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