ComponentBuilder and ValueTree


#1

Hello all,

I’ve just started using JUCE and I’m writing a test app to evaluate it but I have a problem with ComponentBuilder class I can’t solve.

Essentially the ComponentBuilder don’t update the component to reflect changes into the ValueTree.
i.e TypeHandler::updateComponentFromState(…) is never called.

This is a stripped down version of my program I’m using to understand the problem, please forgive the naive implementation.

class View : public Component,
             public Button::Listener,
             public TextEditor::Listener,
			 public ActionBroadcaster
{
public:

	View() { };
	View(ActionListener * listener) { addActionListener(listener); } ;

	virtual ~View() 
	{
		deleteAllChildren();
	};

	void paint (Graphics& g)
	{
		 g.fillAll (Colour (0xff6d8579));
	};
    void resized() 
	{
	};

	virtual void buttonClicked (Button* buttonThatWasClicked)	// Button::Listener		interface
	{
		sendActionMessage (buttonThatWasClicked->getComponentID());
	};

private:

};

template <class ComponentClass>
class ComponentTypeHandler  : public ComponentBuilder::TypeHandler
{
public:
    ComponentTypeHandler(const Identifier& valueTreeType_)
        : ComponentBuilder::TypeHandler (valueTreeType_)
    {
    }

    Component* addNewComponentFromState (const ValueTree& state, Component* parent)
    {
        Component* const c = new ComponentClass();

        if (parent != nullptr)
		parent->addAndMakeVisible (c);

        updateComponentFromState (c, state);
        return c;
    }

    void updateComponentFromState (Component* component, const ValueTree& state)
    {
        ComponentClass* const c = dynamic_cast <ComponentClass*> (component);
        jassert (c != nullptr);

		ValueTree babbo = state.getParent();
		int myIndex = babbo.indexOf(state);

		c->setBounds (72, myIndex*24+5, 150, 24);
		c->setText (state.getProperty("value"));
    }
};

template <>
class ComponentTypeHandler<TextButton>  : public ComponentBuilder::TypeHandler
{
public:
    ComponentTypeHandler()
        : ComponentBuilder::TypeHandler ("TextButton")
    {
    }

    Component* addNewComponentFromState (const ValueTree& state, Component* parent)
    {
        TextButton* const b = new TextButton();

        if (parent != nullptr)
		{
			parent->addAndMakeVisible (b);
			b->addListener(dynamic_cast<Button::Listener*>(parent));
		}

        updateComponentFromState (b, state);
        return b;
    }

    void updateComponentFromState (Component* component, const ValueTree& state)
    {
        TextButton* const b = dynamic_cast <TextButton*> (component);
        jassert (b != nullptr);

		ValueTree babbo = state.getParent();
		int myIndex = babbo.indexOf(state);

		b->setBounds (72, myIndex*24+5, 150, 24);

		b->setButtonText (state.getProperty("value"));
    }
};

template <>
class ComponentTypeHandler<View>  : public ComponentBuilder::TypeHandler
{
public:
    ComponentTypeHandler()
        : ComponentBuilder::TypeHandler ("SimpleForm")
    {
    }

    Component* addNewComponentFromState (const ValueTree& state, Component* parent)
    {
        Component * c = new View();

        if (parent != nullptr)
            parent->addAndMakeVisible (c);

        updateComponentFromState (c, state);

	getBuilder()->updateChildComponents(*c, state);

        return c;
    }

    void updateComponentFromState (Component* component, const ValueTree& state)
    {
        View* const c = dynamic_cast <View*> (component);
        jassert (c != nullptr);

	c->setSize (800, 600);
    }
};

void registerComponentTypeHandlers (ComponentBuilder& builder)
{
	builder.registerTypeHandler (new ComponentTypeHandler <TextEditor>("TextEditor"));
	builder.registerTypeHandler (new ComponentTypeHandler <TextButton>());

	builder.registerTypeHandler (new ComponentTypeHandler <View>());
};


class Controller : public ActionListener
{
public:

	Controller() 
	{
		initValues();

		cb.state = values;

		registerComponentTypeHandlers(cb);

		ActionBroadcaster * view = dynamic_cast<ActionBroadcaster*> (cb.getManagedComponent());

		view-> addActionListener(this);
	};

	virtual ~Controller() {	};

	virtual void actionListenerCallback (const String& message) 
	{
		AlertWindow::showMessageBox (AlertWindow::InfoIcon,
                                message + " clicked",
				values.toXmlString()  );
		
		Logger::writeToLog(values.toXmlString() );

		values.getChildWithProperty("id", "Pippo").setProperty("value", 42, nullptr);
	};

	Component * getComponent () 
	{
		return cb.getManagedComponent();
	};

private:
	ValueTree values;
	ComponentBuilder cb;

	void initValues()
	{
		String xmlString (
			"<SimpleForm id=\"MyForm0001\">"
				"<TextEditor id=\"Pippo\" value=\"123\"/>"
				"<TextEditor id=\"Pluto\" value=\"AAA\"/>"
				"<TextButton id=\"Carlo\" value=\"OK\"/>"
			"</SimpleForm>");

		ScopedPointer<XmlElement> element = XmlDocument::parse(xmlString);

		values = ValueTree::fromXml(*element);

	};
};

and then into MainWindow constructor:

setContentNonOwned (controller.getComponent(), true);

Everything works except for the callback to
ComponentTypeHandler::updateComponentFromState (…)

that should be triggered by changing a ValueTree property in
Controller::actionListenerCallback (…)

(I can confirm that the property value is actually changed)

I think I did all the needed stuff to make it working:

  • I keep an instance of ComponentBuilder for the lifetime of the app.
  • I use ComponentBuilder ::getManagedComponent() and not createComponent.
  • I’ve registered all TypeHandlers needed.

Please let me know if you spot anything wrong.

many thanks
Andrea M.


#2

I’ve got it.
a trivial mistake as expected, sorry

the problem was in my Controller class constructor
where I wrote:

cb.state = values;

instead of using the ComponentBuilder constructor to assign the ValueTree.


ComponentBuilder::ComponentBuilder (const ValueTree& state_)
    : state (state_), imageProvider (nullptr)
{
    state.addListener (this);
}

the addListener call was never performed.

btw, JUCE looks like one the best libraries I’ve found in years.
Easy to understand, browse the code and debug.

Congratulations to the author(s)

AndreaM


#3

Ah, I’ve made similar mistakes myself many times! Thanks, glad you’re enjoying the library!