/* * This file is part of lanterna (http://code.google.com/p/lanterna/). * * lanterna 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 Lesser 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/>. * * Copyright (C) 2010-2017 Martin Berglund */ package com.googlecode.lanterna.gui2; import com.googlecode.lanterna.TerminalPosition; import com.googlecode.lanterna.input.KeyStroke; import com.googlecode.lanterna.TerminalSize; import com.googlecode.lanterna.input.KeyType; import java.util.*; /** * Abstract Window has most of the code requiring for a window to function, all concrete window implementations extends * from this in one way or another. You can define your own window by extending from this, as an alternative to building * up the GUI externally by constructing a {@code BasicWindow} and adding components to it. * @author Martin */ public abstract class AbstractWindow extends AbstractBasePane<Window> implements Window { private String title; private WindowBasedTextGUI textGUI; private boolean visible; private TerminalSize lastKnownSize; private TerminalSize lastKnownDecoratedSize; private TerminalPosition lastKnownPosition; private TerminalPosition contentOffset; private Set<Hint> hints; private WindowPostRenderer windowPostRenderer; private boolean closeWindowWithEscape; /** * Default constructor, this creates a window with no title */ public AbstractWindow() { this(""); } /** * Creates a window with a specific title that will (probably) be drawn in the window decorations * @param title Title of this window */ public AbstractWindow(String title) { super(); this.title = title; this.textGUI = null; this.visible = true; this.lastKnownPosition = null; this.lastKnownSize = null; this.lastKnownDecoratedSize = null; this.closeWindowWithEscape = false; this.hints = new HashSet<Hint>(); } /** * Setting this property to {@code true} will cause pressing the ESC key to close the window. This used to be the * default behaviour of lanterna 3 during the development cycle but is not longer the case. You are encouraged to * put proper buttons or other kind of components to clearly mark to the user how to close the window instead of * magically taking ESC, but sometimes it can be useful (when doing testing, for example) to enable this mode. * @param closeWindowWithEscape If {@code true}, this window will self-close if you press ESC key */ public void setCloseWindowWithEscape(boolean closeWindowWithEscape) { this.closeWindowWithEscape = closeWindowWithEscape; } @Override public void setTextGUI(WindowBasedTextGUI textGUI) { //This is kind of stupid check, but might cause it to blow up on people using the library incorrectly instead of //just causing weird behaviour if(this.textGUI != null && textGUI != null) { throw new UnsupportedOperationException("Are you calling setTextGUI yourself? Please read the documentation" + " in that case (this could also be a bug in Lanterna, please report it if you are sure you are " + "not calling Window.setTextGUI(..) from your code)"); } this.textGUI = textGUI; } @Override public WindowBasedTextGUI getTextGUI() { return textGUI; } /** * Alters the title of the window to the supplied string * @param title New title of the window */ public void setTitle(String title) { this.title = title; invalidate(); } @Override public String getTitle() { return title; } @Override public boolean isVisible() { return visible; } @Override public void setVisible(boolean visible) { this.visible = visible; } @Override public void draw(TextGUIGraphics graphics) { if(!graphics.getSize().equals(lastKnownSize)) { getComponent().invalidate(); } setSize(graphics.getSize(), false); super.draw(graphics); } @Override public boolean handleInput(KeyStroke key) { boolean handled = super.handleInput(key); if(!handled && closeWindowWithEscape && key.getKeyType() == KeyType.Escape) { close(); return true; } return handled; } @Override public TerminalPosition toGlobal(TerminalPosition localPosition) { if(localPosition == null) { return null; } return lastKnownPosition.withRelative(contentOffset.withRelative(localPosition)); } @Override public TerminalPosition fromGlobal(TerminalPosition globalPosition) { if(globalPosition == null) { return null; } return globalPosition.withRelative( -lastKnownPosition.getColumn() - contentOffset.getColumn(), -lastKnownPosition.getRow() - contentOffset.getRow()); } @Override public TerminalSize getPreferredSize() { return contentHolder.getPreferredSize(); } @Override public void setHints(Collection<Hint> hints) { this.hints = new HashSet<Hint>(hints); invalidate(); } @Override public Set<Hint> getHints() { return Collections.unmodifiableSet(hints); } @Override public WindowPostRenderer getPostRenderer() { return windowPostRenderer; } @Override public void addWindowListener(WindowListener windowListener) { addBasePaneListener(windowListener); } @Override public void removeWindowListener(WindowListener windowListener) { removeBasePaneListener(windowListener); } /** * Sets the post-renderer to use for this window. This will override the default from the GUI system (if there is * one set, otherwise from the theme). * @param windowPostRenderer Window post-renderer to assign to this window */ public void setWindowPostRenderer(WindowPostRenderer windowPostRenderer) { this.windowPostRenderer = windowPostRenderer; } @Override public final TerminalPosition getPosition() { return lastKnownPosition; } @Override public final void setPosition(TerminalPosition topLeft) { TerminalPosition oldPosition = this.lastKnownPosition; this.lastKnownPosition = topLeft; // Fire listeners for(BasePaneListener<?> listener: getBasePaneListeners()) { if(listener instanceof WindowListener) { ((WindowListener)listener).onMoved(this, oldPosition, topLeft); } } } @Override public final TerminalSize getSize() { return lastKnownSize; } @Override public void setSize(TerminalSize size) { setSize(size, true); } private void setSize(TerminalSize size, boolean invalidate) { TerminalSize oldSize = this.lastKnownSize; this.lastKnownSize = size; if(invalidate) { invalidate(); } // Fire listeners for(BasePaneListener<?> listener: getBasePaneListeners()) { if(listener instanceof WindowListener) { ((WindowListener)listener).onResized(this, oldSize, size); } } } @Override public final TerminalSize getDecoratedSize() { return lastKnownDecoratedSize; } @Override public final void setDecoratedSize(TerminalSize decoratedSize) { this.lastKnownDecoratedSize = decoratedSize; } @Override public void setContentOffset(TerminalPosition offset) { this.contentOffset = offset; } @Override public void close() { if(textGUI != null) { textGUI.removeWindow(this); } setComponent(null); } @Override public void waitUntilClosed() { WindowBasedTextGUI textGUI = getTextGUI(); if(textGUI != null) { textGUI.waitForWindowToClose(this); } } Window self() { return this; } }