larsmoa
larsmoa

Reputation: 12942

Specifying font for many Text-elements in Qt QML

I have a widget specified through a QML file. This widget contains a top levelRectangle which contains two Columns. Each of these Columns contains many Text-elements. This QML widget is wrapped in a subclass of QDeclarativeView in C++.

I want to specify the font for each of these Text-elements. Today I do this by specifying top-level properties:

property string fontfamily: "Arial"
property bool fontbold: false
property bool fontitalic: false
property int fontpixelsize: 11
property string fontcolor: "White"

and bind each Text-elements to these properties:

Text
{   
    color: fontcolor
    font.family: fontfamily
    font.bold: fontbold
    font.italic: fontitalic
    font.pixelSize: fontpixelsize
    ...
}

This isn't very elegant and new fields needs to be added every time I need support for something new (e.g. underlined fonts). I have not been able to declare a property of type font and bind to this instead (widget is blank and qmlviewer warns about "expected type after property").

Is there a better way to specify a font for all Text-elements?

Note! I'm handwriting the QML files.

Upvotes: 12

Views: 15609

Answers (5)

Moia
Moia

Reputation: 2364

Necro posting, but I feel it's still missing an up-to-date solution. FontMetrics will do the trick without using Qt.font(). You can declare it in your parent item or in a Singleton type, and then you can bind the property to it.

Here there's an example

Item {
  id: root
    
  FontMetrics {
    id: fontMetrics
    font.family: "Arial"
    font.pixelSize: 24
  }
    
  property alias font: fontMetrics.font
    
  Text { font: root.font }
  Text { font: root.font }
}

Upvotes: 2

David K. Hess
David K. Hess

Reputation: 17246

In Qt 5.6 (at least, probably earlier too), you can use Qt.font() to dynamically allocate a font object and refer to it elsewhere. So, this works:

property font myFont: Qt.font({
    family: fontfamily,
    bold: fontbold,
    italic: fontitalic,
    pixelSize: fontpixelsize
});

Text
{   
    color: fontcolor
    font: parent.myFont
}

More info on Qt.font() here: https://doc.qt.io/qt-5/qml-qtqml-qt.html#font-method

Upvotes: 18

Heath Raftery
Heath Raftery

Reputation: 4169

Some useful workarounds here, but I'm stuck being able to define some base fonts while still being able to specify details later, for more than a few fonts. In particular, because:

  1. FontLoader sets the same name for every font of the same family. Ref
  2. If Qt.font() (or FontMetrics) is used, it's all or nothing for the font property.
  3. Defining new Text Components requires one file per font. Update: maybe not true since 5.15.
  4. Triggering a function is difficult to manage effectively across a whole application.

I think there's room for one more solution:

In my main.qml I used one FontLoader per font file, but only bothered setting an id when the family changes:

FontLoader { source: "qrc:/fonts/ITCAvantGardeStd-Bk.otf";     id: flAvantGarde;   }
FontLoader { source: "qrc:/fonts/ITCAvantGardeStd-BkObl.otf";                      }
FontLoader { source: "qrc:/fonts/ITCAvantGardeStd-BoldCn.otf";                     }
FontLoader { source: "qrc:/fonts/ITCAvantGardeStd-Md.otf";                         }
FontLoader { source: "qrc:/fonts/ADAM.CG PRO.otf";             id: flAdam;         }

and then, and here's the significant part, defined one property var per base font like so:

property var fontAgBk:      { "family": flAvantGarde.name, "styleName": "Book" }
property var fontAgBkObl:   { "family": flAvantGarde.name, "styleName": "Book Oblique" }
property var fontAgBoldCn:  { "family": flAvantGarde.name, "styleName": "Bold Condensed" }
property var fontAgMd:      { "family": flAvantGarde.name, "styleName": "Medium" }
property var fontAdam:      { "family": flAdam.name }

The var is key being able to specify a dictionary that can be defined once, but pulled apart later.

Elsewhere in any qml, I can do something like:

Text {
    id: myText
    color: "#123456"
    font.family: fontBoldCn.family
    font.styleName: fontBoldCn.styleName
    font.pixelSize: 24
    font.letterSpacing: 5
}

Still requires some repetitive code, but at least the magic strings are only defined once.

Upvotes: 1

hiddenbit
hiddenbit

Reputation: 2243

One possible solution is to write a function, that iterates over the children of a passed element (for example a Column). In this function all the properties can be set:

import QtQuick 1.0

    Rectangle {
    Row {
        spacing: 10

        Column {
            id: col1

            Text {
                property bool useStyle: true
                text: "Foo1"
            }
            Text {
                property bool useStyle: true
                text: "Bar1"
            }
            Text {
                property bool useStyle: true
                text: "Baz1"
            }
        }

        Column {
            id: col2

            Text {
                property bool useStyle: true
                text: "Foo2"
            }
            Text {
                text: "not styled"
            }
            Text {
                property bool useStyle: true
                text: "Baz2"
            }
        }
    }

    function setTextStyle(parentElement) {
        for (var i = 0; i < parentElement.children.length; ++i) {
            console.log("t", typeof parentElement.children[i]);
            if (parentElement.children[i].useStyle) {  // apply style?
                parentElement.children[i].color = "blue";
                parentElement.children[i].font.family = "Arial"
                parentElement.children[i].font.bold = true;
                parentElement.children[i].font.italic = true;
                parentElement.children[i].font.pixelSize = 12;
            }
        }
    }

    // set style
    Component.onCompleted: {
        setTextStyle(col1);
        setTextStyle(col2);
    }
}

Each element, that contains the property useStyle that is set to true, gets styled. This is shorter, than assigning the style manually, but you can still define which elements should get styled or not.

Upvotes: 5

hiddenbit
hiddenbit

Reputation: 2243

Another possibility is to write a new QML component, that inherits from Text an sets some properties by default:

StyledText.qml

import QtQuick 1.0

Text {
    // set default values
    color: "blue"
    font.family: "Arial"
    font.bold: true
    font.italic: true
    font.pixelSize: 12
}

main.qml

import QtQuick 1.0

Rectangle {
    Row {
        spacing: 10

        Column {
            StyledText {
                text: "Foo1"
            }
            StyledText {
                text: "Bar1"
            }
            StyledText {
                text: "Baz1"
            }
        }

        Column {
            StyledText {
                text: "Foo2"
            }
            StyledText {
                text: "Bar2"
            }
            StyledText {
                text: "Baz2"
            }
        }
    }
}

Upvotes: 14

Related Questions