Is there any way I can draw an arrow at the current position or add an arrow to a paragraph like a chunk to get something like:
This arrow points to the right -> thats it!
How can I draw "->" as arrow like in MS Word inline?
EDIT: if someone needs it:
public final Font SYMBOL_FONT = new Font(Font.getFamily(BaseFont.SYMBOL));
public Chunk getArrow(final boolean paramLeft) {
Chunk arrow;
if (paramLeft) {
arrow = new Chunk(String.valueOf((char)220), SYMBOL_FONT);
} else {
arrow = new Chunk(String.valueOf((char)222), SYMBOL_FONT);
}
return arrow;
}
Related
I'm trying to make a battleship game.
Here's a link to the code: Battleship
Basically, when you click on one of the ship buttons(battleship, cruiser, submarine, etc.) a Rectangle pops up on the screen. You can move this rectangle around the screen by clicking on it and dragging the mouse around. The class that adds all of the event handlers to the Rectangle can be found in class MouseGestures.java.
Event Handlers are added to the GridPane in the GameGUI.java class starting at line 82.
Basically, I'm trying to find the index of whatever node you drag this object over in the GridPane. I'm able to accomplish this without dragging, by applying setOnMouseEntered to each node in the gridpane, but when I try to utilize setOnMouseDragEntered, I get nothing.
Would anyone have an idea for how to get this to work?
Just to reiterate, I want to print in the console the index of each gridpane when I drag and move a node over the nodes contained in the gridpane.
These are the event handlers I have applied to the nodes in the gridpane.
for (int i=0; i<=11; i++) {
for (int j=0; j<=11; j++) {
int colIndex = i;
int rowIndex = j;
grid2[i][j] = new Rectangle();
grid2[i][j].setStroke(Color.BLACK);
grid2[i][j].setFill(null);
grid2[i][j].setStrokeWidth(1);
grid2[i][j].setWidth(30);
grid2[i][j].setHeight(30);
//Prints index of each grid2[][] upon mouse entry.
grid2[i][j].setOnMouseEntered( e -> {
System.out.printf("Mouse entered cell [%d, %d]%n", colIndex, rowIndex);
});
//Does not work! Want to print index of each grid2[][] upon mouse drag enter.
grid2[i][j].setOnMouseDragEntered( e -> {
System.out.printf("Mouse entered cell [%d, %d]%n", colIndex, rowIndex);
});
//Does not work! Want to print index of each grid2[][] upon mouse drag over.
grid2[i][j].setOnMouseDragOver( e -> {
System.out.printf("Mouse entered cell [%d, %d]%n", colIndex, rowIndex);
});
These are the event handlers for the object you create when you push one of the buttons.
private EventHandler<MouseEvent> onMousePressedEventHandler = event -> {
if (event.getSource() instanceof Rectangle) {
shipSelected = (Rectangle) (event.getSource());
dragContext.x = shipSelected.getTranslateX() - shipSelected.getWidth()/2;
dragContext.y = shipSelected.getTranslateY() - shipSelected.getHeight()/2;
}
};
private EventHandler<MouseEvent> onMouseDragDetectedEventHandler = event -> {
shipSelected.startFullDrag();
System.out.println("startfulldrag");
};
private EventHandler<MouseEvent> onMouseDraggedEventHandler = event -> {
if (event.getSource() instanceof Rectangle) {
shipSelected = (Rectangle) (event.getSource());
shipSelected.setX(dragContext.x + event.getX());
shipSelected.setY(dragContext.y + event.getY());
}
};
You need to make the Rectangle you move around mouse transparent with setMouseTransparent(true) while you are dragging it.
private EventHandler<MouseEvent> onMouseDragDetectedEventHandler = event -> {
shipSelected.startFullDrag();
shipSelected.setMouseTransparent(true)
System.out.println("startfulldrag");
};
Otherwise, the Rectangle is always the top-most element under your mouse pointer, and you will not get dragEntered events from the elements below (i.e. the grid).
Of course, do not forget to set mouse transparency back to false, when you ended dragging.
I am writing a board game which has a 20x20 grid.
This is in my board class:
private final Position[][] grid = new Position[GRID_SIZE][GRID_SIZE];
each position has :
public class Position {
private final Coordinates pos;
private Player player;
private final static double RECTANGLE_SIZE = 40.0;
private final Rectangle rect = new Rectangle(RECTANGLE_SIZE, RECTANGLE_SIZE);
}
so basically I have 20x20 Positions and each positions has a Rectangle
This is what I do to display the grid
for (int cols = 0; cols < GRID_SIZE; ++cols) {
for (int rows = 0; rows < GRID_SIZE; ++rows) {
grid.add(gameEngine.getBoard().getGrid()[cols][rows].getRect(), cols, rows);
}
}
Anyway, the grid is initialized and works properly. What I want to do is to make the rectangle objects clickable and to be able to return their Coordinates when they are clicked.
This is how I handle the mouse click
private void setUpRectangle() {
rect.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
rect.setFill(Color.BLACK);
}
});
}
What this code does is to change the color of the rectangle to black, but how could I return the Coordinates.
Basically, I can edit the onclick function to return the coordinates of this position, but how can I acquire them later?
This is not a JavaFX question as much as it is a design question. You have a container (Position) of 2 objects (Coordinates and Rectangle) and you want one of them to know about the other. That is, the rectangle should know the coordinates of its position.
There are a few approaches here, and depending on the bigger picture, one might be better than the others. James_D mentioned a couple in a comment.
Keep a reference of the position object in the rectangle object. This is useful if rectangle needs to access various datum in the container from various places. You would do something like rectangle.getPosition().getCoordinates() or .getPlayer().
Keep a reference of the coordinates object in the rectangle object. This is a more specific approach of 1 useful if you only need that object. You would do something like rectangle.getCoordinates().
Pass the coordinates to your setUpRectangle method. This is useful if you rectangle doesn't need access to this data from various places, it's a local solution. Then in the handle method you would return the coordinates you passed to setUpRectangle, though we can't see what class this method is in.
Use external help. You can keep something like Map<Rectangle, Coordinates> and then call map.get(rectangle). You can hide this map in a method Coordinates getCoordinatesForRectangle(Rectangle rectangle) instead of calling it directly.
You could store this data as userData (or use properties in case userData is preserved for something else in your program):
private final Rectangle rect;
public Position() {
rect = new Rectangle(RECTANGLE_SIZE, RECTANGLE_SIZE);
rect.setUserData(this);
}
rect.setOnMouseClicked((MouseEvent event) -> {
Position pos = (Position) ((Node) event.getSource()).getUserData();
...
});
You could also use a listener that knows about the position:
class CoordinateAwareListener implements EventHandler<MouseEvent> {
private final int coordinateX;
private final int coordinateY;
public CoordinateAwareListener(int coordinateX, int coordinateY) {
this.coordinateX = coordinateX;
this.coordinateY = coordinateY;
}
#Override
public void handle(MouseEvent event) {
// do something with the coordinates
}
}
I am struggling with intersections i need to .getShape() on a button.
But as the API states
When null, the Region is rendered as a rounded rectangle.
that means that by default button dont have shape set.
I don't want to .setShape() on the button but to check my intersections i need to .getShape() of the button which comes back null.
Is there a way to get the default shape of the button node while .getShape() returns null ?
I have 2 .observableArrayList() buttons and circles (kind of bubbles)
private final ObservableList<Circle> circles = FXCollections.observableArrayList();
private final ObservableList<Button> buttonsList = FXCollections.observableArrayList();
private final ObjectProperty<BoundsType> selectedBoundsType = new SimpleObjectProperty<>(BoundsType.BOUNDS_IN_PARENT);
Then I add all of my buttons and circles to obs lists:
buttonsList.addAll(rootPane.getChildrenUnmodifiable()
.stream()
.filter(node -> node instanceof Button)
.map(node -> (Button) node)
.collect(Collectors.toList()));
Circles are a bit different I add them separately with a mouse click.
and then check the intersections between circles and buttons.
for (Button btn : buttonsList) {
for (Circle c : circles) {
ShapePair pair = new ShapePair(c.getShape(), btn.getShape());
if (pair.intersects(selectedBoundsType.get())) {
System.out.println("Colision");
//logic
}
}
}
but like i said the btn.getShape() gets me null ;(
Right i think it just easier to do
for (Button btn : buttonsList) {
for (Circle c : circles) {
if (btn.getBoundsInParent().intersects(b.getBoundsInParent())) {
System.out.println("Colision");
}
}
}
I am looking for a way to display text progressively with libgdx, but I can't find a way to do it exactly the way I want. Here is what I did:
I have a text label that is being updated periodically to display a different text. The label is set to setWrap(true); and setAlignment(Align.center);
Every time I change the text of the label I use a custom Action which I built like this
public class DisplayTextAction extends TemporalAction{
private CharSequence completeText;
#Override
protected void update(float percent) {
((Label)actor).setText(
completeText.subSequence(
0,
(int)Math.round(completeText.length()*percent));
}
public void setText(String newText){
completeText = newText;
}
}
Every text update, I call the action from a pool, change the text and add the action to the label.
Here is my problem: This doesn't work the way I want with a centered and wrapped text.
This happens when text isn't centered (dots represent space):
|h........|
|hel......|
|hello....|
(Works as intended)
This is what happens when the text is centered:
|....h....|
|...hel...|
|..hello..|
And this is how I want it to behave:
|..h......|
|..hel....|
|..hello..|
My original idea to fix this was to use 2 sets of strings, one that is the visible text, and one invisible that acts as "padding". I came up with something like this:
CharSequence visibleText = completeText.subSequence(
0,
(int)Math.round(completeText.length()*percent));
CharSequence invisibleText = completeText.subSequence(
(int)Math.round(completeText.length()*percent),
completeText.length());
So I have my two sets of strings, but I can't find a way to display two different fonts (one visible, and another one which is the same but with an alpha of 0) or styles in the same label with Libgdx.
I'm stuck, I don't know if my approach is the right one or if I should look into something completely different, and if my approach is correct, I don't know how to follow it up using libgdx tools.
EDIT:
I followed Jyro117's instructions and I could make great progress, but I couldn't make it work with centred text on multiple lines.
imagine this text:
|all those lines are|
|..for a very long..|
|........text.......|
And it has to be displayed like this
|all th.............|
|...................|
|...................|
|all those line.....|
|...................|
|...................|
|all those lines are|
|..for a ve.........|
|...................|
|all those lines are|
|..for a very long..|
|........text.......|
Jyro117's solution give either
|all those lines are|
|for a very long....|
|text...............|
displayed correctly.
or
|...................|
|......all tho......|
|...................|
|...................|
|...all those lin...|
|...................|
|all those lines are|
|......for a v......|
|...................|
You are over-complicating the solution. All you really need is to determine the size of the label when all the text is added. Once you have determined that, lock the label size to those dimensions, put it inside of a table that expands to fill up the area around it, and then update your label with the action. (You can use a pool and such as needed, but for simplicity I left that out of the code below).
You will have to obviously adapt the code to yours, but this gives you a code reference to what I mean.
Here is a code snippet on one way to do it:
stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
Gdx.input.setInputProcessor(stage);
uiSkin = new Skin(Gdx.files.internal("skin/uiskin.json"));
Table fullScreenTable = new Table();
fullScreenTable.setFillParent(true);
final String message = "hello";
final Label progressLabel = new Label(message, this.uiSkin);
final TextBounds bounds = progressLabel.getTextBounds(); // Get libgdx to calc the bounds
final float width = bounds.width;
final float height = bounds.height;
progressLabel.setText(""); // clear the text since we want to fill it later
progressLabel.setAlignment(Align.CENTER | Align.TOP); // Center the text
Table progressTable = new Table();
progressTable.add(progressLabel).expand().size(width, height).pad(10);
final float duration = 3.0f;
final TextButton button = new TextButton("Go!", this.uiSkin);
button.addListener(new ClickListener() {
#Override public void clicked(InputEvent event, float x, float y) {
progressLabel.addAction(new TemporalAction(duration){
LabelFormatter formatter = new LabelFormatter(message);
#Override protected void update(float percent) {
progressLabel.setText(formatter.getText(percent));
}
});
}
});
stage.addActor(button);
fullScreenTable.add(progressTable);
fullScreenTable.row();
fullScreenTable.add(button);
stage.addActor(fullScreenTable);
Edit:
Added code to center and top align text in label. Also added code to fill spaces on the end to allow for proper alignment. Note: Only useful for mono-spaced fonts.
class LabelFormatter {
private final int textLength;
private final String[] data;
private final StringBuilder textBuilder;
LabelFormatter(String text) {
this.textBuilder = new StringBuilder();
this.data = text.split("\n");
int temp = 0;
for (int i = 0 ; i < data.length; i++) {
temp += data[i].length();
}
textLength = temp;
}
String getText(float percent) {
textBuilder.delete(0, textBuilder.length());
int current = Math.round(percent * textLength);
for (final String row : data) {
current -= row.length();
if (current >= 0) {
textBuilder.append(row);
if (current != 0) {
textBuilder.append('\n');
}
} else {
textBuilder.append(row.substring(0, row.length() + current));
// Back fill spaces for partial line
for (int i = 0; i < -current; i++) {
textBuilder.append(' ');
}
}
if (current <= 0) {
break;
}
}
return textBuilder.toString();
}
}
Whenever I click a JSlider it gets positioned one majorTick in the direction of the click instead of jumping to the spot I actually click. (If slider is at point 47 and I click 5 it'll jump to 37 instead of 5). Is there any way to change this while using JSliders, or do I have to use another datastructure?
As bizarre as this might seem, it's actually the Look and Feel which controls this behaviour. Take a look at BasicSliderUI, the method that you need to override is scrollDueToClickInTrack(int).
In order to set the value of the JSlider to the nearest value to where the user clicked on the track, you'd need to do some fancy pants translation between the mouse coordinates from getMousePosition() to a valid track value, taking into account the position of the Component, it's orientation, size and distance between ticks etc. Luckily, BasicSliderUI gives us two handy functions to do this: valueForXPosition(int xPos) and valueForYPosition(int yPos):
JSlider slider = new JSlider(JSlider.HORIZONTAL);
slider.setUI(new MetalSliderUI() {
protected void scrollDueToClickInTrack(int direction) {
// this is the default behaviour, let's comment that out
//scrollByBlock(direction);
int value = slider.getValue();
if (slider.getOrientation() == JSlider.HORIZONTAL) {
value = this.valueForXPosition(slider.getMousePosition().x);
} else if (slider.getOrientation() == JSlider.VERTICAL) {
value = this.valueForYPosition(slider.getMousePosition().y);
}
slider.setValue(value);
}
});
This question is kind of old, but I just ran across this problem myself. This is my solution:
JSlider slider = new JSlider(/* your options here if desired */) {
{
MouseListener[] listeners = getMouseListeners();
for (MouseListener l : listeners)
removeMouseListener(l); // remove UI-installed TrackListener
final BasicSliderUI ui = (BasicSliderUI) getUI();
BasicSliderUI.TrackListener tl = ui.new TrackListener() {
// this is where we jump to absolute value of click
#Override public void mouseClicked(MouseEvent e) {
Point p = e.getPoint();
int value = ui.valueForXPosition(p.x);
setValue(value);
}
// disable check that will invoke scrollDueToClickInTrack
#Override public boolean shouldScroll(int dir) {
return false;
}
};
addMouseListener(tl);
}
};
This behavior is derived from OS. Are you sure you want to redefine it and confuse users? I don't think so. ;)