From fca67db090dc7e6476b98b800ce225c2bf60425c Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Fri, 13 Mar 2015 13:50:01 -0400 Subject: [PATCH] Switchable windows --- README.md | 26 +- demos/Demo1.java | 17 +- src/jexer/TApplication.java | 649 ++++++++++++++++++++++------- src/jexer/TMenu.java | 19 +- src/jexer/TWidget.java | 146 ++++++- src/jexer/TWindow.java | 242 ++++++----- src/jexer/bits/MnemonicString.java | 9 + src/jexer/io/Screen.java | 78 ++-- 8 files changed, 857 insertions(+), 329 deletions(-) diff --git a/README.md b/README.md index c045f04..4d4900d 100644 --- a/README.md +++ b/README.md @@ -31,21 +31,21 @@ import jexer.*; public class MyApplication extends TApplication { public MyApplication() { - super(); + super(); - // Create an editor window that has support for - // copy/paste, search text, arrow keys, horizontal - // and vertical scrollbar, etc. - addEditor(); + // Create an editor window that has support for + // copy/paste, search text, arrow keys, horizontal + // and vertical scrollbar, etc. + addEditor(); - // Create standard menus for File and Window - addFileMenu(); - addWindowMenu(); + // Create standard menus for File and Window + addFileMenu(); + addWindowMenu(); } public static void main(String [] args) { - MyApplication app = new MyApplication(); - app.run(); + MyApplication app = new MyApplication(); + app.run(); } } ``` @@ -59,10 +59,8 @@ version 1.0: 0.0.1: -- Get a movable window on screen - - TWidget - - TWindow - - TLabel +- TMenu +- TButton 0.0.2: diff --git a/demos/Demo1.java b/demos/Demo1.java index 29d0345..1270dae 100644 --- a/demos/Demo1.java +++ b/demos/Demo1.java @@ -67,20 +67,24 @@ class DemoMainWindow extends TWindow { } */ - /// Constructor + /** + * Construct demo window. It will be centered on screen. + */ public DemoMainWindow(TApplication parent) { this(parent, CENTERED | RESIZABLE); } - /// Constructor - public DemoMainWindow(TApplication parent, int flags) { + /** + * Constructor. + */ + private DemoMainWindow(TApplication parent, int flags) { // Construct a demo window. X and Y don't matter because it will be // centered on screen. super(parent, "Demo Window", 0, 0, 60, 23, flags); + /* int row = 1; - /* // Add some widgets if (!isModal) { addLabel("Message Boxes", 1, row); @@ -186,6 +190,11 @@ class DemoApplication extends TApplication { public DemoApplication() throws Exception { super(null, null); new DemoMainWindow(this); + TWindow window2 = new DemoMainWindow(this); + window2.setHeight(5); + window2.setWidth(25); + window2.setX(17); + window2.setY(6); } } diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index ef9bd04..2d2c100 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -43,6 +43,7 @@ import jexer.bits.GraphicsChars; import jexer.event.TCommandEvent; import jexer.event.TInputEvent; import jexer.event.TKeypressEvent; +import jexer.event.TMenuEvent; import jexer.event.TMouseEvent; import jexer.event.TResizeEvent; import jexer.backend.Backend; @@ -85,6 +86,21 @@ public class TApplication { */ private List eventQueue; + /** + * Top-level menus in this application. + */ + private List menus; + + /** + * Stack of activated sub-menus in this application. + */ + private List subMenus; + + /** + * The currently acive menu. + */ + private TMenu activeMenu = null; + /** * Windows and widgets pull colors from this ColorTheme. */ @@ -102,7 +118,7 @@ public class TApplication { /** * The top-level windows (but not menus). */ - List windows; + private List windows; /** * When true, exit the application. @@ -117,7 +133,7 @@ public class TApplication { /** * Request full repaint on next screen refresh. */ - public void setRepaint() { + public final void setRepaint() { repaint = true; } @@ -177,6 +193,8 @@ public class TApplication { desktopBottom = getScreen().getHeight() - 1; eventQueue = new LinkedList(); windows = new LinkedList(); + menus = new LinkedList(); + subMenus = new LinkedList(); } /** @@ -226,7 +244,6 @@ public class TApplication { window.drawChildren(); } - /* // Draw the blank menubar line - reset the screen clipping first so // it won't trim it out. getScreen().resetClipping(); @@ -234,10 +251,10 @@ public class TApplication { theme.getColor("tmenu")); // Now draw the menus. int x = 1; - for (TMenu m: menus) { + for (TMenu menu: menus) { CellAttributes menuColor; CellAttributes menuMnemonicColor; - if (menu.active) { + if (menu.getActive()) { menuColor = theme.getColor("tmenu.highlighted"); menuMnemonicColor = theme.getColor("tmenu.mnemonic.highlighted"); } else { @@ -245,19 +262,19 @@ public class TApplication { menuMnemonicColor = theme.getColor("tmenu.mnemonic"); } // Draw the menu title - getScreen().hLineXY(x, 0, menu.title.length() + 2, ' ', + getScreen().hLineXY(x, 0, menu.getTitle().length() + 2, ' ', menuColor); - getScreen().putStrXY(x + 1, 0, menu.title, menuColor); + getScreen().putStrXY(x + 1, 0, menu.getTitle(), menuColor); // Draw the highlight character - getScreen().putCharXY(x + 1 + m.mnemonic.shortcutIdx, 0, - m.mnemonic.shortcut, menuMnemonicColor); + getScreen().putCharXY(x + 1 + menu.getMnemonic().getShortcutIdx(), + 0, menu.getMnemonic().getShortcut(), menuMnemonicColor); - if (menu.active) { + if (menu.getActive()) { menu.drawChildren(); // Reset the screen clipping so we can draw the next title. getScreen().resetClipping(); } - x += menu.title.length + 2; + x += menu.getTitle().length() + 2; } for (TMenu menu: subMenus) { @@ -265,7 +282,6 @@ public class TApplication { getScreen().resetClipping(); menu.drawChildren(); } - */ // Draw the mouse pointer drawMouse(); @@ -282,7 +298,7 @@ public class TApplication { } // Kill the cursor - if (cursor == false) { + if (!cursor) { getScreen().hideCursor(); } @@ -409,8 +425,8 @@ public class TApplication { } // TODO: change to two separate threads - handleEvent(event); - + primaryHandleEvent(event); + /* // Put into the main queue @@ -437,105 +453,123 @@ public class TApplication { /** * Dispatch one event to the appropriate widget or application-level - * event handler. + * event handler. This is the primary event handler, it has the normal + * application-wide event handling. * * @param event the input event to consume + * @see #secondaryHandleEvent(TInputEvent event) */ - private final void handleEvent(TInputEvent event) { + private void primaryHandleEvent(final TInputEvent event) { + + // System.err.printf("Handle event: %s\n", event); + + // Special application-wide events ----------------------------------- + + // Peek at the mouse position + if (event instanceof TMouseEvent) { + // See if we need to switch focus to another window or the menu + checkSwitchFocus((TMouseEvent) event); + } + + // Handle menu events + if ((activeMenu != null) && !(event instanceof TCommandEvent)) { + TMenu menu = activeMenu; + + if (event instanceof TMouseEvent) { + TMouseEvent mouse = (TMouseEvent) event; + + while (subMenus.size() > 0) { + TMenu subMenu = subMenus.get(subMenus.size() - 1); + if (subMenu.mouseWouldHit(mouse)) { + break; + } + if ((mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) + && (!mouse.getMouse1()) + && (!mouse.getMouse2()) + && (!mouse.getMouse3()) + && (!mouse.getMouseWheelUp()) + && (!mouse.getMouseWheelDown()) + ) { + break; + } + // We navigated away from a sub-menu, so close it + closeSubMenu(); + } + + // Convert the mouse relative x/y to menu coordinates + assert (mouse.getX() == mouse.getAbsoluteX()); + assert (mouse.getY() == mouse.getAbsoluteY()); + if (subMenus.size() > 0) { + menu = subMenus.get(subMenus.size() - 1); + } + mouse.setX(mouse.getX() - menu.getX()); + mouse.setY(mouse.getY() - menu.getY()); + } + menu.handleEvent(event); + return; + } /* - // std.stdio.stderr.writefln("Handle event: %s", event); - - // Special application-wide events ----------------------------------- - - // Peek at the mouse position - if (auto mouse = cast(TMouseEvent)event) { - // See if we need to switch focus to another window or the menu - checkSwitchFocus(mouse); - } - - // Handle menu events - if ((activeMenu !is null) && (!cast(TCommandEvent)event)) { - TMenu menu = activeMenu; - if (auto mouse = cast(TMouseEvent)event) { - - while (subMenus.length > 0) { - TMenu subMenu = subMenus[$ - 1]; - if (subMenu.mouseWouldHit(mouse)) { - break; - } - if ((mouse.type == TMouseEvent.Type.MOUSE_MOTION) && - (!mouse.mouse1) && - (!mouse.mouse2) && - (!mouse.mouse3) && - (!mouse.mouseWheelUp) && - (!mouse.mouseWheelDown) - ) { - break; - } - // We navigated away from a sub-menu, so close it - closeSubMenu(); - } - - // Convert the mouse relative x/y to menu coordinates - assert(mouse.x == mouse.absoluteX); - assert(mouse.y == mouse.absoluteY); - if (subMenus.length > 0) { - menu = subMenus[$ - 1]; - } - mouse.x -= menu.x; - mouse.y -= menu.y; - } - menu.handleEvent(event); - return; - } - - if (auto keypress = cast(TKeypressEvent)event) { - // See if this key matches an accelerator, and if so dispatch the - // menu event. - TKeypress keypressLowercase = toLower(keypress.key); - TMenuItem *item = (keypressLowercase in accelerators); - if (item !is null) { - // Let the menu item dispatch - item.dispatch(); - return; - } else { - // Handle the keypress - if (onKeypress(keypress)) { - return; - } - } - } - - if (auto cmd = cast(TCommandEvent)event) { - if (onCommand(cmd)) { - return; - } - } - - if (auto menu = cast(TMenuEvent)event) { - if (onMenu(menu)) { - return; - } - } + TODO + + if (event instanceof TKeypressEvent) { + TKeypressEvent keypress = (TKeypressEvent) event; + // See if this key matches an accelerator, and if so dispatch the + // menu event. + TKeypress keypressLowercase = keypress.getKey().toLowerCase(); + TMenuItem item = accelerators.get(keypressLowercase); + if (item != null) { + // Let the menu item dispatch + item.dispatch(); + return; + } else { + // Handle the keypress + if (onKeypress(keypress)) { + return; + } + } + } */ - // Dispatch events to the active window ------------------------------- - for (TWindow window: windows) { - if (window.active) { + if (event instanceof TCommandEvent) { + if (onCommand((TCommandEvent) event)) { + return; + } + } + + if (event instanceof TMenuEvent) { + if (onMenu((TMenuEvent) event)) { + return; + } + } + + // Dispatch events to the active window ------------------------------- + for (TWindow window: windows) { + if (window.getActive()) { if (event instanceof TMouseEvent) { TMouseEvent mouse = (TMouseEvent) event; - // Convert the mouse relative x/y to window coordinates - assert (mouse.getX() == mouse.getAbsoluteX()); - assert (mouse.getY() == mouse.getAbsoluteY()); - mouse.setX(mouse.getX() - window.x); - mouse.setY(mouse.getY() - window.y); - } - // System.err("TApplication dispatch event: %s\n", event); - window.handleEvent(event); - break; - } - } + // Convert the mouse relative x/y to window coordinates + assert (mouse.getX() == mouse.getAbsoluteX()); + assert (mouse.getY() == mouse.getAbsoluteY()); + mouse.setX(mouse.getX() - window.getX()); + mouse.setY(mouse.getY() - window.getY()); + } + // System.err("TApplication dispatch event: %s\n", event); + window.handleEvent(event); + break; + } + } + } + /** + * Dispatch one event to the appropriate widget or application-level + * event handler. This is the secondary event handler used by certain + * special dialogs (currently TMessageBox and TFileOpenBox). + * + * @param event the input event to consume + * @see #primaryHandleEvent(TInputEvent event) + */ + private void secondaryHandleEvent(final TInputEvent event) { + // TODO } /** @@ -600,23 +634,20 @@ public class TApplication { * @param window the window to remove */ public final void closeWindow(final TWindow window) { - /* - TODO - - uint z = window.z; - window.z = -1; - windows.sort; - windows = windows[1 .. $]; + int z = window.getZ(); + window.setZ(-1); + Collections.sort(windows); + windows.remove(0); TWindow activeWindow = null; - foreach (w; windows) { - if (w.z > z) { - w.z--; - if (w.z == 0) { - w.active = true; - assert(activeWindow is null); + for (TWindow w: windows) { + if (w.getZ() > z) { + w.setZ(w.getZ() - 1); + if (w.getZ() == 0) { + w.setActive(true); + assert (activeWindow == null); activeWindow = w; } else { - w.active = false; + w.setActive(false); } } } @@ -627,6 +658,10 @@ public class TApplication { // Refresh screen repaint = true; + /* + TODO + + // Check if we are closing a TMessageBox or similar if (secondaryEventReceiver !is null) { assert(secondaryEventFiber !is null); @@ -658,48 +693,43 @@ public class TApplication { * otherwise switch to the previous window in the list */ public final void switchWindow(final boolean forward) { - /* - TODO - // Only switch if there are multiple windows - if (windows.length < 2) { + if (windows.size() < 2) { return; } - // Swap z/active between active window and the next in the - // list - ptrdiff_t activeWindowI = -1; - for (auto i = 0; i < windows.length; i++) { - if (windows[i].active) { + // Swap z/active between active window and the next in the list + int activeWindowI = -1; + for (int i = 0; i < windows.size(); i++) { + if (windows.get(i).getActive()) { activeWindowI = i; break; } } - assert(activeWindowI >= 0); + assert (activeWindowI >= 0); // Do not switch if a window is modal - if (windows[activeWindowI].isModal()) { + if (windows.get(activeWindowI).isModal()) { return; } - size_t nextWindowI; + int nextWindowI; if (forward) { - nextWindowI = (activeWindowI + 1) % windows.length; + nextWindowI = (activeWindowI + 1) % windows.size(); } else { if (activeWindowI == 0) { - nextWindowI = windows.length - 1; + nextWindowI = windows.size() - 1; } else { nextWindowI = activeWindowI - 1; } } - windows[activeWindowI].active = false; - windows[activeWindowI].z = windows[nextWindowI].z; - windows[nextWindowI].z = 0; - windows[nextWindowI].active = true; + windows.get(activeWindowI).setActive(false); + windows.get(activeWindowI).setZ(windows.get(nextWindowI).getZ()); + windows.get(nextWindowI).setZ(0); + windows.get(nextWindowI).setActive(true); // Refresh repaint = true; - */ } /** @@ -713,13 +743,352 @@ public class TApplication { assert (window.isModal()); } for (TWindow w: windows) { - w.active = false; + w.setActive(false); w.setZ(w.getZ() + 1); } windows.add(window); - window.active = true; + window.setActive(true); window.setZ(0); } + /** + * Check if there is a system-modal window on top. + * + * @return true if the active window is modal + */ + private boolean modalWindowActive() { + if (windows.size() == 0) { + return false; + } + return windows.get(windows.size() - 1).isModal(); + } + + /** + * Check if a mouse event would hit either the active menu or any open + * sub-menus. + * + * @param mouse mouse event + * @return true if the mouse would hit the active menu or an open + * sub-menu + */ + private boolean mouseOnMenu(final TMouseEvent mouse) { + assert (activeMenu != null); + List menus = new LinkedList(subMenus); + Collections.reverse(menus); + for (TMenu menu: menus) { + if (menu.mouseWouldHit(mouse)) { + return true; + } + } + return activeMenu.mouseWouldHit(mouse); + } + + /** + * See if we need to switch window or activate the menu based on + * a mouse click. + * + * @param mouse mouse event + */ + private void checkSwitchFocus(final TMouseEvent mouse) { + + if ((mouse.getType() == TMouseEvent.Type.MOUSE_DOWN) + && (activeMenu != null) + && (mouse.getAbsoluteY() != 0) + && (!mouseOnMenu(mouse)) + ) { + // They clicked outside the active menu, turn it off + activeMenu.setActive(false); + activeMenu = null; + for (TMenu menu: subMenus) { + menu.setActive(false); + } + subMenus.clear(); + // Continue checks + } + + // See if they hit the menu bar + if ((mouse.getType() == TMouseEvent.Type.MOUSE_DOWN) + && (mouse.getMouse1()) + && (!modalWindowActive()) + && (mouse.getAbsoluteY() == 0) + ) { + + for (TMenu menu: subMenus) { + menu.setActive(false); + } + subMenus.clear(); + + // They selected the menu, go activate it + for (TMenu menu: menus) { + if ((mouse.getAbsoluteX() >= menu.getX()) + && (mouse.getAbsoluteX() < menu.getX() + + menu.getTitle().length() + 2) + ) { + menu.setActive(true); + activeMenu = menu; + } else { + menu.setActive(false); + } + } + repaint = true; + return; + } + + // See if they hit the menu bar + if ((mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) + && (mouse.getMouse1()) + && (activeMenu != null) + && (mouse.getAbsoluteY() == 0) + ) { + + TMenu oldMenu = activeMenu; + for (TMenu menu: subMenus) { + menu.setActive(false); + } + subMenus.clear(); + + // See if we should switch menus + for (TMenu menu: menus) { + if ((mouse.getAbsoluteX() >= menu.getX()) + && (mouse.getAbsoluteX() < menu.getX() + + menu.getTitle().length() + 2) + ) { + menu.setActive(true); + activeMenu = menu; + } + } + if (oldMenu != activeMenu) { + // They switched menus + oldMenu.setActive(false); + } + repaint = true; + return; + } + + // Only switch if there are multiple windows + if (windows.size() < 2) { + return; + } + + // Switch on the upclick + if (mouse.getType() != TMouseEvent.Type.MOUSE_UP) { + return; + } + + Collections.sort(windows); + if (windows.get(0).isModal()) { + // Modal windows don't switch + return; + } + + for (TWindow window: windows) { + assert (!window.isModal()); + if (window.mouseWouldHit(mouse)) { + if (window == windows.get(0)) { + // Clicked on the same window, nothing to do + return; + } + + // We will be switching to another window + assert (windows.get(0).getActive()); + assert (!window.getActive()); + windows.get(0).setActive(false); + windows.get(0).setZ(window.getZ()); + window.setZ(0); + window.setActive(true); + repaint = true; + return; + } + } + + // Clicked on the background, nothing to do + return; + } + + /** + * Turn off the menu. + */ + private void closeMenu() { + if (activeMenu != null) { + activeMenu.setActive(false); + activeMenu = null; + for (TMenu menu: subMenus) { + menu.setActive(false); + } + subMenus.clear(); + } + repaint = true; + } + + /** + * Turn off a sub-menu. + */ + private void closeSubMenu() { + assert (activeMenu != null); + TMenu item = subMenus.get(subMenus.size() - 1); + assert (item != null); + item.setActive(false); + subMenus.remove(subMenus.size() - 1); + repaint = true; + } + + /** + * Switch to the next menu. + * + * @param forward if true, then switch to the next menu in the list, + * otherwise switch to the previous menu in the list + */ + private void switchMenu(final boolean forward) { + assert (activeMenu != null); + + for (TMenu menu: subMenus) { + menu.setActive(false); + } + subMenus.clear(); + + for (int i = 0; i < menus.size(); i++) { + if (activeMenu == menus.get(i)) { + if (forward) { + if (i < menus.size() - 1) { + i++; + } + } else { + if (i > 0) { + i--; + } + } + activeMenu.setActive(false); + activeMenu = menus.get(i); + activeMenu.setActive(true); + repaint = true; + return; + } + } + } + + /** + * Method that TApplication subclasses can override to handle menu or + * posted command events. + * + * @param command command event + * @return if true, this event was consumed + */ + protected boolean onCommand(final TCommandEvent command) { + /* + TODO + // Default: handle cmExit + if (command.equals(cmExit)) { + if (messageBox("Confirmation", "Exit application?", + TMessageBox.Type.YESNO).result == TMessageBox.Result.YES) { + quit = true; + } + repaint = true; + return true; + } + + if (command.equals(cmShell)) { + openTerminal(0, 0, TWindow.Flag.RESIZABLE); + repaint = true; + return true; + } + + if (command.equals(cmTile)) { + tileWindows(); + repaint = true; + return true; + } + if (command.equals(cmCascade)) { + cascadeWindows(); + repaint = true; + return true; + } + if (command.equals(cmCloseAll)) { + closeAllWindows(); + repaint = true; + return true; + } + */ + return false; + } + + /** + * Method that TApplication subclasses can override to handle menu + * events. + * + * @param menu menu event + * @return if true, this event was consumed + */ + protected boolean onMenu(final TMenuEvent menu) { + + /* + TODO + // Default: handle MID_EXIT + if (menu.id == TMenu.MID_EXIT) { + if (messageBox("Confirmation", "Exit application?", + TMessageBox.Type.YESNO).result == TMessageBox.Result.YES) { + quit = true; + } + // System.err.printf("onMenu MID_EXIT result: quit = %s\n", quit); + repaint = true; + return true; + } + + if (menu.id == TMenu.MID_SHELL) { + openTerminal(0, 0, TWindow.Flag.RESIZABLE); + repaint = true; + return true; + } + + if (menu.id == TMenu.MID_TILE) { + tileWindows(); + repaint = true; + return true; + } + if (menu.id == TMenu.MID_CASCADE) { + cascadeWindows(); + repaint = true; + return true; + } + if (menu.id == TMenu.MID_CLOSE_ALL) { + closeAllWindows(); + repaint = true; + return true; + } + */ + return false; + } + + /** + * Method that TApplication subclasses can override to handle keystrokes. + * + * @param keypress keystroke event + * @return if true, this event was consumed + */ + protected boolean onKeypress(final TKeypressEvent keypress) { + // Default: only menu shortcuts + + // Process Alt-F, Alt-E, etc. menu shortcut keys + if (!keypress.getKey().getIsKey() + && keypress.getKey().getAlt() + && !keypress.getKey().getCtrl() + && (activeMenu == null) + ) { + + assert (subMenus.size() == 0); + + for (TMenu menu: menus) { + if (Character.toLowerCase(menu.getMnemonic().getShortcut()) + == Character.toLowerCase(keypress.getKey().getCh()) + ) { + activeMenu = menu; + menu.setActive(true); + repaint = true; + return true; + } + } + } + + return false; + } } diff --git a/src/jexer/TMenu.java b/src/jexer/TMenu.java index b0873f0..bc1bc35 100644 --- a/src/jexer/TMenu.java +++ b/src/jexer/TMenu.java @@ -47,6 +47,15 @@ public class TMenu extends TWindow { */ private MnemonicString mnemonic; + /** + * Get the mnemonic string. + * + * @return the full mnemonic string + */ + public final MnemonicString getMnemonic() { + return mnemonic; + } + // Reserved menu item IDs public static final int MID_UNUSED = -1; @@ -92,15 +101,15 @@ public class TMenu extends TWindow { parent.closeWindow(this); // Setup the menu shortcut - mnemonic = new MnemonicString(title); - this.title = mnemonic.getRawLabel(); + mnemonic = new MnemonicString(label); + setTitle(mnemonic.getRawLabel()); assert (mnemonic.getShortcutIdx() >= 0); // Recompute width and height to reflect an empty menu - width = this.title.length() + 4; - height = 2; + setWidth(getTitle().length() + 4); + setHeight(2); - this.active = false; + setActive(false); } } diff --git a/src/jexer/TWidget.java b/src/jexer/TWidget.java index 4d9e22c..e075949 100644 --- a/src/jexer/TWidget.java +++ b/src/jexer/TWidget.java @@ -53,7 +53,27 @@ public abstract class TWidget { * example, a TWindow might contain several TTextFields, or a TComboBox * may contain a TScrollBar. */ - protected TWidget parent = null; + private TWidget parent = null; + + /** + * Backdoor access for TWindow's constructor. ONLY TWindow USES THIS. + * + * @param window the top-level window + * @param x column relative to parent + * @param y row relative to parent + * @param width width of window + * @param height height of window + */ + protected final void setupForTWindow(final TWindow window, + final int x, final int y, final int width, final int height) { + + this.parent = window; + this.window = window; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } /** * Child widgets that this widget contains. @@ -68,32 +88,122 @@ public abstract class TWidget { /** * If true, this widget will receive events. */ - protected boolean active = false; + private boolean active = false; + + /** + * Get active flag. + * + * @return if true, this widget will receive events + */ + public final boolean getActive() { + return active; + } + + /** + * Set active flag. + * + * @param active if true, this widget will receive events + */ + public final void setActive(boolean active) { + this.active = active; + } /** * The window that this widget draws to. */ - protected TWindow window = null; + private TWindow window = null; /** * Absolute X position of the top-left corner. */ - protected int x = 0; + private int x = 0; + + /** + * Get X position. + * + * @return absolute X position of the top-left corner + */ + public final int getX() { + return x; + } + + /** + * Set X position. + * + * @param x absolute X position of the top-left corner + */ + public final void setX(final int x) { + this.x = x; + } /** * Absolute Y position of the top-left corner. */ - protected int y = 0; + private int y = 0; + + /** + * Get Y position. + * + * @return absolute Y position of the top-left corner + */ + public final int getY() { + return y; + } + + /** + * Set Y position. + * + * @param y absolute Y position of the top-left corner + */ + public final void setY(final int y) { + this.y = y; + } /** * Width. */ - protected int width = 0; + private int width = 0; + + /** + * Get the width. + * + * @return widget width + */ + public final int getWidth() { + return this.width; + } + + /** + * Change the width. + * + * @param width new widget width + */ + public final void setWidth(final int width) { + this.width = width; + } /** * Height. */ - protected int height = 0; + private int height = 0; + + /** + * Get the height. + * + * @return widget height + */ + public final int getHeight() { + return this.height; + } + + /** + * Change the height. + * + * @param height new widget height + */ + public final void setHeight(final int height) { + this.height = height; + } /** * My tab order inside a window or containing widget. @@ -121,19 +231,15 @@ public abstract class TWidget { */ public final void setEnabled(final boolean enabled) { this.enabled = enabled; - /* - - // TODO: get this working after scrollers are going again - - if (enabled == false) { + if (!enabled) { active = false; // See if there are any active siblings to switch to boolean foundSibling = false; - if (parent !is null) { - foreach (w; parent.children) { - if ((w.enabled) && - (!cast(THScroller)this) && - (!cast(TVScroller)this) + if (parent != null) { + for (TWidget w: parent.children) { + if ((w.enabled) + && !(this instanceof THScroller) + && !(this instanceof TVScroller) ) { parent.activate(w); foundSibling = true; @@ -145,7 +251,6 @@ public abstract class TWidget { } } } - */ } /** @@ -312,7 +417,7 @@ public abstract class TWidget { } /** - * Subclasses need this constructor to setup children. + * Default constructor for subclasses. */ protected TWidget() { children = new LinkedList(); @@ -326,7 +431,7 @@ public abstract class TWidget { protected TWidget(final TWidget parent) { this.parent = parent; this.window = parent.window; - + children = new LinkedList(); parent.addChild(this); } @@ -490,6 +595,7 @@ public abstract class TWidget { public void onKeypress(final TKeypressEvent keypress) { if ((children.size() == 0) + // TODO // || (cast(TTreeView)this) // || (cast(TText)this) ) { diff --git a/src/jexer/TWindow.java b/src/jexer/TWindow.java index 0e94808..af34559 100644 --- a/src/jexer/TWindow.java +++ b/src/jexer/TWindow.java @@ -50,7 +50,7 @@ public class TWindow extends TWidget implements Comparable { /** * Window's parent TApplication. */ - protected TApplication application; + private TApplication application; /** * Get this TWindow's parent TApplication. @@ -73,7 +73,25 @@ public class TWindow extends TWidget implements Comparable { /** * Window title. */ - protected String title = ""; + private String title = ""; + + /** + * Get window title. + * + * @return window title + */ + public final String getTitle() { + return title; + } + + /** + * Set window title. + * + * @param title new window title + */ + public final void setTitle(final String title) { + this.title = title; + } /** * Window is resizable (default yes). @@ -144,7 +162,7 @@ public class TWindow extends TWidget implements Comparable { /** * Remember mouse state. */ - protected TMouseEvent mouse; + private TMouseEvent mouse; // For moving the window. resizing also uses moveWindowMouseX/Y private int moveWindowMouseX; @@ -226,22 +244,20 @@ public class TWindow extends TWidget implements Comparable { final int x, final int y, final int width, final int height, final int flags) { + super(); + // I am my own window and parent - this.parent = this; - this.window = this; + setupForTWindow(this, x, y + application.getDesktopTop(), + width, height); // Save fields this.title = title; this.application = application; - this.x = x; - this.y = y + application.getDesktopTop(); - this.width = width; - this.height = height; this.flags = flags; // Minimum width/height are 10 and 2 assert (width >= 10); - assert (height >= 2); + assert (getHeight() >= 2); // MODAL implies CENTERED if (isModal()) { @@ -260,18 +276,17 @@ public class TWindow extends TWidget implements Comparable { */ public final void center() { if ((flags & CENTERED) != 0) { - if (width < getScreen().getWidth()) { - x = (getScreen().getWidth() - width) / 2; + if (getWidth() < getScreen().getWidth()) { + setX((getScreen().getWidth() - getWidth()) / 2); } else { - x = 0; + setX(0); } - y = (application.getDesktopBottom() - application.getDesktopTop()); - y -= height; - y /= 2; - if (y < 0) { - y = 0; + setY(((application.getDesktopBottom() + - application.getDesktopTop()) - getHeight()) / 2); + if (getY() < 0) { + setY(0); } - y += application.getDesktopTop(); + setY(getY() + application.getDesktopTop()); } } @@ -305,8 +320,8 @@ public class TWindow extends TWidget implements Comparable { */ private boolean mouseOnClose() { if ((mouse != null) - && (mouse.getAbsoluteY() == y) - && (mouse.getAbsoluteX() == x + 3) + && (mouse.getAbsoluteY() == getY()) + && (mouse.getAbsoluteX() == getX() + 3) ) { return true; } @@ -321,8 +336,8 @@ public class TWindow extends TWidget implements Comparable { private boolean mouseOnMaximize() { if ((mouse != null) && !isModal() - && (mouse.getAbsoluteY() == y) - && (mouse.getAbsoluteX() == x + width - 4) + && (mouse.getAbsoluteY() == getY()) + && (mouse.getAbsoluteX() == getX() + getWidth() - 4) ) { return true; } @@ -340,9 +355,9 @@ public class TWindow extends TWidget implements Comparable { if (((flags & RESIZABLE) != 0) && !isModal() && (mouse != null) - && (mouse.getAbsoluteY() == y + height - 1) - && ((mouse.getAbsoluteX() == x + width - 1) - || (mouse.getAbsoluteX() == x + width - 2)) + && (mouse.getAbsoluteY() == getY() + getHeight() - 1) + && ((mouse.getAbsoluteX() == getX() + getWidth() - 1) + || (mouse.getAbsoluteX() == getX() + getWidth() - 2)) ) { return true; } @@ -354,21 +369,21 @@ public class TWindow extends TWidget implements Comparable { * * @return the background color */ - protected final CellAttributes getBackground() { + private final CellAttributes getBackground() { if (!isModal() && (inWindowMove || inWindowResize || inKeyboardResize) ) { - assert (active); + assert (getActive()); return application.getTheme().getColor("twindow.background.windowmove"); } else if (isModal() && inWindowMove) { - assert (active); + assert (getActive()); return application.getTheme().getColor("twindow.background.modal"); } else if (isModal()) { - if (active) { + if (getActive()) { return application.getTheme().getColor("twindow.background.modal"); } return application.getTheme().getColor("twindow.background.modal.inactive"); - } else if (active) { + } else if (getActive()) { assert (!isModal()); return application.getTheme().getColor("twindow.background"); } else { @@ -382,22 +397,22 @@ public class TWindow extends TWidget implements Comparable { * * @return the border color */ - protected final CellAttributes getBorder() { + private final CellAttributes getBorder() { if (!isModal() && (inWindowMove || inWindowResize || inKeyboardResize) ) { - assert (active); + assert (getActive()); return application.getTheme().getColor("twindow.border.windowmove"); } else if (isModal() && inWindowMove) { - assert (active); + assert (getActive()); return application.getTheme().getColor("twindow.border.modal.windowmove"); } else if (isModal()) { - if (active) { + if (getActive()) { return application.getTheme().getColor("twindow.border.modal"); } else { return application.getTheme().getColor("twindow.border.modal.inactive"); } - } else if (active) { + } else if (getActive()) { assert (!isModal()); return application.getTheme().getColor("twindow.border"); } else { @@ -411,22 +426,22 @@ public class TWindow extends TWidget implements Comparable { * * @return the border line type */ - protected final int getBorderType() { + private final int getBorderType() { if (!isModal() && (inWindowMove || inWindowResize || inKeyboardResize) ) { - assert (active); + assert (getActive()); return 1; } else if (isModal() && inWindowMove) { - assert (active); + assert (getActive()); return 1; } else if (isModal()) { - if (active) { + if (getActive()) { return 2; } else { return 1; } - } else if (active) { + } else if (getActive()) { return 2; } else { return 1; @@ -451,16 +466,16 @@ public class TWindow extends TWidget implements Comparable { CellAttributes background = getBackground(); int borderType = getBorderType(); - getScreen().drawBox(0, 0, width, height, border, + getScreen().drawBox(0, 0, getWidth(), getHeight(), border, background, borderType, true); // Draw the title - int titleLeft = (width - title.length() - 2) / 2; + int titleLeft = (getWidth() - title.length() - 2) / 2; putCharXY(titleLeft, 0, ' ', border); putStrXY(titleLeft + 1, 0, title); putCharXY(titleLeft + title.length() + 1, 0, ' ', border); - if (active) { + if (getActive()) { // Draw the close button putCharXY(2, 0, '[', border); @@ -480,26 +495,26 @@ public class TWindow extends TWidget implements Comparable { // Draw the maximize button if (!isModal()) { - putCharXY(width - 5, 0, '[', border); - putCharXY(width - 3, 0, ']', border); + putCharXY(getWidth() - 5, 0, '[', border); + putCharXY(getWidth() - 3, 0, ']', border); if (mouseOnMaximize() && mouse.getMouse1()) { - putCharXY(width - 4, 0, GraphicsChars.CP437[0x0F], + putCharXY(getWidth() - 4, 0, GraphicsChars.CP437[0x0F], application.getTheme().getColor("twindow.border.windowmove")); } else { if (maximized) { - putCharXY(width - 4, 0, GraphicsChars.CP437[0x12], + putCharXY(getWidth() - 4, 0, GraphicsChars.CP437[0x12], application.getTheme().getColor("twindow.border.windowmove")); } else { - putCharXY(width - 4, 0, GraphicsChars.UPARROW, + putCharXY(getWidth() - 4, 0, GraphicsChars.UPARROW, application.getTheme().getColor("twindow.border.windowmove")); } } // Draw the resize corner if ((flags & RESIZABLE) != 0) { - putCharXY(width - 2, height - 1, GraphicsChars.SINGLE_BAR, + putCharXY(getWidth() - 2, getHeight() - 1, GraphicsChars.SINGLE_BAR, application.getTheme().getColor("twindow.border.windowmove")); - putCharXY(width - 1, height - 1, GraphicsChars.LRCORNER, + putCharXY(getWidth() - 1, getHeight() - 1, GraphicsChars.LRCORNER, application.getTheme().getColor("twindow.border.windowmove")); } } @@ -518,10 +533,10 @@ public class TWindow extends TWidget implements Comparable { inKeyboardResize = false; - if ((mouse.getAbsoluteY() == y) + if ((mouse.getAbsoluteY() == getY()) && mouse.getMouse1() - && (x <= mouse.getAbsoluteX()) - && (mouse.getAbsoluteX() < x + width) + && (getX() <= mouse.getAbsoluteX()) + && (mouse.getAbsoluteX() < getX() + getWidth()) && !mouseOnClose() && !mouseOnMaximize() ) { @@ -529,8 +544,8 @@ public class TWindow extends TWidget implements Comparable { inWindowMove = true; moveWindowMouseX = mouse.getAbsoluteX(); moveWindowMouseY = mouse.getAbsoluteY(); - oldWindowX = x; - oldWindowY = y; + oldWindowX = getX(); + oldWindowY = getY(); if (maximized) { maximized = false; } @@ -541,8 +556,8 @@ public class TWindow extends TWidget implements Comparable { inWindowResize = true; moveWindowMouseX = mouse.getAbsoluteX(); moveWindowMouseY = mouse.getAbsoluteY(); - resizeWindowWidth = width; - resizeWindowHeight = height; + resizeWindowWidth = getWidth(); + resizeWindowHeight = getHeight(); if (maximized) { maximized = false; } @@ -557,14 +572,14 @@ public class TWindow extends TWidget implements Comparable { * Maximize window. */ private void maximize() { - restoreWindowWidth = width; - restoreWindowHeight = height; - restoreWindowX = x; - restoreWindowY = y; - width = getScreen().getWidth(); - height = application.getDesktopBottom() - 1; - x = 0; - y = 1; + restoreWindowWidth = getWidth(); + restoreWindowHeight = getHeight(); + restoreWindowX = getX(); + restoreWindowY = getY(); + setWidth(getScreen().getWidth()); + setHeight(application.getDesktopBottom() - 1); + setX(0); + setY(1); maximized = true; } @@ -572,10 +587,10 @@ public class TWindow extends TWidget implements Comparable { * Restote (unmaximize) window. */ private void restore() { - width = restoreWindowWidth; - height = restoreWindowHeight; - x = restoreWindowX; - y = restoreWindowY; + setWidth(restoreWindowWidth); + setHeight(restoreWindowHeight); + setX(restoreWindowX); + setY(restoreWindowY); maximized = false; } @@ -607,7 +622,8 @@ public class TWindow extends TWidget implements Comparable { return; } - if ((mouse.getAbsoluteY() == y) && mouse.getMouse1() + if ((mouse.getAbsoluteY() == getY()) + && mouse.getMouse1() && mouseOnMaximize()) { if (maximized) { // Restore @@ -617,7 +633,8 @@ public class TWindow extends TWidget implements Comparable { maximize(); } // Pass a resize event to my children - onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, width, height)); + onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, + getWidth(), getHeight())); return; } @@ -637,50 +654,57 @@ public class TWindow extends TWidget implements Comparable { if (inWindowMove) { // Move window over - x = oldWindowX + (mouse.getAbsoluteX() - moveWindowMouseX); - y = oldWindowY + (mouse.getAbsoluteY() - moveWindowMouseY); + setX(oldWindowX + (mouse.getAbsoluteX() - moveWindowMouseX)); + setY(oldWindowY + (mouse.getAbsoluteY() - moveWindowMouseY)); // Don't cover up the menu bar - if (y < application.getDesktopTop()) { - y = application.getDesktopTop(); + if (getY() < application.getDesktopTop()) { + setY(application.getDesktopTop()); } return; } if (inWindowResize) { // Move window over - width = resizeWindowWidth + (mouse.getAbsoluteX() - moveWindowMouseX); - height = resizeWindowHeight + (mouse.getAbsoluteY() - moveWindowMouseY); - if (x + width > getScreen().getWidth()) { - width = getScreen().getWidth() - x; + setWidth(resizeWindowWidth + (mouse.getAbsoluteX() + - moveWindowMouseX)); + setHeight(resizeWindowHeight + (mouse.getAbsoluteY() + - moveWindowMouseY)); + if (getX() + getWidth() > getScreen().getWidth()) { + setWidth(getScreen().getWidth() - getX()); } - if (y + height > application.getDesktopBottom()) { - y = application.getDesktopBottom() - height + 1; + if (getY() + getHeight() > application.getDesktopBottom()) { + setY(application.getDesktopBottom() - getHeight() + 1); } // Don't cover up the menu bar - if (y < application.getDesktopTop()) { - y = application.getDesktopTop(); + if (getY() < application.getDesktopTop()) { + setY(application.getDesktopTop()); } // Keep within min/max bounds - if (width < minimumWindowWidth) { - width = minimumWindowWidth; + if (getWidth() < minimumWindowWidth) { + setWidth(minimumWindowWidth); inWindowResize = false; } - if (height < minimumWindowHeight) { - height = minimumWindowHeight; + if (getHeight() < minimumWindowHeight) { + setHeight(minimumWindowHeight); inWindowResize = false; } - if ((maximumWindowWidth > 0) && (width > maximumWindowWidth)) { - width = maximumWindowWidth; + if ((maximumWindowWidth > 0) + && (getWidth() > maximumWindowWidth) + ) { + setWidth(maximumWindowWidth); inWindowResize = false; } - if ((maximumWindowHeight > 0) && (height > maximumWindowHeight)) { - height = maximumWindowHeight; + if ((maximumWindowHeight > 0) + && (getHeight() > maximumWindowHeight) + ) { + setHeight(maximumWindowHeight); inWindowResize = false; } // Pass a resize event to my children - onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, width, height)); + onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, + getWidth(), getHeight())); return; } @@ -704,43 +728,43 @@ public class TWindow extends TWidget implements Comparable { } if (keypress.equals(kbLeft)) { - if (x > 0) { - x--; + if (getX() > 0) { + setX(getX() - 1); } } if (keypress.equals(kbRight)) { - if (x < getScreen().getWidth() - 1) { - x++; + if (getX() < getScreen().getWidth() - 1) { + setX(getX() + 1); } } if (keypress.equals(kbDown)) { - if (y < application.getDesktopBottom() - 1) { - y++; + if (getY() < application.getDesktopBottom() - 1) { + setY(getY() + 1); } } if (keypress.equals(kbUp)) { - if (y > 1) { - y--; + if (getY() > 1) { + setY(getY() - 1); } } if (keypress.equals(kbShiftLeft)) { - if (width > minimumWindowWidth) { - width--; + if (getWidth() > minimumWindowWidth) { + setWidth(getWidth() - 1); } } if (keypress.equals(kbShiftRight)) { - if (width < maximumWindowWidth) { - width++; + if (getWidth() < maximumWindowWidth) { + setWidth(getWidth() + 1); } } if (keypress.equals(kbShiftUp)) { - if (height > minimumWindowHeight) { - height--; + if (getHeight() > minimumWindowHeight) { + setHeight(getHeight() - 1); } } if (keypress.equals(kbShiftDown)) { - if (height < maximumWindowHeight) { - height++; + if (getHeight() < maximumWindowHeight) { + setHeight(getHeight() + 1); } } diff --git a/src/jexer/bits/MnemonicString.java b/src/jexer/bits/MnemonicString.java index f97e1e9..d61963c 100644 --- a/src/jexer/bits/MnemonicString.java +++ b/src/jexer/bits/MnemonicString.java @@ -43,6 +43,15 @@ public final class MnemonicString { */ private char shortcut; + /** + * Get the keyboard shortcut character. + * + * @return the highlighted character + */ + public char getShortcut() { + return shortcut; + } + /** * Location of the highlighted character. */ diff --git a/src/jexer/io/Screen.java b/src/jexer/io/Screen.java index 6c872b3..eab9650 100644 --- a/src/jexer/io/Screen.java +++ b/src/jexer/io/Screen.java @@ -53,7 +53,7 @@ public abstract class Screen { /** * Drawing offset for x. */ - protected int offsetX; + private int offsetX; /** * Set drawing offset for x. @@ -67,7 +67,7 @@ public abstract class Screen { /** * Drawing offset for y. */ - protected int offsetY; + private int offsetY; /** * Set drawing offset for y. @@ -77,11 +77,11 @@ public abstract class Screen { public final void setOffsetY(final int offsetY) { this.offsetY = offsetY; } - + /** * Ignore anything drawn right of clipRight. */ - protected int clipRight; + private int clipRight; /** * Get right drawing clipping boundary. @@ -104,7 +104,7 @@ public abstract class Screen { /** * Ignore anything drawn below clipBottom. */ - protected int clipBottom; + private int clipBottom; /** * Get bottom drawing clipping boundary. @@ -127,7 +127,7 @@ public abstract class Screen { /** * Ignore anything drawn left of clipLeft. */ - protected int clipLeft; + private int clipLeft; /** * Get left drawing clipping boundary. @@ -150,7 +150,7 @@ public abstract class Screen { /** * Ignore anything drawn above clipTop. */ - protected int clipTop; + private int clipTop; /** * Get top drawing clipping boundary. @@ -214,7 +214,7 @@ public abstract class Screen { * @param y row coordinate. 0 is the top-most row. * @return attributes at (x, y) */ - public CellAttributes getAttrXY(final int x, final int y) { + public final CellAttributes getAttrXY(final int x, final int y) { CellAttributes attr = new CellAttributes(); attr.setTo(logical[x][y]); return attr; @@ -227,7 +227,9 @@ public abstract class Screen { * @param y row coordinate. 0 is the top-most row. * @param attr attributes to use (bold, foreColor, backColor) */ - public void putAttrXY(final int x, final int y, final CellAttributes attr) { + public final void putAttrXY(final int x, final int y, + final CellAttributes attr) { + putAttrXY(x, y, attr, true); } @@ -239,8 +241,8 @@ public abstract class Screen { * @param attr attributes to use (bold, foreColor, backColor) * @param clip if true, honor clipping/offset */ - public void putAttrXY(final int x, final int y, final CellAttributes attr, - final boolean clip) { + public final void putAttrXY(final int x, final int y, + final CellAttributes attr, final boolean clip) { int X = x; int Y = y; @@ -275,7 +277,7 @@ public abstract class Screen { * @param ch character to draw * @param attr attributes to use (bold, foreColor, backColor) */ - public void putAll(final char ch, final CellAttributes attr) { + public final void putAll(final char ch, final CellAttributes attr) { for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { putCharXY(x, y, ch, attr); @@ -290,7 +292,7 @@ public abstract class Screen { * @param y row coordinate. 0 is the top-most row. * @param ch character + attributes to draw */ - public void putCharXY(final int x, final int y, final Cell ch) { + public final void putCharXY(final int x, final int y, final Cell ch) { putCharXY(x, y, ch.getChar(), ch); } @@ -302,7 +304,7 @@ public abstract class Screen { * @param ch character to draw * @param attr attributes to use (bold, foreColor, backColor) */ - public void putCharXY(final int x, final int y, final char ch, + public final void putCharXY(final int x, final int y, final char ch, final CellAttributes attr) { if ((x < clipLeft) @@ -343,7 +345,7 @@ public abstract class Screen { * @param y row coordinate. 0 is the top-most row. * @param ch character to draw */ - public void putCharXY(final int x, final int y, final char ch) { + public final void putCharXY(final int x, final int y, final char ch) { if ((x < clipLeft) || (x >= clipRight) || (y < clipTop) @@ -371,7 +373,7 @@ public abstract class Screen { * @param str string to draw * @param attr attributes to use (bold, foreColor, backColor) */ - public void putStrXY(final int x, final int y, final String str, + public final void putStrXY(final int x, final int y, final String str, final CellAttributes attr) { int i = x; @@ -393,7 +395,7 @@ public abstract class Screen { * @param y row coordinate. 0 is the top-most row. * @param str string to draw */ - public void putStrXY(final int x, final int y, final String str) { + public final void putStrXY(final int x, final int y, final String str) { int i = x; for (int j = 0; j < str.length(); j++) { char ch = str.charAt(j); @@ -414,8 +416,8 @@ public abstract class Screen { * @param ch character to draw * @param attr attributes to use (bold, foreColor, backColor) */ - public void vLineXY(final int x, final int y, final int n, final char ch, - final CellAttributes attr) { + public final void vLineXY(final int x, final int y, final int n, + final char ch, final CellAttributes attr) { for (int i = y; i < y + n; i++) { putCharXY(x, i, ch, attr); @@ -431,8 +433,8 @@ public abstract class Screen { * @param ch character to draw * @param attr attributes to use (bold, foreColor, backColor) */ - public void hLineXY(final int x, final int y, final int n, final char ch, - final CellAttributes attr) { + public final void hLineXY(final int x, final int y, final int n, + final char ch, final CellAttributes attr) { for (int i = x; i < x + n; i++) { putCharXY(i, y, ch, attr); @@ -490,7 +492,7 @@ public abstract class Screen { * * @param width new screen width */ - public void setWidth(final int width) { + public final void setWidth(final int width) { reallocate(width, this.height); } @@ -500,7 +502,7 @@ public abstract class Screen { * * @param height new screen height */ - public void setHeight(final int height) { + public final void setHeight(final int height) { reallocate(this.width, height); } @@ -511,7 +513,7 @@ public abstract class Screen { * @param width new screen width * @param height new screen height */ - public void setDimensions(final int width, final int height) { + public final void setDimensions(final int width, final int height) { reallocate(width, height); } @@ -520,7 +522,7 @@ public abstract class Screen { * * @return current screen height */ - public int getHeight() { + public final int getHeight() { return this.height; } @@ -529,14 +531,14 @@ public abstract class Screen { * * @return current screen width */ - public int getWidth() { + public final int getWidth() { return this.width; } /** * Public constructor. Sets everything to not-bold, white-on-black. */ - public Screen() { + protected Screen() { offsetX = 0; offsetY = 0; width = 80; @@ -550,7 +552,7 @@ public abstract class Screen { * Reset screen to not-bold, white-on-black. Also flushes the offset and * clip variables. */ - public void reset() { + public final void reset() { dirty = true; for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { @@ -563,7 +565,7 @@ public abstract class Screen { /** * Flush the offset and clip variables. */ - public void resetClipping() { + public final void resetClipping() { offsetX = 0; offsetY = 0; clipLeft = 0; @@ -575,7 +577,7 @@ public abstract class Screen { /** * Force the screen to be fully cleared and redrawn on the next flush(). */ - public void clear() { + public final void clear() { reset(); } @@ -589,7 +591,7 @@ public abstract class Screen { * @param border attributes to use for the border * @param background attributes to use for the background */ - public void drawBox(final int left, final int top, + public final void drawBox(final int left, final int top, final int right, final int bottom, final CellAttributes border, final CellAttributes background) { @@ -610,7 +612,7 @@ public abstract class Screen { * single-line left/right edges (like Qmodem) * @param shadow if true, draw a "shadow" on the box */ - public void drawBox(final int left, final int top, + public final void drawBox(final int left, final int top, final int right, final int bottom, final CellAttributes border, final CellAttributes background, final int borderType, final boolean shadow) { @@ -684,14 +686,14 @@ public abstract class Screen { } /** - * Draw a box shadow + * Draw a box shadow. * * @param left left column of box. 0 is the left-most row. * @param top top row of the box. 0 is the top-most row. * @param right right column of box * @param bottom bottom row of the box */ - public void drawBoxShadow(final int left, final int top, + public final void drawBoxShadow(final int left, final int top, final int right, final int bottom) { int boxTop = top; @@ -734,16 +736,18 @@ public abstract class Screen { * @param x column coordinate to put the cursor on * @param y row coordinate to put the cursor on */ - public void putCursor(final boolean visible, final int x, final int y) { + public final void putCursor(final boolean visible, + final int x, final int y) { + cursorVisible = visible; cursorX = x; cursorY = y; } /** - * Hide the cursor + * Hide the cursor. */ - public void hideCursor() { + public final void hideCursor() { cursorVisible = false; } } -- 2.27.0