1 package com
.googlecode
.lanterna
.gui2
;
3 import com
.googlecode
.lanterna
.TerminalPosition
;
4 import com
.googlecode
.lanterna
.TerminalSize
;
9 * The default window manager implementation used by Lanterna. New windows will be generally added in a tiled manner,
10 * starting in the top-left corner and moving down-right as new windows are added. By using the various window hints
11 * that are available you have some control over how the window manager will place and size the windows.
15 public class DefaultWindowManager
implements WindowManager
{
17 private final WindowDecorationRenderer windowDecorationRenderer
;
18 private TerminalSize lastKnownScreenSize
;
21 * Default constructor, will create a window manager that uses {@code DefaultWindowDecorationRenderer} for drawing
22 * window decorations. Any size calculations done before the text GUI has actually been started and displayed on
23 * the terminal will assume the terminal size is 80x24.
25 public DefaultWindowManager() {
26 this(new DefaultWindowDecorationRenderer());
30 * Creates a new {@code DefaultWindowManager} with a specific window decoration renderer. Any size calculations done
31 * before the text GUI has actually been started and displayed on the terminal will assume the terminal size is
34 * @param windowDecorationRenderer Window decoration renderer to use when drawing windows
36 public DefaultWindowManager(WindowDecorationRenderer windowDecorationRenderer
) {
37 this(windowDecorationRenderer
, null);
41 * Creates a new {@code DefaultWindowManager} using a {@code DefaultWindowDecorationRenderer} for drawing window
42 * decorations. Any size calculations done before the text GUI has actually been started and displayed on the
43 * terminal will use the size passed in with the {@code initialScreenSize} parameter
45 * @param initialScreenSize Size to assume the terminal has until the text GUI is started and can be notified of the
48 public DefaultWindowManager(TerminalSize initialScreenSize
) {
49 this(new DefaultWindowDecorationRenderer(), initialScreenSize
);
53 * Creates a new {@code DefaultWindowManager} using a specified {@code windowDecorationRenderer} for drawing window
54 * decorations. Any size calculations done before the text GUI has actually been started and displayed on the
55 * terminal will use the size passed in with the {@code initialScreenSize} parameter
57 * @param windowDecorationRenderer Window decoration renderer to use when drawing windows
58 * @param initialScreenSize Size to assume the terminal has until the text GUI is started and can be notified of the
61 public DefaultWindowManager(WindowDecorationRenderer windowDecorationRenderer
, TerminalSize initialScreenSize
) {
62 this.windowDecorationRenderer
= windowDecorationRenderer
;
63 if(initialScreenSize
!= null) {
64 this.lastKnownScreenSize
= initialScreenSize
;
67 this.lastKnownScreenSize
= new TerminalSize(80, 24);
72 public boolean isInvalid() {
77 public WindowDecorationRenderer
getWindowDecorationRenderer(Window window
) {
78 if(window
.getHints().contains(Window
.Hint
.NO_DECORATIONS
)) {
79 return new EmptyWindowDecorationRenderer();
81 return windowDecorationRenderer
;
85 public void onAdded(WindowBasedTextGUI textGUI
, Window window
, List
<Window
> allWindows
) {
86 WindowDecorationRenderer decorationRenderer
= getWindowDecorationRenderer(window
);
87 TerminalSize expectedDecoratedSize
= decorationRenderer
.getDecoratedSize(window
, window
.getPreferredSize());
88 window
.setDecoratedSize(expectedDecoratedSize
);
90 if(window
.getHints().contains(Window
.Hint
.FIXED_POSITION
)) {
91 //Don't place the window, assume the position is already set
93 else if(allWindows
.isEmpty()) {
94 window
.setPosition(TerminalPosition
.OFFSET_1x1
);
96 else if(window
.getHints().contains(Window
.Hint
.CENTERED
)) {
97 int left
= (lastKnownScreenSize
.getColumns() - expectedDecoratedSize
.getColumns()) / 2;
98 int top
= (lastKnownScreenSize
.getRows() - expectedDecoratedSize
.getRows()) / 2;
99 window
.setPosition(new TerminalPosition(left
, top
));
102 TerminalPosition nextPosition
= allWindows
.get(allWindows
.size() - 1).getPosition().withRelative(2, 1);
103 if(nextPosition
.getColumn() + expectedDecoratedSize
.getColumns() > lastKnownScreenSize
.getColumns() ||
104 nextPosition
.getRow() + expectedDecoratedSize
.getRows() > lastKnownScreenSize
.getRows()) {
105 nextPosition
= TerminalPosition
.OFFSET_1x1
;
107 window
.setPosition(nextPosition
);
110 // Finally, run through the usual calculations so the window manager's usual prepare method can have it's say
111 prepareWindow(lastKnownScreenSize
, window
);
115 public void onRemoved(WindowBasedTextGUI textGUI
, Window window
, List
<Window
> allWindows
) {
120 public void prepareWindows(WindowBasedTextGUI textGUI
, List
<Window
> allWindows
, TerminalSize screenSize
) {
121 this.lastKnownScreenSize
= screenSize
;
122 for(Window window
: allWindows
) {
123 prepareWindow(screenSize
, window
);
128 * Called by {@link DefaultWindowManager} when iterating through all windows to decide their size and position. If
129 * you override {@link DefaultWindowManager} to add your own logic to how windows are placed on the screen, you can
130 * override this method and selectively choose which window to interfere with. Note that the two key properties that
131 * are read by the GUI system after preparing all windows are the position and decorated size. Your custom
132 * implementation should set these two fields directly on the window. You can infer the decorated size from the
133 * content size by using the window decoration renderer that is attached to the window manager.
135 * @param screenSize Size of the terminal that is available to draw on
136 * @param window Window to prepare decorated size and position for
138 protected void prepareWindow(TerminalSize screenSize
, Window window
) {
139 WindowDecorationRenderer decorationRenderer
= getWindowDecorationRenderer(window
);
140 TerminalSize contentAreaSize
;
141 if(window
.getHints().contains(Window
.Hint
.FIXED_SIZE
)) {
142 contentAreaSize
= window
.getSize();
145 contentAreaSize
= window
.getPreferredSize();
147 TerminalSize size
= decorationRenderer
.getDecoratedSize(window
, contentAreaSize
);
148 TerminalPosition position
= window
.getPosition();
150 if(window
.getHints().contains(Window
.Hint
.FULL_SCREEN
)) {
151 position
= TerminalPosition
.TOP_LEFT_CORNER
;
154 else if(window
.getHints().contains(Window
.Hint
.EXPANDED
)) {
155 position
= TerminalPosition
.OFFSET_1x1
;
156 size
= screenSize
.withRelative(
157 -Math
.min(4, screenSize
.getColumns()),
158 -Math
.min(3, screenSize
.getRows()));
159 if(!size
.equals(window
.getDecoratedSize())) {
163 else if(window
.getHints().contains(Window
.Hint
.FIT_TERMINAL_WINDOW
) ||
164 window
.getHints().contains(Window
.Hint
.CENTERED
)) {
165 //If the window is too big for the terminal, move it up towards 0x0 and if that's not enough then shrink
167 while(position
.getRow() > 0 && position
.getRow() + size
.getRows() > screenSize
.getRows()) {
168 position
= position
.withRelativeRow(-1);
170 while(position
.getColumn() > 0 && position
.getColumn() + size
.getColumns() > screenSize
.getColumns()) {
171 position
= position
.withRelativeColumn(-1);
173 if(position
.getRow() + size
.getRows() > screenSize
.getRows()) {
174 size
= size
.withRows(screenSize
.getRows() - position
.getRow());
176 if(position
.getColumn() + size
.getColumns() > screenSize
.getColumns()) {
177 size
= size
.withColumns(screenSize
.getColumns() - position
.getColumn());
179 if(window
.getHints().contains(Window
.Hint
.CENTERED
)) {
180 int left
= (lastKnownScreenSize
.getColumns() - size
.getColumns()) / 2;
181 int top
= (lastKnownScreenSize
.getRows() - size
.getRows()) / 2;
182 position
= new TerminalPosition(left
, top
);
186 window
.setPosition(position
);
187 window
.setDecoratedSize(size
);