$(SRC_DIR)/jexer/TLabel.java \
$(SRC_DIR)/jexer/TCommand.java \
$(SRC_DIR)/jexer/TKeypress.java \
+ $(SRC_DIR)/jexer/TText.java \
$(SRC_DIR)/jexer/THScroller.java \
$(SRC_DIR)/jexer/TVScroller.java \
$(SRC_DIR)/jexer/TWidget.java \
$(TARGET_DIR)/jexer/TLabel.class \
$(TARGET_DIR)/jexer/TCommand.class \
$(TARGET_DIR)/jexer/TKeypress.class \
+ $(TARGET_DIR)/jexer/TText.class \
$(TARGET_DIR)/jexer/THScroller.class \
$(TARGET_DIR)/jexer/TVScroller.class \
$(TARGET_DIR)/jexer/TWidget.class \
0.0.1:
-- TDirectoryList
- TMessageBox
-- THScroller / TVScroller
-- TText
-- TTreeView
+- AWTBackend
0.0.2:
-- TEditor
+- TTreeView
+- TDirectoryList
- TFileOpen
-- TTerminal
0.0.3:
+- TEditor
+- TTerminal
+
+0.0.4:
+
- Bugs
+ - TTimer is jittery with I/O
- TSubMenu keyboard mnemonic not working
- kbDel assertion failure in TMenu (MID_CLEAR)
+ - TDirectoryList cannot be navigated only with keyboard
+ - TTreeView cannot be navigated only with keyboard
+ - RangeViolation after dragging scrollbar up/down
- TEditor
- Word wrap
- Forward/backward word
- Replace
- Cut/Copy/Paste
-0.0.4:
+0.1.0:
+- TWindow
+ - "Smart placement" for new windows
- ECMATerminal
- Mouse 1006 mode parsing
-- Bugs
- - TDirectoryList cannot be navigated only with keyboard
- - TTreeView cannot be navigated only with keyboard
- - RangeViolation after dragging scrollbar up/down
Wishlist features (2.0):
- TTerminal
- Handle resize events (pass to child process)
- xterm mouse handling
-- TWindow
- - "Smart placement" for new windows
- Screen
- Allow complex characters in putCharXY() and detect them in putStrXY().
- TComboBox
- TText
- TTerminal
- TComboBox
-- AWTBackend
*/
import jexer.*;
+import jexer.event.*;
import jexer.menu.*;
+class DemoTextWindow extends TWindow {
+
+ /**
+ * Hang onto my TText so I can resize it with the window.
+ */
+ private TText textField;
+
+ /**
+ * Public constructor.
+ */
+ public DemoTextWindow(TApplication parent) {
+ super(parent, "Text Areas", 0, 0, 44, 20, RESIZABLE);
+
+ textField = addText(
+"This is an example of a reflowable text field. Some example text follows.\n" +
+"\n" +
+"This library implements a text-based windowing system loosely\n" +
+"reminiscient of Borland's [Turbo\n" +
+"Vision](http://en.wikipedia.org/wiki/Turbo_Vision) library. For those\n" +
+"wishing to use the actual C++ Turbo Vision library, see [Sergio\n" +
+"Sigala's updated version](http://tvision.sourceforge.net/) that runs\n" +
+"on many more platforms.\n" +
+"\n" +
+"Currently the only console platform supported is Posix (tested on\n" +
+"Linux). Input/output is handled through terminal escape sequences\n" +
+"generated by the library itself: ncurses is not required or linked to. \n" +
+"xterm mouse tracking using UTF8 coordinates is supported.\n" +
+"\n" +
+"This library is licensed LGPL (\"GNU Lesser General Public License\")\n" +
+"version 3 or greater. See the file COPYING for the full license text,\n" +
+"which includes both the GPL v3 and the LGPL supplemental terms.\n" +
+"\n",
+ 1, 1, 40, 16);
+ }
+
+ /**
+ * Handle window/screen resize events.
+ *
+ * @param event resize event
+ */
+ @Override
+ public void onResize(final TResizeEvent event) {
+ if (event.getType() == TResizeEvent.Type.WIDGET) {
+ // Resize the text field
+ textField.setWidth(event.getWidth() - 4);
+ textField.setHeight(event.getHeight() - 4);
+ textField.reflow();
+ return;
+ }
+
+ // Pass to children instead
+ for (TWidget widget: getChildren()) {
+ widget.onResize(event);
+ }
+ }
+}
+
class DemoCheckboxWindow extends TWindow {
/**
- * Constructor
+ * Constructor.
*/
DemoCheckboxWindow(TApplication parent) {
this(parent, CENTERED | RESIZABLE);
}
/**
- * Constructor
+ * Constructor.
*/
DemoCheckboxWindow(TApplication parent, int flags) {
// Construct a demo window. X and Y don't matter because it
class DemoMainWindow extends TWindow {
- // Timer that increments a number
+
+ // Timer that increments a number.
private TTimer timer;
- // Timer label is updated with timerrr ticks
+ // Timer label is updated with timer ticks.
TLabel timerLabel;
/*
);
}
row += 2;
+ */
if (!isModal()) {
addLabel("Text areas", 1, row);
addButton("&Text", 35, row,
- {
- new DemoTextWindow(application);
+ new TAction() {
+ public void DO() {
+ new DemoTextWindow(getApplication());
+ }
}
);
}
row += 2;
+ /*
if (!isModal()) {
addLabel("Tree views", 1, row);
addButton("Tree&View", 35, row,
* The demo application itself.
*/
class DemoApplication extends TApplication {
+
/**
* Public constructor
*/
/**
* Main entry point.
*
- * @param args Command line arguments
+ * @param args Command line arguments
*/
- public static void main(String [] args) {
+ public static void main(final String [] args) {
try {
DemoApplication app = new DemoApplication();
app.run();
*/
package jexer;
+import jexer.bits.CellAttributes;
+import jexer.bits.GraphicsChars;
+import jexer.event.TMouseEvent;
+
/**
* THScroller implements a simple horizontal scroll bar.
*/
public final class THScroller extends TWidget {
+ /**
+ * Value that corresponds to being on the left edge of the scroll bar.
+ */
+ private int leftValue = 0;
+
+ /**
+ * Get the value that corresponds to being on the left edge of the scroll
+ * bar.
+ *
+ * @return the scroll value
+ */
+ public int getLeftValue() {
+ return leftValue;
+ }
+
+ /**
+ * Set the value that corresponds to being on the left edge of the
+ * scroll bar.
+ *
+ * @param leftValue the new scroll value
+ */
+ public void setLeftValue(final int leftValue) {
+ this.leftValue = leftValue;
+ }
+
+ /**
+ * Value that corresponds to being on the right edge of the scroll bar.
+ */
+ private int rightValue = 100;
+
+ /**
+ * Get the value that corresponds to being on the right edge of the
+ * scroll bar.
+ *
+ * @return the scroll value
+ */
+ public int getRightValue() {
+ return rightValue;
+ }
+
+ /**
+ * Set the value that corresponds to being on the right edge of the
+ * scroll bar.
+ *
+ * @param rightValue the new scroll value
+ */
+ public void setRightValue(final int rightValue) {
+ this.rightValue = rightValue;
+ }
+
+ /**
+ * Current value of the scroll.
+ */
+ private int value = 0;
+
+ /**
+ * Get current value of the scroll.
+ *
+ * @return the scroll value
+ */
+ public int getValue() {
+ return value;
+ }
+
+ /**
+ * Set current value of the scroll.
+ *
+ * @param value the new scroll value
+ */
+ public void setValue(final int value) {
+ this.value = value;
+ }
+
+ /**
+ * The increment for clicking on an arrow.
+ */
+ private int smallChange = 1;
+
+ /**
+ * Set the increment for clicking on an arrow.
+ *
+ * @param smallChange the new increment value
+ */
+ public void setSmallChange(final int smallChange) {
+ this.smallChange = smallChange;
+ }
+
+ /**
+ * The increment for clicking in the bar between the box and an arrow.
+ */
+ private int bigChange = 20;
+
+ /**
+ * Set the increment for clicking in the bar between the box and an
+ * arrow.
+ *
+ * @param bigChange the new increment value
+ */
+ public void setBigChange(final int bigChange) {
+ this.bigChange = bigChange;
+ }
+
+ /**
+ * When true, the user is dragging the scroll box.
+ */
+ private boolean inScroll = false;
+
+ /**
+ * Public constructor.
+ *
+ * @param parent parent widget
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width height of scroll bar
+ */
+ public THScroller(final TWidget parent, final int x, final int y,
+ final int width) {
+
+ // Set parent and window
+ super(parent);
+
+ setX(x);
+ setY(y);
+ setHeight(1);
+ setWidth(width);
+ }
+
+ /**
+ * Compute the position of the scroll box (a.k.a. grip, thumb).
+ *
+ * @return Y position of the box, between 1 and width - 2
+ */
+ private int boxPosition() {
+ return (getWidth() - 3) * (value - leftValue) / (rightValue - leftValue) + 1;
+ }
+
+ /**
+ * Draw a horizontal scroll bar.
+ */
+ @Override
+ public void draw() {
+ CellAttributes arrowColor = getTheme().getColor("tscroller.arrows");
+ CellAttributes barColor = getTheme().getColor("tscroller.bar");
+ getScreen().putCharXY(0, 0, GraphicsChars.CP437[0x11], arrowColor);
+ getScreen().putCharXY(getWidth() - 1, 0, GraphicsChars.CP437[0x10],
+ arrowColor);
+
+ // Place the box
+ if (rightValue > leftValue) {
+ getScreen().hLineXY(1, 0, getWidth() - 2, GraphicsChars.CP437[0xB1],
+ barColor);
+ getScreen().putCharXY(boxPosition(), 0, GraphicsChars.BOX,
+ arrowColor);
+ } else {
+ getScreen().hLineXY(1, 0, getWidth() - 2, GraphicsChars.HATCH,
+ barColor);
+ }
+
+ }
+
+ /**
+ * Perform a small step change left.
+ */
+ public void decrement() {
+ if (leftValue == rightValue) {
+ return;
+ }
+ value -= smallChange;
+ if (value < leftValue) {
+ value = leftValue;
+ }
+ }
+
+ /**
+ * Perform a small step change right.
+ */
+ public void increment() {
+ if (leftValue == rightValue) {
+ return;
+ }
+ value += smallChange;
+ if (value > rightValue) {
+ value = rightValue;
+ }
+ }
+
+ /**
+ * Handle mouse button releases.
+ *
+ * @param mouse mouse button release event
+ */
+ @Override
+ public void onMouseUp(final TMouseEvent mouse) {
+
+ if (inScroll) {
+ inScroll = false;
+ return;
+ }
+
+ if (rightValue == leftValue) {
+ return;
+ }
+
+ if ((mouse.getX() == 0)
+ && (mouse.getY() == 0)
+ ) {
+ // Clicked on the left arrow
+ decrement();
+ return;
+ }
+
+ if ((mouse.getY() == 0)
+ && (mouse.getX() == getWidth() - 1)
+ ) {
+ // Clicked on the right arrow
+ increment();
+ return;
+ }
+
+ if ((mouse.getY() == 0)
+ && (mouse.getX() > 0)
+ && (mouse.getX() < boxPosition())
+ ) {
+ // Clicked between the left arrow and the box
+ value -= bigChange;
+ if (value < leftValue) {
+ value = leftValue;
+ }
+ return;
+ }
+
+ if ((mouse.getY() == 0)
+ && (mouse.getX() > boxPosition())
+ && (mouse.getX() < getWidth() - 1)
+ ) {
+ // Clicked between the box and the right arrow
+ value += bigChange;
+ if (value > rightValue) {
+ value = rightValue;
+ }
+ return;
+ }
+ }
+
+ /**
+ * Handle mouse movement events.
+ *
+ * @param mouse mouse motion event
+ */
+ @Override
+ public void onMouseMotion(final TMouseEvent mouse) {
+
+ if (rightValue == leftValue) {
+ inScroll = false;
+ return;
+ }
+
+ if ((mouse.getMouse1())
+ && (inScroll)
+ && (mouse.getX() > 0)
+ && (mouse.getX() < getWidth() - 1)
+ ) {
+ // Recompute value based on new box position
+ value = (rightValue - leftValue) * (mouse.getX()) / (getWidth() - 3) + leftValue;
+ return;
+ }
+ inScroll = false;
+ }
+
+ /**
+ * Handle mouse button press events.
+ *
+ * @param mouse mouse button press event
+ */
+ @Override
+ public void onMouseDown(final TMouseEvent mouse) {
+ if (rightValue == leftValue) {
+ inScroll = false;
+ return;
+ }
+
+ if ((mouse.getY() == 0)
+ && (mouse.getX() == boxPosition())
+ ) {
+ inScroll = true;
+ return;
+ }
+
+ }
+
}
--- /dev/null
+/**
+ * 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.LinkedList;
+import java.util.List;
+
+import jexer.bits.CellAttributes;
+import jexer.event.TKeypressEvent;
+import jexer.event.TMouseEvent;
+import static jexer.TKeypress.*;
+
+/**
+ * TText implements a simple text windget.
+ */
+public final class TText extends TWidget {
+
+ /**
+ * Text to display.
+ */
+ private String text;
+
+ /**
+ * Text converted to lines.
+ */
+ private List<String> lines;
+
+ /**
+ * Text color.
+ */
+ private String colorKey;
+
+ /**
+ * Vertical scrollbar.
+ */
+ private TVScroller vScroller;
+
+ /**
+ * Horizontal scrollbar.
+ */
+ private THScroller hScroller;
+
+ /**
+ * Maximum width of a single line.
+ */
+ private int maxLineWidth;
+
+ /**
+ * Number of lines between each paragraph.
+ */
+ private int lineSpacing = 1;
+
+ /**
+ * Convenience method used by TWindowLoggerOutput.
+ *
+ * @param line new line to add
+ */
+ public void addLine(final String line) {
+ if (text.length() == 0) {
+ text = line;
+ } else {
+ text += "\n\n";
+ text += line;
+ }
+ reflow();
+ }
+
+ /**
+ * Recompute the bounds for the scrollbars.
+ */
+ private void computeBounds() {
+ maxLineWidth = 0;
+ for (String line: lines) {
+ if (line.length() > maxLineWidth) {
+ maxLineWidth = line.length();
+ }
+ }
+
+ vScroller.setBottomValue(lines.size() - getHeight() + 1);
+ if (vScroller.getBottomValue() < 0) {
+ vScroller.setBottomValue(0);
+ }
+ if (vScroller.getValue() > vScroller.getBottomValue()) {
+ vScroller.setValue(vScroller.getBottomValue());
+ }
+
+ hScroller.setRightValue(maxLineWidth - getWidth() + 1);
+ if (hScroller.getRightValue() < 0) {
+ hScroller.setRightValue(0);
+ }
+ if (hScroller.getValue() > hScroller.getRightValue()) {
+ hScroller.setValue(hScroller.getRightValue());
+ }
+ }
+
+ /**
+ * Insert newlines into a string to wrap it to a maximum column.
+ * Terminate the final string with a newline. Note that interior
+ * newlines are converted to spaces.
+ *
+ * @param str the string
+ * @param n the maximum number of characters in a line
+ * @return the wrapped string
+ */
+ private String wrap(final String str, final int n) {
+ assert (n > 0);
+
+ StringBuilder sb = new StringBuilder();
+ StringBuilder word = new StringBuilder();
+ int col = 0;
+ for (int i = 0; i < str.length(); i++) {
+ char ch = str.charAt(i);
+ if (ch == '\n') {
+ ch = ' ';
+ }
+ if (ch == ' ') {
+ sb.append(word.toString());
+ sb.append(ch);
+ if (word.length() >= n - 1) {
+ sb.append('\n');
+ col = 0;
+ }
+ word = new StringBuilder();
+ } else {
+ word.append(ch);
+ }
+
+ col++;
+ if (col >= n - 1) {
+ sb.append('\n');
+ col = 0;
+ }
+ }
+ sb.append(word.toString());
+ sb.append('\n');
+ return sb.toString();
+ }
+
+
+ /**
+ * Resize text and scrollbars for a new width/height.
+ */
+ public void reflow() {
+ // Reset the lines
+ lines.clear();
+
+ // Break up text into paragraphs
+ String [] paragraphs = text.split("\n\n");
+ for (String p: paragraphs) {
+ String paragraph = wrap(p, getWidth() - 1);
+ for (String line: paragraph.split("\n")) {
+ lines.add(line);
+ }
+ for (int i = 0; i < lineSpacing; i++) {
+ lines.add("");
+ }
+ }
+
+ // Start at the top
+ if (vScroller == null) {
+ vScroller = new TVScroller(this, getWidth() - 1, 0,
+ getHeight() - 1);
+ vScroller.setTopValue(0);
+ vScroller.setValue(0);
+ } else {
+ vScroller.setX(getWidth() - 1);
+ vScroller.setHeight(getHeight() - 1);
+ }
+ vScroller.setBigChange(getHeight() - 1);
+
+ // Start at the left
+ if (hScroller == null) {
+ hScroller = new THScroller(this, 0, getHeight() - 1,
+ getWidth() - 1);
+ hScroller.setLeftValue(0);
+ hScroller.setValue(0);
+ } else {
+ hScroller.setY(getHeight() - 1);
+ hScroller.setWidth(getWidth() - 1);
+ }
+ hScroller.setBigChange(getWidth() - 1);
+
+ computeBounds();
+ }
+
+ /**
+ * Public constructor.
+ *
+ * @param parent parent widget
+ * @param text text on the screen
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width width of text area
+ * @param height height of text area
+ */
+ public TText(final TWidget parent, final String text, final int x,
+ final int y, final int width, final int height) {
+
+ this(parent, text, x, y, width, height, "ttext");
+ }
+
+ /**
+ * Public constructor.
+ *
+ * @param parent parent widget
+ * @param text text on the screen
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width width of text area
+ * @param height height of text area
+ * @param colorKey ColorTheme key color to use for foreground text.
+ * Default is "ttext"
+ */
+ public TText(final TWidget parent, final String text, final int x,
+ final int y, final int width, final int height, final String colorKey) {
+
+ // Set parent and window
+ super(parent);
+
+ setX(x);
+ setY(y);
+ setWidth(width);
+ setHeight(height);
+ this.text = text;
+ this.colorKey = colorKey;
+
+ lines = new LinkedList<String>();
+
+ reflow();
+ }
+
+ /**
+ * Draw the text box.
+ */
+ @Override
+ public void draw() {
+ // Setup my color
+ CellAttributes color = getTheme().getColor(colorKey);
+
+ int begin = vScroller.getValue();
+ int topY = 0;
+ for (int i = begin; i < lines.size(); i++) {
+ String line = lines.get(i);
+ if (hScroller.getValue() < line.length()) {
+ line = line.substring(hScroller.getValue());
+ } else {
+ line = "";
+ }
+ String formatString = "%-" + Integer.toString(getWidth() - 1) + "s";
+ getScreen().putStrXY(0, topY, String.format(formatString, line),
+ color);
+ topY++;
+
+ if (topY >= getHeight() - 1) {
+ break;
+ }
+ }
+
+ // Pad the rest with blank lines
+ for (int i = topY; i < getHeight() - 1; i++) {
+ getScreen().hLineXY(0, i, getWidth() - 1, ' ', color);
+ }
+
+ }
+
+ /**
+ * Handle mouse press events.
+ *
+ * @param mouse mouse button press event
+ */
+ @Override
+ public void onMouseDown(final TMouseEvent mouse) {
+ if (mouse.getMouseWheelUp()) {
+ vScroller.decrement();
+ return;
+ }
+ if (mouse.getMouseWheelDown()) {
+ vScroller.increment();
+ return;
+ }
+
+ // Pass to children
+ super.onMouseDown(mouse);
+ }
+
+ /**
+ * Handle keystrokes.
+ *
+ * @param keypress keystroke event
+ */
+ @Override
+ public void onKeypress(final TKeypressEvent keypress) {
+ if (keypress.equals(kbLeft)) {
+ hScroller.decrement();
+ } else if (keypress.equals(kbRight)) {
+ hScroller.increment();
+ } else if (keypress.equals(kbUp)) {
+ vScroller.decrement();
+ } else if (keypress.equals(kbDown)) {
+ vScroller.increment();
+ } else if (keypress.equals(kbPgUp)) {
+ vScroller.bigDecrement();
+ } else if (keypress.equals(kbPgDn)) {
+ vScroller.bigIncrement();
+ } else if (keypress.equals(kbHome)) {
+ vScroller.toTop();
+ } else if (keypress.equals(kbEnd)) {
+ vScroller.toBottom();
+ } else {
+ // Pass other keys (tab etc.) on
+ super.onKeypress(keypress);
+ }
+ }
+
+}
*/
package jexer;
+import jexer.bits.CellAttributes;
+import jexer.bits.GraphicsChars;
+import jexer.event.TMouseEvent;
+
/**
* TVScroller implements a simple vertical scroll bar.
*/
public final class TVScroller extends TWidget {
+ /**
+ * Value that corresponds to being on the top edge of the scroll bar.
+ */
+ private int topValue = 0;
+
+ /**
+ * Get the value that corresponds to being on the top edge of the scroll
+ * bar.
+ *
+ * @return the scroll value
+ */
+ public int getTopValue() {
+ return topValue;
+ }
+
+ /**
+ * Set the value that corresponds to being on the top edge of the scroll
+ * bar.
+ *
+ * @param topValue the new scroll value
+ */
+ public void setTopValue(final int topValue) {
+ this.topValue = topValue;
+ }
+
+ /**
+ * Value that corresponds to being on the bottom edge of the scroll bar.
+ */
+ private int bottomValue = 100;
+
+ /**
+ * Get the value that corresponds to being on the bottom edge of the
+ * scroll bar.
+ *
+ * @return the scroll value
+ */
+ public int getBottomValue() {
+ return bottomValue;
+ }
+
+ /**
+ * Set the value that corresponds to being on the bottom edge of the
+ * scroll bar.
+ *
+ * @param bottomValue the new scroll value
+ */
+ public void setBottomValue(final int bottomValue) {
+ this.bottomValue = bottomValue;
+ }
+
+ /**
+ * Current value of the scroll.
+ */
+ private int value = 0;
+
+ /**
+ * Get current value of the scroll.
+ *
+ * @return the scroll value
+ */
+ public int getValue() {
+ return value;
+ }
+
+ /**
+ * Set current value of the scroll.
+ *
+ * @param value the new scroll value
+ */
+ public void setValue(final int value) {
+ this.value = value;
+ }
+
+ /**
+ * The increment for clicking on an arrow.
+ */
+ private int smallChange = 1;
+
+ /**
+ * Set the increment for clicking on an arrow.
+ *
+ * @param smallChange the new increment value
+ */
+ public void setSmallChange(final int smallChange) {
+ this.smallChange = smallChange;
+ }
+
+ /**
+ * The increment for clicking in the bar between the box and an arrow.
+ */
+ private int bigChange = 20;
+
+ /**
+ * Set the increment for clicking in the bar between the box and an
+ * arrow.
+ *
+ * @param bigChange the new increment value
+ */
+ public void setBigChange(final int bigChange) {
+ this.bigChange = bigChange;
+ }
+
+ /**
+ * When true, the user is dragging the scroll box.
+ */
+ private boolean inScroll = false;
+
+ /**
+ * Public constructor.
+ *
+ * @param parent parent widget
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width height of scroll bar
+ */
+ public TVScroller(final TWidget parent, final int x, final int y,
+ final int height) {
+
+ // Set parent and window
+ super(parent);
+
+ setX(x);
+ setY(y);
+ setHeight(height);
+ setWidth(1);
+ }
+
+ /**
+ * Compute the position of the scroll box (a.k.a. grip, thumb).
+ *
+ * @param Y position of the box, between 1 and height - 2
+ */
+ private int boxPosition() {
+ return (getHeight() - 3) * (value - topValue) / (bottomValue - topValue) + 1;
+ }
+
+ /**
+ * Draw a vertical scroll bar.
+ */
+ @Override
+ public void draw() {
+ CellAttributes arrowColor = getTheme().getColor("tscroller.arrows");
+ CellAttributes barColor = getTheme().getColor("tscroller.bar");
+ getScreen().putCharXY(0, 0, GraphicsChars.CP437[0x1E], arrowColor);
+ getScreen().putCharXY(0, getHeight() - 1, GraphicsChars.CP437[0x1F],
+ arrowColor);
+
+ // Place the box
+ if (bottomValue > topValue) {
+ getScreen().vLineXY(0, 1, getHeight() - 2,
+ GraphicsChars.CP437[0xB1], barColor);
+ getScreen().putCharXY(0, boxPosition(), GraphicsChars.BOX,
+ arrowColor);
+ } else {
+ getScreen().vLineXY(0, 1, getHeight() - 2, GraphicsChars.HATCH,
+ barColor);
+ }
+
+ }
+
+ /**
+ * Perform a small step change up.
+ */
+ public void decrement() {
+ if (bottomValue == topValue) {
+ return;
+ }
+ value -= smallChange;
+ if (value < topValue) {
+ value = topValue;
+ }
+ }
+
+ /**
+ * Perform a small step change down.
+ */
+ public void increment() {
+ if (bottomValue == topValue) {
+ return;
+ }
+ value += smallChange;
+ if (value > bottomValue) {
+ value = bottomValue;
+ }
+ }
+
+ /**
+ * Perform a big step change up.
+ */
+ public void bigDecrement() {
+ if (bottomValue == topValue) {
+ return;
+ }
+ value -= bigChange;
+ if (value < topValue) {
+ value = topValue;
+ }
+ }
+
+ /**
+ * Perform a big step change down.
+ */
+ public void bigIncrement() {
+ if (bottomValue == topValue) {
+ return;
+ }
+ value += bigChange;
+ if (value > bottomValue) {
+ value = bottomValue;
+ }
+ }
+
+ /**
+ * Go to the top edge of the scroller.
+ */
+ public void toTop() {
+ value = topValue;
+ }
+
+ /**
+ * Go to the bottom edge of the scroller.
+ */
+ public void toBottom() {
+ value = bottomValue;
+ }
+
+ /**
+ * Handle mouse button releases.
+ *
+ * @param mouse mouse button release event
+ */
+ @Override
+ public void onMouseUp(final TMouseEvent mouse) {
+ if (bottomValue == topValue) {
+ return;
+ }
+
+ if (inScroll) {
+ inScroll = false;
+ return;
+ }
+
+ if ((mouse.getX() == 0)
+ && (mouse.getY() == 0)
+ ) {
+ // Clicked on the top arrow
+ decrement();
+ return;
+ }
+
+ if ((mouse.getX() == 0)
+ && (mouse.getY() == getHeight() - 1)
+ ) {
+ // Clicked on the bottom arrow
+ increment();
+ return;
+ }
+
+ if ((mouse.getX() == 0)
+ && (mouse.getY() > 0)
+ && (mouse.getY() < boxPosition())
+ ) {
+ // Clicked between the top arrow and the box
+ value -= bigChange;
+ if (value < topValue) {
+ value = topValue;
+ }
+ return;
+ }
+
+ if ((mouse.getX() == 0)
+ && (mouse.getY() > boxPosition())
+ && (mouse.getY() < getHeight() - 1)
+ ) {
+ // Clicked between the box and the bottom arrow
+ value += bigChange;
+ if (value > bottomValue) {
+ value = bottomValue;
+ }
+ return;
+ }
+ }
+
+ /**
+ * Handle mouse movement events.
+ *
+ * @param mouse mouse motion event
+ */
+ @Override
+ public void onMouseMotion(final TMouseEvent mouse) {
+ if (bottomValue == topValue) {
+ return;
+ }
+
+ if ((mouse.getMouse1()) &&
+ (inScroll) &&
+ (mouse.getY() > 0) &&
+ (mouse.getY() < getHeight() - 1)
+ ) {
+ // Recompute value based on new box position
+ value = (bottomValue - topValue) * (mouse.getY()) / (getHeight() - 3) + topValue;
+ return;
+ }
+
+ inScroll = false;
+ }
+
+ /**
+ * Handle mouse press events.
+ *
+ * @param mouse mouse button press event
+ */
+ @Override
+ public void onMouseDown(final TMouseEvent mouse) {
+ if (bottomValue == topValue) {
+ return;
+ }
+
+ if ((mouse.getX() == 0)
+ && (mouse.getY() == boxPosition())
+ ) {
+ inScroll = true;
+ return;
+ }
+ }
+
}
updateAction);
}
+ /**
+ * Convenience function to add a scrollable text box to this
+ * container/window.
+ *
+ * @param text text on the screen
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width width of text area
+ * @param height height of text area
+ * @param colorKey ColorTheme key color to use for foreground text
+ * @return the new text box
+ */
+ public TText addText(final String text, final int x,
+ final int y, final int width, final int height, final String colorKey) {
+
+ return new TText(this, text, x, y, width, height, colorKey);
+ }
+
+ /**
+ * Convenience function to add a scrollable text box to this
+ * container/window.
+ *
+ * @param text text on the screen
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width width of text area
+ * @param height height of text area
+ * @return the new text box
+ */
+ public TText addText(final String text, final int x, final int y,
+ final int width, final int height) {
+
+ return new TText(this, text, x, y, width, height, "ttext");
+ }
+
+
}