/*
* 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-2020 Martin Berglund
*/
package com.googlecode.lanterna.gui2;
import com.googlecode.lanterna.TerminalPosition;
import com.googlecode.lanterna.input.KeyStroke;
Default implementation of Interactable that extends from AbstractComponent. If you want to write your own component that is interactable, i.e. can receive keyboard (and mouse) input, you probably want to extend from this class as it contains some common implementations of the methods from Interactable
interface Author: Martin Type parameters: - <T> – Should always be itself, see
AbstractComponent
/**
* Default implementation of Interactable that extends from AbstractComponent. If you want to write your own component
* that is interactable, i.e. can receive keyboard (and mouse) input, you probably want to extend from this class as
* it contains some common implementations of the methods from {@code Interactable} interface
* @param <T> Should always be itself, see {@code AbstractComponent}
* @author Martin
*/
public abstract class AbstractInteractableComponent<T extends AbstractInteractableComponent<T>> extends AbstractComponent<T> implements Interactable {
private InputFilter inputFilter;
private boolean inFocus;
private boolean enabled;
Default constructor
/**
* Default constructor
*/
protected AbstractInteractableComponent() {
inputFilter = null;
inFocus = false;
enabled = true;
}
@Override
public T takeFocus() {
if(!isEnabled()) {
return self();
}
BasePane basePane = getBasePane();
if(basePane != null) {
basePane.setFocusedInteractable(this);
}
return self();
}
{@inheritDoc}
This method is final in AbstractInteractableComponent
, please override afterEnterFocus
instead
/**
* {@inheritDoc}
* <p>
* This method is final in {@code AbstractInteractableComponent}, please override {@code afterEnterFocus} instead
*/
@Override
public final void onEnterFocus(FocusChangeDirection direction, Interactable previouslyInFocus) {
inFocus = true;
afterEnterFocus(direction, previouslyInFocus);
}
Called by AbstractInteractableComponent
automatically after this component has received input focus. You can override this method if you need to trigger some action based on this. Params: - direction – How focus was transferred, keep in mind this is from the previous component's point of view so
if this parameter has value DOWN, focus came in from above
- previouslyInFocus – Which interactable component had focus previously
/**
* Called by {@code AbstractInteractableComponent} automatically after this component has received input focus. You
* can override this method if you need to trigger some action based on this.
* @param direction How focus was transferred, keep in mind this is from the previous component's point of view so
* if this parameter has value DOWN, focus came in from above
* @param previouslyInFocus Which interactable component had focus previously
*/
@SuppressWarnings("EmptyMethod")
protected void afterEnterFocus(FocusChangeDirection direction, Interactable previouslyInFocus) {
//By default no action
}
{@inheritDoc}
This method is final in AbstractInteractableComponent
, please override afterLeaveFocus
instead
/**
* {@inheritDoc}
* <p>
* This method is final in {@code AbstractInteractableComponent}, please override {@code afterLeaveFocus} instead
*/
@Override
public final void onLeaveFocus(FocusChangeDirection direction, Interactable nextInFocus) {
inFocus = false;
afterLeaveFocus(direction, nextInFocus);
}
Called by AbstractInteractableComponent
automatically after this component has lost input focus. You can override this method if you need to trigger some action based on this. Params: - direction – How focus was transferred, keep in mind this is from the this component's point of view so
if this parameter has value DOWN, focus is moving down to a component below
- nextInFocus – Which interactable component is going to receive focus
/**
* Called by {@code AbstractInteractableComponent} automatically after this component has lost input focus. You
* can override this method if you need to trigger some action based on this.
* @param direction How focus was transferred, keep in mind this is from the this component's point of view so
* if this parameter has value DOWN, focus is moving down to a component below
* @param nextInFocus Which interactable component is going to receive focus
*/
@SuppressWarnings("EmptyMethod")
protected void afterLeaveFocus(FocusChangeDirection direction, Interactable nextInFocus) {
//By default no action
}
@Override
protected abstract InteractableRenderer<T> createDefaultRenderer();
@Override
public InteractableRenderer<T> getRenderer() {
return (InteractableRenderer<T>)super.getRenderer();
}
@Override
public boolean isFocused() {
return inFocus;
}
@Override
public synchronized T setEnabled(boolean enabled) {
this.enabled = enabled;
if(!enabled && isFocused()) {
BasePane basePane = getBasePane();
if(basePane != null) {
basePane.setFocusedInteractable(null);
}
}
return self();
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public boolean isFocusable() {
return true;
}
@Override
public final synchronized Result handleInput(KeyStroke keyStroke) {
if(inputFilter == null || inputFilter.onInput(this, keyStroke)) {
return handleKeyStroke(keyStroke);
}
else {
return Result.UNHANDLED;
}
}
This method can be overridden to handle various user input (mostly from the keyboard) when this component is in focus. The input method from the interface, handleInput(..)
is final in AbstractInteractableComponent
to ensure the input filter is properly handled. If the filter decides that this event should be processed, it will call this method. Params: - keyStroke – What input was entered by the user
Returns: Result of processing the key-stroke
/**
* This method can be overridden to handle various user input (mostly from the keyboard) when this component is in
* focus. The input method from the interface, {@code handleInput(..)} is final in
* {@code AbstractInteractableComponent} to ensure the input filter is properly handled. If the filter decides that
* this event should be processed, it will call this method.
* @param keyStroke What input was entered by the user
* @return Result of processing the key-stroke
*/
protected Result handleKeyStroke(KeyStroke keyStroke) {
// Skip the keystroke if ctrl, alt or shift was down
if(!keyStroke.isAltDown() && !keyStroke.isCtrlDown() && !keyStroke.isShiftDown()) {
switch(keyStroke.getKeyType()) {
case ArrowDown:
return Result.MOVE_FOCUS_DOWN;
case ArrowLeft:
return Result.MOVE_FOCUS_LEFT;
case ArrowRight:
return Result.MOVE_FOCUS_RIGHT;
case ArrowUp:
return Result.MOVE_FOCUS_UP;
case Tab:
return Result.MOVE_FOCUS_NEXT;
case ReverseTab:
return Result.MOVE_FOCUS_PREVIOUS;
case MouseEvent:
getBasePane().setFocusedInteractable(this);
return Result.HANDLED;
default:
}
}
return Result.UNHANDLED;
}
@Override
public TerminalPosition getCursorLocation() {
return getRenderer().getCursorLocation(self());
}
@Override
public InputFilter getInputFilter() {
return inputFilter;
}
@Override
public synchronized T setInputFilter(InputFilter inputFilter) {
this.inputFilter = inputFilter;
return self();
}
}