+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.input.KeyStroke;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This class is the basic building block for creating user interfaces, being the standard implementation of
- * {@code Container} that supports multiple children. A {@code Panel} is a component that can contain one or more
- * other components, including nested panels. The panel itself doesn't have any particular appearance and isn't
- * interactable by itself, although you can set a border for the panel and interactable components inside the panel will
- * receive input focus as expected.
- *
- * @author Martin
- */
-public class Panel extends AbstractComponent<Panel> implements Container {
- private final List<Component> components;
- private LayoutManager layoutManager;
- private TerminalSize cachedPreferredSize;
-
- /**
- * Default constructor, creates a new panel with no child components and by default set to a vertical
- * {@code LinearLayout} layout manager.
- */
- public Panel() {
- components = new ArrayList<Component>();
- 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:
- * <pre>
- * {@code
- * component.setLayoutData(layoutData);
- * panel.addComponent(component);
- * }
- * </pre>
- * @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<Component>(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<Component> getChildren() {
- synchronized(components) {
- return new ArrayList<Component>(components);
- }
- }
-
- @Override
- protected ComponentRenderer<Panel> createDefaultRenderer() {
- return new ComponentRenderer<Panel>() {
-
- @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<Component> revComponents = new ArrayList<Component>(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);
- }
-}