2 * This file is part of lanterna (http://code.google.com/p/lanterna/).
4 * lanterna is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 * Copyright (C) 2010-2015 Martin
19 package com
.googlecode
.lanterna
.gui2
;
21 import com
.googlecode
.lanterna
.TerminalPosition
;
22 import com
.googlecode
.lanterna
.TerminalSize
;
23 import com
.googlecode
.lanterna
.input
.KeyStroke
;
24 import com
.googlecode
.lanterna
.input
.KeyType
;
25 import com
.googlecode
.lanterna
.input
.MouseAction
;
28 * This abstract implementation of {@code BasePane} has the common code shared by all different concrete
31 public abstract class AbstractBasePane
implements BasePane
{
32 protected final ContentHolder contentHolder
;
33 protected InteractableLookupMap interactableLookupMap
;
34 private Interactable focusedInteractable
;
35 private boolean invalid
;
36 private boolean strictFocusChange
;
37 private boolean enableDirectionBasedMovements
;
39 protected AbstractBasePane() {
40 this.contentHolder
= new ContentHolder();
41 this.interactableLookupMap
= new InteractableLookupMap(new TerminalSize(80, 25));
43 this.strictFocusChange
= false;
44 this.enableDirectionBasedMovements
= true;
48 public boolean isInvalid() {
49 return invalid
|| contentHolder
.isInvalid();
53 public void invalidate() {
57 contentHolder
.invalidate();
61 public void draw(TextGUIGraphics graphics
) {
62 graphics
.applyThemeStyle(graphics
.getThemeDefinition(Window
.class).getNormal());
64 contentHolder
.draw(graphics
);
66 if(!interactableLookupMap
.getSize().equals(graphics
.getSize())) {
67 interactableLookupMap
= new InteractableLookupMap(graphics
.getSize());
69 interactableLookupMap
.reset();
71 contentHolder
.updateLookupMap(interactableLookupMap
);
72 //interactableLookupMap.debug();
77 public boolean handleInput(KeyStroke key
) {
78 if(key
.getKeyType() == KeyType
.MouseEvent
) {
79 MouseAction mouseAction
= (MouseAction
)key
;
80 TerminalPosition localCoordinates
= fromGlobal(mouseAction
.getPosition());
81 Interactable interactable
= interactableLookupMap
.getInteractableAt(localCoordinates
);
82 interactable
.handleInput(key
);
84 else if(focusedInteractable
!= null) {
85 Interactable next
= null;
86 Interactable
.FocusChangeDirection direction
= Interactable
.FocusChangeDirection
.TELEPORT
; //Default
87 Interactable
.Result result
= focusedInteractable
.handleInput(key
);
88 if(!enableDirectionBasedMovements
) {
89 if(result
== Interactable
.Result
.MOVE_FOCUS_DOWN
|| result
== Interactable
.Result
.MOVE_FOCUS_RIGHT
) {
90 result
= Interactable
.Result
.MOVE_FOCUS_NEXT
;
92 else if(result
== Interactable
.Result
.MOVE_FOCUS_UP
|| result
== Interactable
.Result
.MOVE_FOCUS_LEFT
) {
93 result
= Interactable
.Result
.MOVE_FOCUS_PREVIOUS
;
100 //Filter the event recursively through all parent containers until we hit null; give the containers
101 //a chance to absorb the event
102 Container parent
= focusedInteractable
.getParent();
103 while(parent
!= null) {
104 if(parent
.handleInput(key
)) {
107 parent
= parent
.getParent();
110 case MOVE_FOCUS_NEXT
:
111 next
= contentHolder
.nextFocus(focusedInteractable
);
113 next
= contentHolder
.nextFocus(null);
115 direction
= Interactable
.FocusChangeDirection
.NEXT
;
117 case MOVE_FOCUS_PREVIOUS
:
118 next
= contentHolder
.previousFocus(focusedInteractable
);
120 next
= contentHolder
.previousFocus(null);
122 direction
= Interactable
.FocusChangeDirection
.PREVIOUS
;
124 case MOVE_FOCUS_DOWN
:
125 next
= interactableLookupMap
.findNextDown(focusedInteractable
);
126 direction
= Interactable
.FocusChangeDirection
.DOWN
;
127 if(next
== null && !strictFocusChange
) {
128 next
= contentHolder
.nextFocus(focusedInteractable
);
129 direction
= Interactable
.FocusChangeDirection
.NEXT
;
132 case MOVE_FOCUS_LEFT
:
133 next
= interactableLookupMap
.findNextLeft(focusedInteractable
);
134 direction
= Interactable
.FocusChangeDirection
.LEFT
;
136 case MOVE_FOCUS_RIGHT
:
137 next
= interactableLookupMap
.findNextRight(focusedInteractable
);
138 direction
= Interactable
.FocusChangeDirection
.RIGHT
;
141 next
= interactableLookupMap
.findNextUp(focusedInteractable
);
142 direction
= Interactable
.FocusChangeDirection
.UP
;
143 if(next
== null && !strictFocusChange
) {
144 next
= contentHolder
.previousFocus(focusedInteractable
);
145 direction
= Interactable
.FocusChangeDirection
.PREVIOUS
;
150 setFocusedInteractable(next
, direction
);
158 public Component
getComponent() {
159 return contentHolder
.getComponent();
163 public void setComponent(Component component
) {
164 contentHolder
.setComponent(component
);
168 public Interactable
getFocusedInteractable() {
169 return focusedInteractable
;
173 public TerminalPosition
getCursorPosition() {
174 if(focusedInteractable
== null) {
177 TerminalPosition position
= focusedInteractable
.getCursorLocation();
178 if(position
== null) {
181 //Don't allow the component to set the cursor outside of its own boundaries
182 if(position
.getColumn() < 0 ||
183 position
.getRow() < 0 ||
184 position
.getColumn() >= focusedInteractable
.getSize().getColumns() ||
185 position
.getRow() >= focusedInteractable
.getSize().getRows()) {
188 return focusedInteractable
.toBasePane(position
);
192 public void setFocusedInteractable(Interactable toFocus
) {
193 setFocusedInteractable(toFocus
,
195 Interactable
.FocusChangeDirection
.TELEPORT
: Interactable
.FocusChangeDirection
.RESET
);
198 protected void setFocusedInteractable(Interactable toFocus
, Interactable
.FocusChangeDirection direction
) {
199 if(focusedInteractable
== toFocus
) {
202 if(focusedInteractable
!= null) {
203 focusedInteractable
.onLeaveFocus(direction
, focusedInteractable
);
205 Interactable previous
= focusedInteractable
;
206 focusedInteractable
= toFocus
;
207 if(toFocus
!= null) {
208 toFocus
.onEnterFocus(direction
, previous
);
214 public void setStrictFocusChange(boolean strictFocusChange
) {
215 this.strictFocusChange
= strictFocusChange
;
219 public void setEnableDirectionBasedMovements(boolean enableDirectionBasedMovements
) {
220 this.enableDirectionBasedMovements
= enableDirectionBasedMovements
;
223 protected class ContentHolder
extends AbstractComposite
<Container
> {
225 public void setComponent(Component component
) {
226 if(getComponent() == component
) {
229 setFocusedInteractable(null);
230 super.setComponent(component
);
231 if(focusedInteractable
== null && component
instanceof Interactable
) {
232 setFocusedInteractable((Interactable
)component
);
234 else if(focusedInteractable
== null && component
instanceof Container
) {
235 setFocusedInteractable(((Container
)component
).nextFocus(null));
239 public boolean removeComponent(Component component
) {
240 boolean removed
= super.removeComponent(component
);
242 focusedInteractable
= null;
248 public TextGUI
getTextGUI() {
249 return AbstractBasePane
.this.getTextGUI();
253 protected ComponentRenderer
<Container
> createDefaultRenderer() {
254 return new ComponentRenderer
<Container
>() {
256 public TerminalSize
getPreferredSize(Container component
) {
257 Component subComponent
= getComponent();
258 if(subComponent
== null) {
259 return TerminalSize
.ZERO
;
261 return subComponent
.getPreferredSize();
265 public void drawComponent(TextGUIGraphics graphics
, Container component
) {
266 Component subComponent
= getComponent();
267 if(subComponent
== null) {
270 subComponent
.draw(graphics
);
276 public TerminalPosition
toGlobal(TerminalPosition position
) {
277 return AbstractBasePane
.this.toGlobal(position
);
281 public TerminalPosition
toBasePane(TerminalPosition position
) {
286 public BasePane
getBasePane() {
287 return AbstractBasePane
.this;