port from perforce

This commit is contained in:
2026-04-18 22:31:51 +02:00
commit 8d0ab5b7cc
8409 changed files with 3972376 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace Tweaky.PropertyGrid
{
public class EditorTemplateSelector : DataTemplateSelector
{
public EditorTemplateSelector()
{
defaultDataTemplate = new DataTemplate() { VisualTree = new FrameworkElementFactory(typeof(DefaultEditor)) };
}
private DataTemplate defaultDataTemplate;
public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
if (item == null)
return defaultDataTemplate;
if (!(item is PropertyItem))
return defaultDataTemplate;
var pd = (item as PropertyItem).PropertyDescriptor;
var editorAttribute = pd.Attributes.OfType<EditorAttribute>().LastOrDefault();
if (editorAttribute != null)
{
var editorType = Type.GetType(editorAttribute.EditorTypeName);
if (typeof(FrameworkElement).IsAssignableFrom(editorType))
return new DataTemplate() { VisualTree = new FrameworkElementFactory(editorType) };
}
if (pd.PropertyType.IsEnum)
{
return new DataTemplate() { VisualTree = new FrameworkElementFactory(typeof(EnumEditor)) };
}
else if (pd.PropertyType == typeof(System.Drawing.Color))
{
return new DataTemplate() { VisualTree = new FrameworkElementFactory(typeof(FloatArrayColorEditor)) };
}
else if (pd.PropertyType == typeof(float4))
{
return new DataTemplate() { VisualTree = new FrameworkElementFactory(typeof(Float4Editor)) };
}
return defaultDataTemplate;
}
}
}

View File

@@ -0,0 +1,158 @@
<UserControl x:Class="Tweaky.PropertyGrid.PropertyGrid"
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:pg="clr-namespace:Tweaky.PropertyGrid"
xmlns:t="clr-namespace:Tweaky"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
mc:Ignorable="d"
x:Name="Me"
d:DesignHeight="300" d:DesignWidth="300" d:DataContext="{d:DesignInstance Type={x:Type t:DataViewModel}}" >
<UserControl.Resources>
<pg:EditorTemplateSelector x:Key="EditorTemplateSelector" />
<Style x:Key="ClearFilterButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="{DynamicResource ForegroundGradient}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Background="{DynamicResource PseudoTransparent}">
<Border x:Name="hoverBorder" CornerRadius="2" BorderBrush="{DynamicResource SeparatingBorder}" BorderThickness="1" Background="{DynamicResource ForegroundGradient}" Visibility="Collapsed" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="hoverBorder" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="hoverBorder" Property="Background" Value="{DynamicResource BrightGradient}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<CollectionViewSource Source="{Binding PropertyItems, ElementName=Me}" x:Key="collectionView" Filter="CollectionViewSource_Filter" CollectionViewType="ListCollectionView">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Category" Direction="Ascending" />
<scm:SortDescription PropertyName="Index" Direction="Ascending" />
</CollectionViewSource.SortDescriptions>
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.RowSpan="2" Background="{DynamicResource BarGradient}" />
<StackPanel Orientation="Horizontal" Margin="13,0,0,2">
<TextBlock Foreground="{DynamicResource Foreground}" Text="{Binding ItemsSourceName, ElementName=Me}" MinWidth="50" Margin="0,1,0,0" TextBlock.FontWeight="Bold" VerticalAlignment="Center" />
<TextBlock Foreground="{DynamicResource Foreground}" Text="{Binding ItemsSourceType, ElementName=Me}" Margin="5,1,0,0" VerticalAlignment="Center" />
</StackPanel>
<Border Name="searchBoxContainer" Grid.Row="1" Margin="4" BorderThickness="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="Search:" VerticalAlignment="Center" Margin="3,0" Foreground="{DynamicResource Foreground}" />
<TextBox Grid.Column="1" Text="{Binding Filter, ElementName=Me, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Border>
<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.Row="2" CanContentScroll="False">
<ItemsControl x:Name="PART_PropertyItemsControl" IsTabStop="False" Focusable="False" VerticalAlignment="Top" ItemsSource="{Binding Source={StaticResource collectionView}}">
<ItemsControl.Resources>
<Style TargetType="{x:Type ScrollViewer}" BasedOn="{StaticResource {x:Type ScrollViewer}}">
<Setter Property="HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="VerticalScrollBarVisibility" Value="Hidden" />
<Setter Property="CanContentScroll" Value="False" />
</Style>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Expander IsExpanded="True">
<Expander.Header>
<TextBlock Foreground="{DynamicResource Foreground}" Text="{Binding Name}" />
</Expander.Header>
<ItemsPresenter Margin="10,0,0,0" />
</Expander>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type pg:PropertyItem}">
<Border x:Name="MainBorder" ContextMenuService.Placement="Bottom" SnapsToDevicePixels="True" UseLayoutRounding="False" Background="{DynamicResource PseudoTransparent}" PreviewMouseDown="PropertyItem_PreviewMouseDown">
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Name="PART_Name" VerticalAlignment="Stretch" Margin="0,0,0,1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" Foreground="{DynamicResource Foreground}" />
<TextBlock VerticalAlignment="Center" Text=":" Foreground="{DynamicResource Foreground}" />
</StackPanel>
</Grid>
</Border>
<Border Name="PART_Editor" Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="10,0,1,0">
<ContentPresenter ContentTemplateSelector="{StaticResource EditorTemplateSelector}" Content="{Binding}" SnapsToDevicePixels="True" UseLayoutRounding="False" />
<!--<TextBox Name="PART_ValueContainer" MaxLines="1" PreviewMouseWheel="PART_ValueContainer_PreviewMouseWheel" Text="{Binding Value, Mode=TwoWay}" Focusable="False" IsTabStop="False" IsEnabled="{Binding IsReadOnly, Converter={StaticResource InvBooleanToVisibilityConverter}}" TextOptions.TextFormattingMode="Display" />-->
</Border>
</Grid>
</Border>
<DataTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="True">
<Setter TargetName="MainBorder" Property="Border.Background" Value="{DynamicResource LightGradient}" />
</Trigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="MainBorder" Property="Border.Background" Value="{DynamicResource BrightGradient}" />
</DataTrigger>
<!--<Trigger Property="UIElement.IsEnabled" Value="False">
<Setter TargetName="PART_ValueContainer" Value="{DynamicResource DisabledForegroundBrush}" Property="Control.Foreground" />
</Trigger>-->
<Trigger Property="Validation.HasError" Value="True">
<Setter Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}" Property="FrameworkElement.ToolTip" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<GridSplitter Grid.Row="3" Height="5" ResizeBehavior="PreviousAndNext" HorizontalAlignment="Stretch" Background="{DynamicResource PseudoTransparent}" ResizeDirection="Rows" />
<StackPanel Grid.Row="4" Margin="0,0,0,5">
<TextBlock Foreground="{DynamicResource Foreground}" Padding="2 2 2 0" TextBlock.FontWeight="Bold" Text="{Binding SelectedPropertyItem.Name, ElementName=Me}" />
<TextBlock Foreground="{DynamicResource Foreground}" Padding="5 2 2 0" TextWrapping="WrapWithOverflow" Text="{Binding SelectedPropertyItem.Description, ElementName=Me}" />
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Tweaky.PropertyGrid
{
/// <summary>
/// Interaction logic for PropertyGrid.xaml
/// </summary>
public partial class PropertyGrid : UserControl, INotifyPropertyChanged
{
public PropertyGrid()
{
InitializeComponent();
PropertyItems = new ObservableCollection<PropertyItem>();
LeftColumn = new GridLength(150, GridUnitType.Pixel);
RightColumn = new GridLength(1, GridUnitType.Star);
}
public ObservableCollection<PropertyItem> PropertyItems
{
get;
private set;
}
public GridLength LeftColumn
{
get;
[Notify]
set;
}
public GridLength RightColumn
{
get;
[Notify]
set;
}
private string filter;
public string Filter
{
get
{
return filter;
}
[Notify]
set
{
filter = value;
(this.Resources["collectionView"] as CollectionViewSource).View.Refresh();
}
}
public PropertyItem SelectedPropertyItem
{
get { return (PropertyItem)this.GetValue(SelectedPropertyItemProperty); }
[Notify]
set { this.SetValue(SelectedPropertyItemProperty, value); }
}
public static readonly DependencyProperty SelectedPropertyItemProperty = DependencyProperty.Register("SelectedPropertyItem", typeof(PropertyItem), typeof(PropertyGrid), new FrameworkPropertyMetadata(null));
public string ItemsSourceType
{
get { return (string)this.GetValue(ItemsSourceTypeProperty); }
[Notify]
set { this.SetValue(ItemsSourceTypeProperty, value); }
}
public static readonly DependencyProperty ItemsSourceTypeProperty = DependencyProperty.Register("ItemsSourceType", typeof(string), typeof(PropertyGrid), new FrameworkPropertyMetadata(null));
public string ItemsSourceName
{
get { return (string)this.GetValue(ItemsSourceNameProperty); }
[Notify]
set { this.SetValue(ItemsSourceNameProperty, value); }
}
public static readonly DependencyProperty ItemsSourceNameProperty = DependencyProperty.Register("ItemsSourceName", typeof(string), typeof(PropertyGrid), new FrameworkPropertyMetadata(null));
public object ItemsSource
{
get { return this.GetValue(ItemsSourceProperty); }
[Notify]
set { this.SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(PropertyGrid), new FrameworkPropertyMetadata(null, OnItemsSourceChanged));
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var me = d as PropertyGrid;
if (me != null)
{
if (e.OldValue != null && e.OldValue is INotifyPropertyChanged)
(e.OldValue as INotifyPropertyChanged).PropertyChanged -= me.PropertyGrid_PropertyChanged;
UpdatePropertyItems(me.ItemsSource, me.PropertyItems);
me.RaisePropertyChanged(null);
if (e.NewValue != null && e.NewValue is INotifyPropertyChanged)
(e.NewValue as INotifyPropertyChanged).PropertyChanged += me.PropertyGrid_PropertyChanged;
}
}
void PropertyGrid_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Properties")
{
UpdatePropertyItems(ItemsSource, PropertyItems);
RaisePropertyChanged(null);
}
}
public static void UpdatePropertyItems(object itemsSource, IList<PropertyItem> propertyItems)
{
propertyItems.Clear();
int index = 0;
if (itemsSource != null)
{
var properties = (itemsSource is ICustomTypeDescriptor) ? (itemsSource as ICustomTypeDescriptor).GetProperties() : TypeDescriptor.GetProperties(itemsSource);
foreach (PropertyDescriptor pd in properties.Cast<PropertyDescriptor>())
{
var browsableAttribute = pd.Attributes.OfType<BrowsableAttribute>().FirstOrDefault();
if (browsableAttribute != null && !browsableAttribute.Browsable)
continue;
propertyItems.Add(new PropertyItem(itemsSource, index++, pd));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
{
e.Accepted = string.IsNullOrEmpty(this.Filter) || (e.Item as PropertyItem).Name.ToLower().Contains(this.Filter.ToLower());
}
public void PropertyItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var pi = (sender as FrameworkElement).DataContext as PropertyItem;
if (this.SelectedPropertyItem == pi)
return;
if (pi != null)
{
if (this.SelectedPropertyItem != null)
this.SelectedPropertyItem.IsSelected = false;
pi.IsSelected = true;
this.SelectedPropertyItem = pi;
}
}
}
}

View File

@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace Tweaky.PropertyGrid
{
public class PropertyItem : DependencyObject, INotifyPropertyChanged
{
public PropertyItem(object source, int index, PropertyDescriptor pd)
{
this.PropertyDescriptor = pd;
this.Instance = source;
this.Index = index;
this.PropertyItems = new ObservableCollectionEx<PropertyItem>();
if (source is INotifyPropertyChanged)
(source as INotifyPropertyChanged).PropertyChanged += Source_PropertyChanged;
var categoryAttribute = pd.Attributes.OfType<CategoryAttribute>().FirstOrDefault();
var descriptionAttribute = pd.Attributes.OfType<DescriptionAttribute>().FirstOrDefault();
var displayNameAttribute = pd.Attributes.OfType<DisplayNameAttribute>().FirstOrDefault();
var readOnlyAttribute = pd.Attributes.OfType<ReadOnlyAttribute>().FirstOrDefault();
Category = categoryAttribute == null ? "Misc" : categoryAttribute.Category;
Description = descriptionAttribute == null ? "" : descriptionAttribute.Description;
Name = displayNameAttribute == null ? pd.Name : displayNameAttribute.DisplayName;
if (!pd.IsReadOnly && (readOnlyAttribute == null || !readOnlyAttribute.IsReadOnly))
{
IsReadOnly = false;
BindingOperations.SetBinding(this, ValueProperty, new Binding() { Source = source, Path = new PropertyPath(pd.Name) });
}
else
{
IsReadOnly = true;
BindingOperations.SetBinding(this, ValueProperty, new Binding() { Source = source, Path = new PropertyPath(pd.Name), Mode = BindingMode.OneWay });
}
}
void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == this.PropertyDescriptor.Name || string.IsNullOrEmpty(e.PropertyName))
RaisePropertyChanged("Value");
}
public int Index
{
get;
private set;
}
public PropertyItem Parent
{
get;
private set;
}
public object Instance
{
get;
private set;
}
public PropertyDescriptor PropertyDescriptor
{
get;
private set;
}
public string Category
{
get;
private set;
}
public string Description
{
get;
private set;
}
public string Name
{
get;
private set;
}
public bool IsReadOnly
{
get;
private set;
}
public bool IsEditable
{
get
{
return !IsReadOnly;
}
}
public bool IsExpanded
{
get;
[Notify]
set;
}
public bool IsSelected
{
get;
[Notify]
set;
}
public object Value
{
get { return this.GetValue(ValueProperty); }
[Notify]
set { this.SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(PropertyItem), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var pi = (d as PropertyItem);
pi.RaisePropertyChanged("Value");
(pi.Instance as ViewModelBase).RaisePropertyChanged("Name");
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public ObservableCollectionEx<PropertyItem> PropertyItems
{
get;
private set;
}
}
}