I have the following problem:
I am trying to implement a menu with submenus in libgdx, using the table layout. When a submenu is clicked, a listener is fired, which should setVisible(false) the previous menu, and setVisible(true) the new one. But, although I successfully display the new one, the previous one is still here! Could someone help me?
Here is my code:
The Menu.java:
public class Menu extends Table {
private Stage stage;
public void attachToStage(Stage s) {
if (s != null) {
stage = s;
s.addActor(this);
}
}
public Menu() {
this(null);
}
public Menu(final Stage s) {
attachToStage(s);
setFillParent(true);
top();
left();
}
private void addButtonWithListener(String label, ClickListener listener) {
add().width(10);
Label l = new Label(label, SkinManager.get());
add(l).width(100);
row();
if (listener != null)
l.addListener(listener);
}
/**
* #param label
* #return The menu, for chaining
*/
public Menu addButton(String label) {
addButtonWithListener(label, null);
return this;
}
/**
*
* #param label
* #param m
* the menu to add
* #return The main menu, for chaining
*/
public Menu addMenu(String label, final Menu m) {
addButtonWithListener(label, new ClickListener() {
#Override public void clicked(InputEvent e, float x, float y) {
System.out.println(getChildren());
setVisible(false);
m.setVisible(true);
}
});
m.attachToStage(stage);
m.setVisible(false);
return this;
}
}
My Application:
public class TestApplication implements ApplicationListener {
private Stage stage;
#Override public void create() {
stage = new Stage();
Menu m = new Menu(stage).addButton("Move").addButton("Stay");
m.addMenu("Attack", new Menu().addButton("Sword").addButton("Bow"));
Gdx.input.setInputProcessor(stage);
}
#Override public void render() {
stage.draw();
}
// Other empty methods
}
Thanks a lot!
EDIT: Problem was unrelated, see my answer
Ok, found the problem: not related at all to the Table, just forgott to clear the screen in the Application.
I just added
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
in the render function.
Related
I have a TreeView which is empty at the start and I want to set a placeholder until it is empty. Like the one available for ListView (setPlaceholder())
My first thought was to wrap the TreeView into a BorderPane and just change the center based on the number of elements in the TreeView. The problem is though that I add elements to the TreeView through drag and drop and if I set a label to the center for substituting a placeholder, I won't be able to drag n drop my items in the TreeView anymore. Any suggestions would be much appreciated.
TreeView has no placeholder support - not entirely certain, why not, but could be that an empty tree (whatever that means: null root? root without children? root not showing?) is a rare species.
It's rather simple to implement, though, by following the implementation for a virtualized control that supports it, f.i. TableView. All we need is a custom TreeViewSkin that
manages (creates and adds to the tree's hierarchy, layouts as needed) the placeholder
listens to relevant state of the tree and updates the placeholder's visibilty as appropriate
An example, toggling the emptyness by toggling the tree's root between null/not null via a button:
public class TreeViewWithPlaceholder extends Application {
private static class TreeViewPlaceholderSkin<T> extends TreeViewSkin<T> {
private StackPane placeholderRegion;
private Label placeholderLabel;
public TreeViewPlaceholderSkin(TreeView<T> control) {
super(control);
installPlaceholderSupport();
}
private void installPlaceholderSupport() {
registerChangeListener(getSkinnable().rootProperty(), e -> updatePlaceholderSupport());
updatePlaceholderSupport();
}
/**
* Updating placeholder/flow visibilty depending on whether or not the tree
* is considered empty.
*
* Basically copied from TableViewSkinBase.
*/
private void updatePlaceholderSupport() {
if (isTreeEmpty()) {
if (placeholderRegion == null) {
placeholderRegion = new StackPane();
placeholderRegion.getStyleClass().setAll("placeholder");
getChildren().add(placeholderRegion);
placeholderLabel = new Label("No treeItems");
placeholderRegion.getChildren().setAll(placeholderLabel);
}
}
getVirtualFlow().setVisible(!isTreeEmpty());
if (placeholderRegion != null)
placeholderRegion.setVisible(isTreeEmpty());
}
#Override
protected void layoutChildren(double x, double y, double w, double h) {
super.layoutChildren(x, y, w, h);
if (placeholderRegion != null && placeholderRegion.isVisible()) {
placeholderRegion.resizeRelocate(x, y, w, h);
}
}
private boolean isTreeEmpty() {
return getSkinnable().getRoot() == null;
}
}
private Parent createContent() {
TreeView<String> tree = new TreeView<>() {
#Override
protected Skin<?> createDefaultSkin() {
return new TreeViewPlaceholderSkin<>(this);
}
};
Button toggle = new Button("toggleRoot");
toggle.setOnAction(e -> {
TreeItem<String> root = tree.getRoot();
tree.setRoot(root == null ? new TreeItem<>("root") : null);
});
BorderPane content = new BorderPane(tree);
content.setBottom(toggle);
return content;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
//stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TreeViewWithPlaceholder.class.getName());
}
In my ZK project with Java, I need to show a message box in which there needs to be a hyperlink, from which user can open another web page.
How can I achieve this?
According to the documentation, the customization of the MessageBox is rather limited. There is a possibilty to completely change the UI via MessageBox.setTemplate(), but this affects all MessageBoxes.
In our project, we replaced zk's default MessageBox with our own, which works more like a dialog. With this, we are in full control over the content of the dialog.
public abstract class OurDialog
{
private Window window;
private DialogListener closeListener;
public OurDialog(String title)
{
window = new Window();
window.setTitle(title);
window.setHflex("min");
window.setSizable(false);
window.setPosition("center");
window.setContentStyle("overflow: auto");
window.addEventListener(Events.ON_CLOSE, new EventListener<Event>()
{
#Override
public void onEvent(Event event)
throws Exception
{
if (closeListener != null)
{
if ("confirmed".equals(event.getData()))
{
closeListener.onClose(OurDialog.this);
if (!closeListener.onCloseConfirmation(OurDialog.this))
{
event.stopPropagation();
}
}
else
{
closeListener.onCancel(OurDialog.this);
if (!closeListener.onCancelConfirmation(OurDialog.this))
{
event.stopPropagation();
}
}
}
}
});
}
public final void setWidth(int width)
{
if (width != 0)
{
window.setMinwidth(width);
window.setWidth(width + "px");
}
else
{
window.setHflex("min");
}
}
public final void setHeight(int height)
{
if (height != 0)
{
window.setMinheight(height);
window.setHeight(height + "px");
}
else
{
window.setVflex("min");
}
}
public final void close()
{
Events.sendEvent(Events.ON_CLOSE, window, "confirmed");
}
public final void cancel()
{
Events.sendEvent(Events.ON_CLOSE, window, "cancelled");
}
/**
* #param closeListener called when the dialog is closed.
*/
public final void show(DialogListener closeListener)
{
setCloseListener(closeListener);
window.appendChild(getContent());
GUIHelper.setFocusToFirstInput(window);
OurMeatApplication.getCurrent().showDialog(this);
}
/**
* #return the component to be displayed in the dialog.
*/
protected abstract Component getContent();
}
Our main application class (OurApplication) has a reference to the page and provides the method to show the dialog:
final void showDialog(final OurDialog dialog)
{
Window window = dialog.getWindow();
window.setParent(page);
window.doModal();
}
This is a very generic dialog implementation for pretty much any purpose. For the specific MessageBox case, we have a sub class that provides a prepared UI, error level indicators, several buttons to pick, and a specialised listener to listen to those buttons.
I'm working on a task-planning AWT applet for my dev team, and I'm running into a problem.
I'm using a screen system, where the main class has a "current screen" variable that it uses to paint other screens. When the applet starts, it loads the "main screen" which has a "Chatroom" button. When you click the button, it should open the chatroom screen.
My problem is that it displays the main screen just fine, but when you click the button everything just goes blank and the chatroom does not show up at all. What am I doing wrong?
Each screen is a subclass of the Screen class, which is a subclass of Container.
Main Class:
public class TPApplet extends Applet
{
private static final long serialVersionUID = 7611084043153150559L;
private static final int WIDTH = 400;
private static final int HEIGHT = 350;
private static final String TITLE = "TaskPlanner v";
private static final double VERSION = 0.01;
private boolean setup = false;
public Screen currentScreen;
public void init()
{
setLayout(null);
setScreen(new MainScreen(this));
}
public void stop()
{
}
public void setScreen(Screen s)
{
if (currentScreen != null)
{
currentScreen.destroy();
remove(currentScreen);
}
currentScreen = s;
if (currentScreen != null)
{
currentScreen.init();
add(currentScreen);
}
}
public void paint(Graphics g)
{
if (!setup)
{
setSize(WIDTH, HEIGHT);
setName(TITLE + VERSION);
currentScreen.setLocation(0, 0);
currentScreen.setSize(WIDTH, HEIGHT);
setup = true;
}
if (currentScreen != null)
{
currentScreen.paint(g);
}
}
}
Main Menu class:
public class MainScreen extends Screen
{
private static final long serialVersionUID = -993648854350389881L;
private TPApplet applet;
private Button todoButton;
private Button chatButton;
private boolean setup = false;
public MainScreen(TPApplet tpApplet)
{
applet = tpApplet;
}
#Override
public void init()
{
setLayout(null);
todoButton = createButton("To-Do List");
chatButton = createButton("Chatroom");
}
#Override
public void destroy()
{
removeAll();
}
#Override
public void paint(Graphics g)
{
if (!setup)
{
todoButton.setLocation(25, 50);
todoButton.setSize(100, 40);
chatButton.setLocation(135, 50);
chatButton.setSize(100, 40);
setup = true;
}
}
#Override
public void actionPerformed(ActionEvent e)
{
if (e.getSource() instanceof Button)
{
Button button = (Button) e.getSource();
if (button.getLabel() == chatButton.getLabel())
{
applet.setScreen(new ChatScreen(applet));
}
}
}
}
Chatroom Class:
public class ChatScreen extends Screen
{
private static final long serialVersionUID = -8774060448361093669L;
private TPApplet applet;
private ScrollPane chatWindow;
private TextField textField;
private Button sendButton;
private boolean setup = false;
public ChatScreen(TPApplet tpApplet)
{
applet = tpApplet;
}
#Override
public void init()
{
setLayout(null);
sendButton = createButton("Send");
chatWindow = new ScrollPane();
textField = new TextField();
add(chatWindow);
add(textField);
}
#Override
public void destroy()
{
removeAll();
}
#Override
public void paint(Graphics g)
{
if (!setup)
{
chatWindow.setLocation(20, 20);
chatWindow.setSize(100, 100);
textField.setLocation(150, 150);
textField.setSize(60, 20);
sendButton.setLocation(220, 150);
sendButton.setSize(40, 20);
setup = true;
}
}
#Override
public void actionPerformed(ActionEvent e)
{
if (e.getSource() instanceof Button)
{
Button button = (Button) e.getSource();
if (button.getLabel() == sendButton.getLabel())
{
String text = textField.getText();
}
}
}
}
Thank you in advance for your help!
I suspect that seen as you've chosen to discard the use of layout managers, when you add a new screen, the screen is being added with a 0x0 size
public void setScreen(Screen s)
{
//...//
if (currentScreen != null)
{
currentScreen.init();
// Look ma, I have no size...
add(currentScreen);
}
}
One of the jobs of a layout manger is to decide how any new components should be laid out.
Try setting the applet's layout manager to something like BorderLayout.
The next problem is that the child screens suffer from the same problem, so even though the screen will be sized (based on the needs of the layout manager), the screens themselves also have no layout manager, so the components you add to them have no size and it will appear that the screen hasn't been updated.
I'd also recommend that you take a look at Andrew's example of CardLayout
You could also check out A Visual Guide to Layout Managers and Using Layout Managers for more details...
You will need to invalidate() the applet in your setScreen method.
The new screen component needs to be laid out again to compute the sizes of its children.
It's a shame this isn't done automatically when adding!
Also, consider doing this using a LayoutManager if possible. Would a CardLayout work for you?
I am working on a game using JME3 and Nifty GUI. I have an outer class that has a Nifty member variable. And the inner class should be able to access that variable regardless of access modifier. In the constructor I assign a new Nifty object to it. However when I access that variable in the inner class I run into problems. I did a little debugging and found out it's because the inner class thinks the Nifty member variable is null and I cant figure out why. Its not null in the outer class. Since this is a JME3 game I tried to have the inner class implement the AppState interface but it still shows the Nifty member variable as null. Here is the code:
public class MenuScreen extends SimpleApplication {
/** Used to configure Nifty GUI. */
private Nifty mNifty;
private NiftyJmeDisplay mNiftyDisplay;
private Element popup;
//*******************
// Overridden medhods
//*******************
/** This method is used to initialize everything needed to display the game screen. */
#Override
public void simpleInitApp() {
guiNode.detachAllChildren();
initNifty();
flyCam.setDragToRotate(true);
}
/**
* The game's main update loop.
*
* #param tpf Time Per Fram, the time it takes each loop to run.
*/
#Override
public void simpleUpdate(float tpf) {
// not used
}
#Override
public void simpleRender(RenderManager rm) {
// not used
}
public static void main(String[] args) {
MenuScreen app = new MenuScreen();
app.start();
}
/**
* Helper method to initialize and configure Nifty GUI.
*/
private void initNifty() {
mNiftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, guiViewPort);
mNifty = mNiftyDisplay.getNifty();
guiViewPort.addProcessor(mNiftyDisplay);
// If this is being run on a desktop then load the desktop main menu.
if (Strings.OS_NAME.contains("windows") || Strings.OS_NAME.contains("mac") || Strings.OS_NAME.contains("linux")) {
mNifty.fromXml("Interface/XML/DesktopMenuScreenGui.xml", "start", new MenuScreen().new MenuScreenGui());
}
// If its an Android device load the mobile main menu.
else if (Strings.OS_NAME.contains("android")) {
mNifty.fromXml("Interface/XML/MobileMenuScreenGui.xml", "mobile", new MenuScreen().new MenuScreenGui());
}
}
//**************
// Inner Classes
//**************
/**
* © Jason Crosby 2012 <p>
*
* This class handles all the GUI interactions like button clicks.
*
* #author Jason Crosby
*/
public class MenuScreenGui implements ScreenController, EventTopicSubscriber<MenuItemActivatedEvent>,
AppState {
#Override
public void initialize(AppStateManager stateManager, Application app) {
}
#Override
public void cleanup() {
}
#Override
public boolean isEnabled() {
return false;
}
#Override
public boolean isInitialized() {
return false;
}
#Override
public void postRender() {
}
#Override
public void setEnabled(boolean active) {
}
#Override
public void stateAttached(AppStateManager stateManager) {
}
#Override
public void stateDetached(AppStateManager stateManager) {
}
#Override
public void render(RenderManager rm) {
}
#Override
public void update(float tpf) {
}
#Override
public void bind(Nifty nifty, Screen screen) {
// not used
}
#Override
public void onEndScreen() {
// not used
}
#Override
public void onStartScreen() {
// not used
}
#Override
public void onEvent(String string, MenuItemActivatedEvent t) {
}
//**************
// Class methods
//**************
/**
* Called when the play button is clicked.
*/
public void playButton() {
}
/**
* Called when the high scores button is clicked.
*/
public void highScoresButton() {
}
/**
* Called when the settings button is clicked.
*/
public void settingsButton() {
}
public void quitButton() {
showDialog();
}
/**
* Called when the rate button is clicked. Only Available on mobile.
*/
public void rateButton() {
}
/**
* Called when the feedback button is clicked. Only on mobile devices.
*/
public void feedbackButton() {
}
/**
* Called when the help button is clicked.
*/
public void helpButton() {
}
/**
* Called when the dialog needs to be shown.
*/
public void showDialog() {
System.out.println("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW");
popup = new Nifty().createPopup("popup");
System.out.println("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ");
//Menu myMenu = popup.findNiftyControl("#menu", Menu.class);
//myMenu.setWidth(new SizeValue("100px")); // must be set
//myMenu.addMenuItem("Click me!", new menuItem("menuItemid", "blah blah")); // menuItem is a custom class
//mNifty.subscribe(mNifty.getCurrentScreen(), myMenu.getId(), MenuItemActivatedEvent.class, this);
mNifty.showPopup(mNifty.getCurrentScreen(), popup.getId(), null);
}
public void clsoseDialog() {
}
/**
* Used to return a String to the Nifty xml file.
*
* #param name The name key associated with the String.
* #return The String associated with the key.
*/
public String getString(String name) {
if (name.equals("play")) {
return Strings.PLAY_BUTTON;
}
else if (name.equals("high_score")) {
return Strings.HIGH_SCORES_BUTTON;
}
else if (name.equals("settings")) {
return Strings.SETTINGS_BUTTON;
}
else if (name.equals("quit")) {
return Strings.QUIT_BUTTON;
}
else if (name.equals("rate")) {
return Strings.RATE_BUTTON;
}
else if (name.equals("feedback")) {
return Strings.FEEDBACK_BUTTON;
}
else if (name.equals("rules")) {
return Strings.RULES_BUTTON;
}
return null;
}
}
}
What happens is I click on the quit button which calls the quitButton() method. That works fine. That in turn invokes showDialog() which is where the problem is. In the showDialog() method is this line popup = new Nifty().createPopup("popup"); and it is at that line which mNifty is null when it shouldn't be. Any assistance is appreciated.
The line
popup = new Nifty().createPopup("popup");
does not use mNifty. It creates a new instance of Nifty and then calls creatPopup() on this new instance. Since earlier you initialized mNifty by calling what looks like a factory method
mNifty = mNiftyDisplay.getNifty();
it is quite possible that obtaining a Nifty via new does not return a completely initialized instance. Since you haven't posted the code for Nifty it is unclear what is happening.
I would double-check to make sure that creating a Nifty via new will return a fully initialized instance, and that you really wanted a new instance here.
I have a problem with ClickHandler in my project using GWT.
In the title of dialog box I want to insert a new button.
I created a new insert method: addToTitle(...).
I added ClickHandler to the button
Problem: click event by button doesn't fire. Why?
Here is my code:
DialogBox dialog = new DialogBox();
Button button = new Button("A new Button");
button.addClickHandler(new ClickHandler()
{
#Override
public void onClick(ClickEvent event)
{
Window.alert("yuhuhuhu");
}
});
dialog.addToTitle(button);
code (extracted from the comments section) :
public class PlentyDialogWindow extends DialogBox {
private FlowPanel captionPanel = new FlowPanel();
public Widget closeWidget = null;
private boolean closeOnEscKey = false;
private FlowPanel titleContentWrapper = new FlowPanel();
public PlentyDialogWindow(boolean isModal) {
super( false, isModal);
this.addStyleName("DialogBox");
this.getElement().setId("DialogBoxId");
this.setAnimationEnabled(true);
this.closeWidget = generateCloseButton();
}
public void setCaption( String txt,Widget w) {
captionPanel.setWidth("100%");
this.addCaption(txt);
this.titleContentWrapper.getElement().getStyle().setDisplay(Display.INLINE_BLOCK);
captionPanel.add(this.titleContentWrapper);
FlowPanel widgetWrapper = new FlowPanel();
widgetWrapper.add(w);
widgetWrapper.addStyleName("PlentyPopupCloseIconWrapper");
captionPanel.add(widgetWrapper);
captionPanel.addStyleName("Caption");
Element td = getCellElement(0,1);
td.setInnerHTML("");
td.appendChild(captionPanel.getElement());
}
/** * * #param w */ public void addToTitle(Widget w) {
this.titleContentWrapper.add(w);
}
}
If your only problem is ClickHandler not being called try using addDOMHandler instead of addClickHandler
yourWidget.addDomHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
}
},ClickEvent.getType());
The solution is a bit tricky.
public class PlentyDialogWindow extends DialogBox {
/*
* Create custom inner class extending `FlowPanel`. You need it only
* to make `onAttach` and `onDetach` methods be visible to wrapping
* class (e.g. your `PlentyDialogWindow` class).
*/
static class MyCaptionPanel extends FlowPanel {
#Override
protected void onAttach() {
super.onAttach();
}
#Override
protected void onDetach() {
super.onDetach();
}
}
/*
* `PlentyDialogWindow`'s field `captionPanel` will be an instance of
* this class.
*/
private MyCaptionPanel captionPanel = new MyCaptionPanel();
/*
* ... leave the rest of your class untouched ...
*/
/*
* Finally, overwrite `PlentyDialogWindow`'s `onAttach` and `onDetach`
* methods to invoke `captionPanel`'s corresponding methods:
*/
#Override
protected void onAttach() {
super.onAttach();
captionPanel.onAttach();
}
#Override
protected void onDetach() {
super.onDetach();
captionPanel.onDetach();
}
}
That's all.