From 90d87fca1ee987e6650b90beafc7831cbca0a457 Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Wed, 21 Aug 2019 13:29:56 -0500 Subject: [PATCH] #51 wip --- examples/JexerTilingWindowManager2.java | 18 +++- src/jexer/TSplitPane.java | 133 ++++++++++++++++++++++-- src/jexer/TWidget.java | 96 ++++++++++++++--- src/jexer/TWindow.java | 12 +++ 4 files changed, 232 insertions(+), 27 deletions(-) diff --git a/examples/JexerTilingWindowManager2.java b/examples/JexerTilingWindowManager2.java index cb7f0d0..accbf73 100644 --- a/examples/JexerTilingWindowManager2.java +++ b/examples/JexerTilingWindowManager2.java @@ -88,12 +88,13 @@ public class JexerTilingWindowManager2 extends TApplication { protected boolean onMenu(TMenuEvent event) { if (event.getId() == MENU_SPLIT_VERTICAL) { if (root == null) { + assert (getDesktop().getActiveChild() == null); createRootTerminal(); return true; } - TWidget active = root.getActiveChild(); + TWidget active = getDesktop().getActiveChild(); TSplitPane split = active.splitVertical(false, - new TTerminalWidget(getDesktop(), active.getX(), + new TTerminalWidget(active, active.getX(), active.getY(), active.getWidth(), active.getHeight(), new TAction() { public void DO() { @@ -106,20 +107,22 @@ public class JexerTilingWindowManager2 extends TApplication { } } })); - if (active == root) { root = split; } + System.err.println("\nAfter vertical split:"); + System.err.println(getDesktop().toPrettyString()); return true; } if (event.getId() == MENU_SPLIT_HORIZONTAL) { if (root == null) { + assert (getDesktop().getActiveChild() == null); createRootTerminal(); return true; } - TWidget active = root.getActiveChild(); + TWidget active = getDesktop().getActiveChild(); TSplitPane split = active.splitHorizontal(false, - new TTerminalWidget(getDesktop(), active.getX(), + new TTerminalWidget(active, active.getX(), active.getY(), active.getWidth(), active.getHeight(), new TAction() { public void DO() { @@ -132,6 +135,11 @@ public class JexerTilingWindowManager2 extends TApplication { } } })); + if (active == root) { + root = split; + } + System.err.println("\nAfter horizontal split:"); + System.err.println(getDesktop().toPrettyString()); return true; } diff --git a/src/jexer/TSplitPane.java b/src/jexer/TSplitPane.java index b63ea1f..5a65736 100644 --- a/src/jexer/TSplitPane.java +++ b/src/jexer/TSplitPane.java @@ -124,6 +124,8 @@ public class TSplitPane extends TWidget { // Resize me super.onResize(event); + // System.err.println("onResize(): " + toString()); + if (vertical && (split >= getWidth() - 2)) { center(); } else if (!vertical && (split >= getHeight() - 2)) { @@ -147,9 +149,9 @@ public class TSplitPane extends TWidget { if (mouse.isMouse1()) { if (vertical) { - inSplitMove = (mouse.getX() == split); + inSplitMove = (mouse.getAbsoluteX() - getAbsoluteX() == split); } else { - inSplitMove = (mouse.getY() == split); + inSplitMove = (mouse.getAbsoluteY() - getAbsoluteY() == split); } if (inSplitMove) { return; @@ -170,6 +172,9 @@ public class TSplitPane extends TWidget { this.mouse = mouse; if (inSplitMove && mouse.isMouse1()) { + // DEBUG + // System.err.println(toPrettyString()); + // Stop moving split inSplitMove = false; return; @@ -199,10 +204,10 @@ public class TSplitPane extends TWidget { if (inSplitMove) { if (vertical) { - split = mouse.getX(); + split = mouse.getAbsoluteX() - getAbsoluteX(); split = Math.min(Math.max(1, split), getWidth() - 2); } else { - split = mouse.getY(); + split = mouse.getAbsoluteY() - getAbsoluteY(); split = Math.min(Math.max(1, split), getHeight() - 2); } layoutChildren(); @@ -251,6 +256,29 @@ public class TSplitPane extends TWidget { } + /** + * Generate a human-readable string for this widget. + * + * @return a human-readable string + */ + @Override + public String toString() { + return String.format("%s(%8x) %s position (%d, %d) geometry %dx%d " + + "split %d left %s(%8x) right %s(%8x) top %s(%8x) bottom %s(%8x) " + + "active %s enabled %s visible %s", getClass().getName(), + hashCode(), (vertical ? "VERTICAL" : "HORIZONTAL"), + getX(), getY(), getWidth(), getHeight(), split, + (left == null ? "null" : left.getClass().getName()), + (left == null ? 0 : left.hashCode()), + (right == null ? "null" : right.getClass().getName()), + (right == null ? 0 : right.hashCode()), + (top == null ? "null" : top.getClass().getName()), + (top == null ? 0 : top.hashCode()), + (bottom == null ? "null" : bottom.getClass().getName()), + (bottom == null ? 0 : bottom.hashCode()), + isActive(), isEnabled(), isVisible()); + } + // ------------------------------------------------------------------------ // TSplitPane ------------------------------------------------------------- // ------------------------------------------------------------------------ @@ -383,33 +411,126 @@ public class TSplitPane extends TWidget { getHeight())); } + /** + * Remove a widget, regardless of what pane it is on. + * + * @param widget the widget to remove + */ + public void removeWidget(final TWidget widget) { + if (widget == null) { + throw new IllegalArgumentException("cannot remove null widget"); + } + if (left == widget) { + left = null; + assert(right != widget); + assert(top != widget); + assert(bottom != widget); + return; + } + if (right == widget) { + right = null; + assert(left != widget); + assert(top != widget); + assert(bottom != widget); + return; + } + if (top == widget) { + top = null; + assert(left != widget); + assert(right != widget); + assert(bottom != widget); + return; + } + if (bottom == widget) { + bottom = null; + assert(left != widget); + assert(right != widget); + assert(top != widget); + return; + } + throw new IllegalArgumentException("widget " + widget + + " not in this split"); + } + + /** + * Replace a widget, regardless of what pane it is on, with another + * widget. + * + * @param oldWidget the widget to remove + * @param newWidget the widget to replace it with + */ + public void replaceWidget(final TWidget oldWidget, + final TWidget newWidget) { + + if (oldWidget == null) { + throw new IllegalArgumentException("cannot remove null oldWidget"); + } + if (left == oldWidget) { + setLeft(newWidget); + assert(right != newWidget); + assert(top != newWidget); + assert(bottom != newWidget); + return; + } + if (right == oldWidget) { + setRight(newWidget); + assert(left != newWidget); + assert(top != newWidget); + assert(bottom != newWidget); + return; + } + if (top == oldWidget) { + setTop(newWidget); + assert(left != newWidget); + assert(right != newWidget); + assert(bottom != newWidget); + return; + } + if (bottom == oldWidget) { + setBottom(newWidget); + assert(left != newWidget); + assert(right != newWidget); + assert(top != newWidget); + return; + } + throw new IllegalArgumentException("oldWidget " + oldWidget + + " not in this split"); + } + /** * Layout the two child widgets. */ private void layoutChildren() { + + // System.err.println("layoutChildren(): " + toString()); + if (vertical) { if (left != null) { left.setDimensions(0, 0, split, getHeight()); left.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, left.getWidth(), left.getHeight())); + // System.err.println(" move/size left: " + left.toString()); } if (right != null) { right.setDimensions(split + 1, 0, getWidth() - split - 1, getHeight()); right.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, right.getWidth(), right.getHeight())); + // System.err.println(" move/size right: " + right.toString()); } } else { if (top != null) { top.setDimensions(0, 0, getWidth(), split); top.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, top.getWidth(), top.getHeight())); + // System.err.println(" move/size top: " + top.toString()); } if (bottom != null) { bottom.setDimensions(0, split + 1, getWidth(), getHeight() - split - 1); bottom.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, bottom.getWidth(), bottom.getHeight())); + // System.err.println(" move/size bottom: " + bottom.toString()); } } } @@ -462,7 +583,7 @@ public class TSplitPane extends TWidget { } // Remove me from my parent widget. - TWidget newParent = getParent(); + TWidget myParent = getParent(); remove(false); if (keep == null) { @@ -470,7 +591,7 @@ public class TSplitPane extends TWidget { return null; } - keep.setParent(newParent, false); + keep.setParent(myParent, false); keep.setDimensions(getX(), getY(), getWidth(), getHeight()); keep.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, getWidth(), getHeight())); diff --git a/src/jexer/TWidget.java b/src/jexer/TWidget.java index 6994e6f..5d2612b 100644 --- a/src/jexer/TWidget.java +++ b/src/jexer/TWidget.java @@ -933,10 +933,10 @@ public abstract class TWidget implements Comparable { public final void setDimensions(final int x, final int y, final int width, final int height) { - setX(x); - setY(y); - setWidth(width); - setHeight(height); + this.x = x; + this.y = y; + this.width = width; + this.height = height; if (layout != null) { layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, width, height)); @@ -1560,16 +1560,20 @@ public abstract class TWidget implements Comparable { final TWidget newWidget) { TSplitPane splitPane = new TSplitPane(null, x, y, width, height, true); - List widgets = new ArrayList(children); TWidget myParent = parent; remove(false); + if (myParent instanceof TSplitPane) { + // TSplitPane has a left/right/top/bottom link to me somewhere, + // replace it with a link to splitPane. + ((TSplitPane) myParent).replaceWidget(this, splitPane); + } splitPane.setParent(myParent, false); if (newWidgetOnLeft) { splitPane.setLeft(newWidget); splitPane.setRight(this); } else { - splitPane.setRight(newWidget); splitPane.setLeft(this); + splitPane.setRight(newWidget); } splitPane.activate(); if (newWidget != null) { @@ -1577,15 +1581,20 @@ public abstract class TWidget implements Comparable { } else { activate(); } + assert (parent != null); assert (window != null); - for (TWidget w: widgets) { - assert (w.window != null); - assert (w.parent != null); - } assert (splitPane.getWindow() != null); assert (splitPane.getParent() != null); assert (splitPane.isActive() == true); + assert (parent == splitPane); + if (newWidget != null) { + assert (newWidget.parent == parent); + assert (newWidget.active == true); + assert (active == false); + } else { + assert (active == true); + } return splitPane; } @@ -1603,16 +1612,20 @@ public abstract class TWidget implements Comparable { final TWidget newWidget) { TSplitPane splitPane = new TSplitPane(null, x, y, width, height, false); - List widgets = new ArrayList(children); TWidget myParent = parent; remove(false); + if (myParent instanceof TSplitPane) { + // TSplitPane has a left/right/top/bottom link to me somewhere, + // replace it with a link to splitPane. + ((TSplitPane) myParent).replaceWidget(this, splitPane); + } splitPane.setParent(myParent, false); if (newWidgetOnTop) { splitPane.setTop(newWidget); splitPane.setBottom(this); } else { - splitPane.setBottom(newWidget); splitPane.setTop(this); + splitPane.setBottom(newWidget); } splitPane.activate(); if (newWidget != null) { @@ -1620,18 +1633,69 @@ public abstract class TWidget implements Comparable { } else { activate(); } + assert (parent != null); assert (window != null); - for (TWidget w: widgets) { - assert (w.window != null); - assert (w.parent != null); - } assert (splitPane.getWindow() != null); assert (splitPane.getParent() != null); assert (splitPane.isActive() == true); + assert (parent == splitPane); + if (newWidget != null) { + assert (newWidget.parent == parent); + assert (newWidget.active == true); + assert (active == false); + } else { + assert (active == true); + } return splitPane; } + /** + * Generate a human-readable string for this widget. + * + * @return a human-readable string + */ + @Override + public String toString() { + return String.format("%s(%8x) position (%d, %d) geometry %dx%d " + + "active %s enabled %s visible %s", getClass().getName(), + hashCode(), x, y, width, height, active, enabled, visible); + } + + /** + * Generate a string for this widget's hierarchy. + * + * @param prefix a prefix to use for this widget's place in the hierarchy + * @return a pretty-printable string of this hierarchy + */ + protected String toPrettyString(final String prefix) { + StringBuilder sb = new StringBuilder(prefix); + sb.append(toString()); + String newPrefix = ""; + for (int i = 0; i < prefix.length(); i++) { + newPrefix += " "; + } + for (int i = 0; i < children.size(); i++) { + TWidget child= children.get(i); + sb.append("\n"); + if (i == children.size() - 1) { + sb.append(child.toPrettyString(newPrefix + " \u2514\u2500")); + } else { + sb.append(child.toPrettyString(newPrefix + " \u251c\u2500")); + } + } + return sb.toString(); + } + + /** + * Generate a string for this widget's hierarchy. + * + * @return a pretty-printable string of this hierarchy + */ + public String toPrettyString() { + return toPrettyString(""); + } + // ------------------------------------------------------------------------ // Passthru for Screen functions ------------------------------------------ // ------------------------------------------------------------------------ diff --git a/src/jexer/TWindow.java b/src/jexer/TWindow.java index 6763df3..e0a2073 100644 --- a/src/jexer/TWindow.java +++ b/src/jexer/TWindow.java @@ -1431,4 +1431,16 @@ public class TWindow extends TWidget { this.hideMouse = hideMouse; } + /** + * Generate a human-readable string for this window. + * + * @return a human-readable string + */ + @Override + public String toString() { + return String.format("%s(%8x) \'%s\' position (%d, %d) geometry %dx%d" + + " hidden %s modal %s", getClass().getName(), hashCode(), title, + getX(), getY(), getWidth(), getHeight(), hidden, isModal()); + } + } -- 2.27.0