1515 */
1616package org .springframework .shell .component .view .control ;
1717
18+ import java .util .ArrayList ;
1819import java .util .Arrays ;
1920import java .util .List ;
2021import java .util .function .Function ;
2122
23+ import org .slf4j .Logger ;
24+ import org .slf4j .LoggerFactory ;
25+
2226import org .springframework .shell .component .message .ShellMessageBuilder ;
2327import org .springframework .shell .component .view .control .cell .TextCell ;
2428import org .springframework .shell .component .view .screen .Screen ;
3640 */
3741public class ProgressView extends BoxView {
3842
43+ private final static Logger log = LoggerFactory .getLogger (ProgressView .class );
3944 private final int tickStart ;
4045 private final int tickEnd ;
4146 private int tickValue ;
4247 private boolean running = false ;
4348 private String description ;
44- private Spinner spinner = Spinner .of (Spinner .LINE1 , 130 );
45- private int spinnerFrame ;
49+ private Spinner spinner ;
4650 private List <ProgressViewItem > items ;
4751 private GridView grid ;
52+ private long startTime ;
53+ private long updateTime ;
54+ private List <TextCell <Context >> cells = new ArrayList <>();
4855
4956 private final static Function <Context , TextCell <Context >> DEFAULT_DESCRIPTION_FACTORY =
5057 (item ) -> TextCell .of (item , ctx -> {
@@ -66,9 +73,27 @@ public class ProgressView extends BoxView {
6673 private final static Function <Context , TextCell <Context >> DEFAULT_SPINNER_FACTORY =
6774 item -> {
6875 TextCell <Context > cell = TextCell .of (item , ctx -> {
69- Spinner spin = ctx .spinner ();
70- int spinState = ctx .getState ().sprinnerFrame ();
71- return String .format ("%s" , spin .getFrames ()[spinState ]);
76+ int frame = 0 ;
77+
78+ Spinner spin = ctx .getSpinner ();
79+ if (spin == null ) {
80+ spin = Spinner .of (Spinner .LINE1 , 130 );
81+ }
82+ if (ctx .getState ().running ()) {
83+ // we know start time and current update time,
84+ // calculate elapsed time "frame" to pick rolling
85+ // spinner frame.
86+ int interval = spin .getInterval ();
87+ long startTime = ctx .getState ().startTime ();
88+ long updateTime = ctx .getState ().updateTime ();
89+ long elapsedTime = updateTime - startTime ;
90+ long elapsedFrame = elapsedTime / interval ;
91+ frame = (int ) elapsedFrame % spin .getFrames ().length ;
92+ log .debug ("Calculate frame {} {} {}" , interval , elapsedTime , elapsedFrame );
93+ }
94+ log .debug ("Drawing frame {}" , frame );
95+
96+ return String .format ("%s" , spin .getFrames ()[frame ]);
7297 });
7398 return cell ;
7499 };
@@ -173,11 +198,21 @@ public void setDescription(String description) {
173198 this .description = description ;
174199 }
175200
201+ /**
202+ * Sets an explicit spinner to use.
203+ *
204+ * @param spinner the spinner to use
205+ */
206+ public void setSpinner (Spinner spinner ) {
207+ this .spinner = spinner ;
208+ }
209+
176210 public void start () {
177211 if (running ) {
178212 return ;
179213 }
180214 running = true ;
215+ startTime = System .currentTimeMillis ();
181216 ProgressState state = getState ();
182217 dispatch (ShellMessageBuilder .ofView (this , ProgressViewStartEvent .of (this , state )));
183218 }
@@ -212,6 +247,7 @@ private void initLayout() {
212247 for (ProgressViewItem item : items ) {
213248 columnSizes [index ] = item .size ;
214249 TextCell <Context > cell = item .factory .apply (buildContext ());
250+ cells .add (cell );
215251 cell .setHorizontalAlign (item .align );
216252 grid .addItem (new BoxWrapper (cell ), 0 , index , 1 , 1 , 0 , 0 );
217253 index ++;
@@ -221,27 +257,17 @@ private void initLayout() {
221257
222258 }
223259
224- boolean needLastMillisInit = true ;
225- private long lastMillis ;
226-
227260 @ Override
228261 protected void drawInternal (Screen screen ) {
229262 long current = System .currentTimeMillis ();
230- if (needLastMillisInit ) {
231- needLastMillisInit = false ;
232- lastMillis = current ;
233- }
234263
235- long elapsedFromLast = current - lastMillis ;
236- if ( elapsedFromLast > spinner . getInterval ()) {
237- spinnerFrame = ( spinnerFrame + 1 ) % spinner . getFrames (). length ;
238- lastMillis = current ;
264+ updateTime = current ;
265+ Context context = buildContext ();
266+ for ( TextCell < Context > cell : cells ) {
267+ cell . setItem ( context ) ;
239268 }
240269
241270 Rectangle rect = getRect ();
242- int width = rect .width ();
243- width = width / 3 ;
244-
245271 grid .setRect (rect .x (), rect .y (), rect .width (), rect .height ());
246272 grid .draw (screen );
247273
@@ -291,7 +317,7 @@ else if (value < tickStart) {
291317 * @return a view progress state
292318 */
293319 public ProgressState getState () {
294- return ProgressState .of (tickStart , tickEnd , tickValue , running , spinnerFrame );
320+ return ProgressState .of (tickStart , tickEnd , tickValue , running , startTime , updateTime );
295321 }
296322
297323 private Context buildContext () {
@@ -318,7 +344,7 @@ public int resolveThemeStyle(String tag, int defaultStyle) {
318344 }
319345
320346 @ Override
321- public Spinner spinner () {
347+ public Spinner getSpinner () {
322348 return ProgressView .this .spinner ;
323349 }
324350 };
@@ -355,7 +381,7 @@ public interface Context {
355381 *
356382 * @return spinner frames
357383 */
358- Spinner spinner ();
384+ Spinner getSpinner ();
359385
360386 /**
361387 * Resolve style using existing {@link ThemeResolver} and {@code theme name}.
@@ -376,12 +402,15 @@ public interface Context {
376402 * @param tickEnd the tick end value, positive and more than tick start
377403 * @param tickValue the current tick value, within inclusive bounds of tick start/end
378404 * @param running the running state
379- * @param spinnerFrame the current spinner frame index
405+ * @param startTime the running start time
406+ * @param updateTime the last running update time
380407 */
381- public record ProgressState (int tickStart , int tickEnd , int tickValue , boolean running , int sprinnerFrame ) {
408+ public record ProgressState (int tickStart , int tickEnd , int tickValue , boolean running , long startTime ,
409+ long updateTime ) {
382410
383- public static ProgressState of (int tickStart , int tickEnd , int tickValue , boolean running , int spinnerFrame ) {
384- return new ProgressState (tickStart , tickEnd , tickValue , running , spinnerFrame );
411+ public static ProgressState of (int tickStart , int tickEnd , int tickValue , boolean running , long startTime ,
412+ long updateTime ) {
413+ return new ProgressState (tickStart , tickEnd , tickValue , running , startTime , updateTime );
385414 }
386415 }
387416
0 commit comments