/*
* ShootOFF - Software for Laser Dry Fire Training
* Copyright (C) 2016 phrack
*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.shootoff.gui.pane;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBase;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.TilePane;
import javafx.scene.text.TextAlignment;
public class ItemSelectionPane<T> extends ScrollPane {
private static final Logger logger = LoggerFactory.getLogger(ItemSelectionPane.class);
private static final int DEFAULT_COLUMNS = 6;
// Above MAX_COLUMNS and you end up having to move your mouse side to side
// too much
private static final int MAX_COLUMNS = 8;
private static final int ITEM_DIMS = 150;
private final Map<Object, ButtonBase> items = new HashMap<>();
private final TilePane subContainer = new TilePane(30, 30);
private boolean toggleable;
private ToggleGroup toggleGroup = null;
private T defaultSelection = null;
private T currentSelection = null;
private ItemSelectionListener<T> itemListener = null;
public ItemSelectionPane(boolean toggleItems, ItemSelectionListener<T> itemListener) {
super();
this.itemListener = itemListener;
this.toggleable = toggleItems;
if (toggleable) {
toggleGroup = new ToggleGroup();
}
subContainer.setPrefColumns(DEFAULT_COLUMNS);
subContainer.setPadding(new Insets(0, 65, 65, 65));
widthProperty().addListener((observable, oldValue, newValue) -> {
final Insets padding = subContainer.getPadding();
final int hgap = (int) subContainer.getHgap();
final int columnCount = (newValue.intValue() - (int) (padding.getLeft() + padding.getRight()) + hgap)
/ (ITEM_DIMS + hgap);
if (columnCount <= MAX_COLUMNS) subContainer.setPrefColumns(columnCount);
});
setStyle("-fx-focus-color: transparent; -fx-faint-focus-color: transparent; -fx-background-color:transparent;");
setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
setHbarPolicy(ScrollBarPolicy.NEVER);
setFitToHeight(true);
setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
setContent(subContainer);
}
public ItemSelectionPane(ToggleGroup toggleGroup, ItemSelectionListener<T> itemListener) {
this(true, itemListener);
this.toggleGroup = toggleGroup;
}
public ButtonBase addButton(T ref, String text, Optional<Node> graphic, Optional<Tooltip> tooltip) {
final ButtonBase button;
if (toggleable) {
if (defaultSelection == null) defaultSelection = ref;
button = new ToggleButton(text);
((ToggleButton) button).setToggleGroup(toggleGroup);
} else {
button = new Button(text);
}
button.setContentDisplay(ContentDisplay.TOP);
button.setTextAlignment(TextAlignment.CENTER);
button.setPrefSize(ITEM_DIMS, ITEM_DIMS);
button.setWrapText(true);
if (graphic.isPresent()) button.setGraphic(graphic.get());
if (tooltip.isPresent()) {
button.setOnMouseEntered((event) -> {
final Point2D p = button.localToScreen(button.getLayoutBounds().getMaxX(),
button.getLayoutBounds().getMaxY());
tooltip.get().show(button, p.getX(), p.getY());
});
button.setOnMouseExited((event) -> {
tooltip.get().hide();
});
}
button.setOnAction((event) -> {
itemListener.onItemClicked(ref);
if (toggleable) toggleGroup.selectToggle((Toggle) button);
});
subContainer.getChildren().add(button);
items.put(ref, button);
return button;
}
public ButtonBase addButton(T ref, String text) {
return this.addButton(ref, text, Optional.empty(), Optional.empty());
}
public void setDefault(T ref) {
if (!toggleable) {
logger.error("setDefault only applies to toggleable item selection");
return;
}
if (items.containsKey(ref)) {
if (defaultSelection == null && currentSelection == null) {
currentSelection = ref;
toggleGroup.selectToggle((Toggle) items.get(ref));
}
defaultSelection = ref;
} else
logger.error("setDefault on non-existing ref - {}", ref);
}
public void removeButton(T ref) {
if (!items.containsKey(ref)) {
logger.error("removeButton on non-existing ref - {}", ref);
return;
}
final Node item = items.remove(ref);
if (Platform.isFxApplicationThread()) {
subContainer.getChildren().remove(item);
} else {
Platform.runLater(() -> subContainer.getChildren().remove(item));
}
if (toggleable && ref == defaultSelection) {
defaultSelection = null;
}
if (toggleable && ref == currentSelection && defaultSelection != null) {
currentSelection = defaultSelection;
itemListener.onItemClicked(currentSelection);
toggleGroup.selectToggle((Toggle) items.get(currentSelection));
}
}
public Object getCurrentSelection() {
return currentSelection;
}
public ToggleGroup getToggleGroup() {
return toggleGroup;
}
public void setSelection(T ref) {
if (items.containsKey(ref)) {
currentSelection = ref;
toggleGroup.selectToggle((Toggle) items.get(currentSelection));
} else {
if (logger.isWarnEnabled()) logger.warn("setSelection on non-existing ref - {}", ref);
}
}
}