Skip to content

Commit

Permalink
Added option to change object selection behavior (#2865)
Browse files Browse the repository at this point in the history
Previously when clicking objects it would always take the top-most object
from any visible and unlocked layer.

This new option allows changing that such that when available, an object
from one of the currently selected layers is preferred. It can also be
set to only select objects from selected layers.

Furthermore, when highlighting of current layer option is enabled, 
objects from currently selected layers are always preferred (unless the 
above option is set to only consider selected layers).
  • Loading branch information
bjorn authored Dec 16, 2020
1 parent 56033ba commit 44c02dc
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 68 deletions.
64 changes: 61 additions & 3 deletions src/tiled/abstractobjecttool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ static bool isChangedTemplateInstance(MapObject *mapObject)
return false;
}

Preference<AbstractObjectTool::SelectionBehavior> AbstractObjectTool::ourSelectionBehavior {
"AbstractObjectTool/SelectionBehavior", AbstractObjectTool::AllLayers
};

AbstractObjectTool::AbstractObjectTool(Id id,
const QString &name,
Expand Down Expand Up @@ -189,6 +192,37 @@ void AbstractObjectTool::populateToolBar(QToolBar *toolBar)
toolBar->addAction(mRotateRight);
}

AbstractObjectTool::SelectionBehavior AbstractObjectTool::selectionBehavior()
{
const SelectionBehavior behavior = ourSelectionBehavior;

if (behavior == AllLayers && Preferences::instance()->highlightCurrentLayer())
return PreferSelectedLayers;

return behavior;
}

void AbstractObjectTool::filterMapObjects(QList<MapObject *> &mapObjects) const
{
const SelectionBehavior behavior = selectionBehavior();

if (behavior != AllLayers) {
const auto &selectedLayers = mapDocument()->selectedLayers();

QList<MapObject*> filteredList;

for (MapObject *mapObject : qAsConst(mapObjects)) {
if (std::any_of(selectedLayers.begin(), selectedLayers.end(),
[=] (Layer *layer) { return layer->isParentOrSelf(mapObject->objectGroup()); })) {
filteredList.append(mapObject);
}
}

if (behavior == SelectedLayers || !filteredList.isEmpty())
mapObjects.swap(filteredList);
}
}

void AbstractObjectTool::updateEnabledState()
{
setEnabled(currentObjectGroup() != nullptr);
Expand All @@ -207,6 +241,7 @@ QList<MapObject*> AbstractObjectTool::mapObjectsAt(const QPointF &pos) const
const QList<QGraphicsItem *> &items = mapScene()->items(pos);

QList<MapObject*> objectList;

for (auto item : items) {
if (!item->isEnabled())
continue;
Expand All @@ -215,22 +250,45 @@ QList<MapObject*> AbstractObjectTool::mapObjectsAt(const QPointF &pos) const
if (objectItem && objectItem->mapObject()->objectGroup()->isUnlocked())
objectList.append(objectItem->mapObject());
}

filterMapObjects(objectList);
return objectList;
}

MapObject *AbstractObjectTool::topMostMapObjectAt(const QPointF &pos) const
{
const QList<QGraphicsItem *> &items = mapScene()->items(pos);
const SelectionBehavior behavior = selectionBehavior();

MapObject *topMost = nullptr;

for (QGraphicsItem *item : items) {
if (!item->isEnabled())
continue;

MapObjectItem *objectItem = qgraphicsitem_cast<MapObjectItem*>(item);
if (objectItem && objectItem->mapObject()->objectGroup()->isUnlocked())
return objectItem->mapObject();
if (!objectItem)
continue;

auto mapObject = objectItem->mapObject();
if (!mapObject->objectGroup()->isUnlocked())
continue;

// Return immediately when we don't care if the layer is selected
if (behavior == AllLayers)
return mapObject;

// Return this object instead of the top-most one if it is from a selected layer
for (Layer *layer : mapDocument()->selectedLayers()) {
if (layer->isParentOrSelf(mapObject->objectGroup()))
return mapObject;
}

if (!topMost && behavior != SelectedLayers)
topMost = mapObject;
}
return nullptr;

return topMost;
}

void AbstractObjectTool::duplicateObjects()
Expand Down
13 changes: 13 additions & 0 deletions src/tiled/abstractobjecttool.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#pragma once

#include "abstracttool.h"
#include "preferences.h"

class QAction;

Expand All @@ -41,6 +42,15 @@ class AbstractObjectTool : public AbstractTool
Q_INTERFACES(Tiled::AbstractTool)

public:
enum SelectionBehavior {
AllLayers,
PreferSelectedLayers,
SelectedLayers
};
Q_ENUM(SelectionBehavior)

static Preference<SelectionBehavior> ourSelectionBehavior;

/**
* Constructs an abstract object tool with the given \a name and \a icon.
*/
Expand All @@ -59,6 +69,9 @@ class AbstractObjectTool : public AbstractTool

void populateToolBar(QToolBar*) override;

static SelectionBehavior selectionBehavior();
void filterMapObjects(QList<MapObject*> &mapObjects) const;

protected:
/**
* Overridden to only enable this tool when the currently selected layer is
Expand Down
23 changes: 5 additions & 18 deletions src/tiled/editpolygontool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,23 +208,7 @@ void EditPolygonTool::mousePressed(QGraphicsSceneMouseEvent *event)
mMousePressed = true;
mStart = event->scenePos();
mScreenStart = event->screenPos();

const QList<QGraphicsItem *> items = mapScene()->items(mStart,
Qt::IntersectsItemShape,
Qt::DescendingOrder,
viewTransform(event));

mClickedObject = nullptr;
for (QGraphicsItem *item : items) {
if (!item->isEnabled())
continue;
if (auto mapObjectItem = qgraphicsitem_cast<MapObjectItem*>(item)) {
if (mapObjectItem->mapObject()->objectGroup()->isUnlocked()) {
mClickedObject = mapObjectItem->mapObject();
break;
}
}
}
mClickedObject = topMostMapObjectAt(mStart);
break;
}
case Qt::RightButton: {
Expand Down Expand Up @@ -487,7 +471,10 @@ void EditPolygonTool::updateSelection(QGraphicsSceneMouseEvent *event)
selectedObjects.append(mapObjectItem->mapObject());
}

mapDocument()->setSelectedObjects(selectedObjects);
filterMapObjects(selectedObjects);

if (!selectedObjects.isEmpty())
mapDocument()->setSelectedObjects(selectedObjects);
} else {
// Update the selected handles
QSet<PointHandle*> selectedHandles;
Expand Down
8 changes: 8 additions & 0 deletions src/tiled/objectselectiontool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,12 @@ ObjectSelectionTool::ObjectSelectionTool(QObject *parent)
mRotateHandles[i] = new RotateHandle(static_cast<AnchorPosition>(i));
for (int i = 0; i < AnchorCount; ++i)
mResizeHandles[i] = new ResizeHandle(static_cast<AnchorPosition>(i));

connect(Preferences::instance(), &Preferences::highlightCurrentLayerChanged,
this, [this] {
if (mapScene() && mapDocument()->hoveredMapObject())
updateHover(mLastMousePos);
});
}

ObjectSelectionTool::~ObjectSelectionTool()
Expand Down Expand Up @@ -1111,6 +1117,8 @@ void ObjectSelectionTool::updateSelection(const QPointF &pos,
selectedObjects.append(mapObjectItem->mapObject());
}

filterMapObjects(selectedObjects);

if (modifiers & (Qt::ControlModifier | Qt::ShiftModifier)) {
for (MapObject *object : mapDocument()->selectedObjects())
if (!selectedObjects.contains(object))
Expand Down
17 changes: 14 additions & 3 deletions src/tiled/preferencesdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "preferencesdialog.h"
#include "ui_preferencesdialog.h"

#include "abstractobjecttool.h"
#include "languagemanager.h"
#include "pluginlistmodel.h"
#include "preferences.h"
Expand Down Expand Up @@ -60,13 +61,16 @@ PreferencesDialog::PreferencesDialog(QWidget *parent)
mUi->languageCombo->model()->sort(0);
mUi->languageCombo->insertItem(0, tr("System default"));

mUi->styleCombo->addItems(QStringList()
<< QApplication::translate("PreferencesDialog", "Native")
<< QApplication::translate("PreferencesDialog", "Tiled Fusion"));
mUi->styleCombo->addItems({ QApplication::translate("PreferencesDialog", "Native"),
QApplication::translate("PreferencesDialog", "Tiled Fusion") });

mUi->styleCombo->setItemData(0, Preferences::SystemDefaultStyle);
mUi->styleCombo->setItemData(1, Preferences::TiledStyle);

mUi->objectSelectionBehaviorCombo->addItems({ tr("Select From Any Layer"),
tr("Prefer Selected Layers"),
tr("Selected Layers Only") });

PluginListModel *pluginListModel = new PluginListModel(this);
QSortFilterProxyModel *pluginProxyModel = new QSortFilterProxyModel(this);
pluginProxyModel->setSortLocaleAware(true);
Expand Down Expand Up @@ -117,6 +121,8 @@ PreferencesDialog::PreferencesDialog(QWidget *parent)

connect(mUi->styleCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this, &PreferencesDialog::styleComboChanged);
connect(mUi->objectSelectionBehaviorCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this, [] (int index) { AbstractObjectTool::ourSelectionBehavior = static_cast<AbstractObjectTool::SelectionBehavior>(index); });
connect(mUi->baseColor, &ColorButton::colorChanged,
preferences, &Preferences::setBaseColor);
connect(mUi->selectionColor, &ColorButton::colorChanged,
Expand Down Expand Up @@ -205,6 +211,7 @@ void PreferencesDialog::fromPreferences()
styleComboIndex = 1;

mUi->styleCombo->setCurrentIndex(styleComboIndex);
mUi->objectSelectionBehaviorCombo->setCurrentIndex(AbstractObjectTool::ourSelectionBehavior);
mUi->baseColor->setColor(prefs->baseColor());
mUi->selectionColor->setColor(prefs->selectionColor());
bool systemStyle = prefs->applicationStyle() == Preferences::SystemDefaultStyle;
Expand All @@ -220,6 +227,10 @@ void PreferencesDialog::retranslateUi()

mUi->styleCombo->setItemText(0, QApplication::translate("PreferencesDialog", "Native"));
mUi->styleCombo->setItemText(1, QApplication::translate("PreferencesDialog", "Tiled Fusion"));

mUi->objectSelectionBehaviorCombo->setItemText(0, tr("Select From Any Layer"));
mUi->objectSelectionBehaviorCombo->setItemText(1, tr("Prefer Selected Layers"));
mUi->objectSelectionBehaviorCombo->setItemText(3, tr("Selected Layers Only"));
}

void PreferencesDialog::styleComboChanged()
Expand Down
Loading

0 comments on commit 44c02dc

Please sign in to comment.