UI Popup Opacity Mask JavaFX - java

Is it possible with Popup opacity mask top and bottom JavaFX? I have TextField autocomplete with Popup. So the idea is to put an opacity mask.

Below is another way you can give a try, for getting the opacity masked effect. Though it is not exactly the same implementation, I took some ideas from the link you provided :).
I created a small utility where you can pass the Popup instance. The utility builds the mask panes and include to the root node of the Popup.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Popup;
import javafx.stage.Stage;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class PopupOpacityMaskDemo extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = new StackPane();
root.setStyle("-fx-background-color:grey;");
root.setOnMouseClicked(e -> {
ListView<String> content = new ListView<>();
content.getItems().addAll(IntStream.range(100, 200).mapToObj(i -> i + "").collect(Collectors.toList()));
content.setPrefSize(200, 250);
Popup popup = new Popup();
popup.setAutoHide(true);
popup.getContent().add(content);
popup.setX(e.getScreenX());
popup.setY(e.getScreenY());
popup.show(root.getScene().getWindow());
MaskUtil.applyMask(popup);
});
Scene scene = new Scene(root, 200, 200);
primaryStage.setScene(scene);
primaryStage.setTitle("Demo");
primaryStage.show();
}
static class MaskUtil{
static void applyMask(Popup popup) {
double fadeSize = 70;
Pane pane = (Pane)popup.getScene().getRoot();
// Build the mask panes
Pane topMask = buildMaskPane(pane, fadeSize, false);
Pane bottomMask = buildMaskPane(pane, fadeSize, true);
// Just ensuring to remove any masks (if you are reusing the Popup)
pane.getChildren().removeAll(pane.lookupAll(".mask"));
pane.getChildren().addAll(topMask, bottomMask);
// Update the bottom mask position by listening to height of pane
pane.heightProperty().addListener((obs, old, h) -> bottomMask.setLayoutY(h.doubleValue() - fadeSize));
if (pane.getHeight() > 0) {
bottomMask.setLayoutY(pane.getHeight() - fadeSize);
}
}
private static Pane buildMaskPane(Pane pane, double fadeSize, boolean isBottom) {
Pane mask = new Pane();
mask.setMouseTransparent(true); // Turn this to 'false' if you don't want to interact over mask
mask.setPrefHeight(fadeSize);
mask.prefWidthProperty().bind(pane.widthProperty());
mask.maxHeightProperty().bind(mask.prefHeightProperty());
mask.minHeightProperty().bind(mask.prefHeightProperty());
mask.getStyleClass().add("mask");
mask.setStyle(String.format("-fx-background-color:linear-gradient(to %s, #555555, transparent)", isBottom ? "top" : "bottom"));
return mask;
}
}
}

Related

Javafx Slider Thumb

Is there a way to display an integer on the slider thumb in javafx? Just curious because I am trying to make a clean UI and cannot find anything on displaying an integer on the slider thumb.
One way to address the requirement is by accessing the thumb node and include a Text/Label node. Please check the below demo for what i mean.
You can adjust the thumb padding and the text size for fine tuning.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class SliderTextDemo extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Slider slider = new Slider(1, 10, 3);
slider.setShowTickMarks(true);
slider.setShowTickLabels(true);
slider.setMajorTickUnit(1f);
slider.setBlockIncrement(1f);
slider.setSnapToTicks(true);
Text text = new Text();
slider.skinProperty().addListener((obs,old,skin)->{
if(skin!=null){
StackPane thumb = (StackPane)slider.lookup(".thumb");
thumb.setPadding(new Insets(10));
thumb.getChildren().add(text);
}
});
slider.valueProperty().addListener((obs,old,val)->text.setText(val.intValue()+""));
slider.setValue(2);
VBox root = new VBox(slider);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(20));
root.setSpacing(20);
Scene scene = new Scene(root,600,200);
primaryStage.setScene(scene);
primaryStage.setTitle("Slider Text Demo");
primaryStage.show();
}
}
UPDATE:
If you don't want to rely on accessing the skin,you can indeed implement/initialize the Slider as below. That way you can create a custom Slider and can reuse in multiple places.
Slider slider = new Slider(1, 10, 3) {
Text text;
#Override
protected void layoutChildren() {
super.layoutChildren();
if (text == null) {
text = new Text(((int) getValue()) + "");
valueProperty().addListener((obs, old, val) -> text.setText(val.intValue() + ""));
StackPane thumb = (StackPane) lookup(".thumb");
thumb.setPadding(new Insets(10));
thumb.getChildren().add(text);
}
}
};

Javafx Tooltip blinks when is too big

I want to display a small image and display the image in the original size when the small is hover.
I need a tooltip because the image in the original size could not fit the window.
Executable code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
String bigImageUrl = "https://cdn.lynda.com/course/184457/184457-636806635954727169-16x9.jpg";
String smallImageUrl = "https://assets.exercism.io/tracks/java-bordered-turquoise.png";
VBox root = new VBox();
root.getChildren().add(loadTooltipWithImage(smallImageUrl));
root.getChildren().add(loadTooltipWithImage(bigImageUrl));
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
private Label loadTooltipWithImage(String url) {
ImageView bigImage = new ImageView(new Image(url));
ImageView smallImage = new ImageView(new Image(url));
smallImage.setFitHeight(100.0);
smallImage.setFitWidth(100.0);
Label label = new Label();
label.setGraphic(smallImage);
Tooltip tooltip = new Tooltip();
tooltip.setGraphic(bigImage);
label.setTooltip(tooltip);
return label;
}
}
A tooltip displays an image when a node is hover, but when image is too big the node cannot be hovered by the mouse:
The node is hover
The tooltip displays image
The node is not hover
The tooltip does not display
Go to step 1
What I tried:
Set mouse transparent to true on the tooltip, but I could not
Use a pane instead of tooltip, but the window (stage) is too small for the image
Set the mouse filters, but they do not help me

ScrollPane not showing as needed, FlowPane content

I'm having some difficulty with ScrollPane in JavaFX 8 showing the scrollbar as needed. What I'm currently doing is simply creating a FlowPane with x number of elements, and setting that as the content of the ScrollPane.
The problem happens when I shrink down perpendicular to the orientation of the FlowPane. When elements begin to wrap and go out of bounds, the scrollbar does not appear. This does not happen when I shrink parallel to the orientation. I have a small Java program to exemplify the issue.
Start
Shrinking Parallel
Shrinking Perpendicular
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FlowPane flow = new FlowPane();
flow.setStyle("-fx-border-color: red");
addPanes(flow, 16);
ScrollPane scroll = new ScrollPane(flow);
scroll.setStyle("-fx-border-color: green");
scroll.setFitToHeight(true);
scroll.setFitToWidth(true);
Scene scene = new Scene(scroll, 450, 450);
primaryStage.setScene(scene);
primaryStage.show();
}
public void addPanes(FlowPane root, int panes) {
for(int i = 0; i < panes; i++) {
StackPane filler = new StackPane();
filler.setStyle("-fx-border-color: black");
filler.setPrefSize(100, 100);
root.getChildren().add(filler);
}
}
}
Have a look at the code below and tell me if that's what you want to achieve. I am still not sure what cause the problem, I will have to look the documentation of ScrollPane to find out. My suspicion is at setFitToWidth & setFitToHeight methods. Although I still believe it's not a bug.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FlowPane flow = new FlowPane();
flow.setStyle("-fx-border-color: red");
addPanes(flow, 16);
ScrollPane scroll = new ScrollPane(flow);
scroll.setStyle("-fx-border-color: green");
// Apparently this cause the issue here.
// scroll.setFitToHeight(true);
// scroll.setFitToWidth(true);
// Instead just make the flow pane take the dimensions of the ScrollPane
// the -5 is to not show the Bars when both of panes have the same dimensions
flow.prefWidthProperty().bind(Bindings.add(-5, scroll.widthProperty()));
flow.prefHeightProperty().bind(Bindings.add(-5, scroll.heightProperty()));
Scene scene = new Scene(scroll, 450, 450);
primaryStage.setScene(scene);
primaryStage.show();
}
public void addPanes(FlowPane root, int panes) {
for (int i = 0; i < panes; i++) {
HBox filler = new HBox();
filler.setStyle("-fx-border-color: black");
filler.setPrefSize(100, 100);
root.getChildren().add(filler);
}
}
}
Looking documentation of the ScrollPane, and in specific the setFitToHeight you will find that :
Property description:
If true and if the contained node is a
Resizable, then the node will be kept resized to match the height of
the ScrollPane's viewport. If the contained node is not a Resizable,
this value is ignored.
And because the node inside the ScrollPane will be kept resized to match the width and height of the ScrollPane's viewport thats why the Vertical ScrollBar will never appear.
You can add the code below to always show your vertical scrollbar.
scroll.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
When the required height of the FlowPane inside the ScrollPane is calculated a width value of -1 is passed. The flow pane will then report the height required when all its content fits into a single line.
As a workaround you could pass the width from the last layout calculation in this case.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FlowPane flow = new FlowPane() {
#Override protected double computeMinHeight(double width) {
double minHeight = super.computeMinHeight(width != -1 ? width :
/* When no width is specified, use the current contol size*/
getWidth());
return minHeight;
}
};
flow.setStyle("-fx-border-color: red");
addPanes(flow, 16);
ScrollPane scroll = new ScrollPane(flow);
flow.maxWidthProperty().bind(scroll.widthProperty());
scroll.widthProperty().addListener((observable, oldValue, newValue)->{
/* clearSizeCache */
flow.requestLayout();
});
scroll.setStyle("-fx-border-color: green");
scroll.setFitToHeight(true);
scroll.setFitToWidth(true);
Scene scene = new Scene(scroll, 450, 450);
primaryStage.setScene(scene);
primaryStage.show();
}
public void addPanes(FlowPane root, int panes) {
for(int i = 0; i < panes; i++) {
StackPane filler = new StackPane();
filler.setStyle("-fx-border-color: black");
filler.setPrefSize(100, 100);
root.getChildren().add(filler);
}
}
}

How to drag a JavaFX node and detect a drop event outside the JavaFX Windows?

I'm implementing a TabPane that can be detached from the JavaFX window. When the tab pane is dragged outside of the window where it came from, I would like a new window to be created and the tab to be placed in that window.
I already implemented some methods using drag gestures to drag the tab between existing windows. However, I am not able to receive any mouse events or drag events when the mouse is moved outside of JavaFX scenes. Is this something possible?
You can listen for a mouse released event on the appropriate node (e.g. the tab's graphic). Check the screen coordinates using MouseEvent.getScreenX() and MouseEvent.getScreenY() and see if they lie outside the current window. If they do, create a new window, scene, and tab pane; remove the tab from the current tab pane and place it in the new one.
Here's a basic example with no frills (e.g. no user hints as to the fact that dragging is occuring), but it will give you the idea:
import javafx.application.Application;
import javafx.collections.ListChangeListener.Change;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.stage.Window;
public class DetachableTabExample extends Application {
private int tabCount = 0 ;
#Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
Button newTabButton = new Button("New Tab");
newTabButton.setOnAction(event -> {
Tab tab = new Tab();
Label tabLabel = new Label("Tab "+(++tabCount));
tab.setGraphic(tabLabel);
tab.setContent(new TextArea());
tabPane.getTabs().add(tab);
tabLabel.setOnMouseReleased(me -> {
Point2D mouseLoc = new Point2D(me.getScreenX(), me.getScreenY());
Window window = tabPane.getScene().getWindow();
Rectangle2D windowBounds
= new Rectangle2D(window.getX(), window.getY(),
window.getWidth(), window.getHeight());
if (! windowBounds.contains(mouseLoc)) {
tabPane.getTabs().remove(tab);
Stage newStage = new Stage();
TabPane newTabPane = new TabPane();
newTabPane.getTabs().add(tab);
Scene scene = new Scene(new BorderPane(newTabPane));
newStage.setScene(scene);
newStage.setX(me.getScreenX());
newStage.setY(me.getScreenY());
newStage.setWidth(window.getWidth());
newStage.setHeight(window.getHeight());
newStage.show();
}
});
});
BorderPane root = new BorderPane(tabPane, newTabButton, null, null, null);
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

How to create tabs with icons in JavaFX

I want to create tabs panel with icons similar to the Firefox configuration panel with JavaFX:
Is there any example which I can use to see how to implement this?
Tabs, like many other elements in JavaFX, have a method called setGraphic(Node value), in which you can put any JavaFX node. Example:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class TabPaneTest extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Tabs");
Group root = new Group();
Scene scene = new Scene(root, 400, 250, Color.WHITE);
TabPane tabPane = new TabPane();
BorderPane borderPane = new BorderPane();
for (int i = 0; i < 5; i++) {
Tab tab = new Tab();
tab.setGraphic(new Circle(0, 0, 10));
HBox hbox = new HBox();
hbox.getChildren().add(new Label("Tab" + i));
hbox.setAlignment(Pos.CENTER);
tab.setContent(hbox);
tabPane.getTabs().add(tab);
}
// bind to take available space
borderPane.prefHeightProperty().bind(scene.heightProperty());
borderPane.prefWidthProperty().bind(scene.widthProperty());
borderPane.setCenter(tabPane);
root.getChildren().add(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Result:
I know its an old thread, but i didnt find a direct answer anywhere. So i thought of posting it some that it will be helpfull for some searching for it.
This is what i did to get a tab like firefox preferences screen.
Add the image to the tab with setGraphics and add the following code to the application css file. My image size was 48x48. So i went for height as 70.
.tab-label {
-fx-content-display: top;
}
.tab-pane {
-fx-tab-min-height: 70;
-fx-tab-max-height: 70;
}
How to add image directly from image url:
Tab tab = new Tab();
tab.setGraphic(buildImage("patch/to/image");
// Helper method to create image from image patch
private static ImageView buildImage(String imgPatch) {
Image i = new Image(imgPatch);
ImageView imageView = new ImageView();
//You can set width and height
imageView.setFitHeight(16);
imageView.setFitWidth(16);
imageView.setImage(i);
return imageView;
}

Categories