From feca1c7994886dec467491c51cd42f9064126c52 Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 3 Mar 2026 23:44:24 +0100 Subject: [PATCH 1/5] replace HW with material instead of sprite --- Source/Editor/EditorAssets.cs | 5 +++++ Source/Editor/EditorIcons.cs | 1 - Source/Editor/GUI/Dialogs/ColorSelector.cs | 12 +++++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs index d9b3d7f18..447dc3375 100644 --- a/Source/Editor/EditorAssets.cs +++ b/Source/Editor/EditorAssets.cs @@ -69,6 +69,11 @@ namespace FlaxEditor /// public static string WindowIcon = "Editor/EditorIcon"; + /// + /// The material used for the HS color wheel. + /// + public static string HSWheelMaterial = "Editor/HSWheel"; + /// /// The window icons font. /// diff --git a/Source/Editor/EditorIcons.cs b/Source/Editor/EditorIcons.cs index 8688c502a..7da1d6fb3 100644 --- a/Source/Editor/EditorIcons.cs +++ b/Source/Editor/EditorIcons.cs @@ -134,7 +134,6 @@ namespace FlaxEditor public SpriteHandle Document128; public SpriteHandle XBoxOne128; public SpriteHandle UWPStore128; - public SpriteHandle ColorWheel128; public SpriteHandle LinuxSettings128; public SpriteHandle NavigationSettings128; public SpriteHandle AudioSettings128; diff --git a/Source/Editor/GUI/Dialogs/ColorSelector.cs b/Source/Editor/GUI/Dialogs/ColorSelector.cs index f556e8cd6..79e4029f5 100644 --- a/Source/Editor/GUI/Dialogs/ColorSelector.cs +++ b/Source/Editor/GUI/Dialogs/ColorSelector.cs @@ -12,6 +12,8 @@ namespace FlaxEditor.GUI.Dialogs /// public class ColorSelector : ContainerControl { + private const String GrayedOutParamName = "GrayedOut"; + /// /// The color. /// @@ -22,7 +24,7 @@ namespace FlaxEditor.GUI.Dialogs /// protected Rectangle _wheelRect; - private readonly SpriteHandle _colorWheelSprite; + private readonly MaterialBase _hsWheelMaterial; private bool _isMouseDownWheel; /// @@ -78,7 +80,8 @@ namespace FlaxEditor.GUI.Dialogs { AutoFocus = true; - _colorWheelSprite = Editor.Instance.Icons.ColorWheel128; + _hsWheelMaterial = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.HSWheelMaterial); + _hsWheelMaterial = _hsWheelMaterial.CreateVirtualInstance(); _wheelRect = new Rectangle(0, 0, wheelSize, wheelSize); } @@ -168,9 +171,12 @@ namespace FlaxEditor.GUI.Dialogs var hsv = _color.ToHSV(); bool enabled = EnabledInHierarchy; + _hsWheelMaterial.SetParameterValue(GrayedOutParamName, enabled ? 1.0f : 0.5f); + Render2D.DrawMaterial(_hsWheelMaterial, _wheelRect, enabled ? Color.White : Color.Gray); + // Wheel float boxExpand = (2.0f * 4.0f / 128.0f) * _wheelRect.Width; - Render2D.DrawSprite(_colorWheelSprite, _wheelRect.MakeExpanded(boxExpand), enabled ? Color.White : Color.Gray); + Render2D.DrawMaterial(_hsWheelMaterial, _wheelRect, enabled ? Color.White : Color.Gray); float hAngle = hsv.X * Mathf.DegreesToRadians; float hRadius = hsv.Y * _wheelRect.Width * 0.5f; var hsPos = new Float2(hRadius * Mathf.Cos(hAngle), -hRadius * Mathf.Sin(hAngle)); From b9b11b3c2a967f91dc03645bf2db5ce12e664261 Mon Sep 17 00:00:00 2001 From: Saas Date: Tue, 3 Mar 2026 23:49:16 +0100 Subject: [PATCH 2/5] loop Hue value box --- Source/Editor/GUI/Dialogs/ColorPickerDialog.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 2d6a3882b..46970c50e 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -160,7 +160,7 @@ namespace FlaxEditor.GUI.Dialogs _cAlpha.ValueChanged += OnRGBAChanged; // Hue - _cHue = new FloatValueBox(0, PickerMargin + HSVMargin + ChannelTextWidth, _cSelector.Bottom + PickerMargin, 100, 0, 360) + _cHue = new FloatValueBox(0, PickerMargin + HSVMargin + ChannelTextWidth, _cSelector.Bottom + PickerMargin, 100) { Parent = this }; @@ -306,6 +306,7 @@ namespace FlaxEditor.GUI.Dialogs if (_disableEvents) return; + _cHue.Value = Mathf.Wrap(_cHue.Value, 0f, 360f); SelectedColor = Color.FromHSV(_cHue.Value, _cSaturation.Value / 100.0f, _cValue.Value / 100.0f, _cAlpha.Value); } From 66b4c64f989802ee6e599dbd0d5f8df4415a3cb6 Mon Sep 17 00:00:00 2001 From: Saas Date: Wed, 4 Mar 2026 15:17:47 +0100 Subject: [PATCH 3/5] polish sliders and wheel - Hide mouse cursor when clicking on wheel or sliders - Move mouse position to knob position when letting go of slider or wheel - Show mouse again when letting go of slider or wheel (obviously) - Provide some visual feedback when the clicks on the wheel or sliders - Make sliders wider - Add alpha grid background to alpha slider --- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 4 +- Source/Editor/GUI/Dialogs/ColorSelector.cs | 146 ++++++++++++------ 2 files changed, 103 insertions(+), 47 deletions(-) diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 46970c50e..f528710cd 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -124,7 +124,7 @@ namespace FlaxEditor.GUI.Dialogs _savedColors = JsonSerializer.Deserialize>(savedColors); // Selector - _cSelector = new ColorSelectorWithSliders(180, 18) + _cSelector = new ColorSelectorWithSliders(180, 21) { Location = new Float2(PickerMargin, PickerMargin), Parent = this @@ -421,9 +421,7 @@ namespace FlaxEditor.GUI.Dialogs public override bool OnMouseUp(Float2 location, MouseButton button) { if (base.OnMouseUp(location, button)) - { return true; - } var child = GetChildAtRecursive(location); if (button == MouseButton.Right && child is Button b && b.Tag is Color c) diff --git a/Source/Editor/GUI/Dialogs/ColorSelector.cs b/Source/Editor/GUI/Dialogs/ColorSelector.cs index 79e4029f5..392a8d934 100644 --- a/Source/Editor/GUI/Dialogs/ColorSelector.cs +++ b/Source/Editor/GUI/Dialogs/ColorSelector.cs @@ -14,6 +14,11 @@ namespace FlaxEditor.GUI.Dialogs { private const String GrayedOutParamName = "GrayedOut"; + /// + /// Offset value applied to mouse cursor position when the user lets go of wheel or sliders. + /// + protected const float MouseCursorOffset = 6.0f; + /// /// The color. /// @@ -27,6 +32,19 @@ namespace FlaxEditor.GUI.Dialogs private readonly MaterialBase _hsWheelMaterial; private bool _isMouseDownWheel; + private Rectangle wheelDragRect + { + get + { + var hsv = _color.ToHSV(); + float hAngle = hsv.X * Mathf.DegreesToRadians; + float hRadius = hsv.Y * _wheelRect.Width * 0.5f; + var hsPos = new Float2(hRadius * Mathf.Cos(hAngle), -hRadius * Mathf.Sin(hAngle)); + float wheelBoxSize = IsSliding ? 9.0f : 5.0f; + return new Rectangle(hsPos - (wheelBoxSize * 0.5f) + _wheelRect.Center, new Float2(wheelBoxSize)); + } + } + /// /// Occurs when selected color gets changed. /// @@ -168,7 +186,6 @@ namespace FlaxEditor.GUI.Dialogs { base.Draw(); - var hsv = _color.ToHSV(); bool enabled = EnabledInHierarchy; _hsWheelMaterial.SetParameterValue(GrayedOutParamName, enabled ? 1.0f : 0.5f); @@ -177,11 +194,10 @@ namespace FlaxEditor.GUI.Dialogs // Wheel float boxExpand = (2.0f * 4.0f / 128.0f) * _wheelRect.Width; Render2D.DrawMaterial(_hsWheelMaterial, _wheelRect, enabled ? Color.White : Color.Gray); - float hAngle = hsv.X * Mathf.DegreesToRadians; - float hRadius = hsv.Y * _wheelRect.Width * 0.5f; - var hsPos = new Float2(hRadius * Mathf.Cos(hAngle), -hRadius * Mathf.Sin(hAngle)); - const float wheelBoxSize = 4.0f; - Render2D.DrawRectangle(new Rectangle(hsPos - (wheelBoxSize * 0.5f) + _wheelRect.Center, new Float2(wheelBoxSize)), _isMouseDownWheel ? Color.Gray : Color.Black); + Color hsColor = Color.FromHSV(new Float3(Color.ToHSV().X, 1, 1)); + Rectangle wheelRect = wheelDragRect; + Render2D.FillRectangle(wheelRect, hsColor); + Render2D.DrawRectangle(wheelRect, _isMouseDownWheel ? Color.Gray : Color.Black, _isMouseDownWheel ? 3.0f : 1.0f); } /// @@ -208,6 +224,7 @@ namespace FlaxEditor.GUI.Dialogs if (!_isMouseDownWheel) { _isMouseDownWheel = true; + Cursor = CursorType.Hidden; StartMouseCapture(); SlidingStart?.Invoke(); } @@ -224,6 +241,10 @@ namespace FlaxEditor.GUI.Dialogs if (button == MouseButton.Left && _isMouseDownWheel) { EndMouseCapture(); + // Make the cursor appear where the user expects it to be (position of selection rectangle) + Rectangle dragRect = wheelDragRect; + Root.MousePosition = dragRect.Center + MouseCursorOffset; + Cursor = CursorType.Default; EndSliding(); return true; } @@ -252,10 +273,10 @@ namespace FlaxEditor.GUI.Dialogs /// public class ColorSelectorWithSliders : ColorSelector { - private Rectangle _slider1Rect; - private Rectangle _slider2Rect; - private bool _isMouseDownSlider1; - private bool _isMouseDownSlider2; + private Rectangle _valueSliderRect; + private Rectangle _alphaSliderRect; + private bool _isMouseDownValueSlider; + private bool _isMouseDownAlphaSlider; /// /// Initializes a new instance of the class. @@ -266,26 +287,26 @@ namespace FlaxEditor.GUI.Dialogs : base(wheelSize) { // Setup dimensions - const float slidersMargin = 8.0f; - _slider1Rect = new Rectangle(wheelSize + slidersMargin, 0, slidersThickness, wheelSize); - _slider2Rect = new Rectangle(_slider1Rect.Right + slidersMargin, _slider1Rect.Y, slidersThickness, _slider1Rect.Height); - Size = new Float2(_slider2Rect.Right, wheelSize); + const float slidersMargin = 10.0f; + _valueSliderRect = new Rectangle(wheelSize + slidersMargin, 0, slidersThickness, wheelSize); + _alphaSliderRect = new Rectangle(_valueSliderRect.Right + slidersMargin * 1.5f, _valueSliderRect.Y, slidersThickness, _valueSliderRect.Height); + Size = new Float2(_alphaSliderRect.Right, wheelSize); } /// protected override void UpdateMouse(ref Float2 location) { - if (_isMouseDownSlider1) + if (_isMouseDownValueSlider) { var hsv = _color.ToHSV(); - hsv.Z = 1.0f - Mathf.Saturate((location.Y - _slider1Rect.Y) / _slider1Rect.Height); + hsv.Z = 1.0f - Mathf.Saturate((location.Y - _valueSliderRect.Y) / _valueSliderRect.Height); Color = Color.FromHSV(hsv, _color.A); } - else if (_isMouseDownSlider2) + else if (_isMouseDownAlphaSlider) { var color = _color; - color.A = 1.0f - Mathf.Saturate((location.Y - _slider2Rect.Y) / _slider2Rect.Height); + color.A = 1.0f - Mathf.Saturate((location.Y - _alphaSliderRect.Y) / _alphaSliderRect.Height); Color = color; } @@ -306,32 +327,61 @@ namespace FlaxEditor.GUI.Dialogs var hs = hsv; hs.Z = 1.0f; Color hsC = Color.FromHSV(hs); - const float slidersOffset = 3.0f; - const float slidersThickness = 4.0f; - // Value - float valueY = _slider2Rect.Height * (1 - hsv.Z); - var valueR = new Rectangle(_slider1Rect.X - slidersOffset, _slider1Rect.Y + valueY - slidersThickness / 2, _slider1Rect.Width + slidersOffset * 2, slidersThickness); - Render2D.FillRectangle(_slider1Rect, hsC, hsC, Color.Black, Color.Black); - Render2D.DrawRectangle(_slider1Rect, _isMouseDownSlider1 ? style.BackgroundSelected : Color.Black); - Render2D.DrawRectangle(valueR, _isMouseDownSlider1 ? Color.White : Color.Gray); + // Value slider + float valueKnobExpand = _isMouseDownValueSlider ? 10.0f : 4.0f; + float valueY = _valueSliderRect.Height * (1 - hsv.Z); + float valueKnobWidth = _valueSliderRect.Width + valueKnobExpand; + float valueKnobHeight = _isMouseDownValueSlider ? 7.0f : 4.0f; + float valueKnobX = _valueSliderRect.X - valueKnobExpand * 0.5f; + float valueKnobY = _valueSliderRect.Y + valueY - valueKnobHeight * 0.5f; + Rectangle valueKnobRect = new Rectangle(valueKnobX, valueKnobY, valueKnobWidth, valueKnobHeight); + Render2D.FillRectangle(_valueSliderRect, hsC, hsC, Color.Black, Color.Black); + // Draw one black and one white border to make the knob visible at any saturation level + Render2D.DrawRectangle(valueKnobRect, Color.White, _isMouseDownValueSlider ? 3.0f : 2.0f); + Render2D.DrawRectangle(valueKnobRect, Color.Black, _isMouseDownValueSlider ? 2.0f : 1.0f); - // Alpha - float alphaY = _slider2Rect.Height * (1 - _color.A); - var alphaR = new Rectangle(_slider2Rect.X - slidersOffset, _slider2Rect.Y + alphaY - slidersThickness / 2, _slider2Rect.Width + slidersOffset * 2, slidersThickness); + // Draw checkerboard pattern as background of alpha slider + Render2D.FillRectangle(_alphaSliderRect, Color.White); + var smallRectSize = _alphaSliderRect.Width / 2.0f; + var numHor = Mathf.CeilToInt(_alphaSliderRect.Width / smallRectSize); + var numVer = Mathf.CeilToInt(_alphaSliderRect.Height / smallRectSize); + Render2D.PushClip(_alphaSliderRect); + for (int i = 0; i < numHor; i++) + { + for (int j = 0; j < numVer; j++) + { + if ((i + j) % 2 == 0) + { + var rect = new Rectangle(_alphaSliderRect.X + smallRectSize * i, _alphaSliderRect.Y + smallRectSize * j, new Float2(smallRectSize)); + Render2D.FillRectangle(rect, Color.Gray); + } + } + } + Render2D.PopClip(); + + // Alpha slider + float alphaKnobExpand = _isMouseDownAlphaSlider ? 10.0f : 4.0f; + float alphaY = _alphaSliderRect.Height * (1 - _color.A); + float alphaKnobWidth = _alphaSliderRect.Width + alphaKnobExpand; + float alphaKnobHeight = _isMouseDownAlphaSlider ? 7.0f : 4.0f; + float alphaKnobX = _alphaSliderRect.X - alphaKnobExpand * 0.5f; + float alphaKnobY = _alphaSliderRect.Y + alphaY - alphaKnobExpand * 0.5f; + Rectangle alphaKnobRect = new Rectangle(alphaKnobX, alphaKnobY, alphaKnobWidth, alphaKnobHeight); var color = _color; - color.A = 1; // Keep slider 2 fill rect from changing color alpha while selecting. - Render2D.FillRectangle(_slider2Rect, color, color, Color.Transparent, Color.Transparent); - Render2D.DrawRectangle(_slider2Rect, _isMouseDownSlider2 ? style.BackgroundSelected : Color.Black); - Render2D.DrawRectangle(alphaR, _isMouseDownSlider2 ? Color.White : Color.Gray); + color.A = 1; // Prevent alpha slider fill from becoming transparent + Render2D.FillRectangle(_alphaSliderRect, color, color, Color.Transparent, Color.Transparent); + // Draw one black and one white border to make the knob visible at any saturation level + Render2D.DrawRectangle(alphaKnobRect, Color.White, _isMouseDownAlphaSlider ? 3.0f : 2.0f); + Render2D.DrawRectangle(alphaKnobRect, Color.Black, _isMouseDownAlphaSlider ? 2.0f : 1.0f); } /// public override void OnLostFocus() { // Clear flags - _isMouseDownSlider1 = false; - _isMouseDownSlider2 = false; + _isMouseDownValueSlider = false; + _isMouseDownAlphaSlider = false; base.OnLostFocus(); } @@ -339,15 +389,17 @@ namespace FlaxEditor.GUI.Dialogs /// public override bool OnMouseDown(Float2 location, MouseButton button) { - if (button == MouseButton.Left && _slider1Rect.Contains(location)) + if (button == MouseButton.Left && _valueSliderRect.Contains(location)) { - _isMouseDownSlider1 = true; + _isMouseDownValueSlider = true; + Cursor = CursorType.Hidden; StartMouseCapture(); UpdateMouse(ref location); } - if (button == MouseButton.Left && _slider2Rect.Contains(location)) + if (button == MouseButton.Left && _alphaSliderRect.Contains(location)) { - _isMouseDownSlider2 = true; + _isMouseDownAlphaSlider = true; + Cursor = CursorType.Hidden; StartMouseCapture(); UpdateMouse(ref location); } @@ -358,10 +410,16 @@ namespace FlaxEditor.GUI.Dialogs /// public override bool OnMouseUp(Float2 location, MouseButton button) { - if (button == MouseButton.Left && (_isMouseDownSlider1 || _isMouseDownSlider2)) + if (button == MouseButton.Left && (_isMouseDownValueSlider || _isMouseDownAlphaSlider)) { - _isMouseDownSlider1 = false; - _isMouseDownSlider2 = false; + // Make the cursor appear where the user expects it to be (center of slider horizontally and slider knob position vertically) + float sliderCenter = _isMouseDownValueSlider ? _valueSliderRect.Center.X : _alphaSliderRect.Center.X; + // Calculate y position based on the slider knob to avoid incrementing value by a small amount when the user repeatedly clicks the slider (f.e. when moving in small steps) + float mouseSliderPosition = _isMouseDownValueSlider ? _valueSliderRect.Y + _valueSliderRect.Height * (1 - _color.ToHSV().Z) + MouseCursorOffset : _alphaSliderRect.Y + _alphaSliderRect.Height * (1 - _color.A) + MouseCursorOffset; + Root.MousePosition = new Float2(sliderCenter + MouseCursorOffset, Mathf.Clamp(mouseSliderPosition, _valueSliderRect.Top, _valueSliderRect.Bottom)); + Cursor = CursorType.Default; + _isMouseDownValueSlider = false; + _isMouseDownAlphaSlider = false; EndMouseCapture(); return true; } @@ -373,8 +431,8 @@ namespace FlaxEditor.GUI.Dialogs public override void OnEndMouseCapture() { // Clear flags - _isMouseDownSlider1 = false; - _isMouseDownSlider2 = false; + _isMouseDownValueSlider = false; + _isMouseDownAlphaSlider = false; base.OnEndMouseCapture(); } From c62b3f7624b248eb64a2366809e33ad0f813169b Mon Sep 17 00:00:00 2001 From: Saas Date: Wed, 4 Mar 2026 17:53:27 +0100 Subject: [PATCH 4/5] improve color picker layout and accept color behaviour - Color will accept on close dialog or focus loss - Color will cancel on ESC press - Increased saved colors count from 8 to 10 - Moved RGB and HSV into tabbed interface - Moved eyedropper icon - Removed "Auto Accept Color Picker Change" option since there are no "Cancel" or "OK" button anymore - Made sure to reset cursor type when the color picker is closed --- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 257 ++++++++++-------- Source/Editor/Options/InterfaceOptions.cs | 7 - 2 files changed, 150 insertions(+), 114 deletions(-) diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index f528710cd..ab002505f 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -1,10 +1,11 @@ // Copyright (c) Wojciech Figat. All rights reserved. +using System.Collections.Generic; using FlaxEditor.GUI.Input; +using FlaxEditor.GUI.Tabs; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; -using System.Collections.Generic; namespace FlaxEditor.GUI.Dialogs { @@ -25,15 +26,16 @@ namespace FlaxEditor.GUI.Dialogs /// public class ColorPickerDialog : Dialog, IColorPickerDialog { - private const float ButtonsWidth = 60.0f; private const float PickerMargin = 6.0f; - private const float EyedropperMargin = 8.0f; - private const float RGBAMargin = 12.0f; - private const float HSVMargin = 0.0f; private const float ChannelsMargin = 4.0f; private const float ChannelTextWidth = 12.0f; private const float SavedColorButtonWidth = 20.0f; private const float SavedColorButtonHeight = 20.0f; + private const float TabHeight = 20; + private const float ValueBoxesWidth = 100.0f; + private const float HSVRGBTextWidth = 15.0f; + private const float ColorPreviewHeight = 50.0f; + private const int SavedColorsAmount = 10; private Color _initialValue; private Color _value; @@ -45,16 +47,19 @@ namespace FlaxEditor.GUI.Dialogs private ColorValueBox.ColorPickerClosedEvent _onClosed; private ColorSelectorWithSliders _cSelector; + private Tabs.Tabs _hsvRGBTabs; + private Tab _RGBTab; + private Panel _rgbPanel; private FloatValueBox _cRed; private FloatValueBox _cGreen; private FloatValueBox _cBlue; - private FloatValueBox _cAlpha; + private Tab _hsvTab; + private Panel _hsvPanel; private FloatValueBox _cHue; private FloatValueBox _cSaturation; private FloatValueBox _cValue; private TextBox _cHex; - private Button _cCancel; - private Button _cOK; + private FloatValueBox _cAlpha; private Button _cEyedropper; private List _savedColors = new List(); @@ -131,90 +136,103 @@ namespace FlaxEditor.GUI.Dialogs }; _cSelector.ColorChanged += x => SelectedColor = x; - // Red - _cRed = new FloatValueBox(0, _cSelector.Right + PickerMargin + RGBAMargin + ChannelTextWidth, PickerMargin, 100, 0, float.MaxValue, 0.001f) + _hsvRGBTabs = new Tabs.Tabs { - Parent = this + Location = new Float2(_cSelector.Right + 30.0f, PickerMargin), + TabsTextHorizontalAlignment = TextAlignment.Center, + Width = ValueBoxesWidth + HSVRGBTextWidth * 2.0f + ChannelsMargin * 2.0f, + Height = (FloatValueBox.DefaultHeight + ChannelsMargin) * 4 + ChannelsMargin, + Parent = this, + }; + _hsvRGBTabs.TabsSize = new Float2(_hsvRGBTabs.Width * 0.5f, TabHeight); + _hsvRGBTabs.SelectedTabChanged += SelectedTabChanged; + + // RGB Tab + _RGBTab = _hsvRGBTabs.AddTab(new Tab("RGB")); + _rgbPanel = new Panel(ScrollBars.Vertical) + { + AnchorPreset = AnchorPresets.StretchAll, + Offsets = Margin.Zero, + Parent = _RGBTab, + }; + + // HSV Tab + _hsvTab = _hsvRGBTabs.AddTab(new Tab("HSV")); + _hsvPanel = new Panel(ScrollBars.Vertical) + { + AnchorPreset = AnchorPresets.StretchAll, + Offsets = Margin.Zero, + Parent = _hsvTab, + }; + + // Red + _cRed = new FloatValueBox(0, HSVRGBTextWidth + ChannelsMargin, PickerMargin, ValueBoxesWidth, 0, float.MaxValue, 0.001f) + { + Parent = _rgbPanel, }; _cRed.ValueChanged += OnRGBAChanged; // Green _cGreen = new FloatValueBox(0, _cRed.X, _cRed.Bottom + ChannelsMargin, _cRed.Width, 0, float.MaxValue, 0.001f) { - Parent = this + Parent = _rgbPanel, }; _cGreen.ValueChanged += OnRGBAChanged; // Blue _cBlue = new FloatValueBox(0, _cRed.X, _cGreen.Bottom + ChannelsMargin, _cRed.Width, 0, float.MaxValue, 0.001f) { - Parent = this + Parent = _rgbPanel, }; _cBlue.ValueChanged += OnRGBAChanged; - // Alpha - _cAlpha = new FloatValueBox(0, _cRed.X, _cBlue.Bottom + ChannelsMargin, _cRed.Width, 0, float.MaxValue, 0.001f) - { - Parent = this - }; - _cAlpha.ValueChanged += OnRGBAChanged; - // Hue - _cHue = new FloatValueBox(0, PickerMargin + HSVMargin + ChannelTextWidth, _cSelector.Bottom + PickerMargin, 100) + _cHue = new FloatValueBox(0, HSVRGBTextWidth + ChannelsMargin, PickerMargin, ValueBoxesWidth) { - Parent = this + Parent = _hsvPanel, + Category = Utils.ValueCategory.Angle, }; _cHue.ValueChanged += OnHSVChanged; // Saturation _cSaturation = new FloatValueBox(0, _cHue.X, _cHue.Bottom + ChannelsMargin, _cHue.Width, 0, 100.0f, 0.1f) { - Parent = this + Parent = _hsvPanel, }; _cSaturation.ValueChanged += OnHSVChanged; // Value _cValue = new FloatValueBox(0, _cHue.X, _cSaturation.Bottom + ChannelsMargin, _cHue.Width, 0, float.MaxValue, 0.1f) { - Parent = this + Parent = _hsvPanel, }; _cValue.ValueChanged += OnHSVChanged; - // Set valid dialog size based on UI content - _dialogSize = Size = new Float2(_cRed.Right + PickerMargin, 300); + // Alpha + _cAlpha = new FloatValueBox(0, _hsvRGBTabs.Left + HSVRGBTextWidth + ChannelsMargin, _hsvRGBTabs.Bottom + ChannelsMargin * 4.0f, ValueBoxesWidth, 0, float.MaxValue, 0.001f) + { + Parent = this, + }; + _cAlpha.ValueChanged += OnRGBAChanged; // Hex - const float hexTextBoxWidth = 80; - _cHex = new TextBox(false, Width - hexTextBoxWidth - PickerMargin, _cSelector.Bottom + PickerMargin, hexTextBoxWidth) + _cHex = new TextBox(false, _hsvRGBTabs.Left + HSVRGBTextWidth + ChannelsMargin, _cAlpha.Bottom + ChannelsMargin * 2.0f, ValueBoxesWidth) { - Parent = this + Parent = this, }; _cHex.EditEnd += OnHexChanged; - // Cancel - _cCancel = new Button(Width - ButtonsWidth - PickerMargin, Height - Button.DefaultHeight - PickerMargin, ButtonsWidth) - { - Text = "Cancel", - Parent = this - }; - _cCancel.Clicked += OnCancel; - - // OK - _cOK = new Button(_cCancel.Left - ButtonsWidth - PickerMargin, _cCancel.Y, ButtonsWidth) - { - Text = "Ok", - Parent = this - }; - _cOK.Clicked += OnSubmit; + // Set valid dialog size based on UI content + _dialogSize = Size = new Float2(_hsvRGBTabs.Right + PickerMargin, _cHex.Bottom + 40.0f + ColorPreviewHeight + PickerMargin); // Create saved color buttons - CreateAllSaveButtons(); + CreateAllSavedColorsButtons(); // Eyedropper button var style = Style.Current; - _cEyedropper = new Button(_cOK.X - EyedropperMargin, _cHex.Bottom + PickerMargin) + _cEyedropper = new Button(_cSelector.BottomLeft.X, _cSelector.BottomLeft.Y - 25.0f, 25.0f, 25.0f) { - TooltipText = "Eyedropper tool to pick a color directly from the screen", + TooltipText = "Eyedropper tool to pick a color directly from the screen.", BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Search32), BackgroundColor = style.Foreground, BackgroundColorHighlighted = style.Foreground.RGBMultiplied(0.9f), @@ -223,9 +241,6 @@ namespace FlaxEditor.GUI.Dialogs Parent = this, }; _cEyedropper.Clicked += OnEyedropStart; - _cEyedropper.Height = (_cValue.Bottom - _cEyedropper.Y) * 0.5f; - _cEyedropper.Width = _cEyedropper.Height; - _cEyedropper.X -= _cEyedropper.Width; // Set initial color SelectedColor = initialValue; @@ -244,7 +259,7 @@ namespace FlaxEditor.GUI.Dialogs } } - // Set color of button to current value; + // Set color of button to current value button.BackgroundColor = _value; button.BackgroundColorHighlighted = _value; button.BackgroundColorSelected = _value.RGBMultiplied(0.8f); @@ -256,10 +271,10 @@ namespace FlaxEditor.GUI.Dialogs var savedColors = JsonSerializer.Serialize(_savedColors, typeof(List)); Editor.Instance.ProjectCache.SetCustomData("ColorPickerSavedColors", savedColors); - // create new + button - if (_savedColorButtons.Count < 8) + // Create new + button + if (_savedColorButtons.Count < SavedColorsAmount) { - var savedColorButton = new Button(PickerMargin * (_savedColorButtons.Count + 1) + SavedColorButtonWidth * _savedColorButtons.Count, Height - SavedColorButtonHeight - PickerMargin, SavedColorButtonWidth, SavedColorButtonHeight) + var savedColorButton = new Button(PickerMargin * (_savedColorButtons.Count + 1) + SavedColorButtonWidth * _savedColorButtons.Count, _cHex.Bottom + 40.0f + ColorPreviewHeight * 0.5f, SavedColorButtonWidth, SavedColorButtonHeight) { Text = "+", Parent = this, @@ -276,11 +291,32 @@ namespace FlaxEditor.GUI.Dialogs } } + private void SelectedTabChanged(Tabs.Tabs tabs) + { + if (_rgbPanel == null || _hsvPanel == null) + return; + + switch (tabs.SelectedTabIndex) + { + // RGB + case 0: + _rgbPanel.Visible = true; + _hsvPanel.Visible = false; + break; + // HSV + case 1: + _rgbPanel.Visible = false; + _hsvPanel.Visible = true; + break; + } + } + private void OnColorPicked(Color32 colorPicked) { if (_activeEyedropper) { _activeEyedropper = false; + _cEyedropper.BackgroundColor = _cEyedropper.BackgroundColorHighlighted = Style.Current.Foreground; SelectedColor = colorPicked; ScreenUtilities.PickColorDone -= OnColorPicked; } @@ -289,6 +325,7 @@ namespace FlaxEditor.GUI.Dialogs private void OnEyedropStart() { _activeEyedropper = true; + _cEyedropper.BackgroundColor = _cEyedropper.BackgroundColorHighlighted = Style.Current.BackgroundHighlighted; ScreenUtilities.PickColor(); ScreenUtilities.PickColorDone += OnColorPicked; } @@ -340,64 +377,76 @@ namespace FlaxEditor.GUI.Dialogs base.Draw(); - // RGBA - var rgbaR = new Rectangle(_cRed.Left - ChannelTextWidth, _cRed.Y, 10000, _cRed.Height); - Render2D.DrawText(style.FontMedium, "R", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); - rgbaR.Location.Y = _cGreen.Y; - Render2D.DrawText(style.FontMedium, "G", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); - rgbaR.Location.Y = _cBlue.Y; - Render2D.DrawText(style.FontMedium, "B", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); - rgbaR.Location.Y = _cAlpha.Y; - Render2D.DrawText(style.FontMedium, "A", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center); + switch (_hsvRGBTabs.SelectedTabIndex) + { + // RGB + case 0: + var rgbRect = new Rectangle(_hsvRGBTabs.Left + PickerMargin, _hsvRGBTabs.Top + TabHeight + PickerMargin, 10000, _cRed.Height); + Render2D.DrawText(style.FontMedium, "R", rgbRect, textColor, TextAlignment.Near, TextAlignment.Center); + rgbRect.Location.Y += _cRed.Height + ChannelsMargin; + Render2D.DrawText(style.FontMedium, "G", rgbRect, textColor, TextAlignment.Near, TextAlignment.Center); + rgbRect.Location.Y += _cRed.Height + ChannelsMargin; + Render2D.DrawText(style.FontMedium, "B", rgbRect, textColor, TextAlignment.Near, TextAlignment.Center); + break; + // HSV + case 1: + // Left + var hsvLeftRect = new Rectangle(_hsvRGBTabs.Left + PickerMargin, _hsvRGBTabs.Top + TabHeight + PickerMargin, 10000, _cHue.Height); + Render2D.DrawText(style.FontMedium, "H", hsvLeftRect, textColor, TextAlignment.Near, TextAlignment.Center); + hsvLeftRect.Location.Y += _cHue.Height + ChannelsMargin; + Render2D.DrawText(style.FontMedium, "S", hsvLeftRect, textColor, TextAlignment.Near, TextAlignment.Center); + hsvLeftRect.Location.Y += _cHue.Height + ChannelsMargin; + Render2D.DrawText(style.FontMedium, "V", hsvLeftRect, textColor, TextAlignment.Near, TextAlignment.Center); - // HSV left - var hsvHl = new Rectangle(_cHue.Left - ChannelTextWidth, _cHue.Y, 10000, _cHue.Height); - Render2D.DrawText(style.FontMedium, "H", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); - hsvHl.Location.Y = _cSaturation.Y; - Render2D.DrawText(style.FontMedium, "S", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); - hsvHl.Location.Y = _cValue.Y; - Render2D.DrawText(style.FontMedium, "V", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center); + // Right + var hsvRightRect = new Rectangle(_hsvRGBTabs.Right - HSVRGBTextWidth, _hsvRGBTabs.Top + TabHeight + PickerMargin, ChannelTextWidth, _cHue.Height); + Render2D.DrawText(style.FontMedium, "°", hsvRightRect, textColor, TextAlignment.Near, TextAlignment.Center); + hsvRightRect.Location.Y += _cHue.Height + ChannelsMargin; + Render2D.DrawText(style.FontMedium, "%", hsvRightRect, textColor, TextAlignment.Near, TextAlignment.Center); + hsvRightRect.Location.Y += _cHue.Height + ChannelsMargin; + Render2D.DrawText(style.FontMedium, "%", hsvRightRect, textColor, TextAlignment.Near, TextAlignment.Center); + break; + } - // HSV right - var hsvHr = new Rectangle(_cHue.Right + 2, _cHue.Y, 10000, _cHue.Height); - Render2D.DrawText(style.FontMedium, "°", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); - hsvHr.Location.Y = _cSaturation.Y; - Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); - hsvHr.Location.Y = _cValue.Y; - Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center); + // A + var alphaHexRect = new Rectangle(_hsvRGBTabs.Left + PickerMargin, _cAlpha.Top, ChannelTextWidth, _cAlpha.Height); + Render2D.DrawText(style.FontMedium, "A", alphaHexRect, textColor, TextAlignment.Near, TextAlignment.Center); // Hex - var hex = new Rectangle(_cHex.Left - 26, _cHex.Y, 10000, _cHex.Height); - Render2D.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center); + alphaHexRect.Y += _cAlpha.Height + ChannelsMargin * 2.0f; + alphaHexRect.X -= 5.0f; // "Hex" is two characters wider than the other labels so we need to adjust for that + Render2D.DrawText(style.FontMedium, "Hex", alphaHexRect, textColor, TextAlignment.Far, TextAlignment.Center); // Color difference - var newRect = new Rectangle(_cOK.X - 3, _cHex.Bottom + PickerMargin, 130, 0); - newRect.Size.Y = 50; - Render2D.FillRectangle(newRect, Color.White); - var smallRectSize = 10; - var numHor = Mathf.FloorToInt(newRect.Width / smallRectSize); - var numVer = Mathf.FloorToInt(newRect.Height / smallRectSize); + var differenceRect = new Rectangle(_hsvRGBTabs.Left, _cHex.Bottom + 40.0f, _hsvRGBTabs.Width, ColorPreviewHeight); + // Draw checkerboard for background of color to help with transparency + Render2D.FillRectangle(differenceRect, Color.White); + var smallRectSize = 10; + var numHor = Mathf.CeilToInt(differenceRect.Width / smallRectSize); + var numVer = Mathf.CeilToInt(differenceRect.Height / smallRectSize); + Render2D.PushClip(differenceRect); for (int i = 0; i < numHor; i++) { for (int j = 0; j < numVer; j++) { if ((i + j) % 2 == 0) { - var rect = new Rectangle(newRect.X + smallRectSize * i, newRect.Y + smallRectSize * j, new Float2(smallRectSize)); + var rect = new Rectangle(differenceRect.X + smallRectSize * i, differenceRect.Y + smallRectSize * j, new Float2(smallRectSize)); Render2D.FillRectangle(rect, Color.Gray); } } } - Render2D.FillRectangle(newRect, _value); + Render2D.PopClip(); + Render2D.FillRectangle(differenceRect, _value); } /// protected override void OnShow() { - // Auto cancel on lost focus + // Apply changes on lost focus #if !PLATFORM_LINUX - ((WindowRootControl)Root).Window.LostFocus += OnWindowLostFocus; + ((WindowRootControl)Root).Window.LostFocus += OnSubmit; #endif base.OnShow(); @@ -410,6 +459,7 @@ namespace FlaxEditor.GUI.Dialogs { // Cancel eye dropping _activeEyedropper = false; + _cEyedropper.BackgroundColor = _cEyedropper.BackgroundColorHighlighted = Style.Current.Foreground; ScreenUtilities.PickColorDone -= OnColorPicked; return true; } @@ -483,20 +533,20 @@ namespace FlaxEditor.GUI.Dialogs } _savedColorButtons.Clear(); - CreateAllSaveButtons(); + CreateAllSavedColorsButtons(); // Save new colors var savedColors = JsonSerializer.Serialize(_savedColors, typeof(List)); Editor.Instance.ProjectCache.SetCustomData("ColorPickerSavedColors", savedColors); } - private void CreateAllSaveButtons() + private void CreateAllSavedColorsButtons() { // Create saved color buttons for (int i = 0; i < _savedColors.Count; i++) { var savedColor = _savedColors[i]; - var savedColorButton = new Button(PickerMargin * (i + 1) + SavedColorButtonWidth * i, Height - SavedColorButtonHeight - PickerMargin, SavedColorButtonWidth, SavedColorButtonHeight) + var savedColorButton = new Button(PickerMargin * (i + 1) + SavedColorButtonWidth * i, _cHex.Bottom + 40.0f + ColorPreviewHeight * 0.5f, SavedColorButtonWidth, SavedColorButtonHeight) { Parent = this, Tag = savedColor, @@ -507,9 +557,9 @@ namespace FlaxEditor.GUI.Dialogs savedColorButton.ButtonClicked += OnSavedColorButtonClicked; _savedColorButtons.Add(savedColorButton); } - if (_savedColors.Count < 8) + if (_savedColors.Count < SavedColorsAmount) { - var savedColorButton = new Button(PickerMargin * (_savedColors.Count + 1) + SavedColorButtonWidth * _savedColors.Count, Height - SavedColorButtonHeight - PickerMargin, SavedColorButtonWidth, SavedColorButtonHeight) + var savedColorButton = new Button(PickerMargin * (_savedColors.Count + 1) + SavedColorButtonWidth * _savedColors.Count, _cHex.Bottom + 40.0f + ColorPreviewHeight * 0.5f, SavedColorButtonWidth, SavedColorButtonHeight) { Text = "+", Parent = this, @@ -521,19 +571,6 @@ namespace FlaxEditor.GUI.Dialogs } } - private void OnWindowLostFocus() - { - // Auto apply color on defocus - var autoAcceptColorPickerChange = Editor.Instance.Options.Options.Interface.AutoAcceptColorPickerChange; - if (_useDynamicEditing && _initialValue != _value && _canPassLastChangeEvent && autoAcceptColorPickerChange) - { - _canPassLastChangeEvent = false; - _onChanged?.Invoke(_value, false); - } - - OnCancel(); - } - /// public override void OnSubmit() { @@ -541,6 +578,9 @@ namespace FlaxEditor.GUI.Dialogs return; _disableEvents = true; + // Ensure the cursor is restored + Cursor = CursorType.Default; + // Send color event if modified if (_value != _initialValue) { @@ -557,6 +597,9 @@ namespace FlaxEditor.GUI.Dialogs return; _disableEvents = true; + // Ensure the cursor is restored + Cursor = CursorType.Default; + // Restore color if modified if (_useDynamicEditing && _initialValue != _value && _canPassLastChangeEvent) { diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index f26506c74..50f7c6f04 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -207,13 +207,6 @@ namespace FlaxEditor.Options [EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")] public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal; - /// - /// If checked, color pickers will always modify the color unless 'Cancel' if pressed, otherwise color won't change unless 'Ok' is pressed. - /// - [DefaultValue(true)] - [EditorDisplay("Interface"), EditorOrder(290)] - public bool AutoAcceptColorPickerChange { get; set; } = true; - /// /// Gets or sets the formatting option for numeric values in the editor. /// From d163064f958efdc8a9ccb10e922971daf024af48 Mon Sep 17 00:00:00 2001 From: Saas Date: Thu, 5 Mar 2026 20:33:18 +0100 Subject: [PATCH 5/5] fix hv wheel selector --- Source/Editor/GUI/Dialogs/ColorSelector.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/Dialogs/ColorSelector.cs b/Source/Editor/GUI/Dialogs/ColorSelector.cs index 392a8d934..3d781a8ec 100644 --- a/Source/Editor/GUI/Dialogs/ColorSelector.cs +++ b/Source/Editor/GUI/Dialogs/ColorSelector.cs @@ -194,10 +194,11 @@ namespace FlaxEditor.GUI.Dialogs // Wheel float boxExpand = (2.0f * 4.0f / 128.0f) * _wheelRect.Width; Render2D.DrawMaterial(_hsWheelMaterial, _wheelRect, enabled ? Color.White : Color.Gray); - Color hsColor = Color.FromHSV(new Float3(Color.ToHSV().X, 1, 1)); + Float3 hsv = _color.ToHSV(); + Color hsColor = Color.FromHSV(new Float3(hsv.X, hsv.Y, 1)); Rectangle wheelRect = wheelDragRect; Render2D.FillRectangle(wheelRect, hsColor); - Render2D.DrawRectangle(wheelRect, _isMouseDownWheel ? Color.Gray : Color.Black, _isMouseDownWheel ? 3.0f : 1.0f); + Render2D.DrawRectangle(wheelRect, Color.Black, _isMouseDownWheel ? 2.0f : 1.0f); } ///