using UnityEngine; using UnityEngine.UIElements; namespace BITKit.UX { // An element that displays progress inside a partially filled circle public class RadialProgress : VisualElement { public new class UxmlTraits : VisualElement.UxmlTraits { // The progress property is exposed to UXML. private readonly UxmlFloatAttributeDescription m_ProgressAttribute = new UxmlFloatAttributeDescription() { name = "progress" }; // Use the Init method to assign the value of the progress UXML attribute to the C# progress property. public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc) { base.Init(ve, bag, cc); ((RadialProgress)ve).progress = m_ProgressAttribute.GetValueFromBag(bag, cc); } } // Define a factory class to expose this control to UXML. public new class UxmlFactory : UxmlFactory { } // These are USS class names for the control overall and the label. public static readonly string ussClassName = "radial-progress"; public static readonly string ussLabelClassName = "radial-progress__label"; // These objects allow C# code to access custom USS properties. static CustomStyleProperty s_TrackColor = new CustomStyleProperty("--track-color"); static CustomStyleProperty s_ProgressColor = new CustomStyleProperty("--progress-color"); Color m_TrackColor = Color.gray; Color m_ProgressColor = Color.red; // This is the label that displays the percentage. Label m_Label; // This is the number that the Label displays as a percentage. float m_Progress; // A value between 0 and 100 public float progress { // The progress property is exposed in C#. get => m_Progress; set { // Whenever the progress property changes, MarkDirtyRepaint() is named. This causes a call to the // generateVisualContents callback. m_Progress = value; m_Label.text = Mathf.Clamp(Mathf.Round(value), 0, 100) + "%"; MarkDirtyRepaint(); } } // This default constructor is RadialProgress's only constructor. public RadialProgress() { // Create a Label, add a USS class name, and add it to this visual tree. m_Label = new Label(); m_Label.AddToClassList(ussLabelClassName); Add(m_Label); // Add the USS class name for the overall control. AddToClassList(ussClassName); // Register a callback after custom style resolution. RegisterCallback(evt => CustomStylesResolved(evt)); // Register a callback to generate the visual content of the control. generateVisualContent += GenerateVisualContent; progress = 0.0f; } static void CustomStylesResolved(CustomStyleResolvedEvent evt) { RadialProgress element = (RadialProgress)evt.currentTarget; element.UpdateCustomStyles(); } // After the custom colors are resolved, this method uses them to color the meshes and (if necessary) repaint // the control. void UpdateCustomStyles() { bool repaint = false; if (customStyle.TryGetValue(s_ProgressColor, out m_ProgressColor)) repaint = true; if (customStyle.TryGetValue(s_TrackColor, out m_TrackColor)) repaint = true; if (repaint) MarkDirtyRepaint(); } void GenerateVisualContent(MeshGenerationContext context) { float width = contentRect.width; float height = contentRect.height; var painter = context.painter2D; painter.lineWidth = 10.0f; painter.lineCap = LineCap.Butt; // Draw the track painter.strokeColor = m_TrackColor; painter.BeginPath(); painter.Arc(new Vector2(width * 0.5f, height * 0.5f), width * 0.5f, 0.0f, 360.0f); painter.Stroke(); // Draw the progress painter.strokeColor = m_ProgressColor; painter.BeginPath(); painter.Arc(new Vector2(width * 0.5f, height * 0.5f), width * 0.5f, -90.0f, 360.0f * (progress / 100.0f) - 90.0f); painter.Stroke(); } } }