1
0
mirror of https://invent.kde.org/system/dolphin synced 2024-07-07 10:51:45 +00:00

Make main view react to context menu events

Before this commit, Dolphin's main view would not react to any
context menu events. It only showed context menus based on
hard-coded mouse or keyboard events i.e. mouse right-click and
presses of the "Menu" key.

This commit removes those hard-coded reactions and instead makes it
so the view shows a context menu whenever a QContextMenuEvent is
received. Therefore, a context menu will now be opened when any
platform- or system-specific context menu triggers are invoked e.g.
the Shift+F10 keyboard shortcut.

Aside from this, the only side-effect is a partial removal of an
unrelated bug: Previously, the hover highlight on items was never
cleared when the header column in details view mode was hovered.
With this commit, the hover is now correctly cleared most of the
time.
This commit is contained in:
Felix Ernst 2023-11-13 17:50:24 +01:00 committed by Felix Ernst
parent afc47abcb8
commit 046749b073
5 changed files with 90 additions and 61 deletions

View File

@ -138,6 +138,21 @@ void KItemListContainer::keyPressEvent(QKeyEvent *event)
}
}
void KItemListContainer::contextMenuEvent(QContextMenuEvent *event)
{
// Note copied from the keyPressEvent() method above because the same reasons probably also apply here.
// TODO: We should find a better way to handle the context menu events in the view.
// The reasons why we need this hack are:
// 1. Without reimplementing contextMenuEvent() here, the event would not reach the QGraphicsView.
// 2. By default, the KItemListView does not have the keyboard focus in the QGraphicsScene, so
// simply sending the event to the QGraphicsView which is the KItemListContainer's viewport
// does not work.
KItemListView *view = m_controller->view();
if (view) {
QApplication::sendEvent(view, event);
}
}
void KItemListContainer::showEvent(QShowEvent *event)
{
QAbstractScrollArea::showEvent(event);

View File

@ -46,6 +46,7 @@ public:
protected:
void keyPressEvent(QKeyEvent *event) override;
void contextMenuEvent(QContextMenuEvent *event) override;
void showEvent(QShowEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void scrollContentsBy(int dx, int dy) override;

View File

@ -425,28 +425,6 @@ bool KItemListController::keyPressEvent(QKeyEvent *event)
break;
}
case Qt::Key_Menu: {
// Emit the signal itemContextMenuRequested() in case if at least one
// item is selected. Otherwise the signal viewContextMenuRequested() will be emitted.
const KItemSet selectedItems = m_selectionManager->selectedItems();
int index = -1;
if (selectedItems.count() >= 2) {
const int currentItemIndex = m_selectionManager->currentItem();
index = selectedItems.contains(currentItemIndex) ? currentItemIndex : selectedItems.first();
} else if (selectedItems.count() == 1) {
index = selectedItems.first();
}
if (index >= 0) {
const QRectF contextRect = m_view->itemContextRect(index);
const QPointF pos(m_view->scene()->views().first()->mapToGlobal(contextRect.bottomRight().toPoint()));
Q_EMIT itemContextMenuRequested(index, pos);
} else {
Q_EMIT viewContextMenuRequested(QCursor::pos());
}
break;
}
case Qt::Key_Escape:
if (m_selectionMode) {
Q_EMIT selectionModeChangeRequested(false);
@ -701,19 +679,7 @@ bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event,
}
if (event->button() & Qt::RightButton) {
m_selectionManager->clearSelection();
if (index.has_value()) {
m_selectionManager->setSelected(index.value());
Q_EMIT itemContextMenuRequested(index.value(), event->screenPos());
} else {
const QRectF headerBounds = m_view->headerBoundaries();
if (headerBounds.contains(event->pos())) {
Q_EMIT headerContextMenuRequested(event->screenPos());
} else {
Q_EMIT viewContextMenuRequested(event->screenPos());
}
}
return true;
return false;
}
bool emitItemActivated = !(m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || m_singleClickActivationEnforced)
@ -724,6 +690,59 @@ bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event,
return false;
}
bool KItemListController::contextMenuEvent(QContextMenuEvent *event)
{
if (event->reason() == QContextMenuEvent::Keyboard) {
// Emit the signal itemContextMenuRequested() if at least one item is selected.
// Otherwise the signal viewContextMenuRequested() will be emitted.
const KItemSet selectedItems = m_selectionManager->selectedItems();
int index = -1;
if (selectedItems.count() >= 2) {
const int currentItemIndex = m_selectionManager->currentItem();
index = selectedItems.contains(currentItemIndex) ? currentItemIndex : selectedItems.first();
} else if (selectedItems.count() == 1) {
index = selectedItems.first();
}
if (index >= 0) {
const QRectF contextRect = m_view->itemContextRect(index);
const QPointF pos(m_view->scene()->views().first()->mapToGlobal(contextRect.bottomRight().toPoint()));
Q_EMIT itemContextMenuRequested(index, pos);
} else {
Q_EMIT viewContextMenuRequested(event->globalPos());
}
return true;
}
const auto pos = event->pos();
const auto globalPos = event->globalPos();
if (m_view->headerBoundaries().contains(pos)) {
Q_EMIT headerContextMenuRequested(globalPos);
return true;
}
const auto pressedItem = m_view->itemAt(pos);
// We only open a context menu for the pressed item if it is selected.
// That's because the same click might have de-selected the item or because the press was only in the row of the item but not on it.
if (pressedItem && m_selectionManager->selectedItems().contains(pressedItem.value())) {
// The selection rectangle for an item was clicked
Q_EMIT itemContextMenuRequested(m_pressedIndex.value(), globalPos);
return true;
}
// Remove any hover highlights so the context menu doesn't look like it applies to a row.
const auto widgets = m_view->visibleItemListWidgets();
for (KItemListWidget *widget : widgets) {
if (widget->isHovered()) {
widget->setHovered(false);
Q_EMIT itemUnhovered(widget->index());
}
}
Q_EMIT viewContextMenuRequested(globalPos);
return true;
}
bool KItemListController::dragEnterEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform)
{
Q_UNUSED(event)
@ -1208,6 +1227,8 @@ bool KItemListController::processEvent(QEvent *event, const QTransform &transfor
return mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event), QTransform());
case QEvent::GraphicsSceneMouseDoubleClick:
return mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event), QTransform());
case QEvent::ContextMenu:
return contextMenuEvent(static_cast<QContextMenuEvent *>(event));
case QEvent::GraphicsSceneWheel:
return wheelEvent(static_cast<QGraphicsSceneWheelEvent *>(event), QTransform());
case QEvent::GraphicsSceneDragEnter:
@ -1393,6 +1414,10 @@ KItemListWidget *KItemListController::widgetForPos(const QPointF &pos) const
{
Q_ASSERT(m_view);
if (m_view->headerBoundaries().contains(pos)) {
return nullptr;
}
const auto widgets = m_view->visibleItemListWidgets();
for (KItemListWidget *widget : widgets) {
const QPointF mappedPos = widget->mapFromItem(m_view, pos);
@ -1408,6 +1433,10 @@ KItemListWidget *KItemListController::widgetForDropPos(const QPointF &pos) const
{
Q_ASSERT(m_view);
if (m_view->headerBoundaries().contains(pos)) {
return nullptr;
}
const auto widgets = m_view->visibleItemListWidgets();
for (KItemListWidget *widget : widgets) {
const QPointF mappedPos = widget->mapFromItem(m_view, pos);
@ -1617,12 +1646,6 @@ bool KItemListController::onPress(const QPoint &screenPos, const QPointF &pos, c
}
if (rightClick) {
// Do header hit check and short circuit before commencing any state changing effects
if (m_view->headerBoundaries().contains(pos)) {
Q_EMIT headerContextMenuRequested(screenPos);
return true;
}
// Stop rubber band from persisting after right-clicks
KItemListRubberBand *rubberBand = m_view->rubberBand();
if (rubberBand->isActive()) {
@ -1634,7 +1657,6 @@ bool KItemListController::onPress(const QPoint &screenPos, const QPointF &pos, c
if (m_pressedIndex.has_value()) {
// The hover highlight area of an item is being pressed.
m_selectionManager->setCurrentItem(m_pressedIndex.value());
const auto row = m_view->m_visibleItems.value(m_pressedIndex.value()); // anything outside of row.contains() will be the empty region of the row rect
const bool hitTargetIsRowEmptyRegion = !row->contains(row->mapFromItem(m_view, pos));
// again, when this method returns false, a rubberBand selection is created as the event is not consumed;
@ -1642,18 +1664,13 @@ bool KItemListController::onPress(const QPoint &screenPos, const QPointF &pos, c
bool createRubberBand = (hitTargetIsRowEmptyRegion && m_selectionManager->selectedItems().isEmpty());
if (rightClick && hitTargetIsRowEmptyRegion) {
// We have a right click outside the icon and text rect but within the hover highlight area
// but it is unclear if this means that a selection rectangle for an item was clicked or the background of the view.
if (m_selectionManager->selectedItems().contains(m_pressedIndex.value())) {
// The selection rectangle for an item was clicked
Q_EMIT itemContextMenuRequested(m_pressedIndex.value(), screenPos);
} else {
row->setHovered(false); // Removes the hover highlight so the context menu doesn't look like it applies to the row.
Q_EMIT viewContextMenuRequested(screenPos);
}
// We have a right click outside the icon and text rect but within the hover highlight area.
// We don't want items to get selected through this, so we return now.
return true;
}
m_selectionManager->setCurrentItem(m_pressedIndex.value());
switch (m_selectionBehavior) {
case NoSelection:
break;
@ -1690,19 +1707,9 @@ bool KItemListController::onPress(const QPoint &screenPos, const QPointF &pos, c
break;
}
if (rightClick) {
Q_EMIT itemContextMenuRequested(m_pressedIndex.value(), screenPos);
}
return !createRubberBand;
}
if (rightClick) {
// header right click handling would have been done before this so just normal context
// menu here is fine
Q_EMIT viewContextMenuRequested(screenPos);
return true;
}
return false;
}

View File

@ -24,6 +24,7 @@ class KItemListKeyboardSearchManager;
class KItemListSelectionManager;
class KItemListView;
class KItemListWidget;
class QContextMenuEvent;
class QGestureEvent;
class QGraphicsSceneHoverEvent;
class QGraphicsSceneDragDropEvent;
@ -313,6 +314,7 @@ private:
bool mouseMoveEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
bool mouseReleaseEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
bool mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
bool contextMenuEvent(QContextMenuEvent *event);
bool dragEnterEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
bool dragLeaveEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
bool dragMoveEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);

View File

@ -402,6 +402,10 @@ qreal KItemListView::verticalPageStep() const
std::optional<int> KItemListView::itemAt(const QPointF &pos) const
{
if (headerBoundaries().contains(pos)) {
return std::nullopt;
}
QHashIterator<int, KItemListWidget *> it(m_visibleItems);
while (it.hasNext()) {
it.next();