AWT minimally working

This commit is contained in:
Kevin Lamonte 2015-03-16 15:53:34 -04:00
parent 84614868e6
commit 30bd4abd2a
9 changed files with 764 additions and 81 deletions

View file

@ -1,24 +1,27 @@
Jexer - Java Text User Interface library
========================================
This library is currently in design, but when finished it is intended
to implement a text-based windowing system loosely reminiscient of
Borland's [Turbo Vision](http://en.wikipedia.org/wiki/Turbo_Vision)
library. For those wishing to use the actual C++ Turbo Vision
library, see [Sergio Sigala's updated
version](http://tvision.sourceforge.net/) that runs on many more
platforms.
WARNING: THIS IS ALPHA CODE!
This library is intended to implement a text-based windowing system
loosely reminiscient of Borland's [Turbo
Vision](http://en.wikipedia.org/wiki/Turbo_Vision) library. For those
wishing to use the actual C++ Turbo Vision library, see [Sergio
Sigala's updated version](http://tvision.sourceforge.net/) that runs
on many more platforms.
Two backends are available:
* A command-line ECMA-48 / ANSI X3.64 type terminal (tested on Linux +
xterm) via System.in and System.out. Input/output is handled
through terminal escape sequences generated by the library itself:
ncurses is not required or linked to. xterm mouse tracking using
UTF8 coordinates is supported. This is the default backend.
* System.in/out to a command-line ECMA-48 / ANSI X3.64 type terminal
(tested on Linux + xterm). I/O is handled through terminal escape
sequences generated by the library itself: ncurses is not required
or linked to. xterm mouse tracking using UTF8 coordinates is
supported. This is the default backend on non-Windows platforms.
* Java Swing/AWT UI. This backend can be selected by setting
jexer.AWT=true.
* Java AWT UI. This backend can be selected by setting
jexer.AWT=true. This is the default backend on Windows platforms.
AWT is VERY experimental, please consider filing bugs when you
encounter them.
A demo application showing the existing UI controls is available via
'java -jar jexer.jar' or 'java -Djexer.AWT=true -jar jexer.jar' .
@ -28,17 +31,25 @@ A demo application showing the existing UI controls is available via
License
-------
This library is licensed LGPL ("GNU Lesser General Public License")
This project is licensed LGPL ("GNU Lesser General Public License")
version 3 or greater. See the file LICENSE for the full license text,
which includes both the GPL v3 and the LGPL supplemental terms.
Acknowledgements
----------------
Jexer makes use of the Terminus TrueType font [made available
here](http://files.ax86.net/terminus-ttf/) .
Usage
-----
The library is currently under initial development, usage patterns are
still being worked on. Generally the goal will be to build
applications somewhat as follows:
Usage patterns are still being worked on, but in general the goal will
be to build applications somewhat as follows:
```Java
import jexer.*;
@ -48,11 +59,6 @@ public class MyApplication extends TApplication {
public MyApplication() {
super();
// Create an editor window that has support for
// copy/paste, search text, arrow keys, horizontal
// and vertical scrollbar, etc.
addEditor();
// Create standard menus for File and Window
addFileMenu();
addWindowMenu();
@ -65,22 +71,29 @@ public class MyApplication extends TApplication {
}
```
See the file demos/Demo1.java for example usage.
Roadmap
-------
Many tasks remain before calling this version 1.0:
0.0.1:
- AWTBackend
0.0.2:
- AWT:
- Blinking cursor
- More optimal refresh
- Jittery refresh with mouse movement
- Clean up TWidget constuctors (everyone is doing setX() / setY() / set...)
- ECMA48Backend running on socket
- TTreeView
- TDirectoryList
- TFileOpen
- Decide on naming convention: getText, getValue, getLabel: one or all
of them?
- TPasswordField (displays stars when not active)
0.0.3:
@ -93,16 +106,10 @@ Many tasks remain before calling this version 1.0:
- Bare ESC isn't being returned immediately
- TTimer is jittery with I/O
- TSubMenu keyboard mnemonic not working
- kbDel assertion failure in TMenu (MID_CLEAR)
- kbDel and use by TMenu (MID_CLEAR)
- TDirectoryList cannot be navigated only with keyboard
- TTreeView cannot be navigated only with keyboard
- RangeViolation after dragging scrollbar up/down
- TEditor
- Word wrap
- Forward/backward word
- Search
- Replace
- Cut/Copy/Paste
0.1.0:

View file

@ -312,12 +312,26 @@ public class TApplication {
public TApplication(final InputStream input,
final OutputStream output) throws UnsupportedEncodingException {
if (System.getProperty("jexer.AWT", "false").equals("true")) {
// AWT is the default backend on Windows unless explicitly overridden
// by jexer.AWT.
boolean useAWT = false;
if (System.getProperty("os.name").startsWith("Windows")) {
useAWT = true;
}
if (System.getProperty("jexer.AWT") != null) {
if (System.getProperty("jexer.AWT", "false").equals("true")) {
useAWT = true;
} else {
useAWT = false;
}
}
if (useAWT) {
backend = new AWTBackend();
} else {
backend = new ECMA48Backend(input, output);
}
theme = new ColorTheme();
desktopBottom = getScreen().getHeight() - 1;
fillEventQueue = new ArrayList<TInputEvent>();

View file

@ -56,6 +56,8 @@ public final class AWTBackend extends Backend {
this.screen = screen;
// Create the listeners
terminal = new AWTTerminal(screen);
// Hang onto the session info
this.sessionInfo = terminal.getSessionInfo();
}
/**

View file

@ -288,24 +288,6 @@ class DemoMainWindow extends TWindow {
// Timer label is updated with timer ticks.
TLabel timerLabel;
/*
// The modal window is a more low-level example of controlling a window
// "from the outside". Most windows will probably subclass TWindow and
// do this kind of logic on their own.
private TWindow modalWindow;
private void openModalWindow() {
modalWindow = getApplication().addWindow("Demo Modal Window", 0, 0,
58, 15, TWindow.Flag.MODAL);
modalWindow.addLabel("This is an example of a very braindead modal window.", 1, 1);
modalWindow.addLabel("Modal windows are centered by default.", 1, 2);
modalWindow.addButton("&Close", (modalWindow.width - 8)/2,
modalWindow.height - 4, &modalWindowClose);
}
private void modalWindowClose() {
getApplication().closeWindow(modalWindow);
}
*/
/**
* We need to override onClose so that the timer will no longer be called
* after we close the window. TTimers currently are completely unaware

View file

@ -32,6 +32,7 @@ package jexer.io;
import jexer.bits.Cell;
import jexer.bits.CellAttributes;
import jexer.session.AWTSessionInfo;
import java.awt.Color;
import java.awt.Cursor;
@ -40,7 +41,11 @@ import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.InputStream;
/**
@ -232,11 +237,12 @@ public final class AWTScreen extends Screen {
setTitle("Jexer Application");
setBackground(java.awt.Color.black);
setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
// setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
// setFont(new Font("Liberation Mono", Font.BOLD, 16));
// setFont(new Font(Font.MONOSPACED, Font.PLAIN, 16));
try {
// Always try to use Terminus, the one decent font.
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream in = loader.getResourceAsStream(FONTFILE);
Font terminusRoot = Font.createFont(Font.TRUETYPE_FONT, in);
@ -249,6 +255,16 @@ public final class AWTScreen extends Screen {
}
setVisible(true);
resizeToScreen();
// Kill the X11 cursor
// Transparent 16 x 16 pixel cursor image.
BufferedImage cursorImg = new BufferedImage(16, 16,
BufferedImage.TYPE_INT_ARGB);
// Create a new blank cursor.
Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
cursorImg, new Point(0, 0), "blank cursor");
setCursor(blankCursor);
}
/**
@ -281,6 +297,18 @@ public final class AWTScreen extends Screen {
*/
}
/**
* Update redraws the whole screen.
*
* @param gr the AWT Graphics context
*/
@Override
public void update(final Graphics gr) {
// The default update clears the area. Don't do that, instead
// just paint it directly.
paint(gr);
}
/**
* Paint redraws the whole screen.
*
@ -288,17 +316,31 @@ public final class AWTScreen extends Screen {
*/
@Override
public void paint(final Graphics gr) {
Rectangle bounds = gr.getClipBounds();
for (int y = 0; y < screen.height; y++) {
for (int x = 0; x < screen.width; x++) {
Cell lCell = screen.logical[x][y];
Cell pCell = screen.physical[x][y];
int xPixel = x * textWidth + left;
int yPixel = y * textHeight + top;
if (!lCell.equals(pCell)) {
Cell lCell = screen.logical[x][y];
Cell pCell = screen.physical[x][y];
boolean inBounds = true;
if (bounds != null) {
if (bounds.contains(xPixel, yPixel)
|| bounds.contains(xPixel + textWidth, yPixel)
|| bounds.contains(xPixel, yPixel + textHeight)
|| bounds.contains(xPixel + textWidth,
yPixel + textHeight)
) {
// This area is damaged and will definitely be
// redrawn.
inBounds = true;
}
}
if (!lCell.equals(pCell) || inBounds) {
// Draw the background rectangle, then the foreground
// character.
gr.setColor(attrToBackgroundColor(lCell));
@ -314,6 +356,18 @@ public final class AWTScreen extends Screen {
}
}
}
// Draw the cursor if it is visible
if ((cursorVisible)
&& (cursorY <= screen.height - 1)
&& (cursorX <= screen.width - 1)
) {
int xPixel = cursorX * textWidth + left;
int yPixel = cursorY * textHeight + top;
Cell lCell = screen.logical[cursorX][cursorY];
gr.setColor(attrToForegroundColor(lCell));
gr.fillRect(xPixel, yPixel + textHeight - 2, textWidth, 2);
}
}
}
@ -329,12 +383,82 @@ public final class AWTScreen extends Screen {
frame = new AWTFrame(this);
}
/**
* Create the AWTSessionInfo. Note package private access.
*
* @return the sessionInfo
*/
AWTSessionInfo getSessionInfo() {
AWTSessionInfo sessionInfo = new AWTSessionInfo(frame, frame.textWidth,
frame.textHeight);
return sessionInfo;
}
/**
* Push the logical screen to the physical device.
*/
@Override
public void flushPhysical() {
Graphics gr = frame.getGraphics();
frame.paint(gr);
// Request a repaint, let the frame's repaint/update methods do the
// right thing.
// Find the minimum-size damaged region.
int xMin = frame.getWidth();
int xMax = 0;
int yMin = frame.getHeight();
int yMax = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Cell lCell = logical[x][y];
Cell pCell = physical[x][y];
int xPixel = x * frame.textWidth + frame.left;
int yPixel = y * frame.textHeight + frame.top;
if (!lCell.equals(pCell)
|| ((x == cursorX) && (y == cursorY))
) {
if (xPixel < xMin) {
xMin = xPixel;
}
if (xPixel + frame.textWidth > xMax) {
xMax = xPixel + frame.textWidth;
}
if (yPixel < yMin) {
yMin = yPixel;
}
if (yPixel + frame.textHeight > yMax) {
yMax = yPixel + frame.textHeight;
}
}
}
}
// Ask for a repaint sometime in the next 10 millis.
frame.repaint(10, xMin, yMin, xMax - xMin, yMax - yMin);
}
/**
* Put the cursor at (x,y).
*
* @param visible if true, the cursor should be visible
* @param x column coordinate to put the cursor on
* @param y row coordinate to put the cursor on
*/
@Override
public void putCursor(final boolean visible, final int x, final int y) {
if ((cursorVisible)
&& (cursorY <= height - 1)
&& (cursorX <= width - 1)
) {
// Make the current cursor position dirty
if (physical[cursorX][cursorY].getChar() == ' ') {
physical[cursorX][cursorY].setChar('X');
} else {
physical[cursorX][cursorY].setChar(' ');
}
}
super.putCursor(visible, x, y);
}
}

View file

@ -30,27 +30,38 @@
*/
package jexer.io;
import java.awt.event.KeyListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.List;
import java.util.LinkedList;
import jexer.TKeypress;
import jexer.bits.Color;
import jexer.event.TCommandEvent;
import jexer.event.TInputEvent;
import jexer.event.TKeypressEvent;
import jexer.event.TMouseEvent;
import jexer.event.TResizeEvent;
import jexer.session.SessionInfo;
import jexer.session.TSessionInfo;
import jexer.session.AWTSessionInfo;
import static jexer.TCommand.*;
import static jexer.TKeypress.*;
/**
* This class reads keystrokes and mouse events from an AWT Frame.
*/
public final class AWTTerminal implements KeyListener {
public final class AWTTerminal implements ComponentListener, KeyListener,
MouseListener, MouseMotionListener,
MouseWheelListener, WindowListener {
/**
* The backend Screen.
@ -60,7 +71,7 @@ public final class AWTTerminal implements KeyListener {
/**
* The session information.
*/
private SessionInfo sessionInfo;
private AWTSessionInfo sessionInfo;
/**
* Getter for sessionInfo.
@ -76,11 +87,6 @@ public final class AWTTerminal implements KeyListener {
*/
private List<TInputEvent> eventQueue;
/**
* If true, we want the reader thread to exit gracefully.
*/
private boolean stopReaderThread;
/**
* The reader thread.
*/
@ -89,17 +95,17 @@ public final class AWTTerminal implements KeyListener {
/**
* true if mouse1 was down. Used to report mouse1 on the release event.
*/
private boolean mouse1;
private boolean mouse1 = false;
/**
* true if mouse2 was down. Used to report mouse2 on the release event.
*/
private boolean mouse2;
private boolean mouse2 = false;
/**
* true if mouse3 was down. Used to report mouse3 on the release event.
*/
private boolean mouse3;
private boolean mouse3 = false;
/**
* Check if there are events in the queue.
@ -122,11 +128,15 @@ public final class AWTTerminal implements KeyListener {
mouse1 = false;
mouse2 = false;
mouse3 = false;
stopReaderThread = false;
sessionInfo = new TSessionInfo();
sessionInfo = screen.getSessionInfo();
eventQueue = new LinkedList<TInputEvent>();
screen.frame.addKeyListener(this);
screen.frame.addWindowListener(this);
screen.frame.addComponentListener(this);
screen.frame.addMouseListener(this);
screen.frame.addMouseMotionListener(this);
screen.frame.addMouseWheelListener(this);
}
/**
@ -312,7 +322,7 @@ public final class AWTTerminal implements KeyListener {
alt, ctrl, shift);
break;
case KeyEvent.VK_DELETE:
keypress = new TKeypress(true, TKeypress.F1, ' ',
keypress = new TKeypress(true, TKeypress.DEL, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_RIGHT:
@ -368,6 +378,9 @@ public final class AWTTerminal implements KeyListener {
case 0x0D:
keypress = kbEnter;
break;
case 0x7F:
keypress = kbDel;
break;
default:
if (!alt && ctrl && !shift) {
ch = key.getKeyText(key.getKeyCode()).charAt(0);
@ -381,5 +394,357 @@ public final class AWTTerminal implements KeyListener {
synchronized (eventQueue) {
eventQueue.add(new TKeypressEvent(keypress));
}
// Wake up the backend
synchronized (this) {
this.notifyAll();
}
}
/**
* Pass window events into the event queue.
*
* @param event window event received
*/
@Override
public void windowActivated(final WindowEvent event) {
// Ignore
}
/**
* Pass window events into the event queue.
*
* @param event window event received
*/
@Override
public void windowClosed(final WindowEvent event) {
// Ignore
}
/**
* Pass window events into the event queue.
*
* @param event window event received
*/
@Override
public void windowClosing(final WindowEvent event) {
// Drop a cmAbort and walk away
synchronized (eventQueue) {
eventQueue.add(new TCommandEvent(cmAbort));
}
// Wake up the backend
synchronized (this) {
this.notifyAll();
}
}
/**
* Pass window events into the event queue.
*
* @param event window event received
*/
@Override
public void windowDeactivated(final WindowEvent event) {
// Ignore
}
/**
* Pass window events into the event queue.
*
* @param event window event received
*/
@Override
public void windowDeiconified(final WindowEvent event) {
// Ignore
}
/**
* Pass window events into the event queue.
*
* @param event window event received
*/
@Override
public void windowIconified(final WindowEvent event) {
// Ignore
}
/**
* Pass window events into the event queue.
*
* @param event window event received
*/
@Override
public void windowOpened(final WindowEvent event) {
// Ignore
}
/**
* Pass component events into the event queue.
*
* @param event component event received
*/
@Override
public void componentHidden(final ComponentEvent event) {
// Ignore
}
/**
* Pass component events into the event queue.
*
* @param event component event received
*/
@Override
public void componentShown(final ComponentEvent event) {
// Ignore
}
/**
* Pass component events into the event queue.
*
* @param event component event received
*/
@Override
public void componentMoved(final ComponentEvent event) {
// Ignore
}
/**
* Pass component events into the event queue.
*
* @param event component event received
*/
@Override
public void componentResized(final ComponentEvent event) {
// Drop a new TResizeEvent into the queue
sessionInfo.queryWindowSize();
synchronized (eventQueue) {
TResizeEvent windowResize = new TResizeEvent(TResizeEvent.Type.SCREEN,
sessionInfo.getWindowWidth(), sessionInfo.getWindowHeight());
eventQueue.add(windowResize);
}
// Wake up the backend
synchronized (this) {
this.notifyAll();
}
}
/**
* Pass mouse events into the event queue.
*
* @param mouse mouse event received
*/
@Override
public void mouseDragged(final MouseEvent mouse) {
int modifiers = mouse.getModifiersEx();
boolean eventMouse1 = false;
boolean eventMouse2 = false;
boolean eventMouse3 = false;
if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
eventMouse1 = true;
}
if ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) {
eventMouse2 = true;
}
if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
eventMouse3 = true;
}
mouse1 = eventMouse1;
mouse2 = eventMouse2;
mouse3 = eventMouse3;
int x = sessionInfo.textColumn(mouse.getX());
int y = sessionInfo.textRow(mouse.getY());
TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_MOTION,
x, y, x, y, mouse1, mouse2, mouse3, false, false);
synchronized (eventQueue) {
eventQueue.add(mouseEvent);
}
// Wake up the backend
synchronized (this) {
this.notifyAll();
}
}
/**
* Pass mouse events into the event queue.
*
* @param mouse mouse event received
*/
@Override
public void mouseMoved(final MouseEvent mouse) {
int x = sessionInfo.textColumn(mouse.getX());
int y = sessionInfo.textRow(mouse.getY());
TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_MOTION,
x, y, x, y, mouse1, mouse2, mouse3, false, false);
synchronized (eventQueue) {
eventQueue.add(mouseEvent);
}
// Wake up the backend
synchronized (this) {
this.notifyAll();
}
}
/**
* Pass mouse events into the event queue.
*
* @param mouse mouse event received
*/
@Override
public void mouseClicked(final MouseEvent mouse) {
// Ignore
}
/**
* Pass mouse events into the event queue.
*
* @param mouse mouse event received
*/
@Override
public void mouseEntered(final MouseEvent mouse) {
// Ignore
}
/**
* Pass mouse events into the event queue.
*
* @param mouse mouse event received
*/
@Override
public void mouseExited(final MouseEvent mouse) {
// Ignore
}
/**
* Pass mouse events into the event queue.
*
* @param mouse mouse event received
*/
@Override
public void mousePressed(final MouseEvent mouse) {
int modifiers = mouse.getModifiersEx();
boolean eventMouse1 = false;
boolean eventMouse2 = false;
boolean eventMouse3 = false;
if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
eventMouse1 = true;
}
if ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) {
eventMouse2 = true;
}
if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
eventMouse3 = true;
}
mouse1 = eventMouse1;
mouse2 = eventMouse2;
mouse3 = eventMouse3;
int x = sessionInfo.textColumn(mouse.getX());
int y = sessionInfo.textRow(mouse.getY());
TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_DOWN,
x, y, x, y, mouse1, mouse2, mouse3, false, false);
synchronized (eventQueue) {
eventQueue.add(mouseEvent);
}
// Wake up the backend
synchronized (this) {
this.notifyAll();
}
}
/**
* Pass mouse events into the event queue.
*
* @param mouse mouse event received
*/
@Override
public void mouseReleased(final MouseEvent mouse) {
int modifiers = mouse.getModifiersEx();
boolean eventMouse1 = false;
boolean eventMouse2 = false;
boolean eventMouse3 = false;
if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
eventMouse1 = true;
}
if ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) {
eventMouse2 = true;
}
if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
eventMouse3 = true;
}
if (mouse1) {
mouse1 = false;
eventMouse1 = true;
}
if (mouse2) {
mouse2 = false;
eventMouse2 = true;
}
if (mouse3) {
mouse3 = false;
eventMouse3 = true;
}
int x = sessionInfo.textColumn(mouse.getX());
int y = sessionInfo.textRow(mouse.getY());
TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_UP,
x, y, x, y, eventMouse1, eventMouse2, eventMouse3, false, false);
synchronized (eventQueue) {
eventQueue.add(mouseEvent);
}
// Wake up the backend
synchronized (this) {
this.notifyAll();
}
}
/**
* Pass mouse events into the event queue.
*
* @param mouse mouse event received
*/
@Override
public void mouseWheelMoved(final MouseWheelEvent mouse) {
int modifiers = mouse.getModifiersEx();
boolean eventMouse1 = false;
boolean eventMouse2 = false;
boolean eventMouse3 = false;
boolean mouseWheelUp = false;
boolean mouseWheelDown = false;
if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
eventMouse1 = true;
}
if ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) {
eventMouse2 = true;
}
if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
eventMouse3 = true;
}
mouse1 = eventMouse1;
mouse2 = eventMouse2;
mouse3 = eventMouse3;
int x = sessionInfo.textColumn(mouse.getX());
int y = sessionInfo.textRow(mouse.getY());
if (mouse.getWheelRotation() > 0) {
mouseWheelDown = true;
}
if (mouse.getWheelRotation() < 0) {
mouseWheelUp = true;
}
TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_DOWN,
x, y, x, y, mouse1, mouse2, mouse3, mouseWheelUp, mouseWheelDown);
synchronized (eventQueue) {
eventQueue.add(mouseEvent);
}
// Wake up the backend
synchronized (this) {
this.notifyAll();
}
}
}

View file

@ -216,7 +216,9 @@ public abstract class Screen {
*/
public final CellAttributes getAttrXY(final int x, final int y) {
CellAttributes attr = new CellAttributes();
attr.setTo(logical[x][y]);
if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
attr.setTo(logical[x][y]);
}
return attr;
}
@ -727,7 +729,7 @@ public abstract class Screen {
* Subclasses must provide an implementation to push the logical screen
* to the physical device.
*/
abstract public void flushPhysical();
public abstract void flushPhysical();
/**
* Put the cursor at (x,y).
@ -736,8 +738,7 @@ public abstract class Screen {
* @param x column coordinate to put the cursor on
* @param y row coordinate to put the cursor on
*/
public final void putCursor(final boolean visible,
final int x, final int y) {
public void putCursor(final boolean visible, final int x, final int y) {
cursorVisible = visible;
cursorX = x;

View file

@ -439,7 +439,8 @@ public final class TMenu extends TWindow {
break;
case MID_CLEAR:
label = "C&lear";
key = kbDel;
hasKey = false;
// key = kbDel;
break;
case MID_TILE:

View file

@ -0,0 +1,187 @@
/**
* Jexer - Java Text User Interface
*
* License: LGPLv3 or later
*
* This module is licensed under the GNU Lesser General Public License
* Version 3. Please see the file "COPYING" in this directory for more
* information about the GNU Lesser General Public License Version 3.
*
* Copyright (C) 2015 Kevin Lamonte
*
* This program 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
* 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/, or write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* @author Kevin Lamonte [kevin.lamonte@gmail.com]
* @version 1
*/
package jexer.session;
import java.awt.Frame;
import java.awt.Insets;
/**
* AWTSessionInfo provides a session implementation with a callback into an
* AWT Frame to support queryWindowSize(). The username is blank, language
* is "en_US", with a 80x24 text window.
*/
public final class AWTSessionInfo implements SessionInfo {
/**
* The AWT Frame.
*/
private Frame frame;
/**
* The width of a text cell in pixels.
*/
private int textWidth;
/**
* The height of a text cell in pixels.
*/
private int textHeight;
/**
* User name.
*/
private String username = "";
/**
* Language.
*/
private String language = "en_US";
/**
* Text window width.
*/
private int windowWidth = 80;
/**
* Text window height.
*/
private int windowHeight = 24;
/**
* Username getter.
*
* @return the username
*/
public String getUsername() {
return this.username;
}
/**
* Username setter.
*
* @param username the value
*/
public void setUsername(final String username) {
this.username = username;
}
/**
* Language getter.
*
* @return the language
*/
public String getLanguage() {
return this.language;
}
/**
* Language setter.
*
* @param language the value
*/
public void setLanguage(final String language) {
this.language = language;
}
/**
* Text window width getter.
*
* @return the window width
*/
public int getWindowWidth() {
return windowWidth;
}
/**
* Text window height getter.
*
* @return the window height
*/
public int getWindowHeight() {
return windowHeight;
}
/**
* Public constructor.
*
* @param frame the AWT Frame
* @param textWidth the width of a cell in pixels
* @param textHeight the height of a cell in pixels
*/
public AWTSessionInfo(final Frame frame, final int textWidth,
final int textHeight) {
this.frame = frame;
this.textWidth = textWidth;
this.textHeight = textHeight;
}
/**
* Re-query the text window size.
*/
public void queryWindowSize() {
Insets insets = frame.getInsets();
int height = frame.getHeight() - insets.top - insets.bottom;
int width = frame.getWidth() - insets.left - insets.right;
windowWidth = width / textWidth;
windowHeight = height / textHeight;
/*
System.err.printf("queryWindowSize(): frame %d %d window %d %d\n",
frame.getWidth(), frame.getHeight(),
windowWidth, windowHeight);
*/
}
/**
* Convert pixel column position to text cell column position.
*
* @param x pixel column position
* @return text cell column position
*/
public int textColumn(final int x) {
Insets insets = frame.getInsets();
return ((x - insets.left) / textWidth);
}
/**
* Convert pixel row position to text cell row position.
*
* @param y pixel row position
* @return text cell row position
*/
public int textRow(final int y) {
Insets insets = frame.getInsets();
return ((y - insets.top) / textHeight);
}
}