All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tentackle.fx.component.config.PrefixSelectionFeature Maven / Gradle / Ivy

/*
 * Tentackle - https://tentackle.org
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.tentackle.fx.component.config;

import javafx.event.EventHandler;
import javafx.scene.control.Control;
import javafx.scene.input.KeyEvent;
import javafx.util.StringConverter;

import org.tentackle.fx.FxUtilities;

import java.util.Collection;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * Select items according to a prefix.
* Useful for ComboBox and ChoiceBox but can be applied to any control providing a list of items * that can be selected. *

* The implementation can easily be extended (for ex. camelCase navigation) because access * to members and methods is protected. * * @author harald * @param the control type */ @SuppressWarnings("rawtypes") public class PrefixSelectionFeature { /** * To disable this feature, invoke:
*

   *  control.getProperties().remove(PrefixSelectionFeature.ENABLED);
   * 
*/ public static final String ENABLED = "prefixSelectionEnabled"; protected final T control; protected final BooleanSupplier condition; protected final Supplier itemProvider; protected final StringConverter itemConverter; protected final Consumer selector; protected final StringBuilder prefixBuf; // collected prefix string private long lastPressMillis; // epochal time of last keystroke private int index; // where to start the search /** * Creates a prefix selection feature. * * @param control the control to add this feature to * @param condition the condition to activate this feature * @param itemProvider the items that can be selected * @param itemConverter the item to string converter * @param selector the selector to select an item by its index */ public PrefixSelectionFeature(T control, BooleanSupplier condition, Supplier itemProvider, StringConverter itemConverter, Consumer selector) { this.control = control; this.condition = condition; this.itemProvider = itemProvider; this.itemConverter = itemConverter; this.selector = selector; prefixBuf = new StringBuilder(); } /** * Configures the control to support this feature. */ public void configure() { control.addEventHandler(KeyEvent.KEY_TYPED, createHandler()); } /** * Creates the handler to catch the key events. * * @return the handler */ protected EventHandler createHandler() { return event -> { if (isEnabled()) { String chr = event.getCharacter(); if (chr != null) { // for sure... can it be null at all? long now = System.currentTimeMillis(); if (now > lastPressMillis + FxUtilities.getInstance().getPrefixSelectionTimeout()) { // clear after timeout prefixBuf.setLength(0); index = 0; } lastPressMillis = now; prefixBuf.append(chr); select(); } } }; } /** * Returns whether this feature is enabled for the control. * * @return true if enabled */ protected boolean isEnabled() { return condition.getAsBoolean() && control.getProperties().containsKey(ENABLED); } /** * Select an item according to the current prefix. */ protected void select() { String prefix = getPrefix(); int i = 0; for (Object item: itemProvider.get()) { if (i >= index && isItemMatching(prefix, item)) { index = i; selector.accept(index); break; } i++; } } /** * Gets the prefix string to be used for {@link #isItemMatching}. * * @return the prefix string */ protected String getPrefix() { return prefixBuf.toString().toUpperCase(); } /** * Returns whether item is matching prefix string. * * @param prefix the prefix string * @param item the item * @return true if matching */ protected boolean isItemMatching(String prefix, Object item) { @SuppressWarnings("unchecked") String itemText = itemConverter == null ? item.toString() : itemConverter.toString(item); return itemText != null && itemText.toUpperCase().startsWith(prefix); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy