Custom QML Library: Styling practices
-
Question: Is aliasing each sub-element of a component a bad practice? Are there alternatives?
Background: I'm writing a QML library that contains a few complicated components and I don't want users to have to modify the library QML files in order to style their component. I'm restrained in that I must use Qt4.8; I'm targeting an embedded platform that doesn't support Qt5 yet.
I've noticed that the encapsulation mechanism of QML requires aliasing any sub-component you want access to outside of the component's file. For components with lots of sub components, like a keyboard with lots of keys, this is tedious. More so it feels like I'm somehow violating the whole encapsulation mechanism QML provides. Is this violation a concern?
I'm having difficulty seeing if there are any issues other than the tediousness of providing an alias for each sub component. If there are alternatives that would also help. Note that using style forwarding like the keyboard found in "Colibri":https://projects.developer.nokia.com/colibri would still require 26+ individual styles if I wanted key level style granularity.
It would be great if something like the following worked:
@
// CLKeyboard.qml
Column {
id: qwerty
Column {
id: abcCLButtons
....
Row {
....Row { id: row1 .... } Row { id: row2 .... } Row { id: row3 .... }
// main.qml
Rectangle
{
Keyboard{
// change the 'e' key's text color to red
row1[3].text.color="red"
}
}
@ -
Most likely you are not interested in individual styling for each key but a way to define all of them externally. The pattern I would suggest would be to follow the model that ListView has for styling and declare a delegate component to define the button style and re-use that for all your buttons.
Ie along these lines:
@
property Component buttonDelegate: Rectangle { ... }Repeater {
model: 26
delegate: buttonDelegate
}
@ -
That might work but I'm having difficulty understanding how I could modify a single key without affecting the attributes of the others outside of the library. Suppose the user likes the default of all the keys except 'e'. You could allow the user to change the buttonDelegate, something like:
@
// Key.qml
// regular key
Rectangle
{
Text
{
id: keyText
text:
}
...
}// Keyboard.qml
property Component buttonDelegate: KeyRepeater {
model: 26
delegate: buttonDelegate
}//main.qml
Rectangle
{
property Component myDelegate:
Rectangle
{
// on complete check if the text is an e, if so change color
...
}Keyboard { buttonDelegate: myDelegate }
}
@Although I don't currently see how I could allow a user to switch out the default for one key without applying the same delegate across all of them. I could have swapped out the enclosing component for myDelegate from Rectangle to Key, but that would require modifying the Key component to alias the property I'm interested in or provide some other means to modify the object.
I guess my main problem is getting around aliasing in general.
I'll keep thinking on it, maybe I'm missing something.
-
Changing the color of a single button sounds a bit weird but it is perfectly doable with this method. Each created buttondelegate gets access to the information it requires, including say an index or the key string. When using the repeater you can put that information into the model, and if using a Loader you can explicitly forward the information to it by declaring properties in the loader itself:
@
buttonDelegate: Rectangle {
color: key == "E" ? "red" : "blue"
}Repeater {
model: myModel
Loader {
property string key: myModel.get(index).key
sourceComponent: buttonDelegate
}
}
@ -
EDIT: I think I've found a solution to the questions below, it's in the following post, thanks again Jens!
That works if you're in the library. If you're outside the library it's not quite as simple.
@
// library land
//keyboard.qml
Rectangle
{
id: keyboard
// bunch of properties
....
property Component keyboardDelegate:
KeyboardButton
{
text: value;
value: key
shiftValue: key
style: keyboard.style; width: buttonWidth; height: buttonHeight;
onClicked: keyboardCLButtonClicked(text); buttonCLStyle: styleCLButton;
shiftTextFontSize: keyboard.shiftTextFontSize;
shiftTextFontFamily: keyboard.shiftTextFontFamily;
shiftTextFontWeight: keyboard.shiftTextFontWeight;
shiftTextColor: keyboard.shiftTextColor
}Repeater { model: myModel Loader { property string key: myModel.get(index).key sourceComponent: keyboardDelegate } }
}
// user land
//main.qml
Rectangle
{
property Component buttonDelegate:
Rectangle
{
color: key == "E" ? "red" : "blue"
}Keyboard { keyboardDelegate: buttonDelegate }
}
@Based on that the user must go back to the keyboard.qml and copy the rest of the style information if all they wanted was a single key color changed. Now keyboardbutton could have defaulted all these properties in it's implementation but if you have three different keyboard types each with their own style then those styles won't propagate when the user specifies their own delegate.
In the example above you could also specify properties for each of the values you want to set in the loader but again that forces the user to explicitly assign those values even if they want the default.
The reason I'm interested in this particular use case (single key change) follows from the idea that you may have many sub components for an item and perhaps one or two of them differ in the way you want to display them. I'm trying to determine the various ways to access their information from outside of the qml that is part of the library. I personally like how XAML handles element access, was there a reason QML went for more restrictive component encapsulation?
I'm still curious what the community thinks about propagating access to inner components through aliases. Would aliasing all components be a bad thing? While tedious it seems to give straight forward access to everything involved. Then again it could be totally unnecessary, I just started developing in Qt Quick and am probably overlooking something.
-
Actually, an interesting idea is to remove the keyboard's specific implementation of their keyboard delegate and place it into a different file, then the properties will be correctly inherited:
@
// library land
// Bluekey.qml
KeyboardButton
{
text: value;
value: key
shiftValue: key
style: keyboard.style; width: buttonWidth; height: buttonHeight;
onClicked: keyboardCLButtonClicked(text); buttonCLStyle: styleCLButton;
shiftTextFontSize: keyboard.shiftTextFontSize;
shiftTextFontFamily: keyboard.shiftTextFontFamily;
shiftTextFontWeight: keyboard.shiftTextFontWeight;
shiftTextColor: keyboard.shiftTextColor
}
// bluekeyboard.qml
Rectangle
{
id: keyboard
// bunch of properties
....
property Component keyboardDelegate: Bluekey { }Repeater { model: myModel Loader { property string key: myModel.get(index).key sourceComponent: keyboardDelegate } }
}
// user land
//main.qml
Rectangle
{
property Component buttonDelegate:
Bluekey
{
color: key == "E" ? "red" : "blue"
}Keyboard { keyboardDelegate: buttonDelegate }
}
@
Any feedback on this?
Thanks for the suggestions Jens!