How to use TextFlow from FXML? - javafx-8

Crossposted:
• https://community.oracle.com/message/13853226#13853226
• http://www.coderanch.com/t/666101/JavaFX/java/TextFlow-FXML#3105251
I am trying to use TextFlow coming from FXML in controller during app execution (not at start-up) but no text is shown.
I have tried:
textflow.getChildren.add(text);
and also:
textflow=new TextFlow(text);
where text is:
Text text=new Text("AAA");
I both cases TextFlow shows nothing.
Is there another container for use with rich text using FXML JavaFX app?
For sure if I try both cases in non-FXML JavaFX app it works both of them.
Update:
TextFlow in FXML looks like this
<TextFlow fx:id="txtFlow" layoutX="20.0" layoutY="230.0" prefHeight="70.0" prefWidth="430.0" style="-fx-border-color: ADD9E6;" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="20.0" AnchorPane.rightAnchor="140.0">
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.TextFlow?>
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="testfxmlpackage.FXMLDocumentController">
<children>
<TextFlow fx:id="txtF" layoutX="22.0" layoutY="234.0" prefHeight="74.0" prefWidth="433.0" style="-fx-border-color: ADD8E6;" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="22.0" AnchorPane.rightAnchor="142.0" />
</children>
</AnchorPane>
Controller
package testfxmlpackage;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
public class FXMLDocumentController implements Initializable {
#FXML TextFlow txtF;
#Override
public void initialize(URL url, ResourceBundle rb) {
txtF=new TextFlow(new Text("aaa"));
txtF.getChildren().add(new Text("aaa"));
}
}
Main Class
package testfxmlpackage;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TestFXMLPackage extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Make sure you refresh your project in the IDE after you edit the Text node in the FXML file, also make sure that fx:id matches your object name in the controller class.
Is there another container for use with rich text using FXML JavaFX
app?
You don't need another container, just Text nodes within a TextFlow should work for you.
(it would be better if we can see your code, so that we can try to identify the exact problem)
Here is an example that works perfectly:
TextFlowExample.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<VBox fx:id="container" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TextFlow fx:id="myTextFlow" />
</children>
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
</VBox>
MainApp.java
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
public class MainApp extends Application implements Initializable {
#FXML TextFlow myTextFlow;
#FXML VBox container;
public static void main(String [] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("TextFlowExample.fxml"));
loader.setController(this);
Parent parent = loader.load();
Scene scene = new Scene(parent);
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
Text text = new Text("Now this is a text node");
myTextFlow.getChildren().add(text);
}
}

Related

JavaFX and CDI: How to Inject many Stages

I would like to integrate JavaFX and CDI. There are some good examples over the web about that like these ones:
https://dzone.com/articles/fxml-javafx-powered-cdi-jboss
http://fxapps.blogspot.com.br/2017/10/using-cdi-20-in-javafx-application.html
However, all the examples I have seen don't work in a real world because they are not able to inject more than one Stage (the primaryStage), if they are, I don't know how.
So I would like to know if it's possible in a JavaFX/CDI project to inject more than one Stage (to use in modal windows for example...)
thank's!
You don't need to use CDI to manage the stages: stages themselves simply have a Scene; they do not have any dependencies on any other objects you need to manage. All you need to do is ensure that the FXMLLoader has a controllerFactory that retrieves controller instances from the DI framework.
Here is a quick example (caveat: I have never used CDI/Weld before, so I might not have the optimal way of doing things here).
First, it's probably a good idea to expose a controller factory that gets the appropriate controllers:
package app;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javafx.util.Callback;
#ApplicationScoped
public class CDIControllerFactory implements Callback<Class<?>, Object> {
#Inject
private Instance<Object> instance ;
#Override
public Object call(Class<?> type) {
Object controller = instance.select(type).get();
return controller;
}
}
Here is a model class we want to share with all the controllers. Since we only want one instance, we make it #ApplicationScoped:
package app;
import javax.enterprise.context.ApplicationScoped;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
#ApplicationScoped
public class Model {
private final ObservableList<String> names = FXCollections.observableArrayList();
public ObservableList<String> getNames() {
return names ;
}
public void addName(String name) {
names.add(name);
}
}
The test application will just have a list view (with a list of names) and a button for adding a new name from a dialog. Here is the main controller:
package app;
import java.io.IOException;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class MainController {
#Inject
private Model model ;
#Inject
private CDIControllerFactory controllerFactory ;
#FXML
private ListView<String> listView ;
#FXML
private void initialize() {
listView.setItems(model.getNames());
}
#FXML
private void showAddDialog() throws IOException {
FXMLLoader loader = new FXMLLoader(AddNameController.class.getResource("AddNameDialog.fxml"));
loader.setControllerFactory(controllerFactory);
Scene scene = new Scene(loader.load());
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.setScene(scene);
stage.show();
}
}
Note how it uses the controller factory on the FXMLLoader. The stage can just be created "by hand".
Here's the controller for the dialog that is used to add new names. Note how it has a reference to the same model instance, via CDI:
package app;
import javax.enterprise.inject.Default;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
#Default
public class AddNameController {
#Inject
private Model model ;
#FXML
private TextField nameField ;
#FXML
private void submit() {
model.addName(nameField.getText());
close();
}
#FXML
private void close() {
nameField.getScene().getWindow().hide();
}
}
Here are the two FXML files (they are both in the app package: the only real requirement with the way I coded these is that they should be in the same package as their corresponding controller classes).
Main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.MainController">
<center>
<ListView fx:id="listView" />
</center>
<bottom>
<HBox alignment="CENTER">
<padding>
<Insets top="5" right="5" left="5" bottom="5" />
</padding>
<Button text="Add..." onAction="#showAddDialog" />
</HBox>
</bottom>
</BorderPane>
AddNameDialog.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.MainController">
<center>
<ListView fx:id="listView" />
</center>
<bottom>
<HBox alignment="CENTER">
<padding>
<Insets top="5" right="5" left="5" bottom="5" />
</padding>
<Button text="Add..." onAction="#showAddDialog" />
</HBox>
</bottom>
</BorderPane>
Here's the application class:
package app;
import java.io.IOException;
import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
private Weld weld ;
private WeldContainer container ;
#Override
public void init() {
weld = new Weld();
container = weld.initialize();
}
#Override
public void stop() {
weld.shutdown();
}
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(MainController.class.getResource("Main.fxml"));
loader.setControllerFactory(container.select(CDIControllerFactory.class).get());
Scene scene = new Scene(loader.load(), 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
and of course the CDI configuration class, META-INF/beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
If you really want to let CDI provide your stages, you can, but I don't really see there's much to gain by it. But, e.g. you can do something like:
package app;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD,
ElementType.TYPE, ElementType.PARAMETER})
public #interface ModalStage { }
which lets you provide modal and non-modal stages:
package app;
import javax.enterprise.inject.Produces;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class StageProducer {
#Produces
public Stage stage() {
return new Stage();
}
#Produces
#ModalStage
public Stage modalStage() {
Stage stage = stage();
stage.initModality(Modality.APPLICATION_MODAL);
return stage ;
}
}
And then your MainController might look like
public class MainController {
#Inject
private Model model ;
#Inject
private CDIControllerFactory controllerFactory ;
#Inject
#ModalStage
private Stage addNameDialogStage ;
#FXML
private ListView<String> listView ;
#FXML
private void initialize() {
listView.setItems(model.getNames());
}
#FXML
private void showAddDialog() throws IOException {
FXMLLoader loader = new FXMLLoader(AddNameController.class.getResource("AddNameDialog.fxml"));
loader.setControllerFactory(controllerFactory);
Scene scene = new Scene(loader.load());
addNameDialogStage.setScene(scene);
addNameDialogStage.show();
}
}
There are other facilities you could easily build into this, e.g. providing a class for loading FXML from a resource name, which incorporates the controller factory already, etc etc.

HTTP method GET is not supported by this URL with simple servlet

i'm trying to solve this problem since 2 days, there are the same title of problems in many post on stackoverflow but it was all different, because they allways had an error in code, but in my case i have a sample servlet, and when i run the project i get this error
HTTP Status 405 - HTTP method GET is not supported by this URL
this is my class servlet
package web;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Controller extends HttpServlet{
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public void init() throws ServletException {
}
#Override
protected void doGet(HttpServletRequest requet, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out =response.getWriter();
out.println("hello");
}
}
and my web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>test2</display-name>
<servlet>
<servlet-name>cs</servlet-name>
<servlet-class>web.Controller</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cs</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
please tell me how solve this problem, thanks ...
Simple doGet Servlet Or You Can add try catch blog to debug code
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class logout extends HttpServlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
try
{
PrintWriter out=response.getWriter();
response.setContentType("text/html;charset=UTF-8");
out.println("Hello");
}
catch(Exception e)
{
out.println(e.toString())
}
}
}
I think you probably have not add GAT method on form attribute Like this
<form action="web" method="GET"></form>

Binary XML file line #11: Error inflating class fragment

I am getting two Errors in my Code.
Caused by: java.lang.IllegalStateException: Fragment com.example.dfoley.write_to_file.topFragment did not create a view.
Caused by: android.view.InflateException: Binary XML file line #11: Error inflating class fragment both pointing to Line MainActivity.java:21 which is the following setContentView(R.layout.activity_main);
bottomFragment
package com.example.dfoley.write_to_file;
import android.app.ListFragment;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import java.util.ArrayList;
public class bottomFragment extends ListFragment {
private ArrayAdapter<StateUser> adapter;
#Override
public void onActivityCreated(Bundle saveInstanceState){
ArrayList<StateUser> flight = MainContoller.getInstance().getFlights();
this.adapter = new ArrayAdapter<StateUser>(getActivity(), android.R.layout.simple_list_item_1, flight);
setListAdapter(this.adapter);
super.onActivityCreated(saveInstanceState);
}
public void refreshList(){
this.adapter.notifyDataSetChanged();
}
}
Top Fragment
package com.example.dfoley.write_to_file;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import.android.util.Log;
import android.view.View;
import.android.widget.Button;
import android.widget.EditText;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class topFragment extends Fragment{
private FlightSearcher searcher;
EditText text1;
public interface FlightSearcher {
public void refreshFlightList();
}
#Override
public void onAttach(Activity activity) {
searcher = (FlightSearcher) activity;
super.onAttach(activity);
}
#Override
public void onActivityCreated(Bundle savedInstanceState){
setupListeners();
super.onActivityCreated(savedInstanceState);
}
public void setupListeners() {
Button addUser = (Button)getActivity().findViewById(R.id.button);
addUser.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
writeToFile();
searcher.refreshFlightList();
}
});
}
private void writeToFile() {
text1=(EditText)getActivity().findViewById(R.id.editText);
String AddUsers = text1.getText().toString();
try {
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(getActivity().openFileOutput("UserList", Context.MODE_PRIVATE));
outputStreamWriter.write(AddUsers);
outputStreamWriter.close();
}
catch (IOException e) {
Log.e("Exception", "File write failed: " + e.toString());
}
}
}
Main Activity
package com.example.dfoley.write_to_file;
import android.app.FragmentManager;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends FragmentActivity implements topFragment.FlightSearcher{
public void refreshFlightList() {
FragmentManager mgr = getFragmentManager();
bottomFragment bottomFragmentRef =(bottomFragment) mgr.findFragmentById(R.id.bottom_fragment);
bottomFragmentRef.refreshList();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
activiy_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context=".MainActivity">
<fragment
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:name="com.example.dfoley.write_to_file.topFragment"
android:id="#+id/top_fragment"
android:layout_weight="1"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
tools:layout="#layout/topfragment" />
<fragment
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:name="com.example.dfoley.write_to_file.bottomFragment"
android:id="#+id/bottom_fragment"
android:layout_weight="1"
android:layout_below="#+id/top_fragment"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
tools:layout="#layout/bottomfragment" />
Change fragment to FrameLayout
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
For both of your fragments, you are not telling it how to create a view. I see that you are using the tools:layout tag, but according to the Tools doc, that is only a hint to the designer; it does not actually inflate that layout:
"This attribute is typically set in a tag and is used to record which layout you want to see rendered at designtime (at runtime, this will be determined by the actions of the fragment class listed by the tag)."
Thus you need to override onCreateView, inflate your view hierarchy, and then return that:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.topfragment, container, false);
}

How to integrate GWT UIBinder with Canvas?

I am trying to find my way around with the Google Web Toolkit. Right now I am trying to get a Canvas widget up and running.
But I am getting this error and do not understand why:
Compiling module de.kuntze.HelloCanvas
Computing all possible rebind results for 'de.kuntze.client.HelloCanvas.HelloCanvasUiBinder'
Rebinding de.kuntze.client.HelloCanvas.HelloCanvasUiBinder
Invoking generator com.google.gwt.uibinder.rebind.UiBinderGenerator
[ERROR] com.google.gwt.canvas.client.Canvas has no default (zero args) constructor. To fix this, you can define a #UiFactory method on the UiBinder's owner, or annotate a constructor of Canvas with #UiConstructor.
[ERROR] Errors in 'de/kuntze/client/HelloCanvas.java'
[ERROR] Line 14: Failed to resolve 'de.kuntze.client.HelloCanvas.HelloCanvasUiBinder' via deferred binding
[WARN] For the following type(s), generated source was never committed (did you forget to call commit()?)
[WARN] de.kuntze.client.HelloCanvas_HelloCanvasUiBinderImpl
My code looks like this:
The module HelloCanvas.gwt.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.5.1//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.5.1/distro-source/core/src/gwt-module.dtd">
<module>
<inherits name="com.google.gwt.user.User" />
<source path="client"/>
<entry-point class="de.kuntze.client.HelloCanvas"/>
</module>
The UIBinder file HelloCanvas.ui.xml
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:c='urn:import:com.google.gwt.canvas.client'>
<ui:style>
</ui:style>
<g:HTMLPanel>
<c:Canvas ui:field="canvas"></c:Canvas>
</g:HTMLPanel>
The Java file HelloCanvas.java
package de.kuntze.client;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
public class HelloCanvas extends Composite implements EntryPoint{
private static HelloCanvasUiBinder uiBinder = GWT
.create(HelloCanvasUiBinder.class);
#UiField Canvas canvas;
interface HelloCanvasUiBinder extends UiBinder<Widget, HelloCanvas> {
}
public HelloCanvas() {
initWidget(uiBinder.createAndBindUi(this));
}
#Override
public void onModuleLoad() {
canvas = Canvas.createIfSupported();
canvas.setWidth("400px");
canvas.setHeight("400px");
canvas.setCoordinateSpaceWidth(400);
canvas.setCoordinateSpaceHeight(400);
RootPanel.get().add(this);
}
}
I bet the answer will be pretty easy but I do not know why I get this error message and why the code does not compile.
Edit:
So after trying the advice below it works. Here comes the edited code which draws a black triangle.
The UIBinder file HelloCanvas.ui.xml including a SimplePanel
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:HTMLPanel>
<g:SimplePanel width="200px" height="200px" ui:field="panel">
</g:SimplePanel>
</g:HTMLPanel>
The edited Java file HelloCanvas.java
package de.kuntze.client;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
public class HelloCanvas extends Composite implements EntryPoint {
private static HelloCanvasUiBinder uiBinder = GWT
.create(HelloCanvasUiBinder.class);
#UiField
SimplePanel panel;
interface HelloCanvasUiBinder extends UiBinder<Widget, HelloCanvas> {
}
public HelloCanvas() {
initWidget(uiBinder.createAndBindUi(this));
}
#Override
public void onModuleLoad() {
Canvas tCanvas = Canvas.createIfSupported();
tCanvas.setWidth("400px");
tCanvas.setHeight("400px");
tCanvas.setCoordinateSpaceWidth(400);
tCanvas.setCoordinateSpaceHeight(400);
Context2d tContext2d = tCanvas.getContext2d();
tContext2d.beginPath();
tContext2d.moveTo(25, 25);
tContext2d.lineTo(105, 25);
tContext2d.lineTo(25, 105);
tContext2d.fill();
panel.add(tCanvas);
RootPanel.get().add(this);
}
}
You cannot create a Canvas with the UI:Binder, because there is no zero-arg constructor, nor a #UIConstructor.
I would suggst to create a warpper (A simplePanel) and within your Wrapper-Code, you can create a Canvas, by calling Canvas.createIfSupported():
The canvas itself is prvided.
#UiField(provided = true)
Canvas canvas;
Before you call binder.createAndBindUi(this); you will have to create the Canvas:
canvas = Canvas.createIfSupported()
I have no simple example, but maybe, this link is helpful:
https://code.google.com/p/gwtgae2011/source/browse/src/main/java/com/googlecode/gwtgae2011/client/main/SketchView.java?r=8e7169e7fbb411f320f99f77dcdb27efa27b727a
You also could use a CanvasElement, like described in this question:
GWT uibinder CanvasElement wont resize when deployed

How to listen for WindowEvent.WINDOW_SHOWN in the nodes of the scene graph?

It seems WindowEvent.WINDOW_SHOWN never gets dispatched on any of the nodes in the scene graph, nor is there anyway (that I could find) to know when a node is visible/rendered/shown. For example:
TestLauncher.java
package com.example.javafx.event;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TestLauncher extends Application
{
public static void main(String[] args)
{
Application.launch(TestLauncher.class, args);
}
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(TestController.class.getResource("TestView.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
}
TestController.java
package com.example.javafx.event;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.control.TextField;
import javafx.stage.WindowEvent;
public class TestController implements Initializable
{
#FXML private Parent root;
#FXML private TextField serverAddressInput;
#FXML private TextField usernameInput;
#Override
public void initialize(URL url, ResourceBundle rb)
{
serverAddressInput.setText("127.0.0.1");
//won't work because stage isn't visible yet
trySetFocusOnUsernameInput1();
//apparently Stage never passes on any WindowEvents to the children...
trySetFocusOnUsernameInput2();
}
private void trySetFocusOnUsernameInput1()
{
usernameInput.requestFocus();
}
private void trySetFocusOnUsernameInput2()
{
root.addEventFilter(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>()
{
#Override
public void handle(WindowEvent window)
{
usernameInput.requestFocus();
}
});
root.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>()
{
#Override
public void handle(WindowEvent window)
{
usernameInput.requestFocus();
}
});
}
public void handleWindowShownEvent()
{
usernameInput.requestFocus();
}
}
TestView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox
xmlns:fx="http://javafx.com/fxml"
fx:id="root"
fx:controller="com.example.javafx.event.TestController"
prefHeight="150"
prefWidth="200"
>
<children>
<TextField fx:id="serverAddressInput" />
<TextField fx:id="usernameInput" />
</children>
</VBox>
So, actually, how else can a node become aware of the fact that it's visible/rendered/shown?
I guess one of the possible solutions is to add the following method to TestController.java
public void handleWindowShownEvent()
{
usernameInput.requestFocus();
}
and then change the start method in TestLauncher to the following:
#Override
public void start(Stage stage) throws Exception
{
FXMLLoader loader = new FXMLLoader();
Parent root = (Parent)loader.load(TestController.class.getResourceAsStream("TestView.fxml"));
final TestController controller = (TestController)loader.getController();
stage.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>()
{
#Override
public void handle(WindowEvent window)
{
controller.handleWindowShownEvent();
}
});
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
I would really welcome other solutions as this one seems too clunky...
Another solution that admittedly isn't very sexy but decouples the node from the application:
root.sceneProperty().addListener(new ChangeListener<Scene>() {
#Override
public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
newValue.windowProperty().addListener(new ChangeListener<Window>() {
#Override
public void changed(ObservableValue<? extends Window> observable, Window oldValue, Window newValue) {
newValue.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
usernameInput.requestFocus();
}
});
}
});
}
});
Made more sense in my case.