I want to click on one of three button on title of my internal window to change its color to black. but it some times works and sometimes does`nt work.
please look at my code an tell me whats wrong with it!?
I used javac 1.8u20 to compile and jre 1.9 to run...
if we use to or three layer of Pane inside of each other how the events handle? is there problem?
package core.windowManager;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.effect.DropShadow;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Screen;
import static javafx.scene.paint.Color.rgb;
/**
* Created by yn on 22/08/17.
* just declare a win not more ...
*/
public class win {
/**
* where we add the win to it in scene
*/
private final AnchorPane root;
/**
* just the title and a hashCode from instance will be the win ID
*/
private final String winId;
/**
* win pane contains title and content pane
*/
private final AnchorPane winPane = new AnchorPane();
/**
* title pane to add title label and some three [close-min-max] buttons maybe...
*/
private final AnchorPane titlePane = new AnchorPane();
/**
* where the content goes there ...
*/
private final AnchorPane content = new AnchorPane();
/**
* win title pane height
*/
private final int winTitlePaneH = 30;
/**
* three close-min-max buttons
*/
private final Circle closeShape = new Circle();
private final Circle minShape = new Circle();
private final Circle maxShape = new Circle();
/**
* initialize the win class with some important params
*
* #param root //where the win add to scene
* #param title // and String ID make by title+hashCode and used in windowManager
* #param winW //win width
* #param winH // win height
*/
public win(AnchorPane root, String title, int winW, int winH) {
//init some final vars
this.root = root;
this.winId = title + "--" + this.hashCode();
// make the winPane
winPane.setEffect(new DropShadow(20, rgb(0, 0, 0, 0.9)));
winPane.setPrefSize(winW, winH);
winPane.setStyle("-fx-background-color: #abcdef");
// put winPane center of scene
double screenW = Screen.getPrimary().getVisualBounds().getWidth();
double screenH = Screen.getPrimary().getVisualBounds().getHeight();
double deltaW = (screenW - winW) / 2;
double deltaH = (screenH - winH) / 2;
winPane.setLayoutX(deltaW);
winPane.setLayoutY(deltaH);
// put it to top on click
winPane.setOnMouseClicked(e -> {
winPane.toFront();
});
//make title and top of window ...
titlePane.setPrefHeight(winTitlePaneH);
AnchorPane.setTopAnchor(titlePane, 0.0);
AnchorPane.setLeftAnchor(titlePane, 0.0);
AnchorPane.setRightAnchor(titlePane, 0.0);
// make winPane draggable by titlePane
makeDragable(titlePane);
makeResizable(50);
// add close and min buttons to title pane
closeShape.setRadius(winTitlePaneH / 3);
closeShape.setFill(Color.RED); //red-yellow-green
closeShape.setOnMouseClicked(e -> {
closeShape.setFill(Color.BLACK);
e.consume();
});
//add min button to title pane
minShape.setRadius(winTitlePaneH / 3);
minShape.setFill(Color.YELLOW); //red-yellow-green
minShape.setOnMouseClicked(e -> {
minShape.setFill(Color.BLACK);
});
// add max button to title pane
maxShape.setRadius(winTitlePaneH / 3);
maxShape.setFill(Color.GREEN); //red-yellow-green
maxShape.setOnMouseClicked(e -> {
maxShape.setFill(Color.BLACK);
});
HBox bb = new HBox();
//bb.setBackground(new Background(new BackgroundFill(Color.BLACK, null, Insets.EMPTY)));
AnchorPane.setLeftAnchor(bb, 0d);
AnchorPane.setTopAnchor(bb, winTitlePaneH / 4.5);
AnchorPane.setBottomAnchor(bb, 0d);
bb.getChildren().addAll(closeShape, minShape, maxShape);
titlePane.getChildren().addAll(bb);
// add a label to show title
Label titleL = new Label(title);
titleL.setTextFill(Color.BLACK);
titleL.setAlignment(Pos.BASELINE_CENTER);
AnchorPane.setTopAnchor(titleL, 5.0);
AnchorPane.setLeftAnchor(titleL, 0.0);
AnchorPane.setRightAnchor(titleL, 0.0);
titlePane.getChildren().add(titleL);
titlePane.setBackground(new Background(new BackgroundFill(Color.web("#E2E0E2"), CornerRadii.EMPTY, Insets.EMPTY)));
winPane.getChildren().add(titlePane);
root.getChildren().add(winPane);
}
/**
* titlePane to drag and win move behavior
*
* #param what
*/
public void makeDragable(Node what) {
final Delta dragDelta = new Delta();
what.setOnMousePressed(mouseEvent -> {
dragDelta.x = winPane.getLayoutX() - mouseEvent.getScreenX();
dragDelta.y = winPane.getLayoutY() - mouseEvent.getScreenY();
//also bring to front when moving
winPane.toFront();
});
what.setOnMouseDragged(mouseEvent -> {
winPane.setLayoutX(mouseEvent.getScreenX() + dragDelta.x);
winPane.setLayoutY(mouseEvent.getScreenY() + dragDelta.y);
});
}
//current state
private boolean RESIZE_BOTTOM;
private boolean RESIZE_RIGHT;
public void makeResizable(double mouseBorderWidth) {
winPane.setOnMouseMoved(mouseEvent -> {
//local window's coordiantes
double mouseX = mouseEvent.getX();
double mouseY = mouseEvent.getY();
//window size
double width = winPane.boundsInLocalProperty().get().getWidth();
double height = winPane.boundsInLocalProperty().get().getHeight();
//if we on the edge, change state and cursor
if (Math.abs(mouseX - width) < mouseBorderWidth
&& Math.abs(mouseY - height) < mouseBorderWidth) {
RESIZE_RIGHT = true;
RESIZE_BOTTOM = true;
winPane.setCursor(Cursor.NW_RESIZE);
} else {
RESIZE_BOTTOM = false;
RESIZE_RIGHT = false;
winPane.setCursor(Cursor.DEFAULT);
}
});
winPane.setOnMouseDragged(mouseEvent -> {
//resize root
Region region = (Region) winPane;
//resize logic depends on state
if (RESIZE_BOTTOM && RESIZE_RIGHT) {
region.setPrefSize(mouseEvent.getX(), mouseEvent.getY());
} else if (RESIZE_RIGHT) {
region.setPrefWidth(mouseEvent.getX());
} else if (RESIZE_BOTTOM) {
region.setPrefHeight(mouseEvent.getY());
}
});
}
//just for encapsulation
private static class Delta {
double x, y;
}
}
Mouse click works perfectly fine on Circle in JavaFX. The problem with your code is that the label you add for the title is on top of your circles and catches the mouse clicks. You can only click the circles on the very bottom which made you think that it works "sometimes". Check the image here:
So you can solve it by making the label mouse transparent with:
titleL.setMouseTransparent(true);
or adding first the label and then the HBox with the circles.
For this kind of problems ScenicView comes really handy tool.
I use a HBox to group and layout the three buttons [close min max] and then toFront() it. and they goes over title pane and now events work correctly...
Related
I've managed to move the camera using mouse drag. However, the problem is that once I've moved the camera and released the mouse press, the camera returns back to the origin.
I want the camera to remain in the changed position instead so that when I use the mouse again to move the camera, it moves from that position to wherever instead of from the origin.
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Camera;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Sphere;
public class Main extends Application {
private static final int WIDTH = 900;
private static final int HEIGHT = 600;
//Tracks drag starting point for x and y
private double anchorX, anchorY;
#Override
public void start(Stage primaryStage) {
Sphere sphere = new Sphere(50);
Group group = new Group();
group.getChildren().add(sphere);
Camera camera = new PerspectiveCamera();
Scene scene = new Scene(group, WIDTH, HEIGHT);
scene.setFill(Color.SILVER);
scene.setCamera(camera);
sphere.setTranslateX(WIDTH / 2);
sphere.setTranslateY(HEIGHT / 2);
initMouseControl(scene, camera, primaryStage, sphere);
primaryStage.setTitle("Solar System Simulator");
primaryStage.setScene(scene);
primaryStage.show();
}
private void initMouseControl(Scene scene, Camera camera, Stage stage, Sphere sphere) {
scene.setOnMousePressed(event -> {
//Save start points
anchorX = event.getSceneX();
anchorY = event.getSceneY();
});
scene.setOnMouseDragged(event -> {
camera.setTranslateY(anchorY - event.getSceneY());
camera.setTranslateX(anchorX - event.getSceneX());
});
stage.addEventHandler(ScrollEvent.SCROLL, event -> {
sphere.setTranslateZ(sphere.getTranslateZ() + event.getDeltaY());
});
}
public static void main(String[] args) {
launch(args);
}
}
I've tried googling to find a solution but couldn't find much on camera movement with javafx
First of all, your scene does not have depth buffer enabled . These constructors enable 3d features in a scene instance Scene(Parent root,double width,double height,boolean depthBuffer) and Scene(Parent root,double width,double height,boolean depthBuffer,SceneAntialiasing antiAliasing) both with depthBufer boolean set to true. Second ,if you want to translate a node ; translate it getting its current coordinates and then add new coords . that will avoid reset coords . I divided the dragged result by 10 because it brought too high values
public class Main extends Application {
private static final int WIDTH = 900;
private static final int HEIGHT = 600;
//Tracks drag starting point for x and y
private double anchorX, anchorY;
#Override
public void start(Stage primaryStage) {
Sphere sphere = new Sphere(50);
Group group = new Group();
group.getChildren().add(sphere);
Camera camera = new PerspectiveCamera();
// for 3d Scene constructor must have zbuffer= true and SceneAntialiasing
Scene scene = new Scene(group, WIDTH, HEIGHT, true, SceneAntialiasing.BALANCED);
scene.setFill(Color.SILVER);
scene.setCamera(camera);
sphere.setTranslateX(WIDTH / 2);
sphere.setTranslateY(HEIGHT / 2);
initMouseControl(scene, camera, primaryStage, sphere);
primaryStage.setTitle("Solar System Simulator");
primaryStage.setScene(scene);
primaryStage.show();
}
private void initMouseControl(Scene scene, Camera camera, Stage stage, Sphere sphere) {
scene.setOnMousePressed(event -> {
//Save start points
anchorX = event.getSceneX();
anchorY = event.getSceneY();
});
// translating camera from its current coords pluss event
scene.setOnMouseDragged(event -> {
camera.setTranslateY(camera.getTranslateY() + ((anchorY - event.getSceneY()) / 10));
camera.setTranslateX(camera.getTranslateX() + ((anchorX - event.getSceneX()) / 10));
});
stage.addEventHandler(ScrollEvent.SCROLL, event -> {
sphere.setTranslateZ(sphere.getTranslateZ() + event.getDeltaY());
});
}
public static void main(String[] args) {
launch(args);
}
}
I don't know in how far Google can help you fix your logic problems. I did no further analysis of your code but at least each time when you press the mouse button your camera position is reset to zero by design. Your changes are not cumulative.
I am working in a Java desktop game where I am trying to display some panels composed by a JTable with a JScrollPane, which has only one column and several rows, containing the data I want to display. This panels are not visible when the application starts, because they are shown depending on the JButton pressed.
When I start my application, these panels are already added to the frame, but not visible. At the moment I try to display them by pressing the corresponding button, they are shown, but not all the rows are visible. If I press the button again, the panel is hidden, and if I press again the button to display it again, now it is shown with all its rows visible.
For some reason, just by setting its visibility to true -> false -> true by pressing the corresponding button, the JTable of the panel displays all its rows, but when I display it the first time, it doesn't... I need to do it twice.
To check this, I try to set the panel visibility to true -> false -> true when the application starts programmatically, but it doesn't works. It only works if I press the button that displays the panel (remember, twice, the first time it doesn't shows all the rows, the second time it does). Also, I've checked by debugging if, at the moment I set the panel visibility to true the first time, it has all the data inside, and it is correct, so it is something just only graphic, because the data is there. Also I realized that by scrolling with the mouse the first time I show the panel, it shows all the data, so I guess it must be refreshing something when I scroll, or when I show the panel for the second time I press the corresponding button.
I've tried to revalidate() and repaint() the table, to fireTableDataChanged() and fireTableStructureChanged() the model of the table. Also I've created a thread which starts at the same time the application starts, which sets the visibility of these panels to true and false at the beginning, and revalidating the tables, just to try to see what could be happening when showing/hidding the panels.
After create this thread, I realized that if I set the visibility of this panels to true from the beginning, I see how the UI thread "refresh" the panels in some way, as I can see them uncomplete, and after complete, in a split second. But after, I have to close them manually, because I cannot control (I tried) the time needed to see them filled before close them.
Here is a picture of the panel when I setVisible(true) one of these panels:
And here is the picture of the same panel when I hide and show it again (manually), without perform any other actions:
Here is the code of the table model set in the JTable of the panel:
/**
* Class to create the model instance for the selector panel list
*/
public class CustomTableModelForPredefinedMessagesSelectorKeyboard extends DefaultTableModel {
private ArrayList selectedList = new ArrayList<>();
public CustomTableModelForPredefinedMessagesSelectorKeyboard() {
this.addColumn(LanguageConfiguration.getLanguageSelected().getString("selection"));
//setListType(ListType.PREDEFINED_MESSAGES);
setListType();
}
/**
* Function to get the size of the list
*
* #return the list size
*/
//#Override
public int getSize() {
return selectedList.size();
}
/**
* Function to get the element of a specific index from the list
*
* #param index index of the message to be retrieved
* #return the message text of the corresponding index
*/
public Object getElementAt(int index) {
return selectedList.get(index).toString();
}
public ArrayList getSelectedList() {
return selectedList;
}
public void setSelectedList(ArrayList selectedList) {
this.selectedList = selectedList;
}
/**
* Function to set the list of the selector panel according to the list type selected
*/
#SuppressWarnings("unchecked")
public void setListType() {
selectedList.add("selectorPanel_PredefMsg_EnemyOnSight");
selectedList.add("selectorPanel_PredefMsg_GreenAlert");
selectedList.add("selectorPanel_PredefMsg_HowAreYou");
selectedList.add("selectorPanel_PredefMsg_InminentAttack");
selectedList.add("selectorPanel_PredefMsg_No");
selectedList.add("selectorPanel_PredefMsg_Positioned");
selectedList.add("selectorPanel_PredefMsg_Ready");
selectedList.add("selectorPanel_PredefMsg_RedAlert");
selectedList.add("selectorPanel_PredefMsg_ReturnToBase");
selectedList.add("selectorPanel_PredefMsg_TargetOutOfReach");
selectedList.add("selectorPanel_PredefMsg_WaitingOrders");
selectedList.add("selectorPanel_PredefMsg_WeHaveAProblem");
selectedList.add("selectorPanel_PredefMsg_YellowAlert");
selectedList.add("selectorPanel_PredefMsg_Yes");
for (Object string : selectedList)
this.addRow(new Object[]{LanguageConfiguration.getLanguageSelected().getString((String) string)});
if (SwingInterfaceSubJPanels.getPredefinedMessagesSelectorJPanel() != null)
for (Component component : SwingInterfaceSubJPanels.getPredefinedMessagesSelectorJPanel().getComponents())
if (component instanceof JScrollPane) {
JScrollPane messagesListScrollPane = (JScrollPane) component;
JViewport viewport = messagesListScrollPane.getViewport();
preselectFirstElement((CustomJTable) viewport.getView());
break;
}
}
/**
* Function to pre-select the firs item of the list in the selector list
*
* #param customJTable the table where the selection will be performed
*/
private void preselectFirstElement(CustomJTable customJTable) {
if (customJTable.getRowCount() > 0)
customJTable.getSelectionModel().setSelectionInterval(0, 0);
}
}
And here is the code of the panel:
/**
* Class to create and manage a selector keyboard
*/
public class PredefinedMessagesSelectorKeyboard extends JPanel {
/**
* Color of shadow
*/
private final Color shadowColor;
/**
* Double values for Horizontal and Vertical radius of corner arcs
*/
private final Dimension arcs;
/**
* Variables to get the coordinates of the mouse on mouse click event over the jpanel
*/
private int xJPanelCoord, yJPanelCoord;
Component componentToManage;
SelectorConfirmationController.Type selectorPanelType;
/**
* Constructor to create the selector panel structure and add its components
*/
public PredefinedMessagesSelectorKeyboard() {
DisplayMode displayMode = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode();
double screenWidth = displayMode.getWidth();
double screenHeight = displayMode.getHeight();
this.setPreferredSize(new Dimension((int) Math.round(screenWidth * 0.35), (int) Math.round(screenHeight * 0.6)));
//this.setBackground(new java.awt.Color(5, 122, 122));
this.setBackground(new Color(150, 150, 150));
addSelectorPanelComponents();
setVisible(false);
setOpaque(false);
makeJPanelDraggable();
shadowColor = Color.black;
arcs = new Dimension(100, 100);
}
/**
* Function to add the components of the JPanel
*/
private void addSelectorPanelComponents() {
this.setLayout(new GridBagLayout());
addSelectorPanelJTable();
addSelectorPanelButtons();
addSelectorPanelCloseJButton();
}
/**
* Function to add the panel with the elements available for selection
*/
private void addSelectorPanelJTable() {
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.gridheight = 2;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.fill = GridBagConstraints.BOTH;
gridBagConstraints.insets = new Insets(40, 20, 20, 0);
CustomTableModelForPredefinedMessagesSelectorKeyboard customTableModelForPredefinedMessagesSelectorPanelList = new CustomTableModelForPredefinedMessagesSelectorKeyboard();
CustomJTable predefinedMessagesselectorPanelJTable = new CustomJTable(customTableModelForPredefinedMessagesSelectorPanelList);
predefinedMessagesselectorPanelJTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
predefinedMessagesselectorPanelJTable.setName("predefinedMessagesSelectorPanelJTable");
JScrollPane selectorJTableJScrollPane = new JScrollPane(predefinedMessagesselectorPanelJTable);
selectorJTableJScrollPane.getViewport().setBackground(new Color(24, 27, 34));
add(selectorJTableJScrollPane, gridBagConstraints);
}
/**
* Function to add the close panel button and the selection confirmation button
*/
private void addSelectorPanelButtons() {
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 4;
gridBagConstraints.gridy = 1;
gridBagConstraints.insets = new Insets(20, 20, 0, 20);
JButton selectPreviousButton = createScaledSwingShapedButton("/images/boton_popup_panel_selector_anterior.png",
"/images/boton_popup_panel_selector_anterior_seleccionado.png");
selectPreviousButton.setName("selectorPanelPreviousButton");
selectPreviousButton.setToolTipText(LanguageConfiguration.getLanguageSelected().getString("tooltipTextPreviousButton"));
selectPreviousButton.addActionListener(e -> {
for (Component component : getComponents())
if (component instanceof JScrollPane) {
JScrollPane messagesListScrollPane = (JScrollPane) component;
JViewport viewport = messagesListScrollPane.getViewport();
selectPrevious((CustomJTable) viewport.getView());
break;
}
});
JButton confirmSelectedButton = createScaledSwingShapedButton("/images/boton_popup_panel_selector_confirmar.png",
"/images/boton_popup_panel_selector_confirmar_seleccionado.png");
confirmSelectedButton.setName("selectorPanelConfirmButton");
confirmSelectedButton.setToolTipText(LanguageConfiguration.getLanguageSelected().getString("tooltipTextConfirmButton"));
confirmSelectedButton.addActionListener(new SelectorConfirmationController(componentToManage, selectorPanelType));
JButton selectNextButton = createScaledSwingShapedButton("/images/boton_popup_panel_selector_siguiente.png",
"/images/boton_popup_panel_selector_siguiente_seleccionado.png");
selectNextButton.setName("selectorPanelNextButton");
selectNextButton.setToolTipText(LanguageConfiguration.getLanguageSelected().getString("tooltipTextNextButton"));
selectNextButton.addActionListener(e -> {
for (Component component : getComponents()){
if (component instanceof JScrollPane){
JScrollPane messagesListScrollPane = (JScrollPane) component;
JViewport viewport = messagesListScrollPane.getViewport();
selectNext((CustomJTable) viewport.getView());
break;
}
}
});
JPanel lateralButtonsPanel = new JPanel();
lateralButtonsPanel.setLayout(new BoxLayout(lateralButtonsPanel, BoxLayout.Y_AXIS));
lateralButtonsPanel.setOpaque(false);
lateralButtonsPanel.setName("lateralButtonsPanel");
lateralButtonsPanel.add(selectPreviousButton);
lateralButtonsPanel.add(confirmSelectedButton);
lateralButtonsPanel.add(selectNextButton);
add(lateralButtonsPanel, gridBagConstraints);
}
/**
* Function to create and add the close panel button to the panel
*/
private void addSelectorPanelCloseJButton() {
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 4;
gridBagConstraints.gridy = 0;
//gridBagConstraints.anchor = GridBagConstraints.NORTHEAST;
gridBagConstraints.insets = new Insets(20, 20, 0, 20);
JButton closeMessagesConsoleButton = createScaledSwingShapedButton("/images/boton_consola_mensajes_2.png",
"/images/boton_consola_mensajes_2_seleccionado.png");
closeMessagesConsoleButton.setName("selectorPanelCloseButton");
closeMessagesConsoleButton.setToolTipText(LanguageConfiguration.getLanguageSelected().getString("tooltipTextClosePanelButton"));
//closeMessagesConsoleButton.addActionListener(e -> setVisible(false));
closeMessagesConsoleButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
/*if (getComponentToManage().getName().equals("messageBoxTable"))*/
SwingInterfaceSubJPanels.getMainScreenUpperInformationLabel().setVisible(false);
setVisible(false);
}
});
add(closeMessagesConsoleButton, gridBagConstraints);
}
/**
* Function to select the next item of the list in the selector list
* #param customJTable the table where the selection will be performed
*/
private void selectPrevious(CustomJTable customJTable){
if (customJTable.getSelectedRow() > 0) {
customJTable.getSelectionModel().setSelectionInterval(customJTable.getSelectedRow() - 1, customJTable.getSelectedRow() - 1);
customJTable.scrollRectToVisible(new Rectangle(customJTable.getCellRect(customJTable.getSelectedRow()-1, 0, true)));
}
}
/**
* Function to select the next item of the list in the selector list
* #param customJTable the table where the selection will be performed
*/
private void selectNext(CustomJTable customJTable){
if (customJTable.getSelectedRow() >= 0 && customJTable.getSelectedRow() < customJTable.getRowCount()-1) {
customJTable.getSelectionModel().setSelectionInterval(customJTable.getSelectedRow() + 1, customJTable.getSelectedRow() + 1);
customJTable.scrollRectToVisible(new Rectangle(customJTable.getCellRect(customJTable.getSelectedRow()+1, 0, true)));
}
}
public Component getComponentToManage() {
return componentToManage;
}
public void setComponentToManage(Component componentToManage) {
this.componentToManage = componentToManage;
}
public SelectorConfirmationController.Type getSelectorPanelType() {
return selectorPanelType;
}
public void setSelectorPanelType(SelectorConfirmationController.Type selectorPanelType) {
this.selectorPanelType = selectorPanelType;
}
/**
* Function to create and return a scaled swing shaped button with its corresponding images
*
* #param buttonImage button image when the button is not pressed
* #param buttonPressedImage button image when the button is pressed
* #return the button already built
*/
private JButton createScaledSwingShapedButton(String buttonImage, String buttonPressedImage) {
Image scaledImage;
try {
SwingShapedButton swingShapedButton = new SwingShapedButton();
JButton newButton = (JButton) swingShapedButton.createComponent(SwingShapedButton.ButtonShape.ROUND);
Image imageButton = ImageIO.read(getClass().getResource(buttonImage));
scaledImage = scaleImage(imageButton);
newButton.setIcon(new ImageIcon(scaledImage));
Image imageButtonPressed = ImageIO.read(getClass().getResource(buttonPressedImage));
scaledImage = scaleImage(imageButtonPressed);
newButton.setPressedIcon(new ImageIcon(scaledImage));
return newButton;
} catch (IllegalArgumentException | IOException e) {
e.printStackTrace();
DataBase.database.insertSystemErrorLog(Thread.currentThread().getStackTrace()[1].getMethodName(), e);
return (JButton) new SwingShapedButton().createComponent(SwingShapedButton.ButtonShape.ROUND);
}
}
/**
* Function to scale the images of the buttons according to the screen resolution
*
* #param imageButton the image to be set to the button
* #return the image resized
*/
private Image scaleImage(Image imageButton) {
return imageButton.getScaledInstance((int) Math.round(((BufferedImage) imageButton).getWidth() * InterfaceControlsManager.getWidth_percentage()),
(int) Math.round(((BufferedImage) imageButton).getHeight() * InterfaceControlsManager.getHeight_percentage()),
Image.SCALE_SMOOTH);
}
/**
* Function to allow the panel to be draggable over the frame
*/
private void makeJPanelDraggable() {
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
xJPanelCoord = e.getX();
yJPanelCoord = e.getY();
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
int x = e.getXOnScreen() - xJPanelCoord;
int y = e.getYOnScreen() - yJPanelCoord;
setLocation(x, y);
}
});
}
/**
* Function to set the appearance of the panel
*
* #param g the graphics to be set
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
/*
Distance between shadow border and opaque panel border
*/
int shadowGap = 5;
int shadowAlpha = 10;
Color shadowColorA = new Color(shadowColor.getRed(),
shadowColor.getGreen(), shadowColor.getBlue(), shadowAlpha);
Graphics2D graphics = (Graphics2D) g;
//Sets antialiasing if HQ.
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//Draws shadow borders if any.
int strokeSize = 1;
boolean shady = true;
graphics.setColor(shadowColorA);
int shadowOffset = 4;
graphics.fillRoundRect(
shadowOffset,// X position
shadowOffset,// Y position
width - strokeSize - shadowOffset, // width
height - strokeSize - shadowOffset, // height
arcs.width, arcs.height);// arc Dimension
//Draws the rounded opaque panel with borders.
graphics.setColor(getBackground());
graphics.fillRoundRect(0, 0, width - shadowGap,
height - shadowGap, arcs.width, arcs.height);
graphics.setColor(getForeground());
graphics.setStroke(new BasicStroke(strokeSize));
graphics.drawRoundRect(0, 0, width - shadowGap,
height - shadowGap, arcs.width, arcs.height);
//Sets strokes to default, is better.
graphics.setStroke(new BasicStroke());
}
}
Can someone explain to me why I see this effect in the panel? There is some reason why the JTable doesn't show its data the first time I setVisible(true) the panel, but it is displayed properly the second time? Could be something with the UI, or maybe I am not realizing about something here?
Thanks for any comment you can give me!
I'm building an small application with JavaFX to visualize circuit boards. The application includes the possibility of scaling and shifting. When I display small texts, they are distorted, as you can see on the screenshots below. Only when I have zoomed far enough into the scene will the text be displayed correctly again. Does anyone have a solution to this problem? I included the example code from which the screenshots are taken.
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
addMouseEventHandler(root);
Pane pane1 = new Pane();
Text text1 = new Text(400, 400, "Text 1");
text1.setFont(new Font(5));
pane1.getChildren().addAll(text1);
root.getChildren().add(pane1);
primaryStage.setTitle("Text scaling problem");
primaryStage.setScene(new Scene(root, 800, 800));
primaryStage.show();
}
private void addMouseEventHandler(Pane root) {
// add scroll handling to zoom in and out
root.setOnScroll((event) ->
{
double factor = event.getDeltaY() > 0 ? 1.1 : 0.9;
root.setScaleX(root.getScaleX() * factor);
root.setScaleY(root.getScaleY() * factor);
});
// add drag handling
root.setOnMousePressed((mouseEvent) ->
{
startMouseX = mouseEvent.getSceneX();
startMouseY = mouseEvent.getSceneY();
startTranslationX = root.getTranslateX();
startTranslationY = root.getTranslateY();
});
root.setOnMouseDragged((mouseEvent) ->
{
double movedX = startMouseX - mouseEvent.getSceneX();
double movedY = startMouseY - mouseEvent.getSceneY();
double transX = startTranslationX - movedX;
double transY = startTranslationY - movedY;
root.setTranslateX(transX);
root.setTranslateY(transY);
});
}
correct text
vs
distorted text
The correct text is scrolled one more step than the distorted.
i am not sure if anything can be done about scaling of text inside of the text node. but if you separate the characters in different text nodes it will keep its shape. you might wanna try Canvas and manually draw using GraphicsContext2D.
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class DistortedText extends Application{
private double startMouseX, startMouseY;
private double startTranslationX, startTranslationY;
#Override
public void start(Stage primaryStage) {
HBox box = new HBox(new Text("T"),new Text("e"),new Text("x"),new Text("T"),new Text(" "),new Text("1"));
Pane root = new Pane(box);
addMouseEventHandler(root,box);
primaryStage.setTitle("Text scaling problem");
primaryStage.setScene(new Scene(root, 800, 800));
primaryStage.show();
}
private void addMouseEventHandler( Pane container, Node node) {
// add scroll handling to zoom in and out
container.setOnScroll((event) ->
{
double factor = event.getDeltaY() > 0 ? 1.1 : 0.9;
node.setScaleX(node.getScaleX() * factor);
node.setScaleY(node.getScaleY() * factor);
});
// add drag handling
container.setOnMousePressed((mouseEvent) ->
{
startMouseX = mouseEvent.getX();
startMouseY = mouseEvent.getY();
startTranslationX = node.getTranslateX();
startTranslationY = node.getTranslateY();
});
container.setOnMouseDragged((mouseEvent) ->
{
double movedX = startMouseX - mouseEvent.getX();
double movedY = startMouseY - mouseEvent.getY();
double transX = startTranslationX - movedX;
double transY = startTranslationY - movedY;
node.setTranslateX(transX);
node.setTranslateY(transY);
});
}
public static void main(String[] args){
launch( args);
}
}
I have a program in which it displays 3 random hieroglyphic images on the screen. I have added a "Refresh" button in order to refresh the hieroglyphic images. When I click the button, the images refresh and randomize properly. After the button has been clicked for the first time however, it disappears. I am almost certain it has something to do with my pane.getChildren().clear(); line, but I cant seem to figure it out. Any tips or advice?
If I have posted incorrectly or without using proper guidelines, I apologize. This is one of my first posts.
Here is my code:
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.control.Button;
import javafx.geometry.Pos;
import javafx.scene.layout.HBox;
public class Lab6a extends Application {
#Override
public void start(Stage myStage) {
//Create an HBox layout.
HBox hBox1 = new HBox();
//Set alignment.
hBox1.setAlignment(Pos.CENTER);
getRandomHieroglyphic(hBox1);
//Create a Refresh button.
Button refresh = new Button("Refresh");
refresh.setOnAction(e -> getRandomHieroglyphic(hBox1));
hBox1.getChildren().add(refresh);
//Set the title for the second window.
myStage.setTitle("Random Hieroglyphics with Refresh");
//Create a scene for the window.
Scene myScene = new Scene(hBox1, 400, 400);
//Place the scene in the second window.
myStage.setScene(myScene);
//Show the stage.
myStage.show();
}
public void getRandomHieroglyphic(HBox pane) {
pane.getChildren().clear();
//Create random generators to get a random image
int randomInt1 = (int) (Math.random() * 9) + 1;
int randomInt2 = (int) (Math.random() * 9) + 1;
int randomInt3 = (int) (Math.random() * 9) + 1;
//Create paths for the images to be called
String path1 = "Image/Hieroglyphics/h" + randomInt1 + ".png";
String path2 = "Image/Hieroglyphics/h" + randomInt2 + ".png";
String path3 = "Image/Hieroglyphics/h" + randomInt3 + ".png";
//Add the images into the pane
pane.getChildren().add(new ImageView (path1));
pane.getChildren().add(new ImageView (path2));
pane.getChildren().add(new ImageView (path3));
}
public static void main(String[] args) {
launch(args);
}
}
clear() removes all children from the HBox including the Button.
You've got 3 ImageViews and want to keep the number of ImageViews constant. This means you should not replace them, but replace the images they contain instead. Furthermore you should avoid reloading the images and load all 9 images at the start:
public class Lab6a extends Application {
private Image[] images;
private final Random random = new Random();
#Override
public void start(Stage myStage) {
// load all hieroglyphs
images = new Image[9];
for (int i = 0; i < images.length; i++) {
images[i] = new Image("Image/Hieroglyphics/h" + (i+1) + ".png");
}
// store all imageviews in array
final ImageView[] imageViews = Stream.generate(ImageView::new).limit(3).toArray(ImageView[]::new);
// set initial images
getRandomHieroglyphic(imageViews);
...
hBox1.getChildren().add(refresh);
for (ImageView iv : imageViews) {
hBox1.getChildren().add(iv);
}
...
refresh.setOnAction(e -> getRandomHieroglyphic(imageViews));
...
}
public void getRandomHieroglyphic(ImageView[] imageViews) {
for (ImageView iv : imageViews) {
iv.setImage(images[random.nextInt(images.length)]);
}
}
You're right. You Hbox has 4 cildren initially, 3 images and the button. you should add the button too in the getRandomHieroglyphic() method.
But the better way was separate the button from the images. Best is this done by Adding a BorderPane to the Scene, and put the HBox with the images only in the center and the button in the bottom area.
#Override
public void start(Stage myStage) {
HBox hBox1 = new HBox();
hBox1.setAlignment(Pos.CENTER);
getRandomHieroglyphic(hBox1);
Button refresh = new Button("Refresh");
refresh.setOnAction(e -> getRandomHieroglyphic(hBox1));
BorderPane borderPane = new BorderPane();
borderPane.getBottom().add(refresh);
myStage.setTitle("Random Hieroglyphics with Refresh");
Scene myScene = new Scene(borderPane, 400, 400);
Edit-Answer:
You can check Fabian's answer and also this library (https://github.com/goxr3plus/JFXCustomCursor)
Actual Question
I want to create a cursor which is fading out in JavaFX so for that i am using a WritableImage and i am continuously reading pixels from the original Image and writing them to a new WritableImage.Then i set a custom cursor to the Scene using ImageCursor(writableImage),below is the full code(give it a try).
The problem is that a get black pixels where transparent pixels are expected.
Note that all the below classes have to be in package sample.
Code(Main):
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Main extends Application {
FadingCursor fade = new FadingCursor();
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setWidth(300);
primaryStage.setHeight(300);
Scene scene = new Scene(new FlowPane());
primaryStage.setScene(scene);
fade.startFade(scene,100);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Code(FadingCursor)(Edited):
package sample;
import java.util.concurrent.CountDownLatch;
import javafx.application.Platform;
import javafx.scene.ImageCursor;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
public class FadingCursor {
private int counter;
private Image cursorImage;
/**
* Change the image of the Cursor
*
* #param image
*/
public void setCursorImage(Image image) {
this.cursorImage = image;
}
/**
* Start fading the Cursor
*
* #param scene
*/
public void startFade(Scene scene, int millisecondsDelay) {
// Create a Thread
new Thread(() -> {
// Keep the original image stored here
Image image = new Image(getClass().getResourceAsStream("fire.png"), 64, 64, true, true);
PixelReader pixelReader = image.getPixelReader();
// Let's go
counter = 10;
for (; counter >= 0; counter--) {
CountDownLatch count = new CountDownLatch(1);
Platform.runLater(() -> {
// Create the fading image
WritableImage writable = new WritableImage(64, 64);
PixelWriter pixelWriter = writable.getPixelWriter();
// Fade out the image
for (int readY = 0; readY < image.getHeight(); readY++) {
for (int readX = 0; readX < image.getWidth(); readX++) {
Color color = pixelReader.getColor(readX, readY);
// Now write a brighter color to the PixelWriter.
// -------------------------Here some way happens
// the problem------------------
color = new Color(color.getRed(), color.getGreen(), color.getBlue(), (counter / 10.00) * color.getOpacity());
pixelWriter.setColor(readX, readY, color);
}
}
System.out.println("With counter:"+counter+" opacity is:" + writable.getPixelReader().getColor(32, 32).getOpacity());
scene.setCursor(new ImageCursor(writable));
count.countDown();
});
try {
// Wait JavaFX Thread to change the cursor
count.await();
// Sleep some time
Thread.sleep(millisecondsDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
The image(needs to be downloaded)(Right Click ->Save Image as...):
You set the opacity of every pixel to a value only depending on the loop variable here:
color = new Color(color.getRed(), color.getGreen(), color.getBlue(), counter / 10.00);
For transparent pixels (opacity = 0) you actually increase the opacity making the values stored in the other channels (in this case 0 / black) visible. You need to make sure transparent pixels remain transparent, which usually is done like this:
color = new Color(color.getRed(), color.getGreen(), color.getBlue(), (counter / 10.00) * color.getOpacity());
Alternatively you could use deriveColor:
color = color.deriveColor(0, 1, 1, counter / 10d);
Edit
For some reason ImageCursor doesn't seem to like a completely transparent image. You can check that this works, if at least one pixel is not completely transparent by adding
pixelWriter.setColor(0, 0, new Color(0, 0, 0, 0.01));
After the for loops writing the image.
To fix this you could simply use Cursor.NONE instead of an ImageCursor with a fully transparent image:
for (; counter >= 1; counter--) {
...
}
Platform.runLater(() -> scene.setCursor(Cursor.NONE));
Alternative without the need to recreate the image/cursor
You could simulate the cursor yourself by moving a image Across the root of the Scene. This won't make the image show up beyond the bounds of the Scene, but you can apply animations to the ImageView for fading instead of modifying the opacity of each pixel manually...
public class CursorSimulator {
private final FadeTransition fade;
public CursorSimulator(Image image, Scene scene, ObservableList<Node> rootChildrenWriteable, double hotspotX, double hotspotY) {
ImageView imageView = new ImageView(image);
imageView.setManaged(false);
imageView.setMouseTransparent(true);
fade = new FadeTransition(Duration.seconds(2), imageView);
fade.setFromValue(0);
fade.setToValue(1);
// keep image on top
rootChildrenWriteable.addListener((Observable o) -> {
if (imageView.getParent() != null
&& rootChildrenWriteable.get(rootChildrenWriteable.size() - 1) != imageView) {
// move image to top, after changes are done...
Platform.runLater(() -> imageView.toFront());
}
});
scene.addEventFilter(MouseEvent.MOUSE_ENTERED, evt -> {
rootChildrenWriteable.add(imageView);
});
scene.addEventFilter(MouseEvent.MOUSE_EXITED, evt -> {
rootChildrenWriteable.remove(imageView);
});
scene.addEventFilter(MouseEvent.MOUSE_MOVED, evt -> {
imageView.setLayoutX(evt.getX() - hotspotX);
imageView.setLayoutY(evt.getY() - hotspotY);
});
scene.setCursor(Cursor.NONE);
}
public void fadeOut() {
fade.setRate(-1);
if (fade.getStatus() != Animation.Status.RUNNING) {
fade.playFrom(fade.getTotalDuration());
}
}
public void fadeIn() {
fade.setRate(1);
if (fade.getStatus() != Animation.Status.RUNNING) {
fade.playFromStart();
}
}
}
#Override
public void start(Stage primaryStage) {
Button btn = new Button("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
System.out.println("Hello World!");
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 500, 500);
Image image = new Image("http://i.stack.imgur.com/OHj1R.png");
CursorSimulator simulator = new CursorSimulator(image, scene, root.getChildren(), 32, 50);
scene.setOnMouseClicked(new EventHandler<MouseEvent>() {
private boolean fadeOut = true;
#Override
public void handle(MouseEvent event) {
if (fadeOut) {
simulator.fadeOut();
} else {
simulator.fadeIn();
}
fadeOut = !fadeOut;
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
The reason of this question was to create a kind of cursor that can be
modified.For example here i wanted to make it had a fade effect.For
future users who want to create custom cursors i have created a
library on github and i will show some code here:
https://github.com/goxr3plus/JFXCustomCursor
Code:
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
/**
* This class allows you to set as a Cursor in a JavaFX Scene,whatever you want
* ,even a video!. <br>
* <br>
* <b>What you have to do is create a basic layout,for example:</b><br>
* #-->A BorderPane which contains a MediaView,<br>
* #-->A StackPane which contains an animated ImageView,<br>
* #-->A Pane which contains an animated Rectangle or something more complex
* etc..)<br>
*
* <br>
* <br>
* The options are unlimited!
*
* #author GOXR3PLUS
* #param <T>
* #Version 1.0
*/
public class JFXCustomCursor {
private SimpleIntegerProperty hotSpotX = new SimpleIntegerProperty();
private SimpleIntegerProperty hotSpotY = new SimpleIntegerProperty();
private Scene scene;
private Pane sceneRoot;
private Pane content;
private EventHandler<MouseEvent> eventHandler1;
private EventHandler<MouseEvent> eventHandler2;
private EventHandler<MouseEvent> eventHandler3;
/**
* Constructor
*
* #param scene
* The Scene of your Stage
* #param sceneRoot
* The Root of your Stage Scene
* #param content
* The content of the JFXCustomCursor class
* #param hotspotX
* Represents the location of the cursor inside the content on X
* axis
* #param hotspotY
* Represents the location of the cursor inside the content on Y
* axis
*/
public JFXCustomCursor(Scene scene, Pane sceneRoot, Pane content, int hotspotX, int hotspotY) {
// Go
setRoot(scene, sceneRoot, content, hotspotX, hotspotY);
}
/**
* This method changes the content of the JFXCustomCursor
*
* #param scene
* The Scene of your Stage
* #param sceneRoot
* The Root of your Stage Scene
* #param content
* The content of the JFXCustomCursor class
* #param hotspotX
* Represents the location of the cursor inside the content on X
* axis
* #param hotspotY
* Represents the location of the cursor inside the content on Y
* axis
*/
public void setRoot(Scene scene, Pane sceneRoot, Pane content, int hotSpotX, int hotSpotY) {
// Keep them in case of unRegister-reRegister
unRegister(); // has to be called before the below happens
this.scene = scene;
this.sceneRoot = sceneRoot;
this.content = content;
// hot spots
this.hotSpotX.set(hotSpotX);
this.hotSpotX.set(hotSpotY);
// cursor container
content.setManaged(false);
content.setMouseTransparent(true);
// Keep the Content on the top of Scene
ObservableList<Node> observable = sceneRoot.getChildren();
observable.addListener((Observable osb) -> {
if (content.getParent() != null && observable.get(observable.size() - 1) != content) {
// move the cursor on the top
Platform.runLater(content::toFront);
}
});
if (!observable.contains(content))
observable.add(content);
// Add the event handlers
eventHandler1 = evt -> {
if (!sceneRoot.getChildren().contains(content))
observable.add(content);
};
eventHandler2 = evt -> observable.remove(content);
eventHandler3 = evt -> {
content.setLayoutX(evt.getX() - hotSpotX);
content.setLayoutY(evt.getY() - hotSpotY);
};
scene.addEventFilter(MouseEvent.MOUSE_ENTERED, eventHandler1);
scene.addEventFilter(MouseEvent.MOUSE_EXITED, eventHandler2);
scene.addEventFilter(MouseEvent.MOUSE_MOVED, eventHandler3);
}
/**
* Unregisters the CustomCursor from the Scene completely
*/
public void unRegister() {
if (scene != null) {
sceneRoot.getChildren().remove(content);
scene.removeEventFilter(MouseEvent.MOUSE_ENTERED, eventHandler1);
scene.removeEventFilter(MouseEvent.MOUSE_EXITED, eventHandler2);
scene.removeEventFilter(MouseEvent.MOUSE_MOVED, eventHandler3);
}
}
/**
* Re register the CustomCursor to the Scene,<b>this method is
* experimental(use with caution!)</b>
*/
public void reRegister() {
if (scene != null)
setRoot(scene, sceneRoot, content, hotSpotX.get(), hotSpotY.get());
}
public SimpleIntegerProperty hotSpotXProperty() {
return hotSpotX;
}
public SimpleIntegerProperty hotSpotYProperty() {
return hotSpotY;
}
}