Set scene width and height - javafx-2

I have been trying to set the scene's width and height outside of the constructor and it's been to no avail. After looking through the Scene API I saw a method that lets you get the height and width respectively but not one to set the method.. :s (design flaw maybe).
After further research I came across the SceneBuilder and found methods that could modify the height and width. However, I do not know how to apply it to a scene object already created or how to create a SceneBuilder object that could be used in place of the scene object.

Once you created Scene and assigned it to the Stage you can use Stage.setWidth and Stage.setHeight to change both stage and scene sizes simultaneously.
SceneBuilder can't be applied to an already created object, it can only be used for the scene creation.

It seems not possible to set the size of the Scene after it has been created.
Setting the size of the Stage means to set the size of the window, which includes the size of the decoration. So the Scene is smaller in size, unless the Stage is undecorated.
My solution is to compute the size of the decoration while initialization and add it to the size of the Stage when resizing:
private Stage stage;
private double decorationWidth;
private double decorationHeight;
public void start(Stage stage) throws Exception {
this.stage = stage;
final double initialSceneWidth = 720;
final double initialSceneHeight = 640;
final Parent root = createRoot();
final Scene scene = new Scene(root, initialSceneWidth, initialSceneHeight);
this.stage.setScene(scene);
this.stage.show();
this.decorationWidth = initialSceneWidth - scene.getWidth();
this.decorationHeight = initialSceneHeight - scene.getHeight();
}
public void resizeScene(double width, double height) {
this.stage.setWidth(width + this.decorationWidth);
this.stage.setHeight(height + this.decorationHeight);
}

I just wanted to post another answer for those who might have had a similar problem as mine.
http://docs.oracle.com/javase/8/javafx/api/javafx/scene/Scene.html
There is no setWidth() or setHeight(), and the property is ReadOnly, but if you look at
Constructors
Scene(Parent root)
Creates a Scene for a specific root Node.
Scene(Parent root, double width, double height)
Creates a Scene for a specific root Node with a specific size.
Scene(Parent root, double width, double height, boolean depthBuffer)
Constructs a scene consisting of a root, with a dimension of width and height, and specifies whether a depth buffer is created for this scene.
Scene(Parent root, double width, double height, boolean depthBuffer, SceneAntialiasing antiAliasing)
Constructs a scene consisting of a root, with a dimension of width and height, specifies whether a depth buffer is created for this scene and specifies whether scene anti-aliasing is requested.
Scene(Parent root, double width, double height, Paint fill)
Creates a Scene for a specific root Node with a specific size and fill.
Scene(Parent root, Paint fill)
Creates a Scene for a specific root Node with a fill.
As you can see, this is where you can set the height and width if you need to.
For me, I am using SceneBuilder, just as you described you were doing, and needed the width and height of that. I am creating custom controls, so it was weird that it didn't do it automatically, so this is how to do it if you need to.
I could have used setWidth() / setHeight() from the Stage as well.

I had an similar challenge when I wanted to switch between different size scenes in JavaFX.
I did my scene switching by changing the Parent resource of a Scene. The new resource would have larger dimensions than the previous resource so the entire Scene wasn't visible.
Like user2229691's answer stated - my issue turned out to be the Window not matching the Scene's dimensions.
Although I'm not able to comment about decorations - I was able to solve my issue by using:
sceneName.getWindow().sizeToScene();
It works when downsizing and upsizing from my own testing.
Here's some example code so you can better understand what I did:
public void start(Stage stage) throws IOException {
// Height of 300, width of 300
Parent scene1 = FXMLLoader.load(getClass().getResource("scene1.fxml"));
// Height of 400, width of 400
Parent scene2 = FXMLLoader.load(getClass().getResource("scene2.fxml"));
// Starting off in the smaller scene
Scene mainScene = new Scene(scene1); // Scene & Window are 300x300
stage.setScene(mainScene);
stage.show();
// Switching to the larger scene resource, that doesn't fit in the window
mainScene.setRoot(scene2); // Scene is 400x400 but can only see 300x300
// Resizing the window so the larger scene fits
mainScene.getWindow().sizeToScene(); // Window is now 400x400 & everything is visible
}
I hope this helps someone.
Source:
https://docs.oracle.com/javase/8/javafx/api/javafx/stage/Window.html#sizeToScene--

Related

JavaFX TabPane not filling space

I've placed some content inside a TabPane's tab, but the TabPane's width seems to me being equal to the longest element inside the tab (the longest element is not visible on the picture).
The TabPane is inside an AnchorPane with zero valued anchors, that's why I would expect it to fill the whole space, not just the size of the longest element inside..
How could I make the TabPane fill all available space?
UPDATE:
I've forgot to mention, that my a TabView is loaded through the FXMLLoader.load() method and it's added as a child to the AnchorPane..
If you add the tabPane in the code, you could try like this :
AnchorPane anchorPaneContent = new AnchorPane();
TabPane node = new TabPane();
AnchorPane.setTopAnchor(node, 0.0);
AnchorPane.setLeftAnchor(node, 0.0);
AnchorPane.setRightAnchor(node, 0.0);
AnchorPane.setBottomAnchor(node, 0.0);
anchorPaneContent.getChildren().add(node);
If you add the tabPane in the SceneBuilder, you just need to make the tabpane fit it's parent by right clicking on the node.

When inflating a view in OnCreateView, is it supposed to have correct size right after creation?

I have a Fragment class which I use with Android.support.v4.view.ViewPager.
In the OnCreateView, that is called by the pager when it needs to display another page, I create a label and I want to dynamically set some font sizes inside that label to be a percentage of the label's height:
class LabelFragment : Android.Support.V4.App.Fragment
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
var v = inflater.Inflate(Resource.Layout.Label, container, false);
float BigFontSize = v.Height / 1.5f;
float MediumFontSize = v.Height / 5.0f;
float SmallFontSize = v.Height / 10.0f;
// Setting font sizes for the children of v
return v;
}
}
When I wrote this code my understanding was that v will have correct Width and Height right after inflating.
Indeed, this works on the emulators and on some devices, but on some other devices the label child elements disappear because they get zero font size assigned, which means v.Height was zero.
Is this code valid? Is it documented whether the view returned by Inflate should have correct dimensions? Should I be taking height from container instead, and if so, is it documented that it will be non-null when used with ViewPager?
The problem is that the view has not yet been drawn in the OnCreateView method. So Height will most likely be an unexpected value.
OK, so what can you do to get the height of the view? Use ViewTreeObserver to get the PreDraw event like:
v.ViewTreeObserver.PreDraw += (s, e) =>
{
// do height calculations here
};
Also I noticed you are using font sizes, please make sure you are not using pixel heights for them and use a scaleable unit instead (sp). This is presumably why you are setting font size in the first place.

JavaFX fullscreen - resizing elements based upon screen size

Is there any way to make fullscreen(and if possible resizing too) to instead of rearranging everything (actually what it does is to rearrange the elements like resizing but to the whole screen) to make an actual fullscreen mode? (like games that what usually do is change screen resolution), so that buttons and text grows accordingly to the size of the screen/window
Also how can I remove the message and the effect on click the "esc" key to exit the fullscreen mode?
EDIT: use this way to make resizeable
#Override public void start(Stage stage) throws Exception{
final int initWidth = 720; //initial width
final int initHeight = 1080; //initial height
final Pane root = new Pane(); //necessary evil
Pane controller = new CtrlMainMenu(); //initial view
controller.setPrefWidth(initWidth); //if not initialized
controller.setPrefHeight(initHeight); //if not initialized
root.getChildren().add(controller); //necessary evil
Scale scale = new Scale(1, 1, 0, 0);
scale.xProperty().bind(root.widthProperty().divide(initWidth)); //must match with the one in the controller
scale.yProperty().bind(root.heightProperty().divide(initHeight)); //must match with the one in the controller
root.getTransforms().add(scale);
final Scene scene = new Scene(root, initWidth, initHeight);
stage.setScene(scene);
stage.setResizable(true);
stage.show();
//add listener for the use of scene.setRoot()
scene.rootProperty().addListener(new ChangeListener<Parent>(){
#Override public void changed(ObservableValue<? extends Parent> arg0, Parent oldValue, Parent newValue){
scene.rootProperty().removeListener(this);
scene.setRoot(root);
((Region)newValue).setPrefWidth(initWidth); //make sure is a Region!
((Region)newValue).setPrefHeight(initHeight); //make sure is a Region!
root.getChildren().clear();
root.getChildren().add(newValue);
scene.rootProperty().addListener(this);
}
});
}
There are a couple of ways to resize your UI.
Scale by Font Size
You can scale all controls by setting -fx-font-size in the .root of your scene's style sheet.
For example, if you apply the following stylesheet to your scene, then all controls will be doubled in size (because the default font size is 13px).
.root {
-fx-font-size: 26px;
}
The above will work to scale controls, which is fine for things which are completely control based, but not so good for things which are graphic and shape based.
Scale by Transform
Apply a Scale transform pivoted at (0,0) to your scene's root node.
Scale scale = new Scale(scaleFactor, scaleFactor);
scale.setPivotX(0);
scale.setPivotY(0);
scene.getRoot().getTransforms().setAll(scale);
To scale a game I developed which includes graphics and various shapes, I used a letter boxing technique which sized the game window to a constant aspect ratio, (similar to the letter boxing you see when you watch a 4:3 tv show on a 16:9 screen).
The SceneSizeChangeListener in the code below listens for changes to the scene size and scales the content of the scene appropriate to the available scene size.
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.transform.Scale;
import javafx.stage.Stage;
import org.jewelsea.games.supersnake.layout.LayoutController;
import java.io.IOException;
import java.util.ResourceBundle;
/* Main JavaFX application class */
public class SuperSnake extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(final Stage stage) throws IOException {
FXMLLoader loader = new FXMLLoader(
getClass().getResource("layout/layout.fxml"),
ResourceBundle.getBundle("org.jewelsea.games.supersnake.layout.text")
);
Pane root = (Pane) loader.load();
GameManager.instance().setLayoutController(loader.<LayoutController>getController());
Scene scene = new Scene(new Group(root));
stage.setScene(scene);
stage.show();
GameManager.instance().showMenu();
letterbox(scene, root);
stage.setFullScreen(true);
}
private void letterbox(final Scene scene, final Pane contentPane) {
final double initWidth = scene.getWidth();
final double initHeight = scene.getHeight();
final double ratio = initWidth / initHeight;
SceneSizeChangeListener sizeListener = new SceneSizeChangeListener(scene, ratio, initHeight, initWidth, contentPane);
scene.widthProperty().addListener(sizeListener);
scene.heightProperty().addListener(sizeListener);
}
private static class SceneSizeChangeListener implements ChangeListener<Number> {
private final Scene scene;
private final double ratio;
private final double initHeight;
private final double initWidth;
private final Pane contentPane;
public SceneSizeChangeListener(Scene scene, double ratio, double initHeight, double initWidth, Pane contentPane) {
this.scene = scene;
this.ratio = ratio;
this.initHeight = initHeight;
this.initWidth = initWidth;
this.contentPane = contentPane;
}
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
final double newWidth = scene.getWidth();
final double newHeight = scene.getHeight();
double scaleFactor =
newWidth / newHeight > ratio
? newHeight / initHeight
: newWidth / initWidth;
if (scaleFactor >= 1) {
Scale scale = new Scale(scaleFactor, scaleFactor);
scale.setPivotX(0);
scale.setPivotY(0);
scene.getRoot().getTransforms().setAll(scale);
contentPane.setPrefWidth (newWidth / scaleFactor);
contentPane.setPrefHeight(newHeight / scaleFactor);
} else {
contentPane.setPrefWidth (Math.max(initWidth, newWidth));
contentPane.setPrefHeight(Math.max(initHeight, newHeight));
}
}
}
}
Here is a screenshot where you can see the letterboxing and scaling taking effect. The green grass in the middle is the main game content screen and scales up and down to fit the available screen area. The wood texture around the outside provides a flexibly sized border which fills in the area where the black letterbox bars would normally be if you were watching a tv program at a different aspect ratio to your screen. Note that the background in the screenshot below is blurry at the title page because I make it so, when the game starts, the blur effect is removed and the view is crisp regardless of the size.
Windowed version:
Scaled full screen version:
You might think that the scaling method above might make everything go all blocky and pixelated, but it doesn't. All font's and controls scale smoothly. All standard drawing and graphic commands and css based styles scale smoothly as they are all vector based. Even bitmapped images scale well because JavaFX uses fairly high quality filters when scaling the images.
One trick to get good scaling on images is to provide high resolution images, so that when the screen scales up, the JavaFX system has more raw data to work from. For example, if the preferred window size for an app is quarter of the screen size and it contains a 64x64 icon, instead use a 128x128 icon, so that when the app is put in full screen and all elements scaled, the scaler has more raw pixel data samples to use for interpolating values.
The scaling is also fast as it is hardware accelerated.
how can I remove the message and the effect on click the "esc" key to exit the fullscreen mode?
It's not possible to remove the full screen exit message in JavaFX 2.2, it will be possible in JavaFX 8:
RT-15314 Allow trusted apps to disable the fullscreen overlay warning and disable the "Exit on ESC" behavior
It will be nice when that is done, because then my games won't have that "look at me - I look like a beta" feel about them.
"Also how can I remove the message and the effect on click the "esc" key to exit the fullscreen mode?"
Use this code :
stage.setFullScreenExitHint("");
It will change the string message "Press Esc to quit Fullscreen mode" into empty string so it will not show up.
You may copy this into JavaFXApplication
Dimension resolution = Toolkit.getDefaultToolkit().getScreenSize();
double width = resolution.getWidth();
double height = resolution.getHeight();
double w = width/1280; // your window width
double h = height/720; // your window height
Scale scale = new Scale(w, h, 0, 0);
root.getTransforms().add(scale);

JavaFX SplitPane Divider Position Inconsistent Behaviour

I have two StackPanes (pane1 & pane2) being displayed in a SplitPane (splitPane). The SplitPane's divider position is set to .3 in the builder. The seond StackPane (pane2) contains a Button which also sets the SplitPane's divider position to .3.
Ceteris paribus, the expected behaviour would be that both actions (setting the divider position in the builder & setting the divider position through an action) either work or not.
Yet, only the latter actually works.
What changes between the construction of the SplitPane and the onAction of the Button. What hinders the placement of the divider in the builder?
StackPane pane1 = new StackPane();
StackPane pane2 = new StackPane();
final SplitPane splitPane = SplitPaneBuilder.create()
.items(pane1, pane2)
// Environment A
.dividerPositions(new double[] {.3}) // splitPane.setDividerPosition(s)(...), etc. yield same result
.orientation(Orientation.HORIZONTAL)
.build();
// The following line influences environment A outcome, though it does not fix the issue
SplitPane.setResizableWithParent(pane1, false);
pane2.getChildren().add(ButtonBuilder.create()
.text("Divider Position")
.onAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// Environment B
splitPane.setDividerPositions(.3);
}
})
.build());
Scene primaryScene = SceneBuilder.create()
.root(splitPane)
.build();
primaryStage.setScene(primaryScene);
primaryStage.setTitle("Name");
primaryStage.setWidth(500);
primaryStage.setHeight(500);
primaryStage.show();
I had a problem regarding the position of the split pane and it seems similar with yours. Here it is: I have a tabpane, and whenever I add a new tab, I load the content of the tab from an fxml, in which there exists a split pane, with divider position fixed to 0.8.
When the first tab is loaded, it is ok, the divider is positioned at 0.8. When I add the second tab, exactly in the same way with the first tab via fxml, the position of the split pane in the second tab is 0.5, i.e. the default value.
I tried to programatically set the divider position with setDividerPositions method after loading the content of the tab, but it did not work. Then as in your case, if I place the setDividerPositions call in an onAction method, it successfully sets the divider position.
As I stated, my problem is not exactly same as yours, but seems similar. So I am not sure but hope my work around will help you: In my situation, placing the setDividerPositions call in Platform.runLater did the trick.
So, thanks to James D in the Oracle support forums, here is the full answer:
"The issue appears to be that the scene's size is smaller than that of its window. This causes an additional layout pass when the window is first displayed; the second layout pass counts as a "window resize" and consequently the divider position of the split pane is not respected on this pass."
(https://forums.oracle.com/forums/thread.jspa?threadID=2503701)
Workaround 1
Set the Scene's size instead of the Stage's size:
Scene primaryScene = SceneBuilder.create()
.root(splitPane)
.width(500)
.height(500)
.build();
primaryStage.setScene(primaryScene);
primaryStage.setTitle("Name");
primaryStage.show();
Workaround 2
As Ramazan has correctly pointed out, another solution would be to set the divider position in Platform.runLater(...).
Followups
I have filed a bug at jira (http://javafx-jira.kenai.com/browse/RT-28607), referencing the original bug report (http://javafx-jira.kenai.com/browse/RT-17229) which had been marked "unresolveable". I guess the final solution would be to provide methods for the absolute positioning of the divider.

MonoTouch: How to resize a view.Frame inside Draw override and draw correctly?

the problem I am having it that if inside the UIView Draw override, I change the view frame size, drawing a rectangle is not working as expected.
If I change the view frame size outside of the Draw override, it works fine. Is this an expected behavior or is it a problem with monotouch only?
This is the code I am using:
class ChildView : UIView
{
public override void Draw (RectangleF rect)
{
base.Draw (rect);
CGContext g = UIGraphics.GetCurrentContext();
//adding 30 points to view height
RectangleF rec = new RectangleF(this.Frame.Location,this.Frame.Size);
rec.Height+=30;
RectangleF rec_bounds = new RectangleF(0,0,rec.Width,rec.Height);
this.Frame=rec;
this.Bounds=rec_bounds;
//drawing a red rectangle to the first half of view height
UIColor.Red.SetFill();
RectangleF _rect = new RectangleF(this.Bounds.Location,this.Bounds.Size);
_rect.Height=_rect.Height/2;
g.FillRect(_rect);
}
}
However, the output of this code is this: (it should draw only 30 points red, but it draws 60 points)
Here is a link to download the project to reproduce this issue:
www.grbytes.com\downloads\RectangleDrawProblem.rar
Καλημέρα!
This behavior is expected. If you want to change the view's frame inside the Draw override, do it before getting the current context. That is because the graphics context also has a size and that is the size of the view at the time you are retrieving it.
Also, there is no need to set both the Bounds and the Frame of the view. You can just set either of them in this case.
By the way, I don't think you need to call base.Draw(). According to the Apple documentation, "If you subclass UIView directly, your implementation of this method does not need to call super."

Resources