From 56661844475522242093c8858ceb20fb15589da5 Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Sun, 16 Jul 2017 13:24:20 -0400 Subject: [PATCH] Common Scrollable interface --- docs/TODO.md | 13 - src/jexer/Scrollable.java | 284 +++++++++++ src/jexer/TApplication.java | 1 - src/jexer/TDirectoryTreeItem.java | 6 +- src/jexer/TFileOpenBox.java | 2 +- src/jexer/THScroller.java | 59 +++ src/jexer/TList.java | 115 ++--- src/jexer/TScrollableWidget.java | 593 +++++++++++++++++++++++ src/jexer/TScrollableWindow.java | 602 ++++++++++++++++++++++++ src/jexer/TTerminalWindow.java | 51 +- src/jexer/TText.java | 71 ++- src/jexer/TTreeItem.java | 10 +- src/jexer/TTreeView.java | 118 ++--- src/jexer/TVScroller.java | 19 + src/jexer/demos/DemoTextWindow.java | 6 +- src/jexer/demos/DemoTreeViewWindow.java | 8 +- 16 files changed, 1695 insertions(+), 263 deletions(-) create mode 100644 src/jexer/Scrollable.java create mode 100644 src/jexer/TScrollableWidget.java create mode 100644 src/jexer/TScrollableWindow.java diff --git a/docs/TODO.md b/docs/TODO.md index 29e5194..a3455e5 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -11,22 +11,9 @@ BUG: TTreeView.reflow() doesn't keep the vertical dot within the 0.0.5 -- Scrollable: - - TTreeView - - TText - - TList - - TTerminalWindow - - TScrollableWindow - - - TWindow: - UNCLOSABLE (#8) - - H/V scrollbars (#4) - - Expose toTop()/toLeft()/... -- TText: - - Scrollbars adjust automatically - - Expose toTop()/toLeft()/... - TEditor - Eliminate all Eclipse warnings diff --git a/src/jexer/Scrollable.java b/src/jexer/Scrollable.java new file mode 100644 index 0000000..5a06582 --- /dev/null +++ b/src/jexer/Scrollable.java @@ -0,0 +1,284 @@ +/* + * Jexer - Java Text User Interface + * + * The MIT License (MIT) + * + * Copyright (C) 2017 Kevin Lamonte + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * @author Kevin Lamonte [kevin.lamonte@gmail.com] + * @version 1 + */ +package jexer; + +import jexer.event.TKeypressEvent; +import jexer.event.TMouseEvent; +import static jexer.TKeypress.*; + +/** + * Scrollable provides a public API for horizontal and vertical scrollbars. + * Note that not all Scrollables support both horizontal and vertical + * scrolling; for those that only support a subset, it is expected that the + * methods corresponding to the missing scrollbar quietly succeed without + * throwing any exceptions. + */ +public interface Scrollable { + + /** + * Get the horizontal scrollbar, or null if this Viewport does not + * support horizontal scrolling. + * + * @return the horizontal scrollbar + */ + public THScroller getHorizontalScroller(); + + /** + * Get the vertical scrollbar, or null if this Viewport does not support + * vertical scrolling. + * + * @return the vertical scrollbar + */ + public TVScroller getVerticalScroller(); + + /** + * Get the value that corresponds to being on the top edge of the + * vertical scroll bar. + * + * @return the scroll value + */ + public int getTopValue(); + + /** + * Set the value that corresponds to being on the top edge of the + * vertical scroll bar. + * + * @param topValue the new scroll value + */ + public void setTopValue(final int topValue); + + /** + * Get the value that corresponds to being on the bottom edge of the + * vertical scroll bar. + * + * @return the scroll value + */ + public int getBottomValue(); + + /** + * Set the value that corresponds to being on the bottom edge of the + * vertical scroll bar. + * + * @param bottomValue the new scroll value + */ + public void setBottomValue(final int bottomValue); + + /** + * Get current value of the vertical scroll. + * + * @return the scroll value + */ + public int getVerticalValue(); + + /** + * Set current value of the vertical scroll. + * + * @param value the new scroll value + */ + public void setVerticalValue(final int value); + + /** + * Get the increment for clicking on an arrow on the vertical scrollbar. + * + * @return the increment value + */ + public int getVerticalSmallChange(); + + /** + * Set the increment for clicking on an arrow on the vertical scrollbar. + * + * @param smallChange the new increment value + */ + public void setVerticalSmallChange(final int smallChange); + + /** + * Get the increment for clicking in the bar between the box and an + * arrow on the vertical scrollbar. + * + * @return the increment value + */ + public int getVerticalBigChange(); + + /** + * Set the increment for clicking in the bar between the box and an + * arrow on the vertical scrollbar. + * + * @param bigChange the new increment value + */ + public void setVerticalBigChange(final int bigChange); + + /** + * Perform a small step change up. + */ + public void verticalDecrement(); + + /** + * Perform a small step change down. + */ + public void verticalIncrement(); + + /** + * Perform a big step change up. + */ + public void bigVerticalDecrement(); + + /** + * Perform a big step change down. + */ + public void bigVerticalIncrement(); + + /** + * Go to the top edge of the vertical scroller. + */ + public void toTop(); + + /** + * Go to the bottom edge of the vertical scroller. + */ + public void toBottom(); + + /** + * Get the value that corresponds to being on the left edge of the + * horizontal scroll bar. + * + * @return the scroll value + */ + public int getLeftValue(); + + /** + * Set the value that corresponds to being on the left edge of the + * horizontal scroll bar. + * + * @param leftValue the new scroll value + */ + public void setLeftValue(final int leftValue); + + /** + * Get the value that corresponds to being on the right edge of the + * horizontal scroll bar. + * + * @return the scroll value + */ + public int getRightValue(); + + /** + * Set the value that corresponds to being on the right edge of the + * horizontal scroll bar. + * + * @param rightValue the new scroll value + */ + public void setRightValue(final int rightValue); + + /** + * Get current value of the horizontal scroll. + * + * @return the scroll value + */ + public int getHorizontalValue(); + + /** + * Set current value of the horizontal scroll. + * + * @param value the new scroll value + */ + public void setHorizontalValue(final int value); + + /** + * Get the increment for clicking on an arrow on the horizontal + * scrollbar. + * + * @return the increment value + */ + public int getHorizontalSmallChange(); + + /** + * Set the increment for clicking on an arrow on the horizontal + * scrollbar. + * + * @param smallChange the new increment value + */ + public void setHorizontalSmallChange(final int smallChange); + + /** + * Get the increment for clicking in the bar between the box and an + * arrow on the horizontal scrollbar. + * + * @return the increment value + */ + public int getHorizontalBigChange(); + + /** + * Set the increment for clicking in the bar between the box and an + * arrow on the horizontal scrollbar. + * + * @param bigChange the new increment value + */ + public void setHorizontalBigChange(final int bigChange); + + /** + * Perform a small step change left. + */ + public void horizontalDecrement(); + + /** + * Perform a small step change right. + */ + public void horizontalIncrement(); + + /** + * Perform a big step change left. + */ + public void bigHorizontalDecrement(); + + /** + * Perform a big step change right. + */ + public void bigHorizontalIncrement(); + + /** + * Go to the left edge of the horizontal scroller. + */ + public void toLeft(); + + /** + * Go to the right edge of the horizontal scroller. + */ + public void toRight(); + + /** + * Go to the top-left edge of the horizontal and vertical scrollers. + */ + public void toHome(); + + /** + * Go to the bottom-right edge of the horizontal and vertical scrollers. + */ + public void toEnd(); + +} diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index 6d71dc7..b80e7a3 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -2609,7 +2609,6 @@ public class TApplication implements Runnable { /** * Convenience function to create a new window and make it active. * - * @param application TApplication that manages this window * @param title window title, will be centered along the top border * @param x column relative to parent * @param y row relative to parent diff --git a/src/jexer/TDirectoryTreeItem.java b/src/jexer/TDirectoryTreeItem.java index 1cf377e..6d5d018 100644 --- a/src/jexer/TDirectoryTreeItem.java +++ b/src/jexer/TDirectoryTreeItem.java @@ -76,7 +76,7 @@ public class TDirectoryTreeItem extends TTreeItem { setExpandable(true); if (!isExpanded() || !isExpandable()) { - getTreeView().reflow(); + getTreeView().reflowData(); return; } @@ -103,7 +103,7 @@ public class TDirectoryTreeItem extends TTreeItem { } Collections.sort(getChildren()); - getTreeView().reflow(); + getTreeView().reflowData(); } /** @@ -212,6 +212,6 @@ public class TDirectoryTreeItem extends TTreeItem { getTreeView().setSelected(childFile); setExpanded(oldExpanded); } - getTreeView().reflow(); + getTreeView().reflowData(); } } diff --git a/src/jexer/TFileOpenBox.java b/src/jexer/TFileOpenBox.java index ebf2daa..0a69a97 100644 --- a/src/jexer/TFileOpenBox.java +++ b/src/jexer/TFileOpenBox.java @@ -126,7 +126,7 @@ public final class TFileOpenBox extends TWindow { treeViewRoot = new TDirectoryTreeItem(treeView, newFilename, true); treeView.setTreeRoot(treeViewRoot, true); - treeView.reflow(); + treeView.reflowData(); openButton.setEnabled(false); directoryList.setPath(newFilename); } diff --git a/src/jexer/THScroller.java b/src/jexer/THScroller.java index d488ebf..9e9b372 100644 --- a/src/jexer/THScroller.java +++ b/src/jexer/THScroller.java @@ -115,6 +115,15 @@ public final class THScroller extends TWidget { */ private int smallChange = 1; + /** + * Get the increment for clicking on an arrow. + * + * @return the increment value + */ + public int getSmallChange() { + return smallChange; + } + /** * Set the increment for clicking on an arrow. * @@ -129,6 +138,16 @@ public final class THScroller extends TWidget { */ private int bigChange = 20; + /** + * Set the increment for clicking in the bar between the box and an + * arrow. + * + * @return the increment value + */ + public int getBigChange() { + return bigChange; + } + /** * Set the increment for clicking in the bar between the box and an * arrow. @@ -218,6 +237,46 @@ public final class THScroller extends TWidget { } } + /** + * Perform a big step change left. + */ + public void bigDecrement() { + if (leftValue == rightValue) { + return; + } + value -= bigChange; + if (value < leftValue) { + value = leftValue; + } + } + + /** + * Perform a big step change right. + */ + public void bigIncrement() { + if (rightValue == leftValue) { + return; + } + value += bigChange; + if (value > rightValue) { + value = rightValue; + } + } + + /** + * Go to the left edge of the scroller. + */ + public void toLeft() { + value = leftValue; + } + + /** + * Go to the right edge of the scroller. + */ + public void toRight() { + value = rightValue; + } + /** * Handle mouse button releases. * diff --git a/src/jexer/TList.java b/src/jexer/TList.java index 2312889..7160048 100644 --- a/src/jexer/TList.java +++ b/src/jexer/TList.java @@ -39,7 +39,7 @@ import static jexer.TKeypress.*; /** * TList shows a list of strings, and lets the user select one. */ -public class TList extends TWidget { +public class TList extends TScrollableWidget { /** * The list of strings to display. @@ -89,35 +89,7 @@ public class TList extends TWidget { public final void setList(final List list) { strings.clear(); strings.addAll(list); - reflow(); - } - - /** - * Vertical scrollbar. - */ - private TVScroller vScroller; - - /** - * Get the vertical scrollbar. This is used by subclasses. - * - * @return the vertical scrollbar - */ - public final TVScroller getVScroller() { - return vScroller; - } - - /** - * Horizontal scrollbar. - */ - private THScroller hScroller; - - /** - * Get the horizontal scrollbar. This is used by subclasses. - * - * @return the horizontal scrollbar - */ - public final THScroller getHScroller() { - return hScroller; + reflowData(); } /** @@ -160,7 +132,8 @@ public class TList extends TWidget { /** * Resize for a new width/height. */ - public void reflow() { + @Override + public void reflowData() { // Reset the lines selectedString = -1; @@ -173,37 +146,15 @@ public class TList extends TWidget { } } - // Start at the top - if (vScroller == null) { - vScroller = new TVScroller(this, getWidth() - 1, 0, - getHeight() - 1); - } else { - vScroller.setX(getWidth() - 1); - vScroller.setHeight(getHeight() - 1); + setBottomValue(strings.size() - getHeight() + 1); + if (getBottomValue() < 0) { + setBottomValue(0); } - vScroller.setBottomValue(strings.size() - getHeight() + 1); - vScroller.setTopValue(0); - vScroller.setValue(0); - if (vScroller.getBottomValue() < 0) { - vScroller.setBottomValue(0); - } - vScroller.setBigChange(getHeight() - 1); - // Start at the left - if (hScroller == null) { - hScroller = new THScroller(this, 0, getHeight() - 1, - getWidth() - 1); - } else { - hScroller.setY(getHeight() - 1); - hScroller.setWidth(getWidth() - 1); - } - hScroller.setRightValue(maxLineWidth - getWidth() + 1); - hScroller.setLeftValue(0); - hScroller.setValue(0); - if (hScroller.getRightValue() < 0) { - hScroller.setRightValue(0); + setRightValue(maxLineWidth - getWidth() + 1); + if (getRightValue() < 0) { + setRightValue(0); } - hScroller.setBigChange(getWidth() - 1); } /** @@ -244,7 +195,10 @@ public class TList extends TWidget { if (strings != null) { this.strings.addAll(strings); } - reflow(); + + hScroller = new THScroller(this, 0, getHeight() - 1, getWidth() - 1); + vScroller = new TVScroller(this, getWidth() - 1, 0, getHeight() - 1); + reflowData(); } /** @@ -272,7 +226,10 @@ public class TList extends TWidget { if (strings != null) { this.strings.addAll(strings); } - reflow(); + + hScroller = new THScroller(this, 0, getHeight() - 1, getWidth() - 1); + vScroller = new TVScroller(this, getWidth() - 1, 0, getHeight() - 1); + reflowData(); } /** @@ -281,12 +238,12 @@ public class TList extends TWidget { @Override public void draw() { CellAttributes color = null; - int begin = vScroller.getValue(); + int begin = getVerticalValue(); int topY = 0; for (int i = begin; i < strings.size(); i++) { String line = strings.get(i); - if (hScroller.getValue() < line.length()) { - line = line.substring(hScroller.getValue()); + if (getHorizontalValue() < line.length()) { + line = line.substring(getHorizontalValue()); } else { line = ""; } @@ -326,20 +283,20 @@ public class TList extends TWidget { @Override public void onMouseDown(final TMouseEvent mouse) { if (mouse.isMouseWheelUp()) { - vScroller.decrement(); + verticalDecrement(); return; } if (mouse.isMouseWheelDown()) { - vScroller.increment(); + verticalIncrement(); return; } if ((mouse.getX() < getWidth() - 1) && (mouse.getY() < getHeight() - 1)) { - if (vScroller.getValue() + mouse.getY() < strings.size()) { - selectedString = vScroller.getValue() + mouse.getY(); + if (getVerticalValue() + mouse.getY() < strings.size()) { + selectedString = getVerticalValue() + mouse.getY(); + dispatchEnter(); } - dispatchEnter(); return; } @@ -355,15 +312,15 @@ public class TList extends TWidget { @Override public void onKeypress(final TKeypressEvent keypress) { if (keypress.equals(kbLeft)) { - hScroller.decrement(); + horizontalDecrement(); } else if (keypress.equals(kbRight)) { - hScroller.increment(); + horizontalIncrement(); } else if (keypress.equals(kbUp)) { if (strings.size() > 0) { if (selectedString >= 0) { if (selectedString > 0) { - if (selectedString - vScroller.getValue() == 0) { - vScroller.decrement(); + if (selectedString - getVerticalValue() == 0) { + verticalDecrement(); } selectedString--; } @@ -379,8 +336,8 @@ public class TList extends TWidget { if (selectedString >= 0) { if (selectedString < strings.size() - 1) { selectedString++; - if (selectedString - vScroller.getValue() == getHeight() - 1) { - vScroller.increment(); + if (selectedString - getVerticalValue() == getHeight() - 1) { + verticalIncrement(); } } } else { @@ -391,7 +348,7 @@ public class TList extends TWidget { dispatchMove(); } } else if (keypress.equals(kbPgUp)) { - vScroller.bigDecrement(); + bigVerticalDecrement(); if (selectedString >= 0) { selectedString -= getHeight() - 1; if (selectedString < 0) { @@ -402,7 +359,7 @@ public class TList extends TWidget { dispatchMove(); } } else if (keypress.equals(kbPgDn)) { - vScroller.bigIncrement(); + bigVerticalIncrement(); if (selectedString >= 0) { selectedString += getHeight() - 1; if (selectedString > strings.size() - 1) { @@ -413,7 +370,7 @@ public class TList extends TWidget { dispatchMove(); } } else if (keypress.equals(kbHome)) { - vScroller.toTop(); + toTop(); if (strings.size() > 0) { selectedString = 0; } @@ -421,7 +378,7 @@ public class TList extends TWidget { dispatchMove(); } } else if (keypress.equals(kbEnd)) { - vScroller.toBottom(); + toBottom(); if (strings.size() > 0) { selectedString = strings.size() - 1; } diff --git a/src/jexer/TScrollableWidget.java b/src/jexer/TScrollableWidget.java new file mode 100644 index 0000000..a4ba70a --- /dev/null +++ b/src/jexer/TScrollableWidget.java @@ -0,0 +1,593 @@ +/* + * Jexer - Java Text User Interface + * + * The MIT License (MIT) + * + * Copyright (C) 2017 Kevin Lamonte + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * @author Kevin Lamonte [kevin.lamonte@gmail.com] + * @version 1 + */ +package jexer; + +import jexer.event.TResizeEvent; + +/** + * TScrollableWidget is a convenience superclass for widgets that have + * scrollbars. + */ +public class TScrollableWidget extends TWidget implements Scrollable { + + /** + * The horizontal scrollbar. + */ + protected THScroller hScroller = null; + + /** + * The vertical scrollbar. + */ + protected TVScroller vScroller = null; + + /** + * Place the scrollbars on the edge of this widget, and adjust bigChange + * to match the new size. This is called by onResize(). + */ + protected void placeScrollbars() { + if (hScroller != null) { + hScroller.setY(getHeight() - 1); + hScroller.setWidth(getWidth() - 1); + hScroller.setBigChange(getWidth() - 1); + } + if (vScroller != null) { + vScroller.setX(getWidth() - 1); + vScroller.setHeight(getHeight() - 1); + vScroller.setBigChange(getHeight() - 1); + } + } + + /** + * Recompute whatever data is displayed by this widget. + */ + public void reflowData() { + // Default: nothing to do + } + + /** + * Handle window/screen resize events. + * + * @param event resize event + */ + @Override + public void onResize(final TResizeEvent event) { + if (event.getType() == TResizeEvent.Type.WIDGET) { + setWidth(event.getWidth()); + setHeight(event.getHeight()); + + reflowData(); + placeScrollbars(); + return; + } else { + super.onResize(event); + } + } + + /** + * Protected constructor. + * + * @param parent parent widget + */ + protected TScrollableWidget(final TWidget parent) { + super(parent); + } + + /** + * Protected constructor. + * + * @param parent parent widget + * @param x column relative to parent + * @param y row relative to parent + * @param width width of widget + * @param height height of widget + */ + protected TScrollableWidget(final TWidget parent, final int x, final int y, + final int width, final int height) { + + super(parent, x, y, width, height); + } + + /** + * Protected constructor used by subclasses that are disabled by default. + * + * @param parent parent widget + * @param enabled if true assume enabled + */ + protected TScrollableWidget(final TWidget parent, final boolean enabled) { + + super(parent, enabled); + } + + /** + * Protected constructor used by subclasses that are disabled by default. + * + * @param parent parent widget + * @param enabled if true assume enabled + * @param x column relative to parent + * @param y row relative to parent + * @param width width of widget + * @param height height of widget + */ + protected TScrollableWidget(final TWidget parent, final boolean enabled, + final int x, final int y, final int width, final int height) { + + super(parent, enabled, x, y, width, height); + } + + /** + * Get the horizontal scrollbar, or null if this Viewport does not + * support horizontal scrolling. + * + * @return the horizontal scrollbar + */ + public THScroller getHorizontalScroller() { + return hScroller; + } + + /** + * Get the vertical scrollbar, or null if this Viewport does not support + * vertical scrolling. + * + * @return the vertical scrollbar + */ + public TVScroller getVerticalScroller() { + return vScroller; + } + + /** + * Get the value that corresponds to being on the top edge of the + * vertical scroll bar. + * + * @return the scroll value + */ + public int getTopValue() { + if (vScroller == null) { + return 0; + } else { + return vScroller.getTopValue(); + } + } + + /** + * Set the value that corresponds to being on the top edge of the + * vertical scroll bar. + * + * @param topValue the new scroll value + */ + public void setTopValue(final int topValue) { + if (vScroller == null) { + return; + } else { + vScroller.setTopValue(topValue); + } + } + + /** + * Get the value that corresponds to being on the bottom edge of the + * vertical scroll bar. + * + * @return the scroll value + */ + public int getBottomValue() { + if (vScroller == null) { + return 0; + } else { + return vScroller.getBottomValue(); + } + } + + /** + * Set the value that corresponds to being on the bottom edge of the + * vertical scroll bar. + * + * @param bottomValue the new scroll value + */ + public void setBottomValue(final int bottomValue) { + if (vScroller == null) { + return; + } else { + vScroller.setBottomValue(bottomValue); + } + } + + /** + * Get current value of the vertical scroll. + * + * @return the scroll value + */ + public int getVerticalValue() { + if (vScroller == null) { + return 0; + } else { + return vScroller.getValue(); + } + } + + /** + * Set current value of the vertical scroll. + * + * @param value the new scroll value + */ + public void setVerticalValue(final int value) { + if (vScroller == null) { + return; + } else { + vScroller.setValue(value); + } + } + + /** + * Get the increment for clicking on an arrow on the vertical scrollbar. + * + * @return the increment value + */ + public int getVerticalSmallChange() { + if (vScroller == null) { + return 0; + } else { + return vScroller.getSmallChange(); + } + } + + /** + * Set the increment for clicking on an arrow on the vertical scrollbar. + * + * @param smallChange the new increment value + */ + public void setVerticalSmallChange(final int smallChange) { + if (vScroller == null) { + return; + } else { + vScroller.setSmallChange(smallChange); + } + } + + /** + * Get the increment for clicking in the bar between the box and an + * arrow on the vertical scrollbar. + * + * @return the increment value + */ + public int getVerticalBigChange() { + if (vScroller == null) { + return 0; + } else { + return vScroller.getBigChange(); + } + } + + /** + * Set the increment for clicking in the bar between the box and an + * arrow on the vertical scrollbar. + * + * @param bigChange the new increment value + */ + public void setVerticalBigChange(final int bigChange) { + if (vScroller == null) { + return; + } else { + vScroller.setBigChange(bigChange); + } + } + + /** + * Perform a small step change up. + */ + public void verticalDecrement() { + if (vScroller == null) { + return; + } else { + vScroller.decrement(); + } + } + + /** + * Perform a small step change down. + */ + public void verticalIncrement() { + if (vScroller == null) { + return; + } else { + vScroller.increment(); + } + } + + /** + * Perform a big step change up. + */ + public void bigVerticalDecrement() { + if (vScroller == null) { + return; + } else { + vScroller.bigDecrement(); + } + } + + /** + * Perform a big step change down. + */ + public void bigVerticalIncrement() { + if (vScroller == null) { + return; + } else { + vScroller.bigIncrement(); + } + } + + /** + * Go to the top edge of the vertical scroller. + */ + public void toTop() { + if (vScroller == null) { + return; + } else { + vScroller.toTop(); + } + } + + /** + * Go to the bottom edge of the vertical scroller. + */ + public void toBottom() { + if (vScroller == null) { + return; + } else { + vScroller.toBottom(); + } + } + + /** + * Get the value that corresponds to being on the left edge of the + * horizontal scroll bar. + * + * @return the scroll value + */ + public int getLeftValue() { + if (hScroller == null) { + return 0; + } else { + return hScroller.getLeftValue(); + } + } + + /** + * Set the value that corresponds to being on the left edge of the + * horizontal scroll bar. + * + * @param leftValue the new scroll value + */ + public void setLeftValue(final int leftValue) { + if (hScroller == null) { + return; + } else { + hScroller.setLeftValue(leftValue); + } + } + + /** + * Get the value that corresponds to being on the right edge of the + * horizontal scroll bar. + * + * @return the scroll value + */ + public int getRightValue() { + if (hScroller == null) { + return 0; + } else { + return hScroller.getRightValue(); + } + } + + /** + * Set the value that corresponds to being on the right edge of the + * horizontal scroll bar. + * + * @param rightValue the new scroll value + */ + public void setRightValue(final int rightValue) { + if (hScroller == null) { + return; + } else { + hScroller.setRightValue(rightValue); + } + } + + /** + * Get current value of the horizontal scroll. + * + * @return the scroll value + */ + public int getHorizontalValue() { + if (hScroller == null) { + return 0; + } else { + return hScroller.getValue(); + } + } + + /** + * Set current value of the horizontal scroll. + * + * @param value the new scroll value + */ + public void setHorizontalValue(final int value) { + if (hScroller == null) { + return; + } else { + hScroller.setValue(value); + } + } + + /** + * Get the increment for clicking on an arrow on the horizontal + * scrollbar. + * + * @return the increment value + */ + public int getHorizontalSmallChange() { + if (hScroller == null) { + return 0; + } else { + return hScroller.getSmallChange(); + } + } + + /** + * Set the increment for clicking on an arrow on the horizontal + * scrollbar. + * + * @param smallChange the new increment value + */ + public void setHorizontalSmallChange(final int smallChange) { + if (hScroller == null) { + return; + } else { + hScroller.setSmallChange(smallChange); + } + } + + /** + * Get the increment for clicking in the bar between the box and an + * arrow on the horizontal scrollbar. + * + * @return the increment value + */ + public int getHorizontalBigChange() { + if (hScroller == null) { + return 0; + } else { + return hScroller.getBigChange(); + } + } + + /** + * Set the increment for clicking in the bar between the box and an + * arrow on the horizontal scrollbar. + * + * @param bigChange the new increment value + */ + public void setHorizontalBigChange(final int bigChange) { + if (hScroller == null) { + return; + } else { + hScroller.setBigChange(bigChange); + } + } + + /** + * Perform a small step change left. + */ + public void horizontalDecrement() { + if (hScroller == null) { + return; + } else { + hScroller.decrement(); + } + } + + /** + * Perform a small step change right. + */ + public void horizontalIncrement() { + if (hScroller == null) { + return; + } else { + hScroller.increment(); + } + } + + /** + * Perform a big step change left. + */ + public void bigHorizontalDecrement() { + if (hScroller == null) { + return; + } else { + hScroller.bigDecrement(); + } + } + + /** + * Perform a big step change right. + */ + public void bigHorizontalIncrement() { + if (hScroller == null) { + return; + } else { + hScroller.bigIncrement(); + } + } + + /** + * Go to the left edge of the horizontal scroller. + */ + public void toLeft() { + if (hScroller == null) { + return; + } else { + hScroller.toLeft(); + } + } + + /** + * Go to the right edge of the horizontal scroller. + */ + public void toRight() { + if (hScroller == null) { + return; + } else { + hScroller.toRight(); + } + } + + /** + * Go to the top-left edge of the horizontal and vertical scrollers. + */ + public void toHome() { + if (hScroller != null) { + hScroller.toLeft(); + } + if (vScroller != null) { + vScroller.toTop(); + } + } + + /** + * Go to the bottom-right edge of the horizontal and vertical scrollers. + */ + public void toEnd() { + if (hScroller != null) { + hScroller.toRight(); + } + if (vScroller != null) { + vScroller.toBottom(); + } + } + +} diff --git a/src/jexer/TScrollableWindow.java b/src/jexer/TScrollableWindow.java new file mode 100644 index 0000000..8935ac3 --- /dev/null +++ b/src/jexer/TScrollableWindow.java @@ -0,0 +1,602 @@ +/* + * Jexer - Java Text User Interface + * + * The MIT License (MIT) + * + * Copyright (C) 2017 Kevin Lamonte + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * @author Kevin Lamonte [kevin.lamonte@gmail.com] + * @version 1 + */ +package jexer; + +import jexer.event.TResizeEvent; + +/** + * TScrollableWindow is a convenience superclass for windows that have + * scrollbars. + */ +public class TScrollableWindow extends TWindow implements Scrollable { + + /** + * The horizontal scrollbar. + */ + protected THScroller hScroller = null; + + /** + * The vertical scrollbar. + */ + protected TVScroller vScroller = null; + + /** + * Place the scrollbars on the edge of this widget, and adjust bigChange + * to match the new size. This is called by onResize(). + */ + protected void placeScrollbars() { + if (hScroller != null) { + hScroller.setY(getHeight() - 2); + hScroller.setWidth(getWidth() - 3); + hScroller.setBigChange(getWidth() - 3); + } + if (vScroller != null) { + vScroller.setX(getWidth() - 2); + vScroller.setHeight(getHeight() - 2); + vScroller.setBigChange(getHeight() - 2); + } + } + + /** + * Recompute whatever data is displayed by this widget. + */ + public void reflowData() { + // Default: nothing to do + } + + /** + * Handle window/screen resize events. + * + * @param event resize event + */ + @Override + public void onResize(final TResizeEvent event) { + if (event.getType() == TResizeEvent.Type.WIDGET) { + reflowData(); + placeScrollbars(); + return; + } else { + super.onResize(event); + } + } + + /** + * Public constructor. Window will be located at (0, 0). + * + * @param application TApplication that manages this window + * @param title window title, will be centered along the top border + * @param width width of window + * @param height height of window + */ + public TScrollableWindow(final TApplication application, final String title, + final int width, final int height) { + + super(application, title, width, height); + } + + /** + * Public constructor. Window will be located at (0, 0). + * + * @param application TApplication that manages this window + * @param title window title, will be centered along the top border + * @param width width of window + * @param height height of window + * @param flags bitmask of RESIZABLE, CENTERED, or MODAL + */ + public TScrollableWindow(final TApplication application, final String title, + final int width, final int height, final int flags) { + + super(application, title, width, height, flags); + } + + /** + * Public constructor. + * + * @param application TApplication that manages this window + * @param title window title, will be centered along the top border + * @param x column relative to parent + * @param y row relative to parent + * @param width width of window + * @param height height of window + */ + public TScrollableWindow(final TApplication application, final String title, + final int x, final int y, final int width, final int height) { + + super(application, title, x, y, width, height); + } + + /** + * Public constructor. + * + * @param application TApplication that manages this window + * @param title window title, will be centered along the top border + * @param x column relative to parent + * @param y row relative to parent + * @param width width of window + * @param height height of window + * @param flags mask of RESIZABLE, CENTERED, or MODAL + */ + public TScrollableWindow(final TApplication application, final String title, + final int x, final int y, final int width, final int height, + final int flags) { + + super(application, title, x, y, width, height, flags); + } + + /** + * Get the horizontal scrollbar, or null if this Viewport does not + * support horizontal scrolling. + * + * @return the horizontal scrollbar + */ + public THScroller getHorizontalScroller() { + return hScroller; + } + + /** + * Get the vertical scrollbar, or null if this Viewport does not support + * vertical scrolling. + * + * @return the vertical scrollbar + */ + public TVScroller getVerticalScroller() { + return vScroller; + } + + /** + * Get the value that corresponds to being on the top edge of the + * vertical scroll bar. + * + * @return the scroll value + */ + public int getTopValue() { + if (vScroller == null) { + return 0; + } else { + return vScroller.getTopValue(); + } + } + + /** + * Set the value that corresponds to being on the top edge of the + * vertical scroll bar. + * + * @param topValue the new scroll value + */ + public void setTopValue(final int topValue) { + if (vScroller == null) { + return; + } else { + vScroller.setTopValue(topValue); + } + } + + /** + * Get the value that corresponds to being on the bottom edge of the + * vertical scroll bar. + * + * @return the scroll value + */ + public int getBottomValue() { + if (vScroller == null) { + return 0; + } else { + return vScroller.getBottomValue(); + } + } + + /** + * Set the value that corresponds to being on the bottom edge of the + * vertical scroll bar. + * + * @param bottomValue the new scroll value + */ + public void setBottomValue(final int bottomValue) { + if (vScroller == null) { + return; + } else { + vScroller.setBottomValue(bottomValue); + } + } + + /** + * Get current value of the vertical scroll. + * + * @return the scroll value + */ + public int getVerticalValue() { + if (vScroller == null) { + return 0; + } else { + return vScroller.getValue(); + } + } + + /** + * Set current value of the vertical scroll. + * + * @param value the new scroll value + */ + public void setVerticalValue(final int value) { + if (vScroller == null) { + return; + } else { + vScroller.setValue(value); + } + } + + /** + * Get the increment for clicking on an arrow on the vertical scrollbar. + * + * @return the increment value + */ + public int getVerticalSmallChange() { + if (vScroller == null) { + return 0; + } else { + return vScroller.getSmallChange(); + } + } + + /** + * Set the increment for clicking on an arrow on the vertical scrollbar. + * + * @param smallChange the new increment value + */ + public void setVerticalSmallChange(final int smallChange) { + if (vScroller == null) { + return; + } else { + vScroller.setSmallChange(smallChange); + } + } + + /** + * Get the increment for clicking in the bar between the box and an + * arrow on the vertical scrollbar. + * + * @return the increment value + */ + public int getVerticalBigChange() { + if (vScroller == null) { + return 0; + } else { + return vScroller.getBigChange(); + } + } + + /** + * Set the increment for clicking in the bar between the box and an + * arrow on the vertical scrollbar. + * + * @param bigChange the new increment value + */ + public void setVerticalBigChange(final int bigChange) { + if (vScroller == null) { + return; + } else { + vScroller.setBigChange(bigChange); + } + } + + /** + * Perform a small step change up. + */ + public void verticalDecrement() { + if (vScroller == null) { + return; + } else { + vScroller.decrement(); + } + } + + /** + * Perform a small step change down. + */ + public void verticalIncrement() { + if (vScroller == null) { + return; + } else { + vScroller.increment(); + } + } + + /** + * Perform a big step change up. + */ + public void bigVerticalDecrement() { + if (vScroller == null) { + return; + } else { + vScroller.bigDecrement(); + } + } + + /** + * Perform a big step change down. + */ + public void bigVerticalIncrement() { + if (vScroller == null) { + return; + } else { + vScroller.bigIncrement(); + } + } + + /** + * Go to the top edge of the vertical scroller. + */ + public void toTop() { + if (vScroller == null) { + return; + } else { + vScroller.toTop(); + } + } + + /** + * Go to the bottom edge of the vertical scroller. + */ + public void toBottom() { + if (vScroller == null) { + return; + } else { + vScroller.toBottom(); + } + } + + /** + * Get the value that corresponds to being on the left edge of the + * horizontal scroll bar. + * + * @return the scroll value + */ + public int getLeftValue() { + if (hScroller == null) { + return 0; + } else { + return hScroller.getLeftValue(); + } + } + + /** + * Set the value that corresponds to being on the left edge of the + * horizontal scroll bar. + * + * @param leftValue the new scroll value + */ + public void setLeftValue(final int leftValue) { + if (hScroller == null) { + return; + } else { + hScroller.setLeftValue(leftValue); + } + } + + /** + * Get the value that corresponds to being on the right edge of the + * horizontal scroll bar. + * + * @return the scroll value + */ + public int getRightValue() { + if (hScroller == null) { + return 0; + } else { + return hScroller.getRightValue(); + } + } + + /** + * Set the value that corresponds to being on the right edge of the + * horizontal scroll bar. + * + * @param rightValue the new scroll value + */ + public void setRightValue(final int rightValue) { + if (hScroller == null) { + return; + } else { + hScroller.setRightValue(rightValue); + } + } + + /** + * Get current value of the horizontal scroll. + * + * @return the scroll value + */ + public int getHorizontalValue() { + if (hScroller == null) { + return 0; + } else { + return hScroller.getValue(); + } + } + + /** + * Set current value of the horizontal scroll. + * + * @param value the new scroll value + */ + public void setHorizontalValue(final int value) { + if (hScroller == null) { + return; + } else { + hScroller.setValue(value); + } + } + + /** + * Get the increment for clicking on an arrow on the horizontal + * scrollbar. + * + * @return the increment value + */ + public int getHorizontalSmallChange() { + if (hScroller == null) { + return 0; + } else { + return hScroller.getSmallChange(); + } + } + + /** + * Set the increment for clicking on an arrow on the horizontal + * scrollbar. + * + * @param smallChange the new increment value + */ + public void setHorizontalSmallChange(final int smallChange) { + if (hScroller == null) { + return; + } else { + hScroller.setSmallChange(smallChange); + } + } + + /** + * Get the increment for clicking in the bar between the box and an + * arrow on the horizontal scrollbar. + * + * @return the increment value + */ + public int getHorizontalBigChange() { + if (hScroller == null) { + return 0; + } else { + return hScroller.getBigChange(); + } + } + + /** + * Set the increment for clicking in the bar between the box and an + * arrow on the horizontal scrollbar. + * + * @param bigChange the new increment value + */ + public void setHorizontalBigChange(final int bigChange) { + if (hScroller == null) { + return; + } else { + hScroller.setBigChange(bigChange); + } + } + + /** + * Perform a small step change left. + */ + public void horizontalDecrement() { + if (hScroller == null) { + return; + } else { + hScroller.decrement(); + } + } + + /** + * Perform a small step change right. + */ + public void horizontalIncrement() { + if (hScroller == null) { + return; + } else { + hScroller.increment(); + } + } + + /** + * Perform a big step change left. + */ + public void bigHorizontalDecrement() { + if (hScroller == null) { + return; + } else { + hScroller.bigDecrement(); + } + } + + /** + * Perform a big step change right. + */ + public void bigHorizontalIncrement() { + if (hScroller == null) { + return; + } else { + hScroller.bigIncrement(); + } + } + + /** + * Go to the left edge of the horizontal scroller. + */ + public void toLeft() { + if (hScroller == null) { + return; + } else { + hScroller.toLeft(); + } + } + + /** + * Go to the right edge of the horizontal scroller. + */ + public void toRight() { + if (hScroller == null) { + return; + } else { + hScroller.toRight(); + } + } + + /** + * Go to the top-left edge of the horizontal and vertical scrollers. + */ + public void toHome() { + if (hScroller != null) { + hScroller.toLeft(); + } + if (vScroller != null) { + vScroller.toTop(); + } + } + + /** + * Go to the bottom-right edge of the horizontal and vertical scrollers. + */ + public void toEnd() { + if (hScroller != null) { + hScroller.toRight(); + } + if (vScroller != null) { + vScroller.toBottom(); + } + } + +} diff --git a/src/jexer/TTerminalWindow.java b/src/jexer/TTerminalWindow.java index 41c4b5e..17f32e0 100644 --- a/src/jexer/TTerminalWindow.java +++ b/src/jexer/TTerminalWindow.java @@ -49,7 +49,7 @@ import static jexer.TKeypress.*; /** * TTerminalWindow exposes a ECMA-48 / ANSI X3.64 style terminal in a window. */ -public class TTerminalWindow extends TWindow { +public class TTerminalWindow extends TScrollableWindow { /** * The emulator. @@ -61,11 +61,6 @@ public class TTerminalWindow extends TWindow { */ private Process shell; - /** - * Vertical scrollbar. - */ - private TVScroller vScroller; - /** * Claim the keystrokes the emulator will need. */ @@ -149,6 +144,9 @@ public class TTerminalWindow extends TWindow { super(application, "Terminal", x, y, 80 + 2, 24 + 2, flags); + vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2); + setBottomValue(0); + // Assume XTERM ECMA48.DeviceType deviceType = ECMA48.DeviceType.XTERM; @@ -281,7 +279,7 @@ public class TTerminalWindow extends TWindow { synchronized (emulator) { // Update the scroll bars - reflow(); + reflowData(); // Draw the box using my superclass super.draw(); @@ -292,7 +290,7 @@ public class TTerminalWindow extends TWindow { // Put together the visible rows int visibleHeight = getHeight() - 2; int visibleBottom = scrollback.size() + display.size() - + vScroller.getValue(); + + getVerticalValue(); assert (visibleBottom >= 0); List preceedingBlankLines = new LinkedList(); @@ -385,10 +383,8 @@ public class TTerminalWindow extends TWindow { setCursorX(emulator.getCursorX() + 1); setCursorY(emulator.getCursorY() + 1 - + (getHeight() - 2 - emulator.getHeight())); - if (vScroller != null) { - setCursorY(getCursorY() - vScroller.getValue()); - } + + (getHeight() - 2 - emulator.getHeight()) + - getVerticalValue()); setCursorVisible(emulator.isCursorVisible()); if (getCursorX() > getWidth() - 2) { setCursorVisible(false); @@ -454,10 +450,11 @@ public class TTerminalWindow extends TWindow { if (resize.getType() == TResizeEvent.Type.WIDGET) { // Resize the scroll bars - reflow(); + reflowData(); + placeScrollbars(); // Get out of scrollback - vScroller.setValue(0); + setVerticalValue(0); } return; @@ -467,7 +464,8 @@ public class TTerminalWindow extends TWindow { /** * Resize scrollbars for a new width/height. */ - private void reflow() { + @Override + public void reflowData() { // Synchronize against the emulator so we don't stomp on its reader // thread. @@ -477,19 +475,10 @@ public class TTerminalWindow extends TWindow { readEmulatorState(); // Vertical scrollbar - if (vScroller == null) { - vScroller = new TVScroller(this, getWidth() - 2, 0, - getHeight() - 2); - vScroller.setBottomValue(0); - vScroller.setValue(0); - } else { - vScroller.setX(getWidth() - 2); - vScroller.setHeight(getHeight() - 2); - } - vScroller.setTopValue(getHeight() - 2 + setTopValue(getHeight() - 2 - (emulator.getScrollbackBuffer().size() + emulator.getDisplayBuffer().size())); - vScroller.setBigChange(getHeight() - 2); + setVerticalBigChange(getHeight() - 2); } // synchronized (emulator) } @@ -532,14 +521,14 @@ public class TTerminalWindow extends TWindow { || keypress.equals(kbCtrlPgUp) || keypress.equals(kbAltPgUp) ) { - vScroller.bigDecrement(); + bigVerticalDecrement(); return; } if (keypress.equals(kbShiftPgDn) || keypress.equals(kbCtrlPgDn) || keypress.equals(kbAltPgDn) ) { - vScroller.bigIncrement(); + bigVerticalIncrement(); return; } @@ -548,7 +537,7 @@ public class TTerminalWindow extends TWindow { synchronized (emulator) { if (emulator.isReading()) { // Get out of scrollback - vScroller.setValue(0); + setVerticalValue(0); emulator.keypress(keypress.getKey()); // UGLY HACK TIME! cmd.exe needs CRLF, not just CR, so if @@ -582,11 +571,11 @@ public class TTerminalWindow extends TWindow { } if (mouse.isMouseWheelUp()) { - vScroller.decrement(); + verticalDecrement(); return; } if (mouse.isMouseWheelDown()) { - vScroller.increment(); + verticalIncrement(); return; } if (mouseOnEmulator(mouse)) { diff --git a/src/jexer/TText.java b/src/jexer/TText.java index 21c2f1f..c57e884 100644 --- a/src/jexer/TText.java +++ b/src/jexer/TText.java @@ -48,7 +48,7 @@ import jexer.event.TMouseEvent; * TText implements a simple scrollable text area. It reflows automatically on * resize. */ -public final class TText extends TWidget { +public final class TText extends TScrollableWidget { /** * Available text justifications. @@ -96,24 +96,33 @@ public final class TText extends TWidget { private String colorKey; /** - * Vertical scrollbar. + * Maximum width of a single line. */ - private TVScroller vScroller; + private int maxLineWidth; /** - * Horizontal scrollbar. + * Number of lines between each paragraph. */ - private THScroller hScroller; + private int lineSpacing = 1; /** - * Maximum width of a single line. + * Set the text. + * + * @param text new text to display */ - private int maxLineWidth; + public void setText(final String text) { + this.text = text; + reflowData(); + } /** - * Number of lines between each paragraph. + * Get the text. + * + * @return the text */ - private int lineSpacing = 1; + public String getText() { + return text; + } /** * Convenience method used by TWindowLoggerOutput. @@ -127,7 +136,7 @@ public final class TText extends TWidget { text += "\n\n"; text += line; } - reflow(); + reflowData(); } /** @@ -141,6 +150,7 @@ public final class TText extends TWidget { } } + vScroller.setTopValue(0); vScroller.setBottomValue((lines.size() - getHeight()) + 1); if (vScroller.getBottomValue() < 0) { vScroller.setBottomValue(0); @@ -149,6 +159,7 @@ public final class TText extends TWidget { vScroller.setValue(vScroller.getBottomValue()); } + hScroller.setLeftValue(0); hScroller.setRightValue((maxLineWidth - getWidth()) + 1); if (hScroller.getRightValue() < 0) { hScroller.setRightValue(0); @@ -165,7 +176,7 @@ public final class TText extends TWidget { */ public void setJustification(final Justification justification) { this.justification = justification; - reflow(); + reflowData(); } /** @@ -173,7 +184,7 @@ public final class TText extends TWidget { */ public void leftJustify() { justification = Justification.LEFT; - reflow(); + reflowData(); } /** @@ -181,7 +192,7 @@ public final class TText extends TWidget { */ public void centerJustify() { justification = Justification.CENTER; - reflow(); + reflowData(); } /** @@ -189,7 +200,7 @@ public final class TText extends TWidget { */ public void rightJustify() { justification = Justification.RIGHT; - reflow(); + reflowData(); } /** @@ -197,13 +208,14 @@ public final class TText extends TWidget { */ public void fullJustify() { justification = Justification.FULL; - reflow(); + reflowData(); } /** * Resize text and scrollbars for a new width/height. */ - public void reflow() { + @Override + public void reflowData() { // Reset the lines lines.clear(); @@ -233,29 +245,6 @@ public final class TText extends TWidget { 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(); } @@ -299,7 +288,9 @@ public final class TText extends TWidget { lines = new LinkedList(); - reflow(); + vScroller = new TVScroller(this, getWidth() - 1, 0, getHeight() - 1); + hScroller = new THScroller(this, 0, getHeight() - 1, getWidth() - 1); + reflowData(); } /** diff --git a/src/jexer/TTreeItem.java b/src/jexer/TTreeItem.java index cc48ffa..7de5e12 100644 --- a/src/jexer/TTreeItem.java +++ b/src/jexer/TTreeItem.java @@ -232,7 +232,7 @@ public class TTreeItem extends TWidget { view.setTreeRoot(this, true); } - view.reflow(); + view.reflowData(); } /** @@ -256,7 +256,7 @@ public class TTreeItem extends TWidget { TTreeItem item = new TTreeItem(view, text, expanded); item.level = this.level + 1; getChildren().add(item); - view.reflow(); + view.reflowData(); return item; } @@ -333,7 +333,7 @@ public class TTreeItem extends TWidget { */ @Override public void onMouseUp(final TMouseEvent mouse) { - if ((mouse.getX() == (getExpanderX() - view.getHScroller().getValue())) + if ((mouse.getX() == (getExpanderX() - view.getHorizontalValue())) && (mouse.getY() == 0) ) { if (selectable) { @@ -352,7 +352,7 @@ public class TTreeItem extends TWidget { } // Update the screen after any thing has expanded/contracted - view.reflow(); + view.reflowData(); } /** @@ -403,7 +403,7 @@ public class TTreeItem extends TWidget { return; } - int offset = -view.getHScroller().getValue(); + int offset = -view.getHorizontalValue(); CellAttributes color = getTheme().getColor("ttreeview"); CellAttributes textColor = getTheme().getColor("ttreeview"); diff --git a/src/jexer/TTreeView.java b/src/jexer/TTreeView.java index 6dc4f16..b9b05bf 100644 --- a/src/jexer/TTreeView.java +++ b/src/jexer/TTreeView.java @@ -35,27 +35,7 @@ import static jexer.TKeypress.*; /** * TTreeView implements a simple tree view. */ -public class TTreeView extends TWidget { - - /** - * Vertical scrollbar. - */ - private TVScroller vScroller; - - /** - * Horizontal scrollbar. - */ - private THScroller hScroller; - - /** - * Get the horizontal scrollbar. This is used by TTreeItem.draw(), and - * potentially subclasses. - * - * @return the horizontal scrollbar - */ - public final THScroller getHScroller() { - return hScroller; - } +public class TTreeView extends TScrollableWidget { /** * Root of the tree. @@ -144,6 +124,9 @@ public class TTreeView extends TWidget { super(parent, x, y, width, height); this.action = action; + + vScroller = new TVScroller(this, getWidth() - 1, 0, getHeight() - 1); + hScroller = new THScroller(this, 0, getHeight() - 1, getWidth() - 1); } /** @@ -179,41 +162,14 @@ public class TTreeView extends TWidget { } } - /** - * Update (or instantiate) vScroller and hScroller. - */ - private void updateScrollers() { - // Setup vertical scroller - if (vScroller == null) { - vScroller = new TVScroller(this, getWidth() - 1, 0, - getHeight() - 1); - vScroller.setValue(0); - vScroller.setTopValue(0); - } - vScroller.setX(getWidth() - 1); - vScroller.setHeight(getHeight() - 1); - vScroller.setBigChange(getHeight() - 1); - - // Setup horizontal scroller - if (hScroller == null) { - hScroller = new THScroller(this, 0, getHeight() - 1, - getWidth() - 1); - hScroller.setValue(0); - hScroller.setLeftValue(0); - } - hScroller.setY(getHeight() - 1); - hScroller.setWidth(getWidth() - 1); - hScroller.setBigChange(getWidth() - 1); - } - /** * Resize text and scrollbars for a new width/height. */ - public void reflow() { + @Override + public void reflowData() { int selectedRow = 0; boolean foundSelectedRow = false; - updateScrollers(); if (treeRoot == null) { return; } @@ -253,34 +209,30 @@ public class TTreeView extends TWidget { } if ((centerWindow) && (foundSelectedRow)) { - if ((selectedRow < vScroller.getValue()) - || (selectedRow > vScroller.getValue() + getHeight() - 2) + if ((selectedRow < getVerticalValue()) + || (selectedRow > getVerticalValue() + getHeight() - 2) ) { - vScroller.setValue(selectedRow); + setVerticalValue(selectedRow); centerWindow = false; } } updatePositions(); // Rescale the scroll bars - vScroller.setBottomValue(getChildren().size() - getHeight() + 1); - if (vScroller.getBottomValue() < 0) { - vScroller.setBottomValue(0); + setBottomValue(getChildren().size() - getHeight() + 1); + if (getBottomValue() < 0) { + setBottomValue(0); } - /* - if (vScroller.getValue() > vScroller.getBottomValue()) { - vScroller.setValue(vScroller.getBottomValue()); + if (getVerticalValue() > getBottomValue()) { + setVerticalValue(getBottomValue()); } - */ - hScroller.setRightValue(maxLineWidth - getWidth() + 3); - if (hScroller.getRightValue() < 0) { - hScroller.setRightValue(0); + setRightValue(maxLineWidth - getWidth() + 3); + if (getRightValue() < 0) { + setRightValue(0); } - /* - if (hScroller.getValue() > hScroller.getRightValue()) { - hScroller.setValue(hScroller.getRightValue()); + if (getHorizontalValue() > getRightValue()) { + setHorizontalValue(getRightValue()); } - */ getChildren().add(hScroller); getChildren().add(vScroller); } @@ -293,7 +245,7 @@ public class TTreeView extends TWidget { return; } - int begin = vScroller.getValue(); + int begin = getVerticalValue(); int topY = 0; // As we walk the list we also adjust next/previous pointers, @@ -344,16 +296,16 @@ public class TTreeView extends TWidget { @Override public void onMouseDown(final TMouseEvent mouse) { if (mouse.isMouseWheelUp()) { - vScroller.decrement(); + verticalDecrement(); } else if (mouse.isMouseWheelDown()) { - vScroller.increment(); + verticalIncrement(); } else { // Pass to children super.onMouseDown(mouse); } // Update the screen after the scrollbars have moved - reflow(); + reflowData(); } /** @@ -367,7 +319,7 @@ public class TTreeView extends TWidget { super.onMouseDown(mouse); // Update the screen after any thing has expanded/contracted - reflow(); + reflowData(); } /** @@ -381,36 +333,36 @@ public class TTreeView extends TWidget { || keypress.equals(kbCtrlLeft) || keypress.equals(kbAltLeft) ) { - hScroller.decrement(); + horizontalDecrement(); } else if (keypress.equals(kbShiftRight) || keypress.equals(kbCtrlRight) || keypress.equals(kbAltRight) ) { - hScroller.increment(); + horizontalIncrement(); } else if (keypress.equals(kbShiftUp) || keypress.equals(kbCtrlUp) || keypress.equals(kbAltUp) ) { - vScroller.decrement(); + verticalDecrement(); } else if (keypress.equals(kbShiftDown) || keypress.equals(kbCtrlDown) || keypress.equals(kbAltDown) ) { - vScroller.increment(); + verticalIncrement(); } else if (keypress.equals(kbShiftPgUp) || keypress.equals(kbCtrlPgUp) || keypress.equals(kbAltPgUp) ) { - vScroller.bigDecrement(); + bigVerticalDecrement(); } else if (keypress.equals(kbShiftPgDn) || keypress.equals(kbCtrlPgDn) || keypress.equals(kbAltPgDn) ) { - vScroller.bigIncrement(); + bigVerticalIncrement(); } else if (keypress.equals(kbHome)) { - vScroller.toTop(); + toTop(); } else if (keypress.equals(kbEnd)) { - vScroller.toBottom(); + toBottom(); } else if (keypress.equals(kbEnter)) { if (selectedItem != null) { dispatch(); @@ -422,7 +374,7 @@ public class TTreeView extends TWidget { if (selectedItem.keyboardPrevious != null) { setSelected(selectedItem.keyboardPrevious); if (oldItem.getY() == 0) { - vScroller.decrement(); + verticalDecrement(); } } } @@ -433,7 +385,7 @@ public class TTreeView extends TWidget { if (selectedItem.keyboardNext != null) { setSelected(selectedItem.keyboardNext); if (oldItem.getY() == getHeight() - 2) { - vScroller.increment(); + verticalIncrement(); } } } @@ -454,7 +406,7 @@ public class TTreeView extends TWidget { } // Update the screen after any thing has expanded/contracted - reflow(); + reflowData(); } } diff --git a/src/jexer/TVScroller.java b/src/jexer/TVScroller.java index 32e173a..1817880 100644 --- a/src/jexer/TVScroller.java +++ b/src/jexer/TVScroller.java @@ -115,6 +115,15 @@ public final class TVScroller extends TWidget { */ private int smallChange = 1; + /** + * Get the increment for clicking on an arrow. + * + * @return the increment value + */ + public int getSmallChange() { + return smallChange; + } + /** * Set the increment for clicking on an arrow. * @@ -129,6 +138,16 @@ public final class TVScroller extends TWidget { */ private int bigChange = 20; + /** + * Set the increment for clicking in the bar between the box and an + * arrow. + * + * @return the increment value + */ + public int getBigChange() { + return bigChange; + } + /** * Set the increment for clicking in the bar between the box and an * arrow. diff --git a/src/jexer/demos/DemoTextWindow.java b/src/jexer/demos/DemoTextWindow.java index 1c51296..3538905 100644 --- a/src/jexer/demos/DemoTextWindow.java +++ b/src/jexer/demos/DemoTextWindow.java @@ -120,9 +120,9 @@ public class DemoTextWindow extends TWindow { 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() - 6); - textField.reflow(); + TResizeEvent textSize = new TResizeEvent(TResizeEvent.Type.WIDGET, + event.getWidth() - 4, event.getHeight() - 6); + textField.onResize(textSize); return; } diff --git a/src/jexer/demos/DemoTreeViewWindow.java b/src/jexer/demos/DemoTreeViewWindow.java index 365cbca..7697981 100644 --- a/src/jexer/demos/DemoTreeViewWindow.java +++ b/src/jexer/demos/DemoTreeViewWindow.java @@ -73,10 +73,10 @@ public class DemoTreeViewWindow extends TWindow { @Override public void onResize(final TResizeEvent resize) { if (resize.getType() == TResizeEvent.Type.WIDGET) { - // Resize the text field - treeView.setWidth(resize.getWidth() - 4); - treeView.setHeight(resize.getHeight() - 4); - treeView.reflow(); + // Resize the treeView field + TResizeEvent treeSize = new TResizeEvent(TResizeEvent.Type.WIDGET, + resize.getWidth() - 4, resize.getHeight() - 4); + treeView.onResize(treeSize); return; } -- 2.27.0