net.thirdy.blackmarket.controls.ModSelectionPane.java Source code

Java tutorial

Introduction

Here is the source code for net.thirdy.blackmarket.controls.ModSelectionPane.java

Source

/*
 * Copyright (C) 2015 thirdy
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package net.thirdy.blackmarket.controls;

import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.ImmutableMap;

import io.jexiletools.es.model.ItemType;
import io.jexiletools.es.modsmapping.ModsMapping;
import io.jexiletools.es.modsmapping.ModsMapping.ModMapping;
import io.jexiletools.es.modsmapping.ModsMapping.ModType;
import io.jexiletools.es.modsmapping.ModsMapping.Type;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TextField;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import net.thirdy.blackmarket.domain.RangeOptional;
import net.thirdy.blackmarket.fxcontrols.DoubleTextField;
import net.thirdy.blackmarket.fxcontrols.RangeDoubleTextField;
import net.thirdy.blackmarket.fxcontrols.TableViewPlus;
import net.thirdy.blackmarket.fxcontrols.TriStateButton;
import net.thirdy.blackmarket.fxcontrols.TriStateButton.State;

public class ModSelectionPane extends GridPane implements Consumer<List<ItemType>> {

    private TextField filterField;
    private DoubleTextField tfMinShouldMatch;
    private TableViewPlus<ModMapping> modMappingTable;
    private ListView<Mod> modsListView;

    private ObservableList<ModMapping> masterData = FXCollections.observableList(new LinkedList<>());

    public ModSelectionPane() {
        setHgap(5.0);
        setMaxHeight(Double.MAX_VALUE);
        setMinHeight(560);
        setAlignment(Pos.CENTER);
        setupModListView();
        accept(Collections.emptyList());
        setupModMappingTable();
        setupFilterTextField();
        tfMinShouldMatch = new DoubleTextField("Minimum number of OR modifiers to match");
        tfMinShouldMatch.setMinWidth(350);

        Button add = addButton();
        add.setPrefWidth(150);
        HBox hBox = new HBox(5, new Label("Filter: "), filterField, add);
        hBox.setAlignment(Pos.CENTER);

        VBox.setVgrow(modMappingTable, Priority.ALWAYS);
        VBox left = new VBox(10, hBox, modMappingTable);
        VBox.setVgrow(modsListView, Priority.ALWAYS);
        Label modifiersLbl = new Label("Modifiers");
        modifiersLbl.setFont(Font.font("Verdana", FontWeight.MEDIUM, 14));
        modifiersLbl.setPadding(new Insets(4));
        HBox minShouldMatchHBox = new HBox(3, new Label("Minimum OR Matches:"), tfMinShouldMatch);
        minShouldMatchHBox.setAlignment(Pos.CENTER);
        VBox right = new VBox(3, new StackPane(modifiersLbl), minShouldMatchHBox, modsListView);

        setupGridPaneColumns();

        GridPane.setVgrow(left, Priority.ALWAYS);
        GridPane.setVgrow(right, Priority.ALWAYS);
        add(left, 0, 0);
        add(right, 1, 0);
    }

    private void setupGridPaneColumns() {
        ColumnConstraints column1 = new ColumnConstraints();
        column1.setPercentWidth(50);
        ColumnConstraints column2 = new ColumnConstraints();
        column2.setPercentWidth(50);
        getColumnConstraints().addAll(column1, column2);
    }

    private Button addButton() {
        Button add = new Button("Add");
        add.setOnAction(e -> {
            modMappingTable.getSelectionModel().getSelectedItems().stream()
                    .forEachOrdered(mm -> modsListView.getItems().add(new Mod(mm)));
        });
        return add;
    }

    private void setupModMappingTable() {
        modMappingTable = new TableViewPlus<>(
                ImmutableMap.of("Item Type", "itemType", "Type", "modType", "Mod", "mapping"),
                new double[] { 100, 100, 600 }, masterData);
        modMappingTable.setMaxHeight(Double.MAX_VALUE);
        modMappingTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        modMappingTable.setId("modMappingTable");
    }

    private void setupModListView() {
        modsListView = new ListView<>();
        modsListView.setEditable(true);
        modsListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        modsListView.setMaxHeight(Double.MAX_VALUE);
        // remove focus
        modsListView.setStyle("-fx-background-color: transparent, -fx-inner-border,"
                + " -fx-body-color; -fx-background-insets: 0, 1, 2; -fx-background-radius: 5, 4, 3;");

        modsListView.setCellFactory((listView) -> new ModListCell(listView));
        modsListView.setId("modsListView");
    }

    private void setupFilterTextField() {
        filterField = new TextField();
        filterField.setMinWidth(320);
        filterField.textProperty().addListener((observable, oldValue, newValue) -> {
            modMappingTable.getFilteredData().setPredicate(modmapping -> {
                // If filter text is empty, display all.
                if (newValue == null || newValue.isEmpty()) {
                    return true;
                }

                String lowerCaseFilter = newValue.toLowerCase();
                String key = modmapping.getKey().toLowerCase();

                return key.contains(lowerCaseFilter);
            });
        });
    }

    @Override
    public void accept(List<ItemType> itemTypes) {
        masterData.clear();
        List<ModMapping> modMappings = ModsMapping.getInstance().getModMappings();
        if (!itemTypes.isEmpty()) {
            List<String> itemTypesRaw = itemTypes.stream().map(itemType -> itemType.itemType())
                    .collect(Collectors.toList());
            modMappings = modMappings.stream()
                    .filter(mm -> mm.getModType() == ModType.PSEUDO || itemTypesRaw.contains(mm.getItemType()))
                    .collect(Collectors.toList());
        }

        Comparator<ModMapping> byModType = (m1, m2) -> Integer.valueOf(m1.getModType().ordinal())
                .compareTo(Integer.valueOf(m2.getModType().ordinal()));

        Comparator<ModMapping> byModTypeThenKey = byModType
                .thenComparing((m1, m2) -> m1.getKey().compareTo(m2.getKey()));
        modMappings.sort(byModTypeThenKey);
        masterData.addAll(modMappings);
    }

    public static class Mod {
        ModMapping modMapping;

        public Mod(ModMapping modMapping) {
            this.modMapping = modMapping;
        }

        public ObjectProperty<Optional<RangeOptional>> lowerRange = new SimpleObjectProperty();
        public ObjectProperty<Optional<RangeOptional>> higherRange = new SimpleObjectProperty();

        public ObjectProperty<State> logic = new SimpleObjectProperty<>();
    }

    private static class ModListCell extends ListCell<Mod> {
        private Label lblMod = new Label();

        public RangeDoubleTextField lowerRangeDoubleTf = new RangeDoubleTextField();
        public RangeDoubleTextField higherRangeDoubleTf = new RangeDoubleTextField();

        public TriStateButton logic = new TriStateButton();
        private Region spacer = new Region();
        public HBox container = new HBox(5.0);
        Button remove = new Button("X");

        public ModListCell(ListView<Mod> listView) {
            remove.setOnAction(e -> listView.getItems().remove(getIndex()));
            HBox.setHgrow(spacer, Priority.ALWAYS);
            lowerRangeDoubleTf.setPrefWidth(100);
            higherRangeDoubleTf.setPrefWidth(100);
            container.getChildren().addAll(lblMod, spacer, lowerRangeDoubleTf, higherRangeDoubleTf, logic, remove);
        }

        @Override
        protected void updateItem(Mod item, boolean empty) {
            super.updateItem(item, empty);
            if (item != null) {
                Type modType = item.modMapping.getType();

                // Label
                String key = item.modMapping.getKey();
                key = StringUtils.removeEnd(key, ".min");
                lblMod.setText(key);

                // Lower Range
                boolean showLowerRange = modType == Type.DOUBLE || modType == Type.DOUBLE_MIN_MAX;
                item.lowerRange.unbind();
                container.getChildren().remove(lowerRangeDoubleTf);
                if (showLowerRange) {
                    container.getChildren().add(2, lowerRangeDoubleTf);
                    item.lowerRange.bind(Bindings.createObjectBinding(() -> {
                        return lowerRangeDoubleTf.val();
                    }, lowerRangeDoubleTf.getMin().textProperty(), lowerRangeDoubleTf.getMax().textProperty()));
                }

                // Higher Range
                boolean showHigherRange = modType == Type.DOUBLE_MIN_MAX;
                higherRangeDoubleTf.setVisible(showHigherRange);
                item.higherRange.unbind();
                container.getChildren().remove(higherRangeDoubleTf);
                if (showHigherRange) {
                    container.getChildren().add(3, higherRangeDoubleTf);
                    item.higherRange.bind(Bindings.createObjectBinding(() -> {
                        return higherRangeDoubleTf.val();
                    }, higherRangeDoubleTf.getMin().textProperty(), higherRangeDoubleTf.getMax().textProperty()));
                }

                item.logic.unbind();
                item.logic.bind(logic.stateProperty());
                setGraphic(container);
            } else {
                setGraphic(null);
            }
        }
    }

    public Optional<List<Mod>> mods() {
        return modsListView.getItems().size() == 0 ? Optional.empty() : Optional.of(modsListView.getItems());
    }

    public Optional<Double> mininumShouldMatch() {
        return tfMinShouldMatch.getOptionalValue();
    }
}