Advance Wayland and KDE package bring-up

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -0,0 +1,26 @@
#add_subdirectory(qmlcontrols)
add_subdirectory(calendarevents)
if(BUILD_QCH)
ecm_add_qch(
KF6Declarative_QCH
NAME KDeclarative
BASE_NAME KF6Declarative
VERSION ${KF_VERSION}
ORG_DOMAIN org.kde
SOURCES # using only public headers, to cover only public API
${CalendarEvents_QCH_SOURCES}
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
LINK_QCHS
Qt6Core_QCH
INCLUDE_DIRS
${CalendarEvents_QCH_INCLUDE_DIRS}
BLANK_MACROS
CALENDAREVENTS_EXPORT
"CALENDAREVENTS_DEPRECATED_VERSION(x, y, t)"
"CALENDAREVENTS_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
COMPONENT Devel
)
endif()
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
# Invoke the extractrc script on all .ui, .rc, and .kcfg files in the sources.
# The results are stored in a pseudo .cpp file to be picked up by xgettext.
lst=`find . -name \*.rc -o -name \*.ui -o -name \*.kcfg`
if [ -n "$lst" ] ; then
$EXTRACTRC $lst >> rc.cpp
fi
# Extract strings from all source files.
# If your framework depends on KI18n, use $XGETTEXT. If it uses Qt translation
# system, use $EXTRACT_TR_STRINGS.
$XGETTEXT `find . -name \*.cpp -o -name \*.h -o -name \*.qml` -o $podir/kdeclarative6.pot
@@ -0,0 +1,51 @@
set(calendar-integration_SRCS
calendareventsplugin.cpp
calendareventsplugin.h
eventdata_p.cpp
)
add_library(KF6CalendarEvents ${calendar-integration_SRCS})
add_library(KF6::CalendarEvents ALIAS KF6CalendarEvents)
set_target_properties(KF6CalendarEvents PROPERTIES
VERSION ${KDECLARATIVE_VERSION}
SOVERSION ${KDECLARATIVE_SOVERSION}
EXPORT_NAME CalendarEvents
)
ecm_generate_export_header(KF6CalendarEvents
BASE_NAME CalendarEvents
GROUP_BASE_NAME KF
VERSION ${KF_VERSION}
USE_VERSION_HEADER
VERSION_BASE_NAME KDeclarative
DEPRECATED_BASE_VERSION 0
)
target_link_libraries(KF6CalendarEvents PUBLIC Qt6::Core)
target_include_directories(KF6CalendarEvents
INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KDeclarative>"
PUBLIC "$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>" # module version header
)
install(TARGETS KF6CalendarEvents EXPORT KF6DeclarativeTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
ecm_generate_headers(CalendarEvents_CamelCase_HEADERS
HEADER_NAMES
CalendarEventsPlugin
PREFIX CalendarEvents
REQUIRED_HEADERS CalendarEvents_HEADERS
)
install(FILES ${CalendarEvents_CamelCase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KDeclarative/CalendarEvents COMPONENT Devel)
install (FILES
${CalendarEvents_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/calendarevents_export.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KDeclarative/calendarevents COMPONENT Devel
)
# make available to ecm_add_qch in parent folder
set(CalendarEvents_QCH_SOURCES ${CalendarEvents_HEADERS} PARENT_SCOPE)
set(CalendarEvents_QCH_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
@@ -0,0 +1,6 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(Qt6Core @REQUIRED_QT_VERSION@)
include("${CMAKE_CURRENT_LIST_DIR}/CalendarEventsTargets.cmake")
@@ -0,0 +1,24 @@
/*
SPDX-FileCopyrightText: 2015 Martin Klapetek <mklapetek@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "calendareventsplugin.h"
namespace CalendarEvents
{
CalendarEventsPlugin::CalendarEventsPlugin(QObject *parent)
: QObject(parent)
{
}
CalendarEventsPlugin::~CalendarEventsPlugin()
{
}
ShowEventInterface::~ShowEventInterface() = default;
}
#include "moc_calendareventsplugin.cpp"
@@ -0,0 +1,296 @@
/*
SPDX-FileCopyrightText: 2015 Martin Klapetek <mklapetek@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef CALENDAREVENTSPLUGIN_H
#define CALENDAREVENTSPLUGIN_H
#include <QCalendar>
#include <QDateTime>
#include <QMultiHash>
#include <QObject>
#include <QSharedDataPointer>
#include "calendarevents_export.h"
/**
* The CalendarEvents namespace.
*/
namespace CalendarEvents
{
/**
* @class EventData calendareventsplugin.h <CalendarEvents/CalendarEventsPlugin>
*
* Data about an event.
*/
class CALENDAREVENTS_EXPORT EventData
{
public:
enum EventType {
Holiday, // Any holiday
Event, // General event
Todo, // A Todo item
};
EventData();
EventData(const EventData &other);
~EventData();
EventData &operator=(const EventData &other);
/**
* The start date and time of this event
*/
QDateTime startDateTime() const;
/**
* Set the start date-time of this event
*
* @param startDateTime the date-time of when the event is starting
*/
void setStartDateTime(const QDateTime &startDateTime);
/**
* The end date and time of this event
*/
QDateTime endDateTime() const;
/**
* Set the end date-time of this event
*
* @param endDateTime the date-time of when the event is ending
*/
void setEndDateTime(const QDateTime &endDateTime);
/**
* If true, this event goes on the whole day (eg. a holiday)
*/
bool isAllDay() const;
/**
* If set to true, it will be displayed in the Calendar agenda
* without any time besides it, marked as "going on all day"
*
* This is useful for single-day events only, for multiple-day
* events, leave to false (default)
*
* @param isAllDay set to true if the event takes all day, false otherwise
* (defaults to false)
*/
void setIsAllDay(bool isAllDay);
/**
* If true, this event won't mark the day in the calendar grid
* The main purpose for this flag is to support
* namedays, where in some countries the calendars have
* different name in them every day. This is just a minor holiday
* and as such should not mark the calendar grid, otherwise
* the whole grid would be in a different color.
*/
bool isMinor() const;
/**
* If set to true, it won't be marked in the calendar grid
*
* @param isMinor true if it's a minor event (like a nameday holiday),
* false otherwise (defaults to false)
*/
void setIsMinor(bool isMinor);
/**
* Event title
*/
QString title() const;
/**
* Sets the title of the event
*
* @param title The event title
*/
void setTitle(const QString &title);
/**
* Event description, can provide more details about the event
*/
QString description() const;
/**
* Sets the event description, which allows to add more details
* about this event
*
* @param description The description
*/
void setDescription(const QString &description);
/**
* Type of the current event, eg. a holiday, an event or a todo item
*/
EventType type() const;
/**
* Sets the event type, eg. a holiday, an event or a todo item
*
* @param type The event type,
*/
void setEventType(EventType type);
/**
* The color that should be used to mark this event with
* It comes in the HTML hex format, eg. #AARRGGBB or #RRGGBB
*/
QString eventColor() const;
/**
* This is to support various calendar colors the user might
* have configured elsewhere
*
* @param color The color for this event in the HTML hex format
* eg. #AARRGGBB or #RRGGBB (this is passed directly
* to QML)
*/
void setEventColor(const QString &color);
/**
* Unique ID of the event
*/
QString uid() const;
/**
* Sets the uid of the event
*
* This is a mandatory field only if you want to use
* the eventModified/eventRemoved signals, otherwise
* setting it is optional
*
* @param uid A unique id, recommended is to use the plugin name as prefix (to keep it unique)
*/
void setUid(const QString &uid);
private:
class Private;
QSharedDataPointer<Private> d;
};
/**
* @class CalendarEventsPlugin calendareventsplugin.h <CalendarEvents/CalendarEventsPlugin>
*
* Plugin for feeding events to a calendar instance.
*/
class CALENDAREVENTS_EXPORT CalendarEventsPlugin : public QObject
{
Q_OBJECT
public:
enum class SubLabelPriority {
Low, /**< Usually used in alternate calendars */
Default, /**< For holidays or normal events */
High, /**< For flagged or marked events */
Urgent,
};
Q_ENUM(SubLabelPriority)
explicit CalendarEventsPlugin(QObject *parent = nullptr);
~CalendarEventsPlugin() override;
/**
* When this is called, the plugin should load all events data
* between those two date ranges. Once the data are ready, it should
* just emit the dataReady() signal. The range is usually one month
*
* @param startDate the start of the range
* @param endDate the end of the range
*/
virtual void loadEventsForDateRange(const QDate &startDate, const QDate &endDate) = 0;
struct SubLabel {
QString label; /**< The label will be displayed in the tooltip or beside the full date */
QString yearLabel; /**< The label will be displayed under the year number */
QString monthLabel; /**< The label will be displayed under the month number */
QString dayLabel; /**< The label will be displayed under the day number */
SubLabelPriority priority = SubLabelPriority::Default; /**< The display priority of the sublabel */
};
Q_SIGNALS:
/**
* Emitted when the plugin has loaded the events data
*
* @param data A hash containing a QDate key for the event
* in the value, CalendarEvents::EventData, which holds all
* the details for the given event
* It's a multihash as there can be multiple events
* in the same day
* For multi-day events, insert just one with the key
* being the startdate of the event
*/
void dataReady(const QMultiHash<QDate, CalendarEvents::EventData> &data);
/**
* Should be emitted when there is a modification of an event
* that was previously returned via the dataReady() signal
*
* @param event The modified event data
*/
void eventModified(const CalendarEvents::EventData &modifiedEvent);
/**
* Should be emitted when the plugin removes some event
* from its collection
*
* @param uid The uid of the event that was removed
*/
void eventRemoved(const QString &uid);
/**
* Emitted when the plugin has loaded the alternate dates
*
* @param data A hash containing a QDate key from Gregorian calendar
* for the alternate date in the value, QDate.
* @since 6.0
*/
void alternateCalendarDateReady(const QHash<QDate, QCalendar::YearMonthDay> &data);
/**
* Emitted when the plugin has loaded the sublabels
*
* @param data A hash containing a QDate key for the sublabels
* in the value, SubLabel.
* @since 5.95
*/
void subLabelReady(const QHash<QDate, SubLabel> &data);
};
/**
* @class ShowEventInterface calendareventsplugin.h <CalendarEvents/CalendarEventsPlugin>
*
* Interface for displaying event details
*
* ShowEventInterface is an additional interface the CalendarEventsPlugin
* implementations can implement if they support displaying details about
* events (e.g. opening the event in KOrganizer).
*
* @since 5.61
*/
class CALENDAREVENTS_EXPORT ShowEventInterface
{
public:
virtual ~ShowEventInterface();
/**
* When this is called, the plugin should show a window displaying the
* full preview of the event.
*
* The plugin should return true if the event details can be displayed, false
* otherwise.
*/
virtual bool showEvent(const QString &uid) = 0;
};
}
Q_DECLARE_INTERFACE(CalendarEvents::CalendarEventsPlugin, "org.kde.CalendarEventsPlugin")
Q_DECLARE_INTERFACE(CalendarEvents::ShowEventInterface, "org.kde.CalendarEventsPlugin.ShowEventInterface")
#endif
@@ -0,0 +1,164 @@
/*
SPDX-FileCopyrightText: 2015 Martin Klapetek <mklapetek@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "calendareventsplugin.h"
#include <QSharedData>
class CalendarEvents::EventData::Private : public QSharedData
{
public:
Private()
: isAllDay(false)
, isMinor(false)
{
}
Private(const Private &other)
: QSharedData(other)
{
startDateTime = other.startDateTime;
endDateTime = other.endDateTime;
title = other.title;
description = other.description;
uid = other.uid;
eventColor = other.eventColor;
type = other.type;
isAllDay = other.isAllDay;
isMinor = other.isMinor;
}
~Private()
{
}
QDateTime startDateTime; // Start of the event
QDateTime endDateTime; // End of the event
QString title; // Title of the event
QString description; // Additional info of the event
QString uid; // An internal event id, useful mostly just for the eventModified/Removed signals
QString eventColor; // Optional color of the event in the HTML hex format, eg. #AARRGGBB or #RRGGBB
EventType type; // Type of the event
bool isAllDay; // True if the event takes all day, then it won't be displayed with any time
bool isMinor; // A minor holiday that will not create a colored entry in the calendar
};
//---------------------------------------------------
namespace CalendarEvents
{
EventData::EventData()
: d(new Private())
{
}
EventData::EventData(const EventData &other)
: d(other.d)
{
}
EventData::~EventData()
{
}
EventData &EventData::operator=(const EventData &other)
{
if (this == &other) {
return *this;
}
d = other.d;
return *this;
}
QDateTime EventData::startDateTime() const
{
return d->startDateTime;
}
void EventData::setStartDateTime(const QDateTime &startDateTime)
{
d->startDateTime = startDateTime;
}
QDateTime EventData::endDateTime() const
{
return d->endDateTime;
}
void EventData::setEndDateTime(const QDateTime &endDateTime)
{
d->endDateTime = endDateTime;
}
bool EventData::isAllDay() const
{
return d->isAllDay;
}
void EventData::setIsAllDay(bool isAllDay)
{
d->isAllDay = isAllDay;
}
bool EventData::isMinor() const
{
return d->isMinor;
}
void EventData::setIsMinor(bool isMinor)
{
d->isMinor = isMinor;
}
QString EventData::title() const
{
return d->title;
}
void EventData::setTitle(const QString &title)
{
d->title = title;
}
QString EventData::description() const
{
return d->description;
}
void EventData::setDescription(const QString &description)
{
d->description = description;
}
QString EventData::uid() const
{
return d->uid;
}
void EventData::setUid(const QString &uid)
{
d->uid = uid;
}
EventData::EventType EventData::type() const
{
return d->type;
}
void EventData::setEventType(EventData::EventType type)
{
d->type = type;
}
QString EventData::eventColor() const
{
return d->eventColor;
}
void EventData::setEventColor(const QString &color)
{
d->eventColor = color;
}
}
@@ -0,0 +1,4 @@
add_subdirectory(draganddrop)
add_subdirectory(kquickcontrolsaddons)
add_subdirectory(graphicaleffects)
add_subdirectory(kquickcontrols)
@@ -0,0 +1,25 @@
ecm_add_qml_module(draganddropplugin URI org.kde.draganddrop VERSION 2.0)
target_sources(draganddropplugin PRIVATE
DeclarativeDragArea.cpp
DeclarativeDragArea.h
DeclarativeDragDropEvent.cpp
DeclarativeDragDropEvent.h
DeclarativeDropArea.cpp
DeclarativeDropArea.h
DeclarativeMimeData.cpp
DeclarativeMimeData.h
draganddropplugin.cpp
draganddropplugin.h
MimeDataWrapper.cpp
MimeDataWrapper.h
)
target_link_libraries(draganddropplugin PRIVATE
Qt6::Core
Qt6::Quick
Qt6::Qml
Qt6::Gui
)
ecm_finalize_qml_module(draganddropplugin)
@@ -0,0 +1,370 @@
/*
SPDX-FileCopyrightText: 2010 BetterInbox <contact@betterinbox.com>
SPDX-FileContributor: Gregory Schlomoff <greg@betterinbox.com>
SPDX-License-Identifier: MIT
*/
#include "DeclarativeDragArea.h"
#include <QDrag>
#include <QGuiApplication>
#include <QIcon>
#include <QMimeData>
#include <QMouseEvent>
#include <QPainter>
#include <QQuickItemGrabResult>
#include <QQuickWindow>
#include <QStyleHints>
#include <QDebug>
/*!
A DragArea is used to make an item draggable.
*/
DeclarativeDragArea::DeclarativeDragArea(QQuickItem *parent)
: QQuickItem(parent)
, m_delegate(nullptr)
, m_source(parent)
, m_target(nullptr)
, m_enabled(true)
, m_draggingJustStarted(false)
, m_dragActive(false)
, m_supportedActions(Qt::MoveAction)
, m_defaultAction(Qt::MoveAction)
, m_data(new DeclarativeMimeData()) // m_data is owned by us, and we shouldn't pass it to Qt directly
// as it will automatically delete it after the drag and drop.
, m_pressAndHoldTimerId(0)
{
m_startDragDistance = QGuiApplication::styleHints()->startDragDistance();
setAcceptedMouseButtons(Qt::LeftButton);
// setFiltersChildEvents(true);
setFlag(ItemAcceptsDrops, m_enabled);
setFiltersChildMouseEvents(true);
}
DeclarativeDragArea::~DeclarativeDragArea()
{
if (m_data) {
delete m_data;
}
}
/*!
The delegate is the item that will be displayed next to the mouse cursor during the drag and drop operation.
It usually consists of a large, semi-transparent icon representing the data being dragged.
*/
QQuickItem *DeclarativeDragArea::delegate() const
{
return m_delegate;
}
void DeclarativeDragArea::setDelegate(QQuickItem *delegate)
{
if (m_delegate != delegate) {
// qDebug() << " ______________________________________________ " << delegate;
m_delegate = delegate;
Q_EMIT delegateChanged();
}
}
void DeclarativeDragArea::resetDelegate()
{
setDelegate(nullptr);
}
/*!
The QML element that is the source of this drag and drop operation. This can be defined to any item, and will
be available to the DropArea as event.data.source
*/
QQuickItem *DeclarativeDragArea::source() const
{
return m_source;
}
void DeclarativeDragArea::setSource(QQuickItem *source)
{
if (m_source != source) {
m_source = source;
Q_EMIT sourceChanged();
}
}
void DeclarativeDragArea::resetSource()
{
setSource(nullptr);
}
bool DeclarativeDragArea::dragActive() const
{
return m_dragActive;
}
// target
QQuickItem *DeclarativeDragArea::target() const
{
// TODO: implement me
return nullptr;
}
// data
DeclarativeMimeData *DeclarativeDragArea::mimeData() const
{
return m_data;
}
// startDragDistance
int DeclarativeDragArea::startDragDistance() const
{
return m_startDragDistance;
}
void DeclarativeDragArea::setStartDragDistance(int distance)
{
if (distance == m_startDragDistance) {
return;
}
m_startDragDistance = distance;
Q_EMIT startDragDistanceChanged();
}
// delegateImage
QVariant DeclarativeDragArea::delegateImage() const
{
return m_delegateImage;
}
void DeclarativeDragArea::setDelegateImage(const QVariant &image)
{
if (image.canConvert<QImage>() && image.value<QImage>() == m_delegateImage) {
return;
}
if (image.canConvert<QImage>()) {
m_delegateImage = image.value<QImage>();
} else if (image.canConvert<QString>()) {
m_delegateImage = QIcon::fromTheme(image.toString()).pixmap(QSize(48, 48)).toImage();
} else {
m_delegateImage = image.value<QIcon>().pixmap(QSize(48, 48)).toImage();
}
Q_EMIT delegateImageChanged();
}
// enabled
bool DeclarativeDragArea::isEnabled() const
{
return m_enabled;
}
void DeclarativeDragArea::setEnabled(bool enabled)
{
if (enabled != m_enabled) {
m_enabled = enabled;
Q_EMIT enabledChanged();
}
}
// supported actions
Qt::DropActions DeclarativeDragArea::supportedActions() const
{
return m_supportedActions;
}
void DeclarativeDragArea::setSupportedActions(Qt::DropActions actions)
{
if (actions != m_supportedActions) {
m_supportedActions = actions;
Q_EMIT supportedActionsChanged();
}
}
// default action
Qt::DropAction DeclarativeDragArea::defaultAction() const
{
return m_defaultAction;
}
void DeclarativeDragArea::setDefaultAction(Qt::DropAction action)
{
if (action != m_defaultAction) {
m_defaultAction = action;
Q_EMIT defaultActionChanged();
}
}
void DeclarativeDragArea::mousePressEvent(QMouseEvent *event)
{
m_pressAndHoldTimerId = startTimer(QGuiApplication::styleHints()->mousePressAndHoldInterval());
m_buttonDownPos = event->globalPosition();
m_draggingJustStarted = true;
setKeepMouseGrab(true);
}
void DeclarativeDragArea::mouseReleaseEvent(QMouseEvent *event)
{
Q_UNUSED(event);
killTimer(m_pressAndHoldTimerId);
m_pressAndHoldTimerId = 0;
m_draggingJustStarted = false;
setKeepMouseGrab(false);
ungrabMouse();
}
void DeclarativeDragArea::timerEvent(QTimerEvent *event)
{
if (event->timerId() == m_pressAndHoldTimerId && m_draggingJustStarted && m_enabled) {
// Grab delegate before starting drag
if (m_delegate) {
// Another grab is already in progress
if (m_grabResult) {
return;
}
m_grabResult = m_delegate->grabToImage();
if (m_grabResult) {
connect(m_grabResult.data(), &QQuickItemGrabResult::ready, this, [this]() {
startDrag(m_grabResult->image());
m_grabResult.reset();
});
return;
}
}
// No delegate or grab failed, start drag immediately
startDrag(m_delegateImage);
}
}
void DeclarativeDragArea::mouseMoveEvent(QMouseEvent *event)
{
if (!m_enabled || QLineF(event->globalPosition(), m_buttonDownPos).length() < m_startDragDistance) {
return;
}
// don't start drags on move for touch events, they'll be handled only by press and hold
// reset timer if moved more than m_startDragDistance
if (event->source() == Qt::MouseEventSynthesizedByQt) {
killTimer(m_pressAndHoldTimerId);
m_pressAndHoldTimerId = 0;
return;
}
if (m_draggingJustStarted) {
// Grab delegate before starting drag
if (m_delegate) {
// Another grab is already in progress
if (m_grabResult) {
return;
}
m_grabResult = m_delegate->grabToImage();
if (m_grabResult) {
connect(m_grabResult.data(), &QQuickItemGrabResult::ready, this, [this]() {
startDrag(m_grabResult->image());
m_grabResult.reset();
});
return;
}
}
// No delegate or grab failed, start drag immediately
startDrag(m_delegateImage);
}
}
bool DeclarativeDragArea::childMouseEventFilter(QQuickItem *item, QEvent *event)
{
if (!isEnabled()) {
return false;
}
switch (event->type()) {
case QEvent::MouseButtonPress: {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
// qDebug() << "press in dragarea";
mousePressEvent(me);
break;
}
case QEvent::MouseMove: {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
// qDebug() << "move in dragarea";
mouseMoveEvent(me);
break;
}
case QEvent::MouseButtonRelease: {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
// qDebug() << "release in dragarea";
mouseReleaseEvent(me);
break;
}
default:
break;
}
return QQuickItem::childMouseEventFilter(item, event);
}
void DeclarativeDragArea::startDrag(const QImage &image)
{
grabMouse();
m_draggingJustStarted = false;
QDrag *drag = new QDrag(parent());
DeclarativeMimeData *dataCopy = new DeclarativeMimeData(m_data); // Qt will take ownership of this copy and delete it.
drag->setMimeData(dataCopy);
const qreal devicePixelRatio = window() ? window()->devicePixelRatio() : 1;
const int imageSize = 48 * devicePixelRatio;
if (!image.isNull()) {
drag->setPixmap(QPixmap::fromImage(image));
} else if (mimeData()->hasImage()) {
const QImage im = qvariant_cast<QImage>(mimeData()->imageData());
drag->setPixmap(QPixmap::fromImage(im));
} else if (mimeData()->hasColor()) {
QPixmap px(imageSize, imageSize);
px.fill(mimeData()->color());
drag->setPixmap(px);
} else {
// Icons otherwise
QStringList icons;
if (mimeData()->hasText()) {
icons << QStringLiteral("text-plain");
}
if (mimeData()->hasHtml()) {
icons << QStringLiteral("text-html");
}
if (mimeData()->hasUrls()) {
for (int i = 0; i < std::min<int>(4, mimeData()->urls().size()); ++i) {
icons << QStringLiteral("text-html");
}
}
if (!icons.isEmpty()) {
QPixmap pm(imageSize * icons.count(), imageSize);
pm.fill(Qt::transparent);
QPainter p(&pm);
int i = 0;
for (const QString &ic : std::as_const(icons)) {
p.drawPixmap(QPoint(i * imageSize, 0), QIcon::fromTheme(ic).pixmap(imageSize));
i++;
}
p.end();
drag->setPixmap(pm);
}
}
// drag->setHotSpot(QPoint(drag->pixmap().width()/2, drag->pixmap().height()/2)); // TODO: Make a property for that
// setCursor(Qt::OpenHandCursor); //TODO? Make a property for the cursor
m_dragActive = true;
Q_EMIT dragActiveChanged();
Q_EMIT dragStarted();
Qt::DropAction action = drag->exec(m_supportedActions, m_defaultAction);
setKeepMouseGrab(false);
m_dragActive = false;
Q_EMIT dragActiveChanged();
Q_EMIT drop(action);
ungrabMouse();
}
#include "moc_DeclarativeDragArea.cpp"
@@ -0,0 +1,156 @@
/*
SPDX-FileCopyrightText: 2010 BetterInbox <contact@betterinbox.com>
SPDX-FileContributor: Gregory Schlomoff <greg@betterinbox.com>
SPDX-License-Identifier: MIT
*/
#ifndef DECLARATIVEDRAGAREA_H
#define DECLARATIVEDRAGAREA_H
#include "DeclarativeMimeData.h"
#include <QImage>
#include <QQuickItem>
#include <QSharedPointer>
class QQmlComponent;
class QQuickItemGrabResult;
class DeclarativeDragArea : public QQuickItem
{
Q_OBJECT
/**
* The delegate is the item that will be displayed next to the mouse cursor during the drag and drop operation.
* It usually consists of a large, semi-transparent icon representing the data being dragged.
*/
Q_PROPERTY(QQuickItem *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged RESET resetDelegate)
/**
* The QML element that is the source of the resulting drag and drop operation. This can be defined to any item, and will
* be available to the DropArea as event.data.source
*/
Q_PROPERTY(QQuickItem *source READ source WRITE setSource NOTIFY sourceChanged RESET resetSource)
// TODO: to be implemented
Q_PROPERTY(QQuickItem *target READ source NOTIFY targetChanged)
/**
* the mime data of the drag operation
* @see DeclarativeMimeData
*/
Q_PROPERTY(DeclarativeMimeData *mimeData READ mimeData CONSTANT)
/**
* If false no drag operation will be generate
*/
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) // TODO: Should call setAcceptDrops()
/**
* Supported operations, a combination of
* Qt.CopyAction
* Qt.MoveAction
* Qt.LinkAction
* Qt.ActionMask
* Qt.IgnoreAction
* Qt.TargetMoveAction
*/
Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions WRITE setSupportedActions NOTIFY supportedActionsChanged)
/**
* The default action will be performed during a drag when no modificators are pressed.
*/
Q_PROPERTY(Qt::DropAction defaultAction READ defaultAction WRITE setDefaultAction NOTIFY defaultActionChanged)
/**
* distance in pixel after which a drag event will get started
*/
Q_PROPERTY(int startDragDistance READ startDragDistance WRITE setStartDragDistance NOTIFY startDragDistanceChanged)
/**
* an image to be used as delegate. if present overrides the delegate property. in can be either a QImage or a QIcon
*/
Q_PROPERTY(QVariant delegateImage READ delegateImage WRITE setDelegateImage NOTIFY delegateImageChanged)
/**
* Whether a drag currently originates from this drag area.
*
* @since 5.19
*/
Q_PROPERTY(bool dragActive READ dragActive NOTIFY dragActiveChanged)
public:
DeclarativeDragArea(QQuickItem *parent = nullptr);
~DeclarativeDragArea() override;
QQuickItem *delegate() const;
void setDelegate(QQuickItem *delegate);
void resetDelegate();
QVariant delegateImage() const;
void setDelegateImage(const QVariant &image);
QQuickItem *target() const;
QQuickItem *source() const;
void setSource(QQuickItem *source);
void resetSource();
bool dragActive() const;
bool isEnabled() const;
void setEnabled(bool enabled);
int startDragDistance() const;
void setStartDragDistance(int distance);
// supported actions
Qt::DropActions supportedActions() const;
void setSupportedActions(Qt::DropActions actions);
// default action
Qt::DropAction defaultAction() const;
void setDefaultAction(Qt::DropAction action);
DeclarativeMimeData *mimeData() const;
Q_SIGNALS:
void dragStarted();
void delegateChanged();
void dragActiveChanged();
void sourceChanged();
void targetChanged();
void dataChanged();
void enabledChanged();
void drop(int action);
void supportedActionsChanged();
void defaultActionChanged();
void startDragDistanceChanged();
void delegateImageChanged();
protected:
void mouseMoveEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *) override;
void timerEvent(QTimerEvent *event) override;
bool childMouseEventFilter(QQuickItem *item, QEvent *event) override;
private:
void startDrag(const QImage &image);
QQuickItem *m_delegate;
QQuickItem *m_source;
QQuickItem *m_target;
QSharedPointer<QQuickItemGrabResult> m_grabResult;
bool m_enabled;
bool m_draggingJustStarted;
bool m_dragActive;
Qt::DropActions m_supportedActions;
Qt::DropAction m_defaultAction;
DeclarativeMimeData *const m_data;
QImage m_delegateImage;
int m_startDragDistance;
QPointF m_buttonDownPos;
int m_pressAndHoldTimerId;
};
#endif // DECLARATIVEDRAGAREA_H
@@ -0,0 +1,57 @@
/*
SPDX-FileCopyrightText: 2010 BetterInbox <contact@betterinbox.com>
SPDX-FileContributor: Gregory Schlomoff <greg@betterinbox.com>
SPDX-FileCopyrightText: 2013 Sebastian Kügler <sebas@kde.org>
SPDX-License-Identifier: MIT
*/
#include "DeclarativeDragDropEvent.h"
DeclarativeDragDropEvent::DeclarativeDragDropEvent(QDropEvent *e, DeclarativeDropArea *parent)
: QObject(parent)
, m_x(e->position().x())
, m_y(e->position().y())
, m_buttons(e->buttons())
, m_modifiers(e->modifiers())
, m_data(nullptr)
, m_event(e)
{
}
DeclarativeDragDropEvent::DeclarativeDragDropEvent(QDragLeaveEvent *e, DeclarativeDropArea *parent)
: QObject(parent)
, m_x(0)
, m_y(0)
, m_buttons(Qt::NoButton)
, m_modifiers(Qt::NoModifier)
, m_data(nullptr)
, m_event(nullptr)
{
Q_UNUSED(e);
}
void DeclarativeDragDropEvent::accept(int action)
{
m_event->setDropAction(static_cast<Qt::DropAction>(action));
// qDebug() << "-----> Accepting event: " << this << m_data.urls() << m_data.text() << m_data.html() << ( m_data.hasColor() ? m_data.color().name() : "
// no color");
m_event->accept();
}
void DeclarativeDragDropEvent::ignore()
{
m_event->ignore();
}
DeclarativeMimeData *DeclarativeDragDropEvent::mimeData()
{
if (!m_data && m_event) {
// TODO This should be using MimeDataWrapper eventually, although this is an API break,
// so will need to be done carefully.
m_data.reset(new DeclarativeMimeData(m_event->mimeData()));
}
return m_data.data();
}
#include "moc_DeclarativeDragDropEvent.cpp"
@@ -0,0 +1,121 @@
/*
SPDX-FileCopyrightText: 2010 BetterInbox <contact@betterinbox.com>
SPDX-FileContributor: Gregory Schlomoff <greg@betterinbox.com>
SPDX-License-Identifier: MIT
*/
#ifndef DECLARATIVEDRAGDROPEVENT_H
#define DECLARATIVEDRAGDROPEVENT_H
#include "DeclarativeDropArea.h"
#include "DeclarativeMimeData.h"
#include <QObject>
class DeclarativeDragDropEvent : public QObject
{
Q_OBJECT
/**
* The mouse X position of the event relative to the DropArea that is receiving the event.
*/
Q_PROPERTY(int x READ x)
/**
* The mouse Y position of the event relative to the DropArea that is receiving the event.
*/
Q_PROPERTY(int y READ y)
/**
* The pressed mouse buttons.
* A combination of:
* Qt.NoButton The button state does not refer to any button (see QMouseEvent::button()).
* Qt.LeftButton The left button is pressed, or an event refers to the left button. (The left button may be the right button on left-handed mice.)
* Qt.RightButton The right button.
* Qt.MidButton The middle button.
* Qt.MiddleButton MidButton The middle button.
* Qt.XButton1 The first X button.
* Qt.XButton2 The second X button.
*/
Q_PROPERTY(int buttons READ buttons)
/**
* Pressed keyboard modifiers, a combination of:
* Qt.NoModifier No modifier key is pressed.
* Qt.ShiftModifier A Shift key on the keyboard is pressed.
* Qt.ControlModifier A Ctrl key on the keyboard is pressed.
* Qt.AltModifier An Alt key on the keyboard is pressed.
* Qt.MetaModifier A Meta key on the keyboard is pressed.
* Qt.KeypadModifier A keypad button is pressed.
* Qt.GroupSwitchModifier X11 only. A Mode_switch key on the keyboard is pressed.
*/
Q_PROPERTY(int modifiers READ modifiers)
/**
* The mime data of this operation
* @see DeclarativeMimeData
*/
Q_PROPERTY(DeclarativeMimeData *mimeData READ mimeData)
/**
* The possible different kind of action that can be done in the drop, is a combination of:
* Qt.CopyAction 0x1 Copy the data to the target.
* Qt.MoveAction 0x2 Move the data from the source to the target.
* Qt.LinkAction 0x4 Create a link from the source to the target.
* Qt.ActionMask 0xff
* Qt.IgnoreAction 0x0 Ignore the action (do nothing with the data).
* Qt.TargetMoveAction 0x8002 On Windows, this value is used when the ownership of the D&D data should be taken over by the target application, i.e., the
* source application should not delete the data. On X11 this value is used to do a move. TargetMoveAction is not used on the Mac.
*/
Q_PROPERTY(Qt::DropActions possibleActions READ possibleActions)
/**
* Default action
* @see possibleActions
*/
Q_PROPERTY(Qt::DropAction proposedAction READ proposedAction)
public:
DeclarativeDragDropEvent(QDropEvent *e, DeclarativeDropArea *parent = nullptr);
DeclarativeDragDropEvent(QDragLeaveEvent *e, DeclarativeDropArea *parent = nullptr);
int x() const
{
return m_x;
}
int y() const
{
return m_y;
}
int buttons() const
{
return m_buttons;
}
int modifiers() const
{
return m_modifiers;
}
DeclarativeMimeData *mimeData();
Qt::DropAction proposedAction() const
{
return m_event->proposedAction();
}
Qt::DropActions possibleActions() const
{
return m_event->possibleActions();
}
public Q_SLOTS:
void accept(int action);
void ignore();
private:
int m_x;
int m_y;
Qt::MouseButtons m_buttons;
Qt::KeyboardModifiers m_modifiers;
QScopedPointer<DeclarativeMimeData> m_data;
QDropEvent *m_event;
};
#endif // DECLARATIVEDRAGDROPEVENT_H
@@ -0,0 +1,150 @@
/*
SPDX-FileCopyrightText: 2010 BetterInbox <contact@betterinbox.com>
SPDX-FileContributor: Gregory Schlomoff <greg@betterinbox.com>
SPDX-License-Identifier: MIT
*/
#include "DeclarativeDropArea.h"
#include "DeclarativeDragDropEvent.h"
DeclarativeDropArea::DeclarativeDropArea(QQuickItem *parent)
: QQuickItem(parent)
, m_enabled(true)
, m_preventStealing(false)
, m_temporaryInhibition(false)
, m_containsDrag(false)
{
setFlag(ItemAcceptsDrops, m_enabled);
}
void DeclarativeDropArea::temporaryInhibitParent(bool inhibit)
{
QQuickItem *candidate = parentItem();
while (candidate) {
if (DeclarativeDropArea *da = qobject_cast<DeclarativeDropArea *>(candidate)) {
da->m_temporaryInhibition = inhibit;
if (inhibit) {
Q_EMIT da->dragLeaveEvent(nullptr);
}
}
candidate = candidate->parentItem();
}
}
void DeclarativeDropArea::dragEnterEvent(QDragEnterEvent *event)
{
if (!m_enabled || m_temporaryInhibition) {
return;
}
DeclarativeDragDropEvent dde(event, this);
event->accept();
Q_EMIT dragEnter(&dde);
if (!event->isAccepted()) {
return;
}
if (m_preventStealing) {
temporaryInhibitParent(true);
}
m_oldDragMovePos = event->position().toPoint();
setContainsDrag(true);
}
void DeclarativeDropArea::dragLeaveEvent(QDragLeaveEvent *event)
{
// do it anyways, in the unlikely case m_preventStealing
// was changed while drag
temporaryInhibitParent(false);
m_oldDragMovePos = QPoint(-1, -1);
DeclarativeDragDropEvent dde(event, this);
Q_EMIT dragLeave(&dde);
setContainsDrag(false);
}
void DeclarativeDropArea::dragMoveEvent(QDragMoveEvent *event)
{
if (!m_enabled || m_temporaryInhibition) {
event->ignore();
return;
}
event->accept();
// if the position we export didn't change, don't generate the move event
if (event->position() == m_oldDragMovePos) {
return;
}
m_oldDragMovePos = event->position().toPoint();
DeclarativeDragDropEvent dde(event, this);
Q_EMIT dragMove(&dde);
}
void DeclarativeDropArea::dropEvent(QDropEvent *event)
{
// do it anyways, in the unlikely case m_preventStealing
// was changed while drag, do it after a loop,
// so the parent dropevent doesn't get delivered
metaObject()->invokeMethod(this, "temporaryInhibitParent", Qt::QueuedConnection, Q_ARG(bool, false));
m_oldDragMovePos = QPoint(-1, -1);
if (!m_enabled || m_temporaryInhibition) {
return;
}
DeclarativeDragDropEvent dde(event, this);
Q_EMIT drop(&dde);
setContainsDrag(false);
}
bool DeclarativeDropArea::isEnabled() const
{
return m_enabled;
}
void DeclarativeDropArea::setEnabled(bool enabled)
{
if (enabled == m_enabled) {
return;
}
m_enabled = enabled;
setFlag(ItemAcceptsDrops, m_enabled);
Q_EMIT enabledChanged();
}
bool DeclarativeDropArea::preventStealing() const
{
return m_preventStealing;
}
void DeclarativeDropArea::setPreventStealing(bool prevent)
{
if (prevent == m_preventStealing) {
return;
}
m_preventStealing = prevent;
Q_EMIT preventStealingChanged();
}
void DeclarativeDropArea::setContainsDrag(bool dragging)
{
if (m_containsDrag != dragging) {
m_containsDrag = dragging;
Q_EMIT containsDragChanged(m_containsDrag);
}
}
bool DeclarativeDropArea::containsDrag() const
{
return m_containsDrag;
}
#include "moc_DeclarativeDropArea.cpp"
@@ -0,0 +1,95 @@
/*
SPDX-FileCopyrightText: 2010 BetterInbox <contact@betterinbox.com>
SPDX-FileContributor: Gregory Schlomoff <greg@betterinbox.com>
SPDX-License-Identifier: MIT
*/
#ifndef DECLARATIVEDROPAREA_H
#define DECLARATIVEDROPAREA_H
#include <QQuickItem>
class DeclarativeDragDropEvent;
class DeclarativeDropArea : public QQuickItem
{
Q_OBJECT
/**
* If false the area will receive no drop events
*/
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
/**
*
*/
Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged)
Q_PROPERTY(bool containsDrag READ containsDrag NOTIFY containsDragChanged)
public:
DeclarativeDropArea(QQuickItem *parent = nullptr);
bool isEnabled() const;
void setEnabled(bool enabled);
bool preventStealing() const;
void setPreventStealing(bool prevent);
bool containsDrag() const;
Q_SIGNALS:
/**
* Emitted when the mouse cursor dragging something enters in the drag area
* @param event description of the dragged content
* @see DeclarativeDragDropEvent
*/
void dragEnter(DeclarativeDragDropEvent *event);
/**
* Emitted when the mouse cursor dragging something leaves the drag area
* @param event description of the dragged content
* @see DeclarativeDragDropEvent
*/
void dragLeave(DeclarativeDragDropEvent *event);
/**
* Emitted when the mouse cursor dragging something moves over the drag area
* @param event description of the dragged content
* @see DeclarativeDragDropEvent
*/
void dragMove(DeclarativeDragDropEvent *event);
/**
* Emitted when the user drops something in the area
* @param event description of the dragged content
* @see DeclarativeDragDropEvent
*/
void drop(DeclarativeDragDropEvent *event);
// Notifiers
void enabledChanged();
void preventStealingChanged();
void containsDragChanged(bool contained);
protected:
void dragEnterEvent(QDragEnterEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dropEvent(QDropEvent *event) override;
private Q_SLOTS:
void temporaryInhibitParent(bool inhibit);
private:
void setContainsDrag(bool dragging);
bool m_enabled : 1;
bool m_preventStealing : 1;
bool m_temporaryInhibition : 1;
bool m_containsDrag : 1;
QPoint m_oldDragMovePos;
};
#endif
@@ -0,0 +1,156 @@
/*
SPDX-FileCopyrightText: 2010 BetterInbox <contact@betterinbox.com>
SPDX-FileContributor: Gregory Schlomoff <greg@betterinbox.com>
SPDX-License-Identifier: MIT
*/
#include "DeclarativeMimeData.h"
/*!
\qmlclass MimeData DeclarativeMimeData
This is a wrapper class around QMimeData, with a few extensions to provide better support for in-qml drag & drops.
*/
DeclarativeMimeData::DeclarativeMimeData()
: QMimeData()
, m_source(nullptr)
{
}
/*!
\internal
\class DeclarativeMimeData
Creates a new DeclarativeMimeData by cloning the QMimeData passed as parameter.
This is useful for two reasons :
- In DragArea, we want to clone our "working copy" of the DeclarativeMimeData instance, as Qt will automatically
delete it after the drag and drop operation.
- In the drop events, the QMimeData is const, and we have troubles passing const to QML. So we clone it to
remove the "constness"
This method will try to cast the QMimeData to DeclarativeMimeData, and will clone our extensions to QMimeData as well
*/
DeclarativeMimeData::DeclarativeMimeData(const QMimeData *copy)
: QMimeData()
, m_source(nullptr)
{
// Copy the standard MIME data
const auto formats = copy->formats();
for (const QString &format : formats) {
QMimeData::setData(format, copy->data(format));
}
// If the object we are copying actually is a DeclarativeMimeData, copy our extended properties as well
const DeclarativeMimeData *declarativeMimeData = qobject_cast<const DeclarativeMimeData *>(copy);
if (declarativeMimeData) {
this->setSource(declarativeMimeData->source());
}
}
/*!
\qmlproperty url MimeData::url
Returns the first URL from the urls property of QMimeData
TODO: We should use QDeclarativeListProperty<QUrls> to return the whole list instead of only the first element.
*/
QUrl DeclarativeMimeData::url() const
{
if (this->hasUrls() && !this->urls().isEmpty()) {
return QMimeData::urls().constFirst();
}
return QUrl();
}
void DeclarativeMimeData::setUrl(const QUrl &url)
{
if (this->url() == url) {
return;
}
QList<QUrl> urlList;
urlList.append(url);
QMimeData::setUrls(urlList);
Q_EMIT urlChanged();
}
QJsonArray DeclarativeMimeData::urls() const
{
QJsonArray varUrls;
const auto lstUrls = QMimeData::urls();
for (const QUrl &url : lstUrls) {
varUrls.append(url.toString());
}
return varUrls;
}
void DeclarativeMimeData::setUrls(const QJsonArray &urls)
{
QList<QUrl> urlList;
urlList.reserve(urls.size());
for (const auto &varUrl : urls) {
urlList << QUrl(varUrl.toString());
}
QMimeData::setUrls(urlList);
Q_EMIT urlsChanged();
}
// color
QColor DeclarativeMimeData::color() const
{
if (this->hasColor()) {
return qvariant_cast<QColor>(this->colorData());
}
return QColor();
}
bool DeclarativeMimeData::hasColor() const
{
// qDebug() << " hasColor " << (QMimeData::hasColor() ? color().name() : "false");
return QMimeData::hasColor();
}
void DeclarativeMimeData::setColor(const QColor &color)
{
if (this->color() != color) {
this->setColorData(color);
Q_EMIT colorChanged();
}
}
void DeclarativeMimeData::setData(const QString &mimeType, const QVariant &data)
{
if (data.userType() == QMetaType::QByteArray) {
QMimeData::setData(mimeType, data.toByteArray());
} else if (data.canConvert<QString>()) {
QMimeData::setData(mimeType, data.toString().toLatin1());
}
}
/*!
\qmlproperty item MimeData::source
Setting source to any existing qml item will enable the receiver of the drag and drop operation to know in which item
the operation originated.
In the case of inter-application drag and drop operations, the source will not be available, and will be 0.
Be sure to test it in your QML code, before using it, or it will generate errors in the console.
*/
QQuickItem *DeclarativeMimeData::source() const
{
return m_source;
}
void DeclarativeMimeData::setSource(QQuickItem *source)
{
if (m_source != source) {
m_source = source;
Q_EMIT sourceChanged();
}
}
QByteArray DeclarativeMimeData::getDataAsByteArray(const QString &format)
{
return data(format);
}
#include "moc_DeclarativeMimeData.cpp"
@@ -0,0 +1,100 @@
/*
SPDX-FileCopyrightText: 2010 BetterInbox <contact@betterinbox.com>
SPDX-FileContributor: Gregory Schlomoff <greg@betterinbox.com>
SPDX-License-Identifier: MIT
*/
#ifndef DECLARATIVEMIMEDATA_H
#define DECLARATIVEMIMEDATA_H
#include <QColor>
#include <QJsonArray>
#include <QMimeData>
#include <QQuickItem>
#include <QUrl>
class DeclarativeMimeData : public QMimeData
{
Q_OBJECT
/**
* A plain text (MIME type text/plain) representation of the data.
*/
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
/**
* A string if the data stored in the object is HTML (MIME type text/html); otherwise returns an empty string.
*/
Q_PROPERTY(QString html READ html WRITE setHtml NOTIFY htmlChanged)
/**
* Url contained in the mimedata
*/
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
/**
* A list of URLs contained within the MIME data object.
* URLs correspond to the MIME type text/uri-list.
*/
Q_PROPERTY(QJsonArray urls READ urls WRITE setUrls NOTIFY urlsChanged)
/**
* A color if the data stored in the object represents a color (MIME type application/x-color); otherwise QColor().
*/
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
/**
* The graphical item on the scene that started the drag event. It may be null.
*/
Q_PROPERTY(QQuickItem *source READ source WRITE setSource NOTIFY sourceChanged)
/** @see QMimeData::hasUrls */
Q_PROPERTY(bool hasUrls READ hasUrls NOTIFY urlsChanged)
// TODO: Image property
/**
* @sa QMimeData::formats
*/
Q_PROPERTY(QStringList formats READ formats)
public:
DeclarativeMimeData();
DeclarativeMimeData(const QMimeData *copy);
QUrl url() const;
void setUrl(const QUrl &url);
QJsonArray urls() const;
void setUrls(const QJsonArray &urls);
QColor color() const;
void setColor(const QColor &color);
Q_INVOKABLE bool hasColor() const;
Q_INVOKABLE void setData(const QString &mimeType, const QVariant &data);
QQuickItem *source() const;
void setSource(QQuickItem *source);
Q_INVOKABLE QByteArray getDataAsByteArray(const QString &format);
/*
QString text() const; //TODO: Reimplement this to issue the onChanged signals
void setText(const QString &text);
QString html() const;
void setHtml(const QString &html);
*/
Q_SIGNALS:
void textChanged(); // FIXME not being used
void htmlChanged(); // FIXME not being used
void urlChanged();
void urlsChanged();
void colorChanged();
void sourceChanged();
private:
QQuickItem *m_source;
};
#endif // DECLARATIVEMIMEDATA_H
@@ -0,0 +1,15 @@
/** @mainpage Drag and Drop
<h2>import org.kde.draganddrop</h2>
Use those elements if you want to add drag and drop support to your application
- DeclarativeDragArea
- DeclarativeDropArea
- DeclarativeDragDropEvent
- DeclarativeMimeData
*/
// DOXYGEN_SET_PROJECT_NAME = DragAndDrop
// vim:ts=4:sw=4:expandtab:filetype=doxygen
@@ -0,0 +1,80 @@
/*
SPDX-FileCopyrightText: 2015 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: MIT
*/
#include "MimeDataWrapper.h"
#include <QMimeData>
#include <QUrl>
MimeDataWrapper::MimeDataWrapper(const QMimeData *data, QObject *parent)
: QObject(parent)
, m_data(data)
{
}
QString MimeDataWrapper::text() const
{
return m_data->text();
}
QString MimeDataWrapper::html() const
{
return m_data->html();
}
QUrl MimeDataWrapper::url() const
{
if (m_data->hasUrls() && !m_data->urls().isEmpty()) {
return m_data->urls().constFirst();
}
return QUrl();
}
bool MimeDataWrapper::hasUrls() const
{
return m_data->hasUrls();
}
QJsonArray MimeDataWrapper::urls() const
{
QJsonArray varUrls;
const auto urls = m_data->urls();
for (const QUrl &url : urls) {
varUrls.append(url.toString());
}
return varUrls;
}
QVariant MimeDataWrapper::color() const
{
if (m_data->hasColor()) {
return m_data->colorData();
} else {
return QVariant();
}
}
QStringList MimeDataWrapper::formats() const
{
return m_data->formats();
}
QByteArray MimeDataWrapper::getDataAsByteArray(const QString &format)
{
return m_data->data(format);
}
QVariant MimeDataWrapper::source() const
{
// In case it comes from a DeclarativeMimeData
return m_data->property("source");
}
QMimeData *MimeDataWrapper::mimeData() const
{
return const_cast<QMimeData *>(m_data);
}
#include "moc_MimeDataWrapper.cpp"
@@ -0,0 +1,92 @@
/*
SPDX-FileCopyrightText: 2015 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: MIT
*/
#ifndef MIMEDATAWRAPPER_H
#define MIMEDATAWRAPPER_H
#include <QJsonArray>
#include <QObject>
#include <QString>
class QMimeData;
class QObject;
/**
* Exposes a const QMimeData instance
*
* In contrast to DeclarativeMimeData, doesn't create a copy of the QMimeData instance
*/
class MimeDataWrapper : public QObject
{
Q_OBJECT
/**
* A plain text (MIME type text/plain) representation of the data.
*/
Q_PROPERTY(QString text READ text CONSTANT)
/**
* A string if the data stored in the object is HTML (MIME type text/html); otherwise returns an empty string.
*/
Q_PROPERTY(QString html READ html CONSTANT)
/**
* Url contained in the mimedata
*/
Q_PROPERTY(QUrl url READ url CONSTANT)
/**
* A list of URLs contained within the MIME data object.
* URLs correspond to the MIME type text/uri-list.
*/
Q_PROPERTY(QJsonArray urls READ urls CONSTANT)
/**
* A color if the data stored in the object represents a color (MIME type application/x-color); otherwise QVariant().
*/
Q_PROPERTY(QVariant color READ color CONSTANT)
/**
* The graphical item on the scene that started the drag event. It may be null.
*/
Q_PROPERTY(QVariant source READ source CONSTANT)
/**
* Mimetypes provided by the mime data instance
*
* @sa QMimeData::formats
*/
Q_PROPERTY(QStringList formats READ formats CONSTANT)
/**
* @sa QMimeData::hasUrls
*/
Q_PROPERTY(bool hasUrls READ hasUrls CONSTANT)
/**
* @returns the wrapped object
*/
Q_PROPERTY(QMimeData *mimeData READ mimeData CONSTANT)
public:
MimeDataWrapper(const QMimeData *data, QObject *parent);
QString text() const;
QString html() const;
QUrl url() const;
QJsonArray urls() const;
bool hasUrls() const;
QVariant color() const;
QStringList formats() const;
QVariant source() const;
QMimeData *mimeData() const;
Q_INVOKABLE QByteArray getDataAsByteArray(const QString &format);
private:
const QMimeData *m_data;
};
#endif
@@ -0,0 +1,27 @@
/*
SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
SPDX-License-Identifier: MIT
*/
#include "draganddropplugin.h"
#include "DeclarativeDragArea.h"
#include "DeclarativeDragDropEvent.h"
#include "DeclarativeDropArea.h"
#include "DeclarativeMimeData.h"
#include "MimeDataWrapper.h"
void DragAndDropPlugin::registerTypes(const char *uri)
{
Q_ASSERT(QString::fromLatin1(uri) == QLatin1String("org.kde.draganddrop"));
qmlRegisterType<DeclarativeDropArea>(uri, 2, 0, "DropArea");
qmlRegisterType<DeclarativeDragArea>(uri, 2, 0, "DragArea");
qmlRegisterAnonymousType<QMimeData>(uri, 1);
qmlRegisterUncreatableType<DeclarativeMimeData>(uri, 2, 0, "MimeData", QStringLiteral("MimeData cannot be created from QML."));
qmlRegisterUncreatableType<DeclarativeDragDropEvent>(uri, 2, 0, "DragDropEvent", QStringLiteral("DragDropEvent cannot be created from QML."));
qmlRegisterUncreatableType<MimeDataWrapper>(uri, 2, 0, "MimeDataWrapper", QStringLiteral("DragDropEvent cannot be created from QML."));
}
#include "moc_draganddropplugin.cpp"
@@ -0,0 +1,21 @@
/*
SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
SPDX-License-Identifier: MIT
*/
#ifndef DRAGANDDROPPLUGIN_H
#define DRAGANDDROPPLUGIN_H
#include <QQmlExtensionPlugin>
class DragAndDropPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
public:
void registerTypes(const char *uri) override;
};
#endif
@@ -0,0 +1,20 @@
/*
SPDX-FileCopyrightText: 2023 Mike Noe <noeerover@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
/**
* Uses the badge overlay shader to display on an Item
*/
ShaderEffect {
required property var source
required property var mask
supportsAtlasTextures: true
fragmentShader: Qt.resolvedUrl("qrc:/shaders/badge.frag.qsb")
}
@@ -0,0 +1,23 @@
find_package(Qt6 REQUIRED COMPONENTS ShaderTools)
ecm_add_qml_module(graphicaleffects URI org.kde.graphicaleffects
VERSION 1.0
QML_FILES
Lanczos.qml
BadgeEffect.qml
GENERATE_PLUGIN_SOURCE
)
qt_add_shaders(graphicaleffects "graphicaleffects_shaders"
BATCHABLE
PRECOMPILE
OPTIMIZED
PREFIX
"/shaders"
FILES
"badge.frag"
"preserveaspect.vert"
"lanczos2sharp.frag"
)
ecm_finalize_qml_module(graphicaleffects)
@@ -0,0 +1,66 @@
/*
SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
/**
* A ShaderEffect that makes use of the Lanczos resampling method for scaling textures.
*
* Lanczos resampling tries to preserve detail when scaling down images and
* thus looks less blurry compared to a simple linear interpolation.
*
* This effect implements a single-pass Lanczos resampling filter using two
* lobes. Everything is done in the shader, with some defaults set for
* parameters. These defaults were designed to provide a good visual result when
* scaling down window thumbnails.
*/
ShaderEffect {
/**
* The source texture. Can be any QQuickTextureProvider.
*/
required property var source
/**
* The size of the source texture. Used to perform aspect ratio correction.
*/
required property size sourceSize
/**
* The target size of the Lanczos effect.
*
* Defaults to the width and height of this effect.
*/
property size targetSize: Qt.size(width, height)
/**
* Lanczos window Sinc function factor.
*
* Defaults to 0.4
*/
property real windowSinc: 0.4;
/**
* Lanczos Sinc function factor.
*
* Defaults to 1.0
*/
property real sinc: 1.0;
/**
* The amount of anti-ringing to apply.
*
* Defaults to 0.65
*/
property real antiRingingStrength: 0.65;
/**
* The resolution of the Lanczos effect.
*
* Larger values mean reduced (more pixelated) results.
* Defaults to 0.98 to achieve good results.
*/
property real resolution: 0.98;
vertexShader: Qt.resolvedUrl(":/shaders/preserveaspect.vert.qsb")
fragmentShader: Qt.resolvedUrl(":/shaders/lanczos2sharp.frag.qsb")
}
@@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2023 Mike Noe <noeerover@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#version 440
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
} ubuf;
layout(binding = 1) uniform sampler2D source;
layout(binding = 2) uniform sampler2D mask;
void main() {
fragColor = texture(source, qt_TexCoord0) * (1.0 - (texture(mask, qt_TexCoord0).a)) * ubuf.qt_Opacity;
}
@@ -0,0 +1,165 @@
/*
SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl>
SPDX-FileCopyrightText: 2023 Mike Noe <noeerover@gmail.com>
Hyllian's lanczos windowed-jinc 2-lobe sharper 3D with anti-ringing Shader
Copyright (C) 2011/2016 Hyllian - sergiogdb@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#version 440
layout(location = 0) in vec2 texcoord;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
vec2 targetSize;
float windowSinc;
float sinc;
float antiRingingStrength;
float resolution;
} ubuf;
layout(binding = 1) uniform sampler2D source;
// A=0.5, B=0.825 is the best jinc approximation for x<2.5. if B=1.0, it's a lanczos filter.
// Increase A to get more blur. Decrease it to get a sharper picture.
// B = 0.825 to get rid of dithering. Increase B to get a fine sharpness, though dithering returns.
#define wa (ubuf.windowSinc * pi)
#define wb (ubuf.sinc * pi)
const float pi = 3.1415926535897932384626433832795;
const vec3 dtt = vec3(65536.0, 255.0, 1.0);
vec4 reduce4(vec3 A, vec3 B, vec3 C, vec3 D)
{
return dtt * mat4x3(A, B, C, D);
}
vec3 min4(vec3 a, vec3 b, vec3 c, vec3 d)
{
return min(a, min(b, min(c, d)));
}
vec3 max4(vec3 a, vec3 b, vec3 c, vec3 d)
{
return max(a, max(b, max(c, d)));
}
vec4 lanczos(vec4 x)
{
return (x == vec4(0.0)) ? vec4(wa * wb) : sin(x * wa) * sin(x * wb) / (x * x);
}
void main()
{
// Discard any pixels that are outside the bounds of the texture.
// This prevents artifacts when the texture doesn't have a full-alpha border.
if (any(lessThan(texcoord, vec2(0.0))) || any(greaterThan(texcoord, vec2(1.0)))) {
discard;
}
vec2 dx = vec2(1.0, 0.0);
vec2 dy = vec2(0.0, 1.0);
vec2 pixelCoord = texcoord * ubuf.targetSize / ubuf.resolution;
vec2 texel = (floor(pixelCoord) + vec2(0.5, 0.5)) * ubuf.resolution / ubuf.targetSize;
vec2 texelCenter = (floor(pixelCoord - vec2(0.5, 0.5)) + vec2(0.5, 0.5));
mat4 weights;
weights[0] = lanczos(vec4(distance(pixelCoord, texelCenter - dx - dy),
distance(pixelCoord, texelCenter - dy),
distance(pixelCoord, texelCenter + dx - dy),
distance(pixelCoord, texelCenter + 2.0 * dx - dy)));
weights[1] = lanczos(vec4(distance(pixelCoord, texelCenter - dx),
distance(pixelCoord, texelCenter),
distance(pixelCoord, texelCenter + dx),
distance(pixelCoord, texelCenter + 2.0 * dx)));
weights[2] = lanczos(vec4(distance(pixelCoord, texelCenter - dx + dy),
distance(pixelCoord, texelCenter + dy),
distance(pixelCoord, texelCenter + dx + dy),
distance(pixelCoord, texelCenter + 2.0 * dx + dy)));
weights[3] = lanczos(vec4(distance(pixelCoord, texelCenter - dx + 2.0 * dy),
distance(pixelCoord, texelCenter + 2.0 * dy),
distance(pixelCoord, texelCenter + dx + 2.0 * dy),
distance(pixelCoord, texelCenter + 2.0 * dx + 2.0 * dy)));
dx = dx * ubuf.resolution / ubuf.targetSize;
dy = dy * ubuf.resolution / ubuf.targetSize;
texelCenter = texelCenter * ubuf.resolution / ubuf.targetSize;
// reading the texels
vec3 color = texture(source, texcoord).xyz;
vec3 c00 = texture(source, texelCenter - dx - dy).xyz;
vec3 c10 = texture(source, texelCenter - dy).xyz;
vec3 c20 = texture(source, texelCenter + dx - dy).xyz;
vec3 c30 = texture(source, texelCenter + 2.0 * dx - dy).xyz;
vec3 c01 = texture(source, texelCenter - dx).xyz;
vec3 c11 = texture(source, texelCenter).xyz;
vec3 c21 = texture(source, texelCenter + dx).xyz;
vec3 c31 = texture(source, texelCenter + 2.0 * dx).xyz;
vec3 c02 = texture(source, texelCenter - dx + dy).xyz;
vec3 c12 = texture(source, texelCenter + dy).xyz;
vec3 c22 = texture(source, texelCenter + dx + dy).xyz;
vec3 c32 = texture(source, texelCenter + 2.0 * dx + dy).xyz;
vec3 c03 = texture(source, texelCenter - dx + 2.0 * dy).xyz;
vec3 c13 = texture(source, texelCenter + 2.0 * dy).xyz;
vec3 c23 = texture(source, texelCenter + dx + 2.0 * dy).xyz;
vec3 c33 = texture(source, texelCenter + 2.0 * dx + 2.0 * dy).xyz;
vec3 F6 = texture(source, texel + dx + 0.25 * dx + 0.25 * dy).xyz;
vec3 F7 = texture(source, texel + dx + 0.25 * dx - 0.25 * dy).xyz;
vec3 F8 = texture(source, texel + dx - 0.25 * dx - 0.25 * dy).xyz;
vec3 F9 = texture(source, texel + dx - 0.25 * dx + 0.25 * dy).xyz;
vec3 H6 = texture(source, texel + 0.25 * dx + 0.25 * dy + dy).xyz;
vec3 H7 = texture(source, texel + 0.25 * dx - 0.25 * dy + dy).xyz;
vec3 H8 = texture(source, texel - 0.25 * dx - 0.25 * dy + dy).xyz;
vec3 H9 = texture(source, texel - 0.25 * dx + 0.25 * dy + dy).xyz;
vec4 f0 = reduce4(F6, F7, F8, F9);
vec4 h0 = reduce4(H6, H7, H8, H9);
// Get min/max samples
vec3 min_sample = min4(c11, c21, c12, c22);
vec3 max_sample = max4(c11, c21, c12, c22);
color = weights[0] * transpose(mat4x3(c00, c10, c20, c30));
color += weights[1] * transpose(mat4x3(c01, c11, c21, c31));
color += weights[2] * transpose(mat4x3(c02, c12, c22, c32));
color += weights[3] * transpose(mat4x3(c03, c13, c23, c33));
color = color / dot(vec4(1.0) * weights, vec4(1.0));
// Anti-ringing
vec3 aux = color;
color = clamp(color, min_sample, max_sample);
color = mix(aux, color, ubuf.antiRingingStrength);
float alpha = texture(source, texcoord).a * ubuf.qt_Opacity;
fragColor = vec4(color, alpha);
}
@@ -0,0 +1,36 @@
/*
SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl>
SPDX-FileCopyrightText: 2023 Mike Noe <noeerover@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#version 440
// This shader performs UV coordinates that account for the aspect ratio of some
// source as specified by the sourceSize uniform. The effective result is that
// this results in behaviour similar to the "PreserveAspectFit" mode of QML Image.
layout(location = 0) in vec4 position;
layout(location = 1) in vec2 texcoord;
layout(location = 0) out vec2 coord;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
vec2 sourceSize;
vec2 targetSize;
} ubuf;
void main() {
float scale = min(ubuf.targetSize.x / ubuf.sourceSize.x, ubuf.targetSize.y / ubuf.sourceSize.y);
vec2 newSize = ubuf.sourceSize * scale;
vec2 newOffset = (ubuf.targetSize - newSize) / 2.0;
vec2 uvOffset = (1.0 / newSize) * newOffset;
coord = -uvOffset + (ubuf.targetSize / newSize) * texcoord;
gl_Position = ubuf.qt_Matrix * position;
}
@@ -0,0 +1,12 @@
add_subdirectory(private)
ecm_add_qml_module(kquickcontrols URI "org.kde.kquickcontrols" VERSION 2.0 GENERATE_PLUGIN_SOURCE)
target_sources(kquickcontrols PRIVATE types.h)
ecm_target_qml_sources(kquickcontrols SOURCES ColorButton.qml KeySequenceItem.qml)
ecm_finalize_qml_module(kquickcontrols)
target_link_libraries(kquickcontrols PRIVATE kquickcontrolsprivate)
@@ -0,0 +1,109 @@
/*
SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Dialogs as QtDialogs
/**
* @short A pushbutton to display or allow user selection of a color.
*
* This widget can be used to display or allow user selection of a color.
*
* Example usage:
* @code
* import org.kde.kquickcontrols
*
* ColorButton {
* onColorChanged: console.log(color)
* }
* @endcode
*
* @inherits QtQuick.Controls.Button
*/
QQC2.Button {
id: root
/**
* The user selected color
*/
property alias color: colorDialog.selectedColor
/**
* Title to show in the dialog
*/
property alias dialogTitle: colorDialog.title
/**
* Allow the user to configure an alpha value
*/
property bool showAlphaChannel: true
/**
* This signal is emitted when the color dialog has been accepted
*
* @since 5.61
*/
signal accepted(color color)
readonly property real _buttonMarigns: 4 // same as QStyles. Remove if we can get this provided by the QQC theme
implicitWidth: 40 + _buttonMarigns * 2 // to perfectly clone kcolorbutton from kwidgetaddons
Accessible.name: i18nc("@info:whatsthis for a button", "Color button")
Accessible.description: enabled
? i18nc("@info:whatsthis for a button of current color code %1", "Current color is %1. This button will open a color chooser dialog.", color)
: i18nc("@info:whatsthis for a button of current color code %1", "Current color is %1.", color)
// create a checkerboard background for alpha to be adjusted
Canvas {
anchors.fill: colorBlock
visible: colorDialog.selectedColor.a < 1
onPaint: {
const ctx = getContext('2d');
ctx.fillStyle = "white";
ctx.fillRect(0,0, ctx.width, ctx.height);
ctx.fillStyle = "black";
// in blocks of 16x16 draw two black squares of 8x8 in top left and bottom right
for (let j = 0; j < width; j += 16) {
for (let i = 0; i < height; i += 16) {
// top left, bottom right
ctx.fillRect(j, i, 8, 8);
ctx.fillRect(j + 8, i + 8, 8, 8);
}
}
}
}
Rectangle {
id: colorBlock
anchors.centerIn: parent
height: parent.height - _buttonMarigns * 2
width: parent.width - _buttonMarigns * 2
color: enabled ? colorDialog.selectedColor : disabledPalette.button
SystemPalette {
id: disabledPalette
colorGroup: SystemPalette.Disabled
}
}
QtDialogs.ColorDialog {
id: colorDialog
onAccepted: root.accepted(color)
parentWindow: root.Window.window
options: root.showAlphaChannel ? QtDialogs.ColorDialog.ShowAlphaChannel : undefined
}
onClicked: {
colorDialog.open();
}
}
@@ -0,0 +1,198 @@
// SPDX-FileCopyright: 2014 David Edmundson <kde@davidedmundson.co.uk
// SPDX-FileCopyright: 2020 David Redondo <kde@david-redondo.de
// SPDX-FileCopyright: 2022 Aleix Pol <aleixpol@kde.org>
// SPDX-FileCopyright: 2024 ivan tkachenko <me@ratijas.tk>
// SPDX-License-Identifier: LGPL-2.1-or-later
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.private.kquickcontrols as KQuickControlsPrivate
RowLayout {
id: root
property bool showClearButton: true
property bool showCancelButton: false /// TODO KF6 default to true
property alias modifierOnlyAllowed: helper.modifierOnlyAllowed
property alias modifierlessAllowed: helper.modifierlessAllowed
property alias multiKeyShortcutsAllowed: helper.multiKeyShortcutsAllowed
property alias keySequence: helper.currentKeySequence
/**
* This property controls which types of shortcuts are checked for conflicts when the keySequence
* is set. If a conflict is detected, a messagebox will be shown asking the user to confirm their
* input. Valid values are combinations of the following flags:
* - @p ShortcutType.None Do not check for conflicts.
* - @p ShortcutType.StandardShortcuts Check against standard shortcuts. @see KStandardshortcut
* - @p ShortcutType.GlobalShortcuts Check against global shortcuts. @see KGlobalAccel
*
* The default is `ShortcutType.GlobalShortcuts | ShortcutType.StandardShortcut`
* @since 5.74
*/
property alias checkForConflictsAgainst: helper.checkAgainstShortcutTypes
/**
* This signal is emitted after the user introduces a new key sequence
*
* @since 5.68
* @deprecated Use keySequenceModified()
*/
signal captureFinished()
/***
* Emitted whenever the key sequence is modified by the user, interacting with the component
*
* Either by interacting capturing a key sequence or pressing the clear button.
*
* @since 5.99
*/
signal keySequenceModified()
/**
* Start capturing a key sequence. This equivalent to the user clicking on the main button of the item
* @since 5.70
*/
function startCapturing() {
mainButton.checked = true;
}
// A layout like RowLayout would automatically and implicitly fillHeight
// if placed inside a ColumnLayout, so an explicit binding should prevent
// that behavior. On the contrary, filling width wouldn't hurt, although
// it doesn't make much sense, as this component is not really adaptive.
Layout.fillHeight: false
KQuickControlsPrivate.KeySequenceHelper {
id: helper
onGotKeySequence: keySequence => {
validator.validateSequence(keySequence)
}
onQuestionDialogAccepted: validator.accept()
onQuestionDialogRejected: validator.reject()
}
KQuickControlsPrivate.KeySequenceValidator {
id: validator
validateTypes: helper.checkAgainstShortcutTypes
onError: (title, message) => {
helper.showErrorDialog(title, message)
}
onQuestion: (title, message) => {
helper.showQuestionDialog(title, message)
}
onFinished: keySequence => {
helper.updateKeySequence(keySequence)
mainButton.checked = false
root.captureFinished()
root.keySequenceModified()
}
}
KQuickControlsPrivate.TranslationContext {
id: _tr
domain: "kdeclarative6"
}
QQC2.Button {
id: mainButton
Layout.fillHeight: true
icon.name: "configure"
checkable: true
focus: checked
hoverEnabled: true
text: {
const keySequence = helper.currentKeySequence;
const text = helper.keySequenceIsEmpty(keySequence)
? (helper.isRecording
? _tr.i18nc("What the user inputs now will be taken as the new shortcut", "Input")
: _tr.i18nc("No shortcut defined", "None"))
// Single ampersand gets interpreted by the button as a mnemonic
// and removed; replace it with a double ampersand so that it
// will be displayed by the button as a single ampersand, or
// else shortcuts with the actual ampersand character will
// appear to be partially empty.
: helper.keySequenceNativeText(keySequence).replace('&', '&&');
// These spaces are intentional
return " " + text + (helper.isRecording ? " ... " : " ");
}
Accessible.description: _tr.i18n("Click on the button, then enter the shortcut like you would in the program.\nExample for Ctrl+A: hold the Ctrl key and press A.")
Accessible.role: Accessible.Button
QQC2.ToolTip {
visible: mainButton.hovered
text: mainButton.Accessible.description
}
onCheckedChanged: {
if (checked) {
validator.currentKeySequence = root.keySequence
helper.window = helper.renderWindow(parent.Window.window)
mainButton.forceActiveFocus()
helper.startRecording()
} else if (helper.isRecording) {
helper.cancelRecording()
}
}
onFocusChanged: {
if (!focus) {
mainButton.checked = false
}
}
}
QQC2.Button {
id: clearButton
Layout.fillHeight: true
Layout.preferredWidth: height
visible: root.showClearButton && !helper.isRecording
onClicked: {
root.keySequence = "";
root.keySequenceModified();
root.captureFinished(); // Not really capturing, but otherwise we cannot track this state, hence apps should use keySequenceModified
}
enabled: !helper.keySequenceIsEmpty(helper.currentKeySequence)
hoverEnabled: true
// icon name determines the direction of the arrow, NOT the direction of the app layout
icon.name: Qt.application.layoutDirection === Qt.LeftToRight ? "edit-clear-locationbar-rtl" : "edit-clear-locationbar-ltr"
Accessible.name: _tr.i18nc("@info:tooltip", "Clear Key Sequence")
QQC2.ToolTip {
visible: clearButton.hovered
text: clearButton.Accessible.name
}
}
QQC2.Button {
Layout.fillHeight: true
Layout.preferredWidth: height
onClicked: helper.cancelRecording()
visible: root.showCancelButton && helper.isRecording
icon.name: "dialog-cancel"
Accessible.name: _tr.i18nc("@info:tooltip", "Cancel Key Sequence Recording")
QQC2.ToolTip {
visible: parent.hovered
text: parent.Accessible.name
}
}
}
@@ -0,0 +1 @@
This import contains KDE extras that are visually similar to Qt Quick Controls.
@@ -0,0 +1,47 @@
add_library(kquickcontrolsprivate)
generate_export_header(kquickcontrolsprivate)
ecm_add_qml_module(kquickcontrolsprivate URI "org.kde.private.kquickcontrols" VERSION 2.0 GENERATE_PLUGIN_SOURCE)
qt_extract_metatypes(kquickcontrolsprivate)
set_target_properties(kquickcontrolsprivate PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 0
)
target_sources(kquickcontrolsprivate PRIVATE
keysequencehelper.cpp
keysequencehelper.h
keysequencevalidator.cpp
keysequencevalidator.h
translationcontext.cpp
translationcontext.h
enums.cpp
enums.h
)
target_link_libraries(kquickcontrolsprivate
PRIVATE
Qt6::Core
Qt6::Quick
Qt6::Qml
KF6::I18n
KF6::ConfigGui
KF6::GuiAddons
)
target_include_directories(kquickcontrolsprivate PRIVATE ${CMAKE_BINARY_DIR})
if (HAVE_KGLOBALACCEL)
target_link_libraries(kquickcontrolsprivate PRIVATE KF6::GlobalAccel)
endif()
if (NOT ANDROID)
target_link_libraries(kquickcontrolsprivate PRIVATE KF6::WidgetsAddons)
endif()
ecm_finalize_qml_module(kquickcontrolsprivate)
install(TARGETS kquickcontrolsprivate DESTINATION ${KDE_INSTALL_LIBDIR} LIBRARY NAMELINK_SKIP)
@@ -0,0 +1,7 @@
/*
SPDX-FileCopyrightText: 2024 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "enums.h"
@@ -0,0 +1,28 @@
/*
SPDX-FileCopyrightText: 2014 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include "kquickcontrolsprivate_export.h"
#include <QObject>
namespace KeySequenceEnums
{
Q_NAMESPACE_EXPORT(KQUICKCONTROLSPRIVATE_EXPORT)
enum ShortcutType {
None = 0x00, //!< No checking for conflicts
StandardShortcuts = 0x01, //!< Check against standard shortcuts. @see KStandardShortcut
GlobalShortcuts = 0x02, //!< Check against global shortcuts. @see KGlobalAccel
};
Q_ENUM_NS(ShortcutType);
Q_DECLARE_FLAGS(ShortcutTypes, ShortcutType)
Q_FLAG_NS(ShortcutTypes)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KeySequenceEnums::ShortcutTypes)
@@ -0,0 +1,150 @@
/*
SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
SPDX-FileCopyrightText: 2014 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 1998 Mark Donohoe <donohoe@kde.org>
SPDX-FileCopyrightText: 2001 Ellis Whitehead <ellis@kde.org>
SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "keysequencehelper.h"
#include <QDebug>
#include <QHash>
#include <QPointer>
#include <QQmlEngine>
#include <QQuickRenderControl>
#include <QQuickWindow>
#include <KLocalizedString>
#include <KStandardShortcut>
#ifndef Q_OS_ANDROID
#include <KMessageDialog>
#endif
#include <config-kdeclarative.h>
#if HAVE_KGLOBALACCEL
#include <KGlobalAccel>
#include <KGlobalShortcutInfo>
#endif
class KeySequenceHelperPrivate
{
public:
KeySequenceHelperPrivate(KeySequenceHelper *qq);
// members
KeySequenceHelper *const q;
//! Check the key sequence against KStandardShortcut::find()
KeySequenceEnums::ShortcutTypes checkAgainstShortcutTypes;
};
KeySequenceHelperPrivate::KeySequenceHelperPrivate(KeySequenceHelper *qq)
: q(qq)
, checkAgainstShortcutTypes(KeySequenceEnums::StandardShortcuts | KeySequenceEnums::GlobalShortcuts)
{
}
KeySequenceHelper::KeySequenceHelper(QObject *parent)
: KKeySequenceRecorder(nullptr, parent)
, d(new KeySequenceHelperPrivate(this))
{
}
KeySequenceHelper::~KeySequenceHelper()
{
delete d;
}
void KeySequenceHelper::updateKeySequence(const QKeySequence &keySequence)
{
#if HAVE_KGLOBALACCEL
if (d->checkAgainstShortcutTypes.testFlag(KeySequenceEnums::GlobalShortcuts)) {
KGlobalAccel::stealShortcutSystemwide(keySequence);
}
#endif
setCurrentKeySequence(keySequence);
}
void KeySequenceHelper::showErrorDialog(const QString &title, const QString &text)
{
#ifndef Q_OS_ANDROID
auto dialog = new KMessageDialog(KMessageDialog::Error, text);
dialog->setIcon(QIcon());
dialog->setCaption(title);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setWindowModality(Qt::WindowModal);
connect(dialog, &KMessageDialog::finished, this, [this]() {
Q_EMIT questionDialogRejected();
});
dialog->show();
#endif
}
void KeySequenceHelper::showQuestionDialog(const QString &title, const QString &text)
{
#ifndef Q_OS_ANDROID
auto dialog = new KMessageDialog(KMessageDialog::QuestionTwoActions, text);
dialog->setIcon(QIcon());
dialog->setCaption(title);
dialog->setButtons(KStandardGuiItem::cont(), KStandardGuiItem::cancel());
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setWindowModality(Qt::WindowModal);
connect(dialog, &KMessageDialog::finished, this, [this](int result) {
switch (result) {
case KMessageDialog::PrimaryAction:
case KMessageDialog::Ok:
Q_EMIT questionDialogAccepted();
break;
case KMessageDialog::SecondaryAction:
case KMessageDialog::Cancel:
Q_EMIT questionDialogRejected();
break;
}
});
dialog->show();
#else
Q_EMIT questionDialogAccepted();
#endif
}
KeySequenceEnums::ShortcutTypes KeySequenceHelper::checkAgainstShortcutTypes()
{
return d->checkAgainstShortcutTypes;
}
void KeySequenceHelper::setCheckAgainstShortcutTypes(KeySequenceEnums::ShortcutTypes types)
{
if (d->checkAgainstShortcutTypes != types) {
d->checkAgainstShortcutTypes = types;
}
Q_EMIT checkAgainstShortcutTypesChanged();
}
bool KeySequenceHelper::keySequenceIsEmpty(const QKeySequence &keySequence)
{
return keySequence.isEmpty();
}
QString KeySequenceHelper::keySequenceNativeText(const QKeySequence &keySequence)
{
return keySequence.toString(QKeySequence::NativeText);
}
QWindow *KeySequenceHelper::renderWindow(QQuickWindow *quickWindow)
{
QWindow *renderWindow = QQuickRenderControl::renderWindowFor(quickWindow);
auto window = renderWindow ? renderWindow : quickWindow;
// If we have CppOwnership, set it explicitly to prevent the engine taking ownership of the window
// and crashing on teardown
if (QQmlEngine::objectOwnership(window) == QQmlEngine::CppOwnership) {
QQmlEngine::setObjectOwnership(window, QQmlEngine::CppOwnership);
}
return window;
}
#include "moc_keysequencehelper.cpp"
@@ -0,0 +1,64 @@
/*
SPDX-FileCopyrightText: 2014 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KEYSEQUENCEHELPER_H
#define KEYSEQUENCEHELPER_H
#include <KKeySequenceRecorder>
#include <QKeySequence>
#include <QQuickItem>
#include "enums.h"
class KeySequenceHelperPrivate;
class QQuickWindow;
class KeySequenceHelper : public KKeySequenceRecorder
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(KeySequenceEnums::ShortcutTypes checkAgainstShortcutTypes READ checkAgainstShortcutTypes WRITE setCheckAgainstShortcutTypes NOTIFY
checkAgainstShortcutTypesChanged)
public:
/**
* Constructor.
*/
explicit KeySequenceHelper(QObject *parent = nullptr);
/**
* Destructs the widget.
*/
~KeySequenceHelper() override;
Q_INVOKABLE void updateKeySequence(const QKeySequence &keySequence);
Q_INVOKABLE void showQuestionDialog(const QString &title, const QString &text);
Q_INVOKABLE void showErrorDialog(const QString &title, const QString &text);
KeySequenceEnums::ShortcutTypes checkAgainstShortcutTypes();
void setCheckAgainstShortcutTypes(KeySequenceEnums::ShortcutTypes types);
Q_INVOKABLE static bool keySequenceIsEmpty(const QKeySequence &keySequence);
Q_INVOKABLE static QString keySequenceNativeText(const QKeySequence &keySequence);
Q_INVOKABLE static QWindow *renderWindow(QQuickWindow *quickWindow);
Q_SIGNALS:
void checkAgainstShortcutTypesChanged();
void questionDialogAccepted();
void questionDialogRejected();
private:
friend class KeySequenceHelperPrivate;
KeySequenceHelperPrivate *const d;
Q_DISABLE_COPY(KeySequenceHelper)
};
#endif // KEYSEQUENCEHELPER_H
@@ -0,0 +1,206 @@
/*
SPDX-FileCopyrightText: 2024 Arjen Hiemstra <ahiemstra@heimr.nl>
SPDX-FileCopyrightText: 2020 David Redondo <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2014 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 1998 Mark Donohoe <donohoe@kde.org>
SPDX-FileCopyrightText: 2001 Ellis Whitehead <ellis@kde.org>
SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "keysequencevalidator.h"
#include <KLocalizedString>
#include <KStandardShortcut>
#include <config-kdeclarative.h>
#if HAVE_KGLOBALACCEL
#include <KGlobalAccel>
#include <KGlobalShortcutInfo>
#endif
KeySequenceValidator::KeySequenceValidator(QObject *parent)
: QObject(parent)
{
}
QKeySequence KeySequenceValidator::currentKeySequence() const
{
return m_currentKeySequence;
}
void KeySequenceValidator::setCurrentKeySequence(const QKeySequence &sequence)
{
if (m_currentKeySequence == sequence) {
return;
}
m_currentKeySequence = sequence;
Q_EMIT currentKeySequenceChanged();
}
KeySequenceEnums::ShortcutTypes KeySequenceValidator::validateTypes() const
{
return m_validateTypes;
}
void KeySequenceValidator::setValidateTypes(KeySequenceEnums::ShortcutTypes types)
{
if (m_validateTypes == types) {
return;
}
m_validateTypes = types;
Q_EMIT validateTypesChanged();
}
void KeySequenceValidator::validateSequence(const QKeySequence &keySequence)
{
ValidationResult result = ValidationResult::Accept;
if (m_validateTypes & KeySequenceEnums::GlobalShortcuts) {
result = validateGlobalShortcut(keySequence);
}
if (result == ValidationResult::Reject) {
Q_EMIT finished(m_currentKeySequence);
return;
}
if (result == ValidationResult::QuestionPending) {
return;
}
if (m_validateTypes & KeySequenceEnums::StandardShortcuts) {
result = validateStandardShortcut(keySequence);
}
if (result == ValidationResult::Reject) {
Q_EMIT finished(m_currentKeySequence);
} else if (result == ValidationResult::Accept) {
Q_EMIT finished(keySequence);
}
}
void KeySequenceValidator::accept()
{
QKeySequence keySequence = m_pendingKeySequence;
m_pendingKeySequence = QKeySequence{};
ValidationResult result = ValidationResult::Accept;
if (m_validateGlobalPending) {
m_validateGlobalPending = false;
if (m_validateTypes & KeySequenceEnums::StandardShortcuts) {
result = validateStandardShortcut(keySequence);
}
}
if (result == ValidationResult::Reject) {
Q_EMIT finished(m_currentKeySequence);
} else if (result == ValidationResult::Accept) {
Q_EMIT finished(keySequence);
}
}
void KeySequenceValidator::reject()
{
m_pendingKeySequence = QKeySequence{};
Q_EMIT finished(m_currentKeySequence);
}
KeySequenceValidator::ValidationResult KeySequenceValidator::validateGlobalShortcut(const QKeySequence &keySequence)
{
#ifdef Q_OS_WIN
// on windows F12 is reserved by the debugger at all times, so we can't use it for a global shortcut
if (keySequence.toString().contains(QLatin1String("F12"))) {
QString title = i18n("Reserved Shortcut");
QString message = i18n(
"The F12 key is reserved on Windows, so cannot be used for a global shortcut.\n"
"Please choose another one.");
Q_EMIT error(title, message);
return ValidationResult::Reject;
} else {
return ValidationResult::Accept;
}
#elif HAVE_KGLOBALACCEL
// Global shortcuts are on key+modifier shortcuts. They can clash with a multi key shortcut.
QList<KGlobalShortcutInfo> others;
QList<KGlobalShortcutInfo> shadow;
QList<KGlobalShortcutInfo> shadowed;
if (!KGlobalAccel::isGlobalShortcutAvailable(keySequence, QString())) {
others << KGlobalAccel::globalShortcutsByKey(keySequence);
// look for shortcuts shadowing
shadow << KGlobalAccel::globalShortcutsByKey(keySequence, KGlobalAccel::MatchType::Shadows);
shadowed << KGlobalAccel::globalShortcutsByKey(keySequence, KGlobalAccel::MatchType::Shadowed);
}
if (!shadow.isEmpty() || !shadowed.isEmpty()) {
QString title = i18n("Global Shortcut Shadowing");
QString message;
if (!shadowed.isEmpty()) {
message += i18n("The '%1' key combination is shadowed by following global actions:\n").arg(keySequence.toString());
for (const KGlobalShortcutInfo &info : std::as_const(shadowed)) {
message += i18n("Action '%1' in context '%2'\n").arg(info.friendlyName(), info.contextFriendlyName());
}
}
if (!shadow.isEmpty()) {
message += i18n("The '%1' key combination shadows following global actions:\n").arg(keySequence.toString());
for (const KGlobalShortcutInfo &info : std::as_const(shadow)) {
message += i18n("Action '%1' in context '%2'\n").arg(info.friendlyName(), info.contextFriendlyName());
}
}
Q_EMIT error(title, message);
return ValidationResult::Reject;
}
if (!others.isEmpty()) {
QString title = i18nc("@dialog:title", "Found Conflict");
QString message;
if (others.size() == 1) {
auto info = others.at(0);
message = i18n("Shortcut '%1' is already assigned to action '%2' of %3.\nDo you want to reassign it?",
keySequence.toString(),
info.friendlyName(),
info.componentFriendlyName());
} else {
message = i18n("Shortcut '%1' is already assigned to the following actions:\n");
for (const auto &info : std::as_const(others)) {
message += i18n("Action '%1' of %2\n", info.friendlyName(), info.componentFriendlyName());
}
message += i18n("Do you want to reassign it?");
}
m_pendingKeySequence = keySequence;
m_validateGlobalPending = true;
Q_EMIT question(title, message);
return ValidationResult::QuestionPending;
}
#endif
return ValidationResult::Accept;
}
KeySequenceValidator::ValidationResult KeySequenceValidator::validateStandardShortcut(const QKeySequence &keySequence)
{
KStandardShortcut::StandardShortcut ssc = KStandardShortcut::find(keySequence);
if (ssc != KStandardShortcut::AccelNone) {
QString title = i18n("Conflict with Standard Application Shortcut");
QString message = i18n(
"The '%1' key combination is also used for the standard action "
"\"%2\" that some applications use.\n"
"Do you really want to use it as a global shortcut as well?",
keySequence.toString(QKeySequence::NativeText),
KStandardShortcut::label(ssc));
m_pendingKeySequence = keySequence;
Q_EMIT question(title, message);
return ValidationResult::QuestionPending;
} else {
return ValidationResult::Accept;
}
}
#include "moc_keysequencevalidator.cpp"
@@ -0,0 +1,63 @@
/*
SPDX-FileCopyrightText: 2014 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
SPDX-FileCopyrightText: 2024 Arjen Hiemstra <ahiemstra@heimr.nl>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KEYSEQUENCEVALIDATOR_H
#define KEYSEQUENCEVALIDATOR_H
#include <QKeySequence>
#include <QObject>
#include <QQuickItem>
#include "keysequencehelper.h"
class KeySequenceValidator : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QKeySequence currentKeySequence READ currentKeySequence WRITE setCurrentKeySequence NOTIFY currentKeySequenceChanged)
Q_PROPERTY(KeySequenceEnums::ShortcutTypes validateTypes READ validateTypes WRITE setValidateTypes NOTIFY validateTypesChanged)
public:
/**
* Constructor.
*/
explicit KeySequenceValidator(QObject *parent = nullptr);
QKeySequence currentKeySequence() const;
void setCurrentKeySequence(const QKeySequence &sequence);
Q_SIGNAL void currentKeySequenceChanged();
KeySequenceEnums::ShortcutTypes validateTypes() const;
void setValidateTypes(KeySequenceEnums::ShortcutTypes types);
Q_SIGNAL void validateTypesChanged();
Q_INVOKABLE void validateSequence(const QKeySequence &keySequence);
Q_INVOKABLE void accept();
Q_INVOKABLE void reject();
Q_SIGNAL void error(const QString &title, const QString &message);
Q_SIGNAL void question(const QString &title, const QString &message);
Q_SIGNAL void finished(const QKeySequence &keySequence);
private:
enum class ValidationResult {
Reject,
Accept,
QuestionPending,
};
ValidationResult validateGlobalShortcut(const QKeySequence &keySequence);
ValidationResult validateStandardShortcut(const QKeySequence &keySequence);
KeySequenceEnums::ShortcutTypes m_validateTypes = KeySequenceEnums::GlobalShortcuts | KeySequenceEnums::StandardShortcuts;
QKeySequence m_currentKeySequence;
QKeySequence m_pendingKeySequence;
bool m_validateGlobalPending = false;
};
#endif // KEYSEQUENCEVALIDATOR_H
@@ -0,0 +1,269 @@
/*
SPDX-FileCopyrightText: 2014 Kevin Ottens <ervin@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
// Undefine this because we don't want our i18n*() method names to be turned into i18nd*()
#undef TRANSLATION_DOMAIN
#include "translationcontext.h"
#include <QDebug>
#include <KLocalizedString>
TranslationContext::TranslationContext(QObject *parent)
: QObject(parent)
{
}
TranslationContext::~TranslationContext()
{
}
QString TranslationContext::domain() const
{
return m_domain;
}
void TranslationContext::setDomain(const QString &domain)
{
if (m_domain == domain) {
return;
}
m_domain = domain;
Q_EMIT domainChanged(domain);
}
QString TranslationContext::i18n(const QString &message,
const QString &param1,
const QString &param2,
const QString &param3,
const QString &param4,
const QString &param5,
const QString &param6,
const QString &param7,
const QString &param8,
const QString &param9,
const QString &param10) const
{
if (message.isNull()) {
qWarning() << "i18n() needs at least one parameter";
return QString();
}
KLocalizedString trMessage = ki18nd(m_domain.toUtf8().constData(), message.toUtf8().constData());
if (!param1.isNull()) {
trMessage = trMessage.subs(param1);
}
if (!param2.isNull()) {
trMessage = trMessage.subs(param2);
}
if (!param3.isNull()) {
trMessage = trMessage.subs(param3);
}
if (!param4.isNull()) {
trMessage = trMessage.subs(param4);
}
if (!param5.isNull()) {
trMessage = trMessage.subs(param5);
}
if (!param6.isNull()) {
trMessage = trMessage.subs(param6);
}
if (!param7.isNull()) {
trMessage = trMessage.subs(param7);
}
if (!param8.isNull()) {
trMessage = trMessage.subs(param8);
}
if (!param9.isNull()) {
trMessage = trMessage.subs(param9);
}
if (!param10.isNull()) {
trMessage = trMessage.subs(param10);
}
return trMessage.toString();
}
QString TranslationContext::i18nc(const QString &context,
const QString &message,
const QString &param1,
const QString &param2,
const QString &param3,
const QString &param4,
const QString &param5,
const QString &param6,
const QString &param7,
const QString &param8,
const QString &param9,
const QString &param10) const
{
if (context.isNull() || message.isNull()) {
qWarning() << "i18nc() needs at least two arguments";
return QString();
}
KLocalizedString trMessage = ki18ndc(m_domain.toUtf8().constData(), context.toUtf8().constData(), message.toUtf8().constData());
if (!param1.isNull()) {
trMessage = trMessage.subs(param1);
}
if (!param2.isNull()) {
trMessage = trMessage.subs(param2);
}
if (!param3.isNull()) {
trMessage = trMessage.subs(param3);
}
if (!param4.isNull()) {
trMessage = trMessage.subs(param4);
}
if (!param5.isNull()) {
trMessage = trMessage.subs(param5);
}
if (!param6.isNull()) {
trMessage = trMessage.subs(param6);
}
if (!param7.isNull()) {
trMessage = trMessage.subs(param7);
}
if (!param8.isNull()) {
trMessage = trMessage.subs(param8);
}
if (!param9.isNull()) {
trMessage = trMessage.subs(param9);
}
if (!param10.isNull()) {
trMessage = trMessage.subs(param10);
}
return trMessage.toString();
}
QString TranslationContext::i18np(const QString &singular,
const QString &plural,
const QString &param1,
const QString &param2,
const QString &param3,
const QString &param4,
const QString &param5,
const QString &param6,
const QString &param7,
const QString &param8,
const QString &param9,
const QString &param10) const
{
if (singular.isNull() || plural.isNull()) {
qWarning() << "i18np() needs at least two arguments";
return QString();
}
KLocalizedString trMessage = ki18ndp(m_domain.toUtf8().constData(), singular.toUtf8().constData(), plural.toUtf8().constData());
if (!param1.isNull()) {
bool ok;
int num = param1.toInt(&ok);
if (ok) {
trMessage = trMessage.subs(num);
} else {
trMessage = trMessage.subs(param1);
}
}
if (!param2.isNull()) {
trMessage = trMessage.subs(param2);
}
if (!param3.isNull()) {
trMessage = trMessage.subs(param3);
}
if (!param4.isNull()) {
trMessage = trMessage.subs(param4);
}
if (!param5.isNull()) {
trMessage = trMessage.subs(param5);
}
if (!param6.isNull()) {
trMessage = trMessage.subs(param6);
}
if (!param7.isNull()) {
trMessage = trMessage.subs(param7);
}
if (!param8.isNull()) {
trMessage = trMessage.subs(param8);
}
if (!param9.isNull()) {
trMessage = trMessage.subs(param9);
}
if (!param10.isNull()) {
trMessage = trMessage.subs(param10);
}
return trMessage.toString();
}
QString TranslationContext::i18ncp(const QString &context,
const QString &singular,
const QString &plural,
const QString &param1,
const QString &param2,
const QString &param3,
const QString &param4,
const QString &param5,
const QString &param6,
const QString &param7,
const QString &param8,
const QString &param9,
const QString &param10) const
{
if (context.isNull() || singular.isNull() || plural.isNull()) {
qWarning() << "i18ncp() needs at least three arguments";
return QString();
}
KLocalizedString trMessage =
ki18ndcp(m_domain.toUtf8().constData(), context.toUtf8().constData(), singular.toUtf8().constData(), plural.toUtf8().constData());
if (!param1.isNull()) {
bool ok;
int num = param1.toInt(&ok);
if (ok) {
trMessage = trMessage.subs(num);
} else {
trMessage = trMessage.subs(param1);
}
}
if (!param2.isNull()) {
trMessage = trMessage.subs(param2);
}
if (!param3.isNull()) {
trMessage = trMessage.subs(param3);
}
if (!param4.isNull()) {
trMessage = trMessage.subs(param4);
}
if (!param5.isNull()) {
trMessage = trMessage.subs(param5);
}
if (!param6.isNull()) {
trMessage = trMessage.subs(param6);
}
if (!param7.isNull()) {
trMessage = trMessage.subs(param7);
}
if (!param8.isNull()) {
trMessage = trMessage.subs(param8);
}
if (!param9.isNull()) {
trMessage = trMessage.subs(param9);
}
if (!param10.isNull()) {
trMessage = trMessage.subs(param10);
}
return trMessage.toString();
}
#include "moc_translationcontext.cpp"
@@ -0,0 +1,91 @@
/*
SPDX-FileCopyrightText: 2014 Kevin Ottens <ervin@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef TRANSLATIONCONTEXT_H
#define TRANSLATIONCONTEXT_H
#include <QObject>
#include <qqmlregistration.h>
class TranslationContext : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString domain READ domain WRITE setDomain NOTIFY domainChanged)
public:
explicit TranslationContext(QObject *parent = nullptr);
~TranslationContext() override;
QString domain() const;
public Q_SLOTS:
void setDomain(const QString &domain);
Q_SIGNALS:
void domainChanged(const QString &domain);
public:
Q_INVOKABLE QString i18n(const QString &message,
const QString &param1 = QString(),
const QString &param2 = QString(),
const QString &param3 = QString(),
const QString &param4 = QString(),
const QString &param5 = QString(),
const QString &param6 = QString(),
const QString &param7 = QString(),
const QString &param8 = QString(),
const QString &param9 = QString(),
const QString &param10 = QString()) const;
Q_INVOKABLE QString i18nc(const QString &context,
const QString &message,
const QString &param1 = QString(),
const QString &param2 = QString(),
const QString &param3 = QString(),
const QString &param4 = QString(),
const QString &param5 = QString(),
const QString &param6 = QString(),
const QString &param7 = QString(),
const QString &param8 = QString(),
const QString &param9 = QString(),
const QString &param10 = QString()) const;
Q_INVOKABLE QString i18np(const QString &singular,
const QString &plural,
const QString &param1 = QString(),
const QString &param2 = QString(),
const QString &param3 = QString(),
const QString &param4 = QString(),
const QString &param5 = QString(),
const QString &param6 = QString(),
const QString &param7 = QString(),
const QString &param8 = QString(),
const QString &param9 = QString(),
const QString &param10 = QString()) const;
Q_INVOKABLE QString i18ncp(const QString &context,
const QString &singular,
const QString &plural,
const QString &param1 = QString(),
const QString &param2 = QString(),
const QString &param3 = QString(),
const QString &param4 = QString(),
const QString &param5 = QString(),
const QString &param6 = QString(),
const QString &param7 = QString(),
const QString &param8 = QString(),
const QString &param9 = QString(),
const QString &param10 = QString()) const;
private:
Q_DISABLE_COPY(TranslationContext)
QString m_domain;
};
#endif // TRANSLATIONCONTEXT_H
@@ -0,0 +1,16 @@
/*
SPDX-FileCopyrightText: 2024 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "enums.h"
#include <qqmlregistration.h>
namespace Foo
{
Q_NAMESPACE
QML_NAMED_ELEMENT(ShortcutType)
QML_FOREIGN_NAMESPACE(KeySequenceEnums)
};
@@ -0,0 +1,22 @@
ecm_add_qml_module(kquickcontrolsaddonsplugin URI org.kde.kquickcontrolsaddons VERSION 2.0 DEPENDENCIES QtQuick GENERATE_PLUGIN_SOURCE)
target_sources(kquickcontrolsaddonsplugin PRIVATE
clipboard.cpp
clipboard.h
mouseeventlistener.cpp
mouseeventlistener.h
qimageitem.cpp
qimageitem.h
qpixmapitem.cpp
qpixmapitem.h
)
target_link_libraries(kquickcontrolsaddonsplugin PRIVATE
Qt6::Core
Qt6::Quick
Qt6::Qml
Qt6::Gui
KF6::ConfigCore
)
ecm_finalize_qml_module(kquickcontrolsaddonsplugin)
@@ -0,0 +1,12 @@
/** @mainpage Qt Extra Components
<h2>import org.kde.qtextracomponents</h2>
- QPixmapItem
- QImageItem
- MouseEventListener
*/
// DOXYGEN_SET_PROJECT_NAME = QtExtraComponents
// vim:ts=4:sw=4:expandtab:filetype=doxygen
@@ -0,0 +1,119 @@
/*
SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "clipboard.h"
#include <QDebug>
#include <QGuiApplication>
#include <QMimeData>
#include <QUrl>
Clipboard::Clipboard(QObject *parent)
: QObject(parent)
, m_clipboard(QGuiApplication::clipboard())
, m_mode(QClipboard::Clipboard)
{
connect(m_clipboard, &QClipboard::changed, this, &Clipboard::clipboardChanged);
}
void Clipboard::setMode(QClipboard::Mode mode)
{
m_mode = mode;
Q_EMIT modeChanged(m_mode);
}
void Clipboard::clipboardChanged(QClipboard::Mode m)
{
if (m == m_mode) {
Q_EMIT contentChanged();
}
}
void Clipboard::clear()
{
m_clipboard->clear(m_mode);
}
QClipboard::Mode Clipboard::mode() const
{
return m_mode;
}
QVariant Clipboard::contentFormat(const QString &format) const
{
const QMimeData *data = m_clipboard->mimeData(m_mode);
QVariant ret;
if (format == QLatin1String("text/uri-list")) {
QVariantList retList;
const auto urls = data->urls();
for (const QUrl &url : urls) {
retList += url;
}
ret = retList;
} else if (format.startsWith(QLatin1String("text/"))) {
ret = data->text();
} else if (format.startsWith(QLatin1String("image/"))) {
ret = data->imageData();
} else {
ret = data->data(format.isEmpty() ? data->formats().first() : format);
}
return ret;
}
QVariant Clipboard::content() const
{
return contentFormat(m_clipboard->mimeData(m_mode)->formats().first());
}
void Clipboard::setContent(const QVariant &content)
{
QMimeData *mimeData = new QMimeData;
switch (content.userType()) {
case QMetaType::QString:
mimeData->setText(content.toString());
break;
case QMetaType::QColor:
mimeData->setColorData(content.toString());
break;
case QMetaType::QPixmap:
case QMetaType::QImage:
mimeData->setImageData(content);
break;
default:
if (content.userType() == QMetaType::QVariantList) {
const QVariantList list = content.toList();
QList<QUrl> urls;
bool wasUrlList = true;
for (const QVariant &url : list) {
if (url.userType() != QMetaType::QUrl) {
wasUrlList = false;
break;
}
urls += url.toUrl();
}
if (wasUrlList) {
mimeData->setUrls(urls);
break;
}
}
if (content.canConvert<QString>()) {
mimeData->setText(content.toString());
} else {
mimeData->setData(QStringLiteral("application/octet-stream"), content.toByteArray());
qWarning() << "Couldn't figure out the content type, storing as application/octet-stream";
}
break;
}
m_clipboard->setMimeData(mimeData, m_mode);
}
QStringList Clipboard::formats() const
{
return m_clipboard->mimeData(m_mode)->formats();
}
#include "moc_clipboard.cpp"
@@ -0,0 +1,87 @@
/*
SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef CLIPBOARD_H
#define CLIPBOARD_H
#include <QClipboard>
#include <QVariant>
#include <qqmlregistration.h>
class ClipboardPrivate;
/**
* @brief Wrapper for QClipboard
*
* Offers a simple wrapper to interact with QClipboard from QtQuick.
*
* ```
* import QtQuick
* import org.kde.kquickcontrolsaddons as KQuickControlsAddons
* Text {
* text: "lorem ipsum"
*
* KQuickControlsAddons.Clipboard { id: clipboard }
*
* MouseArea {
* anchors.fill: parent
* acceptedButtons: Qt.LeftButton | Qt.RightButton
* onClicked: clipboard.content = parent.text
* }
* }
* ```
*/
class Clipboard : public QObject
{
Q_OBJECT
QML_ELEMENT
/**
* Controls the state this object will be monitoring and extracting its contents from.
*/
Q_PROPERTY(QClipboard::Mode mode READ mode WRITE setMode NOTIFY modeChanged)
/**
* Provides the contents currently in the clipboard and lets modify them.
*/
Q_PROPERTY(QVariant content READ content WRITE setContent NOTIFY contentChanged)
/**
* Figure out the nature of the contents in the clipboard as mimetype strings.
*/
Q_PROPERTY(QStringList formats READ formats NOTIFY contentChanged)
public:
explicit Clipboard(QObject *parent = nullptr);
QClipboard::Mode mode() const;
void setMode(QClipboard::Mode mode);
/**
* @param format mimetype string
* @return Output based on the mimetype. This may be a list of URLs, text, image data, or use QMimeData::data
*/
Q_SCRIPTABLE QVariant contentFormat(const QString &format) const;
QVariant content() const;
void setContent(const QVariant &content);
QStringList formats() const;
/** @see QClipboard::clear() */
Q_SCRIPTABLE void clear();
Q_SIGNALS:
void modeChanged(QClipboard::Mode mode);
void contentChanged();
private Q_SLOTS:
void clipboardChanged(QClipboard::Mode m);
private:
QClipboard *m_clipboard;
QClipboard::Mode m_mode;
};
#endif
@@ -0,0 +1,506 @@
/*
SPDX-FileCopyrightText: 2011 Marco Martin <notmart@gmail.com>
SPDX-FileCopyrightText: 2013 Sebastian Kügler <sebas@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "mouseeventlistener.h"
#include <QDebug>
#include <QEvent>
#include <QGuiApplication>
#include <QMouseEvent>
#include <QQuickWindow>
#include <QScreen>
#include <QStyleHints>
#include <QTimer>
MouseEventListener::MouseEventListener(QQuickItem *parent)
: QQuickItem(parent)
, m_pressed(false)
, m_pressAndHoldEvent(nullptr)
, m_lastEvent(nullptr)
, m_acceptedButtons(Qt::LeftButton)
{
m_pressAndHoldTimer = new QTimer(this);
m_pressAndHoldTimer->setSingleShot(true);
connect(m_pressAndHoldTimer, &QTimer::timeout, this, &MouseEventListener::handlePressAndHold);
setFiltersChildMouseEvents(true);
setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton | Qt::MiddleButton | Qt::XButton1 | Qt::XButton2);
}
MouseEventListener::~MouseEventListener()
{
}
Qt::MouseButtons MouseEventListener::acceptedButtons() const
{
return m_acceptedButtons;
}
Qt::CursorShape MouseEventListener::cursorShape() const
{
return cursor().shape();
}
void MouseEventListener::setCursorShape(Qt::CursorShape shape)
{
if (cursor().shape() == shape) {
return;
}
setCursor(shape);
Q_EMIT cursorShapeChanged();
}
void MouseEventListener::setAcceptedButtons(Qt::MouseButtons buttons)
{
if (buttons == m_acceptedButtons) {
return;
}
m_acceptedButtons = buttons;
Q_EMIT acceptedButtonsChanged();
}
void MouseEventListener::setHoverEnabled(bool enable)
{
if (enable == acceptHoverEvents()) {
return;
}
setAcceptHoverEvents(enable);
Q_EMIT hoverEnabledChanged(enable);
}
bool MouseEventListener::hoverEnabled() const
{
return acceptHoverEvents();
}
bool MouseEventListener::isPressed() const
{
return m_pressed;
}
void MouseEventListener::hoverEnterEvent(QHoverEvent *event)
{
Q_UNUSED(event);
m_containsMouse = true;
if (!m_childContainsMouse) {
Q_EMIT containsMouseChanged(true);
}
}
void MouseEventListener::hoverLeaveEvent(QHoverEvent *event)
{
Q_UNUSED(event);
m_containsMouse = false;
if (!m_childContainsMouse) {
Q_EMIT containsMouseChanged(false);
}
}
void MouseEventListener::hoverMoveEvent(QHoverEvent *event)
{
if (m_lastEvent == event) {
return;
}
QQuickWindow *w = window();
QPoint screenPos;
if (w) {
screenPos = w->mapToGlobal(event->position()).toPoint();
}
KDeclarativeMouseEvent dme(event->position().x(),
event->position().y(),
screenPos.x(),
screenPos.y(),
Qt::NoButton,
Qt::NoButton,
event->modifiers(),
nullptr,
Qt::MouseEventNotSynthesized);
Q_EMIT positionChanged(&dme);
}
bool MouseEventListener::containsMouse() const
{
return m_containsMouse || m_childContainsMouse;
}
void MouseEventListener::mousePressEvent(QMouseEvent *me)
{
if (m_lastEvent == me || !(me->buttons() & m_acceptedButtons)) {
me->setAccepted(false);
return;
}
// FIXME: when a popup window is visible: a click anywhere hides it: but the old qquickitem will continue to think it's under the mouse
// doesn't seem to be any good way to properly reset this.
// this msolution will still caused a missed click after the popup is gone, but gets the situation unblocked.
QPoint viewPosition;
if (window()) {
viewPosition = window()->position();
}
if (!QRectF(mapToScene(QPoint(0, 0)) + viewPosition, QSizeF(width(), height())).contains(me->globalPosition())) {
me->ignore();
return;
}
m_buttonDownPos = me->globalPosition();
KDeclarativeMouseEvent dme(me->pos().x(),
me->pos().y(),
me->globalPosition().x(),
me->globalPosition().y(),
me->button(),
me->buttons(),
me->modifiers(),
screenForGlobalPos(me->globalPosition()),
me->source());
if (!m_pressAndHoldEvent) {
m_pressAndHoldEvent = new KDeclarativeMouseEvent(me->pos().x(),
me->pos().y(),
me->globalPosition().x(),
me->globalPosition().y(),
me->button(),
me->buttons(),
me->modifiers(),
screenForGlobalPos(me->globalPosition()),
me->source());
}
m_pressed = true;
Q_EMIT pressed(&dme);
Q_EMIT pressedChanged();
if (dme.isAccepted()) {
me->setAccepted(true);
return;
}
m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval());
}
void MouseEventListener::mouseMoveEvent(QMouseEvent *me)
{
if (m_lastEvent == me || !(me->buttons() & m_acceptedButtons)) {
me->setAccepted(false);
return;
}
if (QPointF(me->globalPosition() - m_buttonDownPos).manhattanLength() > QGuiApplication::styleHints()->startDragDistance()
&& m_pressAndHoldTimer->isActive()) {
m_pressAndHoldTimer->stop();
}
KDeclarativeMouseEvent dme(me->pos().x(),
me->pos().y(),
me->globalPosition().x(),
me->globalPosition().y(),
me->button(),
me->buttons(),
me->modifiers(),
screenForGlobalPos(me->globalPosition()),
me->source());
Q_EMIT positionChanged(&dme);
if (dme.isAccepted()) {
me->setAccepted(true);
}
}
void MouseEventListener::mouseReleaseEvent(QMouseEvent *me)
{
if (m_lastEvent == me) {
me->setAccepted(false);
return;
}
KDeclarativeMouseEvent dme(me->pos().x(),
me->pos().y(),
me->globalPosition().x(),
me->globalPosition().y(),
me->button(),
me->buttons(),
me->modifiers(),
screenForGlobalPos(me->globalPosition()),
me->source());
m_pressed = false;
Q_EMIT released(&dme);
Q_EMIT pressedChanged();
if (boundingRect().contains(me->pos()) && m_pressAndHoldTimer->isActive()) {
Q_EMIT clicked(&dme);
m_pressAndHoldTimer->stop();
}
if (dme.isAccepted()) {
me->setAccepted(true);
}
}
void MouseEventListener::wheelEvent(QWheelEvent *we)
{
if (m_lastEvent == we) {
return;
}
KDeclarativeWheelEvent dwe(we->position().toPoint(),
we->globalPosition().toPoint(),
we->angleDelta(),
we->buttons(),
we->modifiers(),
Qt::Vertical /* HACK, deprecated, remove */);
Q_EMIT wheelMoved(&dwe);
}
void MouseEventListener::handlePressAndHold()
{
if (m_pressed) {
Q_EMIT pressAndHold(m_pressAndHoldEvent);
delete m_pressAndHoldEvent;
m_pressAndHoldEvent = nullptr;
}
}
bool MouseEventListener::childMouseEventFilter(QQuickItem *item, QEvent *event)
{
if (!isEnabled()) {
return false;
}
// don't filter other mouseeventlisteners
if (qobject_cast<MouseEventListener *>(item)) {
return false;
}
switch (event->type()) {
case QEvent::MouseButtonPress: {
m_lastEvent = event;
QMouseEvent *me = static_cast<QMouseEvent *>(event);
if (!(me->buttons() & m_acceptedButtons)) {
break;
}
// the parent will receive events in its own coordinates
const QPointF myPos = mapFromScene(me->scenePosition());
KDeclarativeMouseEvent dme(myPos.x(),
myPos.y(),
me->globalPosition().x(),
me->globalPosition().y(),
me->button(),
me->buttons(),
me->modifiers(),
screenForGlobalPos(me->globalPosition()),
me->source());
delete m_pressAndHoldEvent;
m_pressAndHoldEvent = new KDeclarativeMouseEvent(myPos.x(),
myPos.y(),
me->globalPosition().x(),
me->globalPosition().y(),
me->button(),
me->buttons(),
me->modifiers(),
screenForGlobalPos(me->globalPosition()),
me->source());
// qDebug() << "pressed in sceneEventFilter";
m_buttonDownPos = me->globalPosition();
m_pressed = true;
Q_EMIT pressed(&dme);
Q_EMIT pressedChanged();
if (dme.isAccepted()) {
return true;
}
m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval());
break;
}
case QEvent::HoverMove: {
if (!acceptHoverEvents()) {
break;
}
m_lastEvent = event;
QHoverEvent *he = static_cast<QHoverEvent *>(event);
const QPointF myPos = item->mapToItem(this, he->position());
QQuickWindow *w = window();
QPoint screenPos;
if (w) {
screenPos = w->mapToGlobal(myPos.toPoint());
}
KDeclarativeMouseEvent
dme(myPos.x(), myPos.y(), screenPos.x(), screenPos.y(), Qt::NoButton, Qt::NoButton, he->modifiers(), nullptr, Qt::MouseEventNotSynthesized);
// qDebug() << "positionChanged..." << dme.x() << dme.y();
Q_EMIT positionChanged(&dme);
if (dme.isAccepted()) {
return true;
}
break;
}
case QEvent::MouseMove: {
m_lastEvent = event;
QMouseEvent *me = static_cast<QMouseEvent *>(event);
if (!(me->buttons() & m_acceptedButtons)) {
break;
}
const QPointF myPos = mapFromScene(me->scenePosition());
KDeclarativeMouseEvent dme(myPos.x(),
myPos.y(),
me->globalPosition().x(),
me->globalPosition().y(),
me->button(),
me->buttons(),
me->modifiers(),
screenForGlobalPos(me->globalPosition()),
me->source());
// qDebug() << "positionChanged..." << dme.x() << dme.y();
// stop the pressandhold if mouse moved enough
if (QPointF(me->globalPosition() - m_buttonDownPos).manhattanLength() > QGuiApplication::styleHints()->startDragDistance()
&& m_pressAndHoldTimer->isActive()) {
m_pressAndHoldTimer->stop();
// if the mouse moves and we are waiting to emit a press and hold event, update the coordinates
// as there is no update function, delete the old event and create a new one
} else if (m_pressAndHoldEvent) {
delete m_pressAndHoldEvent;
m_pressAndHoldEvent = new KDeclarativeMouseEvent(myPos.x(),
myPos.y(),
me->globalPosition().x(),
me->globalPosition().y(),
me->button(),
me->buttons(),
me->modifiers(),
screenForGlobalPos(me->globalPosition()),
me->source());
}
Q_EMIT positionChanged(&dme);
if (dme.isAccepted()) {
return true;
}
break;
}
case QEvent::MouseButtonRelease: {
m_lastEvent = event;
QMouseEvent *me = static_cast<QMouseEvent *>(event);
const QPointF myPos = mapFromScene(me->scenePosition());
KDeclarativeMouseEvent dme(myPos.x(),
myPos.y(),
me->globalPosition().x(),
me->globalPosition().y(),
me->button(),
me->buttons(),
me->modifiers(),
screenForGlobalPos(me->globalPosition()),
me->source());
m_pressed = false;
Q_EMIT released(&dme);
Q_EMIT pressedChanged();
if (QPointF(me->globalPosition() - m_buttonDownPos).manhattanLength() <= QGuiApplication::styleHints()->startDragDistance()
&& m_pressAndHoldTimer->isActive()) {
Q_EMIT clicked(&dme);
m_pressAndHoldTimer->stop();
}
if (dme.isAccepted()) {
return true;
}
break;
}
case QEvent::UngrabMouse: {
m_lastEvent = event;
handleUngrab();
break;
}
case QEvent::Wheel: {
m_lastEvent = event;
QWheelEvent *we = static_cast<QWheelEvent *>(event);
KDeclarativeWheelEvent dwe(we->position().toPoint(),
we->globalPosition().toPoint(),
we->angleDelta(),
we->buttons(),
we->modifiers(),
Qt::Vertical /* HACK, deprecated, remove */);
Q_EMIT wheelMoved(&dwe);
break;
}
case QEvent::HoverEnter: {
m_childContainsMouse = true;
if (!m_containsMouse) {
Q_EMIT containsMouseChanged(true);
}
break;
}
case QEvent::HoverLeave: {
m_childContainsMouse = false;
if (!m_containsMouse) {
Q_EMIT containsMouseChanged(false);
}
break;
}
default:
break;
}
return QQuickItem::childMouseEventFilter(item, event);
// return false;
}
QScreen *MouseEventListener::screenForGlobalPos(const QPointF &globalPos)
{
const auto screens = QGuiApplication::screens();
for (QScreen *screen : screens) {
if (screen->geometry().contains(globalPos.toPoint())) {
return screen;
}
}
return nullptr;
}
void MouseEventListener::mouseUngrabEvent()
{
handleUngrab();
QQuickItem::mouseUngrabEvent();
}
void MouseEventListener::touchUngrabEvent()
{
handleUngrab();
QQuickItem::touchUngrabEvent();
}
void MouseEventListener::handleUngrab()
{
if (m_pressed) {
m_pressAndHoldTimer->stop();
m_pressed = false;
Q_EMIT pressedChanged();
Q_EMIT canceled();
}
}
#include "moc_mouseeventlistener.cpp"
@@ -0,0 +1,312 @@
/*
SPDX-FileCopyrightText: 2011 Marco Martin <notmart@gmail.com>
SPDX-FileCopyrightText: 2013 Sebastian Kügler <sebas@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef MOUSEEVENTLISTENER_H
#define MOUSEEVENTLISTENER_H
#include <QQuickItem>
/**
* This item spies on mouse events from all child objects including child MouseAreas regardless
* of whether the child MouseArea propagates events. It does not accept the event.
*
* In addition unlike MouseArea events include the mouse position in global coordinates and provides
* the screen the mouse is in.
*/
class KDeclarativeMouseEvent : public QObject
{
Q_OBJECT
QML_ANONYMOUS
Q_PROPERTY(int x READ x)
Q_PROPERTY(int y READ y)
Q_PROPERTY(int screenX READ screenX)
Q_PROPERTY(int screenY READ screenY)
Q_PROPERTY(int button READ button)
Q_PROPERTY(Qt::MouseButtons buttons READ buttons)
Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers)
Q_PROPERTY(QScreen *screen READ screen CONSTANT)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted NOTIFY acceptedChanged)
Q_PROPERTY(int source READ source)
public:
KDeclarativeMouseEvent(int x,
int y,
int screenX,
int screenY,
Qt::MouseButton button,
Qt::MouseButtons buttons,
Qt::KeyboardModifiers modifiers,
QScreen *screen,
Qt::MouseEventSource source)
: m_x(x)
, m_y(y)
, m_screenX(screenX)
, m_screenY(screenY)
, m_button(button)
, m_buttons(buttons)
, m_modifiers(modifiers)
, m_screen(screen)
, m_source(source)
{
}
int x() const
{
return m_x;
}
int y() const
{
return m_y;
}
int screenX() const
{
return m_screenX;
}
int screenY() const
{
return m_screenY;
}
int button() const
{
return m_button;
}
Qt::MouseButtons buttons() const
{
return m_buttons;
}
Qt::KeyboardModifiers modifiers() const
{
return m_modifiers;
}
QScreen *screen() const
{
return m_screen;
}
int source() const
{
return m_source;
}
bool isAccepted() const
{
return m_accepted;
}
void setAccepted(bool accepted)
{
if (m_accepted != accepted) {
m_accepted = accepted;
Q_EMIT acceptedChanged();
}
}
// only for internal usage
void setX(int x)
{
m_x = x;
}
void setY(int y)
{
m_y = y;
}
Q_SIGNALS:
void acceptedChanged();
private:
int m_x;
int m_y;
int m_screenX;
int m_screenY;
Qt::MouseButton m_button;
Qt::MouseButtons m_buttons;
Qt::KeyboardModifiers m_modifiers;
QScreen *m_screen;
bool m_accepted = false;
int m_source;
};
class KDeclarativeWheelEvent : public QObject
{
Q_OBJECT
QML_ANONYMOUS
Q_PROPERTY(int x READ x CONSTANT)
Q_PROPERTY(int y READ y CONSTANT)
Q_PROPERTY(int screenX READ screenX CONSTANT)
Q_PROPERTY(int screenY READ screenY CONSTANT)
Q_PROPERTY(int deltaX READ deltaX CONSTANT)
Q_PROPERTY(int deltaY READ deltaY CONSTANT)
Q_PROPERTY(int delta READ deltaY CONSTANT) // deprecated in favor of deltaY. TODO KF6: remove
Q_PROPERTY(Qt::MouseButtons buttons READ buttons CONSTANT)
Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers CONSTANT)
Q_PROPERTY(Qt::Orientation orientation READ orientation CONSTANT) // deprecated. TODO KF6: remove
public:
KDeclarativeWheelEvent(QPointF pos,
QPoint screenPos,
QPoint angleDelta,
Qt::MouseButtons buttons,
Qt::KeyboardModifiers modifiers,
Qt::Orientation orientation)
: m_x(pos.x())
, m_y(pos.y())
, m_screenX(screenPos.x())
, m_screenY(screenPos.y())
, m_angleDelta(angleDelta)
, m_buttons(buttons)
, m_modifiers(modifiers)
, m_orientation(orientation)
{
}
int x() const
{
return m_x;
}
int y() const
{
return m_y;
}
int screenX() const
{
return m_screenX;
}
int screenY() const
{
return m_screenY;
}
int deltaX() const
{
return m_angleDelta.x();
}
int deltaY() const
{
return m_angleDelta.y();
}
Qt::MouseButtons buttons() const
{
return m_buttons;
}
Qt::KeyboardModifiers modifiers() const
{
return m_modifiers;
}
Qt::Orientation orientation()
{
return m_orientation;
} // TODO KF6: remove
// only for internal usage
void setX(int x)
{
m_x = x;
}
void setY(int y)
{
m_y = y;
}
private:
int m_x;
int m_y;
int m_screenX;
int m_screenY;
QPoint m_angleDelta;
Qt::MouseButtons m_buttons;
Qt::KeyboardModifiers m_modifiers;
Qt::Orientation m_orientation;
};
class MouseEventListener : public QQuickItem
{
Q_OBJECT
QML_ELEMENT
/**
* This property holds whether hover events are handled.
* By default hover events are disabled
*/
Q_PROPERTY(bool hoverEnabled READ hoverEnabled WRITE setHoverEnabled NOTIFY hoverEnabledChanged)
/**
* True if this MouseEventListener or any of its children contains the mouse cursor:
* this property will change only when the mouse button is pressed if hoverEnabled is false.
*/
Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged)
Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged)
/**
* This property holds the cursor shape for this mouse area.
* Note that on platforms that do not display a mouse cursor this may have no effect.
*/
Q_PROPERTY(Qt::CursorShape cursorShape READ cursorShape WRITE setCursorShape RESET unsetCursor NOTIFY cursorShapeChanged)
/**
* True if the mouse is pressed in the item or any of its children
*/
Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged)
public:
MouseEventListener(QQuickItem *parent = nullptr);
~MouseEventListener() override;
bool containsMouse() const;
void setHoverEnabled(bool enable);
bool hoverEnabled() const;
bool isPressed() const;
Qt::MouseButtons acceptedButtons() const;
void setAcceptedButtons(Qt::MouseButtons buttons);
Qt::CursorShape cursorShape() const;
void setCursorShape(Qt::CursorShape shape);
protected:
void hoverEnterEvent(QHoverEvent *event) override;
void hoverLeaveEvent(QHoverEvent *event) override;
void hoverMoveEvent(QHoverEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
bool childMouseEventFilter(QQuickItem *item, QEvent *event) override;
void mouseUngrabEvent() override;
void touchUngrabEvent() override;
Q_SIGNALS:
void pressed(KDeclarativeMouseEvent *mouse);
void positionChanged(KDeclarativeMouseEvent *mouse);
void released(KDeclarativeMouseEvent *mouse);
void clicked(KDeclarativeMouseEvent *mouse);
void pressAndHold(KDeclarativeMouseEvent *mouse);
void wheelMoved(KDeclarativeWheelEvent *wheel);
void containsMouseChanged(bool containsMouseChanged);
void hoverEnabledChanged(bool hoverEnabled);
void acceptedButtonsChanged();
void cursorShapeChanged();
void pressedChanged();
void canceled();
private Q_SLOTS:
void handlePressAndHold();
void handleUngrab();
private:
static QScreen *screenForGlobalPos(const QPointF &globalPos);
bool m_pressed;
KDeclarativeMouseEvent *m_pressAndHoldEvent;
QPointF m_buttonDownPos;
// Important: used only for comparison. If you will ever need to access this pointer, make it a QWeakPointer
QEvent *m_lastEvent;
QTimer *m_pressAndHoldTimer;
bool m_containsMouse = false;
bool m_childContainsMouse = false;
Qt::MouseButtons m_acceptedButtons;
};
#endif
@@ -0,0 +1,183 @@
/*
SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2015 Luca Beltrame <lbeltrame@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "qimageitem.h"
#include <QPainter>
QImageItem::QImageItem(QQuickItem *parent)
: QQuickPaintedItem(parent)
, m_fillMode(QImageItem::Stretch)
{
setFlag(ItemHasContents, true);
}
QImageItem::~QImageItem()
{
}
void QImageItem::setImage(const QImage &image)
{
bool oldImageNull = m_image.isNull();
m_image = image;
updatePaintedRect();
update();
Q_EMIT nativeWidthChanged();
Q_EMIT nativeHeightChanged();
Q_EMIT imageChanged();
if (oldImageNull != m_image.isNull()) {
Q_EMIT nullChanged();
}
}
QImage QImageItem::image() const
{
return m_image;
}
void QImageItem::resetImage()
{
setImage(QImage());
}
int QImageItem::nativeWidth() const
{
return m_image.size().width() / m_image.devicePixelRatio();
}
int QImageItem::nativeHeight() const
{
return m_image.size().height() / m_image.devicePixelRatio();
}
QImageItem::FillMode QImageItem::fillMode() const
{
return m_fillMode;
}
void QImageItem::setFillMode(QImageItem::FillMode mode)
{
if (mode == m_fillMode) {
return;
}
m_fillMode = mode;
updatePaintedRect();
update();
Q_EMIT fillModeChanged();
}
void QImageItem::paint(QPainter *painter)
{
if (m_image.isNull()) {
return;
}
painter->save();
painter->setRenderHint(QPainter::Antialiasing, smooth());
painter->setRenderHint(QPainter::SmoothPixmapTransform, smooth());
if (m_fillMode == TileVertically) {
painter->scale(width() / (qreal)m_image.width(), 1);
}
if (m_fillMode == TileHorizontally) {
painter->scale(1, height() / (qreal)m_image.height());
}
if (m_fillMode == Pad) {
QRect centeredRect = m_paintedRect;
centeredRect.moveCenter(m_image.rect().center());
painter->drawImage(m_paintedRect, m_image, centeredRect);
} else if (m_fillMode >= Tile) {
painter->drawTiledPixmap(m_paintedRect, QPixmap::fromImage(m_image));
} else {
painter->drawImage(m_paintedRect, m_image, m_image.rect());
}
painter->restore();
}
bool QImageItem::isNull() const
{
return m_image.isNull();
}
int QImageItem::paintedWidth() const
{
if (m_image.isNull()) {
return 0;
}
return m_paintedRect.width();
}
int QImageItem::paintedHeight() const
{
if (m_image.isNull()) {
return 0;
}
return m_paintedRect.height();
}
void QImageItem::updatePaintedRect()
{
if (m_image.isNull()) {
return;
}
QRectF sourceRect = m_paintedRect;
QRectF destRect;
switch (m_fillMode) {
case PreserveAspectFit: {
QSizeF scaled = m_image.size();
scaled.scale(boundingRect().size(), Qt::KeepAspectRatio);
destRect = QRectF(QPoint(0, 0), scaled);
destRect.moveCenter(boundingRect().center().toPoint());
break;
}
case PreserveAspectCrop: {
QSizeF scaled = m_image.size();
scaled.scale(boundingRect().size(), Qt::KeepAspectRatioByExpanding);
destRect = QRectF(QPoint(0, 0), scaled);
destRect.moveCenter(boundingRect().center().toPoint());
break;
}
case TileVertically: {
destRect = boundingRect().toRect();
destRect.setWidth(destRect.width() / (width() / (qreal)m_image.width()));
break;
}
case TileHorizontally: {
destRect = boundingRect().toRect();
destRect.setHeight(destRect.height() / (height() / (qreal)m_image.height()));
break;
}
case Stretch:
case Tile:
case Pad:
default:
destRect = boundingRect().toRect();
}
if (destRect != sourceRect) {
m_paintedRect = destRect.toRect();
Q_EMIT paintedHeightChanged();
Q_EMIT paintedWidthChanged();
}
}
void QImageItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
QQuickPaintedItem::geometryChange(newGeometry, oldGeometry);
updatePaintedRect();
}
#include "moc_qimageitem.cpp"
@@ -0,0 +1,80 @@
/*
SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2015 Luca Beltrame <lbeltrame@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef QIMAGEITEM_H
#define QIMAGEITEM_H
#include <QImage>
#include <QQuickPaintedItem>
class QImageItem : public QQuickPaintedItem
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QImage image READ image WRITE setImage NOTIFY imageChanged RESET resetImage)
Q_PROPERTY(int nativeWidth READ nativeWidth NOTIFY nativeWidthChanged)
Q_PROPERTY(int nativeHeight READ nativeHeight NOTIFY nativeHeightChanged)
Q_PROPERTY(int paintedWidth READ paintedWidth NOTIFY paintedWidthChanged)
Q_PROPERTY(int paintedHeight READ paintedHeight NOTIFY paintedHeightChanged)
Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged)
Q_PROPERTY(bool null READ isNull NOTIFY nullChanged)
public:
enum FillMode {
Stretch, // the image is scaled to fit
PreserveAspectFit, // the image is scaled uniformly to fit without cropping
PreserveAspectCrop, // the image is scaled uniformly to fill, cropping if necessary
Tile, // the image is duplicated horizontally and vertically
TileVertically, // the image is stretched horizontally and tiled vertically
TileHorizontally, // the image is stretched vertically and tiled horizontally
Pad, /**< the image is not transformed @since 5.96 **/
};
Q_ENUM(FillMode)
explicit QImageItem(QQuickItem *parent = nullptr);
~QImageItem() override;
void setImage(const QImage &image);
QImage image() const;
void resetImage();
int nativeWidth() const;
int nativeHeight() const;
int paintedWidth() const;
int paintedHeight() const;
FillMode fillMode() const;
void setFillMode(FillMode mode);
void paint(QPainter *painter) override;
bool isNull() const;
Q_SIGNALS:
void nativeWidthChanged();
void nativeHeightChanged();
void fillModeChanged();
void imageChanged();
void nullChanged();
void paintedWidthChanged();
void paintedHeightChanged();
protected:
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
private:
QImage m_image;
FillMode m_fillMode;
QRect m_paintedRect;
private Q_SLOTS:
void updatePaintedRect();
};
#endif
@@ -0,0 +1,190 @@
/*
SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2015 Luca Beltrame <lbeltrame@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "qpixmapitem.h"
#include <QPainter>
#include <QQuickWindow>
QPixmapItem::QPixmapItem(QQuickItem *parent)
: QQuickPaintedItem(parent)
, m_fillMode(QPixmapItem::Stretch)
{
setFlag(ItemHasContents, true);
}
QPixmapItem::~QPixmapItem()
{
}
void QPixmapItem::setPixmap(const QPixmap &pixmap)
{
bool oldPixmapNull = m_pixmap.isNull();
m_pixmap = pixmap;
updatePaintedRect();
update();
Q_EMIT nativeWidthChanged();
Q_EMIT nativeHeightChanged();
Q_EMIT pixmapChanged();
if (oldPixmapNull != m_pixmap.isNull()) {
Q_EMIT nullChanged();
}
}
QPixmap QPixmapItem::pixmap() const
{
return m_pixmap;
}
void QPixmapItem::resetPixmap()
{
setPixmap(QPixmap());
}
int QPixmapItem::nativeWidth() const
{
return m_pixmap.size().width() / m_pixmap.devicePixelRatio();
}
int QPixmapItem::nativeHeight() const
{
return m_pixmap.size().height() / m_pixmap.devicePixelRatio();
}
QPixmapItem::FillMode QPixmapItem::fillMode() const
{
return m_fillMode;
}
void QPixmapItem::setFillMode(QPixmapItem::FillMode mode)
{
if (mode == m_fillMode) {
return;
}
m_fillMode = mode;
updatePaintedRect();
update();
Q_EMIT fillModeChanged();
}
void QPixmapItem::paint(QPainter *painter)
{
if (m_pixmap.isNull()) {
return;
}
painter->save();
painter->setRenderHint(QPainter::Antialiasing, smooth());
painter->setRenderHint(QPainter::SmoothPixmapTransform, smooth());
if (m_fillMode == TileVertically) {
painter->scale(width() / (qreal)m_pixmap.width(), 1);
}
if (m_fillMode == TileHorizontally) {
painter->scale(1, height() / (qreal)m_pixmap.height());
}
if (m_fillMode >= Tile) {
painter->drawTiledPixmap(m_paintedRect, m_pixmap);
} else {
painter->drawPixmap(m_paintedRect, m_pixmap, m_pixmap.rect());
}
painter->restore();
}
bool QPixmapItem::isNull() const
{
return m_pixmap.isNull();
}
int QPixmapItem::paintedWidth() const
{
if (m_pixmap.isNull()) {
return 0;
}
return m_paintedRect.width();
}
int QPixmapItem::paintedHeight() const
{
if (m_pixmap.isNull()) {
return 0;
}
return m_paintedRect.height();
}
void QPixmapItem::updatePaintedRect()
{
if (m_pixmap.isNull()) {
return;
}
QRectF sourceRect = m_paintedRect;
QRectF destRect;
QRectF bounds = boundingRect();
auto posAdjusted = [this](const QPointF point) {
auto w = window();
if (!w) {
return point;
}
const auto effectiveDpr = w->effectiveDevicePixelRatio();
QPointF globalPixelPos = mapToScene(point) * effectiveDpr;
QPointF posAdjust = QPointF(globalPixelPos.x() - std::round(globalPixelPos.x()), globalPixelPos.y() - std::round(globalPixelPos.y())) / effectiveDpr;
return (point - posAdjust);
};
switch (m_fillMode) {
case PreserveAspectFit: {
QSizeF scaled = m_pixmap.size();
scaled.scale(boundingRect().size(), Qt::KeepAspectRatio);
destRect = QRectF(QPoint(0, 0), scaled);
destRect.moveCenter(posAdjusted(bounds.center()));
break;
}
case PreserveAspectCrop: {
QSizeF scaled = m_pixmap.size();
scaled.scale(boundingRect().size(), Qt::KeepAspectRatioByExpanding);
destRect = QRectF(QPoint(0, 0), scaled);
destRect.moveCenter(posAdjusted(bounds.center()));
break;
}
case TileVertically: {
destRect = bounds.toRect();
destRect.setWidth(destRect.width() / (width() / (qreal)m_pixmap.width()));
break;
}
case TileHorizontally: {
destRect = bounds.toRect();
destRect.setHeight(destRect.height() / (height() / (qreal)m_pixmap.height()));
break;
}
case Stretch:
case Tile:
default:
destRect = bounds.toRect();
}
if (destRect != sourceRect) {
m_paintedRect = destRect.toRect();
Q_EMIT paintedHeightChanged();
Q_EMIT paintedWidthChanged();
}
}
void QPixmapItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
QQuickPaintedItem::geometryChange(newGeometry, oldGeometry);
updatePaintedRect();
}
#include "moc_qpixmapitem.cpp"
@@ -0,0 +1,79 @@
/*
SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2015 Luca Beltrame <lbeltrame@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef QPIXMAPITEM_H
#define QPIXMAPITEM_H
#include <QPixmap>
#include <QQuickPaintedItem>
class QPixmapItem : public QQuickPaintedItem
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap NOTIFY pixmapChanged RESET resetPixmap)
Q_PROPERTY(int nativeWidth READ nativeWidth NOTIFY nativeWidthChanged)
Q_PROPERTY(int nativeHeight READ nativeHeight NOTIFY nativeHeightChanged)
Q_PROPERTY(int paintedWidth READ paintedWidth NOTIFY paintedWidthChanged)
Q_PROPERTY(int paintedHeight READ paintedHeight NOTIFY paintedHeightChanged)
Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged)
Q_PROPERTY(bool null READ isNull NOTIFY nullChanged)
public:
enum FillMode {
Stretch, // the image is scaled to fit
PreserveAspectFit, // the image is scaled uniformly to fit without cropping
PreserveAspectCrop, // the image is scaled uniformly to fill, cropping if necessary
Tile, // the image is duplicated horizontally and vertically
TileVertically, // the image is stretched horizontally and tiled vertically
TileHorizontally, // the image is stretched vertically and tiled horizontally
};
Q_ENUM(FillMode)
explicit QPixmapItem(QQuickItem *parent = nullptr);
~QPixmapItem() override;
void setPixmap(const QPixmap &pixmap);
QPixmap pixmap() const;
void resetPixmap();
int nativeWidth() const;
int nativeHeight() const;
int paintedWidth() const;
int paintedHeight() const;
FillMode fillMode() const;
void setFillMode(FillMode mode);
void paint(QPainter *painter) override;
bool isNull() const;
Q_SIGNALS:
void nativeWidthChanged();
void nativeHeightChanged();
void fillModeChanged();
void pixmapChanged();
void nullChanged();
void paintedWidthChanged();
void paintedHeightChanged();
protected:
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
private:
QPixmap m_pixmap;
FillMode m_fillMode;
QRect m_paintedRect;
private Q_SLOTS:
void updatePaintedRect();
};
#endif