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

org.rapidpm.modul.javafx.textfield.autocomplete.AutoCompleteTextFieldSkin Maven / Gradle / Ivy

The newest version!
/*
 * Copyright [2013] [www.rapidpm.org / Sven Ruppert ([email protected])]
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.rapidpm.modul.javafx.textfield.autocomplete;

import java.util.Comparator;
import java.util.List;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SkinBase;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.stage.Popup;
import javafx.stage.Window;
import javafx.util.Callback;
import org.rapidpm.module.se.commons.logger.Logger;


/**
 * This class helps to change the skin of the AutoCompleteTextField Control
 *
 * @author Narayan G. Maharjan
 */
public class AutoCompleteTextFieldSkin
        extends SkinBase>
        implements ChangeListener, EventHandler {

    private static final Logger logger = Logger.getLogger(AutoCompleteTextFieldSkin.class);

    //Final Static variables for Window Insets
    private final static int TITLE_HEIGHT = 28;

    //This is listview for showing the matched words
    private ListView listview;

    //This is Textbox where user types
    private TextField textbox;

    //This is the main Control of AutoCompleteTextField
    private AutoCompleteTextField autofillTextbox;

    //This is the ObservableData where the matching words are saved
    private ObservableList data;

    //This is the Popup where listview is embedded.
    private Popup popup;
    private Object finalSelectedActions;


    public Window getWindow() {
        return autofillTextbox.getScene().getWindow();
    }

    private String temporaryTxt = "";


    public AutoCompleteTextFieldSkin(AutoCompleteTextField text) {
        super(text);
        //variable Assignment
        autofillTextbox = text;
        //listview for autofill textbox
        listview = text.getListview();
        if (text.getFilterMode()) {
            listview.setItems(text.getData());
        }
        listview.itemsProperty().addListener(new ChangeListener() {

            @Override
            public void changed(ObservableValue ov, Object t, Object t1) {
                if (t == null && ((String) t1).length() > 1) {
                    hidePopUp();  //manuelle Eingabe beginnt mit einem Zeichen
                } else if (listview.getItems() != null && listview.getItems().size() > 0) {
                    showPopup();
                } else {
                    hidePopup();
                }
            }


        });
        listview.setOnMouseReleased(this);
        listview.setOnKeyReleased(this);
        //This cell factory helps to know which cell has been selected so that
        //when ever any cell is selected the textbox rawText must be changed
        listview.setCellFactory(new Callback, ListCell>() {
            @Override
            public ListCell call(ListView p) {
                //A simple ListCell containing only Label
                final ListCell cell = new ListCell() {
                    @Override
                    public void updateItem(T item, boolean empty) {
                        super.updateItem(item, empty);
                        if (item != null) {
                            setText(item.getShortinfo());

                        }
                    }

                };
                //A listener to know which cell was selected so that the textbox
                //we can set the rawTextProperty of textbox

                cell.focusedProperty().addListener(new InvalidationListener() {

                    // @Override
                    public void invalidated(Observable ove) {
                        //ObservableValue ov = (ObservableValue) ove;
                        final AutoCompleteElement cellItem = cell.getItem();
                        if (cellItem != null && cell.isFocused()) {
                            //here we are using 'temporaryTxt' as temporary saving text
                            //If temporaryTxt length is 0 then assign with current rawText()
                            String prev = null;

                            //first check ...(either texmporaryTxt is empty char or not)
                            if (temporaryTxt.length() <= 0) {
                                //second check...
                                if (listview.getItems().size() != data.size())
                                    temporaryTxt = textbox.getText();
                            }

                            prev = temporaryTxt;
                            textbox.textProperty().removeListener(AutoCompleteTextFieldSkin.this);
                            //textbox.rawTextProperty().removeListener(AutoCompleteTextFieldSkin.this);
                            //textbox.setText(prev);
                            textbox.textProperty().setValue(cellItem.getShortinfo());
                            textbox.textProperty().addListener(AutoCompleteTextFieldSkin.this);
                            textbox.selectRange(prev.length(), cellItem.getKey().length());
                            if (logger.isDebugEnabled()) {
                                logger.debug(temporaryTxt.length() + "=" + textbox.getText().length() + "::" +
                                        cellItem.getKey().length());
                            }
                        }
                    }
                });

                return cell;
            }

        });

        //main textbox
        textbox = text.getTextbox();
        //textbox.setSelectOnFocus(false);
        textbox.setOnKeyPressed(this);
        textbox.textProperty().addListener(this);


        textbox.focusedProperty().addListener(new ChangeListener() {
            @Override
            public void changed(ObservableValue ov, Object t, Object t1) {
                textbox.end();
            }
        });

        //popup
        popup = new Popup();
        popup.setAutoHide(true);
        popup.getContent().add(listview);

        //list data and sorted ordered
        data = text.getData();
        FXCollections.sort(data, new Comparator() {
            @Override
            public int compare(AutoCompleteElement o1, AutoCompleteElement o2) {
                if (o1 == null || o2 == null) {
                    return 0;
                } else {
                    return o1.getKey().compareTo(o2.getKey());
                }
            }
        });

        //Adding textbox in this control Children
        getChildren().addAll(textbox);
    }

    /**
     * *******************************************************
     * Selects the current Selected Item from the list
     * and the content of that selected Item is set to textbox.
     * ********************************************************
     */
    public T selectList() {
        T selectedItem = listview.getSelectionModel().getSelectedItem();
        if (selectedItem != null) {
            textbox.setText(selectedItem.getKey());
            textbox.requestFocus();
            textbox.requestLayout();
            textbox.end();
            listview.getItems().clear();
            temporaryTxt = "";
            hidePopup();
        }
        return selectedItem;
    }


    /**
     * ***************************************************
     * This is the main event handler which handles all the
     * event of the listview and textbox
     *
     * @param evt
     */
    @Override
    public void handle(Event evt) {

        if (evt.getEventType() == KeyEvent.KEY_PRESSED) {
            KeyEvent t = (KeyEvent) evt;
            if (t.getSource() == textbox) {
                //WHEN USER PRESS DOWN ARROW KEY FOCUS TRANSFER TO LISTVIEW
                if (t.getCode() == KeyCode.DOWN) {
                    if (popup.isShowing()) {
                        listview.requestFocus();
                        listview.getSelectionModel().select(0);
                    }
                }
            }
        } else if (evt.getEventType() == KeyEvent.KEY_RELEASED) {
            KeyEvent t = (KeyEvent) evt;
            if (t.getSource() == listview) {
                if (t.getCode() == KeyCode.ENTER) {
                    final T selectedItem = selectList();
                    executeCustomActions(selectedItem);
                } else if (t.getCode() == KeyCode.UP) {
                    if (listview.getSelectionModel().getSelectedIndex() == 0) {
                        textbox.requestFocus();
                    }
                }
            }
        } else if (evt.getEventType() == MouseEvent.MOUSE_RELEASED) {
            if (evt.getSource() == listview) {
                final T selectedItem = selectList();
                executeCustomActions(selectedItem);
            }
        }
    }

    private void executeCustomActions(T selectedItem) {
        final List customActionList = autofillTextbox.getCustomActionsList();
        for (final AutoCompleteTextFieldAction customAction : customActionList) {
            customAction.execute(selectedItem);
        }
    }

//    @Override
//    protected double computeMaxHeight(double width) {
//        return Math.max(22.0d, textbox.getHeight());
//    }

//    @Override
//    public void setPrefSize(double d, double d1) {
//        super.setPrefSize(d, d1);
//    }

//    @Override
//    protected double computePrefHeight(double width) {
//        return Math.max(22.0d, textbox.getPrefHeight());
//    }

//    @Override
//    protected double computeMinHeight(double width) {
//        return Math.max(22.0d, textbox.getPrefHeight());
//    }
//
//    @Override
//    protected double computePrefWidth(double height) {
//        return Math.max(100.0d, textbox.getPrefWidth());
//    }
//
//    @Override
//    protected double computeMaxWidth(double height) {
//        return Math.max(100.0d, textbox.getPrefWidth());
//    }
//
//    @Override
//    protected double computeMinWidth(double height) {
//        return Math.max(100.0d, textbox.getPrefWidth());
//    }

    public void hidePopUp() {
        popup.hide();
    }

    /**
     * A Popup containing Listview is trigged from this function
     * This function automatically resize it's height and width
     * according to the width of textbox and item's cell height
     */
    public void showPopup() {
        listview.setPrefWidth(textbox.getWidth());
        //final int maxShownElements = 6;
        final int heightPerElement = 24;
        final int weigthPerElement = 8;

        final int itemCount = listview.getItems().size();
        if (itemCount > AutoCompleteTextField.DEFAULT_LIMIT) {
            listview.setPrefHeight((AutoCompleteTextField.DEFAULT_LIMIT * heightPerElement));
            listview.setVisible(true);
        } else if (itemCount > 1) {
            final int tmpCount = itemCount + 1;
            listview.setPrefHeight(tmpCount * heightPerElement);
            listview.setVisible(true);
        }
        if (itemCount == 0) {
            listview.setVisible(false);
        } else {
            listview.setPrefHeight(itemCount * heightPerElement); //Sollte dann weg sein
            listview.setVisible(true);
        }

        //longest ShortInfo
        int maxLength = 0;
        for (final T element : listview.getItems()) {
            final int length = element.getShortinfo().length();
            if (length >= maxLength) {
                maxLength = length;
                if (logger.isDebugEnabled()) {
                    logger.debug("maxlength (changed): " + maxLength);
                }
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("maxlength (not changed): " + maxLength);
                }
            }
        }
        if (maxLength > 0) {
            listview.setPrefWidth(maxLength * weigthPerElement);
        } else {
            //
        }

        final Window window = getWindow();
        popup.show(
                window,
                window.getX() + textbox.localToScene(0, 0).getX() + textbox.getScene().getX(),
                window.getY() + textbox.localToScene(0, 0).getY() + textbox.getScene().getY() + TITLE_HEIGHT);

        listview.getSelectionModel().clearSelection();
        listview.getFocusModel().focus(-1);
    }

    /**
     * This function hides the popup containing listview
     */
    public void hidePopup() {
        popup.hide();
    }


    /**
     * ********************************************
     * When ever the the rawTextProperty is changed
     * then this listener is activated
     *
     * @param ov
     * @param t
     * @param t1 **********************************************
     */


    @Override
    public void changed(ObservableValue ov, String t, String t1) {
        final String value = (String) ov.getValue();
        //setzen von Text
        final int deltalength = Math.abs(t.length() - t1.length());
        final boolean b = deltalength > 1;
        if (b) {
            hidePopUp();
        } else {
            if (value.length() > 0) {
                final String txtdata = (textbox.getText()).trim();
                if (logger.isDebugEnabled()) {
                    logger.debug("content textfield [" + txtdata + "]");
                }
                //Limit of data cell to be shown in ListView
                int limit = 0;
                if (txtdata.length() > 0) {
                    final ObservableList list = FXCollections.observableArrayList();
                    final String compare = txtdata.toLowerCase();
                    for (T dat : data) {
                        final String lowerCaseKey = dat.getKey().toLowerCase();
                        boolean add = false;
                        if (autofillTextbox.isFilterModeStartsWith()) {
                            if (lowerCaseKey.startsWith(compare)) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("added one candidate, " + lowerCaseKey);
                                }
                                add = true;
                            }
                        } else {
                            if (lowerCaseKey.contains(compare)) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("added one candidate, " + lowerCaseKey);
                                }
                                add = true;
                            }
                        }
                        if (add) {
                            list.add(dat);
                            limit++;
                        } else {
                            //
                        }

                        if (limit == autofillTextbox.getListLimit())  //JIRA MOD-58 Anzahl aller Treffer anzeigen
                            break;
                    }
                    final ObservableList items = listview.getItems();
                    if (items != null && items.containsAll(list) && items.size() == list.size())
                        showPopup();
                    else
                        listview.setItems(list);
                } else {
                    listview.setItems(autofillTextbox.getFilterMode() ? data : null);
                }
            }

            if (value.length() <= 0) {
                temporaryTxt = "";
                if (autofillTextbox.getFilterMode()) {
                    listview.setItems(data);
                    showPopup();
                } else {
                    hidePopup();
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy