LookAndFeel Basics question


#1

Hello there.

I’ve been trying to get a basic version of LookAndFeel working but no no avail. I have a simple class inherited from LookAndFeel:

#ifndef ZENITLOOKANDFEEL_H_INCLUDED
#define ZENITLOOKANDFEEL_H_INCLUDED
#include “…/JuceLibraryCode/JuceHeader.h”

class ZenitLookAndFeel : public LookAndFeel_V3
{
Font getTextButtonFont (TextButton&, int) override
{
Font smallFont;
smallFont.setHeight (10);
return smallFont;
};
};

#endif

…and in a separate component class a member of type ZenitLookAndFeel called zlaf. In the body I have the code:

saveAsEnvelopeButton.setLookAndFeel (zlaf);

…however I get the error:

No viable conversion from ‘ZenitLookAndFeel’ to ‘juce::LookAndFeel *’

I inherited from LookAndFeel, and it all seems like it is in the examples. What’s going wrong?

Thank you.


#2

Apologies, I really have no idea why the font size is so different there.


#3

It needs to be a pointer to your look-and-feel class. Try:
saveAsEnvelopeButton.setLookAndFeel (&zlaf);


#4

It’s because ‘#’ in markdown means a heading. Would be helpful to re-edit the original post so it’s more legible (you can see a preview of the rendered markdown on the right hand side as you edit your post).

Here’s a markdown cheat sheet


#5

Thank you Martin, although there same problem occurs.


#6

I think it might be because your ZenitLookAndFeel has no constructor. Also, there’s a syntax error - you have an extra semicolon after your implementation of ZenitLookAndFeel::getTextButtonFont

Try:

class ZenitLookAndFeel : public LookAndFeel_V3
{
    ZenitLookAndFeel() {}
    Font getTextButtonFont (TextButton&, int) override
    {
        return Font (10.0f);
    }
};

and

saveAsEnvelopeButton.setLookAndFeel (&zlaf);

#7

Its because the posts respond markdown and the # character means heading.
If you wrap the code in triple backticks (`) it should sort it out.


#8

Thank you for the advice guys, I did already have a basic constructor, I just removed it for brevity. However the problem is still there. With your solution, Willrice, the error message alters slightly to:

Cannot initialize a parameter of type ‘juce::LookAndFeel *’ with an rvalue of type ‘ZenitLookAndFeel *’


#9

@swdpowell Without seeing your code in full, it’s tricky to figure out what’s going wrong. Perhaps you could get your project on GitHub so we can be more helpful?

I knocked up a rough and ready LookAndFeel example using code similar to yours.


#10

There was a missing public: within the body of the class.
class ZenitLookAndFeel : public LookAndFeel_V3 { public: Font getTextButtonFont (TextButton&, int) override { return Font (10.0f); } };


#11

@martinrobinson: Thank you, although that doesn’t help with the issue of the error at hand.

@willrice: I don’t have my code on GitHub as it’s a private project. Your example is exactly what I’m going for, and is set out in pretty much exactly the same way, I wonder why it doesn’t work… I shall have a close inspection later and get back to you, thank you very much for your help.


#12

…can you post, how the error message reads now, after you employed martinrobinsons changes i.e. supplying a pointer instead of an object?
And another question, do you use your ZenitLookAndFeel class as object or as pointer? Was it constructed properly (now having public: before declaring the constructor)?
If you use ScopedPointer<ZenitLookAndFeel> zlaf; you need to call new, otherwise it’s constructed implicitly while your component is constructed.


#13

@daniel: The error message reads:

Cannot initialize a parameter of type ‘juce::LookAndFeel *’ with an rvalue of type 'ZenitLookAndFeel *

If ZenitLookAndFeel inherits from LookAndFeel_V3, it should work.

@martinrobinson: I have now put it all in exactly as in your example, which should work! It’s very odd as it shouldn’t be complicated, all that’s being altered is the names of objects and classes!


#14

I’m out of suggestions based in this info. I posted an example recently making a look-and-feel for a basic knob:

This uses the same approach outlined above. Is there any more detail on the error as to why it can convert the rvalue?


#15

The dropdown in Xcode only reveals:

Passing argument to parameter ‘newLookAndFeel’ here

It’s like the inheritance just isn’t working at all and so it doesn’t recognise the object as a LookAndFeel at all. Thank you for all your help, I’m sure a reason / solution will pop up along the way and make the head explode with blinding obviousness!


#16

I think, the key is the word “rvalue”. Could it be, that you define the object inside the setLookAndFeel method, like:
saveAsEnvelopeButton.setLookAndFeel (&ZenitLookAndFeel()); ?

That would explain the error message:

An rvalue is a temporary value that does not persist beyond the expression that uses it
(found via google: https://msdn.microsoft.com/en-us/library/f90831hc.aspx , applies to general C++).

You will have to instanciate your lookandfeel class before you assign it to the component, like:

ZenitLookAndFeel zlaf();
saveAsEnvelopeButton.setLookAndFeel (&zlaf);

This will compile, but doesn’t work either, because the zlaf instance will go out of scope at the end of the constructor / the method where you implement this setting.

Instead you will have to create a member variable which you instanciate in the constructor. A very handy way to do is the ScopedPointer. You add in your component as private variable a scoped pointer like this:

private:
    ScopedPointer<ZenitLookAndFeel> zlaf;

and in the constructor:

zlaf = new ZenitLookAndFeel();
saveAsEnvelopeButton.setLookAndFeel (zlaf);

If your saveAsEnvelopeButton is a child of your component (i.e. you are calling addAndMakeVisible), then you can also set the zlaf as default look and feel, so all children will use that common look and feel. Important is only, that you set the default look and feel before you call addAndMakeVisible.

The nice thing about ScopedPointer is, that as soon as the component is destructed, the zlaf instance will get deleted as well, so you are not leaking memory.

HTH, daniel


#17

@daniel. Thank you very much for your long explanatory post. I understand most of the concepts here which is very helpful. However, the types still do not seem to be working:

No viable conversion from ‘ScopedPointer’ to ‘juce::LookAndFeel *’


#18

Ok, I was not sure if they overload the operator, but now I know, you need to call get() on the scopedPointer to get the raw pointer, like:

saveAsEnvelopeButton.setLookAndFeel (zlaf.get());

EDIT: I was just looking into my code, where I used it, and I don’t need to call get there…
You describe it, as if inheritance don’t work, so please check the visibility, do you inherit public? In the code you posted, you do, but maybe you were trying stuff, so make sure you write:

And don’t omit the public there.
Also check if you override public methods as public, otherwise these methods are not visible from outside…

EDIT 2: you can verify the inheritance:

jassert (dynamic_cast<LookAndFeel*> (zlaf.get()));

this should stop in debugger, if you have problems with the inheritance…


#19

@daniel: Everything that is public should be.

The jassert comes back with:

‘ZenitLookAndFeel’ is not polymorphic

…which may be a clue! I have no idea why…


#20

Ok, my last resort, please copy the class name ZenitLookAndFeel into the project-wide search via CMD+Shift+F and see, if you accidently define a type/class/struct with the same name anywhere else, which doesn’t inherit from LookAndFeel or one of it’s heirs…?