31

Currently i had a requirement of drawing a delegate rectangle with the help of ListView control. I was able to draw a series of rectangle either horizontal or vertical within the list view but the problem is with the border of the rectangle. The border width at the intersect point of the adjacent rectangle is of twice the width.

The delegate rectangle is nothing but a Qt Quick Rectangle element.

Is it possible to limit the border width on any one side of the rectangle alone?

Is it possible to change the color on any one side? (Something similar to QLineEdit - Where we can control the border width and color with respect to the sides)

Regards, Santhosh.

Santhosh
  • 637
  • 1
  • 7
  • 19

5 Answers5

36

You can make a custom border element like this :

CustomBorder.qml

import QtQuick 1.0

Rectangle
{

    property bool commonBorder : true

    property int lBorderwidth : 1
    property int rBorderwidth : 1
    property int tBorderwidth : 1
    property int bBorderwidth : 1

    property int commonBorderWidth : 1

    z : -1

    property string borderColor : "white"

    color: borderColor

    anchors
    {
        left: parent.left
        right: parent.right
        top: parent.top
        bottom: parent.bottom

        topMargin    : commonBorder ? -commonBorderWidth : -tBorderwidth
        bottomMargin : commonBorder ? -commonBorderWidth : -bBorderwidth
        leftMargin   : commonBorder ? -commonBorderWidth : -lBorderwidth
        rightMargin  : commonBorder ? -commonBorderWidth : -rBorderwidth
    }
}

main.qml

import QtQuick 1.0

Rectangle
{
    width:  500
    height: 500
    color: "grey"

    Rectangle
    {
        anchors.centerIn: parent
        width : 300
        height: 300
        color: "pink"

        CustomBorder
        {
            commonBorderWidth: 3
            borderColor: "red"
        }
    }


    Rectangle
    {
        anchors.centerIn: parent
        width : 200
        height: 200
        color: "green"

        CustomBorder
        {
            commonBorder: false
            lBorderwidth: 10
            rBorderwidth: 0
            tBorderwidth: 0
            bBorderwidth: 0
            borderColor: "red"
        }
    }


    Rectangle
    {
        anchors.centerIn: parent
        width : 100
        height: 100
        color: "yellow"

        CustomBorder
        {
            commonBorder: false
            lBorderwidth: 0
            rBorderwidth: 0
            tBorderwidth: 10
            bBorderwidth: 10
            borderColor: "blue"
        }
    }

}

In this example I have used the custom element to make different rectangles which have border on all, one or two sides.

Amit Tomar
  • 4,800
  • 6
  • 50
  • 83
8

The simplest solution for a ListView is to give your delegate a 1 pixel border and then use a spacing of -1 to get each cell to overlap the other by 1 pixel:

ListView {
    spacing: -1
    delegate: Rectangle {
        height: 40
        width: parent.width
        border.width: 1
        border.color: "black"
        z: listView.currentIndex === model.index ? 2 : 1
        ...
    }
    ...
}

It should work the same for other border widths.

EDIT: Added a nice enhancement from comment below that makes sure the selected item's border is always above all others so that if you change it to indicate selection it's not obscured by its neighbor delegates.

David K. Hess
  • 16,632
  • 2
  • 49
  • 73
  • 1
    The problem with this solution is that each delegate is on top of the previous one, so if you need to change the color of border on the current item for exemple, the bottom border remains unchanged (because it actually corresponds to the top border of the next delegate). Adding a z property to the delegate fixes the problem: `z: listView.currentIndex === model.index ? 2 : 1` – augre Mar 05 '18 at 12:46
  • Great enhancement. Added that to the answer. – David K. Hess Mar 05 '18 at 14:34
5

If you're trying to add borders between items in ListView, you should use the given property 'spacing' to establish a common border between each item. Then you could potentially add a background to the ListView to customize border colors.

Example:

ListView {
    spacing: 1 // or whatever you want the border to be
}

...But if you really want a specific border you could always use Rectangles to make your own borders:

Item { // this is your 'rectangle'
    Rectangle { // the main thing
        id: rec
        anchors.fill: parent
        anchors.leftMargin: 2
        anchors.rightMargin: 5
        // etc
    }

    Rectangle { // a border example
        anchors.right: rec.right
        height: parent.height
        width: 5
        color: "red"
        // etc
    }
}
Jason Chan
  • 183
  • 1
  • 4
4

A bit late to answer but the accepted solution draws the border outside the geometry of the rectangle which can be problematic in some cases.

Another way to do this is to do something like:

// CustomBorderRect.qml
import QtQuick 2.12

Item
{
    property alias color: innerRect.color

    property alias borderColor : borderRect.color
    property int borderWidth: 0

    property int lBorderwidth : borderWidth
    property int rBorderwidth : borderWidth
    property int tBorderwidth : borderWidth
    property int bBorderwidth : borderWidth

    Rectangle
    {
        id: borderRect
        anchors.fill: parent

        Rectangle
        {
            id: innerRect

            anchors {
                fill: parent

                leftMargin: lBorderwidth
                rightMargin: rBorderwidth
                topMargin: tBorderwidth
                bottomMargin: bBorderwidth
            }
        }
    }
}

This can then be used like this:

CustomBorderRect
{
    width : 50
    height: 30
    color: "lightseagreen"

    lBorderwidth: 0
    rBorderwidth: 5
    tBorderwidth: 5
    bBorderwidth: 0
    borderColor: "lightyellow"
}

This way the border is drawn with the given geometry.

Qutab Qazi
  • 131
  • 1
  • 1
  • 7
0

You can create a custom Border in a separate qml file Stroke.qml like below:

import QtQuick
import QtQuick.Controls.Material


Rectangle {
    property int strokeLeft
    property int strokeTop
    property int strokeRight
    property int strokeBottom
    property var strokeColor: Material.accent

    anchors.fill: parent
    color: Material.primary

    Rectangle {
        visible: strokeLeft
        width: strokeLeft
        anchors {
            left: parent.left
            top: parent.top
            bottom: parent.bottom
        }
        color: strokeColor
    }

    Rectangle {
        visible: strokeTop
        height: strokeTop
        anchors {
            left: parent.left
            top: parent.top
            right: parent.right
        }
        color: strokeColor
    }

    Rectangle {
        visible: strokeRight
        width: strokeRight
        anchors {
            top: parent.top
            right: parent.right
            bottom: parent.bottom
        }
        color: strokeColor
    }

    Rectangle {
        visible: strokeBottom
        height: strokeBottom
        anchors {
            left: parent.left
            right: parent.right
            bottom: parent.bottom
        }
        color: strokeColor
    }
}

And then you can call it in your components like this:

 // Border left
 Stroke { 
     strokeLeft: 1  
     color: Material.accent  
 }  
 // Border top  
 Stroke { 
     strokeTop: 2  
     color: Material.accent  
 }  
 // Border right
 Stroke { 
     strokeRight: 5 
     color: Material.accent  
 }  
 // Border bottom
 Stroke { 
     strokeRight: 3 
     color: Material.accent  
 }  
 // Border top-left
 Stroke { 
     strokeLeft: 1  
     strokeTop: 1
     color: Material.accent  
 }  
 // Border for all sides
 Stroke {  
     strokeLeft: 1  
     strokeTop: 1
     strokeRight: 1 
     strokeBottom: 1
     color: Material.accent  
 }