From d502a0e90eacad7ec676b0abf4686db553b794b1 Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Sat, 14 Mar 2015 20:42:08 -0400 Subject: [PATCH] timer and progress bar working --- README.md | 2 - demos/Demo1.java | 56 +++++++----- src/jexer/TApplication.java | 86 ++++++++++++------- src/jexer/TLabel.java | 18 ++++ src/jexer/TProgressBar.java | 167 ++++++++++++++++++++++++++++++++++++ src/jexer/TTimer.java | 109 +++++++++++++++++++++++ src/jexer/TWidget.java | 14 +++ 7 files changed, 398 insertions(+), 54 deletions(-) create mode 100644 src/jexer/TProgressBar.java create mode 100644 src/jexer/TTimer.java diff --git a/README.md b/README.md index 11e09c34..0ee57260 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,6 @@ Many tasks remain before calling this version 1.0: - TDirectoryList - TField - TMessageBox -- TProgressBar -- TTimer - TRadioGroup / TRadioButton - THScroller / TVScroller - TText diff --git a/demos/Demo1.java b/demos/Demo1.java index 44333a42..db327081 100644 --- a/demos/Demo1.java +++ b/demos/Demo1.java @@ -198,10 +198,13 @@ EOS", class DemoMainWindow extends TWindow { - /* // Timer that increments a number private TTimer timer; + // Timer label is updated with timerrr ticks + TLabel timerLabel; + + /* // The modal window is a more low-level example of controlling a window // "from the outside". Most windows will probably subclass TWindow and // do this kind of logic on their own. @@ -217,14 +220,17 @@ class DemoMainWindow extends TWindow { private void modalWindowClose() { application.closeWindow(modalWindow); } + */ - /// We need to override onClose so that the timer will no longer be - /// called after we close the window. TTimers currently are completely - /// unaware of the rest of the UI classes. - override public void onClose() { - application.removeTimer(timer); - } + /** + * We need to override onClose so that the timer will no longer be called + * after we close the window. TTimers currently are completely unaware + * of the rest of the UI classes. */ + @Override + public void onClose() { + getApplication().removeTimer(timer); + } /** * Construct demo window. It will be centered on screen. @@ -233,6 +239,9 @@ class DemoMainWindow extends TWindow { this(parent, CENTERED | RESIZABLE); } + int timerI = 0; + TProgressBar progressBar; + /** * Constructor. */ @@ -281,7 +290,7 @@ class DemoMainWindow extends TWindow { addButton("&Checkboxes", 35, row, new TAction() { public void DO() { - new DemoCheckboxWindow(getApplication(), MODAL); + new DemoCheckboxWindow(getApplication()); } } ); @@ -328,24 +337,25 @@ class DemoMainWindow extends TWindow { ); } row += 2; + */ - TProgressBar bar = addProgressBar(1, row, 22); + progressBar = addProgressBar(1, row, 22, 0); row++; - TLabel timerLabel = addLabel("Timer", 1, row); - timer = parent.addTimer(100, - { - static int i = 0; - auto writer = appender!dstring(); - formattedWrite(writer, "Timer: %d", i); - timerLabel.text = writer.data; - timerLabel.width = cast(uint)timerLabel.text.length; - if (i < 100) { - i++; + timerLabel = addLabel("Timer", 1, row); + timer = getApplication().addTimer(100, true, + new TAction() { + + public void DO() { + timerLabel.setText(String.format("Timer: %d", timerI)); + timerLabel.setWidth(timerLabel.getText().length()); + if (timerI < 100) { + timerI++; + } + progressBar.setValue(timerI); + DemoMainWindow.this.setRepaint(); } - bar.value = i; - parent.repaint = true; - }, true); - */ + } + ); } } diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index d2b8f446..8786e693 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -34,6 +34,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -135,6 +136,11 @@ public class TApplication { */ private List windows; + /** + * Timers that are being ticked. + */ + private List timers; + /** * When true, exit the application. */ @@ -211,6 +217,7 @@ public class TApplication { windows = new LinkedList(); menus = new LinkedList(); subMenus = new LinkedList(); + timers = new LinkedList(); accelerators = new HashMap(); } @@ -595,28 +602,25 @@ public class TApplication { * Do stuff when there is no user input. */ private void doIdle() { - /* - TODO // Now run any timers that have timed out - auto now = Clock.currTime; - TTimer [] keepTimers; - foreach (t; timers) { - if (t.nextTick < now) { - t.tick(); - if (t.recurring == true) { - keepTimers ~= t; + Date now = new Date(); + List keepTimers = new LinkedList(); + for (TTimer timer: timers) { + if (timer.getNextTick().getTime() < now.getTime()) { + timer.tick(); + if (timer.recurring == true) { + keepTimers.add(timer); } } else { - keepTimers ~= t; + keepTimers.add(timer); } } timers = keepTimers; // Call onIdle's - foreach (w; windows) { - w.onIdle(); + for (TWindow window: windows) { + window.onIdle(); } - */ } /** @@ -626,24 +630,20 @@ public class TApplication { * @return number of milliseconds between now and the next timer event */ protected int getSleepTime(final int timeout) { - /* - auto now = Clock.currTime; - auto sleepTime = dur!("msecs")(timeout); - foreach (t; timers) { - if (t.nextTick < now) { + Date now = new Date(); + long sleepTime = timeout; + for (TTimer timer: timers) { + if (timer.getNextTick().getTime() < now.getTime()) { return 0; } - if ((t.nextTick > now) && - ((t.nextTick - now) < sleepTime) + if ((timer.getNextTick().getTime() > now.getTime()) + && ((timer.getNextTick().getTime() - now.getTime()) < sleepTime) ) { - sleepTime = t.nextTick - now; + sleepTime = timer.getNextTick().getTime() - now.getTime(); } } - assert(sleepTime.total!("msecs")() >= 0); - return cast(uint)sleepTime.total!("msecs")(); - */ - // TODO: fix timers. Until then, come back after 250 millis. - return 250; + assert (sleepTime >= 0); + return (int)sleepTime; } /** @@ -1310,9 +1310,9 @@ public class TApplication { List sorted = new LinkedList(windows); Collections.sort(sorted); Collections.reverse(sorted); - for (TWindow w: sorted) { - w.setX(x); - w.setY(y); + for (TWindow window: sorted) { + window.setX(x); + window.setY(y); x++; y++; if (x > getScreen().getWidth()) { @@ -1324,4 +1324,32 @@ public class TApplication { } } + /** + * Convenience function to add a timer. + * + * @param duration number of milliseconds to wait between ticks + * @param recurring if true, re-schedule this timer after every tick + * @param action function to call when button is pressed + */ + public final TTimer addTimer(final long duration, final boolean recurring, + final TAction action) { + + TTimer timer = new TTimer(duration, recurring, action); + synchronized (timers) { + timers.add(timer); + } + return timer; + } + + /** + * Convenience function to remove a timer. + * + * @param timer timer to remove + */ + public final void removeTimer(final TTimer timer) { + synchronized (timers) { + timers.remove(timer); + } + } + } diff --git a/src/jexer/TLabel.java b/src/jexer/TLabel.java index 15d6376d..d56be67f 100644 --- a/src/jexer/TLabel.java +++ b/src/jexer/TLabel.java @@ -42,6 +42,24 @@ public final class TLabel extends TWidget { */ private String text = ""; + /** + * Get label text. + * + * @return label text + */ + public String getText() { + return text; + } + + /** + * Set label text. + * + * @param text new label text + */ + public void setText(final String text) { + this.text = text; + } + /** * Label color. */ diff --git a/src/jexer/TProgressBar.java b/src/jexer/TProgressBar.java new file mode 100644 index 00000000..1cd950c9 --- /dev/null +++ b/src/jexer/TProgressBar.java @@ -0,0 +1,167 @@ +/** + * Jexer - Java Text User Interface + * + * License: LGPLv3 or later + * + * This module is licensed under the GNU Lesser General Public License + * Version 3. Please see the file "COPYING" in this directory for more + * information about the GNU Lesser General Public License Version 3. + * + * Copyright (C) 2015 Kevin Lamonte + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * http://www.gnu.org/licenses/, or write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * @author Kevin Lamonte [kevin.lamonte@gmail.com] + * @version 1 + */ +package jexer; + +import jexer.bits.CellAttributes; +import jexer.bits.GraphicsChars; + +/** + * TProgressBar implements a simple progress bar. + */ +public final class TProgressBar extends TWidget { + + /** + * Value that corresponds to 0% progress. + */ + private int minValue = 0; + + /** + * Get the value that corresponds to 0% progress. + * + * @return the value that corresponds to 0% progress + */ + public int getMinValue() { + return minValue; + } + + /** + * Set the value that corresponds to 0% progress. + * + * @param minValue the value that corresponds to 0% progress + */ + public void setMinValue(final int minValue) { + this.minValue = minValue; + } + + /** + * Value that corresponds to 100% progress. + */ + private int maxValue = 100; + + /** + * Get the value that corresponds to 100% progress. + * + * @return the value that corresponds to 100% progress + */ + public int getMaxValue() { + return maxValue; + } + + /** + * Set the value that corresponds to 100% progress. + * + * @param maxValue the value that corresponds to 100% progress + */ + public void setMaxValue(final int maxValue) { + this.maxValue = maxValue; + } + + /** + * Current value of the progress. + */ + private int value = 0; + + /** + * Get the current value of the progress. + * + * @return the current value of the progress + */ + public int getValue() { + return value; + } + + /** + * Set the current value of the progress. + * + * @param value the current value of the progress + */ + public void setValue(final int value) { + this.value = value; + } + + /** + * Public constructor. + * + * @param parent parent widget + * @param x column relative to parent + * @param y row relative to parent + * @param width width of progress bar + * @param value initial value of percent complete + */ + public TProgressBar(final TWidget parent, final int x, final int y, + final int width, final int value) { + + // Set parent and window + super(parent, false); + + setX(x); + setY(y); + setHeight(1); + setWidth(width); + this.value = value; + } + + /** + * Draw a static progress bar. + */ + @Override + public void draw() { + CellAttributes completeColor = getTheme().getColor("tprogressbar.complete"); + CellAttributes incompleteColor = getTheme().getColor("tprogressbar.incomplete"); + + float progress = ((float)value - minValue) / ((float)maxValue - minValue); + int progressInt = (int)(progress * 100); + int progressUnit = 100 / (getWidth() - 2); + + getScreen().putCharXY(0, 0, GraphicsChars.CP437[0xC3], incompleteColor); + for (int i = 0; i < getWidth() - 2; i++) { + float iProgress = (float)i / (getWidth() - 2); + int iProgressInt = (int)(iProgress * 100); + if (iProgressInt <= progressInt - progressUnit) { + getScreen().putCharXY(i + 1, 0, GraphicsChars.BOX, + completeColor); + } else { + getScreen().putCharXY(i + 1, 0, GraphicsChars.SINGLE_BAR, + incompleteColor); + } + } + if (value >= maxValue) { + getScreen().putCharXY(getWidth() - 2, 0, GraphicsChars.BOX, + completeColor); + } else { + getScreen().putCharXY(getWidth() - 2, 0, GraphicsChars.SINGLE_BAR, + incompleteColor); + } + getScreen().putCharXY(getWidth() - 1, 0, GraphicsChars.CP437[0xB4], + incompleteColor); + } + +} diff --git a/src/jexer/TTimer.java b/src/jexer/TTimer.java new file mode 100644 index 00000000..0e711b96 --- /dev/null +++ b/src/jexer/TTimer.java @@ -0,0 +1,109 @@ +/** + * Jexer - Java Text User Interface + * + * License: LGPLv3 or later + * + * This module is licensed under the GNU Lesser General Public License + * Version 3. Please see the file "COPYING" in this directory for more + * information about the GNU Lesser General Public License Version 3. + * + * Copyright (C) 2015 Kevin Lamonte + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * http://www.gnu.org/licenses/, or write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * @author Kevin Lamonte [kevin.lamonte@gmail.com] + * @version 1 + */ +package jexer; + +import java.util.Date; + +/** + * TTimer implements a simple timer. + */ +public final class TTimer { + + /** + * If true, re-schedule after every tick. Note package private access. + */ + boolean recurring = false; + + /** + * Duration (in millis) between ticks if this is a recurring timer. + */ + private long duration = 0; + + /** + * The next time this timer needs to be ticked. + */ + private Date nextTick; + + /** + * Get the next time this timer needs to be ticked. Note package private + * access. + * + * @return time at which action should be called + */ + Date getNextTick() { + return nextTick; + } + + /** + * The action to perfom on a tick. + */ + private TAction action; + + /** + * Tick this timer. Note package private access. + */ + void tick() { + if (action != null) { + action.DO(); + } + // Set next tick + Date ticked = new Date(); + if (recurring) { + nextTick = new Date(ticked.getTime() + duration); + } + } + + /** + * Get the number of milliseconds between now and the next tick time. + * + * @return number of millis + */ + public long getMillis() { + return nextTick.getTime() - (new Date()).getTime(); + } + + /** + * Package private constructor. + * + * @param duration number of milliseconds to wait between ticks + * @param recurring if true, re-schedule this timer after every tick + * @param action to perform on next tick + */ + TTimer(final long duration, final boolean recurring, final TAction action) { + + this.recurring = recurring; + this.duration = duration; + this.action = action; + + nextTick = new Date((new Date()).getTime() + duration); + } + +} diff --git a/src/jexer/TWidget.java b/src/jexer/TWidget.java index 6c03a9cc..1ab54e29 100644 --- a/src/jexer/TWidget.java +++ b/src/jexer/TWidget.java @@ -1016,4 +1016,18 @@ public abstract class TWidget implements Comparable { return new TCheckbox(this, x, y, label, checked); } + /** + * Convenience function to add a progress bar to this container/window. + * + * @param x column relative to parent + * @param y row relative to parent + * @param width width of progress bar + * @param value initial value of percent complete + */ + public final TProgressBar addProgressBar(final int x, final int y, + final int width, final int value) { + + return new TProgressBar(this, x, y, width, value); + } + } -- 2.27.0