package com.googlecode.lanterna.gui2;
import com.googlecode.lanterna.*;
import com.googlecode.lanterna.graphics.TextGraphics;
import com.googlecode.lanterna.graphics.Theme;
import com.googlecode.lanterna.graphics.ThemeDefinition;
import java.util.Arrays;
import java.util.List;
public class Borders {
private Borders() {
}
private enum BorderStyle {
Solid,
Bevel,
ReverseBevel,
}
public static Border singleLine() {
return singleLine("");
}
public static Border singleLine(String title) {
return new SingleLine(title, BorderStyle.Solid);
}
public static Border singleLineBevel() {
return singleLineBevel("");
}
public static Border singleLineBevel(String title) {
return new SingleLine(title, BorderStyle.Bevel);
}
public static Border singleLineReverseBevel() {
return singleLineReverseBevel("");
}
public static Border singleLineReverseBevel(String title) {
return new SingleLine(title, BorderStyle.ReverseBevel);
}
public static Border doubleLine() {
return doubleLine("");
}
public static Border doubleLine(String title) {
return new DoubleLine(title, BorderStyle.Solid);
}
public static Border doubleLineBevel() {
return doubleLineBevel("");
}
public static Border doubleLineBevel(String title) {
return new DoubleLine(title, BorderStyle.Bevel);
}
public static Border doubleLineReverseBevel() {
return doubleLineReverseBevel("");
}
public static Border doubleLineReverseBevel(String title) {
return new DoubleLine(title, BorderStyle.ReverseBevel);
}
private static abstract class StandardBorder extends AbstractBorder {
private final String title;
protected final BorderStyle borderStyle;
protected StandardBorder(String title, BorderStyle borderStyle) {
if (title == null) {
throw new IllegalArgumentException("Cannot create a border with null title");
}
this.borderStyle = borderStyle;
this.title = title;
}
public String getTitle() {
return title;
}
@Override
public String toString() {
return getClass().getSimpleName() + "{" + title + "}";
}
}
private static abstract class AbstractBorderRenderer implements Border.BorderRenderer {
private final BorderStyle borderStyle;
protected AbstractBorderRenderer(BorderStyle borderStyle) {
this.borderStyle = borderStyle;
}
@Override
public TerminalSize getPreferredSize(Border component) {
StandardBorder border = (StandardBorder)component;
Component wrappedComponent = border.getComponent();
TerminalSize preferredSize;
if (wrappedComponent == null) {
preferredSize = TerminalSize.ZERO;
} else {
preferredSize = wrappedComponent.getPreferredSize();
}
preferredSize = preferredSize.withRelativeColumns(2).withRelativeRows(2);
String borderTitle = border.getTitle();
return preferredSize.max(new TerminalSize((borderTitle.isEmpty() ? 2 : TerminalTextUtils.getColumnWidth(borderTitle) + 4), 2));
}
@Override
public TerminalPosition getWrappedComponentTopLeftOffset() {
return TerminalPosition.OFFSET_1x1;
}
@Override
public TerminalSize getWrappedComponentSize(TerminalSize borderSize) {
return borderSize
.withRelativeColumns(-Math.min(2, borderSize.getColumns()))
.withRelativeRows(-Math.min(2, borderSize.getRows()));
}
@Override
public void drawComponent(TextGUIGraphics graphics, Border component) {
StandardBorder border = (StandardBorder)component;
Component wrappedComponent = border.getComponent();
if(wrappedComponent == null) {
return;
}
TerminalSize drawableArea = graphics.getSize();
char horizontalLine = getHorizontalLine(component.getTheme());
char verticalLine = getVerticalLine(component.getTheme());
char bottomLeftCorner = getBottomLeftCorner(component.getTheme());
char topLeftCorner = getTopLeftCorner(component.getTheme());
char bottomRightCorner = getBottomRightCorner(component.getTheme());
char topRightCorner = getTopRightCorner(component.getTheme());
char titleLeft = getTitleLeft(component.getTheme());
char titleRight = getTitleRight(component.getTheme());
ThemeDefinition themeDefinition = component.getTheme().getDefinition(AbstractBorder.class);
if(borderStyle == BorderStyle.Bevel) {
graphics.applyThemeStyle(themeDefinition.getPreLight());
}
else {
graphics.applyThemeStyle(themeDefinition.getNormal());
}
graphics.setCharacter(0, drawableArea.getRows() - 1, bottomLeftCorner);
if(drawableArea.getRows() > 2) {
graphics.drawLine(new TerminalPosition(0, drawableArea.getRows() - 2), new TerminalPosition(0, 1), verticalLine);
}
graphics.setCharacter(0, 0, topLeftCorner);
if(drawableArea.getColumns() > 2) {
graphics.drawLine(new TerminalPosition(1, 0), new TerminalPosition(drawableArea.getColumns() - 2, 0), horizontalLine);
}
if(borderStyle == BorderStyle.ReverseBevel) {
graphics.applyThemeStyle(themeDefinition.getPreLight());
}
else {
graphics.applyThemeStyle(themeDefinition.getNormal());
}
graphics.setCharacter(drawableArea.getColumns() - 1, 0, topRightCorner);
if(drawableArea.getRows() > 2) {
graphics.drawLine(new TerminalPosition(drawableArea.getColumns() - 1, 1),
new TerminalPosition(drawableArea.getColumns() - 1, drawableArea.getRows() - 2),
verticalLine);
}
graphics.setCharacter(drawableArea.getColumns() - 1, drawableArea.getRows() - 1, bottomRightCorner);
if(drawableArea.getColumns() > 2) {
graphics.drawLine(new TerminalPosition(1, drawableArea.getRows() - 1),
new TerminalPosition(drawableArea.getColumns() - 2, drawableArea.getRows() - 1),
horizontalLine);
}
if(border.getTitle() != null && !border.getTitle().isEmpty() &&
drawableArea.getColumns() >= TerminalTextUtils.getColumnWidth(border.getTitle()) + 4) {
graphics.applyThemeStyle(themeDefinition.getActive());
graphics.putString(2, 0, border.getTitle());
if(borderStyle == BorderStyle.Bevel) {
graphics.applyThemeStyle(themeDefinition.getPreLight());
}
else {
graphics.applyThemeStyle(themeDefinition.getNormal());
}
graphics.setCharacter(1, 0, titleLeft);
graphics.setCharacter(2 + TerminalTextUtils.getColumnWidth(border.getTitle()), 0, titleRight);
}
wrappedComponent.draw(graphics.newTextGraphics(getWrappedComponentTopLeftOffset(), getWrappedComponentSize(drawableArea)));
joinLinesWithFrame(graphics);
}
protected abstract char getHorizontalLine(Theme theme);
protected abstract char getVerticalLine(Theme theme);
protected abstract char getBottomLeftCorner(Theme theme);
protected abstract char getTopLeftCorner(Theme theme);
protected abstract char getBottomRightCorner(Theme theme);
protected abstract char getTopRightCorner(Theme theme);
protected abstract char getTitleLeft(Theme theme);
protected abstract char getTitleRight(Theme theme);
}
public static void joinLinesWithFrame(TextGraphics graphics) {
TerminalSize drawableArea = graphics.getSize();
if(drawableArea.getRows() <= 2 || drawableArea.getColumns() <= 2) {
return;
}
int upperRow = 0;
int lowerRow = drawableArea.getRows() - 1;
int leftRow = 0;
int rightRow = drawableArea.getColumns() - 1;
List<Character> junctionFromBelowSingle = Arrays.asList(
Symbols.SINGLE_LINE_VERTICAL,
Symbols.BOLD_FROM_NORMAL_SINGLE_LINE_VERTICAL,
Symbols.SINGLE_LINE_CROSS,
Symbols.DOUBLE_LINE_HORIZONTAL_SINGLE_LINE_CROSS,
Symbols.SINGLE_LINE_BOTTOM_LEFT_CORNER,
Symbols.SINGLE_LINE_BOTTOM_RIGHT_CORNER,
Symbols.SINGLE_LINE_T_LEFT,
Symbols.SINGLE_LINE_T_RIGHT,
Symbols.SINGLE_LINE_T_UP,
Symbols.SINGLE_LINE_T_DOUBLE_LEFT,
Symbols.SINGLE_LINE_T_DOUBLE_RIGHT,
Symbols.DOUBLE_LINE_T_SINGLE_UP);
List<Character> junctionFromBelowDouble = Arrays.asList(
Symbols.DOUBLE_LINE_VERTICAL,
Symbols.DOUBLE_LINE_CROSS,
Symbols.DOUBLE_LINE_VERTICAL_SINGLE_LINE_CROSS,
Symbols.DOUBLE_LINE_BOTTOM_LEFT_CORNER,
Symbols.DOUBLE_LINE_BOTTOM_RIGHT_CORNER,
Symbols.DOUBLE_LINE_T_LEFT,
Symbols.DOUBLE_LINE_T_RIGHT,
Symbols.DOUBLE_LINE_T_UP,
Symbols.DOUBLE_LINE_T_SINGLE_LEFT,
Symbols.DOUBLE_LINE_T_SINGLE_RIGHT,
Symbols.SINGLE_LINE_T_DOUBLE_UP);
List<Character> junctionFromAboveSingle = Arrays.asList(
Symbols.SINGLE_LINE_VERTICAL,
Symbols.BOLD_TO_NORMAL_SINGLE_LINE_VERTICAL,
Symbols.SINGLE_LINE_CROSS,
Symbols.DOUBLE_LINE_HORIZONTAL_SINGLE_LINE_CROSS,
Symbols.SINGLE_LINE_TOP_LEFT_CORNER,
Symbols.SINGLE_LINE_TOP_RIGHT_CORNER,
Symbols.SINGLE_LINE_T_LEFT,
Symbols.SINGLE_LINE_T_RIGHT,
Symbols.SINGLE_LINE_T_DOWN,
Symbols.SINGLE_LINE_T_DOUBLE_LEFT,
Symbols.SINGLE_LINE_T_DOUBLE_RIGHT,
Symbols.DOUBLE_LINE_T_SINGLE_DOWN);
List<Character> junctionFromAboveDouble = Arrays.asList(
Symbols.DOUBLE_LINE_VERTICAL,
Symbols.DOUBLE_LINE_CROSS,
Symbols.DOUBLE_LINE_VERTICAL_SINGLE_LINE_CROSS,
Symbols.DOUBLE_LINE_TOP_LEFT_CORNER,
Symbols.DOUBLE_LINE_TOP_RIGHT_CORNER,
Symbols.DOUBLE_LINE_T_LEFT,
Symbols.DOUBLE_LINE_T_RIGHT,
Symbols.DOUBLE_LINE_T_DOWN,
Symbols.DOUBLE_LINE_T_SINGLE_LEFT,
Symbols.DOUBLE_LINE_T_SINGLE_RIGHT,
Symbols.SINGLE_LINE_T_DOUBLE_DOWN);
List<Character> junctionFromLeftSingle = Arrays.asList(
Symbols.SINGLE_LINE_HORIZONTAL,
Symbols.BOLD_TO_NORMAL_SINGLE_LINE_HORIZONTAL,
Symbols.SINGLE_LINE_CROSS,
Symbols.DOUBLE_LINE_VERTICAL_SINGLE_LINE_CROSS,
Symbols.SINGLE_LINE_BOTTOM_LEFT_CORNER,
Symbols.SINGLE_LINE_TOP_LEFT_CORNER,
Symbols.SINGLE_LINE_T_UP,
Symbols.SINGLE_LINE_T_DOWN,
Symbols.SINGLE_LINE_T_RIGHT,
Symbols.SINGLE_LINE_T_DOUBLE_UP,
Symbols.SINGLE_LINE_T_DOUBLE_DOWN,
Symbols.DOUBLE_LINE_T_SINGLE_RIGHT);
List<Character> junctionFromLeftDouble = Arrays.asList(
Symbols.DOUBLE_LINE_HORIZONTAL,
Symbols.DOUBLE_LINE_CROSS,
Symbols.DOUBLE_LINE_HORIZONTAL_SINGLE_LINE_CROSS,
Symbols.DOUBLE_LINE_BOTTOM_LEFT_CORNER,
Symbols.DOUBLE_LINE_TOP_LEFT_CORNER,
Symbols.DOUBLE_LINE_T_UP,
Symbols.DOUBLE_LINE_T_DOWN,
Symbols.DOUBLE_LINE_T_RIGHT,
Symbols.DOUBLE_LINE_T_SINGLE_UP,
Symbols.DOUBLE_LINE_T_SINGLE_DOWN,
Symbols.SINGLE_LINE_T_DOUBLE_RIGHT);
List<Character> junctionFromRightSingle = Arrays.asList(
Symbols.SINGLE_LINE_HORIZONTAL,
Symbols.BOLD_FROM_NORMAL_SINGLE_LINE_HORIZONTAL,
Symbols.SINGLE_LINE_CROSS,
Symbols.DOUBLE_LINE_VERTICAL_SINGLE_LINE_CROSS,
Symbols.SINGLE_LINE_BOTTOM_RIGHT_CORNER,
Symbols.SINGLE_LINE_TOP_RIGHT_CORNER,
Symbols.SINGLE_LINE_T_UP,
Symbols.SINGLE_LINE_T_DOWN,
Symbols.SINGLE_LINE_T_LEFT,
Symbols.SINGLE_LINE_T_DOUBLE_UP,
Symbols.SINGLE_LINE_T_DOUBLE_DOWN,
Symbols.DOUBLE_LINE_T_SINGLE_LEFT);
List<Character> junctionFromRightDouble = Arrays.asList(
Symbols.DOUBLE_LINE_HORIZONTAL,
Symbols.DOUBLE_LINE_CROSS,
Symbols.DOUBLE_LINE_HORIZONTAL_SINGLE_LINE_CROSS,
Symbols.DOUBLE_LINE_BOTTOM_RIGHT_CORNER,
Symbols.DOUBLE_LINE_TOP_RIGHT_CORNER,
Symbols.DOUBLE_LINE_T_UP,
Symbols.DOUBLE_LINE_T_DOWN,
Symbols.DOUBLE_LINE_T_LEFT,
Symbols.DOUBLE_LINE_T_SINGLE_UP,
Symbols.DOUBLE_LINE_T_SINGLE_DOWN,
Symbols.SINGLE_LINE_T_DOUBLE_LEFT);
for(int column = 1; column < drawableArea.getColumns() - 1; column++) {
TextCharacter borderCharacter = graphics.getCharacter(column, upperRow);
if(borderCharacter == null) {
continue;
}
TextCharacter neighbourCharacter = graphics.getCharacter(column, upperRow + 1);
if(neighbourCharacter != null) {
char neighbour = neighbourCharacter.getCharacter();
if(borderCharacter.getCharacter() == Symbols.SINGLE_LINE_HORIZONTAL) {
if(junctionFromBelowSingle.contains(neighbour)) {
graphics.setCharacter(column, upperRow, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_DOWN));
}
else if(junctionFromBelowDouble.contains(neighbour)) {
graphics.setCharacter(column, upperRow, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_DOUBLE_DOWN));
}
}
else if(borderCharacter.getCharacter() == Symbols.DOUBLE_LINE_HORIZONTAL) {
if(junctionFromBelowSingle.contains(neighbour)) {
graphics.setCharacter(column, upperRow, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_SINGLE_DOWN));
}
else if(junctionFromBelowDouble.contains(neighbour)) {
graphics.setCharacter(column, upperRow, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_DOWN));
}
}
}
borderCharacter = graphics.getCharacter(column, lowerRow);
if(borderCharacter == null) {
continue;
}
neighbourCharacter = graphics.getCharacter(column, lowerRow - 1);
if(neighbourCharacter != null) {
char neighbour = neighbourCharacter.getCharacter();
if(borderCharacter.getCharacter() == Symbols.SINGLE_LINE_HORIZONTAL) {
if(junctionFromAboveSingle.contains(neighbour)) {
graphics.setCharacter(column, lowerRow, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_UP));
}
else if(junctionFromAboveDouble.contains(neighbour)) {
graphics.setCharacter(column, lowerRow, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_DOUBLE_UP));
}
}
else if(borderCharacter.getCharacter() == Symbols.DOUBLE_LINE_HORIZONTAL) {
if(junctionFromAboveSingle.contains(neighbour)) {
graphics.setCharacter(column, lowerRow, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_SINGLE_UP));
}
else if(junctionFromAboveDouble.contains(neighbour)) {
graphics.setCharacter(column, lowerRow, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_UP));
}
}
}
}
for(int row = 1; row < drawableArea.getRows() - 1; row++) {
TextCharacter borderCharacter = graphics.getCharacter(leftRow, row);
if(borderCharacter == null) {
continue;
}
TextCharacter neighbourCharacter = graphics.getCharacter(leftRow + 1, row);
if(neighbourCharacter != null) {
char neighbour = neighbourCharacter.getCharacter();
if(borderCharacter.getCharacter() == Symbols.SINGLE_LINE_VERTICAL) {
if(junctionFromRightSingle.contains(neighbour)) {
graphics.setCharacter(leftRow, row, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_RIGHT));
}
else if(junctionFromRightDouble.contains(neighbour)) {
graphics.setCharacter(leftRow, row, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_DOUBLE_RIGHT));
}
}
else if(borderCharacter.getCharacter() == Symbols.DOUBLE_LINE_VERTICAL) {
if(junctionFromRightSingle.contains(neighbour)) {
graphics.setCharacter(leftRow, row, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_SINGLE_RIGHT));
}
else if(junctionFromRightDouble.contains(neighbour)) {
graphics.setCharacter(leftRow, row, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_RIGHT));
}
}
}
borderCharacter = graphics.getCharacter(rightRow, row);
if(borderCharacter == null) {
continue;
}
neighbourCharacter = graphics.getCharacter(rightRow - 1, row);
if(neighbourCharacter != null) {
char neighbour = neighbourCharacter.getCharacter();
if(borderCharacter.getCharacter() == Symbols.SINGLE_LINE_VERTICAL) {
if(junctionFromLeftSingle.contains(neighbour)) {
graphics.setCharacter(rightRow, row, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_LEFT));
}
else if(junctionFromLeftDouble.contains(neighbour)) {
graphics.setCharacter(rightRow, row, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_DOUBLE_LEFT));
}
}
else if(borderCharacter.getCharacter() == Symbols.DOUBLE_LINE_VERTICAL) {
if(junctionFromLeftSingle.contains(neighbour)) {
graphics.setCharacter(rightRow, row, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_SINGLE_LEFT));
}
else if(junctionFromLeftDouble.contains(neighbour)) {
graphics.setCharacter(rightRow, row, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_LEFT));
}
}
}
}
}
private static class SingleLine extends StandardBorder {
private SingleLine(String title, BorderStyle borderStyle) {
super(title, borderStyle);
}
@Override
protected BorderRenderer createDefaultRenderer() {
return new SingleLineRenderer(borderStyle);
}
}
private static class SingleLineRenderer extends AbstractBorderRenderer {
public SingleLineRenderer(BorderStyle borderStyle) {
super(borderStyle);
}
@Override
protected char getTopRightCorner(Theme theme) {
return theme.getDefinition(SingleLine.class).getCharacter("TOP_RIGHT_CORNER", Symbols.SINGLE_LINE_TOP_RIGHT_CORNER);
}
@Override
protected char getBottomRightCorner(Theme theme) {
return theme.getDefinition(SingleLine.class).getCharacter("BOTTOM_RIGHT_CORNER", Symbols.SINGLE_LINE_BOTTOM_RIGHT_CORNER);
}
@Override
protected char getTopLeftCorner(Theme theme) {
return theme.getDefinition(SingleLine.class).getCharacter("TOP_LEFT_CORNER", Symbols.SINGLE_LINE_TOP_LEFT_CORNER);
}
@Override
protected char getBottomLeftCorner(Theme theme) {
return theme.getDefinition(SingleLine.class).getCharacter("BOTTOM_LEFT_CORNER", Symbols.SINGLE_LINE_BOTTOM_LEFT_CORNER);
}
@Override
protected char getVerticalLine(Theme theme) {
return theme.getDefinition(SingleLine.class).getCharacter("VERTICAL_LINE", Symbols.SINGLE_LINE_VERTICAL);
}
@Override
protected char getHorizontalLine(Theme theme) {
return theme.getDefinition(SingleLine.class).getCharacter("HORIZONTAL_LINE", Symbols.SINGLE_LINE_HORIZONTAL);
}
@Override
protected char getTitleLeft(Theme theme) {
return theme.getDefinition(SingleLine.class).getCharacter("TITLE_LEFT", Symbols.SINGLE_LINE_HORIZONTAL);
}
@Override
protected char getTitleRight(Theme theme) {
return theme.getDefinition(SingleLine.class).getCharacter("TITLE_RIGHT", Symbols.SINGLE_LINE_HORIZONTAL);
}
}
private static class DoubleLine extends StandardBorder {
private DoubleLine(String title, BorderStyle borderStyle) {
super(title, borderStyle);
}
@Override
protected BorderRenderer createDefaultRenderer() {
return new DoubleLineRenderer(borderStyle);
}
}
private static class DoubleLineRenderer extends AbstractBorderRenderer {
public DoubleLineRenderer(BorderStyle borderStyle) {
super(borderStyle);
}
@Override
protected char getTopRightCorner(Theme theme) {
return theme.getDefinition(DoubleLine.class).getCharacter("TOP_RIGHT_CORNER", Symbols.DOUBLE_LINE_TOP_RIGHT_CORNER);
}
@Override
protected char getBottomRightCorner(Theme theme) {
return theme.getDefinition(DoubleLine.class).getCharacter("BOTTOM_RIGHT_CORNER", Symbols.DOUBLE_LINE_BOTTOM_RIGHT_CORNER);
}
@Override
protected char getTopLeftCorner(Theme theme) {
return theme.getDefinition(DoubleLine.class).getCharacter("TOP_LEFT_CORNER", Symbols.DOUBLE_LINE_TOP_LEFT_CORNER);
}
@Override
protected char getBottomLeftCorner(Theme theme) {
return theme.getDefinition(DoubleLine.class).getCharacter("BOTTOM_LEFT_CORNER", Symbols.DOUBLE_LINE_BOTTOM_LEFT_CORNER);
}
@Override
protected char getVerticalLine(Theme theme) {
return theme.getDefinition(DoubleLine.class).getCharacter("VERTICAL_LINE", Symbols.DOUBLE_LINE_VERTICAL);
}
@Override
protected char getHorizontalLine(Theme theme) {
return theme.getDefinition(DoubleLine.class).getCharacter("HORIZONTAL_LINE", Symbols.DOUBLE_LINE_HORIZONTAL);
}
@Override
protected char getTitleLeft(Theme theme) {
return theme.getDefinition(DoubleLine.class).getCharacter("TITLE_LEFT", Symbols.DOUBLE_LINE_HORIZONTAL);
}
@Override
protected char getTitleRight(Theme theme) {
return theme.getDefinition(DoubleLine.class).getCharacter("TITLE_RIGHT", Symbols.DOUBLE_LINE_HORIZONTAL);
}
}
}