How to create qt label hover effect? - c++

I must use hover event on Qt label, but I can't found no information about that.
I try use something like ui->label->setText("<a>ads</a>") and onLinkHovered but it's not work correct.
I must change text on hover.

The most flexible solution would be to create your own widget which inherits from QLabel. This way, you could override the enterEvent and leaveEvent #Jeremy and #Moe are writing about which are protected. As a part of these methods implementation you could change the text or decoration accordingly. For example:
class CustomLabel : public QLabel
{
Q_OBJECT
public:
CustomLabel(QWidget* parent = nullptr) : QLabel(parent){ }
protected:
void enterEvent(QEvent *ev) override
{
setStyleSheet("QLabel { background-color : blue; }");
}
void leaveEvent(QEvent *ev) override
{
setStyleSheet("QLabel { background-color : green; }");
}
};
Another approach, but a lot less flexible would be to set the href attribute for the link tag you have specified in label text. This way text would be treated as actual link and you could use the linkHovered signal to connect to. For example:
ui->label->setText("<a href='www.google.com'>link</a>");
connect(ui->label, &QLabel::linkHovered, this, [this](const QString&)
{
// do smth with the widget/text
});
However, please denote that this way you could only make a modification on the hover event.
So if you need to bring the label back to its original state, the first option is the way to go.

Make use of enterEvent and leaveEvent of QLabel.
Create a subclass of QLabel like this for example:
class MyLabel : public QLabel
{
public:
MyLabel();
MyLabel(char* text, MainWindow* w) : QLabel(text, w) { }
void enterEvent(QEvent *event);
void leaveEvent(QEvent *event);
};
and override enterEvent and leaveEvent like this:
void MyLabel::enterEvent(QEvent *event) {
qDebug() << "Entered!";
// Change your text here
}
void MyLabel::leaveEvent(QEvent *event) {
qDebug() << "Left!";
}
You can create instances of this class now like this:
MainWindow w;
MyLabel myLabel("A Test Label", &w);

If you want to determine whether mouse hovers over text inside the QLabel you can use
void mouseMoveEvent(QMouseEvent* event);
and check inside it whether mouse cursor is in bounding rectangle of text.
void PressLabel::mouseMoveEvent(QMouseEvent* event) {
QRect bRect = getTextComponentRectangle();
m_mouseCoord = event->pos();
if(bRect.contains(event->pos())) {
// Mouse pointer over text.
} else {
// Mouse pointer outside text.
}
QLabel::mouseMoveEvent(event);
}
Turn on mouse tracking for your widget to make mouseMoveEvent occur also when mouse button is not pressed.
setMouseTracking(true);
And finally the function that does the calculation of text bounding rectangle. Code below take into account some unexpected cases, too. I tried it with Qt 5.9.1.
For calculation of effective indent in negative case refer to http://doc.qt.io/archives/qt-4.8/qlabel.html#indent-prop .
QRect PressLabel::getTextComponentRectangle() const {
if(frameWidth() < 0) {
throw std::runtime_error("Negative frame width.");
}
int effectiveIndent = indent();
int trueMargin = margin();
if(effectiveIndent < 0) {
if(frameWidth() == 0 || margin() > 0) { // (1)
effectiveIndent = 0;
} else if(frameWidth() > 0) {
QFontMetrics fm(font());
effectiveIndent = fm.width(QChar('x')) / 2;
}
if(frameWidth() > 0 && margin() < 0) { // (2)
trueMargin = 0;
}
}
QFontMetrics fm(font());
QRect bRect = fm.boundingRect(text());
bRect.setWidth(fm.width(text()));
int indentOffset = effectiveIndent + trueMargin + frameWidth();
int offsetX = 0;
int offsetY = 0;
if(alignment() & Qt::AlignHCenter) {
offsetX = rect().width() / 2 - bRect.width() / 2;
} else if(alignment() & Qt::AlignRight) {
offsetX = rect().width() - bRect.width() - indentOffset;
} else if(alignment() & Qt::AlignJustify) {
offsetX = trueMargin + frameWidth();
} else if(alignment() & Qt::AlignLeft) {
offsetX = indentOffset;
}
if(alignment() & Qt::AlignVCenter) {
offsetY = rect().height() / 2 - bRect.height() / 2;
} else if(alignment() & Qt::AlignBottom) {
offsetY = rect().height() - bRect.height() - indentOffset;
} else if(alignment() & Qt::AlignTop) {
offsetY = indentOffset;
}
bRect.moveTopLeft(rect().topLeft());
bRect.setX(bRect.x() + offsetX);
bRect.setWidth(bRect.width() + offsetX);
bRect.setY(bRect.y() + offsetY);
bRect.setHeight(bRect.height() + offsetY);
return bRect;
}
Unexpected cases:
(1) For indent < 0 and margin > 0 effective indent is 0, not width('x')/2 as it is intended to be for negative indent.
(2) For indent < 0 and margin < 0 true margin is 0 and isn't summed to make offset.

I have found that you can also achieve this through the style sheet.
self.label = QtWidgets.QLabel("Toast")
self.label.setStyleSheet("QLabel{color: white;} QLabel:hover {color: blue;}")

Related

Set tab close button position on vertical tabs issue

I want to change the default tab widget close button and set my icon instead. The problem is that it draws the icon on the text. I want to draw the X to the right.
Code:
void AppTabBar::paintEvent(QPaintEvent *event)
{
QStylePainter painter(this);
QStyleOptionTab opt;
for (int i = 0; i < this->count(); i++) {
initStyleOption(&opt, i);
opt.text = painter.fontMetrics().elidedText(opt.text, Qt::ElideRight, 70);
painter.drawControl(QStyle::CE_TabBarTabShape, opt);
painter.save();
QSize s = opt.rect.size();
if (tabPos != AppTabPosition::Top && tabPos != AppTabPosition::Bottom) {
s.transpose();
}
QRect r(QPoint(), s);
r.moveCenter(opt.rect.center());
opt.rect = r;
QPoint c = tabRect(i).center();
painter.translate(c);
if (tabPos == AppTabPosition::Left) {
painter.rotate(90);
} else if (tabPos == AppTabPosition::Right) {
painter.rotate(270); //90 - left pos, 270 - right pos
}
painter.translate(-c);
painter.drawControl(QStyle::CE_TabBarTabLabel, opt);
painter.restore();
}
QWidget::paintEvent(event);
}
void AppTabStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
{
if (element == PE_IndicatorTabClose) {
int size = proxy()->pixelMetric(QStyle::PM_SmallIconSize);
QIcon::Mode mode = option->state & State_Enabled ? (option->state & State_Raised ? QIcon::Active : QIcon::Normal) : QIcon::Disabled;
if (!(option->state & State_Raised) && !(option->state & State_Sunken) && !(option->state & QStyle::State_Selected)) {
mode = QIcon::Disabled;
}
QIcon::State state = option->state & State_Sunken ? QIcon::On : QIcon::Off;
QPixmap pixmap = QIcon(":/Icons/cross_icon.png").pixmap(size, mode, state);
proxy()->drawItemPixmap(painter, option->rect, Qt::AlignRight, pixmap);
} else {
QProxyStyle::drawPrimitive(element, option, painter, widget);
}
}
Screenshot:
This issue only appears on the vertical tabs (AppTabPosition::Left or AppTabPosition::Right). Any ideas how to draw it to the right?
Edited (15.05.2021):
I have set setTabsClosable(false);, then created the TabBarLabel class and set it to setTabButton method. It closes the tabs but the issue with overlapping the tab text still exists:
Screenshot:
It is a lot of code, so I have created and uploaded to Mega the test example to illustrate this issue. Here is my test example: TabExample.zip (6kb)
Any ideas how to change position of tab bar close button? Thank you.
Thank you.
Finally! I have fixed the issue with vertical tab close button position.
Code:
if (tabPos != AppTabPosition::Top && tabPos != AppTabPosition::Bottom) {
s.transpose();
if (this->tabsClosable()) { // check if tab is closable
QRect optRect = opt.rect;
optRect.setX(90); // set X pos of close button
optRect.setY(optRect.y() + 8); // calcs the Y pos of close button
optRect.setSize(QSize(12, 12));
this->tabButton(i, QTabBar::RightSide)->setGeometry(optRect);
}
}
Here I do not change the default opt.rect but instead I copy it to new QRect. Then I change the size and position of optRect and finally set geometry to tabButtton .
Screenshot:
The issue is resolved.

Qt c++ QPainter QVector with QPoint

I have QVector<QPoint> m_vertices; in my drawingwidget.h
class DrawingWidget: public QWidget {
Q_OBJECT
public:
DrawingWidget(MainWindow *parent = 0);
~DrawingWidget();
QVector<QPoint> m_vertices;
I trying to implement adding/deleting vertices on my mainwindow. I managed to make the add function, now it's should be easy to delete them, but i am a bit confused.
The main idea is, that i have a pop-up menu, where i can choose a "tool". I can add vertex, remove vertex, move vertex, add line, delete line. The idea is , when i choose for example "Add Vertex" then the "m_state" will change to "ADD_VERTEX_SELECTED" so i can only add vertices and nothing else.
enum DrawingWidgetState {
NO_TOOL_SELECTED,
ADD_VERTEX_SELECTED,
MOVE_VERTEX_SELECTED,
DELETE_VERTEX_SELECTED,
ADD_LINE_SELECTED,
DELETE_LINE_SELECTED
};
Drawing
void DrawingWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.fillRect(event->rect(), Qt::blue);
painter.setBrush(Qt::black);
for(int i = 0; i < m_vertices.size() ; i++) {
painter.drawEllipse(m_vertices[i], 20, 20);
}
MousePress event. On left click, I have to delete the vertex that I clicked on
void DrawingWidget::mousePressEvent(QMouseEvent *event) {
if(m_state == ADD_VERTEX_SELECTED) {
if(event->button() == Qt::LeftButton) {
//m_x = event->x();
//m_y = event->y();
//update();
QPoint point = event->pos();
m_vertices.append(point);
update();
}
}
if(m_state == DELETE_VERTEX_SELECTED) {
if(event->button() == Qt::LeftButton) {
m_vertices.clear();
}
}
}
How can i do that ?
This code uses std::find_if together with a lambda expression to find the closest point. You probably want to allow a bit more leeway than the exact circle radius, but that is up to you.
auto it = std::find_if(m_vertices.begin(), m_vertices.end(), [point = event->pos](QPoint v) {
auto d = v - point;
return QPoint::dotProduct(d, d) < 400; // dotProduct returns square of distance
});
if (it != m_vertices.end()) {
m_vertices.erase(it);
}

How QTableView or QListView is scrolling with hand drag?

in QGraphicview,
if we set it with : ui->graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
this code make graphicsview can scroll items with mouse pressed and drag.
How can we make QListView or QTableView as the QGraphicsView?
You will need to subclass these widgets and reimplement QWidget::mousePressEvent, QWidget::mousMoveEvent and QWidget::mouseReleaseEvent. However you will have to be careful because you may be interfering with actions that are mapped to these by default implementations (e.g. selecting) so it would need to be tweaked a bit. For example (assumed subclass of QListView):
void MyListView::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::RightButton) //lets map scrolling to right button
m_ScrollStart = event->pos(); //QPoint member, indicates the start of the scroll
else
QListView::mousePressEvent(event);
}
and then
void MyListView::mouseMoveEvent(QMouseEvent *event)
{
if(!m_ScrollStart.isNull()) //if the scroll was started
{
bool direction = (m_ScrollStart.y() < event->pos().y()); //determine direction, true is up (start is below current), false is down (start is above current)
int singleStep = (direction ? 10 : -10); //fill in the desired value
verticalScrollBar()->setValue(verticalScrollBar()->value() + singleStep);
//scroll by the certain amount in determined direction,
//you decide how much will be a single step... test and see what you like
}
QListView::mouseMoveEvent(event);
}
and finally
void MyListView::mouseReleaseEvent(QMouseEvent *event)
{
m_ScrollStart = QPoint(); //resets the scroll drag
QListView::mouseReleaseEvent(event);
}
like Resurrection did mention
You will need to subclass these widgets and reimplement QWidget::mousePressEvent, QWidget::mousMoveEvent and QWidget::mouseReleaseEvent
but below code is more preferred by us:
class MyListView : public QListView
{
typedef QListView super;
public:
explicit MyListView(QWidget *parent = 0);
protected:
// QWidget interface
void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE;
void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE;
void mouseMoveEvent(QMouseEvent *) Q_DECL_OVERRIDE;
private:
enum DragState {
DragStopped,
DragStarted,
Dragged
};
quint8 m_dragState;
int m_dragStartPos;
};
MyListView::MyListView(QWidget *parent)
: super(parent)
, m_dragState(DragStopped)
, m_dragStartPos(-1)
{
}
void MyListView::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton) {
m_dragState = DragStarted;
m_dragStartPos = event->pos().y();
} else
super::mousePressEvent(event);
}
void MyListView::mouseReleaseEvent(QMouseEvent *event)
{
if(m_dragState) {
m_dragState = DragStopped;
m_dragStartPos = -1;
return;
}
super::mouseReleaseEvent(event);
}
void MyListView::mouseMoveEvent(QMouseEvent *event)
{
if(m_dragState != DragStopped) {
const int itemSize = sizeHintForRow(0) / 2;
const int distance = qAbs(m_dragStartPos - event->pos().y());
if(distance > 10)
m_dragState = Dragged;
if(distance > itemSize) {
QScrollBar *scrollBar = this->verticalScrollBar();
int stepCount = (distance/itemSize);
if(m_dragStartPos < event->pos().y())
stepCount = -stepCount; //scrolling up
scrollBar->setValue(scrollBar->value() + (stepCount * scrollBar->singleStep()));
m_dragStartPos = event->y();
}
return;
}
super::mouseMoveEvent(event);
}

Draw a scale ruler in QGraphicsScene?

I am building a map widget (something like the google map) using Qt, basically I used a QGraphicsScene to display the map tile.
Now I want to add a scale ruler to the widget just like the one in google map.
Any suggestions about how could I realize this?
Take a look at this example:
Structure your code base as following:
Write a class inheriting descendants class of QAbstractScrollArea (As example QGraphicsView, QMdiArea, QPlainTextEdit, QScrollArea, QTextEdit, QColumnView, QHeaderView, QListView, QTableView, QTreeView etc.)
In the constructor of your class call setViewportMargins and set the margins of left/top/right/bottom areas length.
Create a QGridLayout and adds your custom Ruler/Scale in the layout.
Set this layout calling setLayout
Example:
setViewportMargins(RULER_BREADTH,RULER_BREADTH,0,0);
QGridLayout* gridLayout = new QGridLayout();
gridLayout->setSpacing(0);
gridLayout->setMargin(0);
mHorzRuler = new QDRuler(QDRuler::Horizontal);
mVertRuler = new QDRuler(QDRuler::Vertical);
QWidget* fake = new QWidget();
fake->setBackgroundRole(QPalette::Window);
fake->setFixedSize(RULER_BREADTH,RULER_BREADTH);
gridLayout->addWidget(fake,0,0);
gridLayout->addWidget(mHorzRuler,0,1);
gridLayout->addWidget(mVertRuler,1,0);
gridLayout->addWidget(this->viewport(),1,1);
this->setLayout(gridLayout);
QDRuler: The ruler class
#define RULER_BREADTH 20
class QDRuler : public QWidget
{
Q_OBJECT
Q_ENUMS(RulerType)
Q_PROPERTY(qreal origin READ origin WRITE setOrigin)
Q_PROPERTY(qreal rulerUnit READ rulerUnit WRITE setRulerUnit)
Q_PROPERTY(qreal rulerZoom READ rulerZoom WRITE setRulerZoom)
public:
enum RulerType { Horizontal, Vertical };
QDRuler(QDRuler::RulerType rulerType, QWidget* parent)
: QWidget(parent), mRulerType(rulerType), mOrigin(0.), mRulerUnit(1.),
mRulerZoom(1.), mMouseTracking(false), mDrawText(false)
{
setMouseTracking(true);
QFont txtFont("Goudy Old Style", 5,20);
txtFont.setStyleHint(QFont::TypeWriter,QFont::PreferOutline);
setFont(txtFont);
}
QSize minimumSizeHint() const
{
return QSize(RULER_BREADTH,RULER_BREADTH);
}
QDRuler::RulerType rulerType() const
{
return mRulerType;
}
qreal origin() const
{
return mOrigin;
}
qreal rulerUnit() const
{
return mRulerUnit;
}
qreal rulerZoom() const
{
return mRulerZoom;
}
public slots:
void setOrigin(const qreal origin)
{
if (mOrigin != origin)
{
mOrigin = origin;
update();
}
}
void setRulerUnit(const qreal rulerUnit)
{
if (mRulerUnit != rulerUnit)
{
mRulerUnit = rulerUnit;
update();
}
}
void setRulerZoom(const qreal rulerZoom)
{
if (mRulerZoom != rulerZoom)
{
mRulerZoom = rulerZoom;
update();
}
}
void setCursorPos(const QPoint cursorPos)
{
mCursorPos = this->mapFromGlobal(cursorPos);
mCursorPos += QPoint(RULER_BREADTH,RULER_BREADTH);
update();
}
void setMouseTrack(const bool track)
{
if (mMouseTracking != track)
{
mMouseTracking = track;
update();
}
}
protected:
void mouseMoveEvent(QMouseEvent* event)
{
mCursorPos = event->pos();
update();
QWidget::mouseMoveEvent(event);
}
void paintEvent(QPaintEvent* event)
{
QPainter painter(this);
painter.setRenderHints(QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing);
QPen pen(Qt::black,0); // zero width pen is cosmetic pen
//pen.setCosmetic(true);
painter.setPen(pen);
// We want to work with floating point, so we are considering
// the rect as QRectF
QRectF rulerRect = this->rect();
// at first fill the rect
//painter.fillRect(rulerRect,QColor(220,200,180));
painter.fillRect(rulerRect,QColor(236,233,216));
// drawing a scale of 25
drawAScaleMeter(&painter,rulerRect,25,(Horizontal == mRulerType ? rulerRect.height()
: rulerRect.width())/2);
// drawing a scale of 50
drawAScaleMeter(&painter,rulerRect,50,(Horizontal == mRulerType ? rulerRect.height()
: rulerRect.width())/4);
// drawing a scale of 100
mDrawText = true;
drawAScaleMeter(&painter,rulerRect,100,0);
mDrawText = false;
// drawing the current mouse position indicator
painter.setOpacity(0.4);
drawMousePosTick(&painter);
painter.setOpacity(1.0);
// drawing no man's land between the ruler & view
QPointF starPt = Horizontal == mRulerType ? rulerRect.bottomLeft()
: rulerRect.topRight();
QPointF endPt = Horizontal == mRulerType ? rulerRect.bottomRight()
: rulerRect.bottomRight();
painter.setPen(QPen(Qt::black,2));
painter.drawLine(starPt,endPt);
}
private:
void drawAScaleMeter(QPainter* painter, QRectF rulerRect, qreal scaleMeter, qreal startPositoin)
{
// Flagging whether we are horizontal or vertical only to reduce
// to cheching many times
bool isHorzRuler = Horizontal == mRulerType;
scaleMeter = scaleMeter * mRulerUnit * mRulerZoom;
// Ruler rectangle starting mark
qreal rulerStartMark = isHorzRuler ? rulerRect.left() : rulerRect.top();
// Ruler rectangle ending mark
qreal rulerEndMark = isHorzRuler ? rulerRect.right() : rulerRect.bottom();
// Condition A # If origin point is between the start & end mard,
//we have to draw both from origin to left mark & origin to right mark.
// Condition B # If origin point is left of the start mark, we have to draw
// from origin to end mark.
// Condition C # If origin point is right of the end mark, we have to draw
// from origin to start mark.
if (mOrigin >= rulerStartMark && mOrigin <= rulerEndMark)
{
drawFromOriginTo(painter, rulerRect, mOrigin, rulerEndMark, 0, scaleMeter, startPositoin);
drawFromOriginTo(painter, rulerRect, mOrigin, rulerStartMark, 0, -scaleMeter, startPositoin);
}
else if (mOrigin < rulerStartMark)
{
int tickNo = int((rulerStartMark - mOrigin) / scaleMeter);
drawFromOriginTo(painter, rulerRect, mOrigin + scaleMeter * tickNo,
rulerEndMark, tickNo, scaleMeter, startPositoin);
}
else if (mOrigin > rulerEndMark)
{
int tickNo = int((mOrigin - rulerEndMark) / scaleMeter);
drawFromOriginTo(painter, rulerRect, mOrigin - scaleMeter * tickNo,
rulerStartMark, tickNo, -scaleMeter, startPositoin);
}
}
void drawFromOriginTo(QPainter* painter, QRectF rulerRect, qreal startMark, qreal endMark, int startTickNo, qreal step, qreal startPosition)
{
bool isHorzRuler = Horizontal == mRulerType;
int iterate = 0;
for (qreal current = startMark;
(step < 0 ? current >= endMark : current <= endMark); current += step)
{
qreal x1 = isHorzRuler ? current : rulerRect.left() + startPosition;
qreal y1 = isHorzRuler ? rulerRect.top() + startPosition : current;
qreal x2 = isHorzRuler ? current : rulerRect.right();
qreal y2 = isHorzRuler ? rulerRect.bottom() : current;
painter->drawLine(QLineF(x1,y1,x2,y2));
if (mDrawText)
{
QPainterPath txtPath;
txtPath.addText(x1 + 1,y1 + (isHorzRuler ? 7 : -2),this->font(),QString::number(qAbs(int(step) * startTickNo++)));
painter->drawPath(txtPath);
iterate++;
}
}
}
void drawMousePosTick(QPainter* painter)
{
if (mMouseTracking)
{
QPoint starPt = mCursorPos;
QPoint endPt;
if (Horizontal == mRulerType)
{
starPt.setY(this->rect().top());
endPt.setX(starPt.x());
endPt.setY(this->rect().bottom());
}
else
{
starPt.setX(this->rect().left());
endPt.setX(this->rect().right());
endPt.setY(starPt.y());
}
painter->drawLine(starPt,endPt);
}
}
private:
RulerType mRulerType;
qreal mOrigin;
qreal mRulerUnit;
qreal mRulerZoom;
QPoint mCursorPos;
bool mMouseTracking;
bool mDrawText;
};

Qt Creator, C++, mouse over function

I want to change the color of an ellipse when I move my mouse over it.
But I haven't found anything from reference and auto-complete from Qt Creator.
Do you guys know how to do it?
Some of my code:
void DrawingWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.fillRect(event->rect(), Qt::white);
for(int i = 0; i < pointList.size(); i++) {
if (pointList[i].x() >= 0 && pointList[i].y() >= 0)
painter.drawEllipse(pointList[i], 10, 10);
}
painter.drawLines(lineList);
m_mainWindow->updateCount();
}
Mouse press event handler:
void DrawingWidget::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton
&& event->buttons() == Qt::LeftButton) {
// DO STUFFF
}
}
Mouse move event handler:
void DrawingWidget::mouseMoveEvent(QMouseEvent *event) {
if (m_mainWindow->getSelectedTool() == MainWindow::moveVertexTool) {
m_x = event->x();
m_y = event->y();
if (isPointNear(m_x, m_y)) {
//STUFF
}
update();
}
}
}
Now I just need a mouse OVER event (handler).
I think what you are looking for are enter and leave events.
Use QWidget::underMouse() for check if the widget is under the mouse cursor.
For example:
void IconButton::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// Note isDown should really use the active state but in most styles
// this has no proper feedback
QIcon::Mode mode = QIcon::Disabled;
if (isEnabled()) {
if (isDown())
mode = QIcon::Selected;
else
mode = underMouse() ? QIcon::Active : QIcon::Normal;
}
QPixmap pixmap = icon().pixmap(iconSize(), mode);
QRect pixmapRect = QRect(0, 0, pixmap.width(), pixmap.height());
pixmapRect.moveCenter(rect().center());
if (m_autoHide)
painter.setOpacity(m_iconOpacity);
painter.drawPixmap(pixmapRect, pixmap);
}
This value is not updated properly during drag and drop operations.