first commit

This commit is contained in:
Beyhan Oğur
2026-09-03 04:45:36 +03:00
commit d5f52cc3d3
1038 changed files with 169579 additions and 0 deletions
@@ -0,0 +1,55 @@
<UserControl x:Class="ClassicAssist.UI.Controls.CustomWindowTitleControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:misc="clr-namespace:ClassicAssist.UI.Misc"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="800"
DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}"
Background="{DynamicResource ThemeWindowBackgroundBrush}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Resources/Icons.xaml" />
<ResourceDictionary Source="../../Resources/ResourceDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</ResourceDictionary>
</UserControl.Resources>
<i:Interaction.Behaviors>
<misc:WindowMouseDownBehaviour />
</i:Interaction.Behaviors>
<Grid Margin="5">
<ContentPresenter Content="{Binding AdditionalContent}" DockPanel.Dock="Left" />
<Label DockPanel.Dock="Left" Content="{Binding CustomTitle}" FontSize="16" Margin="5,0,5,0"
Foreground="{DynamicResource ThemeForegroundBrush}" />
<StackPanel DockPanel.Dock="Right" HorizontalAlignment="Right" Orientation="Horizontal">
<Button Background="{DynamicResource ThemeWindowBackgroundBrush}" BorderBrush="Transparent" Height="24"
Width="24" DockPanel.Dock="Right"
IsEnabled="{Binding CanMinimize}">
<i:Interaction.Behaviors>
<misc:WindowCommandBehaviour Command="Minimize" />
</i:Interaction.Behaviors>
<Image Source="{StaticResource minusIcon}" />
</Button>
<Button Background="{DynamicResource ThemeWindowBackgroundBrush}" BorderBrush="Transparent" Height="24"
Width="24" DockPanel.Dock="Right"
IsEnabled="{Binding CanMaximize}">
<i:Interaction.Behaviors>
<misc:WindowCommandBehaviour Command="Maximize" />
</i:Interaction.Behaviors>
<Image Source="{StaticResource squareIcon}" />
</Button>
<Button Background="{DynamicResource ThemeWindowBackgroundBrush}" BorderBrush="Transparent" Height="24"
Width="24" DockPanel.Dock="Right"
IsEnabled="{Binding CanClose}">
<i:Interaction.Behaviors>
<misc:WindowCommandBehaviour Command="Close" />
</i:Interaction.Behaviors>
<Image Source="{StaticResource closeIcon}" />
</Button>
</StackPanel>
</Grid>
</UserControl>
@@ -0,0 +1,65 @@
using System.Windows;
namespace ClassicAssist.UI.Controls
{
/// <summary>
/// Interaction logic for CustomWindowTitleControl.xaml
/// </summary>
public partial class CustomWindowTitleControl
{
public static readonly DependencyProperty AdditionalContentProperty =
DependencyProperty.Register( "AdditionalContent", typeof( object ), typeof( CustomWindowTitleControl ),
new PropertyMetadata( null ) );
public static readonly DependencyProperty CustomTitleProperty = DependencyProperty.Register( "CustomTitle",
typeof( string ), typeof( CustomWindowTitleControl ),
new FrameworkPropertyMetadata( default( string ), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault ) );
public static readonly DependencyProperty CanCloseProperty = DependencyProperty.Register( "CanClose",
typeof( bool ), typeof( CustomWindowTitleControl ),
new FrameworkPropertyMetadata( true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault ) );
public static readonly DependencyProperty CanMinimizeProperty = DependencyProperty.Register( "CanMinimize",
typeof( bool ), typeof( CustomWindowTitleControl ),
new FrameworkPropertyMetadata( true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault ) );
public static readonly DependencyProperty CanMaxmizeProperty = DependencyProperty.Register( "CanMaximize",
typeof( bool ), typeof( CustomWindowTitleControl ),
new FrameworkPropertyMetadata( true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault ) );
public CustomWindowTitleControl()
{
InitializeComponent();
}
public object AdditionalContent
{
get => GetValue( AdditionalContentProperty );
set => SetValue( AdditionalContentProperty, value );
}
public bool CanClose
{
get => (bool) GetValue( CanCloseProperty );
set => SetValue( CanCloseProperty, value );
}
public bool CanMaximize
{
get => (bool) GetValue( CanMaxmizeProperty );
set => SetValue( CanMaxmizeProperty, value );
}
public bool CanMinimize
{
get => (bool) GetValue( CanMinimizeProperty );
set => SetValue( CanMinimizeProperty, value );
}
public string CustomTitle
{
get => (string) GetValue( CustomTitleProperty );
set => SetValue( CustomTitleProperty, value );
}
}
}
@@ -0,0 +1,50 @@
<UserControl x:Name="root" x:Class="ClassicAssist.UI.Controls.EditTextBlock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:valueConverters="clr-namespace:ClassicAssist.UI.Misc.ValueConverters"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<ResourceDictionary>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<DrawingImage x:Key="PencilIcon">
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V512 H512 V0 H0 Z">
<DrawingGroup Opacity="1">
<DrawingGroup Opacity="1">
<GeometryDrawing
Geometry="F1 M512,512z M0,0z M434.214,0L0,434.213 0,512 77.787,512 512,77.786 434.214,0z M65.359,481.998L30.002,481.998 30.002,446.641 50.125,426.518 85.482,461.875 65.359,481.998z M399.206,77.437L434.214,42.429 469.571,77.786 434.563,112.794 399.206,77.437z"
Brush="{Binding Foreground, ElementName=root}" />
</DrawingGroup>
</DrawingGroup>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<valueConverters:PriorityMultiValueConverter x:Key="PriorityMultiValueConverter" />
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<TextBlock x:Name="textBlock" VerticalAlignment="Center"
MouseDown="TextBlock_OnMouseDown" Background="Transparent"
Foreground="{Binding Foreground, ElementName=root}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource PriorityMultiValueConverter}">
<Binding Path="Label" ElementName="root" />
<Binding Path="Text" ElementName="root" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBox x:Name="textBox"
Text="{Binding ElementName=root, Path=Text, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
VerticalAlignment="Center" Visibility="Collapsed" KeyDown="TextBox_OnKeyDown"
LostFocus="TextBox_LostFocus" />
<Button Width="12" Height="12" BorderThickness="0" Background="Transparent" Margin="0" Padding="0"
HorizontalAlignment="Right"
Visibility="{Binding ShowIcon, Converter={StaticResource BooleanToVisibilityConverter}, ElementName=root}"
Click="Button_OnClick" x:Name="pencilButton">
<Image Source="{StaticResource PencilIcon}" Width="16" HorizontalAlignment="Center" />
</Button>
</Grid>
</UserControl>
@@ -0,0 +1,89 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
namespace ClassicAssist.UI.Controls
{
/// <summary>
/// Interaction logic for EditTextBlock.xaml
/// </summary>
public partial class EditTextBlock
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register( "Text", typeof( string ),
typeof( EditTextBlock ),
new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault ) );
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register( "Label",
typeof( string ), typeof( EditTextBlock ),
new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.None ) );
public static readonly DependencyProperty ShowIconProperty = DependencyProperty.Register( "ShowIcon",
typeof( bool ), typeof( EditTextBlock ), new UIPropertyMetadata() );
public EditTextBlock()
{
InitializeComponent();
}
public string Label
{
get => (string) GetValue( LabelProperty );
set => SetValue( LabelProperty, value );
}
public bool ShowIcon
{
get => (bool) GetValue( ShowIconProperty );
set => SetValue( ShowIconProperty, value );
}
public string Text
{
get => (string) GetValue( TextProperty );
set => SetValue( TextProperty, value );
}
private void TextBlock_OnMouseDown( object sender, MouseButtonEventArgs e )
{
if ( e.ClickCount <= 1 )
{
return;
}
ShowTextBox();
}
private void ShowTextBox()
{
textBlock.Visibility = Visibility.Collapsed;
pencilButton.Visibility = Visibility.Collapsed;
textBox.Visibility = Visibility.Visible;
textBox.CaretIndex = textBox.Text.Length;
textBox.SelectAll();
Dispatcher?.BeginInvoke( (Action) ( () => Keyboard.Focus( textBox ) ), DispatcherPriority.Render );
}
private void TextBox_OnKeyDown( object sender, KeyEventArgs e )
{
if ( e.Key == Key.Return )
{
TextBox_LostFocus( sender, null );
}
}
private void TextBox_LostFocus( object sender, RoutedEventArgs e )
{
textBlock.Visibility = Visibility.Visible;
pencilButton.Visibility = ShowIcon ? Visibility.Visible : Visibility.Hidden;
( (TextBox) sender ).Visibility = Visibility.Collapsed;
}
private void Button_OnClick( object sender, RoutedEventArgs e )
{
ShowTextBox();
}
}
}
@@ -0,0 +1,36 @@
<UserControl x:Class="ClassicAssist.UI.Controls.HexDumpControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ClassicAssist.UI.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="600">
<UserControl.Resources>
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Consolas" />
</Style>
</UserControl.Resources>
<StackPanel
DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:HexDumpControl}}}">
<Label Content="{Binding Packet.Title}" />
<Grid>
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy byte[] declaration" Click="Copy_OnClick" />
<MenuItem Header="Replay" Click="Replay_OnClick" />
</ContextMenu>
</Grid.ContextMenu>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding BinaryData}" Style="{StaticResource TextBlockStyle}" Grid.Column="0"
ScrollViewer.VerticalScrollBarVisibility="Disabled" Margin="10,10,0,0" Width="310" />
<TextBlock Text="{Binding TextData}" Style="{StaticResource TextBlockStyle}" Grid.Column="1"
Margin="10,10,0,0" ScrollViewer.VerticalScrollBarVisibility="Disabled" />
</Grid>
<Label Content="{Binding Status}" />
</StackPanel>
</UserControl>
@@ -0,0 +1,154 @@
using System;
using System.Text;
using System.Windows;
using ClassicAssist.Shared;
using ClassicAssist.UI.Misc;
using ClassicAssist.UO.Network.PacketFilter;
namespace ClassicAssist.UI.Controls
{
/// <summary>
/// Interaction logic for HexDumpControl.xaml
/// </summary>
public partial class HexDumpControl
{
public static readonly DependencyProperty PacketProperty = DependencyProperty.Register( "Packet",
typeof( PacketEntry ), typeof( HexDumpControl ), new UIPropertyMetadata() );
public HexDumpControl()
{
InitializeComponent();
}
public string BinaryData
{
get
{
if ( Packet?.Data == null )
{
return string.Empty;
}
StringBuilder binaryBuilder = new StringBuilder();
for ( int i = 0; i < Packet.Length; i++ )
{
byte b1 = (byte) ( Packet.Data[i] >> 4 );
byte b2 = (byte) ( Packet.Data[i] & 0xF );
binaryBuilder.Append( (char) ( b1 > 9 ? b1 + 0x37 : b1 + 0x30 ) );
binaryBuilder.Append( (char) ( b2 > 9 ? b2 + 0x37 : b2 + 0x30 ) );
binaryBuilder.Append( ' ' );
if ( ( i + 1 ) % 16 != 0 )
{
continue;
}
binaryBuilder.Remove( binaryBuilder.Length - 1, 1 );
binaryBuilder.AppendLine();
}
return binaryBuilder.ToString();
}
}
public PacketEntry Packet
{
get => (PacketEntry) GetValue( PacketProperty );
set => SetValue( PacketProperty, value );
}
public string Status
{
get
{
if ( Packet?.Data == null )
{
return "";
}
return "Length: " + Packet.Length;
}
}
public string TextData
{
get
{
if ( Packet?.Data == null )
{
return string.Empty;
}
StringBuilder textBuilder = new StringBuilder();
for ( int i = 0; i < Packet.Length; i++ )
{
byte b1 = Packet.Data[i];
if ( b1 < 0x20 || b1 == 0xB7 || b1 == 0xFF )
{
b1 = (byte) '.';
}
textBuilder.Append( (char) b1 );
if ( ( i + 1 ) % 16 == 0 )
{
textBuilder.AppendLine();
}
}
return textBuilder.ToString();
}
}
private void Copy_OnClick( object sender, RoutedEventArgs e )
{
try
{
string prepend = "byte[] packet = new byte[] { ";
for ( int i = 0; i < Packet.Data.Length; i++ )
{
prepend += $"0x{Packet.Data[i]:X2}";
if ( i + 1 < Packet.Data.Length )
{
prepend += ", ";
}
}
prepend += " };";
Clipboard.SetData( DataFormats.Text, prepend );
}
catch ( Exception )
{
// ignored
}
}
private void Replay_OnClick( object sender, RoutedEventArgs e )
{
if ( Packet == null )
{
return;
}
switch ( Packet.Direction )
{
case PacketDirection.Incoming:
Engine.SendPacketToClient( Packet.Data, Packet.Data.Length );
break;
case PacketDirection.Outgoing:
Engine.SendPacketToServer( Packet.Data, Packet.Data.Length );
break;
case PacketDirection.Any:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
@@ -0,0 +1,21 @@
<UserControl x:Class="ClassicAssist.UI.Controls.HueEntryControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ClassicAssist.UI.Controls"
mc:Ignorable="d"
d:DesignHeight="20" d:DesignWidth="640">
<d:DesignerProperties.DesignStyle>
<Style TargetType="UserControl">
<Setter Property="Background" Value="White" />
</Style>
</d:DesignerProperties.DesignStyle>
<StackPanel Orientation="Horizontal" x:Name="StackPanel">
<StackPanel.Resources>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="Black" />
</Style>
</StackPanel.Resources>
</StackPanel>
</UserControl>
@@ -0,0 +1,56 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using ClassicAssist.UO.Data;
namespace ClassicAssist.UI.Controls
{
/// <summary>
/// Interaction logic for HueEntryControl.xaml
/// </summary>
public partial class HueEntryControl : UserControl
{
public static readonly DependencyProperty HueEntryProperty = DependencyProperty.Register( "HueEntry",
typeof( HueEntry ), typeof( HueEntryControl ),
new FrameworkPropertyMetadata( default( HueEntry ), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
PropertyChangedCallback ) );
public HueEntryControl()
{
InitializeComponent();
}
public HueEntry HueEntry
{
get => (HueEntry) GetValue( HueEntryProperty );
set => SetValue( HueEntryProperty, value );
}
private static void PropertyChangedCallback( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
if ( d is HueEntryControl control )
{
control.SetHues( (HueEntry) e.NewValue );
}
}
private void SetHues( HueEntry entry )
{
for ( int i = 0; i < 32; i++ )
{
SolidColorBrush brush = new SolidColorBrush( Convert555ToARGB( entry.Colors[i] ) );
StackPanel.Children.Add( new Rectangle { Fill = brush, Width = 10 } );
}
}
protected static Color Convert555ToARGB( short Col )
{
int r = ( (short) ( Col >> 10 ) & 31 ) * 8;
int g = ( (short) ( Col >> 5 ) & 31 ) * 8;
int b = ( Col & 31 ) * 8;
return Color.FromArgb( 255, (byte) r, (byte) g, (byte) b );
}
}
}
@@ -0,0 +1,324 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace ClassicAssist.UI.Controls
{
/*
* https://stackoverflow.com/questions/8004981/how-to-make-wpf-wrappanel-child-items-to-stretch
*/
public class StretchyWrapPanel : Panel
{
public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register( nameof( ItemWidth ),
typeof( double ), typeof( StretchyWrapPanel ),
new FrameworkPropertyMetadata( double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure ) );
public static readonly DependencyProperty ItemHeightProperty =
DependencyProperty.Register( nameof( ItemHeight ), typeof( double ), typeof( StretchyWrapPanel ),
new FrameworkPropertyMetadata( double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure ) );
public static readonly DependencyProperty OrientationProperty =
StackPanel.OrientationProperty.AddOwner( typeof( StretchyWrapPanel ),
new FrameworkPropertyMetadata( Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure,
OnOrientationChanged ) );
public static readonly DependencyProperty StretchProportionallyProperty =
DependencyProperty.Register( nameof( StretchProportionally ), typeof( bool ), typeof( StretchyWrapPanel ),
new PropertyMetadata( true, OnStretchProportionallyChanged ) );
private Orientation _orientation = Orientation.Horizontal;
private bool _stretchProportionally = true;
[TypeConverter( typeof( LengthConverter ) )]
public double ItemHeight
{
get => (double) GetValue( ItemHeightProperty );
set => SetValue( ItemHeightProperty, value );
}
[TypeConverter( typeof( LengthConverter ) )]
public double ItemWidth
{
get => (double) GetValue( ItemWidthProperty );
set => SetValue( ItemWidthProperty, value );
}
public Orientation Orientation
{
get => _orientation;
set => SetValue( OrientationProperty, value );
}
public bool StretchProportionally
{
get => _stretchProportionally;
set => SetValue( StretchProportionallyProperty, value );
}
private static void OnOrientationChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
( (StretchyWrapPanel) d )._orientation = (Orientation) e.NewValue;
}
private static void OnStretchProportionallyChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
{
( (StretchyWrapPanel) o )._stretchProportionally = (bool) e.NewValue;
}
protected override Size MeasureOverride( Size constraint )
{
UVSize curLineSize = new UVSize( Orientation );
UVSize panelSize = new UVSize( Orientation );
UVSize uvConstraint = new UVSize( Orientation, constraint.Width, constraint.Height );
double itemWidth = ItemWidth;
double itemHeight = ItemHeight;
bool itemWidthSet = !double.IsNaN( itemWidth );
bool itemHeightSet = !double.IsNaN( itemHeight );
Size childConstraint = new Size( itemWidthSet ? itemWidth : constraint.Width,
itemHeightSet ? itemHeight : constraint.Height );
UIElementCollection children = InternalChildren;
for ( int i = 0, count = children.Count; i < count; i++ )
{
UIElement child = children[i];
if ( child == null )
{
continue;
}
// Flow passes its own constrint to children
child.Measure( childConstraint );
// This is the size of the child in UV space
UVSize sz = new UVSize( Orientation, itemWidthSet ? itemWidth : child.DesiredSize.Width,
itemHeightSet ? itemHeight : child.DesiredSize.Height );
if ( curLineSize.U + sz.U > uvConstraint.U )
{
// Need to switch to another line
panelSize.U = Math.Max( curLineSize.U, panelSize.U );
panelSize.V += curLineSize.V;
curLineSize = sz;
if ( sz.U > uvConstraint.U )
{
// The element is wider then the constrint - give it a separate line
panelSize.U = Math.Max( sz.U, panelSize.U );
panelSize.V += sz.V;
curLineSize = new UVSize( Orientation );
}
}
else
{
// Continue to accumulate a line
curLineSize.U += sz.U;
curLineSize.V = Math.Max( sz.V, curLineSize.V );
}
}
// The last line size, if any should be added
panelSize.U = Math.Max( curLineSize.U, panelSize.U );
panelSize.V += curLineSize.V;
// Go from UV space to W/H space
return new Size( panelSize.Width, panelSize.Height );
}
protected override Size ArrangeOverride( Size finalSize )
{
int firstInLine = 0;
double itemWidth = ItemWidth;
double itemHeight = ItemHeight;
double accumulatedV = 0;
double itemU = Orientation == Orientation.Horizontal ? itemWidth : itemHeight;
UVSize curLineSize = new UVSize( Orientation );
UVSize uvFinalSize = new UVSize( Orientation, finalSize.Width, finalSize.Height );
bool itemWidthSet = !double.IsNaN( itemWidth );
bool itemHeightSet = !double.IsNaN( itemHeight );
bool useItemU = Orientation == Orientation.Horizontal ? itemWidthSet : itemHeightSet;
UIElementCollection children = InternalChildren;
for ( int i = 0, count = children.Count; i < count; i++ )
{
UIElement child = children[i];
if ( child == null )
{
continue;
}
UVSize sz = new UVSize( Orientation, itemWidthSet ? itemWidth : child.DesiredSize.Width,
itemHeightSet ? itemHeight : child.DesiredSize.Height );
if ( curLineSize.U + sz.U > uvFinalSize.U )
{
// Need to switch to another line
if ( !useItemU && StretchProportionally )
{
ArrangeLineProportionally( accumulatedV, curLineSize.V, firstInLine, i, uvFinalSize.Width );
}
else
{
ArrangeLine( accumulatedV, curLineSize.V, firstInLine, i, true,
useItemU ? itemU : uvFinalSize.Width / Math.Max( 1, i - firstInLine - 1 ) );
}
accumulatedV += curLineSize.V;
curLineSize = sz;
if ( sz.U > uvFinalSize.U )
{
// The element is wider then the constraint - give it a separate line
// Switch to next line which only contain one element
if ( !useItemU && StretchProportionally )
{
ArrangeLineProportionally( accumulatedV, sz.V, i, ++i, uvFinalSize.Width );
}
else
{
ArrangeLine( accumulatedV, sz.V, i, ++i, true, useItemU ? itemU : uvFinalSize.Width );
}
accumulatedV += sz.V;
curLineSize = new UVSize( Orientation );
}
firstInLine = i;
}
else
{
// Continue to accumulate a line
curLineSize.U += sz.U;
curLineSize.V = Math.Max( sz.V, curLineSize.V );
}
}
// Arrange the last line, if any
if ( firstInLine < children.Count )
{
if ( !useItemU && StretchProportionally )
{
ArrangeLineProportionally( accumulatedV, curLineSize.V, firstInLine, children.Count,
uvFinalSize.Width );
}
else
{
ArrangeLine( accumulatedV, curLineSize.V, firstInLine, children.Count, true,
useItemU ? itemU : uvFinalSize.Width / Math.Max( 1, children.Count - firstInLine - 1 ) );
}
}
return finalSize;
}
private void ArrangeLineProportionally( double v, double lineV, int start, int end, double limitU )
{
double u = 0d;
bool horizontal = Orientation == Orientation.Horizontal;
UIElementCollection children = InternalChildren;
double total = 0d;
for ( int i = start; i < end; i++ )
{
total += horizontal ? children[i].DesiredSize.Width : children[i].DesiredSize.Height;
}
double uMultipler = limitU / total;
for ( int i = start; i < end; i++ )
{
UIElement child = children[i];
if ( child != null )
{
UVSize childSize = new UVSize( Orientation, child.DesiredSize.Width, child.DesiredSize.Height );
double layoutSlotU = childSize.U * uMultipler;
child.Arrange( new Rect( horizontal ? u : v, horizontal ? v : u, horizontal ? layoutSlotU : lineV,
horizontal ? lineV : layoutSlotU ) );
u += layoutSlotU;
}
}
}
private void ArrangeLine( double v, double lineV, int start, int end, bool useItemU, double itemU )
{
double u = 0d;
bool horizontal = Orientation == Orientation.Horizontal;
UIElementCollection children = InternalChildren;
for ( int i = start; i < end; i++ )
{
UIElement child = children[i];
if ( child != null )
{
UVSize childSize = new UVSize( Orientation, child.DesiredSize.Width, child.DesiredSize.Height );
double layoutSlotU = useItemU ? itemU : childSize.U;
child.Arrange( new Rect( horizontal ? u : v, horizontal ? v : u, horizontal ? layoutSlotU : lineV,
horizontal ? lineV : layoutSlotU ) );
u += layoutSlotU;
}
}
}
private struct UVSize
{
internal UVSize( Orientation orientation, double width, double height )
{
U = V = 0d;
_orientation = orientation;
Width = width;
Height = height;
}
internal UVSize( Orientation orientation )
{
U = V = 0d;
_orientation = orientation;
}
internal double U;
internal double V;
private readonly Orientation _orientation;
internal double Width
{
get => _orientation == Orientation.Horizontal ? U : V;
private set
{
if ( _orientation == Orientation.Horizontal )
{
U = value;
}
else
{
V = value;
}
}
}
internal double Height
{
get => _orientation == Orientation.Horizontal ? V : U;
private set
{
if ( _orientation == Orientation.Horizontal )
{
V = value;
}
else
{
U = value;
}
}
}
}
}
}
+14
View File
@@ -0,0 +1,14 @@
<UserControl x:Class="ClassicAssist.UI.Controls.TextBoxKey"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" x:Name="UserControl">
<Grid>
<TextBox Text="{Binding Shortcut, ElementName=UserControl, Mode=OneWay}"
PreviewKeyDown="TextBox_PreviewKeyDown" IsReadOnly="True" IsReadOnlyCaretVisible="False"
IsUndoEnabled="False" PreviewMouseDown="UIElement_OnPreviewMouseDown"
MouseWheel="UIElement_OnMouseWheel" />
</Grid>
</UserControl>
@@ -0,0 +1,98 @@
using System.Linq;
using System.Windows;
using System.Windows.Input;
using ClassicAssist.Data.Hotkeys;
using Key = ClassicAssist.Misc.Key;
namespace ClassicAssist.UI.Controls
{
/// <inheritdoc cref="TextBox" />
/// <summary>
/// Interaction logic for TestBoxKey.xaml
/// </summary>
public partial class TextBoxKey
{
public static readonly DependencyProperty ShortcutProperty = DependencyProperty.Register( nameof( Shortcut ),
typeof( ShortcutKeys ), typeof( TextBoxKey ),
new FrameworkPropertyMetadata( default( ShortcutKeys ),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault ) );
private readonly Key[] _modifiers =
{
Key.LeftCtrl, Key.RightCtrl, Key.LeftShift, Key.RightShift, Key.LeftAlt, Key.RightAlt
};
public TextBoxKey()
{
InitializeComponent();
}
public Key Key { get; private set; }
public Key Modifier { get; set; }
public object Shortcut
{
get => GetValue( ShortcutProperty );
set => SetValue( ShortcutProperty, value );
}
private Key CheckModifiers()
{
return _modifiers.FirstOrDefault( e => Keyboard.IsKeyDown( (System.Windows.Input.Key) e ) );
}
private bool IsModifier( Key key )
{
return _modifiers.Any( k => key == k );
}
private void TextBox_PreviewKeyDown( object sender, KeyEventArgs e )
{
e.Handled = true;
Key key = (Key) e.Key;
if ( key == Key.System )
{
key = (Key) e.SystemKey;
}
if ( IsModifier( key ) )
{
e.Handled = true;
return;
}
Modifier = CheckModifiers();
Key = key;
Shortcut = new ShortcutKeys { Modifier = Modifier, Key = Key };
}
private void UIElement_OnPreviewMouseDown( object sender, MouseButtonEventArgs e )
{
if ( ( e.ChangedButton == MouseButton.Left ) | ( e.ChangedButton == MouseButton.Right ) )
{
return;
}
e.Handled = true;
Modifier = CheckModifiers();
Shortcut = new ShortcutKeys { Mouse = (MouseOptions) e.ChangedButton };
}
private void UIElement_OnMouseWheel( object sender, MouseWheelEventArgs e )
{
e.Handled = true;
Modifier = CheckModifiers();
Shortcut = e.Delta < 0
? new ShortcutKeys { Mouse = MouseOptions.MouseWheelDown, Modifier = Modifier }
: new ShortcutKeys { Mouse = MouseOptions.MouseWheelUp, Modifier = Modifier };
}
}
}