I am trying to build a simple planner app using JavaFX. My current goal is to be able to:
click on a panel of the calendar (already implemented)
type in a task, hit enter and have it show up as a Label (already implemented)
click on the currently placed labels and remove them from the calendar. (issue)
Step 3 is where I am having most trouble. I am confident that I am setting up my mouse event for the label correctly but when I click on one of the labels it runs the mouse event for the panel. I need a way to override the pane's mouse event so I can use the labels mouse event, but I'm not too sure how to go about that. Any feedback would be great!
this.setOnMouseClicked(e ->
{
TextField field = new TextField();
this.getChildren().add(field);
//sets field as a label
field.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent key) {
KeyCode k = key.getCode();
if ((k.equals(KeyCode.ENTER))) {
Label lab = new Label(field.getText());
getChildren().add(lab);
getChildren().remove(field);
}
}
});
//removes textfield and label
field.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent ke) {
KeyCode kc = ke.getCode();
if ((kc.equals(KeyCode.ESCAPE))) {
getChildren().remove(field);
}
}
});
});
if(lab != null)
{
lab.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
setStyle("-fx-background-color: #00FF00;");
}
});
}
Related
I need to know whether a certain key is down while performing a drag & drop operation.
So I tried to use setOnKeyPressed / setOnKeyReleased of a Scene with a combination of HashMap, but I have a problem with this approach:
Imagine a scenario that one drags & drops a TableView item to somewhere while holding Control down. Now if I display a dialog at the end of the drop, while still holding Control down, the setOnKeyReleased is never called with this approach... as the Dialog is the one receiving the key released event.
How could I fix this?
Hope I understand your question here is a possible solution(work with any key):
public class Main extends Application {
SimpleBooleanProperty isKeyPress = new SimpleBooleanProperty(false);
#Override
public void start(Stage primaryStage) throws Exception{
Parent window = new VBox();
((VBox) window).getChildren().add(new Label("example of small window:"));
primaryStage.setTitle("example");
Scene scene=new Scene(window);
primaryStage.setScene(scene);
primaryStage.show();
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
System.out.println("Press");
isKeyPress.set(true);
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Information Dialog");
alert.setHeaderText(null);
alert.setContentText("I have a great message for you!");
Scene alertScene = alert.getDialogPane().getScene();
alertScene.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
System.out.println("Released on dialog");
isKeyPress.set(false);
}
});
alert.showAndWait();
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
System.out.println("Released");
isKeyPress.set(false);
}
});
}
public static void main(String[] args) {
launch(args);
}
}
output exmple:
Press
Released on dialog
From your comment the goal is to change the behavior of the drag and drop depending on whether or not Ctrl is down. When it is do a copy operation, otherwise do a move operation. You do not need to deal with KeyEvents to implement this behavior. Instead, you would determine whether to copy or move in the onDragDetected handler. The onDragDetected handler uses a MouseEvent which has methods for querying the status of modifier keys—such as isControlDown(). Using this, we can specify what transfer modes are allowed based on the modifier keys.
Node node = ...;
node.setOnDragDetected(event -> {
Dragboard board;
if (event.isControlDown()) {
board = node.startDragAndDrop(TransferMode.COPY);
} else {
board = node.startDragAndDrop(TransferMode.MOVE);
}
// add contents to Dragboard
});
Note it may be more cross-platform to use isShortcutDown().
I have a game that displays an archer and I am trying to set it up so that when my button is pressed, the user is then allowed to click anywhere on the screen and then a new archer would be set to where the click happen. My code currently allows me to click the screen and set a new archer regardless of whether the button was pressed or not. Could someone explain what is wrong becuase I though MouseEvent would occur on the scene once the button was pressed.
myButton.setOnMouseClicked(
new EventHandler<MouseEvent>()
{
public void handle(MouseEvent e)
{
gc.setFill(color);
gc.fillRect(0,0,width,height);
scene.setOnMouseClicked(new EventHandler<MouseEvent>()
{
public void handle(MouseEvent e)
{
archer.setX((int)e.getSceneX());
archer.setY((int)e.getSceneY());
archer.drawCharStand(gc);
}
});
}
});
You could use a ToggleButton, so that you only place the archer when the toggle button is selected:
private ToggleButton myButton = new ToggleButton("Place Archer");
// ...
scene.setOnMouseClicked(e -> {
if (myButton.isSelected()) {
archer.setX((int)e.getSceneX());
archer.setY((int)e.getSceneY());
archer.drawCharStand(gc);
myButton.setSelected(false);
}
});
The last line will unselect the toggle button "automatically" after placing the archer. If you want the user to be able to place multiple archers easily (and have to manually switch off that mode), omit that line.
You will need to change your code slightly. Perhaps try with a variable that tracks if the button was clicked:
boolean archerPlacementMode = false;
....
myButton.setOnMouseClicked(new EventHandler<MouseEvent>(){
public void handle(MouseEvent e)
{
if(!archerPlacementMode) {
archerPlacementMode = true;
gc.setFill(color);
gc.fillRect(0,0,width,height);
archerPlacementMode = true;
return;
}
}
});
scene.setOnMouseClicked(new EventHandler<MouseEvent>() {
public void handle(MouseEvent e)
{
if(archerPlacementMode) {
archer.setX((int)e.getSceneX());
archer.setY((int)e.getSceneY());
archer.drawCharStand(gc);
archerPlacementMode = false;
}
}
});
I'm switching from JEditorPane to WebEngine(JavaFX).
I used to lock the text highlighting(selecting) in JEditorPane as following.
my_editor.setEditable(false);
my_editor.getInputMap().put(KeyStroke.getKeyStroke("control C"), "none");
Now I like to do the same with WebEngine, how may I do this? disabling copy, highlighting and editing mode. Thanks.
If you want to disable copy, highlighting and editing from JavaFX, without the use of Javascript, one way to do it is by trapping the events and deal accordingly with them, leaving the rest of the options intact.
Let's use an event dispatcher to filter a few events:
For key events:
Avoid copy with Ctrl+C or Ctrl+Insert
Avoid selection with shift+Arrow
For Mouse events:
Avoid selection of word, line, paragraph with mouse click
Avoid selection with mouse dragging, but allowing dragging the scrollbars
(Others could be added if you need to)
public class WebEventDispatcher implements EventDispatcher {
private final EventDispatcher oldDispatcher;
private Point2D limit;
public WebEventDispatcher(EventDispatcher oldDispatcher) {
this.oldDispatcher = oldDispatcher;
}
public void setLimit(Point2D limit){
this.limit = limit;
}
private boolean allowDrag=false;
#Override
public Event dispatchEvent(Event event, EventDispatchChain tail) {
if (event instanceof MouseEvent){
MouseEvent m = (MouseEvent)event;
if (event.getEventType().equals(MouseEvent.MOUSE_CLICKED) ||
event.getEventType().equals(MouseEvent.MOUSE_PRESSED)) {
Point2D origin=new Point2D(m.getX(),m.getY());
allowDrag=!(origin.getX()<limit.getX() && origin.getY()<limit.getY());
}
// avoid selection with mouse dragging, allowing dragging the scrollbars
if (event.getEventType().equals(MouseEvent.MOUSE_DRAGGED)) {
if(!allowDrag){
event.consume();
}
}
// Avoid selection of word, line, paragraph with mouse click
if(m.getClickCount()>1){
event.consume();
}
}
if (event instanceof KeyEvent && event.getEventType().equals(KeyEvent.KEY_PRESSED)){
KeyEvent k= (KeyEvent)event;
// Avoid copy with Ctrl+C or Ctrl+Insert
if((k.getCode().equals(KeyCode.C) || k.getCode().equals(KeyCode.INSERT)) && k.isControlDown()){
event.consume();
}
// Avoid selection with shift+Arrow
if(k.isShiftDown() && (k.getCode().equals(KeyCode.RIGHT) || k.getCode().equals(KeyCode.LEFT) ||
k.getCode().equals(KeyCode.UP) || k.getCode().equals(KeyCode.DOWN))){
event.consume();
}
}
return oldDispatcher.dispatchEvent(event, tail);
}
}
Now on your scene, disable context menu to avoid copy/paste options, find the content area of the webview without the scrollbars, if any, and set the custom event dispatcher.
private Point2D pLimit;
private double width, height;
#Override
public void start(Stage primaryStage) {
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
// disable context menu (copy option)
webView.setContextMenuEnabled(false);
WebEventDispatcher webEventDispatcher = new WebEventDispatcher(webView.getEventDispatcher());
webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
#Override
public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
if(newValue.equals(State.SUCCEEDED)){
// dispatch all events
webView.setEventDispatcher(webEventDispatcher);
}
}
});
webEngine.load("<URL>");
Scene scene = new Scene(webView);
primaryStage.setTitle("WebView scrollbar test");
primaryStage.setScene(scene);
primaryStage.show();
webView.getChildrenUnmodifiable().addListener(new ListChangeListener<Node>() {
#Override
public void onChanged(Change<? extends Node> c) {
pLimit=webView.localToScene(webView.getWidth(),webView.getHeight());
webView.lookupAll(".scroll-bar").stream()
.map(s->(ScrollBar)s).forEach(s->{
if(s.getOrientation().equals(VERTICAL)){
width=s.getBoundsInLocal().getWidth();
}
if(s.getOrientation().equals(HORIZONTAL)){
height=s.getBoundsInLocal().getHeight();
}
});
// dispatch all events
webEventDispatcher.setLimit(pLimit.subtract(width, height));
}
});
}
You can disable highlight and copy with the following CSS:
body {-webkit-user-select: none;}
I have a popupmenu like this
final JPopupMenu contextMenu = new JPopupMenu();
final JMenuItem addTask = new JMenuItem("Add Task");
Then i add a MouseListener:
component.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e)
{
if (e.isPopupTrigger()) {
contextMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
}
In my Actionlistener for the MenuItem i would like to access the x/y-data from my MouseEvent. Is this possible without saving them in an extra variable? i would like to get something like this:
addTask.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//System.out.println(Mouse.getX()+", "+Mouse.getY());
}
});
If you want to get the mouse coordinate of event which has trggered the popup menu - no it's impossible without to save it. If you want to get the mouse event whcih has triggered the menu item action - yes it's possible: EventQueue.getCurrentEvent(); will return the event (you should check whether this event is a mouse event and if yes - cast it, because the action can also be triggered with key event).
public void actionPerformed(ActionEvent arg0) {
AWTEvent evt = EventQueue.getCurrentEvent();
if (evt instanceof MouseEvent) {
MouseEvent me = (MouseEvent) evt;
}
}
I've one Label in my custom FlowPanel which implements HasDoubleClickHandlers.
final Label label = new Label("Click here to write");
label.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
clicked();
}
});
final CustomFlowPanel customFlowPanel=new CustomFlowPanel();
customFlowPanel.addDoubleClickHandler(new DoubleClickHandler() {
#Override
public void onDoubleClick(DoubleClickEvent event) {
if (event.getSource() instanceof FlowPanel) {
doubleClicked();
}
}
});
custoFlowPanel.add(label);
The problem is when i double click to the label doubleClicked() should not execute.
How to prevent executing doubleClicked() when label is double clicked?
Thanks in advance!!!
You could just check the DoubleClickEvent if the label was clicked and if not you call doubleClicked().
customFlowPanel.addDoubleClickHandler(new DoubleClickHandler() {
#Override
public void onDoubleClick(DoubleClickEvent event) {
Element clicked = event.getNativeEvent();
if (!clicked.Equals(label.getElement())
{
doubleClicked();
}
}
});
I haven't tried it yet, but try adding a double click handler on the label and use Event.stopPropagation() on it. This prevents the event from being propagated to the parent.