I'm trying to code a simple maze with a character that moves using a Joystick (touchpad). I used a Fit viewport to maintain the aspect ration of my maze while keeping it at the center of the screen;
However, I find that that the joystick is also added beside the maze. sample of what it looks like currently
I want the joystick to be of the far bottom-left of the screen. I tried doing this using a different viewport for UI elements (including the joystick), but then the whole maze just gets distorted.
Here is part of my BaseScreen class, an extension I made to reuse in other games as well:
public abstract class BaseScreen implements Screen, InputProcessor {
public BaseScreen(){
gameWorldHeight = 1024;
gameWorldWidth = 1024;
cam = new OrthographicCamera(gameWorldWidth, gameWorldHeight);
port = new FitViewport(cam.viewportWidth, cam.viewportHeight, cam);
port.apply();
cam.update();
mainStage = new Stage(port);
uiStage = new Stage(port);
uiTable = new Table();
uiTable.setFillParent(true);
uiStage.addActor(uiTable);
initialize();
}
public abstract void initialize();
public void render(float dt) {
mainStage.getCamera().update();
uiStage.getCamera().update();
uiStage.act(dt);
mainStage.act(dt);
update(dt);
mainStage.draw();
uiStage.draw();
}
public abstract void update(float dt);
public void resize(int width, int height) {
mainStage.getViewport().update(width,height, true);
uiStage.getViewport().update(width,height,false);
}
}
Here is also a part of my gameScreen class, where I actually code all the core mechanics of the game
public class gameScreen extends BaseScreen {
#Override
public void initialize() {
//..
uiTable.pad(10);
uiTable.add().expandX().expandY();
uiTable.row();
uiTable.add(ball.touchpad).left();
uiTable.add().expandX();
}
}
and in case you were wondering, here is what ball.touchpad is
public class Ball extends BaseActor {
public Touchpad touchpad;
public Touchpad.TouchpadStyle touchpadStyle;
public Skin touchpadSkin;
public Drawable touchBackground;
public Drawable touchKnob;
public Ball (float x, float y, Stage s) {
//..
//Create a touchpad skin
touchpadSkin = new Skin();
//Set background image
touchpadSkin.add("touchBackground", new Texture("touchBackground.png"));
//Set knob image
touchpadSkin.add("touchKnob", new Texture("touchKnob.png"));
//Create TouchPad Style
touchpadStyle = new Touchpad.TouchpadStyle();
//Create Drawable's from TouchPad skin
touchBackground = touchpadSkin.getDrawable("touchBackground");
touchKnob = touchpadSkin.getDrawable("touchKnob");
//Apply the Drawables to the TouchPad Style
touchpadStyle.background = touchBackground;
touchpadStyle.knob = touchKnob;
//Create new TouchPad with the created style
touchpad = new Touchpad(10, touchpadStyle);
//setBounds(x,y,width,height)
touchpad.setBounds(15, 15, 200, 200);
}
So how can i add this touchpad on the bottom left of the screen?
Related
Depending on the screen size (and device idiom?) the width of the master page varies: On phones it is about 80 % of the screen width, while on tablets it seems to be a constant dimension like 320 dp.
Does anybody know a general formula for this value? I'd like to use it for laying out some elements during construction time, when the Width property isn't set, yet.
Edit:
I know how to get the current screen size. But how does the width of the presented master page of Xamarin.Form's master-detail page relate to it? It doesn't cover the whole screen, but fills a different fraction of it depending on the device.
You could request the device's actual screen width, height, and scale factor.
(From https://github.com/mattregul/Xamarin_GetDeviceScreensize)
iOS AppDelegate.cs
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
// Store off the device sizes, so we can access them within Xamarin Forms
App.DisplayScreenWidth = (double)UIScreen.MainScreen.Bounds.Width;
App.DisplayScreenHeight = (double)UIScreen.MainScreen.Bounds.Height;
App.DisplayScaleFactor = (double)UIScreen.MainScreen.Scale;
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
}
Android MainActivity.cs
[Activity(Label = "Xamarin_GetDeviceScreensize.Droid", Icon = "#drawable/icon", Theme = "#style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
// Store off the device sizes, so we can access them within Xamarin Forms
App.DisplayScreenWidth = (double)Resources.DisplayMetrics.WidthPixels / (double)Resources.DisplayMetrics.Density; // Width = WidthPixels / Density
App.DisplayScreenHeight = (double)Resources.DisplayMetrics.HeightPixels / (double)Resources.DisplayMetrics.Density; // Height = HeightPixels / Density
App.DisplayScaleFactor = (double)Resources.DisplayMetrics.Density;
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
}
Xamarin Forms App.cs
public class App : Application
{
public static double DisplayScreenWidth;
public static double DisplayScreenHeight;
public static double DisplayScaleFactor;
public App()
{
string ScreenDetails = Device.OS.ToString() + " Device Screen Size:\n" +
$"Width: {DisplayScreenWidth}\n" +
$"Height: {DisplayScreenHeight}\n" +
$"Scale Factor: {DisplayScaleFactor}";
// The root page of your application
var content = new ContentPage
{
Title = "Xamarin_GetDeviceScreensize",
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children = {
new Label {
HorizontalTextAlignment = TextAlignment.Center,
FontSize = Device.GetNamedSize (NamedSize.Large, typeof(Label)),
Text = ScreenDetails
}
}
}
};
MainPage = new NavigationPage(content);
}
}
Please go through the image below.
In the above image you can find that horizontal scrolling is not started.
Now visit the After Scroll image of the same contents...
Now in the second image you can see that horizontal scrolling is done ...
JFXPanel contents are scroll horizontally... Which was perfect...
Now the third image will describe the problem....
It is liitle bit stretched to see as it is maximized...
You can see that the JFXPanel contents have changed their original position...
Moreover the contents must start with X_DisplaceMent = 0.0 [X-Cordinate], which was done automatically in the first two images...
All the contents are nodes like [Rectangle,Line etc.. ], after that all are placed in Group node...
And this Group node is set in the ScrollPane through
js.setContent(Group node);
Each component is placed with given x,y cordiante value .. then how did this happen while doing the maximized ?
Please help me to find the root cause ...
Thanks in advance...
Here are some facts that cause the problem.
- Start Position of Scene : 0.0
- Start Position of Group in Scene : 49.5
- Width of the root : 364.5
- Start Position of Scene : 0.0
- Start Position of Group in Scene : 63.5
- Width of the root : 364.5
- Start Position of Scene : 0.0
- Start Position of Group in Scene : 83.5
- Width of the root : 364.5
Whenever we drag the window horizontally Group is moving in the scene... That should not happen... how to avoid this ...
Ok... Here is the MCVE.....
There is a Frame. which contain SplitPane having vertical split.
The SplitPane will show the contents of two JFxPanels.
Both fxpanels are having rectangle on same x cordinate but Y cordinate is different.
And both the fxPanels are horizontal scroll sync. Not bi-directional. When you scroll lower panel horizonatally, the upper panel will get scrolled due to horizontal sync.
Here is the code for fxPanel 1...
public class FxPanel1 extends JFXPanel
{
private ScrollPane scroll ;
public ScrollPane getJs() {
return scroll;
}
public void setJs(ScrollPane js) {
this.scroll = js;
}
private boolean initFX(JFXPanel fxPanel) {
Scene scene = createScene();
fxPanel.setScene(scene);
return true;
//craneAssignmentChartView.setFxPanel(fxPanel);
}
private Scene createScene() {
Group root = new Group();
Rectangle rect = new Rectangle(10.0, 20.0, 800, 40);
rect.setFill(javafx.scene.paint.Color.TRANSPARENT);
rect.setStroke(javafx.scene.paint.Color.RED);
AnchorPane anchor = new AnchorPane();
anchor.getChildren().add(rect);
GridPane grid = new GridPane();
grid.setHgap(0);
grid.setVgap(0);
grid.setPadding(new Insets(0, 0, 0, 0));
grid.add(anchor, 1, 0);
root.getChildren().add(grid);
ScrollPane scroll = new ScrollPane();
scroll.setHbarPolicy(ScrollBarPolicy.ALWAYS);
scroll.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
scroll.setContent(root);
setJs(scroll);
return new Scene(scroll, javafx.scene.paint.Color.WHITE);
}
private void createUI(final JFXPanel fxPanel)
{
Platform.runLater(new Runnable() {
#Override
public void run()
{
initFX(fxPanel);
}
});
}
public FxPanel1( JFXPanel fxPanel)
{
createUI(fxPanel);
}
}
Now the code for second fxPanel looks like ...
public class FxPanel2 extends JFXPanel
{
private ScrollPane scroll ;
public ScrollPane getJs() {
return scroll;
}
public void setJs(ScrollPane js) {
this.scroll = js;
}
private boolean initFX(JFXPanel fxPanel) {
Scene scene = createScene();
fxPanel.setScene(scene);
return true;
//craneAssignmentChartView.setFxPanel(fxPanel);
}
private Scene createScene() {
Group root = new Group();
Rectangle rect = new Rectangle(10.0, 180.0, 800, 40);
rect.setFill(javafx.scene.paint.Color.TRANSPARENT);
rect.setStroke(javafx.scene.paint.Color.RED);
AnchorPane anchor = new AnchorPane();
anchor.getChildren().add(rect);
GridPane grid = new GridPane();
grid.setHgap(0);
grid.setVgap(0);
grid.setPadding(new Insets(0, 0, 0, 0));
grid.add(anchor, 1, 0);
root.getChildren().add(grid);
ScrollPane scroll = new ScrollPane();
scroll.setHbarPolicy(ScrollBarPolicy.ALWAYS);
scroll.setVbarPolicy(ScrollBarPolicy.NEVER);
scroll.setContent(root);
setJs(scroll);
return new Scene(scroll, javafx.scene.paint.Color.WHITE);
}
private void createUI(final JFXPanel fxPanel)
{
Platform.runLater(new Runnable() {
#Override
public void run()
{
initFX(fxPanel);
}
});
}
public FxPanel2( JFXPanel fxPanel)
{
createUI(fxPanel);
}
}
The main class looks like ....
public class DemoToCheckUIAlignment extends JFrame
{
public static void main(String[] args)
{
final DemoToCheckUIAlignment demo = new DemoToCheckUIAlignment();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFXPanel panel1 = new JFXPanel();
FxPanel1 fxObj1 = new FxPanel1(panel1);
JFXPanel panel2 = new JFXPanel();
FxPanel2 fxObj2 = new FxPanel2(panel2);
DemoToCheckUIAlignment frame = new DemoToCheckUIAlignment();
frame.setSize(800, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JSplitPane chartSplitPane = new JSplitPane();
chartSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
chartSplitPane.setDividerLocation(200);
chartSplitPane.setDividerSize(2);
chartSplitPane.setTopComponent(panel1);
chartSplitPane.setBottomComponent(panel2);
demo.provideScrollSyncBetweenFXPanels(fxObj1.getJs(), fxObj2.getJs());
frame.getContentPane().add(chartSplitPane);
//frame.getContentPane().add(panel2);
frame.setVisible(true);
}
});
}
public static void provideScrollSyncBetweenFXPanels(final ScrollPane upperSP, final ScrollPane lowerSP)
{
lowerSP.hvalueProperty().addListener(new ChangeListener<Number>()
{
public void changed(ObservableValue<? extends Number> ov,
Number old_val, Number new_val)
{
upperSP.hvalueProperty().set(new_val.doubleValue());
}
});
}
}
Now to check the problem follow the simple steps...
Ofcorse run the program...
Scroll the bottom Panel ....that is FxPanel2...
And maximized the window .... The x - position for the inner contents is changed now...
which does not happen with Swing....
Here are the screen shots where the problem reproduce for the attached MCVE....Please go through the images....
You may use setFitToWidth of the ScrollPane Object to match a particular dimension. For more details you may refer to the link http://docs.oracle.com/javafx/2/ui_controls/scrollpane.htm at down the page, Resizing Components in the Scroll Pane, you may find more on the solution
I'm trying to make a mute button for in my android game menu, so when I press the button, the texture changes from a playing speaker symbol to a muted speaker symbol (which I've already made in Photoshop).
So when the audio is playing it will have a 'speaker' symbol, but when I press it, it will change to a 'muted speaker' symbol (a speaker with a cross).
Thanks in advance, any help is appreciated!
first we make a maintexture as the texture that is always used and on awake we put our texture1(speaker) assign to it and if button is pressed we change it to texture2(mute)
public Texture2D Texture1;
public Texutre2D Texture2;
public bool textureBool;
void Awake() {
textureBool=true;
void OnGUI(){
if( GUI.Button( rect , textureBool ? texture1:texture2 ) )
{
textureBool = !textureBool;
}
}
You can also use UI button in this way:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class ChangeSprite : MonoBehaviour {
public Image image;
public bool isPress = false;
public Button button;
public Sprite Fsprite;
public Sprite Ssprite;
// Use this for initialization
void Start () {
image = button.GetComponent <Image>();
}
// Update is called once per frame
public void ChangeSprites () {
isPress = !isPress;
if ( isPress == true )
{
image.sprite = Ssprite;
}
else
{
image.sprite = Fsprite;
}
}
}
I am creating a game similar to Glider PRO. In the game, you control a paper airplane and you use air vents to keep you from falling on the floor. When the airplane collides with the air vent, (using collidesWith method) it pushes the airplane up. Now here is the problem, when I attach 2 air vents to the scene, only the first attached air vent works. The second one doesn't seem to collide with the airplane. I use the onManagedUpdate method in each air vent to check for the collision. This is my first time coding with AndEngine so any help on this will be greatly appreciated.
Here is the AirVent code:
public class AirVent extends Rectangle
{
private Sprite sprite;
private Glider glider;
public AirVent(float pX, float pY, float pWidth, float pHeight,
VertexBufferObjectManager pVertexBufferObjectManager,
ITextureRegion pITextureRegion)
{
super(pX, pY, pWidth, pHeight, pVertexBufferObjectManager);
sprite = new Sprite(0, 0, pITextureRegion, pVertexBufferObjectManager);
sprite.setSize(pWidth, pWidth / 2);
sprite.setPosition((pWidth / 2) - (sprite.getWidth() / 2), pHeight
- sprite.getHeight());
this.attachChild(sprite);
this.setAlpha(0);
}
public Sprite getSprite()
{
return sprite;
}
public void checkCollision()
{
if (glider.collidesWith(this))
{
glider.setGravity(glider.getConstantGravity() * -1f);
}
else
{
glider.setGravity(glider.getConstantGravity());
}
}
public void applyGliderInteraction(Glider glider)
{
this.glider = glider;
}
#Override
public void onManagedUpdate(float secondsElapsed)
{
this.checkCollision();
super.onManagedUpdate(secondsElapsed);
}
}
And the MainActivity code part:
#Override
protected Scene onCreateScene()
{
// TODO Auto-generated method stub
// 1 - Create new scene
final Scene scene = new Scene();
Sprite backgroundSprite = new Sprite(0, 0, this.mBackground, getVertexBufferObjectManager());
scene.attachChild(backgroundSprite);
roomChanger = new RoomChanger(0, 0, screenWidth, screenHeight, getVertexBufferObjectManager(), scene);
// 2 - Create the player
player1 = new Glider(339, 174, this.mPlayerGlider1, getVertexBufferObjectManager())
{
#Override
public void onManagedUpdate(float secondsElapsed)
{
if (player1.getLife() == 0)
{
// Execute your actions.
finish();
}
super.onManagedUpdate(secondsElapsed);
}
};
scene.attachChild(player1);
airVent = new AirVent(200, 100, 100, 400, getVertexBufferObjectManager(), this.mAirVent);
airVent.applyGliderInteraction(player1);
airVent2 = new AirVent(500, 100, 100, 400, getVertexBufferObjectManager(), this.mAirVent);
airVent2.applyGliderInteraction(player1);
scene.attachChild(airVent);
scene.attachChild(airVent2);
createControllers();
return scene;
}
I found the problem. The checkCollison() method works, but when I add a second AirVent, the if/else statements battle each other. Removing the else statement from the checkCollision() method fixed the issue but right now, that is not the way I want my game to behave.
So I have a TextArea and as the user pastes paragraphs into it, or just writes in it, I want it to expand vertically to reveal all the available text. I.e. not to use a scrollbar in the text field itself... much like what happens on many web pages. Many users, myself included, don't like to be forced to edit in a small window. Exactly how Facebook status updates box works.
I've tried
myTextArea.autoSize()
wrapped in an
myTextArea.textProperty().addListener(new ChangeListener()....);
but that doesn't work. I think it's happy autosizing to its current size.
The left, right & top anchors are set to it's parent AnchorPane. I've tried it with the bottom attached and not attached. Ideally I'd like to grow the anchor pane as the textarea grows.
I don't mind reading the TextProperty and calculating a trigger size which I set myself... but this seems a hacky approach IF there is already a best practise. The number of properties and sub objects of javafx is sufficiently daunting that it seems like a good point to ask the question here, rather than trying to figure out how many pixels the font/paragraphs etc are taking up.
Update:
So I thought maybe I was overthinking it, and all I needed to do was to switch the scrollbars off and the rest would happen. Alas, looking for available fields and methods for "scroll", "vertical", "vbar" comes up with nothing I can use. ScrollTopProperty looks like it's for something else.
The problem; the height of textArea is wanted to be grown or shrunk while its text is changing by either user's typing or copy-pasting. Here is another approach:
public class TextAreaDemo extends Application {
private Text textHolder = new Text();
private double oldHeight = 0;
#Override
public void start(Stage primaryStage) {
final TextArea textArea = new TextArea();
textArea.setPrefSize(200, 40);
textArea.setWrapText(true);
textHolder.textProperty().bind(textArea.textProperty());
textHolder.layoutBoundsProperty().addListener(new ChangeListener<Bounds>() {
#Override
public void changed(ObservableValue<? extends Bounds> observable, Bounds oldValue, Bounds newValue) {
if (oldHeight != newValue.getHeight()) {
System.out.println("newValue = " + newValue.getHeight());
oldHeight = newValue.getHeight();
textArea.setPrefHeight(textHolder.getLayoutBounds().getHeight() + 20); // +20 is for paddings
}
}
});
Group root = new Group(textArea);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
// See the explanation below of the following line.
// textHolder.setWrappingWidth(textArea.getWidth() - 10); // -10 for left-right padding. Exact value can be obtained from caspian.css
}
public static void main(String[] args) {
launch(args);
}
}
But it has a drawback; the textarea's height is changing only if there are line breaks (ie Enter keys) between multiple lines, if the user types long enough the text gets wrapped to multiple line but the height is not changing.
To workaround this drawback I added this line
textHolder.setWrappingWidth(textArea.getWidth() - 10);
after primaryStage.show();. It works well for long typings where user does not linebreaks. However this generates another problem. This problem occurs when the user is deleting the text by hitting "backspace". The problem occurs exactly when the textHolder height is changed and where the textArea's height is set to new value. IMO it maybe a bug, didn't observe deeper.
In both case the copy-pasting is handling properly.
Awaiting a better, i use this hacky solution.
lookup the vertical scrollbar of the textarea.
make it transparent
listen to its visible property
when the scrollbar become visible i add a row to the textarea.
The code:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TextArea;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class GrowGrowTextArea extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
AnchorPane root = new AnchorPane();
root.setStyle("-fx-padding:20;-fx-background-color:dodgerblue;");
final TextArea textArea = new TextArea();
AnchorPane.setTopAnchor(textArea, 10.0);
AnchorPane.setLeftAnchor(textArea, 10.0);
AnchorPane.setRightAnchor(textArea, 10.0);
root.getChildren().add(textArea);
primaryStage.setScene(new Scene(root, 400, 300));
primaryStage.show();
ScrollBar scrollBar = lookupVerticalScrollBar(textArea);
scrollBar.setOpacity(0.0);
scrollBar.visibleProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> source,
Boolean wasVisible,
Boolean isVisible) {
if (isVisible) {
textArea.setPrefRowCount(textArea.getPrefRowCount() + 1);
textArea.requestLayout();
}
}
});
}
private ScrollBar lookupVerticalScrollBar(Node node) {
if (node instanceof ScrollBar && ((ScrollBar)node).getOrientation() == Orientation.VERTICAL) {
return (ScrollBar) node;
}
if (node instanceof Parent) {
ObservableList<Node> children = ((Parent) node).getChildrenUnmodifiable();
for (Node child : children) {
ScrollBar scrollBar = lookupVerticalScrollBar(child);
if (scrollBar != null) {
return scrollBar;
}
}
}
return null;
}
}
I had a similar problem with creating expanding TextArea. I was creating TextArea that looks like TextField and expand vertically every time when there is no more space in line.
I have tested all solutions that I could find on this topic on stack and other sources available. I found few good solutions but neither was good enough.
After many hours of fighting, I figured out this approach.
I extended TextArea class, override layoutChildren() method and add a listener on text height.
#Override
protected void layoutChildren() {
super.layoutChildren();
setWrapText(true);
addListenerToTextHeight();
}
private void addListenerToTextHeight() {
ScrollPane scrollPane = (ScrollPane) lookup(".scroll-pane");
scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER);
StackPane viewport = (StackPane) scrollPane.lookup(".viewport");
Region content = (Region) viewport.lookup(".content");
Text text = (Text) content.lookup(".text");
text.textProperty().addListener(textHeightListener(text));
}
private InvalidationListener textHeightListener(Text text) {
return (property) -> {
// + 1 for little margin
double textHeight = text.getBoundsInLocal().getHeight() + 1;
//To prevent that our TextArena will be smaller than our TextField
//I used DEFAULT_HEIGHT = 18.0
if (textHeight < DEFAULT_HEIGHT) {
textHeight = DEFAULT_HEIGHT;
}
setMinHeight(textHeight);
setPrefHeight(textHeight);
setMaxHeight(textHeight);
};
}
I used some of the code found in the previous answers.
The growTextAreaIfNecessary method will increase the height of textArea until the scrollbar is not visible (limited to 20 lines in this example).
The problem with this approach is that the window needs to be redrawn several times until the perfect height is found.
private ScrollBar lookupVerticalScrollBar(Node node) {
if (node instanceof ScrollBar && ((ScrollBar) node).getOrientation() == Orientation.VERTICAL) {
return (ScrollBar) node;
}
if (node instanceof Parent) {
ObservableList<Node> children = ((Parent) node).getChildrenUnmodifiable();
for (Node child : children) {
ScrollBar scrollBar = lookupVerticalScrollBar(child);
if (scrollBar != null) {
return scrollBar;
}
}
}
return null;
}
private void growTextAreaIfNecessary(TextArea textArea) {
Platform.runLater(() -> {
ScrollBar lookupVerticalScrollBar = lookupVerticalScrollBar(textArea);
int prefRowCount = textArea.getPrefRowCount();
if (lookupVerticalScrollBar.isVisible() && prefRowCount < 20) {
textArea.setPrefRowCount(prefRowCount + 1);
System.out.println("increasing height to: " + (prefRowCount + 1));
growTextAreaIfNecessary(textArea);
}
});
}
I have tried many hacks, most of them had jitters while typing, this to me was the perfect result:
textArea.textProperty().addListener((obs,old,niu)->{
Text t = new Text(old+niu);
t.setFont(textArea.getFont());
StackPane pane = new StackPane(t);
pane.layout();
double height = t.getLayoutBounds().getHeight();
double padding = 20 ;
textArea.setMinHeight(height+padding);
});