Adding a behaviour or property to TextEditor class

#1

Here I want to add a value listener to a TextEditor so that it’s contents reflect a value that may be changed from elsewhere:

 class TextListener : public TextEditor, public Value::Listener
    {
    public:
    	TextListener(Value &value,String s) {
    		value.addListener(this);
    		text.setText(s,false);
    	}
    	void Value::Listener::valueChanged(Value &val)
    	{
    		String s = val.getValue();
                text.setText(s,false);
    	};
    	void setText(String s) 
    	{
    		text.setText(s,false);
    	}
    private:
    	TextEditor text;
    };

The first problem is that text is not showing in the editor from the constructor nor outside calls to setText()…
thanks for any hints.

0 Likes

#2

You inherit TextEditor, and you aggregate a TextEditor. The one you see is the one you inherit, but the one you don’t see - the aggregated one - is the one you set the text to.

Easiest is to change:

text.setText(s,false);

to

setText(s,false);

or (the better design) you inherit from Component and call addAndMakeVisible for your aggregated TextEditor and set the bounds in resized().

0 Likes

#3

That’s great thanks, the editor is displaying now. It is reflecting external changes to the referenced value also.
I’m trying to get the edited contents to change the value elsewhere.
How to reference the Value in the Lambda?

class TextListener : public TextEditor, public Value::Listener
{
public:
	TextListener(Value &value,String s) {
		value.addListener(this);
		myValue.referTo(value);		//not working
		setText(s,false);

		onTextChange = [this] {
			String s = getText();
			myValue.setValue(s);   //not working
		};
	}
	void Value::Listener::valueChanged(Value &val)
	{
		String s = val.getValue();
		setText(s, false);
	};
	
private:
	TextEditor text;
	Value myValue;				//not working
}; 
0 Likes

#4

Even if it might not be the source of your current problem, when reading your code I get the feeling that you are missing some basic C++ know how.

So you are inheriting from TextEditor and adding a TextEditor member to your class. What’s your intention to do so, or what’s your understanding of the meaning of public TextEditor?

When inheriting from TextEditor your class actually IS A TextEditor. Adding another TextEditor member also means that it has another additional TextEditor. So in fact, there are two independent editors, however, you never use the member editor in your class. So you could delete the member variable.

Furthermore, you inherit from Value::Listener and override one of its virtual member functions. Usual syntax to do so would be

void valueChanged (Value& val) override
	{
		String s = val.getValue();
		setText(s, false);
	};

e.g. you don’t need the Value::Listener bit before the valueChanged function because by inheriting from Value::Listener, valueChanged becomes a member function of your class which you override from the base class (and therefore mark it with override).

Now you didn’t really tell what’s not working with your code, however, here is a PIP that demonstrates a version that works:

/*******************************************************************************
 The block below describes the properties of this PIP. A PIP is a short snippet
 of code that can be read by the Projucer and used to generate a JUCE project.

 BEGIN_JUCE_PIP_METADATA

  name:             TextListener

  dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics, juce_gui_basics
  exporters:        xcode_mac

  moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1

  type:             Component
  mainClass:        MyComponent

 END_JUCE_PIP_METADATA

*******************************************************************************/

#pragma once

class TextListener : public TextEditor, public Value::Listener
{
public:
    TextListener (Value& value, const String& initialText) : v (value)
    {
        v.addListener(this);
        setText (initialText, false);

        onTextChange = [this] () { v.setValue (getText()); };
    }

    ~TextListener() {v.removeListener (this); }

    void valueChanged (Value& val) override
    {
        String s = val.getValue();
        setText (s, false);
    };

private:
    Value& v;
};

struct PrintListener : public Value::Listener
{
    void valueChanged(Value& val) override {std::cout << val.toString() << std::endl; }
};


//==============================================================================
class MyComponent   : public Component
{
public:
    //==============================================================================
    MyComponent() : value ("Values initial text"), textListener (value, "Editors initial Text")
    {
        value.addListener (&printListener);
        addAndMakeVisible (textListener);
        setSize (200, 50);
    }

    ~MyComponent() {value.removeListener (&printListener); }

    //==============================================================================
    void paint (Graphics& g) override
    {
        g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
    }

    void resized() override
    {
        auto bounds = getLocalBounds();
        textListener.setBounds (bounds);
    }


private:
    Value value;
    TextListener textListener;
    PrintListener printListener;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MyComponent)
};

0 Likes

#5

Thanks for the post, and clearing up those aspects of c++!
The “//not working” lines in my code were referring to the problem regarding the Value passed in, when changing it via the editor, was not changing elsewhere that the Value appears.
The key difference that makes it work now is the declaration

	Value& myValue;	//works as expected

instead of

	Value myValue;	//change does not propagate elsewhere

I think it is clear that the first one refers to the same instance of the Value, the second to a copy.

0 Likes

#6

Those are two different setups. @duggle started using the Value::referTo approach, vs. @PluginPenguin uses a reference to the Value object in MyComponent. Both versions should work.

The advantages of the referTo method:

  • it can be changed during lifetime
  • it will not crash, if the object it refers to was destroyed while a reference will turn dangling

The advantage of using a normal reference:

  • easier to setup and to maintain
0 Likes

#7

I’ve tested both methods for my own learning sake.
In both cases the Value being passed is referTo() a property in a ValueTree.
If I use referTo() in my class, I have to execute the constructor of my class after the Value has been referTo() the ValueTree. This makes sense and results in well defined start up behaviour.

0 Likes