Transparent button text over the background - qt

I am trying to make transparent text in qml.
I have a customized button:
ApplicationWindow {
visible: true
width: 320
height:240
style: ApplicationWindowStyle {
background: Image { // paste any url here
source: "https://t4.ftcdn.net/jpg/01/05/18/57/240_F_105185755_w384KDBvemK5Mn4oXzrWbCz9SRvHuDIh.jpg"
}
}
Button
{
anchors.centerIn: parent
style: ButtonStyle
{
padding
{
left: 16
right: 16
top: 8
bottom: 8
}
background:
Rectangle
{
antialiasing: true
color: control.pressed ? "#d1d1d1" : control.hovered ? "#666" : "transparent"
border.color: "white"
radius: height/2
border.width: 1
}
label:
Text
{
text: "buttonText"
color: control.pressed ? "white" : control.hovered ? "#00000000" : "white"
}
}
}
}
All I want is to have transparent text in button in hovered state. Text should have the color of background. Example:
upd. I need this to work without shaders on slow pc.

One option would be to use a custom QQuickPaintedItem and use a QPainterPath to draw the "text shaped hole".
Basically like this
void MyItem::paint(QPainter *painter)
{
QPainterPath path;
path.addRect(0, 0, width(), height());
path.addText(textX, textY, font, yourText);
painter->setBrush(yourBackgroundColor);
painter->setPen(Qt::transparent);
painter->drawPath(path);
}
The position, i.e. textX and textY, would have to be calculated manually I am afraid, though QFontMetrics::boundingRect() should help there.

Related

How to handle press state from inside the button if contentItem is defined from the outside?

I have a custom button that shows a white overlay over the content and background area when pressed. The user of the button can specify how to align the content (label + text) by defining the contentItem.
The current implementation works, but I would like to control the press state inside the button. How can the button display the white overlay on top of the contentItem from within the button?
I prefer not to add a bunch of properties to the button to define the layout of the text/icon. Is there another way to accomplish this?
Working code so far:
ButtonOverlay {
id: btnOverlay
width: 100
height: 50
state: ""
contentItem: Rectangle {
color: "red"
Label {
text: "button"
}
Rectangle {
id: rectId
anchors.fill: parent
color: "white"
opacity: 0.5
visible: btnOverlay.state === "pressed"
}
}
onPressed: {
btnOverlay.state = "pressed"
}
onReleased: {
btnOverlay.state = ""
}
}
ButtonOverlay.qml
Button {
id: root
background: Rectangle {
radius: 10
color: "gray"
Rectangle {
id: overlay
anchors.fill: parent
radius: parent.radius
visible: root.state === "pressed"
color: "white"
opacity: 0.5
}
}
}
I've removed the overlay in the contentItem and moved the overlay from the background into the Button. Furthermore I've used the z property to change the drawing order.
component ButtonOverlay: Button {
id: btn
background: Rectangle {
radius: 10
color: "gray"
}
Rectangle {
id: overlay
anchors.fill: parent
radius: btn.background.radius
color: "white"
opacity: 0.5
visible: btn.pressed
z: 1
}
}
ButtonOverlay {
width: 100
height: 50
contentItem: Rectangle {
color: "red"
Label { text: "button" }
}
}
Perhaps you could use Frame for your ButtonOverlay. Frame works by automatically sizing to your inner contents but allows you to easily replace the borders via the background property. To use it, you will have to move the entire Button out:
import QtQuick
import QtQuick.Controls
Page {
ButtonOverlay {
id: btnOverlay
pressed: btn.pressed
Button {
id: btn
width: 100
height: 50
implicitWidth: width
implicitHeight: height
contentItem: Rectangle {
color: "red"
Label {
text: "button"
}
Rectangle {
id: rectId
anchors.fill: parent
color: "white"
opacity: 0.5
visible: btnOverlay.pressed
}
}
}
}
}
// ButtonOverlay.qml
import QtQuick
import QtQuick.Controls
Frame {
id: root
property bool pressed: false
background: Rectangle {
radius: 10
color: "gray"
Rectangle {
id: overlay
anchors.fill: parent
radius: parent.radius
visible: pressed
color: "white"
opacity: 0.5
}
}
}
You can Try it Online!

QML Rectangle Hide overlapping Objects

I'm trying to achieve a simple Sliding Bar in QML. Here's a prerequisite I have to follow: no SVG images ( for some BR I cannot discuss here ).
Rectangle {
id: backgroundPower
anchors.fill: parent
color: "#a2aaae"
radius: 50
MouseArea {
id: progressArea
anchors.fill: parent
onPositionChanged: {
updateValue(progressArea.mouseX)
}
onReleased: {
updateValue(progressArea.mouseX)
}
onClicked: updateValue(progressArea.mouseX)
}
Rectangle {
id: powerImage
width: calculateWidth()
radius: 100
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
LinearGradient {
id: defaultGradient
anchors.fill: parent
source: powerImage
start: Qt.point(0, 0)
end: Qt.point(0, powerImage.height)
gradient: Gradient {
GradientStop { position: 0.0; color: "#46c0e4" }
GradientStop { position: 0.50; color: "#0583ca" }
GradientStop { position: 0.95; color: "#fbffff" }
GradientStop { position: 1.0; color: "#fbffff" }
}
}
Here's what I obtain with this code when the bar is at 100%:
When I update the value ( mouse area events ) I change the width of the
powerImage with the function:
var maxWidth = powerImage.parent.width
var currentWidth = (maxWidth * barPercentage) / 100
if (currentWidth >= maxWidth){
currentWidth = maxWidth
}
PROBLEM
When the percentage of the bar reaches a low value ( like 10% ) I cannot fit the bar into the background, like this:
you can see how the bar doesn't fin anymore.
I know the problem is linked to the rectangle ( powerImage ) and its radius. But I don't know how to fix this.
Clearly with a SVG it's really simple but in this case, I cannot use this.
Is there a way to show the bar when it's only inside the background rectangle?
Unfortunately it is not possible to use clip: true on non-rectangular regions.
But following this answer, you can use an OpacityMask:
Rectangle {
id: backgroundPower
anchors.fill: parent
color: "#a2aaae"
radius: 50
MouseArea {
// ...
}
}
Item {
anchors.fill: backgroundPower
layer.enabled: true
layer.effect: OpacityMask {
maskSource: backgroundPower
}
Rectangle {
id: powerImage
// ...
}
}
Result:
But as mentionned in the answer:
But using it will be GPU consuming task as the inner item and mask have to be drawn on buffer first and then redrawn on window, so not very good for old mobile or weak embedded devices.

Qml slider style, assigning different groove color before and after the slider's handle

So I wanted to create a slider style, similar to the default one, and also more matching to my applications style, but I found it hard to assign a different color before and after the slider's handle.
Here's a simplified version of my code, without gradients, anchor points and other properties:
Slider{
id: widthSlider
style: SliderStyle {
groove: Rectangle {
height: widthSlider.height*0.17
width: widthSlider.width
color: "black"
radius: 8
}
handle: Rectangle {
color: "grey"
}
}
}
I tried a rough workaround, to put two rectangles in the slider, anchored to the handle position like this:
Slider{
id: widthSlider
Rectangle {
anchors.left: widthSlider.left
anchors.right: widthSlider.__handlePos
color: "black"
}
Rectangle {
anchors.left: widthSlider.__handlePos
anchors.right: widthSlider.right
color: "white"
}
...
}
but I cannot anchor to the handle's position since it's just a double(I get the error: Unable to assign double to QQuickAnchorLine).
Does anyone have an idea of how I could do this in Qml?
Something like this?
Slider {
anchors.centerIn: parent
value: 0.5
width: 400
style: SliderStyle {
groove: Item {
implicitHeight: 10
LinearGradient {
anchors.fill: parent
start: Qt.point(0, control.height / 2)
end: Qt.point(control.width, control.height / 2)
gradient: Gradient {
GradientStop { position: 0.0; color: "orange" }
GradientStop { position: control.value; color: "brown" }
GradientStop { position: 1.0; color: "orange" }
}
}
}
}
}
Or:
Slider {
anchors.centerIn: parent
value: 0.5
width: 400
style: SliderStyle {
groove: Rectangle {
implicitHeight: 10
color: "lightgrey"
border {
color: "#999"
width: 1
}
Rectangle {
implicitHeight: 10
color: "orange"
implicitWidth: control.value * parent.width
border {
color: "#999"
width: 1
}
}
}
}
}
I realize this is an old question. However for future reference the Rectangle that represents the colour before the handle should have a width of styleData.handlePosition. The below code is from folibis second solution with this change.
Slider {
anchors.centerIn: parent
value: 0.5
width: 400
style: SliderStyle {
groove: Rectangle {
implicitHeight: 10
color: "lightgrey"
border {
color: "#999"
width: 1
}
Rectangle {
implicitHeight: 10
color: "orange"
implicitWidth: styleData.handlePosition
border {
color: "#999"
width: 1
}
}
}
}
}

Creating a custom QML Button with native look and feel

I would like to create a custom Button by defining my own QML type. This Button type should contain two text fields, one with a single character of a symbol font and the other one the actual button text.
That's simple, but how could I use the native colors, gradients, fonts and borders defined for the target system?
Is it possible to extend Button itself? And how would I disable the possibility to set an image when extending Button?
import QtQuick 2.5
Rectangle {
id:anyButton
property string image:"\ue43f"
property string text:"Button"
border.color : "black"
border.width: 1
radius: 5
Gradient {
id: lightGradient
GradientStop { position: 0.0; color: anyButton.color }
GradientStop { position: 1.0; color: Qt.darker(anyButton.color,1.5) }
}
Gradient {
id: darkGradient
GradientStop { position: 0.0; color: Qt.darker(anyButton.color,1.7) }
GradientStop { position: 1.0; color: Qt.darker(anyButton.color,1.7) }
}
Rectangle{
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Text{
id:buttonImage
}
Text{
id: buttonLabel
font.pixelSize:20
text: anyButton.text
}
}
signal buttonClick()
MouseArea{
id: buttonMouseArea
anchors.fill: parent
onClicked: parent.buttonClick()
hoverEnabled: true
onEntered:{
parent.border.width= 2
}
onCanceled:{
parent.border.width= 1
}
onExited: {
parent.border.width= 1
}
}
gradient: buttonMouseArea.pressed ? darkGradient : lightGradient
}
It's quite simple in QML to create a button with custom style, text etc.
With ButtonStyle you can define custom background and label as you want. To get system colors use SystemPalette. Here you can find its application for real controls.
For example:
Button {
anchors.centerIn: parent
property string firstfield: "a"
property string secondfield: "sometext"
iconSource: "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
text: firstfield + " " + secondfield
style: ButtonStyle {
background: Rectangle {
id: bg
border.width: 1
border.color: palette.mid
radius: 3
gradient: Gradient {
GradientStop { position: 0.0; color: control.pressed ? palette.button : palette.light }
GradientStop { position: 0.5; color: palette.midlight }
GradientStop { position: 1.0; color: control.pressed ? palette.light : palette.button }
}
}
label: RowLayout {
id: row
spacing: 5
Image { source: control.iconSource }
Label {text: control.firstfield; font.family: "Symbol"; font.pixelSize: 18; color: palette.buttonText}
Label {text: control.secondfield; color: palette.buttonText}
}
}
}
SystemPalette { id: palette; colorGroup: SystemPalette.Active }
Sure, you can add a shadow etc. If you will drop the ButtonStyle it should look like a regular button

How to access the buttonStyle object inside of one button using JavaScript in QML 5.2

Below is my Qml code:
Button {
id: newMenu
anchors {
top: topMenu.top
topMargin: 15
left: topMenu.left
leftMargin: 16
}
text: "New"
iconSource: "../images/New.png"
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true //this line will enable mouseArea.containsMouse
onClicked: {
newProjectFileDlg.visible = true
}
onEntered: {
console.log(tt1);
}
}
style: ButtonStyle {
id: buttonStyle
background: Rectangle {
id: tt1
implicitWidth: 100
implicitHeight: 25
border.width: 0
radius: 4
color: mousearea.entered ? "lightsteelblue" : "#2e2e2e"
}
}
I want to access this button's style property, change the background.color when mouse is hover. But the console.log outpu is always
qrc:/qmls/menu.qml:40: ReferenceError: tt1 is not defined
How to get the element using JavaScript? Or do we have other approach to change background color when mouse is entered.
Answering to your question, you should define public property like:
Button {
id: root
property color backgroundColor: pressed ? 'skyblue'
: mousearea.entered ? "lightsteelblue"
: "#2e2e2e"
...
MouseArea { id: mousearea; ... }
style: ButtonStyle {
background: Rectanlge { color: root.backgroundColor; ... }
}
}
and then use is property to override default implementation.
But,
You are trying to use styles in a completely wrong way. Style is a visual representation of Control's state and should't be changed manually in run-time. So, a proper way is to bind control properties to style (e.g. using property control).
style: ButtonStyle {
background: Rectangle {
color: control.hovered ? 'lightsteelblue'
: 'skyblue'
}
}
You can achieve something similar without using styles by nesting a rectangle inside the button and then using the onHoveredChanged property to modify the opacity. An example is below. I did it this way to avoid conflicting with the normal button style's hover effect.
Button {
text: "Push me"
Rectangle{
id: myRectId
anchors.fill: parent
anchors.margins: 1
color: "green"
opacity : .2
}
onHoveredChanged: hovered ? myRectId.opacity = 0 : myRectId.opacity = .2;
}
This ends up looking like this:

Resources