namespace Editor; [CustomEditor( typeof( ParticleFloat ) )] public class ParticleFloatControlWidget : ControlWidget { public Color HighlightColor { get; set; } public string Label { get; set; } Layout ControlArea; SerializedObject Target; Button ModeSwitchButton; public ParticleFloatControlWidget( SerializedProperty property ) : this( property, "f", Theme.Green ) { } public ParticleFloatControlWidget( SerializedProperty property, string label, Color color ) : base( property ) { SetSizeMode( SizeMode.Ignore, SizeMode.Default ); if ( !property.TryGetAsObject( out Target ) ) return; Label = label; HighlightColor = color; Layout = Layout.Row(); Layout.Spacing = 3; Layout.AddStretchCell(); ModeSwitchButton = new Button(); ModeSwitchButton.Text = "Mode"; ModeSwitchButton.OnPaintOverride = PaintButton; ModeSwitchButton.Pressed = () => OpenPopup( ModeSwitchButton.ScreenRect ); ModeSwitchButton.FixedWidth = Theme.RowHeight; Layout.Add( ModeSwitchButton ); ControlArea = Layout.AddRow( 1 ); ControlArea.Spacing = 2; Target.OnPropertyChanged += ( p ) => { if ( p.Name != "Type" && p.Name != "Evaluation" ) return; Rebuild(); }; Rebuild(); } private void OpenPopup( Rect parentRect ) { var popup = new ParticleFloatConfigPopup( Target, this ); popup.Position = parentRect.BottomRight; popup.AdjustSize(); popup.Position -= new Vector2( popup.Width, 0 ); popup.Show(); popup.ConstrainToScreen(); } bool PaintButton() { Paint.Antialiasing = true; if ( Paint.HasPressed ) { Paint.SetBrushAndPen( Theme.ControlBackground.Lighten( 0.3f ) ); Paint.DrawRect( Paint.LocalRect.Shrink( 0.5f ), Theme.ControlRadius ); Paint.Pen = Theme.TextControl.Lighten( 0.4f ); } else if ( Paint.HasMouseOver ) { Paint.SetBrushAndPen( Theme.TextControl.Lighten( 0.2f ).WithAlpha( 0.1f ) ); Paint.DrawRect( Paint.LocalRect.Shrink( 1 ), Theme.ControlRadius ); Paint.Pen = Theme.TextControl.Lighten( 0.5f ); } else { Paint.Pen = Theme.TextControl.WithAlpha( 0.5f ); } var type = Target.GetProperty( "Type" ).GetValue(); var eval = Target.GetProperty( "Evaluation" ).GetValue(); var icon = "people"; float iconSize = 15; if ( type == ParticleFloat.ValueType.Constant ) { icon = "radio_button_unchecked"; Paint.Pen = Paint.Pen.WithAlpha( 0.3f ); iconSize = 11; } else { if ( eval == ParticleFloat.EvaluationType.Seed ) { icon = "scatter_plot"; } if ( eval == ParticleFloat.EvaluationType.Life ) { icon = "play_arrow"; } if ( eval == ParticleFloat.EvaluationType.Frame ) { icon = "casino"; } } var iconRect = Paint.DrawIcon( Paint.LocalRect, icon, iconSize, TextFlag.Center ); return true; } void Rebuild() { RebuildForType( Target.GetProperty( "Type" ).GetValue(), Target.GetProperty( "Evaluation" ).GetValue() ); } string GetTypeName( ParticleFloat.ValueType type, ParticleFloat.EvaluationType eval ) { switch ( type ) { case ParticleFloat.ValueType.Constant: return "Constant value"; case ParticleFloat.ValueType.Curve: { switch ( eval ) { case ParticleFloat.EvaluationType.Seed: return "Random from curve per particle"; case ParticleFloat.EvaluationType.Frame: return "Random from curve"; case ParticleFloat.EvaluationType.Life: return "Curve over lifetime"; default: return "Unknown"; } } case ParticleFloat.ValueType.CurveRange: switch ( eval ) { case ParticleFloat.EvaluationType.Seed: return "Random from range, per particle"; case ParticleFloat.EvaluationType.Frame: return "Random from range (per frame)"; case ParticleFloat.EvaluationType.Life: return "path between curve over lifetime, per particle"; default: return "Unknown"; } case ParticleFloat.ValueType.Range: { switch ( eval ) { case ParticleFloat.EvaluationType.Seed: return "Between range, per particle"; case ParticleFloat.EvaluationType.Frame: return "Random between range (per frame)"; case ParticleFloat.EvaluationType.Life: return "Lerp between range over lifetime"; default: return "Unknown"; } } } return "Unknown Combo"; } void RebuildForType( ParticleFloat.ValueType type, ParticleFloat.EvaluationType eval ) { ControlArea.Clear( true ); ModeSwitchButton.ToolTip = GetTypeName( type, eval ); if ( type == ParticleFloat.ValueType.Constant ) { var control = new FloatControlWidget( Target.GetProperty( "ConstantValue" ) ) { HighlightColor = HighlightColor, Label = Label }; ControlArea.Add( control ); } if ( type == ParticleFloat.ValueType.Range ) { var controlA = new FloatControlWidget( Target.GetProperty( "ConstantA" ) ) { HighlightColor = HighlightColor, Label = Label }; ControlArea.Add( controlA ); var controlB = new FloatControlWidget( Target.GetProperty( "ConstantB" ) ) { HighlightColor = HighlightColor, Label = Label }; ControlArea.Add( controlB ); } if ( type == ParticleFloat.ValueType.Curve ) { var controlA = new CurveControlWidget( Target.GetProperty( "CurveA" ) ) { HighlightColor = HighlightColor }; ControlArea.Add( controlA ); } if ( type == ParticleFloat.ValueType.CurveRange ) { var controlA = new CurveRangeControlWidget( Target.GetProperty( "CurveRange" ) ) { HighlightColor = HighlightColor }; ControlArea.Add( controlA ); } Update(); } protected override void OnPaint() { } } /// /// A popup that lets you choose between modes for a Particle Float Controller. /// We can offer more configuration here, eventually.. but for now we're keeping it simple. /// file class ParticleFloatConfigPopup : PopupWidget { SerializedObject SerializedObject; SerializedProperty Type; SerializedProperty Eval; public ParticleFloatConfigPopup( SerializedObject target, Widget parent ) : base( parent ) { Layout = Layout.Column(); Layout.Spacing = 8; Layout.Margin = 16; SerializedObject = target; Type = target.GetProperty( "Type" ); Eval = target.GetProperty( "Evaluation" ); AddQuickModes( Type.GetValue(), Eval.GetValue() ); // tODO - make this window fucking awesome // var dropDown = Create( Type ); // dropDown.FixedWidth = 100; // Layout.Add( dropDown ); // var evalDropDown = Create( Eval ); // evalDropDown.FixedWidth = 100; // Layout.Add( evalDropDown ); } void AddQuickModes( ParticleFloat.ValueType type, ParticleFloat.EvaluationType eval ) { var grid = new GridLayout(); grid.Spacing = 8; grid.AddCell( 0, 0, MakeQuickMode( "radio_button_unchecked", "Constant", "Value is constant. It stays the same. It doesn't change", ParticleFloat.ValueType.Constant, ParticleFloat.EvaluationType.Life ) ); grid.AddCell( 1, 0, MakeQuickMode( "casino", "Random", "Choose a value between two constants every frame", ParticleFloat.ValueType.Range, ParticleFloat.EvaluationType.Frame ) ); grid.AddCell( 0, 1, MakeQuickMode( "hdr_strong", "Range", "Choose a value between two constants", ParticleFloat.ValueType.Range, ParticleFloat.EvaluationType.Seed ) ); grid.AddCell( 1, 1, MakeQuickMode( "animation", "Lerp", "Lerp between two values over the lifetime of the particle", ParticleFloat.ValueType.Range, ParticleFloat.EvaluationType.Life ) ); grid.AddCell( 0, 2, MakeQuickMode( "show_chart", "Curve", "Get the value by querying a curve over the particle's lifetime", ParticleFloat.ValueType.Curve, ParticleFloat.EvaluationType.Life ) ); grid.AddCell( 1, 2, MakeQuickMode( "area_chart", "Curve with Range", "Choose a path between two curves - over the lifetime of the particle", ParticleFloat.ValueType.CurveRange, ParticleFloat.EvaluationType.Life ) ); Layout.Add( grid ); } private Widget MakeQuickMode( string icon, string label, string description, ParticleFloat.ValueType t, ParticleFloat.EvaluationType e ) { var b = new Widget(); b.Cursor = CursorShape.Finger; b.Layout = new IconTitleDescriptionLayout( icon, label, description ); b.Layout.Margin = new Sandbox.UI.Margin( 8, 4 ); b.SetStyles( "color: #ffffff;" ); bool isCurrent = t == Type.GetValue() && e == Eval.GetValue(); b.MouseClick += () => { Type.SetValue( t ); Eval.SetValue( e ); Close(); }; b.OnPaintOverride = () => { if ( isCurrent || Paint.HasMouseOver ) { Paint.SetBrushAndPen( Theme.Blue.Darken( 0.5f ).WithAlpha( 0.5f ) ); Paint.DrawRect( Paint.LocalRect, 4 ); } return true; }; return b; } } file class IconTitleDescriptionLayout : GridLayout { public IconTitleDescriptionLayout( string icon, string title, string description ) { VerticalSpacing = 0; HorizontalSpacing = 8; var iconLabel = AddCell( 0, 0, new IconButton( icon ) { Background = Color.Transparent, IconSize = 33, FixedSize = 40, TransparentForMouseEvents = true }, ySpan: 2 ); var titleLabel = AddCell( 1, 0, new Label( title ) ); var descLabel = AddCell( 1, 1, new Label( description ) { WordWrap = true } ); titleLabel.SetStyles( "font-size: 13px; font-family: Poppins; font-weight: bold;" ); descLabel.SetStyles( "font-size: 9px; font-family: Poppins;" ); descLabel.SetEffectOpacity( 0.5f ); } }