mirror of
https://gitlab.com/AutumnMeowMeow/jexer
synced 2024-09-19 11:50:19 -06:00
image thumbnail viewer example
This commit is contained in:
parent
d4c334ef77
commit
5434cb2bf4
3 changed files with 329 additions and 0 deletions
|
@ -148,6 +148,10 @@ The examples/ folder currently contains:
|
|||
manager](/examples/JexerTilingWindowManager.java) in less than 250
|
||||
lines of code.
|
||||
|
||||
* A [prototype image thumbnail
|
||||
viewer](/examples/JexerImageViewer.java) in less than 350 lines of
|
||||
code.
|
||||
|
||||
jexer.demos contains official demos showing all of the existing UI
|
||||
controls. The demos can be run as follows:
|
||||
|
||||
|
|
324
examples/JexerImageViewer.java
Normal file
324
examples/JexerImageViewer.java
Normal file
|
@ -0,0 +1,324 @@
|
|||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import jexer.TAction;
|
||||
import jexer.TApplication;
|
||||
import jexer.TDesktop;
|
||||
import jexer.TDirectoryList;
|
||||
import jexer.TImage;
|
||||
import jexer.backend.SwingTerminal;
|
||||
import jexer.bits.CellAttributes;
|
||||
import jexer.bits.GraphicsChars;
|
||||
import jexer.event.TKeypressEvent;
|
||||
import jexer.event.TResizeEvent;
|
||||
import jexer.menu.TMenu;
|
||||
import jexer.ttree.TDirectoryTreeItem;
|
||||
import jexer.ttree.TTreeItem;
|
||||
import jexer.ttree.TTreeViewWidget;
|
||||
import static jexer.TKeypress.*;
|
||||
|
||||
/**
|
||||
* Implements a simple image thumbnail file viewer. Much of this code was
|
||||
* stripped down from TFileOpenBox.
|
||||
*/
|
||||
public class JexerImageViewer extends TApplication {
|
||||
|
||||
/**
|
||||
* Main entry point.
|
||||
*/
|
||||
public static void main(String [] args) throws Exception {
|
||||
// For this application, we must use ptypipe so that the tile shells
|
||||
// can be aware of their size.
|
||||
|
||||
JexerImageViewer app = new JexerImageViewer();
|
||||
(new Thread(app)).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Public constructor chooses the ECMA-48 / Xterm backend.
|
||||
*/
|
||||
public JexerImageViewer() throws Exception {
|
||||
super(BackendType.XTERM);
|
||||
|
||||
// The stock tool menu has items for redrawing the screen, opening
|
||||
// images, and (when using the Swing backend) setting the font.
|
||||
addToolMenu();
|
||||
|
||||
// We will have one menu containing a mix of new and stock commands
|
||||
TMenu fileMenu = addMenu("&File");
|
||||
|
||||
// Stock commands: a new shell, exit program.
|
||||
fileMenu.addDefaultItem(TMenu.MID_SHELL);
|
||||
fileMenu.addSeparator();
|
||||
fileMenu.addDefaultItem(TMenu.MID_EXIT);
|
||||
|
||||
// Filter the files list to support image suffixes only.
|
||||
List<String> filters = new ArrayList<String>();
|
||||
filters.add("^.*\\.[Jj][Pp][Gg]$");
|
||||
filters.add("^.*\\.[Jj][Pp][Ee][Gg]$");
|
||||
filters.add("^.*\\.[Pp][Nn][Gg]$");
|
||||
filters.add("^.*\\.[Gg][Ii][Ff]$");
|
||||
filters.add("^.*\\.[Bb][Mm][Pp]$");
|
||||
setDesktop(new ImageViewerDesktop(this, ".", filters));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The desktop contains a tree view on the left, list of files on the top
|
||||
* right, and image view on the bottom right.
|
||||
*/
|
||||
class ImageViewerDesktop extends TDesktop {
|
||||
|
||||
/**
|
||||
* The left-side tree view pane.
|
||||
*/
|
||||
private TTreeViewWidget treeView;
|
||||
|
||||
/**
|
||||
* The data behind treeView.
|
||||
*/
|
||||
private TDirectoryTreeItem treeViewRoot;
|
||||
|
||||
/**
|
||||
* The top-right-side directory list pane.
|
||||
*/
|
||||
private TDirectoryList directoryList;
|
||||
|
||||
/**
|
||||
* The bottom-right-side image pane.
|
||||
*/
|
||||
private TImage imageWidget;
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
* @param application the TApplication that manages this window
|
||||
* @param path path of selected file
|
||||
* @param filters a list of strings that files must match to be displayed
|
||||
* @throws IOException of a java.io operation throws
|
||||
*/
|
||||
public ImageViewerDesktop(final TApplication application, final String path,
|
||||
final List<String> filters) throws IOException {
|
||||
|
||||
super(application);
|
||||
setActive(true);
|
||||
|
||||
// Add directory treeView
|
||||
treeView = addTreeViewWidget(0, 0, getWidth() / 2, getHeight() - 1,
|
||||
new TAction() {
|
||||
public void DO() {
|
||||
TTreeItem item = treeView.getSelected();
|
||||
File selectedDir = ((TDirectoryTreeItem) item).getFile();
|
||||
try {
|
||||
directoryList.setPath(selectedDir.getCanonicalPath());
|
||||
if (directoryList.getList().size() > 0) {
|
||||
setThumbnail(directoryList.getPath());
|
||||
} else {
|
||||
if (imageWidget != null) {
|
||||
getChildren().remove(imageWidget);
|
||||
}
|
||||
imageWidget = null;
|
||||
}
|
||||
activate(treeView);
|
||||
} catch (IOException e) {
|
||||
// If the backend is Swing, we can emit the stack
|
||||
// trace to stderr. Otherwise, just squash it.
|
||||
if (getScreen() instanceof SwingTerminal) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
treeViewRoot = new TDirectoryTreeItem(treeView, path, true);
|
||||
|
||||
// Add directory files list
|
||||
directoryList = addDirectoryList(path, getWidth() / 2 + 1, 0,
|
||||
getWidth() / 2 - 1, getHeight() / 2,
|
||||
|
||||
new TAction() {
|
||||
public void DO() {
|
||||
setThumbnail(directoryList.getPath());
|
||||
}
|
||||
},
|
||||
new TAction() {
|
||||
|
||||
public void DO() {
|
||||
setThumbnail(directoryList.getPath());
|
||||
}
|
||||
},
|
||||
filters);
|
||||
|
||||
// This appears to be a bug. If I pass keystrokes to the tree view,
|
||||
// it will center correctly.
|
||||
treeView.onKeypress(new TKeypressEvent(kbDown));
|
||||
treeView.onKeypress(new TKeypressEvent(kbUp));
|
||||
|
||||
if (directoryList.getList().size() > 0) {
|
||||
activate(directoryList);
|
||||
setThumbnail(directoryList.getPath());
|
||||
} else {
|
||||
activate(treeView);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle window/screen resize events.
|
||||
*
|
||||
* @param event resize event
|
||||
*/
|
||||
@Override
|
||||
public void onResize(final TResizeEvent event) {
|
||||
|
||||
// Resize the tree and list
|
||||
treeView.setY(1);
|
||||
treeView.setWidth(getWidth() / 2);
|
||||
treeView.setHeight(getHeight() - 1);
|
||||
treeView.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
|
||||
treeView.getWidth(),
|
||||
treeView.getHeight()));
|
||||
treeView.getTreeView().onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
|
||||
treeView.getWidth() - 1,
|
||||
treeView.getHeight() - 1));
|
||||
directoryList.setX(getWidth() / 2 + 1);
|
||||
directoryList.setY(1);
|
||||
directoryList.setWidth(getWidth() / 2 - 1);
|
||||
directoryList.setHeight(getHeight() / 2 - 1);
|
||||
directoryList.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
|
||||
directoryList.getWidth(),
|
||||
directoryList.getHeight()));
|
||||
|
||||
// Recreate the image
|
||||
if (imageWidget != null) {
|
||||
getChildren().remove(imageWidget);
|
||||
}
|
||||
imageWidget = null;
|
||||
if (directoryList.getList().size() > 0) {
|
||||
activate(directoryList);
|
||||
setThumbnail(directoryList.getPath());
|
||||
} else {
|
||||
activate(treeView);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle keystrokes.
|
||||
*
|
||||
* @param keypress keystroke event
|
||||
*/
|
||||
@Override
|
||||
public void onKeypress(final TKeypressEvent keypress) {
|
||||
|
||||
if (treeView.isActive() || directoryList.isActive()) {
|
||||
if ((keypress.equals(kbEnter))
|
||||
|| (keypress.equals(kbUp))
|
||||
|| (keypress.equals(kbDown))
|
||||
|| (keypress.equals(kbPgUp))
|
||||
|| (keypress.equals(kbPgDn))
|
||||
|| (keypress.equals(kbHome))
|
||||
|| (keypress.equals(kbEnd))
|
||||
) {
|
||||
// Tree view will be changing, update the directory list.
|
||||
super.onKeypress(keypress);
|
||||
|
||||
// This is the same action as treeView's enter.
|
||||
TTreeItem item = treeView.getSelected();
|
||||
File selectedDir = ((TDirectoryTreeItem) item).getFile();
|
||||
try {
|
||||
if (treeView.isActive()) {
|
||||
directoryList.setPath(selectedDir.getCanonicalPath());
|
||||
}
|
||||
if (directoryList.getList().size() > 0) {
|
||||
activate(directoryList);
|
||||
setThumbnail(directoryList.getPath());
|
||||
} else {
|
||||
if (imageWidget != null) {
|
||||
getChildren().remove(imageWidget);
|
||||
}
|
||||
imageWidget = null;
|
||||
activate(treeView);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// If the backend is Swing, we can emit the stack trace
|
||||
// to stderr. Otherwise, just squash it.
|
||||
if (getScreen() instanceof SwingTerminal) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Pass to my parent
|
||||
super.onKeypress(keypress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw me on screen.
|
||||
*/
|
||||
@Override
|
||||
public void draw() {
|
||||
CellAttributes background = getTheme().getColor("tdesktop.background");
|
||||
putAll(' ', background);
|
||||
|
||||
vLineXY(getWidth() / 2, 0, getHeight(),
|
||||
GraphicsChars.WINDOW_SIDE, getBackground());
|
||||
|
||||
hLineXY(getWidth() / 2, getHeight() / 2, (getWidth() + 1) / 2,
|
||||
GraphicsChars.WINDOW_TOP, getBackground());
|
||||
|
||||
putCharXY(getWidth() / 2, getHeight() / 2,
|
||||
GraphicsChars.WINDOW_LEFT_TEE, getBackground());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the image thumbnail.
|
||||
*
|
||||
* @param file the image file
|
||||
*/
|
||||
private void setThumbnail(final File file) {
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BufferedImage image = null;
|
||||
try {
|
||||
image = ImageIO.read(file);
|
||||
} catch (IOException e) {
|
||||
// If the backend is Swing, we can emit the stack trace to
|
||||
// stderr. Otherwise, just squash it.
|
||||
if (getScreen() instanceof SwingTerminal) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (imageWidget != null) {
|
||||
getChildren().remove(imageWidget);
|
||||
}
|
||||
int width = getWidth() / 2 - 1;
|
||||
int height = getHeight() / 2 - 1;
|
||||
|
||||
imageWidget = new TImage(this, getWidth() - width,
|
||||
getHeight() - height, width, height, image, 0, 0, null);
|
||||
|
||||
// Resize the image down until it can fit within the pane.
|
||||
while ((imageWidget.getRows() > height)
|
||||
|| (imageWidget.getColumns() > width)
|
||||
) {
|
||||
imageWidget.onKeypress(new TKeypressEvent(kbAltDown));
|
||||
}
|
||||
|
||||
imageWidget.setActive(false);
|
||||
activate(directoryList);
|
||||
}
|
||||
|
||||
}
|
|
@ -936,6 +936,7 @@ public class TApplication implements Runnable {
|
|||
if (desktop != null) {
|
||||
desktop.setDimensions(0, 0, resize.getWidth(),
|
||||
resize.getHeight() - 1);
|
||||
desktop.onResize(resize);
|
||||
}
|
||||
|
||||
// Change menu edges if needed.
|
||||
|
|
Loading…
Reference in a new issue