making a button clickable


I’m exploring the basics, and just starting with playing with some of the PGui elements. I’ve managed to create button with a callback by using the following:

        PGButton *but;
	but = new PGButton("button");
	but->setup("my button!");
	framework.define_key(but->get_click_event(MouseButton::one() ), "button press", button_callback, NULL);
	NodePath butNP = window->get_aspect_2d().attach_new_node(but);
	butNP.set_scale(0.25, 1.0, 0.25);

Using a simple callback that just prints “pressed” to the console, I know that this works.
However, if I try to make a more interesting button with a custom appearance using:

        // Load images for button states, and create 2d "cards" on which they will be rendered.
	Texture *readyTex, *pressedTex, *rolloverTex;
	readyTex = TexturePool::load_texture("ready.png");
	pressedTex = TexturePool::load_texture("depressed.png");
	rolloverTex = TexturePool::load_texture("rollover.png");
	CardMaker cm("cardMaker");
	PT(PandaNode) readyCard = cm.generate();
	PT(PandaNode) pressCard = cm.generate();
	PT(PandaNode) rollCard  = cm.generate();
	PT(PandaNode) inactCard = cm.generate();
	// create node paths
	NodePath readyPath(readyCard);
	readyPath.set_texture( readyTex );
	NodePath pressPath(pressCard);
	pressPath.set_texture( pressedTex );
	NodePath rollPath(rollCard);
	rollPath.set_texture( rolloverTex );
	NodePath inactPath( inactCard );
	inactPath.set_texture( readyTex );
	// create button
	PGButton *but;
	but = new PGButton("button");
	// setup button with the above nodepaths
	but->setup(readyPath, pressPath, rollPath, inactPath);
	// setup such that event handler calls the button callback function
	// when the botton click event occurs
	framework.define_key(but->get_click_event(MouseButton::one() ), "button press", button_callback, NULL);
	// attach the button to the 2d aspec-ratio corrected render node.
	NodePath butNP = window->get_aspect_2d().attach_new_node(but);
	// tweak the button size a wee bit
	butNP.set_scale(0.25, 1.0, 0.075);
        //do the main loop, iterating the event handler etc.

then in this case, although correctly displayed, the button is not interactive (can’t be clicked, doesn’t change to rollover state etc).

So, what have I missed in the second approach that means the button doesn’t work?

This is kinda unrelated, but I see you’re storing Texture and PGButton in ordinary pointers, while you should be using Panda’s PointerTo, to make sure that Panda will not accidentally delete your reference counted object.

So, use PT(Texture) instead of Texture* and PT(PGButton) instead of PGButton* (but I see you’ve actually done it correctly for PandaNode). Unless you are really sure that the object has no change of getting deleted before you’re done with it, but it’s still good coding practice to use PT, to avoid random invalid memory reads.
You can check when to do so by checking if the class inherits from ReferenceCount in some way.

Note that you do not need to delete reference-counted objects yourself, Panda will do so when they go out of scope.

About the actual problem, I don’t know, although maybe it’s related and something gets destructed here accidentally.

Pro-rsoft is correct, though this is not causing the problem in your case. The problem is that this version of PGButton::setup() doesn’t automatically set the frame size, when arguably it should. I’ll make changes so that it does so in the future, but in the meantime, you can add code like this:

   but = new PGButton("button");
   // setup button with the above nodepaths
   but->setup(readyPath, pressPath, rollPath, inactPath);

   // Set the button frame size.
   LPoint3f min_point, max_point;
   readyPath.calc_tight_bounds(min_point, max_point);
   but->set_frame(min_point[0], max_point[0],
                  min_point[2], max_point[2]);


Excellent, setting the frame size does it. Thank you both.