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.
Related
How to get the look of curved Scroll bar/scroll view as shown below in QML with Label or TextArea?
Basically this application is not a touch application.
Environment, Qt 5.7.0 in Linux.
You can use PathInterpolator from Controls.2. The example below is some Slider modification, you can adopt it for your needs:
import QtQuick 2.9
import QtQuick.Controls 2.2
ApplicationWindow {
id: mainWindow
visible: true
width: 400
height: 400
Path {
id: myPath
startX: 0; startY: 20
PathCurve { x: 100; y: 40 }
PathCurve { x: 200; y: 10 }
PathCurve { x: 300; y: 40 }
}
Slider {
id: control
width: 300
height: 50
anchors.centerIn: parent
background: Rectangle {
anchors.fill: parent
color: "orange"
Canvas {
anchors.fill: parent
contextType: "2d"
onPaint: {
context.strokeStyle = "MediumPurple";
context.path = myPath;
context.stroke();
}
}
PathInterpolator {
id: motionPath
path: myPath
progress: control.visualPosition
}
}
handle: Rectangle {
width: 30
height: 30
radius: 15
color: "DodgerBlue"
x: motionPath.x - 15
y: motionPath.y - 15
}
}
}
You can use a Flickable to have your view. To this Flickable you attatch a ScrollBar which you can style.
To style this ScrollBar is a bit tricky, for some of its properties are bullshit.
The position-property, which is documented as
This property holds the position of the scroll bar, scaled to 0.0 - 1.0.
will never reach 1.0 unless, the handles size is 0. You don't really have the ability to set the size of the handle, though. It will be automatically resized. So if you don't want to have a handle that fills the width of the ScrollBar entirely, you need to use a Item as a base and add a the visual inside this, so you have the sovereignity again.
All together, it might look like this:
Flickable {
anchors.fill: parent
contentWidth: width
contentHeight: mainWindow.height * 10
Rectangle {
width: 640
height: mainWindow.height * 10
gradient: Gradient {
GradientStop { color: 'orchid'; position: 0 }
GradientStop { color: 'orange'; position: 1 }
}
}
ScrollBar.vertical: ScrollBar {
id: scrollBar
width: 50
contentItem: Item {
// This will deal with the bullshit of the position. Imperfect, as I do not consider any margins/paddings
property real normalizedPosition: scrollBar.position * (scrollBar.height / (scrollBar.height - height))
Rectangle {
// Draw the curve by defining a function for x in dependance of the position.
x: Math.sin(Math.PI * parent.normalizedPosition) * 40
width: 10
height: parent.height // I use the default height, so it
// really goes from top to bottom.
// A smaller height means, you should
// also alter the y value to have a
// more natural behavior.
radius: 5
color: 'green'
Text {
text: parent.parent.normalizedPosition
}
}
}
}
}
I want to make a header-fixed table. The idea I have now is using two ScrollView, one for the header (RowLayout), one for the body (GridLayout). Is there any simple way to link these two on horizontal direction, so one scroll, the other scroll the same?
I don't know how to do it with the low-performing QtQuick.Controls 1.x,
but the QtQuick.Controls 2.0 ScrollBar has a property position.
So here, the trick is, to create two ScrollBars, one for each Item to scroll, and bind the position of each to the position to the other one.
ApplicationWindow {
visible: true
width: 300
height: 120
title: qsTr("Hello World")
Column {
anchors.fill: parent
Flickable {
id: flick1
width: parent.width
height: parent.height / 2
contentHeight: 2 * height
contentWidth: 2 * width
Item {
anchors.fill: parent
Rectangle {
width: parent.height
height: parent.width
anchors.centerIn: parent
rotation: 90
gradient: Gradient {
GradientStop { position: 1; color: 'black' }
GradientStop { position: 0; color: 'white' }
}
}
}
ScrollBar.horizontal: scrl1
}
Flickable {
id: flick2
width: parent.width
height: parent.height / 2
contentHeight: 2 * height
contentWidth: 2 * width
clip: true
Item {
anchors.fill: parent
Rectangle {
width: parent.height
height: parent.width
anchors.centerIn: parent
rotation: 90
gradient: Gradient {
GradientStop { position: 0; color: 'black' }
GradientStop { position: 1; color: 'white' }
}
}
}
ScrollBar.horizontal: scrl2
}
}
ScrollBar {
id: scrl1
orientation: Qt.Horizontal
}
ScrollBar {
id: scrl2
orientation: Qt.Horizontal
}
Binding {
target: scrl2
property: 'position'
value: scrl1.position
}
Binding {
target: scrl1
property: 'position'
value: scrl2.position
}
}
How to attach ScrollBars to almost anything, you can find in the answer to this question. I do not work with Layouts so, I can't be more specific on them.
how to create a scrollbar for rectangle in QML
I am trying to create a custom QML Slider style as follows:
SliderStyle {
groove: Item {
anchors.verticalCenter: parent.verticalCenter
implicitWidth: 500
implicitHeight: 10
Rectangle {
radius: height/2
anchors.fill: parent
border.width: 1
border.color: "#888"
gradient: Gradient {
GradientStop { color: "#0A406E" ; position: 0 }
GradientStop { color: "#FFA800" ; position: 1 }
}
}
}
}
However, the gradient on the groove here is going from top to bottom rather than left to right. I tried swapping the width and height values and rotating the Rectangle by -90 degrees but then the slider was unresponsive. Also, I could never get the rotation to be done along the centre of the slider control, which made placement a bit problematic.
I was wondering if there is a way to achieve this left->right gradient flow.
Rectangle's property gradient allows for the construction of simple vertical gradients. For more complex gradients there are LinearGradient, RadialGradient and ConicalGradient types.
For example, horizontal gradient:
import QtGraphicalEffects 1.0
SliderStyle {
groove: Item {
anchors.verticalCenter: parent.verticalCenter
implicitWidth: 500
implicitHeight: 10
Rectangle {
radius: height/2
anchors.fill: parent
border.width: 1
border.color: "#888"
layer.enabled: true
layer.effect: LinearGradient {
start: Qt.point(0, 0)
end: Qt.point(500, 0)
gradient: Gradient {
GradientStop { position: 0.0; color: "#0A406E" }
GradientStop { position: 1.0; color: "#FFA800" }
}
}
}
}
}
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
Does anyone know how to create a textEdit that can wrap using a slider?
i tried to do it but i got a problem with binding loop...
code:
Flickable
{
id: flick
anchors.fill:parent
contentWidth: edit.paintedWidth
contentHeight: edit.paintedHeight
clip: true
interactive :false
contentY: slider.y
function ensureVisible(r)
{
if (contentY >= r.y)
contentY = r.y;
else if (contentY+height <= r.y+r.height)
contentY = r.y+r.height-height;
}
TextEdit
{
id: edit
width: flick.width*0.9
height: flick.height
focus: true
wrapMode: TextEdit.Wrap
onCursorRectangleChanged: flick.ensureVisible(cursorRectangle)
text: defaultText
color: textColor
font.family: fontFamily
font.pointSize: fontSize
font.bold: bold
font.italic: italic
font.overline: overline
font.underline: underline
horizontalAlignment: alignment
selectByMouse:true
}
}
Rectangle
{
id: container
height: multiLineEdit.height
width:multiLineEdit.width*0.1
anchors.right:multiLineEdit.right
anchors.top:multiLineEdit.top
radius: 4
opacity: 0.7
smooth: true
gradient: Gradient {
GradientStop { position: 0.0; color: "gray" }
GradientStop { position: 1.0; color: "white" }
}
Rectangle {
id: slider
property int value: Math.round(container.y*100/(slider.width-container.width))
property int tmpVal: 0
x: 1
y: flick.visibleArea.yPosition * flick.height//1
width: parent.width
//The height will change according to the flickable area (the text area)
height: (flick.visibleArea.heightRatio > 1) ? (container.height) :(flick.visibleArea.heightRatio*container.height)
radius: 2
smooth: true
color:"black"
MouseArea {
anchors.fill: parent
drag.target: parent; drag.axis: Drag.YAxis
drag.minimumY: 0; drag.maximumY: container.height - slider.height
}
}
}
in this way i create a textEdit and a slier at the right side of the textEditBox. the slider is now move according to the text, but it(the slider) doe's not control the textEdit box... how i can add this action? (in my way it bring a binding loop)
Maybe you could grab keyboard button press event with a custom slot if key event match left and right arrow i.e.
Please take a look here: link for QWidget::grabKeyboard()