1 import jexer
.TApplication
;
2 import jexer
.TTerminalWindow
;
4 import jexer
.event
.TKeypressEvent
;
5 import jexer
.event
.TMenuEvent
;
6 import jexer
.event
.TMouseEvent
;
7 import jexer
.event
.TResizeEvent
;
8 import jexer
.menu
.TMenu
;
11 * Implements a simple tiling window manager. A root non-moveable
12 * non-resizable terminal window is created first, which can be split
13 * horizontally or vertically. Each new window retains a reference to its
14 * "parent", and upon closing resizes that parent back to its original size.
16 * This example shows what can be done with minimal changes to stock Jexer
17 * widgets. You will quickly see that closing a "parent" tile does not cause
18 * the "child" tile to resize. You could make a real subclass of
19 * TTerminalWindow that has extra fields and/or communicates more with
20 * JexerTilingWindowManager to get full coverage of tile creation,
21 * destruction, placement, movement, and so on.
23 public class JexerTilingWindowManager
extends TApplication
{
26 * Menu item: split the terminal vertically.
28 private static final int MENU_SPLIT_VERTICAL
= 2000;
31 * Menu item: split the terminal horizontally.
33 private static final int MENU_SPLIT_HORIZONTAL
= 2001;
38 public static void main(String
[] args
) throws Exception
{
39 // For this application, we must use ptypipe so that the tile shells
40 // can be aware of their size.
41 System
.setProperty("jexer.TTerminal.ptypipe", "true");
43 JexerTilingWindowManager jtwm
= new JexerTilingWindowManager();
44 (new Thread(jtwm
)).start();
48 * Public constructor chooses the ECMA-48 / Xterm backend.
50 public JexerTilingWindowManager() throws Exception
{
51 super(BackendType
.XTERM
);
53 // The stock tool menu has items for redrawing the screen, opening
54 // images, and (when using the Swing backend) setting the font.
57 // We will have one menu containing a mix of new and stock commands
58 TMenu tileMenu
= addMenu("&Tile");
60 // New commands for this example: split vertical and horizontal.
61 tileMenu
.addItem(MENU_SPLIT_VERTICAL
, "&Vertical Split");
62 tileMenu
.addItem(MENU_SPLIT_HORIZONTAL
, "&Horizontal Split");
64 // Stock commands: a new shell with resizable window, previous, next,
65 // close, and exit program.
66 tileMenu
.addItem(TMenu
.MID_SHELL
, "&Floating");
67 tileMenu
.addSeparator();
68 tileMenu
.addDefaultItem(TMenu
.MID_WINDOW_PREVIOUS
);
69 tileMenu
.addDefaultItem(TMenu
.MID_WINDOW_NEXT
);
70 tileMenu
.addDefaultItem(TMenu
.MID_WINDOW_CLOSE
);
71 tileMenu
.addSeparator();
72 tileMenu
.addDefaultItem(TMenu
.MID_EXIT
);
74 // Spin up the root tile
75 TTerminalWindow rootTile
= makeTile(0, 0, getScreen().getWidth(),
76 getDesktopBottom() - 1, null);
78 // Let's add some bling! Enable focus-follows-mouse.
79 setFocusFollowsMouse(true);
83 * Process menu events.
86 protected boolean onMenu(TMenuEvent event
) {
87 if (event
.getId() == MENU_SPLIT_VERTICAL
) {
91 if (event
.getId() == MENU_SPLIT_HORIZONTAL
) {
96 return super.onMenu(event
);
100 * Perform the vertical split.
102 private void splitVertical() {
103 TWindow window
= getActiveWindow();
104 if (!(window
instanceof TTerminalWindow
)) {
108 TTerminalWindow tile
= (TTerminalWindow
) window
;
109 // Give the extra column to the new tile.
110 int newWidth
= (tile
.getWidth() + 1) / 2;
111 int newY
= tile
.getY() - 1;
112 int newX
= tile
.getX() + tile
.getWidth() - newWidth
;
113 makeTile(newX
, newY
, newWidth
, tile
.getHeight(), tile
);
114 tile
.setWidth(tile
.getWidth() - newWidth
);
115 tile
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
116 tile
.getWidth(), tile
.getHeight()));
120 * Perform the horizontal split.
122 private void splitHorizontal() {
123 TWindow window
= getActiveWindow();
124 if (!(window
instanceof TTerminalWindow
)) {
128 TTerminalWindow tile
= (TTerminalWindow
) window
;
129 // Give the extra row to the new tile.
130 int newHeight
= (tile
.getHeight() + 1) / 2;
131 int newY
= tile
.getY() - 1 + tile
.getHeight() - newHeight
;
132 int newX
= tile
.getX();
133 makeTile(newX
, newY
, tile
.getWidth(), newHeight
, tile
);
134 tile
.setHeight(tile
.getHeight() - newHeight
);
135 tile
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
136 tile
.getWidth(), tile
.getHeight()));
140 * Create a non-resizable non-movable terminal window.
142 * @param x the column number to place the top-left corner at. 0 is the
144 * @param y the row number to place the top-left corner at. 0 is the
146 * @param width the width of the window
147 * @param height the height of the window
148 * @param otherTile the other tile to resize when this window closes
150 private TTerminalWindow
makeTile(int x
, int y
, int width
, int height
,
151 final TTerminalWindow otherTile
) {
153 // We pass flags to disable the zoom (maximize) button, disable
154 // "smart" window placement, and set the specific location.
155 TTerminalWindow tile
= new TTerminalWindow(this, x
, y
,
156 TWindow
.NOZOOMBOX
| TWindow
.ABSOLUTEXY
,
157 new String
[] { "/bin/bash", "--login" }, true) {
160 * When this terminal closes, if otherTile is defined then resize
161 * it to overcover me.
164 public void onClose() {
167 if (otherTile
!= null) {
168 if (otherTile
.getX() != getX()) {
169 // Undo the vertical split
170 otherTile
.setX(Math
.min(otherTile
.getX(), getX()));
171 otherTile
.setWidth(otherTile
.getWidth() + getWidth());
173 if (otherTile
.getY() != getY()) {
174 otherTile
.setY(Math
.min(otherTile
.getY(), getY()));
175 otherTile
.setHeight(otherTile
.getHeight() + getHeight());
177 otherTile
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
178 otherTile
.getWidth(), otherTile
.getHeight()));
183 * Prevent the user from resizing or moving this window.
186 public void onMouseDown(final TMouseEvent mouse
) {
187 super.onMouseDown(mouse
);
192 * Prevent the user from resizing or moving this window.
195 public void onKeypress(final TKeypressEvent keypress
) {
196 super.onKeypress(keypress
);
201 * Permit the user to use all of the menu items.
204 public void onIdle() {
206 removeShortcutKeypress(jexer
.TKeypress
.kbAltT
);
207 removeShortcutKeypress(jexer
.TKeypress
.kbF6
);
212 // The initial window size was stock VT100 80x24. Change that now,
213 // and then call onResize() to notify ptypipe to set the shell's
215 tile
.setWidth(width
);
216 tile
.setHeight(height
);
217 tile
.onResize(new TResizeEvent(TResizeEvent
.Type
.WIDGET
,
218 tile
.getWidth(), tile
.getHeight()));