mirror of
https://gitlab.com/AutumnMeowMeow/jexer
synced 2024-09-19 11:50:19 -06:00
Add versioned about box, set title
This commit is contained in:
parent
f5a270d545
commit
55d2b2c2b2
16 changed files with 334 additions and 40 deletions
33
README.md
33
README.md
|
@ -211,34 +211,5 @@ ambiguous. This section describes such issues.
|
|||
Roadmap
|
||||
-------
|
||||
|
||||
Many tasks remain before calling this version 1.0:
|
||||
|
||||
0.0.4
|
||||
|
||||
- TStatusBar
|
||||
- TEditor
|
||||
- TWindow
|
||||
- "Smart placement" for new windows
|
||||
|
||||
0.0.5: BUG HUNT
|
||||
|
||||
- Swing performance is better, triple-buffering appears to have helped.
|
||||
|
||||
0.1.0: BETA RELEASE
|
||||
|
||||
- TSpinner
|
||||
- TComboBox
|
||||
- TCalendar
|
||||
|
||||
Wishlist features (2.0):
|
||||
|
||||
- TTerminal
|
||||
- Handle resize events (pass to child process)
|
||||
- Screen
|
||||
- Allow complex characters in putCharXY() and detect them in putStringXY().
|
||||
- Drag and drop
|
||||
- TEditor
|
||||
- TField
|
||||
- TText
|
||||
- TTerminal
|
||||
- TComboBox
|
||||
Many tasks remain before calling this version 1.0. See docs/TODO.md
|
||||
for the complete list of tasks.
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
<!-- <property name="build.compiler" value="gcj"/> -->
|
||||
|
||||
<property name="version" value="0.0.4"/>
|
||||
<property name="src.dir" value="src"/>
|
||||
<property name="resources.dir" value="resources"/>
|
||||
<property name="build.dir" value="build"/>
|
||||
|
@ -62,6 +63,7 @@
|
|||
<fileset dir="${src.dir}"/>
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="jexer.demos.Demo1"/>
|
||||
<attribute name="Implementation-Version" value="${version}"/>
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
|
95
docs/TODO.md
Normal file
95
docs/TODO.md
Normal file
|
@ -0,0 +1,95 @@
|
|||
Jexer TODO List
|
||||
===============
|
||||
|
||||
|
||||
Roadmap
|
||||
-------
|
||||
|
||||
0.0.4
|
||||
|
||||
- TStatusBar
|
||||
- TMenu version
|
||||
- TWindow version
|
||||
- Click mouse
|
||||
|
||||
- TWindow
|
||||
- "Smart placement" for new windows
|
||||
|
||||
0.0.5
|
||||
|
||||
- TEditor
|
||||
|
||||
0.0.6
|
||||
|
||||
- TSpinner
|
||||
- TComboBox
|
||||
- TCalendar
|
||||
|
||||
0.0.7
|
||||
|
||||
- THelpWindow
|
||||
- TText + clickable links
|
||||
- Index
|
||||
|
||||
0.1.0: BETA RELEASE and BUG HUNT
|
||||
|
||||
- Verify vttest in multiple tterminals.
|
||||
|
||||
1.0.0
|
||||
|
||||
|
||||
|
||||
1.1.0 Wishlist
|
||||
--------------
|
||||
|
||||
- TTerminal
|
||||
- Handle resize events (pass to child process)
|
||||
|
||||
- Screen
|
||||
- Allow complex characters in putCharXY() and detect them in putStringXY().
|
||||
|
||||
- Drag and drop
|
||||
- TEditor
|
||||
- TField
|
||||
- TText
|
||||
- TTerminal
|
||||
- TComboBox
|
||||
|
||||
|
||||
|
||||
Regression Checklist
|
||||
--------------------
|
||||
|
||||
TTerminal
|
||||
No hang when closing, whether or not script is running
|
||||
No dead script children lying around
|
||||
vttest passing
|
||||
|
||||
|
||||
|
||||
Release Checklist √
|
||||
-------------------
|
||||
|
||||
Fix all marked TODOs in code
|
||||
|
||||
Eliminate DEBUG, System.err prints
|
||||
|
||||
Version in:
|
||||
|
||||
Update written by date to current year:
|
||||
All code headers
|
||||
VERSION
|
||||
|
||||
Tag github
|
||||
|
||||
Upload to SF
|
||||
|
||||
|
||||
|
||||
Brainstorm Wishlist
|
||||
-------------------
|
||||
|
||||
|
||||
|
||||
Bugs Noted In Other Programs
|
||||
----------------------------
|
26
docs/worklog.md
Normal file
26
docs/worklog.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
Jexer Work Log
|
||||
==============
|
||||
|
||||
March 17, 2017
|
||||
|
||||
Jexer is coming back to active development status. I had a lot of
|
||||
other projects ahead of it in the queue, mostly Qodem but also Jermit
|
||||
and of course lots of actual day job work keeping me too tired for
|
||||
afterhours stuff. But here we are now, and I want to get Jexer to its
|
||||
1.0.0 release before the end of 2018. After that it will be a
|
||||
critical bit of function for IWP and NIB, if I ever get those going.
|
||||
I need to re-organize the demo app a bit so that it fits within 80x25,
|
||||
and then get to TStatusBar.
|
||||
|
||||
A status bar will be an optional part of TWindow. If it exists, then
|
||||
it will be drawn last by TApplication and get events routed to it from
|
||||
TWindow's event handlers. This will have the nice effect that the
|
||||
status bar can change depending on which window is active, without any
|
||||
real extra work on TApplication's part.
|
||||
|
||||
Putting together a proper TODO now, with release and regression
|
||||
checklists. I think I will see if jexer is available at SourceForge,
|
||||
and if so grab it. Perhaps I can put together some good Turbo Vision
|
||||
resources too. At the very least direct people to the Borland-derived
|
||||
C++ releases and Free Vision.
|
||||
|
|
@ -370,6 +370,15 @@ public class TApplication implements Runnable {
|
|||
*/
|
||||
private Backend backend;
|
||||
|
||||
/**
|
||||
* Get the Backend.
|
||||
*
|
||||
* @return the Backend
|
||||
*/
|
||||
public final Backend getBackend() {
|
||||
return backend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Screen.
|
||||
*
|
||||
|
@ -1514,6 +1523,15 @@ public class TApplication implements Runnable {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the about dialog.
|
||||
*/
|
||||
protected void showAboutDialog() {
|
||||
messageBox("About", "Jexer Version " +
|
||||
this.getClass().getPackage().getImplementationVersion(),
|
||||
TMessageBox.Type.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that TApplication subclasses can override to handle menu
|
||||
* events.
|
||||
|
@ -1549,6 +1567,10 @@ public class TApplication implements Runnable {
|
|||
closeAllWindows();
|
||||
return true;
|
||||
}
|
||||
if (menu.getId() == TMenu.MID_ABOUT) {
|
||||
showAboutDialog();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1752,6 +1774,24 @@ public class TApplication implements Runnable {
|
|||
return windowMenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to add a default "Help" menu.
|
||||
*
|
||||
* @return the new menu
|
||||
*/
|
||||
public final TMenu addHelpMenu() {
|
||||
TMenu helpMenu = addMenu("&Help");
|
||||
helpMenu.addDefaultItem(TMenu.MID_HELP_CONTENTS);
|
||||
helpMenu.addDefaultItem(TMenu.MID_HELP_INDEX);
|
||||
helpMenu.addDefaultItem(TMenu.MID_HELP_SEARCH);
|
||||
helpMenu.addDefaultItem(TMenu.MID_HELP_PREVIOUS);
|
||||
helpMenu.addDefaultItem(TMenu.MID_HELP_HELP);
|
||||
helpMenu.addDefaultItem(TMenu.MID_HELP_ACTIVE_FILE);
|
||||
helpMenu.addSeparator();
|
||||
helpMenu.addDefaultItem(TMenu.MID_ABOUT);
|
||||
return helpMenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all open windows.
|
||||
*/
|
||||
|
|
|
@ -32,6 +32,7 @@ import java.io.InputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -158,17 +159,27 @@ public class TTerminalWindow extends TWindow {
|
|||
|
||||
// You cannot run a login shell in a bare Process interactively,
|
||||
// due to libc's behavior of buffering when stdin/stdout aren't a
|
||||
// tty. Use 'script' instead to run a shell in a pty.
|
||||
String [] cmdShell = {
|
||||
// tty. Use 'script' instead to run a shell in a pty. And
|
||||
// because BSD and GNU differ on the '-f' vs '-F' flags, we need
|
||||
// two different commands. Lovely.
|
||||
String [] cmdShellGNU = {
|
||||
"script", "-fqe", "/dev/null"
|
||||
};
|
||||
String [] cmdShellBSD = {
|
||||
"script", "-qe", "-F", "/dev/null"
|
||||
};
|
||||
// Spawn a shell and pass its I/O to the other constructor.
|
||||
|
||||
ProcessBuilder pb;
|
||||
if (System.getProperty("os.name").startsWith("Windows")) {
|
||||
pb = new ProcessBuilder(cmdShellWindows);
|
||||
} else if (System.getProperty("os.name").startsWith("Mac")) {
|
||||
pb = new ProcessBuilder(cmdShellBSD);
|
||||
} else if (System.getProperty("os.name").startsWith("Linux")) {
|
||||
pb = new ProcessBuilder(cmdShellGNU);
|
||||
} else {
|
||||
pb = new ProcessBuilder(cmdShell);
|
||||
// When all else fails, assume GNU.
|
||||
pb = new ProcessBuilder(cmdShellGNU);
|
||||
}
|
||||
Map<String, String> env = pb.environment();
|
||||
env.put("TERM", ECMA48.deviceTypeTerm(deviceType));
|
||||
|
@ -191,6 +202,39 @@ public class TTerminalWindow extends TWindow {
|
|||
addShortcutKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate the child of the 'script' process used on POSIX. This may
|
||||
* or may not work.
|
||||
*/
|
||||
private void terminateShellChildProcess() {
|
||||
int pid = -1;
|
||||
if (shell.getClass().getName().equals("java.lang.UNIXProcess")) {
|
||||
/* get the PID on unix/linux systems */
|
||||
try {
|
||||
Field field = shell.getClass().getDeclaredField("pid");
|
||||
field.setAccessible(true);
|
||||
pid = field.getInt(shell);
|
||||
} catch (Throwable e) {
|
||||
// SQUASH, this didn't work. Just bail out quietly.
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (pid != -1) {
|
||||
// shell.destroy() works successfully at killing this side of
|
||||
// 'script'. But we need to make sure the other side (child
|
||||
// process) is also killed.
|
||||
String [] cmdKillIt = {
|
||||
"pkill", "-P", Integer.toString(pid)
|
||||
};
|
||||
try {
|
||||
Runtime.getRuntime().exec(cmdKillIt);
|
||||
} catch (Throwable e) {
|
||||
// SQUASH, this didn't work. Just bail out quietly.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
|
@ -317,7 +361,7 @@ public class TTerminalWindow extends TWindow {
|
|||
@Override public void onClose() {
|
||||
emulator.close();
|
||||
if (shell != null) {
|
||||
// System.err.println("shell.destroy()");
|
||||
terminateShellChildProcess();
|
||||
shell.destroy();
|
||||
shell = null;
|
||||
}
|
||||
|
|
|
@ -89,4 +89,11 @@ public abstract class Backend {
|
|||
*/
|
||||
public abstract void shutdown();
|
||||
|
||||
/**
|
||||
* Subclasses must provide an implementation that sets the window title.
|
||||
*
|
||||
* @param title the new title
|
||||
*/
|
||||
public abstract void setTitle(final String title);
|
||||
|
||||
}
|
||||
|
|
|
@ -160,4 +160,14 @@ public final class ECMA48Backend extends Backend {
|
|||
terminal.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the window title.
|
||||
*
|
||||
* @param title the new title
|
||||
*/
|
||||
@Override
|
||||
public void setTitle(final String title) {
|
||||
((ECMA48Screen) screen).setTitle(title);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -91,4 +91,14 @@ public final class SwingBackend extends Backend {
|
|||
((SwingScreen) screen).shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the window title.
|
||||
*
|
||||
* @param title the new title
|
||||
*/
|
||||
@Override
|
||||
public void setTitle(final String title) {
|
||||
((SwingScreen) screen).setTitle(title);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -48,6 +48,9 @@ public class Demo1 {
|
|||
if (System.getProperty("os.name").startsWith("Windows")) {
|
||||
backendType = TApplication.BackendType.SWING;
|
||||
}
|
||||
if (System.getProperty("os.name").startsWith("Mac")) {
|
||||
backendType = TApplication.BackendType.SWING;
|
||||
}
|
||||
if (System.getProperty("jexer.Swing") != null) {
|
||||
if (System.getProperty("jexer.Swing", "false").equals("true")) {
|
||||
backendType = TApplication.BackendType.SWING;
|
||||
|
|
|
@ -74,6 +74,7 @@ public class DemoApplication extends TApplication {
|
|||
item = subMenu.addItem(2002, "&Normal (sub)");
|
||||
|
||||
addWindowMenu();
|
||||
addHelpMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,6 +94,8 @@ public class DemoApplication extends TApplication {
|
|||
final OutputStream output) throws UnsupportedEncodingException {
|
||||
super(input, output);
|
||||
addAllWidgets();
|
||||
|
||||
getBackend().setTitle("Jexer Demo Application");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,6 +114,8 @@ public class DemoApplication extends TApplication {
|
|||
final PrintWriter writer, final boolean setRawMode) {
|
||||
super(input, reader, writer, setRawMode);
|
||||
addAllWidgets();
|
||||
|
||||
getBackend().setTitle("Jexer Demo Application");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -183,5 +188,6 @@ public class DemoApplication extends TApplication {
|
|||
public DemoApplication(final BackendType backendType) throws Exception {
|
||||
super(backendType);
|
||||
addAllWidgets();
|
||||
getBackend().setTitle("Jexer Demo Application");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -282,4 +282,15 @@ public final class ECMA48Screen extends Screen {
|
|||
terminal.getOutput().write(result);
|
||||
terminal.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the window title.
|
||||
*
|
||||
* @param title the new title
|
||||
*/
|
||||
public void setTitle(final String title) {
|
||||
terminal.getOutput().write(terminal.setTitle(title));
|
||||
terminal.flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1228,6 +1228,17 @@ public final class ECMA48Terminal implements Runnable {
|
|||
return "\033[?1036l";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an xterm OSC sequence to change the window title. Note package
|
||||
* private access.
|
||||
*
|
||||
* @param title the new title
|
||||
* @return the string to emit to xterm
|
||||
*/
|
||||
String setTitle(final String title) {
|
||||
return "\033]2;" + title + "\007";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a SGR parameter sequence for a single color change. Note
|
||||
* package private access.
|
||||
|
|
|
@ -165,6 +165,11 @@ public final class SwingScreen extends Screen {
|
|||
*/
|
||||
SwingScreen screen;
|
||||
|
||||
/**
|
||||
* If true, we were successful getting Terminus.
|
||||
*/
|
||||
private boolean gotTerminus = false;
|
||||
|
||||
/**
|
||||
* Width of a character cell.
|
||||
*/
|
||||
|
@ -329,8 +334,9 @@ public final class SwingScreen extends Screen {
|
|||
getContextClassLoader();
|
||||
InputStream in = loader.getResourceAsStream(FONTFILE);
|
||||
Font terminusRoot = Font.createFont(Font.TRUETYPE_FONT, in);
|
||||
Font terminus = terminusRoot.deriveFont(Font.PLAIN, 22);
|
||||
Font terminus = terminusRoot.deriveFont(Font.PLAIN, 20);
|
||||
setFont(terminus);
|
||||
gotTerminus = true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// setFont(new Font("Liberation Mono", Font.PLAIN, 24));
|
||||
|
@ -375,15 +381,24 @@ public final class SwingScreen extends Screen {
|
|||
Rectangle2D bounds = fm.getMaxCharBounds(gr);
|
||||
int leading = fm.getLeading();
|
||||
textWidth = (int)Math.round(bounds.getWidth());
|
||||
textHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
|
||||
// This also produces the same number, but works better for ugly
|
||||
// textHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
|
||||
|
||||
// This produces the same number, but works better for ugly
|
||||
// monospace.
|
||||
textHeight = fm.getMaxAscent() + maxDescent - leading;
|
||||
|
||||
if (gotTerminus == true) {
|
||||
textHeight++;
|
||||
}
|
||||
|
||||
if (System.getProperty("os.name").startsWith("Windows")) {
|
||||
textAdjustY = -1;
|
||||
textAdjustX = 0;
|
||||
}
|
||||
if (System.getProperty("os.name").startsWith("Mac")) {
|
||||
textAdjustY = -1;
|
||||
textAdjustX = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -857,4 +872,13 @@ public final class SwingScreen extends Screen {
|
|||
return ((y - frame.top) / frame.textHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the window title.
|
||||
*
|
||||
* @param title the new title
|
||||
*/
|
||||
public void setTitle(final String title) {
|
||||
frame.setTitle(title);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -88,6 +88,15 @@ public final class TMenu extends TWindow {
|
|||
public static final int MID_WINDOW_PREVIOUS = 26;
|
||||
public static final int MID_WINDOW_CLOSE = 27;
|
||||
|
||||
// Help menu
|
||||
public static final int MID_HELP_CONTENTS = 40;
|
||||
public static final int MID_HELP_INDEX = 41;
|
||||
public static final int MID_HELP_SEARCH = 42;
|
||||
public static final int MID_HELP_PREVIOUS = 43;
|
||||
public static final int MID_HELP_HELP = 44;
|
||||
public static final int MID_HELP_ACTIVE_FILE = 45;
|
||||
public static final int MID_ABOUT = 46;
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
|
@ -439,6 +448,31 @@ public final class TMenu extends TWindow {
|
|||
key = kbCtrlW;
|
||||
break;
|
||||
|
||||
case MID_HELP_CONTENTS:
|
||||
label = "&Contents";
|
||||
break;
|
||||
case MID_HELP_INDEX:
|
||||
label = "&Index";
|
||||
key = kbShiftF1;
|
||||
break;
|
||||
case MID_HELP_SEARCH:
|
||||
label = "&Topic search";
|
||||
key = kbCtrlF1;
|
||||
break;
|
||||
case MID_HELP_PREVIOUS:
|
||||
label = "&Previous topic";
|
||||
key = kbAltF1;
|
||||
break;
|
||||
case MID_HELP_HELP:
|
||||
label = "&Help on help";
|
||||
break;
|
||||
case MID_HELP_ACTIVE_FILE:
|
||||
label = "Active &file...";
|
||||
break;
|
||||
case MID_ABOUT:
|
||||
label = "&About...";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid menu ID: " + id);
|
||||
}
|
||||
|
|
|
@ -66,12 +66,12 @@ public final class SwingSessionInfo implements SessionInfo {
|
|||
/**
|
||||
* Text window width.
|
||||
*/
|
||||
private int windowWidth = 132;
|
||||
private int windowWidth = 80;
|
||||
|
||||
/**
|
||||
* Text window height.
|
||||
*/
|
||||
private int windowHeight = 40;
|
||||
private int windowHeight = 25;
|
||||
|
||||
/**
|
||||
* Username getter.
|
||||
|
|
Loading…
Reference in a new issue