A while back, I got to thinking about the great look of Microsoft Acrylic. Being a Linux user, I didn't actually have Acrylic available on my system, but I wanted to try to create something similar in QML. Unfortunately, I wasn't able to figure out how to use it as an app background material with the desktop showing through, but I did manage to create something that should be useful for creating impressive apps.
The current implementation of QML Acrylic is not perfect. Putting items on top of the Acrylic can influence its appearance, and if the items are changing (think a blinking text cursor), the Acrylic will change with it. However, I hope to improve it. (Who knows? Maybe I'll have to post a second, improved Acrylic later on.)
To start off, let's create a QML window that displays an image. Fire up Qt Creator (or whatever IDE you use) and create main.qml
:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("Acrylic")
Image {
id: img
fillMode: Image.PreserveAspectCrop
anchors.fill: parent
source: "pic.jpeg"
}
}
where pic.jpeg
is the image you're using. Run this to make sure it displays a window containing an image before you move on.
Now let's think a bit about how to create an Acrylic-style rectangular pane. From screenshots available on the Web, we can tell that it's rectangular, white (OK, depends on your system theme, but white is common), and partly transparent. So, let's create a Rectangle that meets these criteria and that is a child of img
:
Rectangle {
id: rect
anchors.centerIn: parent
color: "white"
opacity: 0.4
width: img.width * 1/2
height: img.height * 1/2
}
Running this should produce a boring-looking rectangle in the middle of the window. Now, let's add a blur as a child of rect
(after adding import QtGraphicalEffects 1.0
to the top of main.qml
):
GaussianBlur {
anchors.fill: parent
source: parent
radius: 50
// this formula is suggested by Qt
samples: 1 + radius * 2
}
Wait, that doesn't do anything! What's going on?
Well, it turns out that this only blurs the rectangle, and leaves the image alone. To blur the whole thing, we'll need to add a ShaderEffectSource
. Also, we'll need to move the ShaderEffectSource
and the GaussianBlur
out of the Rectangle to make everything behave properly (if you don't believe me, just try it yourself!). This gives us a final result of:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtGraphicalEffects 1.0
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("Acrylic")
Image {
id: img
fillMode: Image.PreserveAspectCrop
anchors.fill: parent
source: "pic.jpeg"
Rectangle {
id: rect
anchors.centerIn: parent
color: "white"
opacity: 0.4
width: img.width * 1/2
height: img.height * 1/2
}
ShaderEffectSource {
id: ses
anchors.fill: rect
sourceItem: img
sourceRect: Qt.rect(x, y, width, height)
}
GaussianBlur {
radius: 50
anchors.fill: ses
source: ses
samples: 1 + radius * 2
}
}
}
which gives us a nice, Acrylic-y pane. You can also do like I did and split the Acrylic out into a reusable file with some alias
es for easy configuration:
import QtQuick 2.12
import QtGraphicalEffects 1.0
Item {
id: acrylicRoot
property alias color: rect.color
property alias acrylicOpacity: rect.opacity
Rectangle {
id: rect
anchors.fill: parent
color: "white"
opacity: 0.4
}
ShaderEffectSource {
id: ses
anchors.fill: parent
sourceItem: acrylicRoot.parent
sourceRect: Qt.rect(acrylicRoot.x, acrylicRoot.y, acrylicRoot.width, acrylicRoot.height)
}
GaussianBlur {
radius: 50
anchors.fill: ses
source: ses
samples: 1 + radius * 2
}
}
and then stick it into any old QML scene, like so:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("Acrylic")
Image {
id: img
fillMode: Image.PreserveAspectCrop
anchors.fill: parent
source: "pic.jpeg"
Acrylic {
anchors.centerIn: parent
width: parent.width * 1/2
height: parent.height * 1/2
Label {
anchors.centerIn: parent
text: "Acrylic"
font.pointSize: 32
}
}
}
}
(This basic code, by the way, was used to generate the screenshot at the top of this article.)
The Label
in the above code provides an interesting bug: the ShaderEffectSource
is grabbing the Label
for part of its source, which means that the Label
influences the blur. In fact, whenever the Acrylic is updated, the ShaderEffectSource
is grabbing the blur as part of its source instead of just getting what's underneath of it. This is rather undesirable behavior. Do I know how to fix this? No. But it certainly will be an interesting topic to investigate.
Found this useful? Let me know in the comments below what you're doing with this.
Top comments (0)