eg)
1. When X sprite is touched, pointer of X is stored in class Y
2. when X sprite is removedFromParent, X sprite will notify Y that it's going away
How do I achieve the second step?
Specifically, onExit(of class X) is a good place to do this kind of stuff?
When does onEnter/onExit get called? I don't find the reference helpful on this.
(void) onExit callback that is called every time the CCNode leaves the 'stage'. If the CCNode leaves the 'stage' with a
transition, this callback is called when the transition finishes.
During onExit you can't access a sibling node.
X::onExit()
{
getY()->notify_X_Dies(this);
}
yeah, you are right. onExit is called when you remove node from it's parent, or when it's parent is removed from the stage/other parent.
Related
I'm working through "Programming Principles and Practice", and I don't understand why this Widget class uses pointers as data members.
The book's explanations is this:
Note that our Widget keeps track of its FLTK widget and the Window with which it is associated. Note that we need pointers for that because a Widget can be associated with different Windows during its life. A reference or a named object wouldn’t suffice. (Why not?)
So, I still don't understand why the Widget can't have a named object Window win as a data member, which can take a different value when it's associated with a different Window. Could someone explain this a bit?
class Widget {
// Widget is a handle to a Fl_widget — it is *not* a Fl_widget
// we try to keep our interface classes at arm’s length from FLTK
public:
Widget(Point xy, int w, int h, const string& s, Callback cb)
:loc(xy), width(w), height(h), label(s), do_it(cb) { }
virtual ~Widget() { } // destructor
virtual void move(int dx,int dy)
{ hide(); pw–>position(loc.x+=dx, loc.y+=dy); show(); }
virtual void hide() { pw–>hide(); }
virtual void show() { pw–>show(); }
virtual void attach(Window&) = 0; // each Widget defines at least one action for a window
Point loc;
int width;
int height;
string label;
Callback do_it;
protected:
Window* own; // every Widget belongs to a Window
Fl_Widget* pw; // a Widget “knows” its Fl_Widget
};
Window own;
This will copy the whole Window object. If the real window changes, this widget will have an old and useless copy.
Window & own;
A reference. We meet two issues:
A) We can not know if the window has been deleted outside the widget code
B) The window must exists before this widget
Window * own;
A pointer can be NULL, as opposed to a reference. This avoids reference issues.
The book seems a bit old, just because it uses raw-pointers.
EDIT due to comments
It is absolutely true that issue "A)" is the same for references and pointers. But it can be easier handled with a pointer.
What I wanted to point to is that when a window is deleted, the widget must be informed about it, so as to not use the "own" object.
With a pointer, it can be resetted to NULL, so any further attemp to use "own" can be easily catched. But with a reference you need, at least, an extra bool in your code just to store if "own" is valid or not.
First, we need to work out the logical and implementation details. On the logical side, a window contains widgets: just like a mother has children. This is a one to many relationship. Each widget has an implementation entity, in this case an Fl_widget. It could equally be a MS windows Window, a QtWidget or and X-Windows Widget. It is one of those implementation dependent things
___________
|window |
| _______ | _________
| |widget-|-|--->|Fl_widget|
| _______ | _________
| |widget-|-|--->|Fl_widget|
|___________|
Within window itself, there will also be an implementation detail like another Fl_Widget or Fl_window.
The reason why we cannot have Window win instead of Window win* is because the window owns the widget and not the other way round. If we had Window win then whenever any window property changed, you'd have to modify all the widgets containing the same window. Taking, the mother child relationship, if the mother had a haircut, the mother member for all the children of the same mother would have to have a haircut. You'd have to have a method to keep track of and update all the mothers of all the children. This would just be a maintenance nightmare.
If we just hold a pointer to the window then we know that if the window is modified, all sibling widgets will be able to query the change in the parent window and get the same answer without a lot of effort.
The second part of your question about moving widgets between windows - I've never seen anyone do that. In all the implementations I have ever used, this is impossible. The parent window owns the widget for life. To "move it", you'd normally have to reincarnate it i.e. destroy it from the current window and re-create it in the new window.
I am attempting to properly constrain the movement of a QGraphicsItem (specifically QGraphicsRectItem) without changing the native behavior to function as a scrollbar on the X-axis.
I tried overriding the mouseMoveEvent function, but then I need to re-write the behavior for the rectangle in both the X and Y directions. At best, I can get the rectangle to snap to a single position with the mouse. (Here the rectangle will snap so the mouse holds it at the midpoint):
void SegmentItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
setY(0);
setX(event->scenePos().x() - boundingRect().width()/2);
}
I'm looking at itemChange right now, as described here, but it looks a little unwieldy and not exactly elegant.
EDIT: This should work, but I currently cannot coerce it to work.
Is there a way to just constrain the y-axis movement? (I will also need to create endstops for the scrollbar, but later.)
I tinkered with the code from the itemChange Class Reference Page, and enhanced it so all four corners of my QGraphicsRectItem would stay within the QGraphicsScene:
QVariant SegmentItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == ItemPositionChange && scene()) {
// value is the new position.
QPointF newPos = value.toPointF();
QRectF rect = scene()->sceneRect();
rect.setWidth(rect.width() - boundingRect().width());
rect.setHeight(0);
if (!rect.contains(newPos)) {
// Keep the item inside the scene rect.
newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
newPos.setY(2);
return newPos;
}
}
return QGraphicsItem::itemChange(change, value);
}
To answer the other part of your question about constraining movement to just one direction...use the same itemChange structure as laid out in the above answer. The only additional thing you need to do is transfer your item's current X or Y coordinate to the new position before returning it. This line allows Y to track the mouse, but keeps X the same (i.e. movement is restricted to vertical):
newPos.setX (this->pos().x());
Similarly, to allow X to track the mouse, but keep Y the same (i.e. movement is restricted to horizontal):
newPos.setY (this->pos().y());
With the ItemPositionChange notification, the item's current position hasn't yet changed, so you can manipulate the new position any way you want before returning the new value.
And again, trying to implement bezier curves redactor. There is
class BezierNode : public QGraphicsItem
BezierNode::BezierNode(QPointF point, Type type) : QGraphicsItem()
{
setPos(point);
setFlags(ItemIsMovable | ItemSendsScenePositionChanges | ItemSendsGeometryChanges);
}
It properly moves around in scene on mousePress + mouseMove, and I can catch events in itemChange() for some additional acting with control points. In QGraphicsItem::mouseMoveEvent() (according to Qt source on gitorious) there is a call to item->setPos(...). However, if I try to reimplement BezierNode::setPos(..), it's never triggered on moving object.
void BezierNode::setPos(const QPointF &pos) {
qDebug() << "setPos " << pos;
m_point = pos;
QGraphicsItem::setPos(pos);
}
In my case setPos() triggers only in constructor (there I call it manually). Yes, it moves in scene properly, I can get its position with pos() and use it everywhere instead of m_point, but I want to understand, what happens there.
Thanks in advance.
QGraphicsItem::setPos() is not virtual, so you can't override it. That's why BezierNode::setPos() will never be called.
I have a Class Player like this:
class Player
{
public:
Player();
~Player(void);
Sprite *sprite;
Sprite *rocket;
void draw(int x, int y, SpaceInvaders *system);
}
and in Player.cpp
void Player::draw(int x, int y, SpaceInvaders *system) {
sprite = system->createSprite("data/player.bmp");
sprite->draw(x, y);
}
Player::~Player(void)
{
sprite->destroy();
rocket->destroy();
}
This draw method is called in a while loop in main:
player.draw(int(xPos), 480-32, system);
The game runs fine until I X the window. That's when I get "Access violation reading location 0x00000004" on the first line in the Player::draw method.
I've read that it might be due to passing a null pointer or null reference but I don't know how to fix this.
Would appreciate any help, thanks!
It's most probably because when closing the window, something gets destroyed while draw is called - most probably the system pointer.
In your case, draw should never be called when the user wants to close its window (unless the x calls another function to start a closing process of some sort). The best would be to first validate that system is not NULL or even better, use a shared pointer to ensure it is still valid when being used. Afterwwards, you shoiuld ensure that draw is not called when the window is closing - that should be done when calling the draw function (or above depending on how you've designed your application.
On a side note, unless you have a caching mechanism (and even that is not the best way to do it), you're recreating your sprite everytime it's being drawn. I suggest you keep a member variable and initialize the sprite in the construtor.
i have many sprites and i want to move them in screen sequentially. for example 'A' moves to (x1,y1) place then 'B' goes to (x2,y2). i want to run these actions sequentially means first 'A' finishes it's job then 'B' starts.
i have a function that when i call it, returns a sprite and a place and i should send the sprite to that place. in the move function i have somthing like this:
void move(){
for(int i=0;i<10;i++){
pair<CCSprite,CCPoint> x=get();
CCFinitTimeAction* act=CCMoveTo::actionWithDuration(DIST/SPEED,x.second);
x.first->runAction(act)
}
}
now how can i make a delay until a motion finishes. i had put this line after runAction but it didn't work:
while(!act->isDone());
Assuming that get() can be called to fetch sprite 2 right when sprite 1 finishes, you could implement this using a callback that calls get() and starts the next sprite's movement. For this, you need to create a callback method. Assuming you are doing this in a scene, the following code should do it
void YourSceneClass::move() {
moveNext();
}
void YourSceneClass::moveNext() {
pair<CCSprite,CCPoint> x=get();
CCFinitTimeAction* move=CCMoveTo::actionWithDuration(DIST/SPEED,x.second);
CCCallFunc* startNext=CCCallFunc::create( this,
callfunc_selector(YourSceneClass::moveNext) );
CCSequence* act=CCSequence::create(move, startNext, NULL);
x.first->runAction(act);
}
With this code in your scene, you should be able to call move and have the first sprite start moving with the second one moving after that and so on. To make it stop, you have to add an appropriate condition in moveNext().