Note that this library is deeply inspired by Lanterna. Check it out if you are looking for a terminal emulator instead.
Need info? Check the Wiki | or Create an issue | Check our project Board | Ask us on Discord
If you want to work with Zircon you can add it to your project as a dependency.
from Maven:
<dependency>
<groupId>org.codetome.zircon</groupId>
<artifactId>zircon</artifactId>
<version>2017.4.0-RELEASE</version>
</dependency>
or you can also use Gradle:
compile("org.codetome.zircon:zircon:2017.4.0-RELEASE")
Want to use a SNAPSHOT
? Check this Wiki page
Before we start there are some guidelines which can help you if you are stuck:
- If you want to build something (a
TextImage
, aComponent
or anything which is part of the public API) it is almost sure that there is aBuilder
or a*Factory
for it. If you want to build aTextImage
you can use theTextImageBuilder
to do so. Always look for aBuilder
or aFactory
(in case ofTextColor
s for example) to create the desired object. Your IDE will help you with that - If you want to work with external files like tilesets or REXPaint files check the resource package.
There are a bunch of built-in tilesets for example which you can choose from but you can also load your own. The rule of thumb
is that if you need something external there is probably a
*Resource
for it (like the REXPaintResource). - Anything in the
api.beta
package is considered a BETA feature and is subject to change. - You can use anything you can find in the API package and they will not change (so your code won't break). The internal package however is considered private to Zircon so don't depend on anything in it.
- Some topics are explained in depth on the Wiki
- If you want to see some example code look here.
- If all else fails read the javadoc. API classes are rather well documented.
- If you have any problems which are not answered here feel free to ask us at the Hexworks Discord server.
In Zircon almost every object you might want to use has a Builder
for it.
This is the same for Terminals as well so let's create one using a TerminalBuilder:
import org.codetome.zircon.api.Size;
import org.codetome.zircon.api.builder.TerminalBuilder;
import org.codetome.zircon.api.terminal.Terminal;
public class Playground {
public static void main(String[] args) {
final Terminal terminal = TerminalBuilder.newBuilder()
.initialTerminalSize(Size.of(32, 16))
.build();
terminal.flush();
}
}
Running this snippet will result in this screen:
Adding and formatting content is also very simple:
import org.codetome.zircon.api.Modifiers;
import org.codetome.zircon.api.Size;
import org.codetome.zircon.api.builder.TerminalBuilder;
import org.codetome.zircon.api.color.ANSITextColor;
import org.codetome.zircon.api.terminal.Terminal;
public class Playground {
public static void main(String[] args) {
final Terminal terminal = TerminalBuilder.newBuilder()
.initialTerminalSize(Size.of(20, 8))
.build();
terminal.enableModifiers(Modifiers.VERTICAL_FLIP);
terminal.setForegroundColor(ANSITextColor.CYAN);
terminal.putCharacter('a');
terminal.resetColorsAndModifiers();
terminal.setForegroundColor(ANSITextColor.GREEN);
terminal.enableModifiers(Modifiers.HORIZONTAL_FLIP);
terminal.putCharacter('b');
terminal.resetColorsAndModifiers();
terminal.setForegroundColor(ANSITextColor.RED);
terminal.enableModifiers(Modifiers.CROSSED_OUT);
terminal.putCharacter('c');
terminal.flush();
}
}
Running the above code will result in something like this:
You can do a lot of fancy stuff with Modifiers, like this:
If interested check out the code examples here.
You might have noticed that the default font is not very nice looking, so let's see what else the TerminalBuilder can do for us:
import org.codetome.zircon.api.Size;
import org.codetome.zircon.api.builder.DeviceConfigurationBuilder;
import org.codetome.zircon.api.builder.TerminalBuilder;
import org.codetome.zircon.api.color.ANSITextColor;
import org.codetome.zircon.api.resource.CP437TilesetResource;
import org.codetome.zircon.api.terminal.Terminal;
import org.codetome.zircon.api.terminal.config.CursorStyle;
public class Playground {
private static final String TEXT = "Hello Zircon!";
public static void main(String[] args) {
final Terminal terminal = TerminalBuilder.newBuilder()
.initialTerminalSize(Size.of(20, 8))
.deviceConfiguration(DeviceConfigurationBuilder.newBuilder()
.cursorColor(ANSITextColor.RED)
.cursorStyle(CursorStyle.VERTICAL_BAR)
.cursorBlinking(true)
.blinkLengthInMilliSeconds(500)
.clipboardAvailable(true)
.build())
.font(CP437TilesetResource.WANDERLUST_16X16.toFont())
.title(TEXT)
.build();
terminal.setForegroundColor(ANSITextColor.GREEN);
terminal.setCursorVisibility(true);
TEXT.chars().forEach((c) -> terminal.putCharacter((char)c));
terminal.flush();
}
}
Font (and by extension resource) handling in Zircon is very simple and if you are interested in how to load your custom fonts and other resources take a look at the Resource handling wiki page.
Terminals alone won't suffice if you want to get any serious work done since they are rather rudimentary.
Let's create a Screen and fill it up with some stuff:
import org.codetome.zircon.api.Position;
import org.codetome.zircon.api.Size;
import org.codetome.zircon.api.builder.TerminalBuilder;
import org.codetome.zircon.api.builder.TextCharacterBuilder;
import org.codetome.zircon.api.builder.TextImageBuilder;
import org.codetome.zircon.api.component.ColorTheme;
import org.codetome.zircon.api.graphics.TextImage;
import org.codetome.zircon.api.resource.CP437TilesetResource;
import org.codetome.zircon.api.resource.ColorThemeResource;
import org.codetome.zircon.api.screen.Screen;
import org.codetome.zircon.api.terminal.Terminal;
public class Playground {
public static void main(String[] args) {
final Terminal terminal = TerminalBuilder.newBuilder()
.initialTerminalSize(Size.of(20, 8))
.font(CP437TilesetResource.WANDERLUST_16X16.toFont())
.build();
final Screen screen = TerminalBuilder.createScreenFor(terminal);
final ColorTheme theme = ColorThemeResource.ADRIFT_IN_DREAMS.getTheme();
final TextImage image = TextImageBuilder.newBuilder()
.size(terminal.getBoundableSize())
.filler(TextCharacterBuilder.newBuilder()
.foregroundColor(theme.getBrightForegroundColor())
.backgroundColor(theme.getBrightBackgroundColor())
.character('~')
.build())
.build();
screen.draw(image, Position.DEFAULT_POSITION);
screen.display();
}
}
and we've got a nice ocean:
What happens here is that we:
- Create a Screen
- Fetch a nice ColorTheme which has colors we need
- Create a TextImage with the colors added and fill it with
~
s - Draw the image onto the Screen
You can do so much more with Screens. If interested then check out A primer on Screens on the Wiki!
Zircon supports a bunch of Components out of the box:
- Button: A simple clickable component with corresponding event listeners
- CheckBox: Like a BUTTON but with checked / unchecked state
- Label: Simple component with text
- Header: Like a label but this one has emphasis (useful when using ColorThemes)
- Panel: A [Container] which can hold multiple Components
- RadioButtonGroup and RadioButton: Like a CheckBox but only one can be selected at a time
- TextBox
These components are rather simple and you can expect them to work in a way you might be familiar with:
- You can click on them (press and release are different events)
- You can attach event listeners on them
- Zircon implements focus handling so you can navigate between the components using the
[Tab]
key (forwards) or the[Shift]+[Tab]
key stroke (backwards). - Components can be hovered and they can change their styling when you do so
Let's look at an example (notes about how it works are in the comments):
import org.codetome.zircon.api.Position;
import org.codetome.zircon.api.Size;
import org.codetome.zircon.api.builder.TerminalBuilder;
import org.codetome.zircon.api.component.Button;
import org.codetome.zircon.api.component.CheckBox;
import org.codetome.zircon.api.component.Header;
import org.codetome.zircon.api.component.Panel;
import org.codetome.zircon.api.component.builder.ButtonBuilder;
import org.codetome.zircon.api.component.builder.CheckBoxBuilder;
import org.codetome.zircon.api.component.builder.HeaderBuilder;
import org.codetome.zircon.api.component.builder.PanelBuilder;
import org.codetome.zircon.api.resource.CP437TilesetResource;
import org.codetome.zircon.api.resource.ColorThemeResource;
import org.codetome.zircon.api.screen.Screen;
import org.codetome.zircon.api.terminal.Terminal;
public class Playground {
public static void main(String[] args) {
final Terminal terminal = TerminalBuilder.newBuilder()
.initialTerminalSize(Size.of(34, 18))
.font(CP437TilesetResource.WANDERLUST_16X16.toFont())
.build();
final Screen screen = TerminalBuilder.createScreenFor(terminal);
// We create a Panel which will hold our components
// Note that you can add components to the screen without a panel as well
Panel panel = PanelBuilder.newBuilder()
.wrapWithBox() // panels can be wrapped in a box
.title("Panel") // if a panel is wrapped in a box a title can be displayed
.wrapWithShadow() // shadow can be added
.size(Size.of(32, 16)) // the size must be smaller than the parent's size
.position(Position.OFFSET_1x1) // position is always relative to the parent
.build();
final Header header = HeaderBuilder.newBuilder()
// this will be 1x1 left and down from the top left
// corner of the panel
.position(Position.OFFSET_1x1)
.text("Header")
.build();
final CheckBox checkBox = CheckBoxBuilder.newBuilder()
.text("Check me!")
.position(Position.of(0, 1)
// the position class has some convenience methods
// for you to specify your component's position as
// relative to another one
.relativeToBottomOf(header))
.build();
final Button left = ButtonBuilder.newBuilder()
.position(Position.of(0, 1) // this means 1 row below the check box
.relativeToBottomOf(checkBox))
.text("Left")
.build();
final Button right = ButtonBuilder.newBuilder()
.position(Position.of(1, 0) // 1 column right relative to the left BUTTON
.relativeToRightOf(left))
.text("Right")
.build();
panel.addComponent(header);
panel.addComponent(checkBox);
panel.addComponent(left);
panel.addComponent(right);
screen.addComponent(panel);
// we can apply color themes to a screen
screen.applyColorTheme(ColorThemeResource.TECH_LIGHT.getTheme());
// this is how you can define interactions with a component
left.onMouseReleased((mouseAction -> {
screen.applyColorTheme(ColorThemeResource.ADRIFT_IN_DREAMS.getTheme());
}));
right.onMouseReleased((mouseAction -> {
screen.applyColorTheme(ColorThemeResource.SOLARIZED_DARK_ORANGE.getTheme());
}));
// in order to see the changes you need to display your screen.
screen.display();
}
}
And the result will look like this:
You can check out more examples here. Here are some screenshots of them:
There are a bunch of other stuff Zircon can do which are not detailed in this README but you can read about them in either the source code or the Wiki:
Both the Terminal and the Screen interfaces implement Layerable which means that you can add Layers on top of them. Every Layerable can have an arbitrary amount of Layers. Layers are like TextImages and you can also have transparency in them which can be used to create fancy effects. Look at the LayerBuilder to see how to use them. For more details check the layers Wiki page.
Note that when creating
Layer
s you can set itsoffset
from the builder but after attaching it to aTerminal
orScreen
you can change its position by callingmoveTo
with a newPosition
.
Both the Terminal and the Screen interfaces implement InputEmitter which means that they re-emit all inputs from your users (key strokes and mouse actions) and you can listen on them. There is a Wiki page with more info.
You can draw Shapes like rectangles and triangles by using one of the ShapeFactory implementations. Check the corresponding Wiki page for more info.
Zircon comes with a bunch of built-in fonts and tilesets. These come in 3 flavors:
- Physical fonts (Want to use physical fonts? Check how to use them here)
- CP437 tilesets (More on using them here)
- and Graphic tilesets (Usage info here)
Read more about them in the resource handling Wiki page if you want to know more or if you want to use your own tilesets and fonts.
Zircon also comes with its own tileset format (ztf
: Zircon Tileset Format) which is very easy to use. Its usage is detailed here.
REXPaint files (.xp
) can be loaded into Zircon Layer
s. Read about this feature here.
Zircon comes with a bunch of built-in color themes which you can apply to your components. If interested you can read more about how this works here.
Animations are a beta feature. More info here.
If you just want to peruse the Zircon API just navigate here. Everything which is intented to be the public API is there.
In order to work with Zircon you should get familiar with the core concepts. Zircon provides multiple layers of abstractions and it depends on your needs which one you should pick.
At the lowest level Zircon provides the Terminal interface. This provides you with a surface on which
you can draw TextCharacters. A TextCharacter is basically a character (like an x
) with additional
metadata like foregroundColor
and backgroundColor
. This surface sits on top of a GUI layer and
completely abstracts away how that layer works. For example the default implementation of the Terminal
interface uses Swing under the hood. The main advantage of using Terminals is that by implementing all
its methods you can swap Swing with something else (like SWT) and use all higher
level abstractions on top of it (like Screens) which depend on Terminal (more on Screens later).
Working with Terminals is very simple but somewhat limited. A Terminal is responsible for:
- drawing characters (by position or by cursor) on the screen
- handling inputs (keyboard and mouse) which are emitted by the GUI layer
- handling the cursor which is visible to the user
- handling Layering
- storing style information
- drawing TextImages on top of it
This seems like a lot of things to do at once so you might ask "How is this SOLID?". Zircon solves this problem with composition: All of the above mentioned categories are handled by an object within a Terminal which is responsible for only one thing. For example Terminal implements the Layerable interface and internally all operations defined by it are delegated to an object which implements Layerable only. You can peruse these here. In this sense you can consider a Terminal as a Facade.
Objects like TextCharacters can have foreground and background colors. You can either use the ANSITextColor
enum
to pick a pre-defined TextColor or you can create a new one by using TextColorFactory. This class
has some useful factory methods for this like: fromAWTColor
, fromRGB
and fromString
. The latter can be
called with simple CSS-like strings (eg: #334455
).
If you don't want to set all these colors by hand or you want to have a color template and use it to set colors to multiple things you can use a StyleSet which is basically a Value Object which holds fore/background colors and modifiers.
When working with TextCharacters apart from giving them color you might want to apply some special
Modifier to them like UNDERLINE
or VERTICAL_FLIP
.
You can do this by picking the right Modifier from the Modifiers class.
You can set any number of Modifiers to each TextCharacter individually and when
you refresh your Terminal by calling flush
on it you will see them applied.
An image built from TextCharacters with color and style information. These are completely in memory and not visible, but can be used when drawing on other DrawSurfaces, like a Screen or a Terminal. In other words TextImages are like real images but composed of TextCharacters to create ASCII art and the like.
Screens are in-memory representations of your Terminal. They are double buffered
which means that you write to a back-buffer and when you refresh
your Screen only the changes will
be written to the backing Terminal instance. Multiple Screens can be attached to the same Terminal
object which means that you can have more than one screen in your app and you can switch between them
simultaneously by using the display
method. Screens also let you use Components like Buttons
and Panels.
If you want to read more about the design philosophy behind Zircon check this page on Wiki!
If you are interested in how components work then this Wiki page can help you.
If you want to see a new feature feel free to create a new Issue or discuss it with us on discord. Here are some features which are either under way or planned:
- libGDX support
- Layouts for Components with resizing support
- Components for games like MapDisplay
- Multi-Font support
- Next to
ColorTheme
s we'll introduceComponentTheme
s as well (custom look and feel for your components)
Zircon is made available under the MIT License.
Zircon is created and maintained by Adam Arold, Milan Boleradszki and Gergely Lukacsy
We're open to suggestions, feel free to message us on Discord or open an issue. Pull requests are also welcome!
Zircon is powered by: