tiling window manager example

This commit is contained in:
Kevin Lamonte 2019-02-23 10:46:43 -06:00
parent 4ac6237dc3
commit 528874e2e7
3 changed files with 237 additions and 3 deletions

1
.gitignore vendored
View file

@ -26,3 +26,4 @@ misc/**
pmd.bash
pmd-results.html
examples/*.sh

View file

@ -137,9 +137,19 @@ it and you'll see an application like this:
![The Example Code Above](/screenshots/readme_application.png?raw=true "The application in the text of README.md")
See the files in jexer.demos for many more detailed examples showing
all of the existing UI controls. The available demos can be run as
follows:
More Examples
-------------
The examples/ folder currently contains:
* A [prototype tiling window
manager](/examples/JavaTilingWindowManager.java) in less than 250
lines of code.
jexer.demos contains official demos showing all of the existing UI
controls. The demos can be run as follows:
* 'java -jar jexer.jar' . This will use System.in/out with
xterm-like sequences on non-Windows non-Mac platforms. On Windows

View file

@ -0,0 +1,223 @@
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);
tile.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
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);
tile.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
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());
}
otherTile.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
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);
tile.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
tile.getWidth(), tile.getHeight()));
return tile;
}
}