qml textedit with slider - qt

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()

Related

Landing Page Button Organization

I am using a template to create an app using QT Creator and QML and am hoping to create a landing page that allows users to select which "page" they want to navigate to by clicking an icon.
I've figured out how to get a button on the landing page and have it open another page. However, I am using x and y positions of the button and it doesn't scale correctly when the window size changes.
Ultimately, I am trying to put 6 buttons on the landing page in a way that scales correctly.
I have attached an image of my ideal Landing Page design and have also attached code for what I already have.
I hope I was able to explain this well enough. Please let me know if I can clarify anything.
import QtQuick 2.2
import QtQuick 2.6
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.4
import "components" as Components
//BACKGROUND COLOR
Rectangle {
signal signInClicked(string tourId)
color: "#242424"
AnimatedImage {
anchors.fill: parent
source: app.landingpageBackground
fillMode: Image.PreserveAspectCrop
visible: source > ""
}
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "#00000000";}
GradientStop { position: 1.0; color: "#00000000";}
}
}
//TITLE TEXT
Text {
id: titleText
anchors {
left: parent.left
right: parent.right
top: parent.top
topMargin: app.height/10
}
font.family: app.customTitleFont.name
text: app.info.title
font {
pointSize: 60
pointSize: app.titleFontSize * 1.4
}
color: "#00000000"
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
}
Button {
id: signInButton
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: 60 * app.scaleFactor
}
opacity: 0.0
style: ButtonStyle {
id: btnStyle
property real width: parent.width
label: Text {
id: lbl
text: signInButton.text
anchors.centerIn: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
width: parent.width
maximumLineCount: 2
elide: Text.ElideRight
wrapMode: Text.WordWrap
color: app.titleColor
font.family: app.customTextFont.name
font.pointSize: app.baseFontSize
}
background: Rectangle {
color: Qt.darker(app.headerBackgroundColor, 1.2)
border.color: app.titleColor
radius: app.scaleFactor * 2
}
}
height: implicitHeight < app.units(56) ? app.units(56) : undefined // set minHeight = 64, otherwise let it scale by content height which is the default behavior
width: Math.min(0.5 * parent.width, app.units(250))
text: qsTr("Let's Play!")
MouseArea{
anchors.fill: parent
onClicked: {
signInClicked("");
}
}
NumberAnimation{
id: signInButtonAnimation
target: signInButton
running: false
properties: "opacity"
from: 0.0
to: 1.0
easing.type: Easing.InQuad
duration: 1000
}
}
AboutPage {
id: aboutPage
}
NewsAndUpdates {
id: newsPage
}
ProgramsPage {
id: programsPage
}
Connections {
target: app
onUrlParametersChanged: {
if (app.urlParameters.hasOwnProperty("appid")) {
signInClicked(app.urlParameters.appid)
}
}
}
Component.onCompleted: {
signInButtonAnimation.start()
}
}
I think this doc should help you. You haven't really defined what you want very well, but I'll show you some examples so you can hopefully take from it what you need.
QML has the concept of "positioners" and "layouts". Positioners help automatically position your objects neatly on the screen. And Layouts try to do that too, but can also stretch your objects to fill available space.
Row:
You can arrange all your buttons in a row and center the row horizontally.
Row {
anchors.horizontalCenter: parent.horizontalCenter
Button { id: btn1 }
Button { id: btn2 }
...
}
Grid:
Similarly, Grid is a positioner that arranges objects into a grid:
Grid {
anchors.horizontalCenter: parent.horizontalCenter
columns: 3
Button { id: btn1 }
Button { id: btn2 }
...
}
GridLayout:
A GridLayout is just like a Grid, but it can also resize the objects to fill up the space. My opinion is layouts can do more, but they're often trickier to use. In this example, the first button should be a fixed size, while the second button should fill up the remaining width.
GridLayout {
anchors.fill: parent
rows: 2
Button { id: btn1; Layout.preferredWidth: 200 }
Button { id: btn2; Layout.fillWidth: true}
...
}

Putting an element between a child and parent (z values) QML

I was working with a GridView in QML. When I click on an element, I want to following highlight to happen:
However, my problem is that I want the blue color to appear below the delegate (not in the white area but still visible on the transparent side part) while the checkmark appears above (so it is visible). I have tried playing around with the z values so that the lowest z should be the blue rectangle, the middle should be the white rectangle part of the delegate, and the highest should be the check mark but i can't seem to make it work. Either the highlight or the delegate has to be on top. Does anyone know any way I can fix this so that it works correctly?
Code for highlight:
highlight:
Rectangle {
z:5
color: "steelblue"; radius: 5; opacity: 0.5
Image{
z:8
id: checkMark
visible: found;
x: parent.width-8-width
y: 8
width: 40;
height: 40;
source: "file:///Users/arjun/Documents/CompetitiveBall/images/checkMark.png"
}
}
Code for delegate:
Component {
id: contactsDelegate
Rectangle{
width: grid.cellWidth
height: grid.cellHeight
color: "transparent"
Rectangle {
z:7
width: grid.cellWidth-20
height: grid.cellHeight-20
id: wrapper
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
border.width: 3
border.color: "black"
radius: 5;
Image{
id: mImage
x:parent.x
width: 65
height:65;
source: picSource
}
Text{
width: grid.cellWidth-15
y: mImage.y+mImage.height+4
anchors.horizontalCenter: parent.horizontalCenter
id: nameText
text: name
font.family: "Palatino Linotype"
font.bold: (grid.isCurrentItem===true)?"true":"false"
horizontalAlignment: Text.AlignHCenter
color:"#050027"
}
MouseArea{
anchors.fill: parent
onClicked:{
console.log("Clicked on :" + name)
//what happens when u click
grid.currentIndex=index;
}
}
}
}
}
Since you want part of the highlight to be underneath the delegate and part of it to be on top, you need to break it up into different pieces. I tested the code below with Qt 5.15.0. I made the normal highlight object draw underneath the delegate. Then I added another Rectangle that follows the highlight that draws on top of the delegate.
GridView
{
id: lv
anchors.fill: parent
anchors.margins: 50
cellWidth: 50
cellHeight: 50
model: 30
// By default, highlight draws behind delegates
// (You can specify a positive z-value to make it draw on top)
highlight: Item
{
Rectangle
{
anchors.centerIn: parent
width: 50
height: 50
color: "green"
}
}
delegate: Rectangle
{
width: 30
height: 30
color: "red"
MouseArea
{
anchors.fill: parent
onClicked: lv.currentIndex = index;
}
}
// This will draw on top of the delegates
// (You can change that by specifying a negative z-value.)
Rectangle
{
id: checkbox
x: lv.highlightItem.x - lv.contentX
y: lv.highlightItem.y - lv.contentY
width: 10
height: 10
color: "blue"
}
}

Line numbers/ line height for a Qml TextArea

We want to implement an embedded code editor in our QtQuick based application. For highlighting we use a QSyntaxHighlighter based on KSyntaxHighlighting. We found no way to determine the line height and line spacing that would allow us to display line numbers next to the code. Supporting dynamic line-wrap would also be a great addition.
Flickable {
id: flickable
flickableDirection: Flickable.VerticalFlick
Layout.preferredWidth: parent.width
Layout.maximumWidth: parent.width
Layout.minimumHeight: 200
Layout.fillHeight: true
Layout.fillWidth: true
boundsBehavior: Flickable.StopAtBounds
clip: true
ScrollBar.vertical: ScrollBar {
width: 15
active: true
policy: ScrollBar.AlwaysOn
}
property int rowHeight: textArea.font.pixelSize+3
property int marginsTop: 10
property int marginsLeft: 4
property int lineCountWidth: 40
Column {
id: lineNumbers
anchors.left: parent.left
anchors.leftMargin: flickable.marginsLeft
anchors.topMargin: flickable.marginsTop
y: flickable.marginsTop
width: flickable.lineCountWidth
function range(start, end) {
var rangeArray = new Array(end-start);
for(var i = 0; i < rangeArray.length; i++){
rangeArray[i] = start+i;
}
return rangeArray;
}
Repeater {
model: textArea.lineCount
delegate:
Label {
color: (!visualization.urdfPreviewIsOK && (index+1) === visualization.urdfPreviewErrorLine) ? "white" : "#666"
font: textArea.font
width: parent.width
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
height: flickable.rowHeight
renderType: Text.NativeRendering
text: index+1
background: Rectangle {
color: (!visualization.urdfPreviewIsOK && (index+1) === visualization.urdfPreviewErrorLine) ? "red" : "white"
}
}
}
}
Rectangle {
y: 4
height: parent.height
anchors.left: parent.left
anchors.leftMargin: flickable.lineCountWidth + flickable.marginsLeft
width: 1
color: "#ddd"
}
TextArea.flickable: TextArea {
id: textArea
property bool differentFromSavedState: fileManager.textDifferentFromSaved
text: fileManager.textTmpState
textFormat: Qt.PlainText
//dont wrap to allow for easy line annotation wrapMode: TextArea.Wrap
focus: false
selectByMouse: true
leftPadding: flickable.marginsLeft+flickable.lineCountWidth
rightPadding: flickable.marginsLeft
topPadding: flickable.marginsTop
bottomPadding: flickable.marginsTop
background: Rectangle {
color: "white"
border.color: "green"
border.width: 1.5
}
Component.onCompleted: {
fileManager.textEdit = textArea.textDocument
}
onTextChanged: {
fileManager.textTmpState = text
}
function update()
{
text = fileManager.textTmpState
}
}
}
As you can see we use property int rowHeight: textArea.font.pixelSize+3 to guess the line height and line spacing but that of course breaks as soon as DPI or other properties of the system change.
The TextArea type has two properties contentWidth and contentHeight which contains the size of the text content.
So, if you divide the height by the number of lines (which you can get with the property lineCount), you will get the height of a line:
property int rowHeight: textArea.contentHeight / textArea.lineCount
But, if you plan to have multiple line spacing in the same document, you will have to handle each line by manipulating the QTextDocument:
class LineManager: public QObject
{
Q_OBJECT
Q_PROPERTY(int lineCount READ lineCount NOTIFY lineCountChanged)
public:
LineManager(): QObject(), document(nullptr)
{}
Q_INVOKABLE void setDocument(QQuickTextDocument* qdoc)
{
document = qdoc->textDocument();
connect(document, &QTextDocument::blockCountChanged, this, &LineManager::lineCountChanged);
}
Q_INVOKABLE int lineCount() const
{
if (!document)
return 0;
return document->blockCount();
}
Q_INVOKABLE int height(int lineNumber) const
{
return int(document->documentLayout()->blockBoundingRect(document->findBlockByNumber(lineNumber)).height());
}
signals:
void lineCountChanged();
private:
QTextDocument* document;
};
LineManager* mgr = new LineManager();
QQuickView *view = new QQuickView;
view->rootContext()->setContextProperty("lineCounter", mgr);
view->setSource(QUrl("qrc:/main.qml"));
view->show();
Repeater {
model: lineCounter.lineCount
delegate:
Label {
color: "#666"
font: textArea.font
width: parent.width
height: lineCounter.height(index)
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
renderType: Text.NativeRendering
text: index+1
background: Rectangle {
border.color: "black"
}
}
}
I found a QML only solution:
Use TextEdit instead of TextArea to avoid alignment issues between line numbers and text
Use a 'ListView' to generate the line numbers for the text edit:
Here is an initial solution:
RowLayout {
anchors.fill: parent
ListView {
Layout.preferredWidth: 30
Layout.fillHeight: true
model: textEdit.text.split(/\n/g)
delegate: Text { text: index + 1 }
}
TextEdit {
id: textEdit
Layout.fillWidth: true
Layout.fillHeight: true
}
}
The ListView has a complete copy of each row of text. We can use this copy to compute the line height (taking into account of word wrap). We do this by creating an invisible Text. We can improve the answer further by adding a Flickable to the TextEdit and synchronize the scroll between the ListView and the TextEdit:
Here is a more complete solution:
// NumberedTextEdit.qml
import QtQuick 2.12
import QtQuick.Controls 2.5
Item {
property alias lineNumberFont: lineNumbers.textMetrics.font
property color lineNumberBackground: "#e0e0e0"
property color lineNumberColor: "black"
property alias font: textEdit.font
property alias text: textEdit.text
property color textBackground: "white"
property color textColor: "black"
Rectangle {
anchors.fill: parent
color: textBackground
ListView {
id: lineNumbers
property TextMetrics textMetrics: TextMetrics { text: "99999"; font: textEdit.font }
model: textEdit.text.split(/\n/g)
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: 10
width: textMetrics.boundingRect.width
clip: true
delegate: Rectangle {
width: lineNumbers.width
height: lineText.height
color: lineNumberBackground
Text {
id: lineNumber
anchors.horizontalCenter: parent.horizontalCenter
text: index + 1
color: lineNumberColor
font: textMetrics.font
}
Text {
id: lineText
width: flickable.width
text: modelData
font: textEdit.font
visible: false
wrapMode: Text.WordWrap
}
}
onContentYChanged: {
if (!moving) return
flickable.contentY = contentY
}
}
Item {
anchors.left: lineNumbers.right
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: 10
Flickable {
id: flickable
anchors.fill: parent
clip: true
contentWidth: textEdit.width
contentHeight: textEdit.height
TextEdit {
id: textEdit
width: flickable.width
color: textColor
wrapMode: Text.WordWrap
}
onContentYChanged: {
if (lineNumbers.moving) return
lineNumbers.contentY = contentY
}
}
}
}
}
I've found that you can query the line height using FontMetrics and then getting the true height by Math.ceil(fontMetrics.lineSpacing) for example:
TextEdit {
id: textArea
FontMetrics {
id: fontMetricsId
font: textArea.font
}
Component.onCompleted: {
console.log("Line spacing:" + Math.ceil(fontMetricsId.lineSpacing)
}
}

auto KeyNavigation.tab for custom components "rectangle or item" in QML

in Qml there is auto key navigation for already known components like checkbox,textfield,button and etc , i have my custom component which is a item or rectangle and i want same functionality for it,without writing
KeyNavigation.tab: componentid
here is one of my custom controls :
Rectangle {
signal clicked
property alias font : icoText.font.family
property alias icon : icoText.text
property alias size : icoText.font.pixelSize
property alias toolTip : tooltipText.text
property string colorEnter :"#0481ff"
property string colorExit :"#00171f"
id: root
implicitWidth: 50
implicitHeight: 50
//width: childrenRect.width
radius: 0
//height: childrenRect.height
color: colorExit
state: "default"
Text {
id: icoText
text: ""
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 25
font.family: "fontawesome"
visible: text!= ""
color: "white"
}
ToolTip {
id:tooltipText
text: ""
delay: 500
timeout: 2000
visible: mouseArea.containsMouse && text!=""
font.family: "B Nazanin"
contentItem: Text {
text: tooltipText.text
font: tooltipText.font
color: "white"
}
background: Rectangle {
color: "#cc000000"
border.color: "black"
}
}
InnerShadow {
id:shadow
anchors.fill: icoText
radius: 1.0
samples: 17
horizontalOffset: 1
color: colorExit
source: icoText
visible: false
}
MouseArea{
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: root.color = colorEnter
onExited: root.color = root.state == "transparent"? "transparent" : root.colorExit
onPressed: {
shadow.visible = true
}
onReleased: {
shadow.visible = false
}
onClicked: {
root.clicked()
}
}
states: [
State {
name: "transparent"
PropertyChanges {
target: root
color:"transparent"
}
PropertyChanges {
target: icoText
color:colorExit
}
},
State{
name: "default"
PropertyChanges {
target: root
color:colorExit
}
PropertyChanges {
target: icoText
color:"white"
}
}
]
}
which will be inside a page like this :
Item{
myControl{
}
myControl{
}
}
this component by default does not loop through pressing tab what should i do?
i already tried this without success, i think this should be inside FocusScope but cause of poor documentation i did not get a simple example for this
set activeFocusOnTab on parent and focus:true in child you want to get focus
Rectangle{
activeFocusOnTab: true
Control{
focus: true
}
}
Focusable Qml Components (Focus My Control On Tab)
For my experience key navigation only works with native components like checkbox, textfield, button, etc.
To work arround this problem I used a fake native component hided with the same size of my custom component like the next example:
Rectangle {
id: myCustomComponent1
width: 100
height: 100
color: red
Button {
id: buttonFake1
text: "My Accessible text Component 1"
width: parent.width
height: parent.height
opacity: 0 // hide the fake component
Accessible.role: Accessible.defaultButton
Accessible.name: text
KeyNavigation.tab: buttonFake2
onClicked: {
console.log(index)
}
onFocusChanged: {
if(focus === true){
// Here do what you want with your custom component
// For example, change color, size, ...
Do_what_you_Want()
// And then set back the focus at the fake native component
// to key navigation keeps working from the same component
buttonFake1.focus = true
}
}
}
}
Rectangle {
id: myCustomComponent2
width: 100
height: 100
color: green
Button {
id: buttonFake2
text: "My Accessible text Component 2"
width: parent.width
height: parent.height
opacity: 0 // hide the fake component
Accessible.role: Accessible.defaultButton
Accessible.name: text
KeyNavigation.tab: buttonFake1
onClicked: {
console.log(index)
}
onFocusChanged: {
if(focus === true){
// Here do what you want with your custom component
// For example, change color, size, ...
Do_what_you_Want()
// And then set back the focus at the fake native component
// to key navigation keeps working from the same component
buttonFake2.focus = true
}
}
}
}

How to make Rectangle behaves like a scrollbar in qml

I am trying to make a scroll bar without using ScrollBar Component in QML.
So i have made this component and attach to the ListView. But it doesn't flick the listview items.
I want, this rectangle scrolls the content of ListView or GridView on scrolling.
What I did?
Ist I create a rectangle then make another rectangle as a child of ist one. And applied the dragging technique on Y axis and set the coordinates for y axis.
My Code is given below:
import QtQuick 2.0
Rectangle{
property bool is_parentDrag: false
property bool is_childDrag: false
id:parent_screen
anchors.fill:parent
color:"#ebeaee"
Rectangle{
id:foot
width:parent.width*0.9
height:parent.height*0.133
color:"#ffffff"
border.width:1
anchors.bottom:parent.bottom
anchors.bottomMargin:lv.height*0.005
anchors.horizontalCenter: parent.horizontalCenter
Rectangle{
width:parent.width*0.125
height:parent.height*0.5
radius:20
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: parent.height*0.2
color:"transparent"
Image{
source: "left_direction_icon.png"
anchors.centerIn: parent
sourceSize.width: parent.width*0.4
sourceSize.height: parent.width*0.4
}
MouseArea{
anchors.fill:parent
onClicked: {
stack.pop()
}
}
}
}
Flickable{
id:flick_1
width:parent.width*0.9
height:parent.height*0.7
anchors.centerIn: parent
flickableDirection:Flickable.HorizontalFlick
boundsBehavior: Flickable.StopAtBounds
ListView{
id:lv
clip:true
boundsBehavior: Flickable.StopAtBounds
height:parent.height
width:parent.width*0.9
anchors.left:parent.left
anchors.leftMargin: parent.width*0.11
model:Data{}
delegate: Rectangle{
id:delg
width:lv.width*0.5
height:lv.height*0.170
Text{
text:txt
anchors.centerIn: parent
font.pixelSize: 22
}
Rectangle{
id:right
width:1
height:parent.height
color:"black"
anchors.right:parent.right
}
Rectangle{
id:bottom
width:parent.width
height:1
color:"black"
anchors.bottom:parent.bottom
}
Rectangle{
id:left
width:1
height:parent.height
color:"black"
anchors.left:parent.left
}
}
Rectangle{
id:scrollbar
width:flick_1.width*0.02
height:flick_1.height
visible: lv.contentHeight>lv.height
radius:width/2
color:"lightgrey"
anchors.right: lv.right
anchors.rightMargin: lv.width*0.1
Rectangle {
id:scroll
Drag.active:is_parentDrag?parent_drag_area.drag.active:is_childDrag?drag_area.drag.active:false
Drag.source: scroll
implicitWidth: parent.width
implicitHeight: parent.height*0.7
radius:width/2
opacity:0.85
color: "grey"
MouseArea{
id:drag_area
anchors.fill:parent
drag.target: scroll
drag.axis: Drag.YAxis
drag.minimumY: 0
drag.maximumY: scrollbar.height-scroll.height
onPressed:{
if(is_childDrag)
is_childDrag=false
else
is_childDrag=true
}
}
}
MouseArea{
id:parent_drag_area
anchors.fill:parent
drag.target: scroll
drag.axis: Drag.YAxis
drag.minimumY: 0
drag.maximumY: scrollbar.height-scroll.height
onPressed:{
if(is_parentDrag)
is_parentDrag=false
else
is_parentDrag=true
}
}
}
// Rectangle {
// id: scrollbar
// //height:parent.height*0.04
// width:parent.width
// radius:width/2
// anchors.bottom: parent.bottom
// y: flick_1.visibleArea.yPosition * flick_1.height
// height: flick_1.visibleArea.heightRatio * flick_1.height*0.04
// color: "lightgrey"
// anchors.bottomMargin: parent.height*0.1
// Rectangle {
// id:scroll
// y: flick_1.visibleArea.yPosition * flick_1.height
// implicitWidth: parent.width*0.7
// implicitHeight: parent.height
// radius:width/2
// opacity:0.85
// color: "grey"
// }
// }
}
}
}
You can try this (copied from QML Material Project).
Create a new QML-File called ScrollbarCustom.qml:
Item {
id: root
property Flickable flickableItem
property int orientation: Qt.Vertical
property int thickness: 5
property bool moving: flickableItem.moving
property alias currentY: scrollBar.y
width: thickness
height: thickness
clip: true
smooth: true
visible: orientation === Qt.Vertical ? flickableItem.contentHeight > flickableItem.height
: flickableItem.contentWidth > flickableItem.width
anchors {
top: orientation === Qt.Vertical ? flickableItem.top : undefined
bottom: flickableItem.bottom
left: orientation === Qt.Horizontal ? flickableItem.left : undefined
right: flickableItem.right
margins: 2
}
signal stopAnimation
onStopAnimation: {
hideAnimation.stop();
showAnimation.start();
}
signal startAnimation
onStartAnimation: {
hideAnimation.start();
showAnimation.stop();
}
Component.onCompleted: hideAnimation.start()
onMovingChanged: {
if (moving) {
hideAnimation.stop()
showAnimation.start()
} else {
hideAnimation.start()
showAnimation.stop()
}
}
NumberAnimation {
id: showAnimation
target: scrollBar;
property: "opacity";
to: 0.3;
duration: 200;
easing.type: Easing.InOutQuad
}
SequentialAnimation {
id: hideAnimation
NumberAnimation { duration: 500 }
NumberAnimation {
target: scrollBar;
property: "opacity";
to: 0;
duration: 500;
easing.type: Easing.InOutQuad
}
}
onOrientationChanged: {
if (orientation == Qt.Vertical) {
width = thickness
} else {
height = thickness
}
}
Rectangle {
id: scrollBar
property int length: orientation == Qt.Vertical ? root.height
: root.width;
property int targetLength: orientation == Qt.Vertical ? flickableItem.height
: flickableItem.width;
property int contentStart: orientation == Qt.Vertical ? flickableItem.contentY
: flickableItem.contentX;
property int contentLength: orientation == Qt.Vertical ? flickableItem.contentHeight
: flickableItem.contentWidth;
property int start: Math.max(0, length * contentStart/contentLength);
property int end: Math.min(length,
length * (contentStart + targetLength)/contentLength)
color: Theme.accentColor //"black"//theme.foreground
opacity: 0.2
radius: thickness/2
width: Math.max(orientation == Qt.Horizontal ? end - start : 0, thickness)
height: Math.max(orientation == Qt.Vertical ? end - start : 0, thickness)
x: orientation == Qt.Horizontal ? start : 0
y: orientation == Qt.Vertical ? start : 0
}
}
And use it like this:
Flickable {
id: flickable
clip: true
anchors {
top: parent.top
left: parent.left
right: parent.right
bottom: parent.bottom
}
}
ScrollbarCustom {
flickableItem: flickable
}
A solution is to leverage Qt Quick Templates 2. This Qt module is the base of Qt own controls Qt Quick Controls 2 and contains multiple base UI components that can be fully customized.
In your case, you should look at ScrollBar and how to customize it.
Your code could end up being something like this:
Flickable {
id: flickable
clip: true
// ...
ScrollBar.vertical: ScrollBar {
id: control
size: 0.3
position: 0.2
active: true
orientation: Qt.Vertical
contentItem: Rectangle {
implicitWidth: 6
implicitHeight: 100
radius: width / 2
color: control.pressed ? "#81e889" : "#c2f4c6"
}
}
}

Resources