Commit | Line | Data |
---|---|---|
528874e2 KL |
1 | import jexer.TApplication; |
2 | import jexer.TTerminalWindow; | |
3 | import jexer.TWindow; | |
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; | |
9 | ||
10 | /** | |
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. | |
15 | * | |
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. | |
22 | */ | |
23 | public class JexerTilingWindowManager extends TApplication { | |
24 | ||
25 | /** | |
26 | * Menu item: split the terminal vertically. | |
27 | */ | |
28 | private static final int MENU_SPLIT_VERTICAL = 2000; | |
29 | ||
30 | /** | |
31 | * Menu item: split the terminal horizontally. | |
32 | */ | |
33 | private static final int MENU_SPLIT_HORIZONTAL = 2001; | |
34 | ||
35 | /** | |
36 | * Main entry point. | |
37 | */ | |
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"); | |
42 | ||
43 | JexerTilingWindowManager jtwm = new JexerTilingWindowManager(); | |
44 | (new Thread(jtwm)).start(); | |
45 | } | |
46 | ||
47 | /** | |
48 | * Public constructor chooses the ECMA-48 / Xterm backend. | |
49 | */ | |
50 | public JexerTilingWindowManager() throws Exception { | |
51 | super(BackendType.XTERM); | |
52 | ||
53 | // The stock tool menu has items for redrawing the screen, opening | |
54 | // images, and (when using the Swing backend) setting the font. | |
55 | addToolMenu(); | |
56 | ||
57 | // We will have one menu containing a mix of new and stock commands | |
58 | TMenu tileMenu = addMenu("&Tile"); | |
59 | ||
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"); | |
63 | ||
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); | |
73 | ||
74 | // Spin up the root tile | |
75 | TTerminalWindow rootTile = makeTile(0, 0, getScreen().getWidth(), | |
76 | getDesktopBottom() - 1, null); | |
77 | ||
78 | // Let's add some bling! Enable focus-follows-mouse. | |
79 | setFocusFollowsMouse(true); | |
80 | } | |
81 | ||
82 | /** | |
83 | * Process menu events. | |
84 | */ | |
85 | @Override | |
86 | protected boolean onMenu(TMenuEvent event) { | |
87 | if (event.getId() == MENU_SPLIT_VERTICAL) { | |
88 | splitVertical(); | |
89 | return true; | |
90 | } | |
91 | if (event.getId() == MENU_SPLIT_HORIZONTAL) { | |
92 | splitHorizontal(); | |
93 | return true; | |
94 | } | |
95 | ||
96 | return super.onMenu(event); | |
97 | } | |
98 | ||
99 | /** | |
100 | * Perform the vertical split. | |
101 | */ | |
102 | private void splitVertical() { | |
103 | TWindow window = getActiveWindow(); | |
104 | if (!(window instanceof TTerminalWindow)) { | |
105 | return; | |
106 | } | |
107 | ||
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())); | |
117 | } | |
118 | ||
119 | /** | |
120 | * Perform the horizontal split. | |
121 | */ | |
122 | private void splitHorizontal() { | |
123 | TWindow window = getActiveWindow(); | |
124 | if (!(window instanceof TTerminalWindow)) { | |
125 | return; | |
126 | } | |
127 | ||
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())); | |
137 | } | |
138 | ||
139 | /** | |
140 | * Create a non-resizable non-movable terminal window. | |
141 | * | |
142 | * @param x the column number to place the top-left corner at. 0 is the | |
143 | * left-most column. | |
144 | * @param y the row number to place the top-left corner at. 0 is the | |
145 | * top-most column. | |
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 | |
149 | */ | |
150 | private TTerminalWindow makeTile(int x, int y, int width, int height, | |
151 | final TTerminalWindow otherTile) { | |
152 | ||
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) { | |
158 | ||
159 | /** | |
160 | * When this terminal closes, if otherTile is defined then resize | |
161 | * it to overcover me. | |
162 | */ | |
163 | @Override | |
164 | public void onClose() { | |
165 | super.onClose(); | |
166 | ||
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()); | |
172 | } | |
173 | if (otherTile.getY() != getY()) { | |
174 | otherTile.setY(Math.min(otherTile.getY(), getY())); | |
175 | otherTile.setHeight(otherTile.getHeight() + getHeight()); | |
176 | } | |
177 | otherTile.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, | |
178 | otherTile.getWidth(), otherTile.getHeight())); | |
179 | } | |
180 | } | |
181 | ||
182 | /** | |
183 | * Prevent the user from resizing or moving this window. | |
184 | */ | |
185 | @Override | |
186 | public void onMouseDown(final TMouseEvent mouse) { | |
187 | super.onMouseDown(mouse); | |
188 | stopMovements(); | |
189 | } | |
190 | ||
191 | /** | |
192 | * Prevent the user from resizing or moving this window. | |
193 | */ | |
194 | @Override | |
195 | public void onKeypress(final TKeypressEvent keypress) { | |
196 | super.onKeypress(keypress); | |
197 | stopMovements(); | |
198 | } | |
199 | ||
200 | /** | |
201 | * Permit the user to use all of the menu items. | |
202 | */ | |
203 | @Override | |
204 | public void onIdle() { | |
205 | super.onIdle(); | |
206 | removeShortcutKeypress(jexer.TKeypress.kbAltT); | |
207 | removeShortcutKeypress(jexer.TKeypress.kbF6); | |
208 | } | |
209 | ||
210 | }; | |
211 | ||
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 | |
214 | // window size. | |
215 | tile.setWidth(width); | |
216 | tile.setHeight(height); | |
217 | tile.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, | |
218 | tile.getWidth(), tile.getHeight())); | |
219 | ||
220 | return tile; | |
221 | } | |
222 | ||
223 | } |