Reputation: 29
I have made a single contour .svg cursor (basically a triangle curved at the bottom) without any fill, and I want to be able to change its fill color. I can't just make few different svgs for this, due to fill color being picked by user.
QtGraphicalEffects library is not supported since Qt6, so all I could find on this topic was of little use. The solution for this might be to just drop my svg over a colored rectangle, but then I'll have to mask out the area outside of my cursor to make it transparent(or vice-versa), and I'm not sure if it's even possible. I also found some shader-based solutions but none of then seem to work for my case (might just be me being new to QML) SVG:
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #414141;
stroke-width: 44px;
fill-rule: evenodd;
}
</style>
</defs>
<path id="Pointer_1" data-name="Pointer 1" class="cls-1" d="M300.291,48.248L556.347,560.4s-127.234-47.825-255.133-47.825c-128.157,0-256.979,47.825-256.979,47.825Z"/>
</svg>
Upvotes: 0
Views: 617
Reputation: 25946
[MULTIPLE EDITS]
Firstly, the latest builds of Qt6 do support QtGraphicalEffects via Qt5Compat.GraphicalEffects. https://doc.qt.io/qt-6/qtgraphicaleffects5-
The approach I prefer to use is to leverage the fact that most components have an icon
property which has a built-in mechanism for recoloring an SVG. You set the following properties:
By creating two SVGs we can use the above approach to control the fill and stroke independently.
The typical component I use is Button
, but, I strip off the default Button
UI/UX and just leverage from the icon
property as follows:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
SplitView {
anchors.fill: parent
orientation: Qt.Horizontal
Item {
SplitView.preferredWidth: parent.width / 2
SplitView.fillHeight: true
Item {
anchors.centerIn: parent
width: parent.width * 8 / 10
height: parent.height * 8 / 10
Button {
anchors.centerIn: parent
visible: fillCombo.currentText !== 'none'
background: Item { }
icon.source: "shape-fill.svg"
icon.width: Math.min(parent.width, parent.height)
icon.height: icon.width
icon.color: fillCombo.currentText
}
Button {
anchors.centerIn: parent
visible: strokeCombo.currentText !== 'none'
background: Item { }
icon.source: "shape-outline.svg"
icon.width: Math.min(parent.width, parent.height)
icon.height: icon.width
icon.color: strokeCombo.currentText
}
}
}
Item {
SplitView.fillWidth: true
SplitView.fillHeight: true
ColumnLayout {
anchors.centerIn: parent
Label {
text: qsTr("Stroke")
}
ComboBox {
id: strokeCombo
model: [ "lightsteelblue", "red", "orange", "yellow", "green", "blue" ]
}
Label {
Layout.topMargin: 20
text: qsTr("Fill")
}
ComboBox {
id: fillCombo
model: [ "none", "red", "orange", "yellow", "green", "blue" ]
}
}
}
}
}
// shape-outline.svg
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600">
<path stroke="red" stroke-width="44" fill="none" d="M300.291,48.248L556.347,560.4s-127.234-47.825-255.133-47.825c-128.157,0-256.979,47.825-256.979,47.825Z"/>
</svg>
// shape-fill.svg
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600">
<path stroke="none" fill="black" d="M300.291,48.248L556.347,560.4s-127.234-47.825-255.133-47.825c-128.157,0-256.979,47.825-256.979,47.825Z"/>
</svg>
You can Try it Online!
Alternatively, another approach is you can build your SVG as a data uri by adding a "data:image/svg+xml,"
prefix to your SVG string. Because you're using a data uri, you can make a function that generates the SVG based on use input:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
SplitView {
anchors.fill: parent
orientation: Qt.Horizontal
Item {
SplitView.preferredWidth: parent.width / 2
SplitView.fillHeight: true
Image {
anchors.centerIn: parent
width: parent.width * 8 / 10
height: parent.height * 8 / 10
source: todatauri(getsvg(strokeCombo.currentText, fillCombo.currentText))
fillMode: Image.PreserveAspectFit
}
}
Item {
SplitView.fillWidth: true
SplitView.fillHeight: true
ColumnLayout {
anchors.centerIn: parent
Label {
text: qsTr("Stroke")
}
ComboBox {
id: strokeCombo
model: [ "black", "red", "orange", "yellow", "green", "blue" ]
}
Label {
Layout.topMargin: 20
text: qsTr("Fill")
}
ComboBox {
id: fillCombo
model: [ "none", "red", "orange", "yellow", "green", "blue" ]
}
}
}
}
function todatauri(svg) {
return "data:image/svg+xml," + svg;
}
function getsvg(stroke, fill) {
return `<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #414141;
stroke-width: 44px;
fill-rule: evenodd;
}
</style>
</defs>
<path id="Pointer_1" data-name="Pointer 1" class="cls-1" stroke="${stroke}" stroke-width="44" fill="${fill}" d="M300.291,48.248L556.347,560.4s-127.234-47.825-255.133-47.825c-128.157,0-256.979,47.825-256.979,47.825Z"/>
</svg>
`;
}
}
You can Try it Online!
Upvotes: 1