How to create a round mouse area in QML
Stealing code from PieMenu, here's RoundMouseArea.qml
:
import QtQuick 2.0
Item {
id: roundMouseArea
property alias mouseX: mouseArea.mouseX
property alias mouseY: mouseArea.mouseY
property bool containsMouse: {
var x1 = width / 2;
var y1 = height / 2;
var x2 = mouseX;
var y2 = mouseY;
var distanceFromCenter = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
var radiusSquared = Math.pow(Math.min(width, height) / 2, 2);
var isWithinOurRadius = distanceFromCenter < radiusSquared;
return isWithinOurRadius;
}
readonly property bool pressed: containsMouse && mouseArea.pressed
signal clicked
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: if (roundMouseArea.containsMouse) roundMouseArea.clicked()
}
}
You can use it like this:
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
width: 640
height: 480
visible: true
RoundMouseArea {
id: roundMouseArea
width: 100
height: 100
anchors.centerIn: parent
onClicked: print("clicked")
// Show the boundary of the area and whether or not it's hovered.
Rectangle {
color: roundMouseArea.pressed ? "red" : (roundMouseArea.containsMouse ? "darkorange" : "transparent")
border.color: "darkorange"
radius: width / 2
anchors.fill: parent
}
}
}
Another option is a C++/QML way as decribed in this example. This example provides a way to use masks of any shapes. It can be customized to fit your needs.
Posting the code as it is:
maskedmousearea.cpp
MaskedMouseArea::MaskedMouseArea(QQuickItem *parent)
: QQuickItem(parent),
m_pressed(false),
m_alphaThreshold(0.0),
m_containsMouse(false)
{
setAcceptHoverEvents(true);
setAcceptedMouseButtons(Qt::LeftButton);
}
void MaskedMouseArea::setPressed(bool pressed)
{
if (m_pressed != pressed) {
m_pressed = pressed;
emit pressedChanged();
}
}
void MaskedMouseArea::setContainsMouse(bool containsMouse)
{
if (m_containsMouse != containsMouse) {
m_containsMouse = containsMouse;
emit containsMouseChanged();
}
}
void MaskedMouseArea::setMaskSource(const QUrl &source)
{
if (m_maskSource != source) {
m_maskSource = source;
m_maskImage = QImage(QQmlFile::urlToLocalFileOrQrc(source));
emit maskSourceChanged();
}
}
void MaskedMouseArea::setAlphaThreshold(qreal threshold)
{
if (m_alphaThreshold != threshold) {
m_alphaThreshold = threshold;
emit alphaThresholdChanged();
}
}
bool MaskedMouseArea::contains(const QPointF &point) const
{
if (!QQuickItem::contains(point) || m_maskImage.isNull())
return false;
QPoint p = point.toPoint();
if (p.x() < 0 || p.x() >= m_maskImage.width() ||
p.y() < 0 || p.y() >= m_maskImage.height())
return false;
qreal r = qBound<int>(0, m_alphaThreshold * 255, 255);
return qAlpha(m_maskImage.pixel(p)) > r;
}
void MaskedMouseArea::mousePressEvent(QMouseEvent *event)
{
setPressed(true);
m_pressPoint = event->pos();
emit pressed();
}
void MaskedMouseArea::mouseReleaseEvent(QMouseEvent *event)
{
setPressed(false);
emit released();
const int threshold = qApp->styleHints()->startDragDistance();
const bool isClick = (threshold >= qAbs(event->x() - m_pressPoint.x()) &&
threshold >= qAbs(event->y() - m_pressPoint.y()));
if (isClick)
emit clicked();
}
void MaskedMouseArea::mouseUngrabEvent()
{
setPressed(false);
emit canceled();
}
void MaskedMouseArea::hoverEnterEvent(QHoverEvent *event)
{
Q_UNUSED(event);
setContainsMouse(true);
}
void MaskedMouseArea::hoverLeaveEvent(QHoverEvent *event)
{
Q_UNUSED(event);
setContainsMouse(false);
}
Usage in QML:
import Example 1.0
MaskedMouseArea {
id: moonArea
anchors.fill: parent
alphaThreshold: 0.4
maskSource: moon.source
}
Register the custom item:
qmlRegisterType<MaskedMouseArea>("Example", 1, 0, "MaskedMouseArea");