From: Kevin Lamonte Date: Tue, 15 Aug 2017 15:44:13 +0000 (-0400) Subject: Bug fixes X-Git-Tag: fanfix-swing-0.0.1~12^2~13^2~241 X-Git-Url: https://git.nikiroo.be/?a=commitdiff_plain;h=fe0770f988e64fc0ccafd3d3b086b4a0eb559d3b;p=fanfix-swing.git Bug fixes --- diff --git a/README.md b/README.md index 7dc0bb4b..ff50ca75 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Jexer currently supports three backends: Additional backends can be created by subclassing jexer.backend.Backend and passing it into the TApplication -constructor. +constructor. See Demo5 and Demo6 for examples of other backends. The Jexer homepage, which includes additional information and binary release downloads, is at: https://jexer.sourceforge.io . The Jexer @@ -227,6 +227,11 @@ ambiguous. This section describes such issues. - Closing a TTerminalWindow without exiting the process inside it may result in a zombie 'script' process. + - TTerminalWindow cannot notify the child process of changes in + window size, due to Java's lack of support for forkpty() and + similar. Solving this requires C, and will be pursued only if + sufficient user requests come in. + - Java's InputStreamReader as used by the ECMA48 backend requires a valid UTF-8 stream. The default X10 encoding for mouse coordinates outside (160,94) can corrupt that stream, at best diff --git a/docs/TODO.md b/docs/TODO.md index 95e1add9..dfb39eb7 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -5,60 +5,83 @@ Jexer TODO List Roadmap ------- -BUG: TTreeView.reflow() doesn't keep the vertical dot within the - scrollbar. - 0.0.6 +- TSpinner +- TComboBox +- TCalendar + - TEditor + - Horizontal scrollbar integration - True tokenization and syntax highlighting: Java, C, Clojure, XML + - Carat notation for control characters - Tab character support - - Cut and Paste + - Cut/copy/paste + - Performance: behave smoothly on 100MB text files + +0.0.7 - Finish up multiscreen support: - cmAbort to cmScreenDisconnected - cmScreenConnected - Handle screen resizes -- TSpinner -- TComboBox -- TCalendar +- TEditor + - Word wrap + - Performance: behave smoothly on 1GB text files -0.0.7 +- Additional main color themes: + - Dark / L33t + - Green / NoReallyElite + - Red/brown + - Monochrome + - OMGPonies + +0.0.8 - THelpWindow - - TText + clickable links + - TEditor + clickable links - Index -0.0.8 +- TEditor + - Expose API: + - Cursor movement + - Movement within document + - Cut/copy/paste -- Undo / Redo support +0.0.9 + +- TEditor: + - Undo / Redo support 0.1.0: BETA RELEASE and BUG HUNT - Verify vttest in multiple tterminals. +0.2.0: + +- Drag and drop + - TEditor + - TField + - TText + - TTerminal + - TComboBox + 1.0.0 -- Maven artifact. +- Publish to the whole wide world 1.1.0 Wishlist -------------- - TTerminal - - Handle resize events (pass to child process) + - Handle resize events (pass to child process). Will need to switch + to forkpty(), or ship a C wrapper process. - Screen - Allow complex characters in putCharXY() and detect them in putStringXY(). -- Drag and drop - - TEditor - - TField - - TText - - TTerminal - - TComboBox - Regression Checklist diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index 691e1c72..c0166b11 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -1396,6 +1396,13 @@ public class TApplication implements Runnable { return; } + // Whatever window might be moving/dragging, stop it now. + for (TWindow w: windows) { + if (w.inMovements()) { + w.stopMovements(); + } + } + assert (windows.size() > 0); if (window.isHidden()) { @@ -1455,6 +1462,13 @@ public class TApplication implements Runnable { return; } + // Whatever window might be moving/dragging, stop it now. + for (TWindow w: windows) { + if (w.inMovements()) { + w.stopMovements(); + } + } + assert (windows.size() > 0); if (!window.hidden) { @@ -1486,6 +1500,13 @@ public class TApplication implements Runnable { return; } + // Whatever window might be moving/dragging, stop it now. + for (TWindow w: windows) { + if (w.inMovements()) { + w.stopMovements(); + } + } + assert (windows.size() > 0); if (window.hidden) { @@ -1511,6 +1532,13 @@ public class TApplication implements Runnable { } synchronized (windows) { + // Whatever window might be moving/dragging, stop it now. + for (TWindow w: windows) { + if (w.inMovements()) { + w.stopMovements(); + } + } + int z = window.getZ(); window.setZ(-1); window.onUnfocus(); @@ -1575,6 +1603,12 @@ public class TApplication implements Runnable { assert (activeWindow != null); synchronized (windows) { + // Whatever window might be moving/dragging, stop it now. + for (TWindow w: windows) { + if (w.inMovements()) { + w.stopMovements(); + } + } // Swap z/active between active window and the next in the list int activeWindowI = -1; @@ -1633,6 +1667,13 @@ public class TApplication implements Runnable { } synchronized (windows) { + // Whatever window might be moving/dragging, stop it now. + for (TWindow w: windows) { + if (w.inMovements()) { + w.stopMovements(); + } + } + // Do not allow a modal window to spawn a non-modal window. If a // modal window is active, then this window will become modal // too. diff --git a/src/jexer/TDirectoryList.java b/src/jexer/TDirectoryList.java index 642366b7..4b62bf31 100644 --- a/src/jexer/TDirectoryList.java +++ b/src/jexer/TDirectoryList.java @@ -73,6 +73,11 @@ public final class TDirectoryList extends TList { } } setList(newStrings); + + // Select the first entry + if (getMaxSelectedIndex() >= 0) { + setSelectedIndex(0); + } } /** diff --git a/src/jexer/TEditorWidget.java b/src/jexer/TEditorWidget.java index 8d2a0104..690c5731 100644 --- a/src/jexer/TEditorWidget.java +++ b/src/jexer/TEditorWidget.java @@ -183,8 +183,13 @@ public final class TEditorWidget extends TWidget { // Need to move topLine to bring document back into view. if (topLineIsTop) { topLine = line - (getHeight() - 1); + if (topLine < 0) { + topLine = 0; + } + assert (topLine >= 0); } else { topLine = line; + assert (topLine >= 0); } } @@ -193,6 +198,7 @@ public final class TEditorWidget extends TWidget { */ // Document is in view, let's set cursorY + assert (line >= topLine); setCursorY(line - topLine); alignCursor(); } @@ -385,7 +391,11 @@ public final class TEditorWidget extends TWidget { * @param row the new editing row number. Row 1 is the first row. */ public void setEditingRowNumber(final int row) { - document.setLineNumber(row - 1); + assert (row > 0); + if ((row > 0) && (row < document.getLineCount())) { + document.setLineNumber(row - 1); + alignTopLine(true); + } } /** @@ -404,7 +414,10 @@ public final class TEditorWidget extends TWidget { * column. */ public void setEditingColumnNumber(final int column) { - document.setCursor(column - 1); + if ((column > 0) && (column < document.getLineLength())) { + document.setCursor(column - 1); + alignCursor(); + } } /** diff --git a/src/jexer/TEditorWindow.java b/src/jexer/TEditorWindow.java index 69e254b9..5bf664d2 100644 --- a/src/jexer/TEditorWindow.java +++ b/src/jexer/TEditorWindow.java @@ -41,6 +41,7 @@ import jexer.TWidget; import jexer.bits.CellAttributes; import jexer.bits.GraphicsChars; import jexer.event.TCommandEvent; +import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; import jexer.event.TResizeEvent; import static jexer.TCommand.*; @@ -201,7 +202,7 @@ public class TEditorWindow extends TScrollableWindow { * editor. * * @param mouse a mouse-based event - * @return whether or not the mouse is on the emulator + * @return whether or not the mouse is on the editor */ private final boolean mouseOnEditor(final TMouseEvent mouse) { if ((mouse.getAbsoluteX() >= getAbsoluteX() + 1) @@ -235,8 +236,69 @@ public class TEditorWindow extends TScrollableWindow { // Vertical scrollbar actions editField.setEditingRowNumber(getVerticalValue()); } + } + } + + /** + * Handle mouse release events. + * + * @param mouse mouse button release event + */ + @Override + public void onMouseUp(final TMouseEvent mouse) { + // Use TWidget's code to pass the event to the children. + super.onMouseUp(mouse); + + if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) { + // Clicked on vertical scrollbar + editField.setEditingRowNumber(getVerticalValue()); + } + + // TODO: horizontal scrolling + } + + /** + * Method that subclasses can override to handle mouse movements. + * + * @param mouse mouse motion event + */ + @Override + public void onMouseMotion(final TMouseEvent mouse) { + // Use TWidget's code to pass the event to the children. + super.onMouseMotion(mouse); + + if (mouseOnEditor(mouse) && mouse.isMouse1()) { + // The editor might have changed, update the scollbars. + setBottomValue(editField.getMaximumRowNumber()); + setVerticalValue(editField.getEditingRowNumber()); + setRightValue(editField.getMaximumColumnNumber()); + setHorizontalValue(editField.getEditingColumnNumber()); + } else { + if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) { + // Clicked/dragged on vertical scrollbar + editField.setEditingRowNumber(getVerticalValue()); + } + // TODO: horizontal scrolling } + + } + + /** + * Handle keystrokes. + * + * @param keypress keystroke event + */ + @Override + public void onKeypress(final TKeypressEvent keypress) { + // Use TWidget's code to pass the event to the children. + super.onKeypress(keypress); + + // The editor might have changed, update the scollbars. + setBottomValue(editField.getMaximumRowNumber()); + setVerticalValue(editField.getEditingRowNumber()); + setRightValue(editField.getMaximumColumnNumber()); + setHorizontalValue(editField.getEditingColumnNumber()); } /** diff --git a/src/jexer/TList.java b/src/jexer/TList.java index 71600485..644c5647 100644 --- a/src/jexer/TList.java +++ b/src/jexer/TList.java @@ -81,6 +81,15 @@ public class TList extends TScrollableWidget { return null; } + /** + * Get the maximum selection index value. + * + * @return -1 if the list is empty + */ + public final int getMaxSelectedIndex() { + return strings.size() - 1; + } + /** * Set the new list of strings to display. * diff --git a/src/jexer/TScrollableWindow.java b/src/jexer/TScrollableWindow.java index ce20df7d..ae5f50c5 100644 --- a/src/jexer/TScrollableWindow.java +++ b/src/jexer/TScrollableWindow.java @@ -28,6 +28,7 @@ */ package jexer; +import jexer.event.TMouseEvent; import jexer.event.TResizeEvent; /** @@ -599,4 +600,46 @@ public class TScrollableWindow extends TWindow implements Scrollable { } } + /** + * Check if a mouse press/release/motion event coordinate is over the + * vertical scrollbar. + * + * @param mouse a mouse-based event + * @return whether or not the mouse is on the scrollbar + */ + protected final boolean mouseOnVerticalScroller(final TMouseEvent mouse) { + if (vScroller == null) { + return false; + } + if ((mouse.getAbsoluteX() == vScroller.getAbsoluteX()) + && (mouse.getAbsoluteY() >= vScroller.getAbsoluteY()) + && (mouse.getAbsoluteY() < vScroller.getAbsoluteY() + + vScroller.getHeight()) + ) { + return true; + } + return false; + } + + /** + * Check if a mouse press/release/motion event coordinate is over the + * horizontal scrollbar. + * + * @param mouse a mouse-based event + * @return whether or not the mouse is on the scrollbar + */ + protected final boolean mouseOnHorizontalScroller(final TMouseEvent mouse) { + if (hScroller == null) { + return false; + } + if ((mouse.getAbsoluteY() == hScroller.getAbsoluteY()) + && (mouse.getAbsoluteX() >= hScroller.getAbsoluteX()) + && (mouse.getAbsoluteX() < hScroller.getAbsoluteX() + + hScroller.getWidth()) + ) { + return true; + } + return false; + } + } diff --git a/src/jexer/TTerminalWindow.java b/src/jexer/TTerminalWindow.java index 17f32e07..96043e85 100644 --- a/src/jexer/TTerminalWindow.java +++ b/src/jexer/TTerminalWindow.java @@ -28,10 +28,7 @@ */ package jexer; -import java.io.InputStream; import java.io.IOException; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.util.LinkedList; import java.util.List; @@ -189,7 +186,7 @@ public class TTerminalWindow extends TScrollableWindow { emulator = new ECMA48(deviceType, shell.getInputStream(), shell.getOutputStream()); } catch (IOException e) { - e.printStackTrace(); + messageBox("Error", "Error launching shell: " + e.getMessage()); } // Setup the scroll bars @@ -236,39 +233,6 @@ public class TTerminalWindow extends TScrollableWindow { } } - /** - * Public constructor. - * - * @param application TApplication that manages this window - * @param x column relative to parent - * @param y row relative to parent - * @param flags mask of CENTERED, MODAL, or RESIZABLE - * @param input an InputStream connected to the remote side. For type == - * XTERM, input is converted to a Reader with UTF-8 encoding. - * @param output an OutputStream connected to the remote user. For type - * == XTERM, output is converted to a Writer with UTF-8 encoding. - * @throws UnsupportedEncodingException if an exception is thrown when - * creating the InputStreamReader - */ - public TTerminalWindow(final TApplication application, final int x, - final int y, final int flags, final InputStream input, - final OutputStream output) throws UnsupportedEncodingException { - - super(application, "Terminal", x, y, 80 + 2, 24 + 2, flags); - - emulator = new ECMA48(ECMA48.DeviceType.XTERM, input, output); - - // Setup the scroll bars - onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, getWidth(), - getHeight())); - - // Claim the keystrokes the emulator will need. - addShortcutKeys(); - - // Add shortcut text - newStatusBar("Terminal session executing..."); - } - /** * Draw the display buffer. */ diff --git a/src/jexer/TWindow.java b/src/jexer/TWindow.java index 406eb789..cde0113a 100644 --- a/src/jexer/TWindow.java +++ b/src/jexer/TWindow.java @@ -547,6 +547,27 @@ public class TWindow extends TWidget { // General behavior ------------------------------------------------------- // ------------------------------------------------------------------------ + /** + * See if this window is undergoing any movement/resize/etc. + * + * @return true if the window is moving + */ + public boolean inMovements() { + if (inWindowResize || inWindowMove || inKeyboardResize) { + return true; + } + return false; + } + + /** + * Stop any pending movement/resize/etc. + */ + public void stopMovements() { + inWindowResize = false; + inWindowMove = false; + inKeyboardResize = false; + } + /** * Returns true if this window is modal. * diff --git a/src/jexer/backend/ECMA48Terminal.java b/src/jexer/backend/ECMA48Terminal.java index 78aae45c..f23f6f44 100644 --- a/src/jexer/backend/ECMA48Terminal.java +++ b/src/jexer/backend/ECMA48Terminal.java @@ -746,6 +746,8 @@ public final class ECMA48Terminal extends LogicalScreen public void flushPhysical() { String result = flushString(); if ((cursorVisible) + && (cursorY >= 0) + && (cursorX >= 0) && (cursorY <= height - 1) && (cursorX <= width - 1) ) { diff --git a/src/jexer/backend/SwingTerminal.java b/src/jexer/backend/SwingTerminal.java index b7a16249..8ac6c2b5 100644 --- a/src/jexer/backend/SwingTerminal.java +++ b/src/jexer/backend/SwingTerminal.java @@ -633,6 +633,8 @@ public final class SwingTerminal extends LogicalScreen private void drawCursor(final Graphics gr) { if (cursorVisible + && (cursorY >= 0) + && (cursorX >= 0) && (cursorY <= height - 1) && (cursorX <= width - 1) && cursorBlinkVisible @@ -942,6 +944,8 @@ public final class SwingTerminal extends LogicalScreen } if (cursorVisible + && (cursorY >= 0) + && (cursorX >= 0) && (cursorY <= height - 1) && (cursorX <= width - 1) ) { diff --git a/src/jexer/teditor/Document.java b/src/jexer/teditor/Document.java index c8ab7460..c84c2078 100644 --- a/src/jexer/teditor/Document.java +++ b/src/jexer/teditor/Document.java @@ -456,4 +456,13 @@ public class Document { return n; } + /** + * Get the current line length. + * + * @return the number of cells needed to display the current line + */ + public int getLineLength() { + return lines.get(lineNumber).getDisplayLength(); + } + }