I'm writing a GUI, and need to be able to replace a border-pane's :center section with another pane. The problem is, the call to config! in the handler doesn't have the effect of changing the pane. All other nearly identical calls elsewhere work.
The problem can be summed up as:
(ns live-math-pet.gui.mcve
(:require [seesaw.core :as sc]))
(defn -main []
(let [; The starting contents
child1 (sc/label :text "Hello!")
; What I want to change to at the click of a button
child2 (sc/label :text "World!")
base-pane (sc/border-panel :center child1)
change-btn (sc/button :listen [:action
(fn [_]
; This doesn't change the :center area
(sc/config! base-pane :center child2))])
frame (sc/frame :content base-pane)]
(sc/config! base-pane :south change-btn)
; These work, but the call to config! in the handler doesn't
(sc/config! base-pane :center (sc/label "Test1"))
(sc/config! base-pane :center child1)
(-> frame
(sc/pack!)
(sc/show!))))
The problem is, this doesn't do anything when I click the button. I expect config! to replace the :center section, but it doesn't. The click handler is firing, the config! just doesn't seem to do anything.
The calls to config! at the bottom work, even though the last call is identical.
After searching around, I found a solution, but it kind of rubs me wrong. If I change the handler to
(fn [_]
(sc/replace! base-pane child1 child2)
it works. I don't like this for a couple reasons though:
I need to know what the current contents are. If I don't know, I need to do a lookup, which seems wasteful.
If I'm giving it the child to replace, presumably it needs to search the parent to find the child. Again, this seems needlessly wasteful.
If base-pane was a frame, and I wanted to replace its :contents using config!, it would work as I'd expect.
Why doesn't config! work here? Is it necessary to use replace!?
Updates:
It turns out the button is having an effect, but it only makes the change after resizing the window for some reason.
When it adds the new panel, it overlays it over the previous one instead of replacing it. It looked like it needed to be repainted, so I tried running (sc/repaint! (sc/select base-pane [:*])), but it didn't make a difference.
At this point I realized I might just have to use sc/replace!, but then ran into the problem of how to get the previous child. (config base-pane :center) doesn't work. I think I'm going to have to give each child a class, then use sc/select.
Related
In my scene I have a vector with multiple custom sprites. When I tap on one of them, I want an action to be fired on another element on the scene, can be another sprite in the vector, or another node. I have been researching the best way to do this, but I'm not quite sure how to implement it. The options are:
Add a touch listener to the scene, and verify if it was tapped inside the bounds of the sprite with rect. containsPoint(point). And after that, I have to get the sprite that was tapped to do the action I want. For me, it doesn't seems very clean to do it this way. And if two sprites are overlaped, I have to verify if the sprite is behind or in the front in order to retrieve the desired sprite. I followed this example: Touch Event example
Add a touch listener in the subclass of the sprite (my custom sprite). And add onTouchBegan and onTouchEnded inside it. But this way, I don't know how to modify an attribute of another sprite, or another element in the scene (Is it possible to use Delegates like Objective-C does?). I followed this example: Subclass Sprite Example
My main problem is that I don't understand very well how to make a node interact with another node in the scene. I have seen a lot of tutorials, but in all of them, when you interact with a node, it only changes its attributes, not other nodes' attributes.
Thanks in advance.
I shall propose "EventCustom" way :)
You can add in your touchBegan / touchEnded methods (wherever you put them... you got the point...) additional code for passing an EventCusto to the _eventDispatcher and get it announced to the world ;)
EventCustom *e = new EventCustom("MyAwesomeEvent");
e->setUserData(ptrMyFantasticData); //This function takes a void pointer. cheers :)
_eventDispatcher->dispatchEvent(e);
You may subclass the EventCustom class but that is hardly necessary. You can always hang an object to it with setUserData().
Now the objects which need to react to the event can listen to it by
_myCustomListener = EventListenerCustom::create(
"MyAwesomeEvent",
CC_CALLBACK_1(
ListeningClass::onMyAwesomeEvent,
this
)
);
_eventDispatcher->addEventListenerWithXXXXXPriority(_myCustomListener, XXX);
//ScreenGraphPriority / FixedPriority depends on situation. Either should work.
It's always a good practice to remove your listeners when you go off, so somewhere, perhaps in onExit(), where you removed touch listeners remove this listener too, as
_eventDispatcher->removeEventListener(_myCustomListener);
Going a bit off the track, a clarification:
CC_CALLBACK_X are a bit tricky names. The X indicates the no. of args the target function will get. Here, event dispatcher will pass 1 arg i.e. ptr to object of EventCustom you handed it, so we use CC_CALLBACK_1. The next arg - here "this" - is the object on which the method will be invoked.
In short, we may say that this callback is going to result into a function call this->onMyAwesomeEvent(e);
For CC_CALLBACK_2 onwards, we can specify additional args, 3rd arg onwards.
Coming back to the issue at hand, ListeningClass::onMyAwesomeEvent will look something like
void ListeningClass::onMyAwesomeEvent(EventCustom *e)
{
MyFantasticData *d = (MyFantasticData *) e->getUserData();
CCLOG("[ListeningClass::onMyAwesomeEvent] %d", d->getMyPreciousInt());
}
Hope it helps :)
Set your elements tags or names with setTag and setName. Then if element x is touched, get them with getChildByTag or getChildByName and do what you need to do.
With the second option you list above.
To make a node interact with another node in the scene you can add touch callback function to your custom sprite object like that:
https://github.com/Longpc/RTS/tree/master/Classes/base/dialogBase
and in main scene you can define function to handle this callback. So you can do every thing to unit in you scene
I'm new with Qt and I'm making a little application. I've do it with QWizard and QWizardPages.
I Have added 2 CustomButtons to the wizard so it has 5 buttons down: ButA, ButB, Back, Next/Finish, and Cancel.
ButA and ButB don't have to appear in all WizardPages. Eg:
WP0: just ButB
WP1: ButA and ButB
To do that, I have:
void WP0::initializePage()
{
wizard()->button(QWizard::CustomButton1)->setVisible(false);
}
With that when the app starts, you can't see butA. BUT if you go to the next page (where you see ButA and ButB) and then you click on BackButton, then you see ButA in WP0.
I supose that then you click on BackButton there is no call to WP0::initializePage() so my question is: how or where should I call that wizard()->button(QWizard::CustomButton1)->setVisible(false);
to never see ButA on WP0 ? Or what should I do?
I don't know if I understand your question completely. your description is kind of complicated. I think you should try button's events. It means you should call this function in press event or something like that and it's better that you define a boolean variable for true or false and call it by reference. I think this should solve your problem.
In my application, I have my re-implemented QGraphicsView checking for a mouseReleaseEvent(), and then telling the item at the position the mouse is at to handle the event.
The QGraphicsItem for my view is made up of two other QGraphicsItems, and I check which one of the two is being clicked on (or rather having the button released on), and handle the respective events.
In my Widget's constructor, I set one of the items as selected by default, using the same methods I used when the items detect a release.
When I debugged, I found that for the LabelItem, select is called without a problem from the constructor (and the result is clear when I first start the application). But, when I click on the items, the application terminates. I saw that I was getting into the select function, but not leaving it. So the problem is here.
Which is very weird, because the select function is just a single line setter.
void LabelItem::select()
{
selected = true;
}
This is the mouseReleaseEvent;
void LayerView::mouseReleaseEvent(QMouseEvent *event)
{
LayerItem *l;
if(event->button() == Qt::LeftButton)
{
l = (LayerItem *) itemAt(event->pos());
if(l->inLabel(event->pos()))
{ //No problem upto this point, if label is clicked on
l->setSelection(true); //in setSelection, I call select() or unselect() of LabelItem,
//which is a child of LayerItem, and the problem is there.
//In the constructor for my main widget, I use setSelection
//for the bottom most LayerItem, and have no issues.
emit selected(l->getId());
}
else if(l->inCheckBox(event->pos()))
{
bool t = l->toggleCheckState();
emit toggled(l->getId(), t);
}
}
}
When I commented the line out in the function, I had no errors. I have not debugged for the other QGraphicsItem, CheckBoxItem, but the application terminates for its events as well. I think the problem might be related, so I'm concentrating on select, for now.
I have absolutely no clue as to what could have caused this and why this is happening. From my past experience, I'm pretty sure it's something simple which I'm stupidly not thinking of, but I can't figure out what.
Help would really be appreciated.
If the LabelItem is on top of the LayerItem, itemAt will most likely return the LabelItem because it is the topmost item under the mouse. Unless the LabelItem is set to not accept any mouse button with l->setAcceptedMouseButtons(0).
Try to use qgraphicsitem_cast to test the type of the item. Each derived class must redefine QGraphicsItem::type() to return a distinct value for the cast function to be able to identify the type.
You also could handle the clicks in the items themselves by redefining their QGraphicsItem::mouseReleaseEvent() method, it would remove the need for the evil cast, but you have to remove the function LayerView::mouseReleaseEvent() or at least recall the base class implementation, QGraphicsView::mouseReleaseEvent(), to allow the item(s) to receive the event.
I have seen these odd behaviours: It was mostly binary incompatibility - the c++ side looks correct, and the crash just does not make sense. As you stated: In your code the "selected" variable cannot be the cause. Do you might have changed the declaration and forgot the recompile all linked objects. Just clean and recompile all object files. Worked for me in 99% of the cases.
I have a CCRepeatForever action with a tag of 20. I call this:
[player stopActionByTag:20];
Noting happens.
I call this:
[player stopAllActions];
It stops. Any idea? The action is created like this and runs fine:
CCRepeatForever *repeat=[CCRepeatForever actionWithAction:animate];
repeat.tag=20;
[player runAction:repeat];
update: i also tried setting animate.tag=21 and stopping that action by itself or in addition to the repeat action, but neither works.
This should work, I made a quick test with stopActionWithTag using CCRepeatForever and it stops it correctly.
I can only imagine two cases where it wouldn't work:
You have more than one action with the tag = 20 running on the same object.
The object you send runAction to is a different object than the one you send stopActionByTag to.
The former is easy to check. If this code fixes your problem, you're running multiple actions with the same tag on the player object:
while ([player getActionByTag:20]) {
[player stopActionByTag:20];
};
The latter is rather unlikely but possible. One way to find out is to set a breakpoint on the runAction line and note the address of the player variable. Then set another breakpoint at the stopActionByTag line and compare that player's address with the previous one. If they're not the same, then that would be the problem.
I'm invoking a NSOpenPanel from a thread created by boost C++.
the panel behaves erratically and doesn't respond well to mouse, that is clicking on objects does nothing sometime when clicking on top level combo box does improve response.
do i've to run a separate runloop I'm doing a runModalForDirectory which should take care of running its own loop.
I've also created a separate objc class which does performSelectorOnMainThread to show panel in main thread but still the behavior is same.
[ps performSelectorOnMainThread:#selector(showOpenPanel) withObject:nil
waitUntilDone:YES
modes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
I've also tried with waitUntilDone:NO and running a CFRunLoopRunInMode which isn't helping either.
- (bool) showOpenPanel
{
NSOpenPanel *op = [NSOpenPanel openPanel];
[op setAllowsMultipleSelection:YES];
[op setTitle:#"Choose File"];
[op setMessage:#"Choose file for Importing."];
[op setFloatingPanel:true];
bool result =[op runModalForDirectory:NSHomeDirectory() file:nil types:self.fileTypes];
if (result == NSOKButton) {
[self setSelectedFiles:[op filenames]];
[self setLastShowResult:true];
}
else {
[self setLastShowResult:false];
}
[self setPanelIsDone:true];
return self.lastShowResult;
}
NSOpenPanel is part of AppKit. AppKit functions and classes can only be safely used on the main thread.
Show us the code you used with performSelectorOnMainThread so we can help figure out why you might still be seeing problems. I suspect you're calling individual methods with it--don't; it won't work the way you expect. Call back to the main thread for the totality of your interaction with NSOpenPanel.