X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2FTApplication.java;h=8b436ab9a99ec9219b9755ffce379c62d834e10f;hb=e8a11f986bfe2556e450d7b8ad6ef0059b369bbc;hp=d8bec5e5d07b00dd54faca3f0660fbfcf9e4884a;hpb=9245321388306b5b49d6385ce2f46ea6a82ab619;p=nikiroo-utils.git diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index d8bec5e..8b436ab 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -44,7 +44,6 @@ import java.util.Map; import jexer.bits.CellAttributes; import jexer.bits.ColorTheme; -import jexer.bits.GraphicsChars; import jexer.event.TCommandEvent; import jexer.event.TInputEvent; import jexer.event.TKeypressEvent; @@ -52,16 +51,19 @@ import jexer.event.TMenuEvent; import jexer.event.TMouseEvent; import jexer.event.TResizeEvent; import jexer.backend.Backend; +import jexer.backend.Screen; import jexer.backend.SwingBackend; import jexer.backend.ECMA48Backend; -import jexer.io.Screen; +import jexer.backend.TWindowBackend; import jexer.menu.TMenu; import jexer.menu.TMenuItem; import static jexer.TCommand.*; import static jexer.TKeypress.*; /** - * TApplication sets up a full Text User Interface application. + * TApplication is the main driver class for a full Text User Interface + * application. It manages windows, provides a menu bar and status bar, and + * processes events received from the user. */ public class TApplication implements Runnable { @@ -404,7 +406,15 @@ public class TApplication implements Runnable { * @return the Screen */ public final Screen getScreen() { - return backend.getScreen(); + if (backend instanceof TWindowBackend) { + // We are being rendered to a TWindow. We can't use its + // getScreen() method because that is how it is rendering to a + // hardware backend somewhere. Instead use its getOtherScreen() + // method. + return ((TWindowBackend) backend).getOtherScreen(); + } else { + return backend.getScreen(); + } } /** @@ -570,7 +580,7 @@ public class TApplication implements Runnable { } /** - * Get the list of windows. + * Get a (shallow) copy of the window list. * * @return a copy of the list of windows for this application */ @@ -580,6 +590,32 @@ public class TApplication implements Runnable { return result; } + /** + * If true, focus follows mouse: windows automatically raised if the + * mouse passes over them. + */ + private boolean focusFollowsMouse = false; + + /** + * Get focusFollowsMouse flag. + * + * @return true if focus follows mouse: windows automatically raised if + * the mouse passes over them + */ + public boolean getFocusFollowsMouse() { + return focusFollowsMouse; + } + + /** + * Set focusFollowsMouse flag. + * + * @param focusFollowsMouse if true, focus follows mouse: windows + * automatically raised if the mouse passes over them + */ + public void setFocusFollowsMouse(final boolean focusFollowsMouse) { + this.focusFollowsMouse = focusFollowsMouse; + } + // ------------------------------------------------------------------------ // General behavior ------------------------------------------------------- // ------------------------------------------------------------------------ @@ -610,6 +646,12 @@ public class TApplication implements Runnable { switch (backendType) { case SWING: + // The default SwingBackend is 80x25, 20 pt font. If you want to + // change that, you can pass the extra arguments to the + // SwingBackend constructor here. For example, if you wanted + // 90x30, 16 pt font: + // + // backend = new SwingBackend(this, 90, 30, 16); backend = new SwingBackend(this); break; case XTERM: @@ -686,6 +728,7 @@ public class TApplication implements Runnable { */ public TApplication(final Backend backend) { this.backend = backend; + backend.setListener(this); TApplicationImpl(); } @@ -877,6 +920,13 @@ public class TApplication implements Runnable { // Main loop -------------------------------------------------------------- // ------------------------------------------------------------------------ + /** + * Force this application to exit. + */ + public void exit() { + quit = true; + } + /** * Run this application until it exits. */ @@ -1021,19 +1071,6 @@ public class TApplication implements Runnable { return; } - // Peek at the mouse position - if (event instanceof TMouseEvent) { - TMouseEvent mouse = (TMouseEvent) event; - synchronized (getScreen()) { - if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) { - oldMouseX = mouseX; - oldMouseY = mouseY; - mouseX = mouse.getX(); - mouseY = mouse.getY(); - } - } - } - // Put into the main queue drainEventQueue.add(event); } @@ -1056,6 +1093,14 @@ public class TApplication implements Runnable { // Peek at the mouse position if (event instanceof TMouseEvent) { + TMouseEvent mouse = (TMouseEvent) event; + if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) { + oldMouseX = mouseX; + oldMouseY = mouseY; + mouseX = mouse.getX(); + mouseY = mouse.getY(); + } + // See if we need to switch focus to another window or the menu checkSwitchFocus((TMouseEvent) event); } @@ -1191,6 +1236,17 @@ public class TApplication implements Runnable { * @see #primaryHandleEvent(TInputEvent event) */ private void secondaryHandleEvent(final TInputEvent event) { + // Peek at the mouse position + if (event instanceof TMouseEvent) { + TMouseEvent mouse = (TMouseEvent) event; + if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) { + oldMouseX = mouseX; + oldMouseY = mouseY; + mouseX = mouse.getX(); + mouseY = mouse.getY(); + } + } + secondaryEventReceiver.handleEvent(event); } @@ -1278,9 +1334,9 @@ public class TApplication implements Runnable { } /** - * Return the number of windows that are visible. + * Return the number of windows that are showing. * - * @return the number of windows that are visible + * @return the number of windows that are showing on screen */ public final int shownWindowCount() { int n = 0; @@ -1292,6 +1348,21 @@ public class TApplication implements Runnable { return n; } + /** + * Return the number of windows that are hidden. + * + * @return the number of windows that are hidden + */ + public final int hiddenWindowCount() { + int n = 0; + for (TWindow w: windows) { + if (w.isHidden()) { + n++; + } + } + return n; + } + /** * Check if a window instance is in this application's window list. * @@ -1304,6 +1375,7 @@ public class TApplication implements Runnable { } for (TWindow w: windows) { if (w == window) { + assert (window.getApplication() == this); return true; } } @@ -1496,8 +1568,8 @@ public class TApplication implements Runnable { * otherwise switch to the previous window in the list */ public final void switchWindow(final boolean forward) { - // Only switch if there are multiple windows - if (windows.size() < 2) { + // Only switch if there are multiple visible windows + if (shownWindowCount() < 2) { return; } assert (activeWindow != null); @@ -1522,18 +1594,23 @@ public class TApplication implements Runnable { return; } - int nextWindowI; - if (forward) { - nextWindowI = (activeWindowI + 1) % windows.size(); - } else { - if (activeWindowI == 0) { - nextWindowI = windows.size() - 1; + int nextWindowI = activeWindowI; + for (;;) { + if (forward) { + nextWindowI++; + nextWindowI %= windows.size(); } else { - nextWindowI = activeWindowI - 1; + nextWindowI--; + if (nextWindowI < 0) { + nextWindowI = windows.size() - 1; + } } - } - activateWindow(windows.get(nextWindowI)); + if (windows.get(nextWindowI).isShown()) { + activateWindow(windows.get(nextWindowI)); + break; + } + } } // synchronized (windows) } @@ -1746,11 +1823,11 @@ public class TApplication implements Runnable { continue; } for (int x = w.getX(); x < w.getX() + w.getWidth(); x++) { - if (x == width) { + if (x >= width) { continue; } for (int y = w.getY(); y < w.getY() + w.getHeight(); y++) { - if (y == height) { + if (y >= height) { continue; } overlapMatrix[x][y]++; @@ -1793,11 +1870,11 @@ public class TApplication implements Runnable { long newOverlapN = 0; // Start by adding each new cell. for (int wx = x; wx < x + window.getWidth(); wx++) { - if (wx == width) { + if (wx >= width) { continue; } for (int wy = y; wy < y + window.getHeight(); wy++) { - if (wy == height) { + if (wy >= height) { continue; } newMatrix[wx][wy]++; @@ -1941,55 +2018,64 @@ public class TApplication implements Runnable { return; } - // Only switch if there are multiple windows - if (windows.size() < 2) { + // If a menu is still active, don't switch windows + if (activeMenu != null) { return; } - // Switch on the upclick - if (mouse.getType() != TMouseEvent.Type.MOUSE_UP) { + // Only switch if there are multiple windows + if (windows.size() < 2) { return; } - synchronized (windows) { - Collections.sort(windows); - if (windows.get(0).isModal()) { - // Modal windows don't switch - return; - } + if (((focusFollowsMouse == true) + && (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION)) + || (mouse.getType() == TMouseEvent.Type.MOUSE_UP) + ) { + synchronized (windows) { + Collections.sort(windows); + if (windows.get(0).isModal()) { + // Modal windows don't switch + return; + } - for (TWindow window: windows) { - assert (!window.isModal()); + for (TWindow window: windows) { + assert (!window.isModal()); - if (window.isHidden()) { - assert (!window.isActive()); - continue; - } + if (window.isHidden()) { + assert (!window.isActive()); + continue; + } - if (window.mouseWouldHit(mouse)) { - if (window == windows.get(0)) { - // Clicked on the same window, nothing to do - assert (window.isActive()); + if (window.mouseWouldHit(mouse)) { + if (window == windows.get(0)) { + // Clicked on the same window, nothing to do + assert (window.isActive()); + return; + } + + // We will be switching to another window + assert (windows.get(0).isActive()); + assert (windows.get(0) == activeWindow); + assert (!window.isActive()); + activeWindow.onUnfocus(); + activeWindow.setActive(false); + activeWindow.setZ(window.getZ()); + activeWindow = window; + window.setZ(0); + window.setActive(true); + window.onFocus(); return; } - - // We will be switching to another window - assert (windows.get(0).isActive()); - assert (windows.get(0) == activeWindow); - assert (!window.isActive()); - activeWindow.onUnfocus(); - activeWindow.setActive(false); - activeWindow.setZ(window.getZ()); - activeWindow = window; - window.setZ(0); - window.setActive(true); - window.onFocus(); - return; } } + + // Clicked on the background, nothing to do + return; } - // Clicked on the background, nothing to do + // Nothing to do: this isn't a mouse up, or focus isn't following + // mouse. return; } @@ -2007,6 +2093,53 @@ public class TApplication implements Runnable { } } + /** + * Get a (shallow) copy of the menu list. + * + * @return a copy of the menu list + */ + public final List getAllMenus() { + return new LinkedList(menus); + } + + /** + * Add a top-level menu to the list. + * + * @param menu the menu to add + * @throws IllegalArgumentException if the menu is already used in + * another TApplication + */ + public final void addMenu(final TMenu menu) { + if ((menu.getApplication() != null) + && (menu.getApplication() != this) + ) { + throw new IllegalArgumentException("Menu " + menu + " is already " + + "part of application " + menu.getApplication()); + } + closeMenu(); + menus.add(menu); + recomputeMenuX(); + } + + /** + * Remove a top-level menu from the list. + * + * @param menu the menu to remove + * @throws IllegalArgumentException if the menu is already used in + * another TApplication + */ + public final void removeMenu(final TMenu menu) { + if ((menu.getApplication() != null) + && (menu.getApplication() != this) + ) { + throw new IllegalArgumentException("Menu " + menu + " is already " + + "part of application " + menu.getApplication()); + } + closeMenu(); + menus.remove(menu); + recomputeMenuX(); + } + /** * Turn off a sub-menu. */ @@ -2588,7 +2721,6 @@ public class TApplication implements Runnable { /** * Convenience function to create a new window and make it active. * - * @param application TApplication that manages this window * @param title window title, will be centered along the top border * @param x column relative to parent * @param y row relative to parent