I'm working on a SmartGWT project where I'd like my main navigation to be done via a treegrid. The treegrid renders proprerly and its DataSource is functioning appropriately as well. The treegrid is correctly situated to the left of the mainView Canvas.
What I can't seem to figure out is how to switch the contents of the mainView Canvas based on what is selected in the NavigationTree. I've mimicked the functionality I'd like by adding new windows to the existing Canvas, but I can't find an example demonstrating how to clear the canvas entirely and replace it with a new Window.
Am I on the right track here? Can anyone point me at an example that shows roughly what I'm trying to accomplish?
public class NavigationTree extends TreeGrid {
public NavigationTree(Canvas mainView)
{
setDataSource(NavigationDataSource.getInstance());
setAutoFetchData(true);
setShowHeader(false);
addNodeClickHandler(new NavClickHandler(mainView));
}
// Handler for clicking an item on the Navigation Tree.
private class NavClickHandler implements NodeClickHandler
{
private Canvas mainView;
public NavClickHandler(Canvas mainView)
{
super();
this.mainView = mainView;
}
#Override
public void onNodeClick(NodeClickEvent event)
{
Window window = new Window();
window.setWidth(300);
window.setHeight(230);
window.setCanDragReposition(true);
window.setCanDragResize(true);
window.setTitle(event.getNode().getAttribute("name"));
window.addItem(new Label("huzzah!"));
window.setParentElement(mainView);
window.redraw();
}
}
}
You can keep the mainView canvas, clear its children (if any is set) and then set the newly created window as its new child. Something like the following as the body of your click handler:
Window window = new Window();
window.setWidth(300);
window.setHeight(230);
window.setCanDragReposition(true);
window.setCanDragResize(true);
window.setTitle(event.getNode().getAttribute("name"));
window.addItem(new Label("huzzah!"));
for (Canvas child: mainView.getChildren()) {
mainView.removeChild(child);
}
mainView.addChild(window);
I managed to accomplish what I needed with the following change to the event handler code:
public NavClickHandler(UI ui) //UI extends HLayout
{
this.ui = ui;
}
#Override
public void onNodeClick(NodeClickEvent event) {
Window window = new Window();
window.setWidth100();
window.setHeight100();
window.setHeaderControls(HeaderControls.HEADER_LABEL);
window.setTitle(event.getNode().getAttribute("name"));
window.addItem(new Label("Huzzah!"));
ui.setMainView(window);
}
...and the following change to my main UI layout:
public void setMainView(Canvas canvas)
{
mainView.destroy();
mainView = canvas;
addMember(mainView);
this.redraw();
}
Related
Currently, I'm playing around with JavaFX as I'm writing a Snake game for my Java Fundamentals class final project. I'm not that new to creating simple games with animations as I've made a bit of them using PyGame module and SDL in C. Anyway, now I'm quite struggling with understanding the correlations of some objects in JavaFX, especially when combined with SceneBuilder's FXMLs.
I just can't understand how to create an equivalent of gameloop I used to implement in PyGame or SDL. What I want to do with the code below is to enter the gameloop as soon as a new Game object is created and draw the state of the game continuously on the gameCanvas created in the SceneBuilder. I think I can easily manage all the stuff later, but I just can't sort it out how to create a legit bond between the FXML canvas and the gameloop I want to run.
GameController.java
public class GameController implements Initializable, ControlledScreen {
#FXML
private Canvas gameCanvas;
#Override
public void setScreenParent(ScreensController screenPage) {
// THIS IS FOR SCENE MANAGEMENT CONCEPT
}
Game.java
public class Game implements Runnable {
public static final int EASY = 1,
MEDIUM = 2,
HARD = 3;
int difficultyLevel, score = 0;
Snake snake;
Food food;
boolean isRunning = true;
public void setLevelEasy() {
difficultyLevel = EASY;
}
public void setLevelMedium() {
difficultyLevel = MEDIUM;
}
public void setLevelHard() {
difficultyLevel = HARD;
}
#Override
public void run() {
while (isRunning) {
}
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {
// TODO Auto-generated method stub
}
}
You cant use FXML file to create new scene. Use this instead
public class Screen extends Application implements Runnable
{
#Override
public void start ( Stage primaryStage )
{
Pane pane = new Pane ();
Scene scene = new Scene(pane,500,300);
primaryStage.setScene ( scene );
primaryStage.show ();
}
#Override
public void run ()
{
launch ();
}
}
anything you want to add goes to pane element your canvas etc.
and by the way make dificulty enum not int like
enum {easy,medium,hard}
that way nobody can set dificulty level to something does not exist.
Been trying around and searching but couldn't find any solution, so I finally decided to give up and ask further...
Creating a javafx app, I load tiles in a TilePane.
This tiles are clickable and lead to a details page of their respective content.
On each tile, if they do belong to a certain pack, I do display the pack name, that is also clickable and lead to a page showing that specific pack content.
So that means the container, the tile, that is a Pane is clickable and on top of it I have a Label that is claickable also. What happens is when I do click the Label, it also triggers the Pane onMousePressed()... Here is a part of the tile creation code, the part focused on the onMousePressed(). I tried to make the Pane react by double click and the Label by single, it works, but I want to Pane to open with a single click.
I would be more than thankfull for any ideas how to solve that.
public DownloadTile (Downloadable upload, MainApp mainApp) {
_mainApp = mainApp;
_upload = upload;
_tile = new Pane();
_tile.setPrefHeight(100);
_tile.setPrefWidth(296);
_tile.setStyle("-fx-background-color: #ffffff;");
_tile.setCursor(Cursor.HAND);
}
public void refresh() {
_tile.getChildren().clear();
_tile.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.isPrimaryButtonDown() /*&& event.getClickCount() == 2*/) {
_mainApp.showDownloadDialog(dt, _upload);
}
}
});
if (_upload.getPack() != null) {
Label pack = new Label();
pack.setText(_upload.getPack());
pack.getStyleClass().add("pack-link");
pack.setCursor(Cursor.HAND);
pack.relocate(10, 48);
_tile.getChildren().add(pack);
pack.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.isPrimaryButtonDown()) {
_mainApp.showPackPage(_upload);
}
}
});
}
}
Your label will receive the mouseclick first (since it's on top), so after you have processed the click, you can stop it from being passed down the chain using 'consume':
pane.setOnMouseClicked(
(Event event) -> {
// process your click here
System.out.println("Panel clicked");
pane.requestFocus();
event.consume();
};
I am trying to create a custom control in JavaFx. The main aim is to create a node that acts as a button. It is simply a Label that would be styled with css to look like a button. I have created a class MyControl that extends the control class like so:
public class MyControl extends Control {
#Override
protected String getUserAgentStylesheet() {
return MyControl.class.getResource("myControl.css").toExternalForm();
}
public MyControl() {
getStyleClass().add("new-control");
}
public MyControl(String item) {
getStyleClass().add("new-control");
}
}
}
For the SkinBase class MyControlSkin I have:
public class MyControlSkin extends SkinBase<MyControlSkin> {
public MyControlSkin(MyControlSkin control) {
super(control);
Label label = new Label("Some text here");
getChildren.add(label);
}
}
In the myControl.css file i simply have:
.tree-node-view {
-fx-skin: "treeviewsample.TreeViewSkin";
}
.label {
-fx-text-fill: -fx-text-background-color;
}
I have created the css for this but the problem is that I don't see the label displayed on the scene:
MyControl control = new MyControl();
but no Label displays on the screen. Please help me I am new to this so ask me for more information if in case my question does not make sense.
I am using javaFx 2.2
I don't know if you made errors transferring the code from your IDE to the post, but as it stands the skin class will not compile. You need
public class MyControlSkin extends SkinBase<MyControl> {
public MyControlSkin(MyControl control) {
super(control);
Label label = new Label("Some text here");
getChildren().add(label);
}
}
The css must define an fx:skin property for a class that matches the control. So you need
.new-control {
-fx-skin: "MyControlSkin";
}
.label {
-fx-text-fill: -fx-text-background-color;
}
I have a Swing app with a glass pane over a map.
It paints dots at certain positions. When I click somewhere on the map, and the glass pane receives the message CONTROLLER_NEW_POLYGON_MARK I
want do display an additional dot at the position specified in the event data (see MyGlassPane.propertyChange).
The glass pane class is called MyGlassPane. Using the debugger I validated that addPointToMark is actually called in propertyChange.
But no additional dots appear on the screen.
How can I change the code so that PointSetMarkingGlassPane.paintComponent is called whenever an event (IEventBus.CONTROLLER_NEW_POLYGON_MARK) is fired?
public class PointSetMarkingGlassPane extends JComponent implements IGlassPane {
private final ILatLongToScreenCoordinatesConverter latLongToScreenCoordinatesConverter;
private final List<Point.Double> pointsToMark = new LinkedList<Point.Double>();
public PointSetMarkingGlassPane(final ILatLongToScreenCoordinatesConverter aConverter) {
this.latLongToScreenCoordinatesConverter = aConverter;
}
protected void addPointToMark(final Point.Double aPoint)
{
if (aPoint != null)
{
pointsToMark.add(aPoint);
}
}
#Override
protected void paintComponent(final Graphics aGraphics) {
for (final Point.Double pointToMark : pointsToMark)
{
final Point positionInScreenCoords = latLongToScreenCoordinatesConverter.getScreenCoordinates(pointToMark);
drawCircle(aGraphics, positionInScreenCoords, Color.red);
}
}
private void drawCircle(Graphics g, Point point, Color color) {
g.setColor(color);
g.fillOval(point.x, point.y, 10, 10);
}
}
public class MyGlassPane extends PointSetMarkingGlassPane implements PropertyChangeListener {
public MyGlassPane(ILatLongToScreenCoordinatesConverter aConverter) {
super(aConverter);
addPointToMark(DemoGlassPane.ARTYOM);
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (IEventBus.CONTROLLER_NEW_POLYGON_MARK.equals(evt.getPropertyName()))
{
addPointToMark((Point.Double)evt.getNewValue());
invalidate();
}
}
}
As I think invalidate() only flags your component to check sizes and layout. You should call repaint() to repaint your pane.
Also I am wondering why you use propertyChangeListener for mouse clicks. I would prefer just simple mouse listener + MouseAdapter and MouseEvent x, y, buttons state.
invalidate() probably won't help you, as it flags a component for layout changes, not painting changes. Why not call repaint() instead?
For better performance, you could call the repaint method which takes a Rectangle (or four ints representing a rectangle), so that only the newly added point is repainted; I would suggest changing the return type of addPointToMark from void to java.awt.Point, and have it return the result of latLongToScreenCoordinatesConverter.getScreenCoordinates, so MyGlassPane can derive a rectangle from that Point which can then be passed to a repaint method.
I may seem ignorant but I have not found much information about how to go about my problem developing my BlackBerry application.
Basically I have constructed a UI that contains a few image buttons. What I want to do is when I click on one of the buttons, I want an image at the center of the screen to switch seemlessly to another image.
I am currently not using any threads though from what I've gathered I need to?
Or do I simply have to push a new screen in my button listener and recall the class's constructor and rebuild the page with different images?
Sorry if this is unclear, I would really appreciate some basic info on how to do this or a link that explains page refreshing in detail for blackberry!
Thanks alot
EDIT : Okay here is a very small part of my code :
This is basically the last thing I have tried in the listeners, I dont know how to change the image (flagField) when I press one of the two buttons! The only thing that worked for me was pushScreen(new Csps("americanflag")); every time I would press on a button, but the performance was bad and I was left with a stack of screens, not ideal.
I have been trying to do this all day long hehe... sigh , here goes:
public class CSP extends UiApplication
{
public static void main(String[] args)
{
CSP theApp = new CSP();
theApp.enterEventDispatcher();
}
public CSP()
{
CSPS csps = new CSPS();
pushScreen(csps);
}
}
class CSPS extends MainScreen implements FieldChangeListener
{
int width = Display.getWidth();
int height = Display.getHeight();
ButtonField backButton;
ImageButtonField canadianFlag;
ImageButtonField americanFlag;
Bitmap changeableFlag;
String currentFlag ="canada_flag.png";
BitmapField flagField;
canadianFlag = construct("canada_flag.png", 0.18);
americanFlag = construct("us.gif", 0.18);
canadianFlag.setChangeListener(this);
americanFlag.setChangeListener(this);
add(flagField);
add(canadianFlag);
add(americanFlag);
//LISTENERS
public void fieldChanged(Field field, int context){
if(field == canadianFlag){
setCurrentFlagResource("canada_flag.png");
BitmapField newFlagField = populateFlagField(currentFlag, 0.3);
replace(flagField, newFlagField);
this.invalidate();
this.doPaint();
this.updateDisplay();
}
else if(field == americanFlag){
setCurrentFlagResource("american-flag.gif");
BitmapField newFlagField = populateFlagField(currentFlag, 0.3);
replace(flagField, newFlagField);
this.invalidate();
this.doPaint();
this.updateDisplay();
}
the setCurrentFlagRessource method sets the flagField attribute to the appropriate BitmapField
There is a method called setBitmap() in the BitmapField.
In the listeners you should simply call flagField.setBitmap(newbitmap). You can also use the method setImage(). There is no need to call invalidate(), updateUI etc.
If you get any IllegalStateException while using this method, simply get a lock on the Applicaton Event lock object or invokeLater() this code.
You will only need to use threads if you are getting your images from some place that might block (like a web server). You should only need to update the images, though you may need to call invalidate() on your button or screen. What would really help is some information on what you are doing. You didn't even tell us what class you're using to display the image, if you've extended it or not, etc.
I don't know what you are doing in your code; But The way of refresh the screen is not like that;
So, I change Your CSPS screen which you are trying to refresh the screen; See the below code...
class CSPS extends MainScreen implements FieldChangeListener
{
int width = Display.getWidth();
int height = Display.getHeight();
ButtonField backButton;
//ImageButtonField canadianFlag;
//ImageButtonField americanFlag;
Bitmap changeableFlag;
String currentFlag ="canada_flag.png";
BitmapField flagField;
public CSPS()
{
createGUI();
}
public void createGUI()
{
canadianFlag = construct("canada_flag.png", 0.18);
americanFlag = construct("us.gif", 0.18);
canadianFlag.setChangeListener(this);
americanFlag.setChangeListener(this);
add(flagField);
add(canadianFlag);
add(americanFlag);
//LISTENERS
}
public void fieldChanged(Field field, int context){
if(field == canadianFlag)
{
setCurrentFlagResource("canada_flag.png");
BitmapField newFlagField = populateFlagField(currentFlag, 0.3);
replace(flagField, newFlagField);
deleteAll();
createGUI();
invalidate();
}
else if(field == americanFlag)
{
setCurrentFlagResource("american-flag.gif");
BitmapField newFlagField = populateFlagField(currentFlag, 0.3);
replace(flagField, newFlagField);
deleteAll();
createGUI();
invalidate();
}
}
}
Construct a createGUI() method, in that write the code that what you have to design for that screen. Not only this screen; What ever you are creating do like this;
and
deleteAll();
createGUI();
invalidate();
For refresh the screen do the above three lines; delete all the fields and call again the createGUI() method; and at last invalidate();