8

How do I implement a Rectangle in QML with an inner shadow?

See example in link below:

Create inner shadow in UIView

UPDATE:

Here's a simplified version of what I'm trying to do (which does not show any shadow):

import QtQuick 2.0
import QtGraphicalEffects 1.0

Item {
   width: 400
   height: 400

   Item {
      anchors.fill: parent

      Rectangle {
         id: myRectangle
         anchors.centerIn: parent
         width: 200
         height: 200
         color: "grey"
      }
   }

   InnerShadow {
      anchors.fill: myRectangle
      cached: true
      visible: true
      horizontalOffset: 0
      verticalOffset: 0
      radius: 25
      samples: 16
      color: "#b0000000"
      smooth: true
      source: myRectangle
   }
}

I'm sorry. My stupid... I got it wrong when i simplified the code (the Item was used for a DropShadow test, which works). This is how it was supposed to look like:

import QtQuick 2.0
import QtGraphicalEffects 1.0

Item {
   width: 400
   height: 400

   Rectangle {
      id: myRectangle
      anchors.centerIn: parent
      width: 200
      height: 200
      color: "grey"
   }

   InnerShadow {
      anchors.fill: myRectangle
      cached: true
      visible: true
      horizontalOffset: 0
      verticalOffset: 0
      radius: 25
      samples: 16
      color: "#b0000000"
      smooth: true
      source: myRectangle
   }
}
Community
  • 1
  • 1
daybyday
  • 163
  • 2
  • 7
  • Anchors don't work on items that are not parents or siblings, so try moving the `InnerShadow` into your `Item`. – cmannett85 Dec 15 '14 at 15:08
  • @cmannett85 is right. Have you not seen any warning/error? In which case, the internal Item becomes moot. – László Papp Dec 15 '14 at 20:11
  • Moving inside `myRectangle` is not an options as for the documentation of `source` property --> "Note: It is not supported to let the effect include itself, for instance by setting source to the effect's parent." – BaCaRoZzo Dec 16 '14 at 14:21

2 Answers2

4

I'm not sure why, but it works if you use the item above the item you're trying cast a shadow within (in this case it just happens to be the root item, but it doesn't have to be):

import QtQuick 2.0
import QtGraphicalEffects 1.0

Item {
    id: root
    width: 300
    height: 300

    Rectangle {
        id: myRectangle
        anchors.centerIn: parent
        width: 100
        height: 100
        color: "grey"
    }

    InnerShadow {
        anchors.fill: root
        cached: true
        horizontalOffset: 0
        verticalOffset: 0
        radius: 16
        samples: 32
        color: "#b0000000"
        smooth: true
        source: root
    }
}

inner shadow screenshot

I got the idea from QTBUG-27213 when I was searching for related bugs.

Mitch
  • 23,716
  • 9
  • 83
  • 122
  • Good one. The question now is: is it a bug or intended behaviour? Given the source document (I've cited in question), it seems the first, IMO. – BaCaRoZzo Dec 16 '14 at 19:00
  • 1
    It definitely doesn't seem right; I'd say it's worth creating a bug report for it. – Mitch Dec 16 '14 at 19:19
  • Did you create the bug report? Can you link it here for future references? Thanks :) – BaCaRoZzo Dec 17 '14 at 10:52
  • No, I haven't created one, sorry. – Mitch Dec 17 '14 at 12:31
  • Thanks for your reponses! When I use `myRectangle` as `source`/`anchors.fill` I get no shadow. When I change it to `root` I get a similar result as described in the bug report mentioned above. I am a new user in this forum so it seems like I don't have the privileges to include images in my posts yet. But the visual result is a strange mix of other parts of the display (on different colors). – daybyday Dec 17 '14 at 13:09
  • It seems the "correct" way to do that is as described in [this answer](http://lists.qt-project.org/pipermail/interest/2014-December/014507.html). Having the `source` `Item` filling its parent + including the targeted `Rectangle` does the trick. At least this does not break the documentation. – BaCaRoZzo Dec 17 '14 at 17:08
3

Here https://bugreports.qt.io/browse/QTBUG-56467 in first comment is the informative answer for those issues:

(c) Gunnar Sletta:

The InnerShadow will produce a shade in the opaque areas based on the translucent areas of the source object. When the source is fully opaque (like in the case of this rectangle, the result will thus be no shading.

DropShadow has a transparentBorder property which is set to true by default to aid the user in getting the expected results even for fully opaque input. The InnerShadow lacks this.

On a side note, the InnerShadow also uses the "old" gaussian code which requires significantly more samples to get the right

Anyway, the way do get an inner shadow on a rectangle is to pad it with a single pixel border of transparent pixels. This is also why it will sometimes work to give the parent of the intended source item as source of the effect. (Please note that this doesn't work in general, and that the parent is in fact unsupported as source as many GPUs will fail when you do this).

The following code produces the expected results:

import QtQuick 2.4
import QtGraphicalEffects 1.0

Item
{
    width: 320
    height: 480

    Item {
        anchors.centerIn: parent
        width: 100
        height: 100

        Rectangle {
            anchors.fill: parent
            anchors.margins: 1
            color: "lightsteelblue"
        }

        layer.enabled: true
        layer.effect: InnerShadow {
            color: "black"
            samples: 32
            radius: 10
            spread: 0.4
        }
    }
}

Alternatively, if you want to save the extra item, and can live with a tiny bit of trickery:

Rectangle {
    anchors.centerIn: parent
    width: 100
    height: 100
    color: "lightsteelblue"
    border.color: Qt.rgba(0, 0, 0, 0.01)
    border.width: 1
    layer.enabled: true
    layer.effect: InnerShadow {
        color: "black"
        samples: 20
        radius: 8.5
        spread: 0.2
    }
}

(the border.color, can't be 'transparent' because that is interpreted as no border)

Oleg
  • 101
  • 7