class AbstractInteractableComponent<T extends AbstractInteractableComponent<T>> extends AbstractComponent<T> implements Interactable {

    private InputFilter inputFilter;
    private boolean inFocus;
    private boolean enabled;

    /**
     * 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}
     * <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 {@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}
     * <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 {@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, {@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 ARROW_DOWN:
                    return Result.MOVE_FOCUS_DOWN;
                case ARROW_LEFT:
                    return Result.MOVE_FOCUS_LEFT;
                case ARROW_RIGHT:
                    return Result.MOVE_FOCUS_RIGHT;
                case ARROW_UP:
                    return Result.MOVE_FOCUS_UP;
                case TAB:
                    return Result.MOVE_FOCUS_NEXT;
                case REVERSE_TAB:
                    return Result.MOVE_FOCUS_PREVIOUS;
                case MOUSE_EVENT:
                    if (isMouseMove(keyStroke)) {
                        // do nothing
                        return Result.UNHANDLED;
                    }
                    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();
    }
    
    public boolean isKeyboardActivationStroke(KeyStroke keyStroke) {
        boolean isKeyboardActivation = (keyStroke.getKeyType() == KeyType.CHARACTER && keyStroke.getCharacter() == ' ') || keyStroke.getKeyType() == KeyType.ENTER;
        
        return isFocused() && isKeyboardActivation;
    }
    
    public boolean isMouseActivationStroke(KeyStroke keyStroke) {<FILL_FUNCTION_BODY>}
    
    public boolean isActivationStroke(KeyStroke keyStroke) {
        boolean isKeyboardActivationStroke = isKeyboardActivationStroke(keyStroke);
        boolean isMouseActivationStroke = isMouseActivationStroke(keyStroke);
        
        return isKeyboardActivationStroke || isMouseActivationStroke;
    }
    
    public boolean isMouseDown(KeyStroke keyStroke) {
        return keyStroke.getKeyType() == KeyType.MOUSE_EVENT && ((MouseAction)keyStroke).isMouseDown();
    }
    
    public boolean isMouseDrag(KeyStroke keyStroke) {
        return keyStroke.getKeyType() == KeyType.MOUSE_EVENT && ((MouseAction)keyStroke).isMouseDrag();
    }
    
    public boolean isMouseMove(KeyStroke keyStroke) {
        return keyStroke.getKeyType() == KeyType.MOUSE_EVENT && ((MouseAction)keyStroke).isMouseMove();
    }
    
    public boolean isMouseUp(KeyStroke keyStroke) {
        return keyStroke.getKeyType() == KeyType.MOUSE_EVENT && ((MouseAction)keyStroke).isMouseUp();
    }
    
	
}

        boolean isMouseActivation = false;
        if (keyStroke instanceof MouseAction) {
            MouseAction action = (MouseAction)keyStroke;
            isMouseActivation = action.getActionType() == MouseActionType.CLICK_DOWN;
        }
        
        return isMouseActivation;
    
<methods>public void <init>() ,public synchronized T addTo(com.googlecode.lanterna.gui2.Panel) ,public final synchronized void draw(com.googlecode.lanterna.gui2.TextGUIGraphics) ,public com.googlecode.lanterna.gui2.BasePane getBasePane() ,public com.googlecode.lanterna.TerminalPosition getGlobalPosition() ,public com.googlecode.lanterna.gui2.LayoutData getLayoutData() ,public com.googlecode.lanterna.gui2.Container getParent() ,public com.googlecode.lanterna.TerminalPosition getPosition() ,public final com.googlecode.lanterna.TerminalSize getPreferredSize() ,public synchronized ComponentRenderer<T> getRenderer() ,public com.googlecode.lanterna.TerminalSize getSize() ,public com.googlecode.lanterna.gui2.TextGUI getTextGUI() ,public synchronized com.googlecode.lanterna.graphics.Theme getTheme() ,public com.googlecode.lanterna.graphics.ThemeDefinition getThemeDefinition() ,public boolean hasParent(com.googlecode.lanterna.gui2.Container) ,public void invalidate() ,public boolean isInside(com.googlecode.lanterna.gui2.Container) ,public boolean isInvalid() ,public boolean isVisible() ,public synchronized void onAdded(com.googlecode.lanterna.gui2.Container) ,public synchronized void onRemoved(com.googlecode.lanterna.gui2.Container) ,public synchronized T setLayoutData(com.googlecode.lanterna.gui2.LayoutData) ,public synchronized T setPosition(com.googlecode.lanterna.TerminalPosition) ,public final synchronized T setPreferredSize(com.googlecode.lanterna.TerminalSize) ,public T setRenderer(ComponentRenderer<T>) ,public synchronized T setSize(com.googlecode.lanterna.TerminalSize) ,public synchronized com.googlecode.lanterna.gui2.Component setTheme(com.googlecode.lanterna.graphics.Theme) ,public T setVisible(boolean) ,public com.googlecode.lanterna.TerminalPosition toBasePane(com.googlecode.lanterna.TerminalPosition) ,public com.googlecode.lanterna.TerminalPosition toGlobal(com.googlecode.lanterna.TerminalPosition) ,public synchronized com.googlecode.lanterna.gui2.Border withBorder(com.googlecode.lanterna.gui2.Border) <variables>private ComponentRenderer<T> defaultRenderer,private com.googlecode.lanterna.TerminalSize explicitPreferredSize,private boolean invalid,private com.googlecode.lanterna.gui2.LayoutData layoutData,private ComponentRenderer<T> overrideRenderer,private com.googlecode.lanterna.gui2.Container parent,private com.googlecode.lanterna.TerminalPosition position,private com.googlecode.lanterna.TerminalSize size,private com.googlecode.lanterna.graphics.Theme themeOverride,private ComponentRenderer<T> themeRenderer,private com.googlecode.lanterna.graphics.Theme themeRenderersTheme,private boolean visible