X-Git-Url: http://git.nikiroo.be/?p=jvcard.git;a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Fjvcard%2Ftui%2FMainWindow.java;h=a09751ef26a81dbbfa3f00c1e869fd9757ca05d9;hp=aa996353c9b2b8d292dfa92761f763c94bd98a09;hb=0b0b2b0ff1f5e21f7b0feb955b4b54855fb3d508;hpb=9c8baf0c360173b864683176c567757429c4fb12 diff --git a/src/be/nikiroo/jvcard/tui/MainWindow.java b/src/be/nikiroo/jvcard/tui/MainWindow.java index aa99635..a09751e 100644 --- a/src/be/nikiroo/jvcard/tui/MainWindow.java +++ b/src/be/nikiroo/jvcard/tui/MainWindow.java @@ -1,13 +1,11 @@ package be.nikiroo.jvcard.tui; -import java.io.File; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import be.nikiroo.jvcard.Card; import be.nikiroo.jvcard.Contact; -import be.nikiroo.jvcard.i18n.Trans; import be.nikiroo.jvcard.i18n.Trans.StringId; import be.nikiroo.jvcard.tui.KeyAction.Mode; import be.nikiroo.jvcard.tui.UiColors.Element; @@ -16,8 +14,10 @@ import be.nikiroo.jvcard.tui.panes.ContactList; import be.nikiroo.jvcard.tui.panes.MainContent; import com.googlecode.lanterna.TerminalSize; +import com.googlecode.lanterna.TextColor; import com.googlecode.lanterna.gui2.BasicWindow; import com.googlecode.lanterna.gui2.BorderLayout; +import com.googlecode.lanterna.gui2.ComponentRenderer; import com.googlecode.lanterna.gui2.Direction; import com.googlecode.lanterna.gui2.Interactable; import com.googlecode.lanterna.gui2.Label; @@ -30,8 +30,8 @@ import com.googlecode.lanterna.input.KeyStroke; import com.googlecode.lanterna.input.KeyType; /** - * This is the main "window" of the program. It will host one - * {@link MainContent} at any one time. + * This is the main "window" of the program. It can show up to one + * {@link MainContent} at any one time, but keeps a stack of contents. * * @author niki * @@ -39,10 +39,11 @@ import com.googlecode.lanterna.input.KeyType; public class MainWindow extends BasicWindow { private List defaultActions = new LinkedList(); private List actions = new LinkedList(); - private List content = new LinkedList(); + private List contentStack = new LinkedList(); private boolean actionsPadded; - private Boolean waitForOneKeyAnswer; // true, false, (null = do not wait for - // an answer) + private boolean waitForOneKeyAnswer; + private KeyStroke questionKey; // key that "asked" a question, and to replay + // later with an answer private String title; private Panel titlePanel; private Panel mainPanel; @@ -51,10 +52,19 @@ public class MainWindow extends BasicWindow { private Panel messagePanel; private TextBox text; + /** + * Create a new, empty window. + */ public MainWindow() { this(null); } + /** + * Create a new window hosting the given content. + * + * @param content + * the content to host + */ public MainWindow(MainContent content) { super(content == null ? "" : content.getTitle()); @@ -81,15 +91,14 @@ public class MainWindow extends BasicWindow { llayout.setSpacing(0); actionPanel.setLayoutManager(llayout); - llayout = new LinearLayout(Direction.VERTICAL); - llayout.setSpacing(0); - titlePanel.setLayoutManager(llayout); + BorderLayout blayout = new BorderLayout(); + titlePanel.setLayoutManager(blayout); llayout = new LinearLayout(Direction.VERTICAL); llayout.setSpacing(0); messagePanel.setLayoutManager(llayout); - BorderLayout blayout = new BorderLayout(); + blayout = new BorderLayout(); mainPanel.setLayoutManager(blayout); blayout = new BorderLayout(); @@ -113,6 +122,12 @@ public class MainWindow extends BasicWindow { setComponent(mainPanel); } + /** + * "push" some content to the window stack. + * + * @param content + * the new top-of-the-stack content + */ public void pushContent(MainContent content) { List actions = null; String title = null; @@ -122,7 +137,7 @@ public class MainWindow extends BasicWindow { title = content.getTitle(); actions = content.getKeyBindings(); contentPanel.addComponent(content, BorderLayout.Location.CENTER); - this.content.add(content); + this.contentStack.add(content); Interactable focus = content.nextFocus(null); if (focus != null) @@ -130,44 +145,30 @@ public class MainWindow extends BasicWindow { } setTitle(title); - setActions(actions, true, true); + setActions(actions, true); invalidate(); } + /** + * "pop" the latest, top-of-the-stack content from the window stack. + * + * @return the removed content if any + */ public MainContent popContent() { MainContent removed = null; MainContent prev = null; - if (content.size() > 0) - removed = content.remove(content.size() - 1); - if (content.size() > 0) - prev = content.remove(content.size() - 1); - pushContent(prev); - return removed; - } + MainContent content = getContent(); + if (content != null) + removed = contentStack.remove(contentStack.size() - 1); - /** - * Set the application title. - * - * @param title - * the new title or NULL for the default title - */ - public void setTitle(String title) { - if (title == null) { - title = Trans.StringId.TITLE.trans(); - } + if (contentStack.size() > 0) + prev = contentStack.remove(contentStack.size() - 1); - if (!title.equals(this.title)) { - super.setTitle(title); - this.title = title; - } - - Label lbl = new Label(title); - titlePanel.removeAllComponents(); + pushContent(prev); - titlePanel.addComponent(lbl, LinearLayout - .createLayoutData(LinearLayout.Alignment.Center)); + return removed; } /** @@ -189,36 +190,45 @@ public class MainWindow extends BasicWindow { } } - public void setQuestion(String mess, boolean oneKey) { + /** + * Show a question to the user and switch to "ask for answer" mode see + * {@link MainWindow#handleQuestion}. + * + * @param mess + * the message to display + * @param oneKey + * TRUE for a one-key answer, FALSE for a text answer validated + * by ENTER + */ + public void setQuestion(KeyStroke key, String mess, boolean oneKey) { + questionKey = key; + waitForOneKeyAnswer = oneKey; + messagePanel.removeAllComponents(); - if (mess != null) { - waitForOneKeyAnswer = oneKey; - Panel hpanel = new Panel(); - LinearLayout llayout = new LinearLayout(Direction.HORIZONTAL); - llayout.setSpacing(0); - hpanel.setLayoutManager(llayout); + Panel hpanel = new Panel(); + LinearLayout llayout = new LinearLayout(Direction.HORIZONTAL); + llayout.setSpacing(0); + hpanel.setLayoutManager(llayout); - Label lbl = UiColors.Element.LINE_MESSAGE_QUESTION.createLabel(" " - + mess + " "); - text = new TextBox(new TerminalSize(getSize().getColumns() - - lbl.getSize().getColumns(), 1)); + Label lbl = UiColors.Element.LINE_MESSAGE_QUESTION.createLabel(" " + + mess + " "); + text = new TextBox(new TerminalSize(getSize().getColumns() + - lbl.getSize().getColumns(), 1)); - hpanel.addComponent(lbl, LinearLayout - .createLayoutData(LinearLayout.Alignment.Beginning)); - hpanel.addComponent(text, LinearLayout - .createLayoutData(LinearLayout.Alignment.Fill)); + hpanel.addComponent(lbl, LinearLayout + .createLayoutData(LinearLayout.Alignment.Beginning)); + hpanel.addComponent(text, LinearLayout + .createLayoutData(LinearLayout.Alignment.Fill)); - messagePanel.addComponent(hpanel, LinearLayout - .createLayoutData(LinearLayout.Alignment.Beginning)); + messagePanel.addComponent(hpanel, LinearLayout + .createLayoutData(LinearLayout.Alignment.Beginning)); - text.takeFocus(); - } + text.takeFocus(); } @Override public void draw(TextGUIGraphics graphics) { - setTitle(title); if (!actionsPadded) { // fill with "desc" colour actionPanel.addComponent(UiColors.Element.ACTION_DESC @@ -229,9 +239,83 @@ public class MainWindow extends BasicWindow { super.draw(graphics); } - private void setActions(List actions, boolean allowKeys, - boolean enableDefaultactions) { + @Override + public void setTitle(String title) { + String prefix = " " + Main.APPLICATION_TITLE + " (version " + + Main.APPLICATION_VERSION + ")"; + + int count = -1; + MainContent content = getContent(); + if (content != null) + count = content.getCount(); + + if (title != null) { + prefix = prefix + ": "; + } + + if (getSize() != null) { + if (title != null) + title = StringUtils.padString(title, getSize().getColumns()); + else + // cause busy-loop freeze: + prefix = StringUtils.padString(prefix, getSize().getColumns()); + } + + if (!(title + count).equals(this.title)) { + this.title = title + count; + + super.setTitle(prefix + title); + + Label lblPrefix = new Label(prefix); + UiColors.Element.TITLE_MAIN.themeLabel(lblPrefix); + + Label lblTitle = null; + if (title != null) { + lblTitle = new Label(title); + UiColors.Element.TITLE_VARIABLE.themeLabel(lblTitle); + } + + Label lblCount = null; + if (count > -1) { + lblCount = new Label("[" + count + "]"); + UiColors.Element.TITLE_COUNT.themeLabel(lblCount); + } + + titlePanel.removeAllComponents(); + titlePanel.addComponent(lblPrefix, BorderLayout.Location.LEFT); + if (lblTitle != null) + titlePanel.addComponent(lblTitle, BorderLayout.Location.CENTER); + if (lblCount != null) + titlePanel.addComponent(lblCount, BorderLayout.Location.RIGHT); + + invalidate(); + } + } + + /** + * Return the current {@link MainContent} from the stack if any. + * + * @return the current {@link MainContent} + */ + private MainContent getContent() { + if (contentStack.size() > 0) { + return contentStack.get(contentStack.size() - 1); + } + + return null; + } + + /** + * Update the list of actions and refresh the action panel. + * + * @param actions + * the list of actions to support + * @param enableDefaultactions + * TRUE to enable the default actions + */ + private void setActions(List actions, + boolean enableDefaultactions) { this.actions.clear(); actionsPadded = false; @@ -285,6 +369,15 @@ public class MainWindow extends BasicWindow { } } + /** + * Handle user input when in "ask for question" mode (see + * {@link MainWindow#questionKey}). + * + * @param key + * the key that has been pressed by the user + * + * @return the user's answer if done + */ private String handleQuestion(KeyStroke key) { String answer = null; @@ -301,9 +394,9 @@ public class MainWindow extends BasicWindow { if (answer != null) { Interactable focus = null; - if (this.content.size() > 0) - // focus = content.get(0).getDefaultFocusElement(); - focus = content.get(0).nextFocus(null); + MainContent content = getContent(); + if (content != null) + focus = content.nextFocus(null); focus.takeFocus(); } @@ -311,83 +404,121 @@ public class MainWindow extends BasicWindow { return answer; } - @Override - public boolean handleInput(KeyStroke key) { + /** + * Handle the input in case of "normal" (not "ask for answer") mode. + * + * @param key + * the key that was pressed + * @param answer + * the answer given for this key + * + * @return if the window handled the inout + */ + private boolean handleInput(KeyStroke key, String answer) { boolean handled = false; - if (waitForOneKeyAnswer != null) { - String answer = handleQuestion(key); - if (answer != null) { - waitForOneKeyAnswer = null; - setMessage("ANS: " + answer, false); + setMessage(null, false); - handled = true; - } - } else { - setMessage(null, false); - - for (KeyAction action : actions) { - if (!action.match(key)) - continue; - - handled = true; - - if (action.onAction()) { - switch (action.getMode()) { - case MOVE: - int x = 0; - int y = 0; - - if (action.getKey().getKeyType() == KeyType.ArrowUp) - x = -1; - if (action.getKey().getKeyType() == KeyType.ArrowDown) - x = 1; - if (action.getKey().getKeyType() == KeyType.ArrowLeft) - y = -1; - if (action.getKey().getKeyType() == KeyType.ArrowRight) - y = 1; - - if (content.size() > 0) { - String err = content.get(content.size() - 1).move( - x, y); - if (err != null) - setMessage(err, true); - } + for (KeyAction action : actions) { + if (!action.match(key)) + continue; - break; - // mode with windows: - case CONTACT_LIST: - Card card = action.getCard(); - if (card != null) { - pushContent(new ContactList(card)); - } - break; - case CONTACT_DETAILS: - Contact contact = action.getContact(); - if (contact != null) { - pushContent(new ContactDetails(contact)); + MainContent content = getContent(); + handled = true; + + if (action.onAction()) { + switch (action.getMode()) { + case MOVE: + int x = 0; + int y = 0; + + if (action.getKey().getKeyType() == KeyType.ArrowUp) + x = -1; + if (action.getKey().getKeyType() == KeyType.ArrowDown) + x = 1; + if (action.getKey().getKeyType() == KeyType.ArrowLeft) + y = -1; + if (action.getKey().getKeyType() == KeyType.ArrowRight) + y = 1; + + if (content != null) { + String err = content.move(x, y); + if (err != null) + setMessage(err, true); + } + + break; + // mode with windows: + case CONTACT_LIST: + Card card = action.getCard(); + if (card != null) { + pushContent(new ContactList(card)); + } + break; + case CONTACT_DETAILS: + Contact contact = action.getContact(); + if (contact != null) { + pushContent(new ContactDetails(contact)); + } + break; + // mode interpreted by MainWindow: + case HELP: + // TODO + // setMessage("Help! I need somebody! Help!", false); + if (answer == null) { + setQuestion(key, "Test question?", false); + } else { + setMessage("You answered: " + answer, false); + } + + handled = true; + break; + case BACK: + if (content != null) { + String warning = content.getExitWarning(); + if (warning != null) { + if (answer == null) { + setQuestion(key, warning, true); + } else { + if (answer.equalsIgnoreCase("y")) { + popContent(); + } + } + } else { + popContent(); } - break; - // mode interpreted by MainWindow: - case HELP: - // TODO - // setMessage("Help! I need somebody! Help!", false); - setQuestion("Test question?", false); - handled = true; - break; - case BACK: - popContent(); - if (content.size() == 0) - close(); - break; - default: - case NONE: - break; } + + if (contentStack.size() == 0) + close(); + break; + default: + case NONE: + break; } + } - break; + break; + } + + return handled; + } + + @Override + public boolean handleInput(KeyStroke key) { + boolean handled = false; + + if (questionKey != null) { + String answer = handleQuestion(key); + if (answer != null) { + // TODO + key = questionKey; + questionKey = null; + + handled = handleInput(key, answer); } + } else { + handled = handleInput(key, null); } if (!handled)