The main layout of my ScalaFX 8 application consists of a BorderPane. The top attribute contains a menu whereas bottom contains something similar to a status bar. My goal is to display a component for viewing 3D objects in the center of the BorderPane (acting as a SubScene).
stage = new PrimaryStage {
scene = new Scene(900, 900, true, SceneAntialiasing.Balanced) {
root = new BorderPane {
top = createMenu // creates a menu inside of a VBox
center = createViewer // should create a subscene inside of whatever is needed
bottom = createStatusBar // creates a status bar inside of a VBox
}
}
}
I'm trying to create a minimal working example with a SubScene that only consists of black background and a simple sphere, no more, no less. The SubScene should use the whole space that is available in the center of BorderPane and resize accordingly. Unfortunately, I can't make it work.
Since the size of a SubScene is fixed, I assume it is necessary to embed the SubScene in another container (which is able to resize automatically) and bind the dimensions of the SubScene to the dimensions of the container around it.
def createViewer = {
val bp = new BorderPane
val subScene: SubScene = new SubScene(bp, 200, 200, true, SceneAntialiasing.Balanced) {
fill = Color.Black
width <== bp.width
height <== bp.height
content = new Sphere(3) { material = new PhongMaterial(Color.Red) }
camera = new PerspectiveCamera(true) { ... }
}
bp.center = subScene
subScene
}
The result looks like this:
Two obvious problems:
The SubScene keeps the fixed size from its constructor. In neither "maximizes" in the center of the outer BorderPane, nor does it do anything when the window gets resized
There is the red dot, but the bottom right corner of the SubScene is not black (?)
My assumption is that I have some problems understanding what the root element of the SubScene really is and what it does. I found another thread for JavaFX with a similar problem, this solution distinguishes between the root element of the SubScene (I'm not sure where that element comes from) and the Pane but I couldn't apply it to my case. Any help is appreciated. Thanks.
the idea here is to get the read only properties for the top level scene there is probably a more elegant way to do this, but this works
scene = new Scene(900, 900, true, SceneAntialiasing.Balanced) {
// these are read only properties for the scene
var tmpw = this. width
var tmph = this. height
root = new BorderPane {
top = new HBox {
content = new Label {
text = "menu"
}
}
center = createView(tmpw, tmph)
}
}
width onChange show
height onChange show
}
the idea here is to bind the read only properties to the properties of the subscene, then
the subscene will re-size there is probably a way to avoid the 'this' key-word.
I have tested this and the subscene re-sizes with the parent scene. I have omitted the PerspectiveCamera code block as you did not include what you were using
def createView(boundWidth : ReadOnlyDoubleProperty, boundHeight : ReadOnlyDoubleProperty): BorderPane = {
new BorderPane {
center = new SubScene(boundWidth.get(), boundHeight.get(), true, SceneAntialiasing.Balanced) {
fill = Color.BLACK
content = new Sphere(3) { material = new PhongMaterial(Color.RED) }
camera = new PerspectiveCamera(true)
// bind the subscene properties to the parents
this.width.bind(boundWidth.add(-200))
this.height.bind(boundHeight.add(-200))
}
}
}
Related
I'm trying to create a scroll grid view in which every cell object is tapable.
When a cell object is tapped I want to scale and traslate it to the center of the screen and render it above other cells.
I was able to make it tapable and scale it in its position. Now I want to move the cell object to the center of the screen and render it above other cells.
I've tried many solutions but none of them works.
This is my hierarchy:
This is the grid in normal state:
This is the grid when a cell was tapped:
I'm populating the grid from a C# script dynamically.
void Populate()
{
GameObject cardContainerInstance, cardInstance;
foreach (var c in cardsCollection.GetAll())
{
if (c.IsOwned)
{
cardContainerInstance = Instantiate(cardContainer, transform);
cardInstance = cardContainerInstance.transform.Find("Card").gameObject;
var cardManager = cardInstance.GetComponent<CardManager>();
cardManager.card = c;
cardManager.AddListener(this);
}
else
{
Instantiate(cardSlot, transform);
}
}
}
public void OnCardClick(GameObject cardObject, Card card)
{
Debug.Log("OnCardClick " + card.name);
if (openedCard != null) {
if (openedCard.Number == card.Number)
{
CloseCard(openedCardObject);
}
else
{
CloseCard(openedCardObject);
OpenCard(cardObject, card);
}
}
else
{
OpenCard(cardObject, card);
}
}
void OpenCard(GameObject cardObject, Card card)
{
//cardObject.GetComponent<Canvas>().sortingOrder = 1;
var animator = cardObject.GetComponent<Animator>();
animator.SetTrigger("Open");
openedCard = card;
openedCardObject = cardObject;
}
void CloseCard(GameObject cardObject)
{
var animator = cardObject.GetComponent<Animator>();
animator.SetTrigger("Close");
openedCard = null;
openedCardObject = null;
}
I can't figure out how to move the cell to the center and render it above others.
Note that all is animated using an animator attached to the object itself.
Could anyone help me please? Thank you very much!
EDIT: more details
All cell object have the following hierarchy:
where:
CardContainer is an empty object added to use animator on Card child object
Card is the object itself that has a script, a canvas renderer and an animator
StatsImage is the object that slide out when the card is tapped
Image is a calssic UIImage with Image script, Shadow script and canvas renderer
Other component are simple texts.
EDIT: fix in progress
Trying to apply this suggestions I was able to manage the rendering order (as you see on the image below) but it seems that prevent touch events to be detected on the game object.
I've added a GraphicsRaycaster too and now the bottom horizontal scroll view scrolls again but only if I click and drag a card.
Moreover, with the GraphicsRaycaster, the main grid card still are not clickable and it's possible to open the card only if it is behind the bottom panel (if I click on the red spot in the image below the card behind the panel receives che click)
This is the CardContainer at runtime(note that I'm attaching new Canvas and GraphicsRaycaster on the CardContainer, which is the "root" element):
You didn't clarify whether you are using a sprite renderer or some other method but here is an answer for each.
Sprite renderer:
this the simple one. In each sprite renderer, there is a variable called "sortingOrder" in script and "Order in layer" in the inspector. sprite renderer with sorting Orders that are higher is rendered first. All you would need to do is call:
cardObject.GetComponent<SpriteRenderer>().sortingOrder = 1;
when you click the card, and
cardObject.GetComponent<SpriteRenderer>().sortingOrder = 0;
when you unclick it. I hope that makes sense!
Other Method:
this one is a bit harder and I would suggest that you switch to sprite renderers and it will be much easier and more stable down the road, but I can understand if you have already written a lot of scripts and don't want to go back and change them.
Anyway, all you will need to do Is create two layers: cardLower and cardUpper. then create a new camera and call it topCamera. now under the top camera object in the inspector, change the culling mask (it's near the top) and make sure cardUpper is selected. then change the Clear flags (first one) to "Don't Clear" finally change the depth to 0 (if that doesn't work change it to -2). Now objects in the cardUpper will always be rendered above everything else. You can change the layer through script with
cardObject.layer = "cardUpper"
or
cardObject.layer = "cardLower"
I hope that helps!
Ok, so its pretty simple. So you are going to want to add another canvas component to the game object, and check the override sorting to true. Then use
cardObject.GetComponent<Canvas>().sortingOrder = 1;
to place it in the front and
cardObject.GetComponent<Canvas>().sortingOrder = 0;
to put it in the back.
you are also going to need to put a GraphicsRaycaster on to each of the cardObjects
Ignore my other answer about sprite renderers, they are not needed here
I am writing a new Imageview with mask in Javafx.
The mask can show according to the direction of mouse.
Using Timeline makes it work well. But when the mask leaves, the mask is still visible.
I want to hide the part of mask, which is out of the Image. How can I scale it?
When mouse enter:
When mouse leave:
And my animation code
private void leftOut() {
KeyFrame k1 = new KeyFrame(Duration.ZERO, new KeyValue(mask.translateXProperty(), 0));
KeyFrame k2 = new KeyFrame(time, new KeyValue(mask.translateXProperty(), control.getWidth()*-1.0));
timeline = new Timeline();
timeline.getKeyFrames().addAll(k1,k2);
timeline.play();
}
Other directions are just like this.
You don't need to resize the mask at all. Simply apply a clip to it that has the same size as the image:
Image image = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF.jpg/164px-Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF.jpg");
ImageView imageView = new ImageView(image);
// init mask
Label mask = new Label("SomeText");
mask.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
mask.setPrefSize(image.getWidth(), image.getHeight());
mask.setStyle("-fx-background-color: rgba(255, 255, 255, 0.6);");
mask.setAlignment(Pos.CENTER);
// create clip
Rectangle clip = new Rectangle(image.getWidth(), image.getHeight());
mask.setClip(clip);
// keep clip in position horizontally in root
clip.translateXProperty().bind(mask.translateXProperty().negate());
StackPane root = new StackPane(imageView, mask);
root.setPrefSize(500, 500);
root.setStyle("-fx-background-color: orange;");
// probably a bit simpler to use TranslateTransition instead of Timeline
TranslateTransition transition = new TranslateTransition(Duration.seconds(5), mask);
transition.setByX(image.getWidth());
transition.play();
What I'm trying to do:
I'm trying to render a game board for a user using a canvas. The user
will be able to zoom.
Rendering the entire game board is impossible when the user is
zoomed in as the game board is very large. Instead I will render what
is onscreen every 1/30 sec.
I would like to create the illusion of the entire game board beings
inside a gui like the ScrollPane where the user can scroll with a
mouse.
In reality the ScrollPane will be linked to a single canvas exactly
the size of the viewport.
I'm not sure what the right way to go about this is. The scroll pane does not seem to have a property that allows you to pretend it has a large object in it. I could place a large canvas in the scroll pane but large canvases take up memory, as my crashes have suggested. Should I be using an AnchorPane or something? What's the proper way to do this?
It seems like putting an AnchorPane into the ScrollPane, setting that AnchorPane to the correct size (the size of the entire gameboard) works well to let the user scroll freely. From there all you have to do is put a canvas in the AnchorPane at the right place and keep moving it (and redrawing it) makes everything work out. To make it run smoothly I think you should render 100 to 500 pixels outside of the viewport of the ScrollPane. Here's some example code from my project:
public class GridViewPane {
/** ScrollPane that this grid exits in */
private ScrollPane gridScroll;
/** The anchor pane immediately inside of {#link #gridScroll}. The
* dimensions of this equals the dimensions of the {#link #can} if it was to
* render the entire grid at once */
private Pane gridView;
/** Exists in {#link #gridView} and is exactly the size of the viewport of
* {#link #gridScroll} */
private Canvas can;
/** A label the specifies what hex is currently moused-over */
private Label currentHexLabel;
public void update() {
// sets the canvas to the right size if needed
final Bounds viewportSize = gridScroll.getBoundsInLocal();
if (can == null || !can.getBoundsInLocal().equals(viewportSize)) {
can = new Canvas(viewportSize.getWidth(),
viewportSize.getHeight());
gc = can.getGraphicsContext2D();
}
// sets the anchorpane to the right size
final double[] dim = getPixleSizeOfGrid(w.columns(), w.rows(), r);
if (gridView == null) {
gridView = new AnchorPane();
gridScroll.setContent(gridView);
}
Bounds oldDim = gridView.getBoundsInLocal();
if (!(oldDim.getWidth() == dim[0]
&& oldDim.getHeight() == dim[1])) {
gridView.setPrefSize(dim[0], dim[1]);
}
// puts the canvas in the right position
if (!gridView.getChildren().contains(can))
gridView.getChildren().add(can);
double[] x = getXScreenRange();
double[] y = getYScreenRange();
can.relocate(x[0], y[0]);
// *********** Drawing Hexes ***********/
}
}
My purpose is to draw in the CENTER of the composite. Actually, I have an rcp view and I'm drawing some shapes inside it. this is the code that I use :
display = parent.getDisplay();
white= display.getSystemColor(SWT.COLOR_WHITE);
parent.setLayout(new FillLayout(SWT.VERTICAL));
// Create the ScrolledComposite to scroll horizontally and vertically
final ScrolledComposite sc =new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
sc.setExpandHorizontal(true);
sc.setExpandVertical(true);
sc.setMinHeight(100);
sc.setMinWidth(100);
sc.setSize(100,100);
Composite child = new Composite(sc,SWT.NONE);
child.setLayout(new FillLayout());
child.layout(true);
parent.addListener (SWT.Resize, new Listener () {
public void handleEvent (Event e) {
x = child.getBounds().width/2;
y = child.getBounds().height/2;
child.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
dessin(gc); // to raw the circle
}
});
sc.getDisplay().update();
}
});
I defined the view with a ratio (so when the view is empty I get the wanted size)...I don't know the exact size of the view since it can be resized by the user at anymoment, or when an editor is opened... So, my problem is how to draw just in the center of the view and keep the drawings in the center even if the view is resized...
PS: Using (Point.x and point.y), I get (0,0) when the view appears first, then I get other values...
Pleaaaaaaaaaaaaaase help
You can use getOrigin() method on ScrolledComposite, which will return Point instance with the point in the content that currently appears in the top left corner of the scrolled composite. See docs getOrigin method on ScrolledComposite.
With that information and size of the component which you'll get from getBounds() method you can easily calculate the 'real' center.
I've been trying to create a Draw2D Figure that consists of two parts - a central resizeable shape, such as a circle or rectangle, and an editable label for the bottom part. An example of this type of figure is the icon/label you see on a computer's Desktop.
The first attempt was to create a parent container figure with two child sub-figures - a shape figure placed centrally and a label placed at the bottom. It also implemented HandleBounds so that selection and resizing occurs only on the upper shape sub-figure. This turned out not to be a working solution because as the label gets wider with more text so does the main parent figure and consequently the central shape figure. In other words the overall parent figure is as wide as the child label figure.
What I'm seeking is a Figure that maintains the size of the shape figure but allows the width of the label figure to grow independently. Exactly the same behaviour as a desktop icon.
Ok I get your question right now. It's impossible to do what you want:
The parent figure can't be smaller than one of its children or this child will not be visible !!!
You have to create a container figure as you mentioned with an XYLayout and "manually" place and "size" the 2 (the shape and the label) children figure inside this layout using the IFigure.add(IFigure child, Object constraint) method with a Constraint of type Rectangle (Draw2d)
Edit with code sample
Here is an example of what your figure class could look like:
package draw2dtest.views;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Ellipse;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.FigureListener;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.MouseListener;
import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Rectangle;
public class LabeledFigure extends Figure {
private final Figure shapeFigure;
private final Label labelFigure;
private Rectangle customShapeConstraint;
public LabeledFigure(String label) {
setLayoutManager(new XYLayout());
setBackgroundColor(ColorConstants.lightGray);
setOpaque(true);
shapeFigure = new Ellipse();
this.add(shapeFigure);
shapeFigure.setBackgroundColor(ColorConstants.yellow);
shapeFigure.addMouseListener(new MouseListener.Stub() {
#Override
public void mousePressed(MouseEvent me) {
customShapeConstraint = new Rectangle(
(Rectangle) LabeledFigure.this.getLayoutManager()
.getConstraint(shapeFigure));
customShapeConstraint.width -= 6;
customShapeConstraint.x += 3;
LabeledFigure.this.getLayoutManager().setConstraint(
shapeFigure, customShapeConstraint);
LabeledFigure.this.revalidate();
}
});
labelFigure = new Label(label);
labelFigure.setOpaque(true);
labelFigure.setBackgroundColor(ColorConstants.green);
labelFigure.addMouseListener(new MouseListener.Stub() {
#Override
public void mousePressed(MouseEvent me) {
Rectangle shapeFigureConstraint = new Rectangle(0, 0,
bounds.width, bounds.height - 15);
LabeledFigure.this.getLayoutManager().setConstraint(
shapeFigure, shapeFigureConstraint);
LabeledFigure.this.revalidate();
}
});
this.add(labelFigure);
this.addFigureListener(new FigureListener() {
#Override
public void figureMoved(IFigure source) {
Rectangle bounds = LabeledFigure.this.getBounds();
Rectangle shapeFigureConstraint = new Rectangle(0, 0,
bounds.width, bounds.height - 15);
LabeledFigure.this.getLayoutManager().setConstraint(
shapeFigure, shapeFigureConstraint);
Rectangle labelFigureConstraint = new Rectangle(0,
bounds.height - 15, bounds.width, 15);
if (customShapeConstraint != null) {
labelFigureConstraint = customShapeConstraint;
}
LabeledFigure.this.getLayoutManager().setConstraint(
labelFigure, labelFigureConstraint);
}
});
}
}
This is not a clean class but it should be a good entry to show you how to achieve your goal. This is an example based on pure Draw2d without any Gef code, thus the resizing of the shape is done by clicking in the yellow Ellipse (the size is decreased) and on the green label (the initial size is restored)
To test this class I created a simple Eclipse view as following:
#Override
public void createPartControl(Composite parent) {
FigureCanvas fc = new FigureCanvas(parent, SWT.DOUBLE_BUFFERED);
fc.setBackground(ColorConstants.red);
Panel panel = new Panel();
panel.setLayoutManager(new XYLayout());
LabeledFigure labeledFigure = new LabeledFigure("This is the label");
fc.setContents(panel);
panel.add(labeledFigure, new Rectangle(10,10, 200,100));
}
Hope this can help,
Manu