mirror of
https://gitlab.com/AutumnMeowMeow/jexer
synced 2024-09-19 11:50:19 -06:00
AWT minimally working
This commit is contained in:
parent
84614868e6
commit
30bd4abd2a
9 changed files with 764 additions and 81 deletions
75
README.md
75
README.md
|
@ -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:
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
187
src/jexer/session/AWTSessionInfo.java
Normal file
187
src/jexer/session/AWTSessionInfo.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue