I want to make a JavaFX application that basically overlays the entire user screen with a Canvas object, so basically I can draw anything on the user's screen.
Making a window that covers the whole screen is simple. Making it essentially transparent can be achieved with this tutorial: https://assylias.wordpress.com/2013/12/08/383/
So the one and only thing stopping me is the fact that obviously, the window, albeit being transparent it will still capture user mouse and key events.
Is there a way I can achieve this? For a more concrete example, imagine I want to make a red circle surround the user's mouse cursor wherever it goes, but the user input will not be interrupted.
What you want isn't possible in plain JavaFX.
You can check out my answer here, that's the closest thing. But you can't overlay a transparent canvas over the entire desktop and forward the mouse events to the underlying windows.
Having the Canvas semi-transparent would catch all events, but you could see the underlying windows. But when you have the Canvas fully transparent, your application wouldn't catch any events.
However, your "concrete example" could be solved in a different way. Here's the code:
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class CircleAroundCursor extends Application {
double radius = 50;
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Circle circle = new Circle( radius * 2,radius * 2,radius);
circle.setStroke(Color.RED);
circle.setFill(Color.TRANSPARENT);
root.getChildren().add(circle);
Scene scene = new Scene(root, Color.TRANSPARENT);
scene.getRoot().setStyle("-fx-background-color: transparent");
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setAlwaysOnTop(true);
AnimationTimer loop = new AnimationTimer() {
#Override
public void handle(long now) {
PointerInfo info = MouseInfo.getPointerInfo();
Point p = info.getLocation();
primaryStage.setX(p.getX() - radius * 2);
primaryStage.setY(p.getY() - radius * 2);
}
};
loop.start();
}
public static void main(String[] args) {
launch(args);
}
}
This at least solves "I want to make a red circle surround the user's mouse cursor wherever it goes, but the user input will not be interrupted"
Note: Here AWT classes are mixed with FX classes. You may need to use an EDT & FX thread handling. It does work without though.
Screenshot:
You may have a look at the Robot class. I have abused its powers many times, although I consider most solutions I used that class for as hacky.
Maybe you could do it like this:
intercept MouseEvent and save its properties
do your things like drawing
make the Window invisible
invoke the same MouseEvent with the help of Robot
make the Windows visible again
Related
Let us have a JavaFX program with the scene graph Group -> Canvas. The root (Group) is put inside a Scene, and the Scene is attached to a Window, specifically a Stage.
Once the Window is displayed on screen, the user may resize the Window. The height and the width may be changed. However, there are usually some restrictions to how small the Window can be made. Notably the Window has a titlebar, and it has an associated minimum width. See also the picture below.
I suspect that the minimum width of the titlebar is platform-dependent and further depends on user settings of the platform. So a more-less general way of accessing the parameter is desirable.
Is it possible to generally access the minimum (stable) width of the titlebar of a Window? If so, how?
A picture to explain concisely which length I am looking for:
(In the picture, the Window could not be made any smaller in the horisontal dimension).
Here is a MWE for testing (please try to decrease the horisontal width as far as possible):
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.stage.Stage;
public class HelloApplication extends Application {
#Override
public void start(Stage stage) {
Group group = new Group();
Canvas canvas = new Canvas(200, 200);
group.getChildren().add(canvas);
Scene scene = new Scene(group);
stage.setTitle("Title");
stage.setScene(scene);
stage.show();
//System.out.println(stage.getMinWidth()); // default is also 0.0
//stage.setMinWidth(0); // we can see that a lower value than the sought-for minimum value will not have an effect
}
public static void main(String[] args) {
launch();
}
}
So goal here is to use custom ScrollBar for ScrollPane without having trouble with layout when maximizing/minimizing window.
Consider example program:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Layout extends Application
{
#Override
public void start(Stage stage)
{
BorderPane main = new BorderPane();
main.setPrefSize(800, 600);
BorderPane center = new BorderPane(); // begin center
center.setBackground(new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, Insets.EMPTY)));
main.setCenter(center); // end center
BorderPane left = new BorderPane(); // begin left
ScrollPane pane = new ScrollPane();
pane.setFitToWidth(true);
Pane p1 = new Pane(); // child 1
p1.setPrefSize(200, 100);
p1.setBackground(new Background(new BackgroundFill(Color.YELLOW, CornerRadii.EMPTY, Insets.EMPTY)));
this.makeResizable(p1);
Pane p2 = new Pane(); // child 2
p2.setPrefSize(200, 100);
p2.setBackground(new Background(new BackgroundFill(Color.BLUE, CornerRadii.EMPTY, Insets.EMPTY)));
this.makeResizable(p2);
VBox content = new VBox(10, p1, p2); // content in scroll pane
pane.setContent(content);
// replace normal bars
pane.setHbarPolicy(ScrollBarPolicy.NEVER);
pane.setVbarPolicy(ScrollBarPolicy.NEVER);
// with custom
ScrollBar sb = new ScrollBar();
sb.setOrientation(Orientation.VERTICAL);
sb.minProperty().bind(pane.vminProperty());
sb.maxProperty().bind(pane.vmaxProperty());
sb.visibleAmountProperty().bind(pane.heightProperty().divide(content.heightProperty()));
sb.managedProperty().bind(sb.visibleAmountProperty().lessThan(1.0)); // bar should be managed when it is needed (content too long)
sb.visibleProperty().bind(sb.managedProperty()); // and also visible only when managed
sb.valueProperty().bindBidirectional(pane.vvalueProperty());
left.setCenter(pane); // content
left.setRight(sb); // scroll bar
main.setLeft(left); //end left
Scene scene = new Scene(main);
stage.setScene(scene);
stage.show();
}
// Simple for testing
double prevY;
boolean dragging;
// Makes node resizable on drag.
private void makeResizable(Region region)
{
region.addEventFilter(MouseEvent.MOUSE_PRESSED, e ->
{
this.dragging = true;
region.setPrefHeight(region.getHeight());
this.prevY = e.getSceneY();
});
region.addEventFilter(MouseEvent.MOUSE_DRAGGED, e ->
{
if (!this.dragging) return;
region.setPrefHeight(region.getPrefHeight() + (e.getSceneY() - this.prevY));
this.prevY = e.getSceneY();
});
region.addEventFilter(MouseEvent.MOUSE_RELEASED, e -> this.dragging = false);
}
public static void main(String[] args)
{
launch(args);
}
}
It will produce GUI with ScrollPane on the left with a custom ScrollBar that appears when content (2 nodes p1 and p2) exceed bounds. In order to make it easy to test - both p1 and p2 are made resizable when dragging them with mouse (try it). While the ScrollBar appears and works as expected, there is a flaw in layout if we would start maximizing and minimizing window.
For example:
Start program
Resize content so ScrollBar appears, but not too
much (make it so that when you maximize it, it will be in bounds)
Maximize window - you will notice that bar might have disappeard, but "empty space" appears.
Now, if you would somehow make refresh (e.g by resizing content again), bar would disappear due to layout pass update.
Other bug:
Start program and maximize.
Resize content so it is still contained in maximized window, but is big enough that when you minimize, it will exceed bounds.
Minimize
Notice how ScrollBar is misplaced.
There are few other bugs if you would try other stuff, but all originate from the fact that when you maximize/minimize this happens (using 1st example):
ScrollBar is managed and visible (considering content exceeds).
Maximize
Window is resized layout is calculated (using old values - managed=true && visible=true)
Layout happens, everything is in place, all properties receive update, including sb.visibleAmountProperty() making ScrollBar set managed and visible to false (since they are bound, see code).
ScrollBar becomes invisible and unmanaged, but layout already happened, and will not re-run.
How do I make it work with window maximizing? How else could I bind ScrollBar so it doesn't break when maximizing? Please note that we talk about maximizing, not resizing (which works).
I found one workaround, but it doesn't seem like absolutely best thing to do, if there is more proper one - please share.
sb.managedProperty().addListener(e -> Platform.runLater(() -> content.requestLayout()));
Since this will be ran "later" (and since also on main thread which 1st should finish previous layout), this will cause relayout to happen.
Why is it not best? Well for one it would be called also when not really needed (while not maximizing) causing double layout (and we can't really resove this with some if statement).
I don't know how to compare all of the x and y of the circle objects on pane to those of the mouse. The problem Im working on asks me to to set it so the secondary click of the mouse removes a point when it is placed on it, I figure I can do this by comparing all of the distances of the circles coordinates and the mouse coordinates (using distance formula) to the radi of the circles. If one of the distances is less than the radi I would remove that circle. The problem is that I dont know how to call all of the points on the pane so I can compare them. This is the code I have so far to give you a better understanding of how the points are set up.
import javafx.scene.paint.Color;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Homework6 extends Application {
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
// Create a pane and set its properties
Pane pane = new Pane();
//Circle circle1 = new Circle(7);
Circle[] circles = new Circle[0];
pane.setOnMouseClicked(e -> {
switch (e.getButton()) {
case PRIMARY:
Circle circle1 = new Circle(7);
circle1.setCenterX(e.getX());
circle1.setCenterY(e.getY());
pane.getChildren().add(circle1);
circle1.setFill(Color.WHITE);
circle1.setStroke(Color.BLACK);
case SECONDARY:
}
});
// Create a scene and place the pane in the stage
Scene scene = new Scene(pane);
primaryStage.setTitle("KeyEventDemo"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
pane.requestFocus(); // text is focused to receive key input
}
public static void main(String[] args) {
launch(args);
}
}
No need to do any computations! Method evt.getTarget() should already return the circle that has been clicked.
Have a look at Oracle's JavaFX tutorials to learn how to handle event.
I have created a board game in Java that is not different in principle from checkers. It works fine in the console but now I'm trying to create graphics. I have a Piece class, a Tile class that checks if it is empty or occupied by a white or a black checkers piece, a Grid class that keeps track of the tiles in a matrix, and a Game class.
Currently, the game can be played in the Grid class; when we run the Grid class, the user specifies the size of the board in the console and then plays the game by giving the x and y coordinates of the tile that the user wishes to select. What I would like to change is to run the game in the Game class, which is an extension of JPanel and implements MouseListener (code given below). The game board will be a fixed size (I'll begin with 5x5) and I have drawn a picture of a grid which should be in the background of the game. There will be an instance variable (Grid g = new Grid(5,5)). I have also drawn pictures of the different "checkers" pieces which will be used, they should be distributed in the foreground on specific tiles. What I want to happen is that when the user clicks a tile, the checkers pieces move. Ideally, I would do this so that the program sees the coordinates of the place that the mouse clicks (say that the JPanel is 500x500 pixels and the user clicks the pixel with the coordinate (0,500), then we check if (0,500) belongs to some tile, if it doesn't then nothing happens, if it does belong to a tile on the Grid g then g.play(something,something)).
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.FlowLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
public class Game extends JPanel implements MouseListener {
private Grid g = new Grid(5,5);
public Game() {
JFrame frame = new JFrame("Boardgame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.setLayout(new FlowLayout());
frame.setPreferredSize(new Dimension(500,500));
frame.pack();
frame.setVisible(true);
frame.addMouseListener(this);
}
public void mouseClicked(MouseEvent e) {
//here we check if the user clicked on a tile,
if that happens then we get the x and y-coordinates of the tile and then g.play(x,y)
}
public void mouseEntered(MouseEvent e) {
// we are not really interested in this method or the following
mouse methods but they are necessary for the mouselistener
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public static void main(String args[]) {
new Game();
}
}
So in essence, what I would like to do is:
i) Have a background picture (this is easy right?) [update: I have done this by means of using a JLabel, if there is any better way to do it please tell me].
ii) Fix the MouseListener so that the pieces move when a tile is clicked. The only input I require is which tile is to be moved, we don't need to know which piece is supposed to go there.
Any help would be really appreciated and please ask if I can clarify something. This is not for school or anything, just a private project.
You could use Shape and attach listeners to catch user actions on the shapes.
To enable the user to interact with the graphics you display, you need to be able to determine when the user clicks on one of them. The hit method of the Graphics2D class provides a way to easily determine whether a mouse click occurred over a particular Shape object. Alternatively you can get the location of the mouse click and call contains on the Shape to determine whether the click was within the bounds of the Shape.
read on http://docs.oracle.com/javase/tutorial/2d/advanced/user.html
I want to create a simple GUI application that displays a map of a city.
I then want to programatically add items (assets) such as hotels, restaurants to this map as images.
Pseudo-Code would be as follows
[set up background object with map image covering entire form]
[create hotel1 object (image, label with icon or whatever]
hotel1.image = "hotel.png";
hotel1.size-x = 30;
hotel1.size-y = 30;
hotel1.location-x = 450; (pixels)
hotel1.location-y = 300;
background-object.add(hotel1);
[create restaurant1 object (image, label with icon or whatever]
restaurant1 .image = "hotel.png";
restaurant1 .size-x = 30;
restaurant1 .size-y = 30;
restaurant1 .location-x = 600; (pixels)
restaurant1 .location-y = 400;
background-object.add(restaurant1);
[repeat for hotel2, hotel3, restaurant2 etc...]
This way I could add any number of Assets to the map. The other functions I would require are
change the image of an asset (e.g. to show different image for an asset)
hotel1.image = "hotel_closed.png";
overlap assets (if they are close together)
register a click event handler for each asset
change visibility of asset
hotel1.visible = false;
I am an experienced .Net programmer. This task would be a simple one in .Net, however I am not clear on the best way to accomplish the above in Java. Please could someone suggest the best approach to achieving the above. I am happy to Google if a concept is suggested (I don't need a full coded solution!!)
Many thanks, Ian
There's a lot of places you could start, without knowing the entire requirements (ie if you need to download the maps, tile the maps etc), I can only give you a few overview suggestions
I'd start by having a read through (in no particular order)
Creating a GUI With JFC/Swing
Concurrency in Swing
Custom Painting in Swing
2D Graphics
Basic I/O
I'd also make my self familiar with The Java Tutorials
While most of the above are GUI specific, I'd be reading through things like
Essential classes
Learning the Java Language
Simply because it doesn't matter where you code in Java, these will always be useful.
Happy readings :)
UPDATE
Oh, and of course, the all important API docs (AKA JavaDocs)
UPDATE
When you're reasonable comfortable with all that, you might like to check out SwingX WS, it has a great example of pulling Google & OpenStreet Maps
I am assuming that you want your application to be desktop rather than web based. In which case I have done something similar (though rather more complicated) before using a third party mapping solution. Unfortunately that solution required a licence and is no longer available anyway.
If you just want a simple non-scrollable map in a desktop application I suggest you start with a Swing solution. Look at extending the JComponent object and override the method
public void paintComponent(Graphics g){
// use g to draw things
}
Use the graphics object to paint your map image and your icons. Add this Component to your Swing JFrame and set up the correct sizes and layouts.
If you want this in a webpage then someone else would be better placed to help you.
Edit:
From the feedback given here and in the other post I think you may benefit from a slice of code so here goes:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SwingPaintDemo extends JPanel {
private BufferedImage map = null;
private BufferedImage pointer = null;
public SwingPaintDemo() {
this.setPreferredSize(new Dimension(200, 200));
loadImagesFromFile();
}
private void loadImagesFromFile() {
// load your images form file - these are fakes:
map = new BufferedImage(200, 200, BufferedImage.TYPE_3BYTE_BGR);
pointer = new BufferedImage(10, 10, BufferedImage.TYPE_4BYTE_ABGR);
Graphics g = map.getGraphics();
g.setColor(Color.GREEN);
g.fillRect(0, 0, 200, 200);
g = pointer.getGraphics();
g.setColor(Color.BLUE);
g.fillOval(0, 0, 10, 10);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// paint map
g.drawImage(map, 0, 0, this);
// paint pointers
g.drawImage(pointer, 50, 75, this);
}
// this main is for testing the class but can be used as a reference
public static void main(String... args) {
JFrame jf = new JFrame();
SwingPaintDemo mapper = new SwingPaintDemo();
jf.getContentPane().setLayout(new BorderLayout());
jf.getContentPane().add(mapper, BorderLayout.CENTER);
jf.setVisible(true);
jf.pack();
}
}
You will need to edit this to load in your images from your file - I wanted to make this self contained and easily runnable so I have just created the images inline.
An answer has not been accepted, yet. So, using your pseudo code as an example, I coded up a quick overlay example using JavaFX 2. The WebView can easily be replaced with an ImageView using the JPG file you mentioned.
Here is the code:
package simple.map.overlay;
import java.io.InputStream;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class SimpleMapOverlay extends Application
{
#Override
public void start(Stage primaryStage)
{
WebView mapView = new WebView();
WebEngine webEngine = mapView.getEngine();
String url = "http://maps.google.com/maps?q=Baramerica,+South+Alamo+Street,+San+Antonio,+TX&hl=en&ll=29.416647,-98.488655&spn=0.025196,0.035233&sll=29.416423,-98.489814&sspn=0.006299,0.008808&hq=Baramerica,&hnear=S+Alamo+St,+San+Antonio,+Texas&t=m&z=15";
url += "&output=embed";
webEngine.load(url);
VBox vBox = new VBox(5);
vBox.getChildren().add(mapView);
InputStream instream = SimpleMapOverlay.class.getResourceAsStream("beer.png");
Image beerImage = new Image(instream);
instream = SimpleMapOverlay.class.getResourceAsStream("food.jpg");
Image foodImage = new Image(instream);
Marker laTunaMarker = new Marker(beerImage, "La Tuna");
laTunaMarker.setLayoutX(210);
laTunaMarker.setLayoutY(480);
Marker rosariosMarker = new Marker(foodImage, "Rosarios");
rosariosMarker.setLayoutX(360);
rosariosMarker.setLayoutY(300);
Group root = new Group();
root.getChildren().add(vBox);
root.getChildren().add(laTunaMarker);
root.getChildren().add(rosariosMarker);
Scene scene = new Scene(root);
primaryStage.setTitle("Hello Map World with Markers!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
class Marker extends Group
{
public Marker(Image image, String text)
{
ImageView imageView = new ImageView(image);
Label label = new Label(text);
VBox vbox = new VBox(5);
vbox.getChildren().add(imageView);
vbox.getChildren().add(label);
getChildren().add(vbox);
}
}
}