/* * Copyright 2010, Maarten Billemont * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.lyndir.lanterna.view; import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.googlecode.lanterna.input.Key; import com.googlecode.lanterna.screen.Screen; import com.googlecode.lanterna.terminal.Terminal; import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.util.StringUtils; import java.util.LinkedList; import java.util.List; import javax.annotation.Nonnull; /** * @author lhunath, 2013-07-19 */ public class View { @SuppressWarnings("NonConstantLogger") protected final Logger logger = Logger.get( getClass() ); private final List<View> children = new LinkedList<>(); private View parent; private Box oldMaximumMeasuredBoxInParent = new Box(); private Box measuredBoxInParent = new Box(); private Inset padding = new Inset(); private Inset margin = new Inset(); private Terminal.Color backgroundColor; private Terminal.Color backgroundPatternColor; private String backgroundPattern; private Terminal.Color infoTextColor; private Terminal.Color infoBackgroundColor; private Theme theme; public final void measure(final Screen screen, final Box maximumMeasuredBoxInParent) { boolean maximumMeasuredBoxChanged = !maximumMeasuredBoxInParent.equals( oldMaximumMeasuredBoxInParent ); oldMaximumMeasuredBoxInParent = maximumMeasuredBoxInParent; measuredBoxInParent = measureInParent( screen, maximumMeasuredBoxInParent, maximumMeasuredBoxChanged ); measureChildren( screen ); } protected Box measureInParent(final Screen screen, final Box maximumMeasuredBoxInParent, final boolean maximumMeasuredBoxChanged) { return maximumMeasuredBoxInParent; } protected void measureChildren(final Screen screen) { for (final View child : getChildren()) child.measure( screen, measuredBoxForChildInView( child ) ); } protected Box measuredBoxForChildInView(final View child) { return new Box( 0, getMeasuredBoxInParent().getSize().getWidth(), getMeasuredBoxInParent().getSize().getHeight(), 0 ); } public void addChild(final View child) { Preconditions.checkArgument( !child.getParent().isPresent(), "Cannot add child: child is already added to a parent." ); getChildren().add( child ); child.setParent( this ); } public final void draw(final Screen screen) { drawBackground( screen ); drawForeground( screen ); //drawLayoutDebug( screen ); drawChildren( screen ); } @SuppressWarnings({ "ForLoopThatDoesntUseLoopVariable", "AssignmentToForLoopParameter", "UnusedDeclaration" }) private void drawLayoutDebug(final Screen screen) { int parents = 0; for (View v = this; v.getParent().isPresent(); ++parents) v = v.getParent().get(); int p = 0; Box drawBox = getDrawBoxOnScreen(); for (int y = drawBox.getTop(); y <= drawBox.getBottom(); ++y) screen.putString( drawBox.getLeft(), y, StringUtils.repeat( "+", parents ) + p++, // Terminal.Color.WHITE, Terminal.Color.RED ); } protected void drawBackground(final Screen screen) { int p = 0; Box drawBox = getDrawBoxOnScreen(); for (int y = drawBox.getTop(); y <= drawBox.getBottom(); ++y) for (int x = drawBox.getLeft(); x <= drawBox.getRight(); ++x) { int backgroundPatternOffset = ((y % 2) + p++) % getBackgroundPattern().length(); screen.putString( x, y, getBackgroundPattern().substring( backgroundPatternOffset, backgroundPatternOffset + 1 ), // getBackgroundPatternColor(), getBackgroundColor() ); } } protected void drawForeground(final Screen screen) { } protected void drawChildren(final Screen screen) { for (final View child : getChildren()) child.draw( screen ); } @Nonnull public Optional<?> layoutValue(final LayoutParameter layoutParameter) { return Optional.absent(); } public final boolean handleKey(final Key key) { if (onKey( key )) return true; for (final View child : getChildren()) if (child.handleKey( key )) return true; return false; } /** * Handle key input. * * @param key The key that was read. * * @return true if the view handled the key (which will consume the key and stop other views from seeing it). */ protected boolean onKey(final Key key) { return false; } /** * The view hierarchy has been fully measured for the first time. */ protected void onReady() { } @Nonnull public Optional<View> getParent() { return Optional.fromNullable( parent ); } private void setParent(final View parent) { this.parent = parent; } protected List<View> getChildren() { return children; } public Box getMeasuredBoxInParent() { return measuredBoxInParent; } public final Box getMeasuredBoxOnScreen() { if (!getParent().isPresent()) return measuredBoxInParent; Box parentContentBox = getParent().get().getContentBoxOnScreen(); return measuredBoxInParent.translate( parentContentBox.getOrigin() ); } public final Box getDrawBoxOnScreen() { return getMeasuredBoxOnScreen().shrink( getMargin() ); } public final Box getContentBoxOnScreen() { return getDrawBoxOnScreen().shrink( getPadding() ); } public Inset getPadding() { return padding; } public void setPadding(final Inset padding) { this.padding = padding; } public Inset getMargin() { return margin; } public void setMargin(final Inset margin) { this.margin = margin; } public Terminal.Color getBackgroundColor() { return ifNotNullElse( backgroundColor, getTheme().bg() ); } public void setBackgroundColor(final Terminal.Color backgroundColor) { this.backgroundColor = backgroundColor; } public Terminal.Color getBackgroundPatternColor() { return ifNotNullElse( backgroundPatternColor, getTheme().bgPatternFg() ); } public void setBackgroundPatternColor(final Terminal.Color backgroundPatternColor) { this.backgroundPatternColor = backgroundPatternColor; } public String getBackgroundPattern() { return ifNotNullElse( backgroundPattern, getTheme().bgPattern() ); } public void setBackgroundPattern(final String backgroundPattern) { this.backgroundPattern = backgroundPattern; } public Terminal.Color getInfoTextColor() { return ifNotNullElse( infoTextColor, getTheme().infoFg() ); } public void setInfoTextColor(final Terminal.Color infoTextColor) { this.infoTextColor = infoTextColor; } public Terminal.Color getInfoBackgroundColor() { return ifNotNullElse( infoBackgroundColor, getTheme().infoBg() ); } public void setInfoBackgroundColor(final Terminal.Color infoBackgroundColor) { this.infoBackgroundColor = infoBackgroundColor; } public Theme getTheme() { if (theme != null) return theme; if (getParent().isPresent()) return getParent().get().getTheme(); return Themes.DEFAULT; } public void setTheme(final Theme theme) { this.theme = theme; } }