소스 검색

Timeline visual improvements

flabbet 1 년 전
부모
커밋
555d45f044

+ 23 - 0
src/PixiEditor.AvaloniaUI/Helpers/Converters/FrameToTimeConverter.cs

@@ -0,0 +1,23 @@
+using System.Globalization;
+
+namespace PixiEditor.AvaloniaUI.Helpers.Converters;
+
+internal class FrameToTimeConverter : SingleInstanceMultiValueConverter<FrameToTimeConverter>
+{
+    public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        return Convert(new[] { value }, targetType, parameter, culture); 
+    }
+
+    public override object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
+    {
+        if(values.Count < 2) return null;
+        
+        if (values[0] is int frame && values[1] is int fps)
+        {
+            return TimeSpan.FromSeconds(frame / (double)fps).ToString("mm\\:ss\\.ff");
+        }
+        
+        return null;
+    }
+}

+ 1 - 1
src/PixiEditor.AvaloniaUI/Models/Dialogs/SizeUnit.cs

@@ -1,3 +1,3 @@
 namespace PixiEditor.AvaloniaUI.Models.Dialogs;
 namespace PixiEditor.AvaloniaUI.Models.Dialogs;
 
 
-public enum SizeUnit { Pixel, Percentage }
+public enum SizeUnit { Pixel, Percentage }

+ 8 - 2
src/PixiEditor.AvaloniaUI/Styles/PortingWipStyles.axaml

@@ -45,14 +45,15 @@
     <Style Selector="CheckBox.ImageCheckBox:checked">
     <Style Selector="CheckBox.ImageCheckBox:checked">
         <Setter Property="Content" Value="{DynamicResource icon-eye}" />
         <Setter Property="Content" Value="{DynamicResource icon-eye}" />
     </Style>
     </Style>
-    
+
     <Style Selector="ToggleButton.PlayButton">
     <Style Selector="ToggleButton.PlayButton">
         <Setter Property="Content" Value="{DynamicResource icon-play}" />
         <Setter Property="Content" Value="{DynamicResource icon-play}" />
         <Setter Property="Template">
         <Setter Property="Template">
             <Setter.Value>
             <Setter.Value>
                 <ControlTemplate>
                 <ControlTemplate>
                     <Border Cursor="Hand" Background="{TemplateBinding Background}">
                     <Border Cursor="Hand" Background="{TemplateBinding Background}">
-                        <TextBlock Text="{TemplateBinding Content}" FontSize="{TemplateBinding Width}" Classes="pixi-icon" />
+                        <TextBlock Text="{TemplateBinding Content}" FontSize="{TemplateBinding Width}"
+                                   Classes="pixi-icon" />
                     </Border>
                     </Border>
                 </ControlTemplate>
                 </ControlTemplate>
             </Setter.Value>
             </Setter.Value>
@@ -61,7 +62,12 @@
 
 
     <Style Selector="ToggleButton.PlayButton:checked">
     <Style Selector="ToggleButton.PlayButton:checked">
         <Setter Property="Content" Value="{DynamicResource icon-pause}" />
         <Setter Property="Content" Value="{DynamicResource icon-pause}" />
+        <Setter Property="Background" Value="Transparent" />
     </Style>
     </Style>
+    
+    <Style Selector="ToggleButton.PlayButton:pressed">
+        <Setter Property="Background" Value="Transparent" />
+   </Style> 
     <Style Selector="ToggleButton.ExpandCollapseToggleStyle">
     <Style Selector="ToggleButton.ExpandCollapseToggleStyle">
 
 
     </Style>
     </Style>

+ 105 - 62
src/PixiEditor.AvaloniaUI/Styles/Templates/Timeline.axaml

@@ -19,28 +19,67 @@
                         <RowDefinition Height="Auto" />
                         <RowDefinition Height="Auto" />
                         <RowDefinition Height="*" />
                         <RowDefinition Height="*" />
                     </Grid.RowDefinitions>
                     </Grid.RowDefinitions>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="200" />
+                        <ColumnDefinition Width="*" />
+                    </Grid.ColumnDefinitions>
 
 
-                    <DockPanel Grid.Column="0" Grid.Row="0" LastChildFill="True">
-                        <Button DockPanel.Dock="Left" Classes="pixi-icon"
-                                Content="{DynamicResource icon-plus-square}"
-                                Command="{TemplateBinding NewKeyFrameCommand}" />
+                    <Border Grid.Row="0" Grid.Column="0" BorderThickness="0 0 1 0"
+                            BorderBrush="{DynamicResource ThemeBorderMidBrush}">
+                        <input:SizeInput Unit="FPS"
+                            Width="80" Height="25" HorizontalAlignment="Left"
+                            Size="{Binding Fps, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
+                    </Border>
+                    <Border Grid.Row="0" Grid.Column="1">
+                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="5">
+                            <ToggleButton Margin="0, 5" Width="24" HorizontalAlignment="Center" Classes="PlayButton"
+                                          Name="PART_PlayToggle" />
+                            <TextBlock VerticalAlignment="Center" FontSize="14">
+                                <Run>
+                                    <Run.Text>
+                                        <MultiBinding>
+                                            <MultiBinding.Converter>
+                                                <converters:FrameToTimeConverter />
+                                            </MultiBinding.Converter>
+                                            <Binding Path="ActiveFrame" RelativeSource="{RelativeSource TemplatedParent}" />
+                                            <Binding Path="Fps" RelativeSource="{RelativeSource TemplatedParent}" />
+                                        </MultiBinding>
+                                    </Run.Text>
+                                </Run>
+                                <Run Text="/"/>
+                                <Run>
+                                    <Run.Text>
+                                        <MultiBinding>
+                                            <MultiBinding.Converter>
+                                                <converters:FrameToTimeConverter />
+                                            </MultiBinding.Converter>
+                                            <Binding Path="EndFrame" RelativeSource="{RelativeSource TemplatedParent}" />
+                                            <Binding Path="Fps" RelativeSource="{RelativeSource TemplatedParent}" />
+                                        </MultiBinding>
+                                    </Run.Text>
+                                </Run>
+                            </TextBlock>
+                        </StackPanel>
+                    </Border>
 
 
-                        <Button DockPanel.Dock="Left" Classes="pixi-icon"
-                                Content="{DynamicResource icon-duplicate}"
-                                Command="{TemplateBinding DuplicateKeyFrameCommand}" />
-                        <Button DockPanel.Dock="Left" Classes="pixi-icon"
-                                Content="{DynamicResource icon-trash}"
-                                Command="{TemplateBinding DeleteKeyFrameCommand}"
-                                IsEnabled="{Binding SelectedKeyFrames.Count, RelativeSource={RelativeSource TemplatedParent}}"
-                                CommandParameter="{Binding SelectedKeyFrames, RelativeSource={RelativeSource TemplatedParent}}" />
-                        <input:NumberInput Min="1"
-                                           Value="{Binding Fps, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
-                        <Panel>
-                            <ToggleButton Margin="0, 5" Width="24" HorizontalAlignment="Center" Classes="PlayButton" Name="PART_PlayToggle" />
-                        </Panel>
-                    </DockPanel>
+                    <Border Grid.Column="0" Grid.Row="1" BorderThickness="0 1 1 1"
+                            BorderBrush="{DynamicResource ThemeBorderMidBrush}">
+                        <StackPanel Orientation="Horizontal" Height="40">
+                            <Button Classes="pixi-icon"
+                                    Content="{DynamicResource icon-plus-square}"
+                                    Command="{TemplateBinding NewKeyFrameCommand}" />
+                            <Button Classes="pixi-icon"
+                                    Content="{DynamicResource icon-duplicate}"
+                                    Command="{TemplateBinding DuplicateKeyFrameCommand}" />
+                            <Button Classes="pixi-icon"
+                                    Content="{DynamicResource icon-trash}"
+                                    Command="{TemplateBinding DeleteKeyFrameCommand}"
+                                    IsEnabled="{Binding SelectedKeyFrames.Count, RelativeSource={RelativeSource TemplatedParent}}"
+                                    CommandParameter="{Binding SelectedKeyFrames, RelativeSource={RelativeSource TemplatedParent}}" />
+                        </StackPanel>
+                    </Border>
 
 
-                    <Grid Grid.Row="2">
+                    <Grid Grid.Row="1" Grid.RowSpan="2" Grid.Column="0" Grid.ColumnSpan="2">
                         <Grid.RowDefinitions>
                         <Grid.RowDefinitions>
                             <RowDefinition Height="Auto" /> <!-- For the timeline slider -->
                             <RowDefinition Height="Auto" /> <!-- For the timeline slider -->
                             <RowDefinition Height="*" />    <!-- For the keyframes and headers -->
                             <RowDefinition Height="*" />    <!-- For the keyframes and headers -->
@@ -49,50 +88,53 @@
                             <ColumnDefinition Width="200" /> <!-- For the headers -->
                             <ColumnDefinition Width="200" /> <!-- For the headers -->
                             <ColumnDefinition Width="*" />    <!-- For the timeline slider and keyframes -->
                             <ColumnDefinition Width="*" />    <!-- For the timeline slider and keyframes -->
                         </Grid.ColumnDefinitions>
                         </Grid.ColumnDefinitions>
-                        <animations:TimelineSlider
-                            Grid.Row="0" Grid.Column="1"
-                            TickFrequency="1" Height="35" ClipToBounds="False"
-                            TickPlacement="TopLeft" VerticalAlignment="Top"
-                            SmallChange="1" ZIndex="10"
-                            LargeChange="10"
-                            Scale="{Binding Scale, RelativeSource={RelativeSource TemplatedParent}}"
-                            Offset="{Binding ScrollOffset, RelativeSource={RelativeSource TemplatedParent}}"
-                            MinLeftOffset="{Binding MinLeftOffset, RelativeSource={RelativeSource TemplatedParent}}"
-                            IsSnapToTickEnabled="True"
-                            Name="PART_TimelineSlider"
-                            Minimum="1">
-                            <animations:TimelineSlider.Maximum>
-                                <MultiBinding>
-                                    <MultiBinding.Converter>
-                                        <converters:TimelineSliderWidthToMaximumConverter />
-                                    </MultiBinding.Converter>
-                                    <Binding Path="Bounds"
-                                             RelativeSource="{RelativeSource Self}" />
-                                    <Binding RelativeSource="{RelativeSource TemplatedParent}"
-                                             Path="Scale" />
-                                    <Binding RelativeSource="{RelativeSource TemplatedParent}"
-                                             Path="ScrollOffset" />
-                                </MultiBinding>
-                            </animations:TimelineSlider.Maximum>
-                            <Interaction.Behaviors>
-                                <behaviours:SliderUpdateBehavior
-                                    Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActiveFrame, Mode=OneWay}"
-                                    DragStarted="{xaml:Command PixiEditor.Document.StartChangeActiveFrame}"
-                                    DragValueChanged="{xaml:Command PixiEditor.Document.ChangeActiveFrame, UseProvided=True}"
-                                    DragEnded="{xaml:Command PixiEditor.Document.EndChangeActiveFrame}"
-                                    SetValueCommand="{xaml:Command PixiEditor.Animation.ActiveFrameSet, UseProvided=True}"
-                                    ValueFromSlider="{Binding ElementName=PART_TimelineSlider, Path=Value, Mode=TwoWay}" />
-                            </Interaction.Behaviors>
-                        </animations:TimelineSlider>
+                        <Border Grid.Row="0" Grid.Column="1" BorderBrush="{DynamicResource ThemeBorderMidBrush}"
+                                BorderThickness="0 1">
+                            <animations:TimelineSlider
+                                TickFrequency="1" Height="40" ClipToBounds="False"
+                                TickPlacement="TopLeft" VerticalAlignment="Top"
+                                SmallChange="1" ZIndex="10"
+                                LargeChange="10"
+                                Scale="{Binding Scale, RelativeSource={RelativeSource TemplatedParent}}"
+                                Offset="{Binding ScrollOffset, RelativeSource={RelativeSource TemplatedParent}}"
+                                MinLeftOffset="{Binding MinLeftOffset, RelativeSource={RelativeSource TemplatedParent}}"
+                                IsSnapToTickEnabled="True"
+                                Name="PART_TimelineSlider"
+                                Minimum="1">
+                                <animations:TimelineSlider.Maximum>
+                                    <MultiBinding>
+                                        <MultiBinding.Converter>
+                                            <converters:TimelineSliderWidthToMaximumConverter />
+                                        </MultiBinding.Converter>
+                                        <Binding Path="Bounds"
+                                                 RelativeSource="{RelativeSource Self}" />
+                                        <Binding RelativeSource="{RelativeSource TemplatedParent}"
+                                                 Path="Scale" />
+                                        <Binding RelativeSource="{RelativeSource TemplatedParent}"
+                                                 Path="ScrollOffset" />
+                                    </MultiBinding>
+                                </animations:TimelineSlider.Maximum>
+                                <Interaction.Behaviors>
+                                    <behaviours:SliderUpdateBehavior
+                                        Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActiveFrame, Mode=OneWay}"
+                                        DragStarted="{xaml:Command PixiEditor.Document.StartChangeActiveFrame}"
+                                        DragValueChanged="{xaml:Command PixiEditor.Document.ChangeActiveFrame, UseProvided=True}"
+                                        DragEnded="{xaml:Command PixiEditor.Document.EndChangeActiveFrame}"
+                                        SetValueCommand="{xaml:Command PixiEditor.Animation.ActiveFrameSet, UseProvided=True}"
+                                        ValueFromSlider="{Binding ElementName=PART_TimelineSlider, Path=Value, Mode=TwoWay}" />
+                                </Interaction.Behaviors>
+                            </animations:TimelineSlider>
+                        </Border>
 
 
-                        <Panel ClipToBounds="True" Grid.Row="1" Grid.Column="1" Margin="29, -16, 0, 0" VerticalAlignment="Stretch"
+                        <Panel ClipToBounds="True" Grid.Row="1" Grid.Column="1" Margin="29, -22, 0, 0"
+                               VerticalAlignment="Stretch"
                                ZIndex="11" HorizontalAlignment="Left" IsHitTestVisible="False">
                                ZIndex="11" HorizontalAlignment="Left" IsHitTestVisible="False">
                             <Border Width="2" Background="{DynamicResource ThemeAccentBrush}">
                             <Border Width="2" Background="{DynamicResource ThemeAccentBrush}">
                                 <Border.Margin>
                                 <Border.Margin>
                                     <MultiBinding Converter="{converters:TimelineSliderValueToMarginConverter}">
                                     <MultiBinding Converter="{converters:TimelineSliderValueToMarginConverter}">
                                         <Binding Path="ActiveFrame"
                                         <Binding Path="ActiveFrame"
                                                  RelativeSource="{RelativeSource TemplatedParent}" />
                                                  RelativeSource="{RelativeSource TemplatedParent}" />
-                                        <Binding Path="Minimum" ElementName="PART_TimelineSlider"/>
+                                        <Binding Path="Minimum" ElementName="PART_TimelineSlider" />
                                         <Binding Path="Scale"
                                         <Binding Path="Scale"
                                                  RelativeSource="{RelativeSource TemplatedParent}" />
                                                  RelativeSource="{RelativeSource TemplatedParent}" />
                                         <Binding Path="ScrollOffset"
                                         <Binding Path="ScrollOffset"
@@ -106,7 +148,7 @@
                                       Name="PART_TimelineHeaderScroll"
                                       Name="PART_TimelineHeaderScroll"
                                       Grid.Row="1" Grid.Column="0">
                                       Grid.Row="1" Grid.Column="0">
                             <StackPanel Orientation="Vertical" Background="{DynamicResource ThemeBackgroundBrush1}">
                             <StackPanel Orientation="Vertical" Background="{DynamicResource ThemeBackgroundBrush1}">
-                                <ItemsControl Margin="0, 35"
+                                <ItemsControl
                                               ItemsSource="{Binding KeyFrames, RelativeSource={RelativeSource TemplatedParent}}">
                                               ItemsSource="{Binding KeyFrames, RelativeSource={RelativeSource TemplatedParent}}">
                                     <ItemsControl.DataTemplates>
                                     <ItemsControl.DataTemplates>
                                         <DataTemplate DataType="document:KeyFrameGroupViewModel">
                                         <DataTemplate DataType="document:KeyFrameGroupViewModel">
@@ -118,7 +160,8 @@
                             </StackPanel>
                             </StackPanel>
                         </ScrollViewer>
                         </ScrollViewer>
                         <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
                         <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
-                                      Grid.Row="1" Offset="{Binding ScrollOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
+                                      Grid.Row="1"
+                                      Offset="{Binding ScrollOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
                                       Name="PART_TimelineKeyFramesScroll" Grid.Column="1">
                                       Name="PART_TimelineKeyFramesScroll" Grid.Column="1">
                             <Grid Background="{DynamicResource ThemeBackgroundBrush}" Name="PART_ContentGrid">
                             <Grid Background="{DynamicResource ThemeBackgroundBrush}" Name="PART_ContentGrid">
                                 <Interaction.Behaviors>
                                 <Interaction.Behaviors>
@@ -130,7 +173,6 @@
                                     </EventTriggerBehavior>
                                     </EventTriggerBehavior>
                                 </Interaction.Behaviors>
                                 </Interaction.Behaviors>
                                 <ItemsControl ClipToBounds="False" Name="PART_KeyFramesHost"
                                 <ItemsControl ClipToBounds="False" Name="PART_KeyFramesHost"
-                                              Margin="0, 35, 0, 0"
                                               ItemsSource="{Binding KeyFrames, RelativeSource={RelativeSource TemplatedParent}}">
                                               ItemsSource="{Binding KeyFrames, RelativeSource={RelativeSource TemplatedParent}}">
                                     <ItemsControl.DataTemplates>
                                     <ItemsControl.DataTemplates>
                                         <DataTemplate DataType="document:KeyFrameGroupViewModel">
                                         <DataTemplate DataType="document:KeyFrameGroupViewModel">
@@ -194,10 +236,11 @@
                                         </DataTemplate>
                                         </DataTemplate>
                                     </ItemsControl.DataTemplates>
                                     </ItemsControl.DataTemplates>
                                 </ItemsControl>
                                 </ItemsControl>
-                                
-                                <Rectangle Name="PART_SelectionRectangle" HorizontalAlignment="Left" VerticalAlignment="Top"
+
+                                <Rectangle Name="PART_SelectionRectangle" HorizontalAlignment="Left"
+                                           VerticalAlignment="Top"
                                            IsVisible="False" ZIndex="100"
                                            IsVisible="False" ZIndex="100"
-                                           Fill="{DynamicResource SelectionFillBrush}" Opacity="1"/>
+                                           Fill="{DynamicResource SelectionFillBrush}" Opacity="1" />
                             </Grid>
                             </Grid>
                         </ScrollViewer>
                         </ScrollViewer>
                     </Grid>
                     </Grid>

+ 1 - 1
src/PixiEditor.AvaloniaUI/Styles/Templates/TimelineGroupHeader.axaml

@@ -7,7 +7,7 @@
     <ControlTheme TargetType="animations:TimelineGroupHeader" x:Key="{x:Type animations:TimelineGroupHeader}">
     <ControlTheme TargetType="animations:TimelineGroupHeader" x:Key="{x:Type animations:TimelineGroupHeader}">
         <Setter Property="Template">
         <Setter Property="Template">
             <ControlTemplate>
             <ControlTemplate>
-                <Border BorderBrush="{DynamicResource ThemeBorderMidBrush}" BorderThickness="1">
+                <Border BorderBrush="{DynamicResource ThemeBorderMidBrush}" BorderThickness="0 0 1 1">
                     <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                     <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                         <CheckBox Classes="ImageCheckBox" IsChecked="{Binding Item.IsVisible, RelativeSource={RelativeSource TemplatedParent}}"/>
                         <CheckBox Classes="ImageCheckBox" IsChecked="{Binding Item.IsVisible, RelativeSource={RelativeSource TemplatedParent}}"/>
                         <TextBlock Text="{Binding Item.LayerName, RelativeSource={RelativeSource TemplatedParent}}" />
                         <TextBlock Text="{Binding Item.LayerName, RelativeSource={RelativeSource TemplatedParent}}" />

+ 4 - 2
src/PixiEditor.AvaloniaUI/Styles/Templates/TimelineSlider.axaml

@@ -24,7 +24,7 @@
                 <ControlTemplate>
                 <ControlTemplate>
                     <Grid Name="grid">
                     <Grid Name="grid">
                         <Border Background="{DynamicResource ThemeControlLowBrush}"
                         <Border Background="{DynamicResource ThemeControlLowBrush}"
-                                Height="35"
+                                Height="40"
                                 VerticalAlignment="Center">
                                 VerticalAlignment="Center">
                         </Border>
                         </Border>
                         <Canvas Margin="-6,-1">
                         <Canvas Margin="-6,-1">
@@ -38,7 +38,9 @@
                             Offset="{TemplateBinding Offset}"
                             Offset="{TemplateBinding Offset}"
                             MinLeftOffset="{TemplateBinding MinLeftOffset}"
                             MinLeftOffset="{TemplateBinding MinLeftOffset}"
                             MinValue="{TemplateBinding Minimum}"
                             MinValue="{TemplateBinding Minimum}"
-                            Fill="{DynamicResource ThemeForegroundBrush}" />
+                            Fill="{DynamicResource ThemeControlHighBrush}" 
+                            Foreground="{DynamicResource ThemeForegroundBrush}"
+                            Margin="0 0 0 5"/>
                         <animations:TimelineSliderTrack Name="PART_Track"
                         <animations:TimelineSliderTrack Name="PART_Track"
                                IsDirectionReversed="{TemplateBinding IsDirectionReversed}"
                                IsDirectionReversed="{TemplateBinding IsDirectionReversed}"
                                Margin="15, 0, 0, 0"
                                Margin="15, 0, 0, 0"

+ 23 - 1
src/PixiEditor.AvaloniaUI/Views/Animations/Timeline.cs

@@ -139,6 +139,8 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         set { SetValue(FpsProperty, value); }
         set { SetValue(FpsProperty, value); }
     }
     }
 
 
+    public int EndFrame => KeyFrames?.FrameCount > 0 ? KeyFrames.FrameCount : DefaultEndFrame;
+
     public ICommand DraggedKeyFrameCommand { get; }
     public ICommand DraggedKeyFrameCommand { get; }
     public ICommand ReleasedKeyFrameCommand { get; }
     public ICommand ReleasedKeyFrameCommand { get; }
     public ICommand ClearSelectedKeyFramesCommand { get; }
     public ICommand ClearSelectedKeyFramesCommand { get; }
@@ -172,6 +174,7 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         IsPlayingProperty.Changed.Subscribe(IsPlayingChanged);
         IsPlayingProperty.Changed.Subscribe(IsPlayingChanged);
         FpsProperty.Changed.Subscribe(FpsChanged);
         FpsProperty.Changed.Subscribe(FpsChanged);
         KeyFramesProperty.Changed.Subscribe(OnKeyFramesChanged);
         KeyFramesProperty.Changed.Subscribe(OnKeyFramesChanged);
+        DefaultEndFrameProperty.Changed.Subscribe(OnDefaultEndFrameChanged);
     }
     }
 
 
     public Timeline()
     public Timeline()
@@ -332,7 +335,7 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
 
 
     private void PlayTimerOnTick(object? sender, EventArgs e)
     private void PlayTimerOnTick(object? sender, EventArgs e)
     {
     {
-        if (ActiveFrame >= (KeyFrames.Count > 0 ? KeyFrames.FrameCount : DefaultEndFrame))
+        if (ActiveFrame >= EndFrame) 
         {
         {
             ActiveFrame = 1;
             ActiveFrame = 1;
         }
         }
@@ -570,12 +573,16 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
             newCollection.KeyFrameAdded += timeline.KeyFrames_KeyFrameAdded;
             newCollection.KeyFrameAdded += timeline.KeyFrames_KeyFrameAdded;
             newCollection.KeyFrameRemoved += timeline.KeyFrames_KeyFrameRemoved;
             newCollection.KeyFrameRemoved += timeline.KeyFrames_KeyFrameRemoved;
         }
         }
+        
+        timeline.PropertyChanged(timeline, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
+        timeline.PropertyChanged(timeline, new PropertyChangedEventArgs(nameof(EndFrame)));
     }
     }
 
 
     private void KeyFrames_KeyFrameAdded(KeyFrameViewModel keyFrame)
     private void KeyFrames_KeyFrameAdded(KeyFrameViewModel keyFrame)
     {
     {
         keyFrame.PropertyChanged += KeyFrameOnPropertyChanged;
         keyFrame.PropertyChanged += KeyFrameOnPropertyChanged;
         PropertyChanged(this, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
         PropertyChanged(this, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
+        PropertyChanged(this, new PropertyChangedEventArgs(nameof(EndFrame)));
     }
     }
 
 
     private void KeyFrames_KeyFrameRemoved(KeyFrameViewModel keyFrame)
     private void KeyFrames_KeyFrameRemoved(KeyFrameViewModel keyFrame)
@@ -587,6 +594,17 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         }
         }
         
         
         PropertyChanged(this, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
         PropertyChanged(this, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
+        PropertyChanged(this, new PropertyChangedEventArgs(nameof(EndFrame)));
+    }
+    
+    private static void OnDefaultEndFrameChanged(AvaloniaPropertyChangedEventArgs e)
+    {
+        if (e.Sender is not Timeline timeline)
+        {
+            return;
+        }
+
+        timeline.PropertyChanged(timeline, new PropertyChangedEventArgs(nameof(EndFrame)));
     }
     }
     
     
     private void KeyFrameOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
     private void KeyFrameOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
@@ -597,6 +615,10 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
             {
             {
                 PropertyChanged(this, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
                 PropertyChanged(this, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
             }
             }
+            else if (e.PropertyName == nameof(KeyFrameViewModel.StartFrameBindable) || e.PropertyName == nameof(KeyFrameViewModel.DurationBindable))
+            {
+                PropertyChanged(this, new PropertyChangedEventArgs(nameof(EndFrame)));
+            }
         }
         }
     }
     }
 }
 }

+ 13 - 4
src/PixiEditor.AvaloniaUI/Views/Animations/TimelineTickBar.cs

@@ -20,6 +20,15 @@ public class TimelineTickBar : Control
     public static readonly StyledProperty<int> MinValueProperty = AvaloniaProperty.Register<TimelineTickBar, int>(
     public static readonly StyledProperty<int> MinValueProperty = AvaloniaProperty.Register<TimelineTickBar, int>(
         nameof(MinValue), 1);
         nameof(MinValue), 1);
 
 
+    public static readonly StyledProperty<IBrush> ForegroundProperty = AvaloniaProperty.Register<TimelineTickBar, IBrush>(
+        nameof(Foreground), Brushes.White);
+
+    public IBrush Foreground
+    {
+        get => GetValue(ForegroundProperty);
+        set => SetValue(ForegroundProperty, value);
+    }
+    
     public int MinValue
     public int MinValue
     {
     {
         get => GetValue(MinValueProperty);
         get => GetValue(MinValueProperty);
@@ -52,7 +61,7 @@ public class TimelineTickBar : Control
 
 
     static TimelineTickBar()
     static TimelineTickBar()
     {
     {
-        AffectsRender<TimelineTickBar>(ScaleProperty, FillProperty, OffsetProperty);
+        AffectsRender<TimelineTickBar>(ScaleProperty, FillProperty, OffsetProperty, MinValueProperty, ForegroundProperty, MinLeftOffsetProperty);
     }
     }
     
     
     private readonly int[] possibleLargeTickIntervals = { 1, 5, 10, 50, 100 };
     private readonly int[] possibleLargeTickIntervals = { 1, 5, 10, 50, 100 };
@@ -83,8 +92,8 @@ public class TimelineTickBar : Control
             smallTickInterval = 1;
             smallTickInterval = 1;
         }
         }
 
 
-        Pen largeTickPen = new Pen(Fill);
-        Pen smallTickPen = new Pen(Fill, 0.5);
+        Pen largeTickPen = new Pen(Fill, thickness: 2);
+        Pen smallTickPen = new Pen(Fill, 1.5);
         
         
         int largeStart = visibleMin - (visibleMin % largeTickInterval);
         int largeStart = visibleMin - (visibleMin % largeTickInterval);
         
         
@@ -117,7 +126,7 @@ public class TimelineTickBar : Control
             context.DrawLine(largeTickPen, new Point(x, height), new Point(x, height * 0.55f));
             context.DrawLine(largeTickPen, new Point(x, height), new Point(x, height * 0.55f));
             
             
             var text = new FormattedText((i + MinValue).ToString(), CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
             var text = new FormattedText((i + MinValue).ToString(), CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
-                Typeface.Default, 12, Fill);
+                Typeface.Default, 12, Foreground);
             
             
             double textCenter = text.WidthIncludingTrailingWhitespace / 2;
             double textCenter = text.WidthIncludingTrailingWhitespace / 2;
             Point textPosition = new Point(x - textCenter, height * 0.05);
             Point textPosition = new Point(x - textCenter, height * 0.05);

+ 67 - 52
src/PixiEditor.AvaloniaUI/Views/Input/NumberInput.cs

@@ -19,29 +19,33 @@ internal partial class NumberInput : TextBox
 {
 {
     public static readonly StyledProperty<double> ValueProperty =
     public static readonly StyledProperty<double> ValueProperty =
         AvaloniaProperty.Register<NumberInput, double>(
         AvaloniaProperty.Register<NumberInput, double>(
-            nameof(Value), 0);
+            nameof(Value), 0, coerce: CoerceValue);
 
 
     public static readonly StyledProperty<double> MinProperty =
     public static readonly StyledProperty<double> MinProperty =
         AvaloniaProperty.Register<NumberInput, double>(
         AvaloniaProperty.Register<NumberInput, double>(
-            nameof(Min), float.NegativeInfinity);
+            nameof(Min), float.NegativeInfinity, coerce: CoerceValue);
 
 
     public static readonly StyledProperty<double> MaxProperty =
     public static readonly StyledProperty<double> MaxProperty =
         AvaloniaProperty.Register<NumberInput, double>(
         AvaloniaProperty.Register<NumberInput, double>(
-            nameof(Max), double.PositiveInfinity);
+            nameof(Max), double.PositiveInfinity, coerce: CoerceValue);
 
 
-    public static readonly StyledProperty<string> FormattedValueProperty = AvaloniaProperty.Register<NumberInput, string>(
-        nameof(FormattedValue), "0");
+    public static readonly StyledProperty<string> FormattedValueProperty =
+        AvaloniaProperty.Register<NumberInput, string>(
+            nameof(FormattedValue), "0");
+
+    public static readonly StyledProperty<bool> EnableScrollChangeProperty =
+        AvaloniaProperty.Register<NumberInput, bool>(
+            "EnableScrollChange", true);
 
 
-    public static readonly StyledProperty<bool> EnableScrollChangeProperty = AvaloniaProperty.Register<NumberInput, bool>(
-        "EnableScrollChange", true);
     public string FormattedValue
     public string FormattedValue
     {
     {
         get => GetValue(FormattedValueProperty);
         get => GetValue(FormattedValueProperty);
         set => SetValue(FormattedValueProperty, value);
         set => SetValue(FormattedValueProperty, value);
     }
     }
 
 
-    public static readonly StyledProperty<bool> SelectOnMouseClickProperty = AvaloniaProperty.Register<NumberInput, bool>(
-        nameof(SelectOnMouseClick), true);
+    public static readonly StyledProperty<bool> SelectOnMouseClickProperty =
+        AvaloniaProperty.Register<NumberInput, bool>(
+            nameof(SelectOnMouseClick), true);
 
 
     public static readonly StyledProperty<bool> ConfirmOnEnterProperty = AvaloniaProperty.Register<NumberInput, bool>(
     public static readonly StyledProperty<bool> ConfirmOnEnterProperty = AvaloniaProperty.Register<NumberInput, bool>(
         nameof(ConfirmOnEnter), true);
         nameof(ConfirmOnEnter), true);
@@ -60,14 +64,8 @@ internal partial class NumberInput : TextBox
 
 
     private static Regex regex;
     private static Regex regex;
 
 
-    public int Decimals
-    {
-        get { return (int)GetValue(DecimalsProperty); }
-        set { SetValue(DecimalsProperty, value); }
-    }
-
     public static readonly StyledProperty<int> DecimalsProperty =
     public static readonly StyledProperty<int> DecimalsProperty =
-        AvaloniaProperty.Register<NumberInput, int>(nameof(Decimals), 2);
+        AvaloniaProperty.Register<NumberInput, int>(nameof(Decimals), 2, coerce: CoerceDecimals);
 
 
     public Action OnScrollAction
     public Action OnScrollAction
     {
     {
@@ -96,6 +94,13 @@ internal partial class NumberInput : TextBox
         set => SetValue(MaxProperty, value);
         set => SetValue(MaxProperty, value);
     }
     }
 
 
+
+    public int Decimals
+    {
+        get { return (int)GetValue(DecimalsProperty); }
+        set { SetValue(DecimalsProperty, value); }
+    }
+
     public static readonly StyledProperty<bool> FocusNextProperty =
     public static readonly StyledProperty<bool> FocusNextProperty =
         AvaloniaProperty.Register<NumberInput, bool>(
         AvaloniaProperty.Register<NumberInput, bool>(
             nameof(FocusNext));
             nameof(FocusNext));
@@ -107,10 +112,11 @@ internal partial class NumberInput : TextBox
     }
     }
 
 
     private static readonly DataTable DataTable = new DataTable();
     private static readonly DataTable DataTable = new DataTable();
+
     private static char[] allowedChars = new char[]
     private static char[] allowedChars = new char[]
     {
     {
-        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '*', '/', '(', ')', '.', ',', ' ',
-        'i', 'n', 'f', 't', 'y', 'e', 'I', 'N', 'F', 'T', 'Y', 'E'
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '*', '/', '(', ')', '.', ',', ' ', 'i', 'n',
+        'f', 't', 'y', 'e', 'I', 'N', 'F', 'T', 'Y', 'E'
     };
     };
 
 
 
 
@@ -124,10 +130,10 @@ internal partial class NumberInput : TextBox
 
 
     private Control? leftGrabber;
     private Control? leftGrabber;
     private Control? rightGrabber;
     private Control? rightGrabber;
-    
+
     private double _pressedValue;
     private double _pressedValue;
     private double _pressedRelativeX;
     private double _pressedRelativeX;
-    
+
     static NumberInput()
     static NumberInput()
     {
     {
         ValueProperty.Changed.Subscribe(OnValueChanged);
         ValueProperty.Changed.Subscribe(OnValueChanged);
@@ -143,11 +149,7 @@ internal partial class NumberInput : TextBox
         behaviors.Add(behavior);
         behaviors.Add(behavior);
         Interaction.SetBehaviors(this, behaviors);
         Interaction.SetBehaviors(this, behaviors);
 
 
-        Binding binding = new Binding(nameof(FormattedValue))
-        {
-            Source = this,
-            Mode = BindingMode.TwoWay
-        };
+        Binding binding = new Binding(nameof(FormattedValue)) { Source = this, Mode = BindingMode.TwoWay };
 
 
         this.Bind(TextProperty, binding);
         this.Bind(TextProperty, binding);
 
 
@@ -159,10 +161,10 @@ internal partial class NumberInput : TextBox
     protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
     protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
     {
     {
         base.OnApplyTemplate(e);
         base.OnApplyTemplate(e);
-        
-        InnerLeftContent = leftGrabber = CreateMouseGrabber(); 
+
+        InnerLeftContent = leftGrabber = CreateMouseGrabber();
         leftGrabber.HorizontalAlignment = HorizontalAlignment.Left;
         leftGrabber.HorizontalAlignment = HorizontalAlignment.Left;
-        InnerRightContent = rightGrabber = CreateMouseGrabber(); 
+        InnerRightContent = rightGrabber = CreateMouseGrabber();
         rightGrabber.HorizontalAlignment = HorizontalAlignment.Right;
         rightGrabber.HorizontalAlignment = HorizontalAlignment.Right;
     }
     }
 
 
@@ -172,10 +174,10 @@ internal partial class NumberInput : TextBox
         {
         {
             rightGrabber.IsVisible = false;
             rightGrabber.IsVisible = false;
         }
         }
-        
+
         leftGrabber.Height = e.NewSize.Height - 10;
         leftGrabber.Height = e.NewSize.Height - 10;
         leftGrabber.Width = e.NewSize.Width / 4f;
         leftGrabber.Width = e.NewSize.Width / 4f;
-        
+
         rightGrabber.Height = e.NewSize.Height - 10;
         rightGrabber.Height = e.NewSize.Height - 10;
         rightGrabber.Width = e.NewSize.Width / 4f;
         rightGrabber.Width = e.NewSize.Width / 4f;
     }
     }
@@ -184,16 +186,15 @@ internal partial class NumberInput : TextBox
     {
     {
         var grabber = new Grid()
         var grabber = new Grid()
         {
         {
-            Cursor = new Cursor(StandardCursorType.SizeWestEast),
-            Background = Brushes.Transparent,
+            Cursor = new Cursor(StandardCursorType.SizeWestEast), Background = Brushes.Transparent,
         };
         };
 
 
         grabber.PointerPressed += GrabberPressed;
         grabber.PointerPressed += GrabberPressed;
         grabber.PointerMoved += GrabberMoved;
         grabber.PointerMoved += GrabberMoved;
-        
+
         return grabber;
         return grabber;
     }
     }
-    
+
     private void GrabberPressed(object sender, PointerPressedEventArgs e)
     private void GrabberPressed(object sender, PointerPressedEventArgs e)
     {
     {
         e.Pointer.Capture(leftGrabber);
         e.Pointer.Capture(leftGrabber);
@@ -201,53 +202,67 @@ internal partial class NumberInput : TextBox
         _pressedRelativeX = e.GetPosition(this).X;
         _pressedRelativeX = e.GetPosition(this).X;
         e.Handled = true;
         e.Handled = true;
     }
     }
-    
+
     private void GrabberMoved(object sender, PointerEventArgs e)
     private void GrabberMoved(object sender, PointerEventArgs e)
     {
     {
-        if(e.Pointer.Captured != null && (e.Pointer.Captured.Equals(leftGrabber) || e.Pointer.Captured.Equals(rightGrabber)))
+        if (e.Pointer.Captured != null &&
+            (e.Pointer.Captured.Equals(leftGrabber) || e.Pointer.Captured.Equals(rightGrabber)))
         {
         {
             double relativeX = e.GetPosition(this).X;
             double relativeX = e.GetPosition(this).X;
             double diff = relativeX - _pressedRelativeX;
             double diff = relativeX - _pressedRelativeX;
 
 
             double pixelsPerUnit = 5;
             double pixelsPerUnit = 5;
-            
+
             double newValue = _pressedValue + diff / pixelsPerUnit;
             double newValue = _pressedValue + diff / pixelsPerUnit;
-            Value = (float)Math.Round(Math.Clamp(newValue, Min, Max), Decimals);
-            e.Handled = true; 
+            Value = newValue;
+            e.Handled = true;
         }
         }
     }
     }
 
 
     private void BindTextBoxBehavior(TextBoxFocusBehavior behavior)
     private void BindTextBoxBehavior(TextBoxFocusBehavior behavior)
     {
     {
-        Binding focusNextBinding = new Binding(nameof(FocusNext))
-        {
-            Source = this,
-            Mode = BindingMode.OneWay
-        };
+        Binding focusNextBinding = new Binding(nameof(FocusNext)) { Source = this, Mode = BindingMode.OneWay };
 
 
         behavior.Bind(TextBoxFocusBehavior.FocusNextProperty, focusNextBinding);
         behavior.Bind(TextBoxFocusBehavior.FocusNextProperty, focusNextBinding);
 
 
         Binding selectOnMouseClickBinding = new Binding(nameof(SelectOnMouseClick))
         Binding selectOnMouseClickBinding = new Binding(nameof(SelectOnMouseClick))
         {
         {
-            Source = this,
-            Mode = BindingMode.OneWay
+            Source = this, Mode = BindingMode.OneWay
         };
         };
 
 
         behavior.Bind(TextBoxFocusBehavior.SelectOnMouseClickProperty, selectOnMouseClickBinding);
         behavior.Bind(TextBoxFocusBehavior.SelectOnMouseClickProperty, selectOnMouseClickBinding);
 
 
         Binding confirmOnEnterBinding = new Binding(nameof(ConfirmOnEnter))
         Binding confirmOnEnterBinding = new Binding(nameof(ConfirmOnEnter))
         {
         {
-            Source = this,
-            Mode = BindingMode.OneWay
+            Source = this, Mode = BindingMode.OneWay
         };
         };
 
 
         behavior.Bind(TextBoxFocusBehavior.ConfirmOnEnterProperty, confirmOnEnterBinding);
         behavior.Bind(TextBoxFocusBehavior.ConfirmOnEnterProperty, confirmOnEnterBinding);
     }
     }
 
 
+    private static double CoerceValue(AvaloniaObject o, double value)
+    {
+        double min = (double)o.GetValue(MinProperty);
+        double max = (double)o.GetValue(MaxProperty);
+        int decimals = (int)o.GetValue(DecimalsProperty);
+
+        return Math.Round(Math.Clamp(value, min, max), decimals);
+    }
+    
+    private static int CoerceDecimals(AvaloniaObject o, int value)
+    {
+        if (value < 0)
+        {
+            value = 0;
+        }
+        
+        return value;
+    }
+
     private static void OnValueChanged(AvaloniaPropertyChangedEventArgs<double> e)
     private static void OnValueChanged(AvaloniaPropertyChangedEventArgs<double> e)
     {
     {
         NumberInput input = (NumberInput)e.Sender;
         NumberInput input = (NumberInput)e.Sender;
-        input.Value = (float)Math.Round(Math.Clamp(e.NewValue.Value, input.Min, input.Max), input.Decimals);
+        //input.Value = (float)Math.Round(Math.Clamp(e.NewValue.Value, input.Min, input.Max), input.Decimals);
 
 
         var preFormatted = FormatValue(input.Value, input.Decimals);
         var preFormatted = FormatValue(input.Value, input.Decimals);
         input.FormattedValue = preFormatted;
         input.FormattedValue = preFormatted;
@@ -317,7 +332,7 @@ internal partial class NumberInput : TextBox
     private static void FormattedValueChanged(AvaloniaPropertyChangedEventArgs<string> e)
     private static void FormattedValueChanged(AvaloniaPropertyChangedEventArgs<string> e)
     {
     {
         NumberInput input = (NumberInput)e.Sender;
         NumberInput input = (NumberInput)e.Sender;
-        if(ContainsInvalidCharacter(e.NewValue.Value))
+        if (ContainsInvalidCharacter(e.NewValue.Value))
         {
         {
             input.FormattedValue = e.OldValue.Value;
             input.FormattedValue = e.OldValue.Value;
         }
         }
@@ -325,7 +340,7 @@ internal partial class NumberInput : TextBox
 
 
     private static bool ContainsInvalidCharacter(string text)
     private static bool ContainsInvalidCharacter(string text)
     {
     {
-        if(text == null)
+        if (text == null)
         {
         {
             return false;
             return false;
         }
         }
@@ -339,7 +354,7 @@ internal partial class NumberInput : TextBox
         {
         {
             return;
             return;
         }
         }
-        
+
         int step = (int)e.Delta.Y;
         int step = (int)e.Delta.Y;
 
 
         double newValue = Value;
         double newValue = Value;

+ 4 - 3
src/PixiEditor.AvaloniaUI/Views/Input/SizeInput.axaml

@@ -5,8 +5,6 @@
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
              xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
-             xmlns:converters="clr-namespace:PixiEditor.AvaloniaUI.Helpers.Converters"
-             xmlns:behaviours="clr-namespace:PixiEditor.AvaloniaUI.Helpers.Behaviours"
              xmlns:input="clr-namespace:PixiEditor.AvaloniaUI.Views.Input"
              xmlns:input="clr-namespace:PixiEditor.AvaloniaUI.Views.Input"
              mc:Ignorable="d" Focusable="True"
              mc:Ignorable="d" Focusable="True"
              d:DesignHeight="30" Name="uc"
              d:DesignHeight="30" Name="uc"
@@ -37,6 +35,8 @@
                      Decimals="0"
                      Decimals="0"
                      x:Name="input"
                      x:Name="input"
                      Value="{Binding Size, ElementName=uc, Mode=TwoWay}"
                      Value="{Binding Size, ElementName=uc, Mode=TwoWay}"
+                     Min="1"
+                     Max="{Binding MaxSize, ElementName=uc}"
                      d:Value="22"
                      d:Value="22"
                      FocusNext="{Binding FocusNext, ElementName=uc}"
                      FocusNext="{Binding FocusNext, ElementName=uc}"
                      SelectOnMouseClick="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
                      SelectOnMouseClick="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
@@ -44,7 +44,8 @@
                      Width="43"/>
                      Width="43"/>
             <Grid Grid.Column="1" Background="{Binding BorderBrush, ElementName=border}"
             <Grid Grid.Column="1" Background="{Binding BorderBrush, ElementName=border}"
                   d:Background="{DynamicResource ThemeAccentBrush}"/>
                   d:Background="{DynamicResource ThemeAccentBrush}"/>
-            <TextBlock ui:Translator.Key="{Binding Unit, ElementName=uc, Converter={converters:EnumToStringConverter}}" TextAlignment="Right"
+            <TextBlock ui:Translator.Key="{Binding Unit, ElementName=uc}" 
+                       TextAlignment="Right"
                        Grid.Column="2" Margin="5,0" VerticalAlignment="Center"
                        Grid.Column="2" Margin="5,0" VerticalAlignment="Center"
             />
             />
         </Grid>
         </Grid>

+ 5 - 45
src/PixiEditor.AvaloniaUI/Views/Input/SizeInput.axaml.cs

@@ -11,7 +11,7 @@ namespace PixiEditor.AvaloniaUI.Views.Input;
 internal partial class SizeInput : UserControl
 internal partial class SizeInput : UserControl
 {
 {
     public static readonly StyledProperty<int> SizeProperty =
     public static readonly StyledProperty<int> SizeProperty =
-        AvaloniaProperty.Register<SizeInput, int>(nameof(Size), defaultValue: 1, coerce: Coerce);
+        AvaloniaProperty.Register<SizeInput, int>(nameof(Size), defaultValue: 1);
 
 
     public static readonly StyledProperty<int> MaxSizeProperty =
     public static readonly StyledProperty<int> MaxSizeProperty =
         AvaloniaProperty.Register<SizeInput, int>(nameof(MaxSize), defaultValue: int.MaxValue);
         AvaloniaProperty.Register<SizeInput, int>(nameof(MaxSize), defaultValue: int.MaxValue);
@@ -19,8 +19,8 @@ internal partial class SizeInput : UserControl
     public static readonly StyledProperty<bool> BehaveLikeSmallEmbeddedFieldProperty =
     public static readonly StyledProperty<bool> BehaveLikeSmallEmbeddedFieldProperty =
         AvaloniaProperty.Register<SizeInput, bool>(nameof(BehaveLikeSmallEmbeddedField), defaultValue: true);
         AvaloniaProperty.Register<SizeInput, bool>(nameof(BehaveLikeSmallEmbeddedField), defaultValue: true);
 
 
-    public static readonly StyledProperty<SizeUnit> UnitProperty =
-        AvaloniaProperty.Register<SizeInput, SizeUnit>(nameof(Unit), defaultValue: SizeUnit.Pixel);
+    public static readonly StyledProperty<string> UnitProperty =
+        AvaloniaProperty.Register<SizeInput, string>(nameof(Unit), defaultValue: "PIXEL_UNIT");
 
 
     public static readonly StyledProperty<bool> FocusNextProperty = AvaloniaProperty.Register<SizeInput, bool>(
     public static readonly StyledProperty<bool> FocusNextProperty = AvaloniaProperty.Register<SizeInput, bool>(
         nameof(FocusNext), defaultValue: true);
         nameof(FocusNext), defaultValue: true);
@@ -58,11 +58,6 @@ internal partial class SizeInput : UserControl
         set => SetValue(BehaveLikeSmallEmbeddedFieldProperty, value);
         set => SetValue(BehaveLikeSmallEmbeddedFieldProperty, value);
     }
     }
 
 
-    static SizeInput()
-    {
-        SizeProperty.Changed.Subscribe(InputSizeChanged);
-    }
-
     public SizeInput()
     public SizeInput()
     {
     {
         InitializeComponent();
         InitializeComponent();
@@ -97,47 +92,12 @@ internal partial class SizeInput : UserControl
             input.Focus();
             input.Focus();
     }
     }
 
 
-    public SizeUnit Unit
+    public string Unit
     {
     {
-        get => (SizeUnit)GetValue(UnitProperty);
+        get => (string)GetValue(UnitProperty);
         set => SetValue(UnitProperty, value);
         set => SetValue(UnitProperty, value);
     }
     }
 
 
-
-    private static int Coerce(AvaloniaObject sender, int value)
-    {
-        if (value <= 0)
-        {
-            return 1;
-        }
-
-        int maxSize = sender.GetValue(MaxSizeProperty);
-        
-        if (value > maxSize)
-        {
-            return maxSize;
-        }
-        
-        return value;
-    }
-
-    private static void InputSizeChanged(AvaloniaPropertyChangedEventArgs<int> e)
-    {
-        int newValue = e.NewValue.Value;
-        int maxSize = (int)e.Sender.GetValue(MaxSizeProperty);
-
-        if (newValue > maxSize)
-        {
-            e.Sender.SetValue(SizeProperty, maxSize);
-
-            return;
-        }
-        else if (newValue <= 0)
-        {
-            e.Sender.SetValue(SizeProperty, 1);
-        }
-    }
-
     private void Border_MouseWheel(object? sender, PointerWheelEventArgs e)
     private void Border_MouseWheel(object? sender, PointerWheelEventArgs e)
     {
     {
         int step = (int)e.Delta.Y / 100;
         int step = (int)e.Delta.Y / 100;

+ 1 - 1
src/PixiEditor.AvaloniaUI/Views/Input/SizePicker.axaml

@@ -55,7 +55,7 @@
                                      x:Name="PercentageSizePicker"
                                      x:Name="PercentageSizePicker"
                                      IsEnabled="{Binding EditingEnabled, ElementName=uc}"
                                      IsEnabled="{Binding EditingEnabled, ElementName=uc}"
                                      Size="{Binding Path=ChosenPercentageSize, ElementName=uc, Mode=TwoWay}"
                                      Size="{Binding Path=ChosenPercentageSize, ElementName=uc, Mode=TwoWay}"
-                                     Unit="Percentage"
+                                     Unit="%"
                                      Margin="-10,0,0,0"
                                      Margin="-10,0,0,0"
                                      MaxSize="9999"
                                      MaxSize="9999"
                                      Width="{Binding Bounds.Width, ElementName=WidthPicker}">
                                      Width="{Binding Bounds.Width, ElementName=WidthPicker}">