package com.insightfullogic.honest_profiler.ports.javafx.util;
import static com.insightfullogic.honest_profiler.ports.javafx.util.ResourceUtil.getDefaultBundle;
import static java.lang.Math.max;
import static javafx.application.Platform.runLater;
import static javafx.geometry.Pos.CENTER;
import static javafx.geometry.Pos.CENTER_LEFT;
import static javafx.scene.paint.Color.BEIGE;
import static javafx.scene.paint.Color.CHARTREUSE;
import static javafx.scene.paint.Color.CYAN;
import static javafx.scene.paint.Color.GOLD;
import static javafx.scene.paint.Color.LIGHTBLUE;
import static javafx.scene.paint.Color.LIGHTGREEN;
import static javafx.scene.paint.Color.LIGHTGREY;
import static javafx.scene.paint.Color.LIGHTPINK;
import static javafx.scene.paint.Color.LIGHTSTEELBLUE;
import static javafx.scene.paint.Color.ORANGE;
import com.insightfullogic.honest_profiler.ports.javafx.model.ProfileContext;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TableView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
/**
* Utility class which offers various JavaFX-specific convenience methods.
*/
public final class FxUtil
{
// Class Properties
private static String FXML_DIR = "/com/insightfullogic/honest_profiler/ports/javafx/fxml/";
public static String FXML_ROOT = FXML_DIR + "Root.fxml";
public static String FXML_PROFILE_ROOT = FXML_DIR + "ProfileRoot.fxml";
public static String FXML_PROFILE_DIFF_ROOT = FXML_DIR + "ProfileDiffRoot.fxml";
private static Color[] LABEL_PALETTE = new Color[]
{ LIGHTSTEELBLUE, LIGHTGREEN, ORANGE, LIGHTBLUE, BEIGE, GOLD, LIGHTGREY, LIGHTPINK, CYAN,
CHARTREUSE };
// Class Methods
/**
* Returns an {@link FXMLLoader} for the specified resource.
* <p>
* @param originator the calling {@link Object} used for resource resolution
* @param resource the String representation of the resource URL
* @return an {@link FXMLLoader} for the specified resource
*/
public static FXMLLoader loaderFor(Object originator, String resource)
{
return new FXMLLoader(originator.getClass().getResource(resource), getDefaultBundle());
}
// Utility methods for adding "coloured labels".
/**
* Generates a "colored label", which is a rectangular graphic filled with the specified color, and containing the
* specified text, and adds it to the provided {@link Pane}.
* <p>
* This method should be called after the provided {@link Pane} has already been added to the scene graph. The idea
* is that with the logic below, the text gets rendered inside its proper place in the scene graph, so that CSS and
* other style settings are automatically taken into account. The rectangle is then resized to be a bit bigger than
* the text.
* <p>
* @param pane the {@link Pane} into which the graphic will be added
* @param content the text content to be rendered in the colored label
* @param color the background color of the label
* @return the provided {@link Pane}
*/
public static Node addColouredLabel(Pane pane, String content, Color color)
{
StackPane stackPane = new StackPane();
stackPane.setAlignment(CENTER);
Text text = new Text(content);
Rectangle rectangle = new Rectangle();
rectangle.setFill(color);
stackPane.getChildren().addAll(rectangle, text);
pane.getChildren().add(stackPane);
// Render the text, and use the resulting image size to resize the
// rectangle
WritableImage image = text.snapshot(null, null);
double width = image.getWidth() + 2;
double height = image.getHeight() + 2;
rectangle.setWidth(max(width, height));
rectangle.setHeight(image.getHeight() + 2);
return pane;
}
// Convenience method specifically for displaying Profile Nrs.
// Call this after the Pane has already been added to the scene graph !
/**
* Generates a colored label for the "profile number" of the provided {@link ProfileContext}, as per
* {@link #addColouredLabel(Pane, String, Color)}, and adds it to the provided pane.
* <p>
* This method should be called after the provided {@link Pane} has already been added to the scene graph.
* <p>
* @param pane the {@link Pane} into which the graphic will be added
* @param profileContext the {@link ProfileContext} whose number will be used as text in the colored label
*/
public static void addProfileNr(Pane pane, ProfileContext profileContext)
{
addColouredLabel(
pane,
Integer.toString(profileContext.getId()),
LABEL_PALETTE[profileContext.getId() % LABEL_PALETTE.length]);
}
/**
* Create a typical HBox which can be used for displaying info, and which is ready for adding coloured labels to
* (see above). The contents will be aligned using the specified alignemnt.
* <p>
* @param alignment a {@link Pos} specifying how the {@link HBox} content should be aligned
* @return the created {@link HBox}
*/
public static HBox createColoredLabelContainer(Pos alignment)
{
HBox box = new HBox();
box.setAlignment(alignment);
box.setSpacing(5);
return box;
}
/**
* Create a typical HBox which can be used for displaying info, and which is ready for adding coloured labels to
* (see above), with contents aligned using {@link Pos#CENTER_LEFT}.
* <p>
* @return the created {@link HBox}
*/
public static Pane createColoredLabelContainer()
{
return createColoredLabelContainer(CENTER_LEFT);
}
// Hack to work around defective TableView refresh. Tables do not properly
// update when items are added or removed.
// See :
// http://stackoverflow.com/questions/11065140/javafx-2-1-tableview-refresh-items
/**
* Hack to work around defective TableView refresh. Tables do not always properly update when items are added or
* removed.
* <p>
* See : <a href="http://stackoverflow.com/questions/11065140/javafx-2-1-tableview-refresh-items">this StackOverflow
* topic</a>.
* <p>
* @param table the {@link TableView} to be refreshed
*/
public static void refreshTable(TableView<?> table)
{
runLater(() ->
{
table.getColumns().get(0).setVisible(false);
table.getColumns().get(0).setVisible(true);
});
}
/**
* Returns an indeterminate {@link ProgressIndicator} with the specified dimensions.
* <p>
* @param maxWidth the maximum width of the {@link ProgressIndicator}
* @param maxHeight the maximum height of the {@link ProgressIndicator}
* @return the created {@link ProgressIndicator}
*/
public static ProgressIndicator getProgressIndicator(double maxWidth, double maxHeight)
{
ProgressIndicator progress = new ProgressIndicator();
progress.setMaxSize(maxWidth, maxHeight);
return progress;
}
// Instance Constructors
/**
* Private constructor for Utility Class
*/
private FxUtil()
{
// Private constructor for Utility Class
}
}