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:
@@ -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()
|
||||
@@ -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)
|
||||
+6
@@ -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)
|
||||
+370
@@ -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"
|
||||
+156
@@ -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
|
||||
+57
@@ -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"
|
||||
+121
@@ -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
|
||||
+150
@@ -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"
|
||||
+95
@@ -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
|
||||
+156
@@ -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"
|
||||
+100
@@ -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
|
||||
+80
@@ -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"
|
||||
+92
@@ -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
|
||||
+27
@@ -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"
|
||||
+21
@@ -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
|
||||
+20
@@ -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")
|
||||
}
|
||||
+23
@@ -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)
|
||||
+66
@@ -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;
|
||||
}
|
||||
|
||||
+165
@@ -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);
|
||||
}
|
||||
+36
@@ -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;
|
||||
}
|
||||
+12
@@ -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)
|
||||
+109
@@ -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();
|
||||
}
|
||||
}
|
||||
+198
@@ -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.
|
||||
+47
@@ -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)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "enums.h"
|
||||
+28
@@ -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)
|
||||
+150
@@ -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"
|
||||
+64
@@ -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
|
||||
+206
@@ -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"
|
||||
+63
@@ -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
|
||||
+269
@@ -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 ¶m1,
|
||||
const QString ¶m2,
|
||||
const QString ¶m3,
|
||||
const QString ¶m4,
|
||||
const QString ¶m5,
|
||||
const QString ¶m6,
|
||||
const QString ¶m7,
|
||||
const QString ¶m8,
|
||||
const QString ¶m9,
|
||||
const QString ¶m10) 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 ¶m1,
|
||||
const QString ¶m2,
|
||||
const QString ¶m3,
|
||||
const QString ¶m4,
|
||||
const QString ¶m5,
|
||||
const QString ¶m6,
|
||||
const QString ¶m7,
|
||||
const QString ¶m8,
|
||||
const QString ¶m9,
|
||||
const QString ¶m10) 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 ¶m1,
|
||||
const QString ¶m2,
|
||||
const QString ¶m3,
|
||||
const QString ¶m4,
|
||||
const QString ¶m5,
|
||||
const QString ¶m6,
|
||||
const QString ¶m7,
|
||||
const QString ¶m8,
|
||||
const QString ¶m9,
|
||||
const QString ¶m10) 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 ¶m1,
|
||||
const QString ¶m2,
|
||||
const QString ¶m3,
|
||||
const QString ¶m4,
|
||||
const QString ¶m5,
|
||||
const QString ¶m6,
|
||||
const QString ¶m7,
|
||||
const QString ¶m8,
|
||||
const QString ¶m9,
|
||||
const QString ¶m10) 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"
|
||||
+91
@@ -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 ¶m1 = QString(),
|
||||
const QString ¶m2 = QString(),
|
||||
const QString ¶m3 = QString(),
|
||||
const QString ¶m4 = QString(),
|
||||
const QString ¶m5 = QString(),
|
||||
const QString ¶m6 = QString(),
|
||||
const QString ¶m7 = QString(),
|
||||
const QString ¶m8 = QString(),
|
||||
const QString ¶m9 = QString(),
|
||||
const QString ¶m10 = QString()) const;
|
||||
|
||||
Q_INVOKABLE QString i18nc(const QString &context,
|
||||
const QString &message,
|
||||
const QString ¶m1 = QString(),
|
||||
const QString ¶m2 = QString(),
|
||||
const QString ¶m3 = QString(),
|
||||
const QString ¶m4 = QString(),
|
||||
const QString ¶m5 = QString(),
|
||||
const QString ¶m6 = QString(),
|
||||
const QString ¶m7 = QString(),
|
||||
const QString ¶m8 = QString(),
|
||||
const QString ¶m9 = QString(),
|
||||
const QString ¶m10 = QString()) const;
|
||||
|
||||
Q_INVOKABLE QString i18np(const QString &singular,
|
||||
const QString &plural,
|
||||
const QString ¶m1 = QString(),
|
||||
const QString ¶m2 = QString(),
|
||||
const QString ¶m3 = QString(),
|
||||
const QString ¶m4 = QString(),
|
||||
const QString ¶m5 = QString(),
|
||||
const QString ¶m6 = QString(),
|
||||
const QString ¶m7 = QString(),
|
||||
const QString ¶m8 = QString(),
|
||||
const QString ¶m9 = QString(),
|
||||
const QString ¶m10 = QString()) const;
|
||||
|
||||
Q_INVOKABLE QString i18ncp(const QString &context,
|
||||
const QString &singular,
|
||||
const QString &plural,
|
||||
const QString ¶m1 = QString(),
|
||||
const QString ¶m2 = QString(),
|
||||
const QString ¶m3 = QString(),
|
||||
const QString ¶m4 = QString(),
|
||||
const QString ¶m5 = QString(),
|
||||
const QString ¶m6 = QString(),
|
||||
const QString ¶m7 = QString(),
|
||||
const QString ¶m8 = QString(),
|
||||
const QString ¶m9 = QString(),
|
||||
const QString ¶m10 = 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)
|
||||
};
|
||||
+22
@@ -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)
|
||||
+12
@@ -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
|
||||
+119
@@ -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"
|
||||
+87
@@ -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
|
||||
+506
@@ -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"
|
||||
+312
@@ -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
|
||||
+183
@@ -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"
|
||||
+80
@@ -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
|
||||
+190
@@ -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"
|
||||
+79
@@ -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
|
||||
Reference in New Issue
Block a user