1010import org .eclipse .swt .layout .GridData ;
1111import org .eclipse .swt .layout .GridLayout ;
1212import org .eclipse .swt .widgets .Composite ;
13- import org .eclipse .swt .widgets .Display ;
1413import org .eclipse .swt .widgets .Label ;
1514import org .eclipse .ui .ISharedImages ;
1615import org .eclipse .ui .PlatformUI ;
1716import org .osgi .service .event .EventHandler ;
1817
1918import com .microsoft .copilot .eclipse .core .events .CopilotEventConstants ;
2019import com .microsoft .copilot .eclipse .ui .swt .CssConstants ;
20+ import com .microsoft .copilot .eclipse .ui .swt .SpinnerAnimator ;
2121import com .microsoft .copilot .eclipse .ui .utils .AccessibilityUtils ;
2222import com .microsoft .copilot .eclipse .ui .utils .UiUtils ;
2323
2424/**
2525 * A label with icon that displays the running status of the agent.
2626 */
2727public class AgentStatusLabel extends Composite {
28- private static final int TOTAL_FRAMES = 8 ; // Adjust based on actual number of spinner images
29-
30- private Image runningIcon ;
3128 private Image completedIcon ;
3229 private Image cancelledIcon ;
3330 private Label iconLabel ;
3431 private ChatMarkupViewer textLabel ;
35- private int currentFrame = 1 ;
36- private Runnable animationRunnable ;
32+ private SpinnerAnimator spinner ;
3733 private Status status ;
3834 private EventHandler cancelStatusHandler ;
3935 private IEventBroker eventBroker ;
@@ -47,14 +43,12 @@ public class AgentStatusLabel extends Composite {
4743 public AgentStatusLabel (Composite parent , int style ) {
4844 super (parent , style );
4945 GridLayout layout = new GridLayout (2 , false );
46+ layout .marginWidth = 0 ;
47+ layout .marginHeight = 0 ;
5048 layout .horizontalSpacing = 0 ;
5149 setLayout (layout );
5250 setLayoutData (new GridData (SWT .FILL , SWT .CENTER , true , false ));
5351 this .addDisposeListener (e -> {
54- stopAnimation ();
55- if (this .runningIcon != null && !this .runningIcon .isDisposed ()) {
56- this .runningIcon .dispose ();
57- }
5852 if (this .completedIcon != null && !this .completedIcon .isDisposed ()) {
5953 this .completedIcon .dispose ();
6054 }
@@ -66,6 +60,7 @@ public AgentStatusLabel(Composite parent, int style) {
6660 }
6761 });
6862 iconLabel = new Label (this , SWT .LEFT );
63+ spinner = new SpinnerAnimator (iconLabel );
6964
7065 this .status = Status .RUNNING ;
7166 this .cancelStatusHandler = new EventHandler () {
@@ -84,7 +79,7 @@ public void handleEvent(org.osgi.service.event.Event event) {
8479 * @param statusText the text to display when the agent is completed
8580 */
8681 public void setCompletedStatus (String statusText ) {
87- stopAnimation ();
82+ spinner . stop ();
8883
8984 if (this .completedIcon == null ) {
9085 this .completedIcon = UiUtils .buildImageFromPngPath ("/icons/complete_status.png" );
@@ -101,11 +96,7 @@ public void setCompletedStatus(String statusText) {
10196 * @param statusText the text to display when the agent is running
10297 */
10398 public void setRunningStatus (String statusText ) {
104- // Stop any existing animation
105- stopAnimation ();
106-
107- // Start new animation
108- startAnimation ();
99+ spinner .start ();
109100
110101 setText (statusText );
111102 this .status = Status .RUNNING ;
@@ -116,7 +107,7 @@ public void setRunningStatus(String statusText) {
116107 */
117108 public void setErrorStatus () {
118109 if (this .status == Status .RUNNING ) {
119- stopAnimation ();
110+ spinner . stop ();
120111 }
121112 iconLabel .setImage (PlatformUI .getWorkbench ().getSharedImages ().getImage (ISharedImages .IMG_OBJS_ERROR_TSK ));
122113 this .status = Status .ERROR ;
@@ -127,7 +118,7 @@ public void setErrorStatus() {
127118 */
128119 public void setCancelledStatus () {
129120 if (this .status == Status .RUNNING ) {
130- stopAnimation ();
121+ spinner . stop ();
131122
132123 if (this .cancelledIcon == null ) {
133124 this .cancelledIcon = UiUtils .buildImageFromPngPath ("/icons/cancel_status.png" );
@@ -138,47 +129,6 @@ public void setCancelledStatus() {
138129 }
139130 }
140131
141- private void startAnimation () {
142- final Display display = getDisplay ();
143-
144- animationRunnable = new Runnable () {
145- @ Override
146- public void run () {
147- if (isDisposed () || iconLabel .isDisposed ()) {
148- return ;
149- }
150-
151- // Dispose previous image
152- if (runningIcon != null && !runningIcon .isDisposed ()) {
153- runningIcon .dispose ();
154- }
155-
156- // Load the next frame
157- String imagePath = String .format ("/icons/spinner/%d.png" , currentFrame );
158- runningIcon = UiUtils .buildImageFromPngPath (imagePath );
159- iconLabel .setImage (runningIcon );
160- // request layout to update the icon, otherwise the scale of the spinner will be wrong
161- iconLabel .requestLayout ();
162-
163- // Update frame counter
164- currentFrame = (currentFrame % TOTAL_FRAMES ) + 1 ;
165-
166- // Schedule next frame
167- display .timerExec (100 , this );
168- }
169- };
170-
171- // Start the animation
172- display .timerExec (0 , animationRunnable );
173- }
174-
175- private void stopAnimation () {
176- if (animationRunnable != null ) {
177- getDisplay ().timerExec (-1 , animationRunnable );
178- animationRunnable = null ;
179- }
180- }
181-
182132 /**
183133 * Set the text to display next to the icon.
184134 */
0 commit comments