mirror of
https://gitlab.com/AutumnMeowMeow/jexer
synced 2024-09-19 11:50:19 -06:00
tiling window manager example
This commit is contained in:
parent
4ac6237dc3
commit
528874e2e7
3 changed files with 237 additions and 3 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -26,3 +26,4 @@ misc/**
|
|||
|
||||
pmd.bash
|
||||
pmd-results.html
|
||||
examples/*.sh
|
||||
|
|
16
README.md
16
README.md
|
@ -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
|
||||
|
|
223
examples/JexerTilingWindowManager.java
Normal file
223
examples/JexerTilingWindowManager.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue