Setting font per arbitrary component


#1

Hi guys,

I know that maybe that's an overly asked question, but I couldn't find an answer. I'm designing a GUI that needs to have different font types and sizes across the various components. As instance, one ComboBox has a font size of 16.. another of 13... a label a different font with size 10 and so on.

I made a custom LookAndFeel, but it only looks like I can change the font for ALL the components.

i.e:

Font getComboBoxFont (ComboBox& box) override
{
  return Font (16);
}

will change ALL the ComboBox to the same size. Even if I would like to catch the box, for now I have no way to get a reference like I do on PluginEditor.

Any advice?

Thanks.


#2

There may very well be a better way but I find a components properties can get you me out of all sorts of issues like this.

So for example in your editor you could do something like this

NamedValueSet& myComboProperties = myCombo.getProperties();
myComboProperties.set ("fontTypefaceName", "Sans-Serif");
myComboProperties.set ("fontSize", 13);
myComboProperties.set ("fontStyleFlag", Font::italic);

NamedValueSet& anotherComboProperties = anotherCombo.getProperties();
anotherComboProperties.set ("fontTypefaceName", "Times New Roman");
anotherComboProperties.set ("fontSize", 10);
anotherComboProperties.set ("fontStyleFlag", Font::bold);

Then in your lookAndFeel you will normally have a reference to the component you are drawing, from which you can call

Font getComboBoxFont (ComboBox& box) 
{     
    const NamedValueSet& properties = box.getProperties();
    String typefaceName = properties.getWithDefault ("fontTypefaceName", "Ariel");
    int fontSize = properties.getWithDefault ("fontSize", 16);
    int styleFlag = properties.getWithDefault ("fontStyleFlag", Font::plain)

    return Font (typefaceName, fontSize, styleFlag);
}

On the other hand if your dealing with a juce::Label then it has a (const Font& method), and by default the LookAndFeel getLabelFont (Label& label) method simply returns label.getFont();

Quite honestly thou getProperties() is a total life saver!


Disclaimer: none of the above code has actually been tested but you should get the general idea.


#3

I think you should try looking at the look and feel system this way: a means of instilling a consistent UI.

Since I'm far from a graphic designer, I decided to ask around because you got me curious.

From a fellow coworker/GD, answering "why is it good to have a consistent set of fonts and sizes in a UI?":

Familiarity, consistency, repeating patterns

If every new section is a new font/size you’re retraining the purpose/importance

People don’t want to see the interface - they want the content

Different fonts/weights/sizes should generally be used for hierarchy

Like on web you really should only have one <h1> because it’s the most important: you wouldn’t make your footer links <h1>.

Now, varying combobox fonts/sizes seems like a bad idea based on that information.

So I guess that answers your question: why you can't be "setting font per arbitrary component" - it's (likely) bad and wrong (badong).


#4

Thank you, Anthony. Your suggestion looks to be a life saver. I'll try it later!

@jrlanglois: I may agree with you, but not quite this time. It's correct to say that consistency is the key and surely I'll not use different typefaces like the in the old glorious geocities websites ;) Let's say I have two combobox in my UI: a big one, in a "toolbar" to recall the presets, and a little one in another section to let the user pick the desired sample to load. It seems natural to me to have the font size that best suits the size of each combobox while, right now, the size is the same but the font is stretched to keep in the bounds of the component... or, another example, I need to put some labels with some stats (CPU %, Voice count and so on) in a little space on the "toolbar". In this case, would be better to have a font that is very readable at small sizes. So, to me, varying the font sizes or even the typeface according to both the usability and aesthetic requirements of the interface doesn't looks so bad to me. My 2 cents!

Thanks again, guys.

 


#5

That's OK glad I can help.

I totally agree with you thou that there are cases when you will need to differ the Look and Feel. The kind of situation that crosses my mind is when you have a graph that may have some options selectable by a combobox / drop down menu internal to the graph. These will quite possibly look quite different from other combobox / drop down menus in the plugin / app.

Which leads me to an alternative solution that crossed my mind earlier. I've never tried before but you could set different look and feels for the different components of the plugin / app, each look and feel could inherit from a common look and feel, each specific one differeing only where they need to. I guess this depends on the extent of the differences, if there are only a few exceptional cases I would use the properties, for more substantial differences where there are clear panels or windows that must differ then individual lookAndFeels might make more sense. I would make sure that I would encapsulate any different areas such as a graph or toolbar into it's own component so you are only setting the look and feel on single components in your editor.


#6

Thanks again, Anthony. Unfortunately the getProperties doesn't work for the ComboBox.. that components lacks a NamedValueSet with the properties. Btw, I found a rather pratical solution by setting a name for each combo ed using that as a reference to set different font sizes via my L+F. 

Font getComboBoxFont (ComboBox& box) override
    {
        //return Font (jmin (15.0f, box.getHeight() * 0.85f));
        if(box.getName() == "presetMenu")
            return Font (16);
        
        if(box.getName() == "sampleMenu")
            return Font (13);
        
        return Font(14);
    }

It's not the most elegant solution, but for now I can avoid to create a new component overloading the original one.

Thanks again!


#7

Unfortunately the getProperties doesn't work for the ComboBox.. that components lacks a NamedValueSet with the properties.

I'm not sure I follow, the getProperties() method is a method in Component which ComboBox inherits from (see here for a complete list of all ComboBox members http://learn.juce.com/doc/classComboBox-members.php)

The following is working for me...

In the editors constructor:

setLookAndFeel (&lookAndFeel); // lookAndFeel is a member of type ComboLookAndFeel

comboBox1.getProperties().set ("fontSize", 10);
comboBox2.getProperties().set ("fontSize", 20);

comboBox1.setText ("Size 10");
comboBox2.setText ("Size 20");

addAndMakeVisible(comboBox1);
addAndMakeVisible(comboBox2);

Then in my ComboLookAndFeel class:

Font getComboBoxFont (ComboBox& box) override
{
    int fontSize = box.getProperties().getWithDefault ("fontSize", 0);
    jassert (fontSize > 0);
    return LookAndFeel_V3::getComboBoxFont (box).withHeight (fontSize);
}

However I found an issue with the popup menu created in the ComboBox for displaying the items. If this needs to change font too (which I strongly suspect you will want) then the method called to determine the font for the popup menu is getPopupMenuFont() which takes no arguments and therefore you cannot access the properties or the components name to change anything to do with the font for each instance. Therefore I think the best option is to create seperated look and feel classes.

So I tried the following...

In the editors constructor:

comboBox1.setLookAndFeel (&combo1LookAndFeel); // Member of type Combo1LookAndFeel
comboBox2.setLookAndFeel (&combo2LookAndFeel); // Member of type Combo2LookAndFeel

comboBox1.setText ("Size 10");
comboBox2.setText ("Size 20");

comboBox1.addItem ("Item", 1);
comboBox2.addItem ("Item", 1);

addAndMakeVisible (comboBox1);
addAndMakeVisible (comboBox2);

Then I had two LookAndFeel classes, in this case each was identical except for the font size

Font getComboBoxFont (ComboBox&) override
{
    return getPopupMenuFont();
}


Font getPopupMenuFont() override
{
    return Font (10); // this is 20 in the Combo2LookAndFeel
}

Creating seperate look and feel classes for each ComboBox is overkill and likely indicates an issue with the GUI design (as jrlanglois pointed out), but having for example a ToolbarLookAndFeel or a GraphLookAndFeel would seem to be a sensible solution in my opinion.