Default SansSerif Typeface of LookAndFeel does not work(in non-defaulted LnF)

In my app, there are custom LookAndFeel members and I apply them during initialization.
I want to apply the same font to the all Label objects (and I don’t want to write vast amount of label.setFont()s).
I thought I can do it by LookAndFeel::setDefaultSansSerifTypefaceName(), but it does not work for the non-defaulted LookAndFeel(and strange thing is, it works defaulted LookAndFeel).

I’m using JUCE 6.1.0 and XCode 12.4.
I tested on iOS and Mac project, made by Audio App from Projucer.
If I remember properly, in previous version(6.0.0 or lower?), setDefaultSansSerifTypefaceName() apply custom(I specified) font to the all applied components.

Here’s my test code that represents my intention clearer than my bad English :stuck_out_tongue:
Thanks.

/** Enable/Disable this macro and build. 
    You will find the characters at the center of the component
    is different, which means setDefaultSansSerifTypefaceName()
    does not do the same thing whether it's 
    defaulted LnF or custom LnF.
*/
#define USE_MEMBER_LnF 1

class MainComponent  : public juce::AudioAppComponent
{
public:
    MainComponent()
    {
#if USE_MEMBER_LnF
        LnF.setDefaultSansSerifTypefaceName("Hiragino Sans");
        getTopLevelComponent()->setLookAndFeel(&LnF);
        setLookAndFeel(&LnF);
#else
        getLookAndFeel().setDefaultSansSerifTypefaceName("Hiragino Sans");
#endif
        addAndMakeVisible(label);
        /** This string represents Japanese Characters "アイウエオ". 
            Mac's defaulted font (maybe Helvetica?) can't draw these characters 
            and Hiragino Sans can properly.
        */
        label.setText(juce::CharPointer_UTF8 ("\xe3\x82\xa2\xe3\x82\xa4\xe3\x82\xa6\xe3\x82\xa8\xe3\x82\xaa"), juce::dontSendNotification);

    }

    resized()
    {
        deviceName.setBounds(getLocalBounds().withSizeKeepingCentre(100, 40));
    }

    //...

private
    juce::Label label;
    juce::LookAndFeel_V4 LnF;

    //...
}

#define USE_MEMBER_LnF 1

#define USE_MEMBER_LnF 0

I checked with 5.0.0 and 6.0.0 and saw the same behaviour as with 6.1.2, so I don’t think this is a recent regression. If I trace execution through the font loading process when drawing the label for the first time, I see that Font::getTypefacePtr() ends up calling LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font), where font is the ‘placeholder’ sans serif font.

The best option is probably to create a custom LookAndFeel and to override getLabelFont:

struct MyLnF : juce::LookAndFeel_V4 {
  juce::Font getLabelFont(juce::Label &) override {
    return {"Hiragino Sans", "Regular", 15.0f};
  }
};

struct MainComponent : juce::Component {
  MainComponent() {
    setLookAndFeel(&lnf);

    addAndMakeVisible(label);
    label.setText(
        juce::CharPointer_UTF8(
            "\xe3\x82\xa2\xe3\x82\xa4\xe3\x82\xa6\xe3\x82\xa8\xe3\x82\xaa"),
        juce::dontSendNotification);

    setSize(400, 400);
  }

  ~MainComponent() override { setLookAndFeel(nullptr); }

  void resized() override {
    label.setBounds(getLocalBounds().withSizeKeepingCentre(100, 40));
  }

private:
  MyLnF lnf;
  juce::Label label;
};

Thanks!
That’ll solve my problem!

Can I ask other questions?

What setDefaultSansSerifTypefaceName() for?
When should I use it?

Why only default LnF’s default sans serif affects Label?
It seems like non-default LnF’s sans serif typeface does not affects Labels and no Components get a callback when it has changed (while default LnF does).
It’s a little weird, isn’t it?

I found there’re still garbled characters in my app, because it uses AudioDeviceSelectorComponent that uses juce::Graphics::drawText() in paintListBoxItem() callback.
I have an iPhone localized in Japanese.
In the input device list, it tried to draw “iPhone マイク”(マイク is Japanese characters that means microphone), but because juce::Graphics that passed in has typeface of Helvetica, it can’t draw texts correctly.

Though I can call getLookAndFeel().setDefaultSansSerifTypefaceName("Hiragino Sans"); at somewhere in my MainComponent, which can solve garbled text problem, but why the juce::Graphics object doesn’t have Hiragino Sans typeface?
I’m sure I attached my custom LnF whose defaultSanSerif is set Hiragino Sans to the AudioDeviceSelectorComponent.

It looks to me like setDefaultSansSerifTypefaceName() is designed to set the ‘default’ font for controls in your app, but it will normally only have an effect when called on the default look and feel. If you wanted to change the default font for all controls in your app, you would do something like LookAndFeel::getDefaultLookAndFeel().setDefaultSansSerifTypefaceName ("Futura");.

The AudioDeviceSelectorComponent draws items using AttributedStrings, passing a Font constructed with a height, but no typeface name:

attributedString.setFont ((float) textBounds.getHeight() * 0.6f);

When a font is constructed in this way, its typeface name is a placeholder, <Sans-Serif>. The actual typeface is loaded by calling:

LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font)

This call will check whether the typeface name is equal to the placeholder sans-serif name, and load the specified default font in that case.

Note that the font object itself doesn’t keep a record of the Component that created it (it might not be used with a Component at all), so there’s no way of querying a specific Component’s LookAndFeel at the point where the typeface is loaded.

To make the AudioDeviceSelectorComponent paint in the way you’re expecting, we would have to modify the paint implementation to select the font like so:

const auto name = owner.getLookAndFeel().getDefaultSansSerifTypefaceName();
attributedString.setFont ({ name, "Regular", (float) textBounds.getHeight() * 0.6f });

However, this would be a breaking change, and would be inconsistent with other JUCE components, so I think we’re unlikely to make this change.

1 Like

in any case you probably want to use TextLayout instead of having specific font that support non latin character and reimplement drawLabel to use it so fallback font is automatic.

1 Like

Thanks. It’s clear now!

So, the default LnF is special.
No other LnF can determine ‘default’ font.
If I want to change ‘default’ font, I should get default LnF and set some typeface.
TBH, I feel that’s a little weird behaviour, because non-static method does not affect its correspondance(when it is not a defaulted). But if so, there is no choice but to do so.
I suggest how about adding comment to setDefaultSansSerifTypefaceName()?

Anyway, I will go back to fix my codes!
Thanks again.