jexer/examples/JexerTilingWindowManager.java

225 lines
7.9 KiB
Java
Raw Permalink Normal View History

2019-02-23 09:46:43 -07:00
import jexer.TApplication;
import jexer.TTerminalWindow;
import jexer.TWindow;
import jexer.event.TKeypressEvent;
import jexer.event.TMenuEvent;
import jexer.event.TMouseEvent;
import jexer.event.TResizeEvent;
import jexer.menu.TMenu;
/**
* Implements a simple tiling window manager. A root non-moveable
* non-resizable terminal window is created first, which can be split
* horizontally or vertically. Each new window retains a reference to its
* "parent", and upon closing resizes that parent back to its original size.
*
* This example shows what can be done with minimal changes to stock Jexer
* widgets. You will quickly see that closing a "parent" tile does not cause
* the "child" tile to resize. You could make a real subclass of
* TTerminalWindow that has extra fields and/or communicates more with
* JexerTilingWindowManager to get full coverage of tile creation,
* destruction, placement, movement, and so on.
*/
public class JexerTilingWindowManager extends TApplication {
/**
* Menu item: split the terminal vertically.
*/
private static final int MENU_SPLIT_VERTICAL = 2000;
/**
* Menu item: split the terminal horizontally.
*/
private static final int MENU_SPLIT_HORIZONTAL = 2001;
/**
* Main entry point.
*/
public static void main(String [] args) throws Exception {
// For this application, we must use ptypipe so that the tile shells
// can be aware of their size.
System.setProperty("jexer.TTerminal.ptypipe", "true");
JexerTilingWindowManager jtwm = new JexerTilingWindowManager();
(new Thread(jtwm)).start();
}
/**
* Public constructor chooses the ECMA-48 / Xterm backend.
*/
public JexerTilingWindowManager() throws Exception {
super(BackendType.XTERM);
// The stock tool menu has items for redrawing the screen, opening
// images, and (when using the Swing backend) setting the font.
addToolMenu();
// We will have one menu containing a mix of new and stock commands
TMenu tileMenu = addMenu("&Tile");
// New commands for this example: split vertical and horizontal.
tileMenu.addItem(MENU_SPLIT_VERTICAL, "&Vertical Split");
tileMenu.addItem(MENU_SPLIT_HORIZONTAL, "&Horizontal Split");
// Stock commands: a new shell with resizable window, previous, next,
// close, and exit program.
tileMenu.addItem(TMenu.MID_SHELL, "&Floating");
tileMenu.addSeparator();
tileMenu.addDefaultItem(TMenu.MID_WINDOW_PREVIOUS);
tileMenu.addDefaultItem(TMenu.MID_WINDOW_NEXT);
tileMenu.addDefaultItem(TMenu.MID_WINDOW_CLOSE);
tileMenu.addSeparator();
tileMenu.addDefaultItem(TMenu.MID_EXIT);
// Spin up the root tile
TTerminalWindow rootTile = makeTile(0, 0, getScreen().getWidth(),
getDesktopBottom() - 1, null);
// Let's add some bling! Enable focus-follows-mouse.
setFocusFollowsMouse(true);
}
/**
* Process menu events.
*/
@Override
protected boolean onMenu(TMenuEvent event) {
if (event.getId() == MENU_SPLIT_VERTICAL) {
splitVertical();
return true;
}
if (event.getId() == MENU_SPLIT_HORIZONTAL) {
splitHorizontal();
return true;
}
return super.onMenu(event);
}
/**
* Perform the vertical split.
*/
private void splitVertical() {
TWindow window = getActiveWindow();
if (!(window instanceof TTerminalWindow)) {
return;
}
TTerminalWindow tile = (TTerminalWindow) window;
// Give the extra column to the new tile.
int newWidth = (tile.getWidth() + 1) / 2;
int newY = tile.getY() - 1;
int newX = tile.getX() + tile.getWidth() - newWidth;
makeTile(newX, newY, newWidth, tile.getHeight(), tile);
tile.setWidth(tile.getWidth() - newWidth);
2021-12-19 10:57:05 -07:00
tile.onResize(new TResizeEvent(getBackend(), TResizeEvent.Type.WIDGET,
2019-02-23 09:46:43 -07:00
tile.getWidth(), tile.getHeight()));
}
/**
* Perform the horizontal split.
*/
private void splitHorizontal() {
TWindow window = getActiveWindow();
if (!(window instanceof TTerminalWindow)) {
return;
}
TTerminalWindow tile = (TTerminalWindow) window;
// Give the extra row to the new tile.
int newHeight = (tile.getHeight() + 1) / 2;
int newY = tile.getY() - 1 + tile.getHeight() - newHeight;
int newX = tile.getX();
makeTile(newX, newY, tile.getWidth(), newHeight, tile);
tile.setHeight(tile.getHeight() - newHeight);
2021-12-19 10:57:05 -07:00
tile.onResize(new TResizeEvent(getBackend(), TResizeEvent.Type.WIDGET,
2019-02-23 09:46:43 -07:00
tile.getWidth(), tile.getHeight()));
}
/**
* Create a non-resizable non-movable terminal window.
*
* @param x the column number to place the top-left corner at. 0 is the
* left-most column.
* @param y the row number to place the top-left corner at. 0 is the
* top-most column.
* @param width the width of the window
* @param height the height of the window
* @param otherTile the other tile to resize when this window closes
*/
private TTerminalWindow makeTile(int x, int y, int width, int height,
final TTerminalWindow otherTile) {
// We pass flags to disable the zoom (maximize) button, disable
// "smart" window placement, and set the specific location.
TTerminalWindow tile = new TTerminalWindow(this, x, y,
TWindow.NOZOOMBOX | TWindow.ABSOLUTEXY,
new String[] { "/bin/bash", "--login" }, true) {
/**
* When this terminal closes, if otherTile is defined then resize
* it to overcover me.
*/
@Override
public void onClose() {
super.onClose();
if (otherTile != null) {
if (otherTile.getX() != getX()) {
// Undo the vertical split
otherTile.setX(Math.min(otherTile.getX(), getX()));
otherTile.setWidth(otherTile.getWidth() + getWidth());
}
if (otherTile.getY() != getY()) {
otherTile.setY(Math.min(otherTile.getY(), getY()));
otherTile.setHeight(otherTile.getHeight() + getHeight());
}
2021-12-19 10:57:05 -07:00
otherTile.onResize(new TResizeEvent(getBackend(),
TResizeEvent.Type.WIDGET,
2019-02-23 09:46:43 -07:00
otherTile.getWidth(), otherTile.getHeight()));
}
}
/**
* Prevent the user from resizing or moving this window.
*/
@Override
public void onMouseDown(final TMouseEvent mouse) {
super.onMouseDown(mouse);
stopMovements();
}
/**
* Prevent the user from resizing or moving this window.
*/
@Override
public void onKeypress(final TKeypressEvent keypress) {
super.onKeypress(keypress);
stopMovements();
}
/**
* Permit the user to use all of the menu items.
*/
@Override
public void onIdle() {
super.onIdle();
removeShortcutKeypress(jexer.TKeypress.kbAltT);
removeShortcutKeypress(jexer.TKeypress.kbF6);
}
};
// The initial window size was stock VT100 80x24. Change that now,
// and then call onResize() to notify ptypipe to set the shell's
// window size.
tile.setWidth(width);
tile.setHeight(height);
2021-12-19 10:57:05 -07:00
tile.onResize(new TResizeEvent(getBackend(), TResizeEvent.Type.WIDGET,
2019-02-23 09:46:43 -07:00
tile.getWidth(), tile.getHeight()));
return tile;
}
}