CallOutBox how to, usage


#1

I want to put some elements (Slider, toggle Button) inside a Popup . I’m using for this propose a CallOutBox launchAsynchronously. The problem is, the CallOutBox doesn’t shows the slider and toggle, and the Application crashes always when I leave the CallOutBox. Maybe someone can explain me how has to be used, for example I tried to use it in the following way:

 std::shared_ptr<Slider> mySlider = std::make_shared<Slider>();
 std::shared_ptr<Label> myLabel = std::make_shared<Label>();
 mySlider->setRange(40, 220);
 mySlider->setTextValueSuffix("bmp");
 mySlider->addListener(this);
            
 myLabel->setText("BPM", dontSendNotification);
 myLabel->attachToComponent(mySlider.get(), true);
 std::shared_ptr<ComponentSlider> myComponentSlider = std::make_shared< ComponentSlider >(slider, 200, 100);
  CallOutBox::launchAsynchronously (myComponentSlider.get(), metronomeButton->getBoundsInParent(), fWindow);

The class ComponentSlider is only adding a Slider to a Component, and making it Visible. I’m calling CallOutBox always when I press some buttons should the CalloutBox appear with the slider. I tried to declare the Slider, Label and ComponentSlider on the stack, but the CallOutBox still doesn’t works. Maybe someone can show me a working example how this function has to be used.
Regards


#2

I’m relatively new to JUCE and had similar issues. I’m using CallOutBox to display a GUI Component called PreferencesBox which I’ve created in Projucer. The top four lines calculate the areas the box is shown and the pointer points to.

    Rectangle<int> pointTo = imageButtonPreferences->getScreenBounds();
    Rectangle<int> fitIn = getParentComponent()->getScreenBounds();
    fitIn.expand (8, 0); // not sure about this
    fitIn.removeFromTop (TOP_BAR_HEIGHT);
    
    PreferencesBox* preferencesBox = new PreferencesBox ();
    CallOutBox::launchAsynchronously (preferencesBox, pointTo, nullptr).updatePosition (pointTo, fitIn);

As for the crash: Documentation about launchAsynchronously() says:

It returns a reference to the newly-created box so that you can customise it, but don’t keep a pointer to it, as it’ll be deleted at some point when it gets closed.

Are you closing the applications main window without closing the CallOutBox first? If so the reference is still there causing a leak


#3

I think you got confused with the different smart pointers. The shared_ptr is the same like the ReferenceCountedObject. This is used, if you need to pass an object around, without having a dedicated owner. In this case this is not necessary.
You probably thought of the std::unique_ptr, which is the same like a ScopedPointer. It keeps the object alive, as long as the ScopedPointer exists on the stack somewhere.
But for the CalloutBox you need neither of them, because the created CalloutBox will own the component you give it to display, and delete it when the CalloutBox is dismissed.

Your code has a shared pointer myComponentSlider. You hand over the address of the pointee (using .get()), which means, the lifetime of that raw pointer is no longer managed by any “smartness”. The next thing what happens is, that the only reference myComponentSlider goes out of scope, and the pointee, the ComponentSlider is deleted. But the CalloutBox still has the raw pointer, which points to an unreserved memory address. Even worse, it tries to free it again, when the CalloutBox is dismissed.
A crash is unavoidable at this point.

HTH


#4

@daniel .get() is something necessary in order to interact with libraries written in “old” c++ 98. Here, Juce need a raw pointer, for this reason I have to take the raw pointer from the smart pointer with get().
@aph Thanks for your answer. No, I’m not closing the main Window without closing the CallOutBox, I solved this problem creating a new class inherited from Component, with a Slider an another elements I’ll need inside the CallOutBox, the following piece of code works without crash:

MyOwnComponent* myComponent = new MyOwnComponent(component, 220, 200);
CallOutBox::launchAsynchronously (myComponent, metronomeButton->getBoundsInParent(), this);  

Apparently CallOutBox needs a raw pointer in order to work properly.
Regards


#5

I know, that it wouldn’t compile without get. Still it is wrong, because the CalloutBox takes ownership, and you can’t have two different kinds of ownership. The shared_ptr owns the myComponent and the CallOutBox will own the myComponent after that call. That was my whole point.
The raw pointer is not, because it is an “old” api, but gives a hint, that you cannot keep the thing you hand over here. Otherwise the API would have used a reference Component& here.