I try to draw defualt inspector for serializable object manually , and Here is my code:
[System.Serializable]
public class Item
{
public string Name;
public int ID;
}
[System.Serializable]
public class ItemGroup
{
public Item item;
public int count;
}
public class Test : MonoBehaviour
{
public Item item;
public ItemGroup itemGroup;
public Item[] items;
public ItemGroup[] itemGroups;
}
The default inspector is look like this:
Then I add the PropertyDrawer script for Item and ItemGroup
[CustomPropertyDrawer(typeof(Item))]
public class ItemDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property, label);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var style = new GUIStyle(EditorStyles.foldoutHeader);
style.fontStyle = FontStyle.Normal;
if (property.isExpanded = EditorGUI.BeginFoldoutHeaderGroup(position, property.isExpanded, label, style))
{
EditorGUI.indentLevel++;
position.height = EditorGUIUtility.singleLineHeight;
position.y += position.height;
EditorGUI.PropertyField(position, property.FindPropertyRelative("Name"));
position.y += position.height;
EditorGUI.PropertyField(position, property.FindPropertyRelative("ID"));
EditorGUI.indentLevel--;
}
EditorGUI.EndFoldoutHeaderGroup();
}
}
[CustomPropertyDrawer(typeof(ItemGroup))]
public class ItemGroupDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property, label);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var style = new GUIStyle(EditorStyles.foldoutHeader);
style.fontStyle = FontStyle.Normal;
var itemProperty = property.FindPropertyRelative("item");
var countProperty = property.FindPropertyRelative("count");
position.height = EditorGUIUtility.singleLineHeight;
if (property.isExpanded = EditorGUI.BeginFoldoutHeaderGroup(position, property.isExpanded, label, style))
{
EditorGUI.indentLevel++;
position.y += position.height;
position.height = EditorGUI.GetPropertyHeight(itemProperty);
EditorGUI.PropertyField(position, itemProperty, true);
position.y += position.height;
position.height = EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(position, countProperty);
EditorGUI.indentLevel--;
}
EditorGUI.EndFoldoutHeaderGroup();
}
}
Question 1: When Enable ItemDrawer and Disable ItemGroupDrawer, the indent of Item field is wrong, see below.How to make it correct both in ItemGroup class and Array?
[CustomPropertyDrawer(typeof(Item))] //Enable ItemDrawer
//[CustomPropertyDrawer(typeof(ItemGroup))] //Disable ItemGroupDrawer
Question 2: When enable both ItemDrawer and ItemGroupDrawer, there is a error:
but the default inspector it did has foldout header
So how to use PropertyDrawer to draw Item and ItemGroup's properties manually that make it look like default inspector?
I don't want to use EditorGUI.PropertyField(position, property);
I want to draw their properties one by one
The indent is wrong since you pass in the original position without any indentation .. the EditorGUI.indentLevel only directly affects the property fields drawn without a rect bit via EditorGUILayout.
You can get an indented rect however by using
var indentedRect = EditorGUI.IndentedRect(position);
However, until now I don't see a real purpose in having a custom drawer for your types .. what's wrong with the default drawer? ;)
thanks derHugo, I have solved it
Answer 1: I should add position = EditorGUI.IndentedRect(position); to the top of OnGUI
Answer 2: I should use EditorGUI.Foldout rather than EditorGUI.BeginFoldoutHeaderGroup
Related
I'm trying to access a boolean from another script, which I using a custom namespace. The idea is to have a checkbox in the inspector per item if it should be animated or not. On a weaponmesh, I have a materialoffset animator that should trigger if that box is checked.
using MFPSEditor;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MFPS.Internal.Structures;
namespace MFPS.Addon.Customizer
{
At the bottom of this script, I have this:
[System.Serializable]
public class GlobalCamo
{
public string Name;
[SpritePreview(50)] public Texture2D Preview;
public MFPSItemUnlockability Unlockability;
public string Description;
public bool isAnimated;
public int Price
{
get => Unlockability.Price;
}
public bool animatedCamo()
{
return isAnimated;
}
public bool isFree() { return Price <= 0; }
[HideInInspector] public Sprite m_sprite = null;
public Sprite spritePreview()
{
if (m_sprite == null && Preview != null)
{
m_sprite = Sprite.Create(Preview, new Rect(0, 0, Preview.width, Preview.height), new Vector2(0.5f, 0.5f));
}
return m_sprite;
}
}
}
I am trying to access the variable "isAnimated" in the script below:
using UnityEngine;
public class MaterialOffsetAnimator : MonoBehaviour
{
// The material that will be animated
Material material;
// The speed at which the offset will be animated
public Vector2 animationSpeed;
void Update()
{
material = GetComponent<Renderer>().material;
if ( isAnimated == true )
{
// Update the material's offset based on the animation speed
material.mainTextureOffset += animationSpeed * Time.deltaTime;
}
else
{
}
}
}
isAnimated is an instance field in your GlobalCamo class, not a static field. You can't access it without a reference to an instance of GlobalCamo.
You can just make the isAnimated field static, like this:
public static bool isAnimated;
And then access it in MaterialOffsetAnimator like this:
if (GlobalCamo.isAnimated)
{
// Your code
}
Also, a side note: You don't need to use == true with booleans. If a boolean is true, the condition will evaluate to true.
A NatTable requires to set cells of column 3 with BACKGROUND_COLOR = GUIHelper.COLOR_GREEN when the property of the Material is of type "Composite".
Material list is the source of data (the DataProvider)
NatTable is configured with StackingTableConfiguration.class
NatTable uses a custom label to set the cell style by means of StackingTableLabelAccumulator.class
As suggested in https://www.eclipse.org/forums/index.php/t/781508/
I have set the cellLabelAccumulator body data layer.
I used AggregateConfigLabelAccumulator since I have the custom label accumulator (AggregateConfigLabelAccumulator) and a columnLabelAccumulator.
When the Material is of type Composite, the label is added to the config labels, but the green color is nor render.
public class StackingTable {
private NatTable natTable;
public static IDataProvider bodyDataProvider;
public static String COLUMN_ONE_LABEL = "ColumnOneLabel";
public static String COLUMN_TWO_LABEL = "ColumnTwoLabel";
public static String COLUMN_THREE_LABEL = "ColumnThreeLabel";
public static String TEST = "Composite_Label";
public StackingTable(Composite parent,
EventList<AncolabStackingLayer> ancolabStackingData,
SelectionLayer stackingTableSelectionLayer ) {
bodyDataProvider = new ListDataProvider<>(ancolabStackingData, colAccessor);
final DataLayer bodyDataLayer = new DataLayer(bodyDataProvider);
final ColumnOverrideLabelAccumulator columnLabelAccumulator =
new ColumnOverrideLabelAccumulator(bodyDataLayer);
columnLabelAccumulator.registerColumnOverrides(0, COLUMN_ONE_LABEL);
columnLabelAccumulator.registerColumnOverrides(1, COLUMN_TWO_LABEL);
columnLabelAccumulator.registerColumnOverrides(2, COLUMN_THREE_LABEL);
//Create the gridLayer
natTable = new NatTable(parent, gridLayer, false);
AggregateConfigLabelAccumulator aggregateConfigLabelAccumulator =
new AggregateConfigLabelAccumulator();
aggregateConfigLabelAccumulator.add(columnLabelAccumulator);
aggregateConfigLabelAccumulator.add(
new StackingTableLabelAccumulator(bodyDataProvider));
bodyDataLayer.setConfigLabelAccumulator(aggregateConfigLabelAccumulator);
bodyDataLayer.addConfiguration(
new DefaultNatTableStyleConfiguration());
bodyDataLayer.addConfiguration(
new StackingTableConfiguration(bodyDataProvider));
bodyDataLayer.addConfiguration(new DefaultEditConfiguration());
bodyDataLayer.addConfiguration(new DefaultEditBindings());
natTable.configure();
}
The configuration registry:
public class StackingTableConfiguration extends AbstractRegistryConfiguration {
private IDataProvider bodyDataProvider;
public StackingTableConfiguration(IDataProvider dp) {
this.bodyDataProvider = dp;
#Override
public void configureRegistry(IConfigRegistry configRegistry) {
//...some configutarion attributes for other columns
Style cellStyle2 = new Style();
cellStyle2.setAttributeValue(
CellStyleAttributes.BACKGROUND_COLOR,
GUIHelper.COLOR_GREEN);
configRegistry.registerConfigAttribute(
CellConfigAttributes.CELL_STYLE, cellStyle2,
DisplayMode.NORMAL, StackingTable.TEST);
}
}
The custom label accumulator:
//Add a label to cells in column 2 when the material of the row is of type = "Composite"
public class StackingTableLabelAccumulator extends AbstractOverrider {
IDataProvider dataProvider;
public StackingTableLabelAccumulator(IDataProvider dataProvider){
this.dataProvider = dataProvider;
}
#Override
public void accumulateConfigLabels(LabelStack configLabels,
int columnPosition, int rowPosition) {
Material mat =
(Material) ((IRowDataProvider) dataProvider).getRowObject(rowPosition);
if(mat.getType().equals("Composite")&& columnPosition == 2) {
configLabels.addLabel(StackingTable.TEST);
//When a material of type composite,
//the code reachs this point, i.e. the label is added to the labelStack
System.out.println(configLabels.getLabels().get(1).toString() +
"\t" + columnPosition + "\t" + rowPosition);
}
}
}
I am currently trying to make a slider in ViewA change the font size of text in viewA and viewB. I have everything bound correctly, but the delegate command is not calling the execute method when the font size property is changed. If I manually call this function everything works as expected, so it is likely one line of code that is the problem. The ViewAViewModel is below:
public class ViewAViewModel : BindableBase
{
private Person _CoolChick = new Person();
private int _fontSize = 12;
private IEventAggregator _eventAggregator;
public DelegateCommand UpdateSizeCommand { get; set; }
public Person CoolChick
{
get
{
return _CoolChick;
}
set
{
SetProperty(ref _CoolChick, value);
}
}
public int FontSize
{
get { return _fontSize; }
set {
Console.WriteLine(_fontSize + " => Font Size");
SetProperty(ref _fontSize, value);
//Execute();
}
}
public ViewAViewModel(IEventAggregator eventAggregator)
{
CoolChick.Age = 25;
CoolChick.Name = "Methalous";
_eventAggregator = eventAggregator;
//likely the problem in this code
UpdateSizeCommand = new DelegateCommand(Execute, CanExecute).ObservesProperty(() => FontSize);
}
private void Execute()
{
_eventAggregator.GetEvent<UpdateEvent>().Publish(FontSize);
}
private bool CanExecute()
{
return true;
}
}
Why would it? You're not calling UpdateSizeCommand.Execute in the setter of your Font property. The command will not invoke unless you bind it to a command property or invoke it manually.
I'm trying to bind a UI element to different model properties A, B and AB. The first two properties A and B are controlled by two sliders. The third property AB is the sum of A and B. For each of the three properties there is a label displaying its value.
Now if I move one of the sliders, the corresponding label updates its Text. But the label for the combined property AB is not updated. Probably no "property changed" event is fired, since there is no setter for AB.
Is there any possibility for binding to such an "aggregated" property?
Here is the bindable object containing the properties A, B and AB:
public class Settings: BindableObject
{
public static readonly BindableProperty AProperty = BindableProperty.Create<Settings, double>(p => p.A, 0);
public static readonly BindableProperty BProperty = BindableProperty.Create<Settings, double>(p => p.B, 0);
public static readonly BindableProperty ABProperty = BindableProperty.Create<Settings, double>(p => p.AB, 0);
public double A {
get{ return (double)GetValue(AProperty); }
set{ SetValue(AProperty, (double)value); }
}
public double B {
get{ return (double)GetValue(BProperty); }
set{ SetValue(BProperty, (double)value); }
}
public double AB {
get{ return A + B; }
}
}
And here is the page containing both sliders and the three labels:
public class App : Application
{
public App()
{
var settings = new Settings();
var sliderA = new Slider();
sliderA.ValueChanged += (sender, e) => settings.A = e.NewValue;
var sliderB = new Slider();
sliderB.ValueChanged += (sender, e) => settings.B = e.NewValue;
var labelA = new Label{ BindingContext = settings };
labelA.SetBinding(Label.TextProperty, "A");
var labelB = new Label{ BindingContext = settings };
labelB.SetBinding(Label.TextProperty, "B");
var labelAB = new Label{ BindingContext = settings };
labelAB.SetBinding(Label.TextProperty, "AB");
MainPage = new ContentPage {
Content = new StackLayout {
VerticalOptions = LayoutOptions.Center,
Children = { sliderA, sliderB, labelA, labelB, labelAB },
},
};
}
}
This is what the running application looks like on iOS:
The last label should display the sum of the first two numbers.
Edit:
I wonder why I can't write
public static readonly BindableProperty ABProperty =
BindableProperty.Create<Settings, double>(p => p.A + p.B, 0);
But this yields the run-time error "System.TypeInitializationException: An exception was thrown by the type initializer for AggregatedBindablePropertyMnml.Settings ---> System.Exception: getter must be a MemberExpression"
Based on the suggestion from Taekahn (updating AB within the setters of A and B) I came up with the following solution.
By overriding the OnPropertyChanged method and setting the ABProperty, the bound label text is updated as well. In contrast to modifying each setter individually, this way we only need to modify the Settings class at one place.
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
SetValue(ABProperty, A + B);
}
Now both sliders impact the third label:
I have this method fired on a button click. MyObj is an extended Control type with two properties CenterX and CenterY with backing dp CenterXProperty and CenterYProperty.
With the animation playing the CenterX and CenterY properties of MyObj are changing, but I can't see any movement of the object.
private void MoveMyObjectsWithAnimation(MyObj item, Point point)
{
Duration duration = new Duration(TimeSpan.FromSeconds(3));
Storyboard sb = new Storyboard();
sb.Duration = duration;
DoubleAnimation da = new DoubleAnimation();
da.Duration = duration;
da.EasingFunction = new SineEase();
DoubleAnimation da1 = new DoubleAnimation();
da1.Duration = duration;
da1.EasingFunction = new SineEase();
Storyboard.SetTarget(da, item);
Storyboard.SetTargetProperty(da, new PropertyPath(MyObj.CenterXProperty));
da.From = item.CenterX;
da.To = point.X;
Storyboard.SetTarget(da1, item);
Storyboard.SetTargetProperty(da1, new PropertyPath(MyObj.CenterYProperty));
da1.From = item.CenterY;
da1.To = point.Y;
sb.Children.Add(da);
sb.Children.Add(da1);
sb.Begin();
}
Following is the property system declared in MyObj class.
public double CenterX
{
get
{
return (double)GetValue(CenterXProperty)+25;
}
set
{
Canvas.SetLeft(this, value - 25);
SetValue(CenterXProperty, value);
OnPropertyChanged(new PropertyChangedEventArgs("CenterX"));
}
}
public double CenterY
{
get
{
return (double)GetValue(CenterYProperty) + 25;
}
set
{
Canvas.SetTop(this, value - 25);
SetValue(CenterYProperty, value);
OnPropertyChanged(new PropertyChangedEventArgs("CenterY"));
}
}
public static readonly DependencyProperty CenterXProperty =
DependencyProperty.Register("CenterX", typeof(double), typeof(MyObj), null);
public static readonly DependencyProperty CenterYProperty =
DependencyProperty.Register("CenterY", typeof(double), typeof(MyObj), null);
What am I doing wrong?
This was a tough one as your code appeared correct.
The problem is actually that the DP setter is not called when a property is animated. Your property setters were never hit.
You can only catch the callback on the property change instead.
This works:
public double CenterX
{
get { return (double)GetValue(CentreXProperty); }
set { SetValue(CentreXProperty, value); }
}
// Using a DependencyProperty as the backing store for CentreX.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty CentreXProperty =
DependencyProperty.Register("CentreX",
typeof(double),
typeof(MyObj),
new PropertyMetadata(0.0,
new PropertyChangedCallback(OnCentreXChanged)));
static void OnCentreXChanged(object sender, DependencyPropertyChangedEventArgs args)
{
// Get reference to self
MyObj source = (MyObj)sender;
// Add Handling Code
double newValue = (double)args.NewValue;
Canvas.SetTop(source, newValue - 25);
}
Just duplicate for your CenterYProperty.
I suggest you get a hold of the SLDP snippet from here and always use that to add DPs. (There is one typo in the code it generates, but the snippet it easy to fix).