();
layoutManager = new LinearLayout();
cachedPreferredSize = null;
}
/**
* Adds a new child component to the panel. Where within the panel the child will be displayed is up to the layout
* manager assigned to this panel.
* @param component Child component to add to this panel
* @return Itself
*/
public synchronized Panel addComponent(Component component) {
if(component == null) {
throw new IllegalArgumentException("Cannot add null component");
}
if(components.contains(component)) {
return this;
}
components.add(component);
component.onAdded(this);
invalidate();
return this;
}
/**
* This method is a shortcut for calling:
*
* {@code
* component.setLayoutData(layoutData);
* panel.addComponent(component);
* }
*
* @param component Component to add to the panel
* @param layoutData Layout data to assign to the component
* @return Itself
*/
public Panel addComponent(Component component, LayoutData layoutData) {
if(component != null) {
component.setLayoutData(layoutData);
addComponent(component);
}
return this;
}
@Override
public boolean containsComponent(Component component) {
return component != null && component.hasParent(this);
}
@Override
public synchronized boolean removeComponent(Component component) {
if(component == null) {
throw new IllegalArgumentException("Cannot remove null component");
}
int index = components.indexOf(component);
if(index == -1) {
return false;
}
if(getBasePane() != null && getBasePane().getFocusedInteractable() == component) {
getBasePane().setFocusedInteractable(null);
}
components.remove(index);
component.onRemoved(this);
invalidate();
return true;
}
/**
* Removes all child components from this panel
* @return Itself
*/
public synchronized Panel removeAllComponents() {
for(Component component: new ArrayList(components)) {
removeComponent(component);
}
return this;
}
/**
* Assigns a new layout manager to this panel, replacing the previous layout manager assigned. Please note that if
* the panel is not empty at the time you assign a new layout manager, the existing components might not show up
* where you expect them and their layout data property might need to be re-assigned.
* @param layoutManager New layout manager this panel should be using
* @return Itself
*/
public synchronized Panel setLayoutManager(LayoutManager layoutManager) {
if(layoutManager == null) {
layoutManager = new AbsoluteLayout();
}
this.layoutManager = layoutManager;
invalidate();
return this;
}
/**
* Returns the layout manager assigned to this panel
* @return
*/
public LayoutManager getLayoutManager() {
return layoutManager;
}
@Override
public int getChildCount() {
synchronized(components) {
return components.size();
}
}
@Override
public Collection getChildren() {
synchronized(components) {
return new ArrayList(components);
}
}
@Override
protected ComponentRenderer createDefaultRenderer() {
return new ComponentRenderer() {
@Override
public TerminalSize getPreferredSize(Panel component) {
cachedPreferredSize = layoutManager.getPreferredSize(components);
return cachedPreferredSize;
}
@Override
public void drawComponent(TextGUIGraphics graphics, Panel component) {
if(isInvalid()) {
layout(graphics.getSize());
}
for(Component child: components) {
TextGUIGraphics componentGraphics = graphics.newTextGraphics(child.getPosition(), child.getSize());
child.draw(componentGraphics);
}
}
};
}
@Override
public TerminalSize calculatePreferredSize() {
if(cachedPreferredSize != null && !isInvalid()) {
return cachedPreferredSize;
}
return super.calculatePreferredSize();
}
@Override
public boolean isInvalid() {
for(Component component: components) {
if(component.isInvalid()) {
return true;
}
}
return super.isInvalid() || layoutManager.hasChanged();
}
@Override
public Interactable nextFocus(Interactable fromThis) {
boolean chooseNextAvailable = (fromThis == null);
for (Component component : components) {
if (chooseNextAvailable) {
if (component instanceof Interactable) {
return (Interactable) component;
}
else if (component instanceof Container) {
Interactable firstInteractable = ((Container)(component)).nextFocus(null);
if (firstInteractable != null) {
return firstInteractable;
}
}
continue;
}
if (component == fromThis) {
chooseNextAvailable = true;
continue;
}
if (component instanceof Container) {
Container container = (Container) component;
if (fromThis.isInside(container)) {
Interactable next = container.nextFocus(fromThis);
if (next == null) {
chooseNextAvailable = true;
} else {
return next;
}
}
}
}
return null;
}
@Override
public Interactable previousFocus(Interactable fromThis) {
boolean chooseNextAvailable = (fromThis == null);
List revComponents = new ArrayList(components);
Collections.reverse(revComponents);
for (Component component : revComponents) {
if (chooseNextAvailable) {
if (component instanceof Interactable) {
return (Interactable) component;
}
if (component instanceof Container) {
Interactable lastInteractable = ((Container)(component)).previousFocus(null);
if (lastInteractable != null) {
return lastInteractable;
}
}
continue;
}
if (component == fromThis) {
chooseNextAvailable = true;
continue;
}
if (component instanceof Container) {
Container container = (Container) component;
if (fromThis.isInside(container)) {
Interactable next = container.previousFocus(fromThis);
if (next == null) {
chooseNextAvailable = true;
} else {
return next;
}
}
}
}
return null;
}
@Override
public boolean handleInput(KeyStroke key) {
return false;
}
@Override
public void updateLookupMap(InteractableLookupMap interactableLookupMap) {
for(Component component: components) {
if(component instanceof Container) {
((Container)component).updateLookupMap(interactableLookupMap);
}
else if(component instanceof Interactable) {
interactableLookupMap.add((Interactable)component);
}
}
}
@Override
public void invalidate() {
super.invalidate();
//Propagate
for(Component component: components) {
component.invalidate();
}
}
private void layout(TerminalSize size) {
layoutManager.doLayout(size, components);
}
}