Skip to content

Commit

Permalink
WIP: Made custom properties selectable
Browse files Browse the repository at this point in the history
Implemented:

- Top-level properties can be selected by clicking.
- Holding Ctrl allows selecting multiple.
- Remove and Rename actions on tool bar update enabled state (most of the
  time) and are implemented.
- Copy/paste shortcuts work.

ToDo:

- Select range of properties when holding Shift.
- Allow changing the selected property with Up/Down keys, adding to
  selection when holding Shift.
- Resolve interference between selecting and expanding properties.
- Resolve issue with selectedPropertiesChanged now getting emitted when
  a selected property is removed.
- Add Copy/Paste actions to context menu.
  • Loading branch information
bjorn committed Nov 22, 2024
1 parent ca2e8ac commit 58db1d0
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 51 deletions.
56 changes: 27 additions & 29 deletions src/tiled/propertieswidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2181,8 +2181,8 @@ PropertiesWidget::PropertiesWidget(QWidget *parent)
mActionRenameProperty->setEnabled(false);
mActionRenameProperty->setIcon(QIcon(QLatin1String(":/images/16/rename.png")));
mActionRenameProperty->setPriority(QAction::LowPriority);
// connect(mActionRenameProperty, &QAction::triggered,
// this, &PropertiesWidget::renameProperty);
connect(mActionRenameProperty, &QAction::triggered,
this, &PropertiesWidget::renameSelectedProperty);

Utils::setThemeIcon(mActionAddProperty, "add");
Utils::setThemeIcon(mActionRemoveProperty, "remove");
Expand All @@ -2207,8 +2207,8 @@ PropertiesWidget::PropertiesWidget(QWidget *parent)
mPropertyBrowser->setContextMenuPolicy(Qt::CustomContextMenu);
connect(mPropertyBrowser, &QWidget::customContextMenuRequested,
this, &PropertiesWidget::showContextMenu);
// connect(mPropertyBrowser, &PropertyBrowser::selectedItemsChanged,
// this, &PropertiesWidget::updateActions);
connect(mCustomProperties, &VariantMapProperty::selectedPropertiesChanged,
this, &PropertiesWidget::updateActions);

connect(mCustomProperties, &VariantMapProperty::renameRequested,
this, &PropertiesWidget::renameProperty);
Expand Down Expand Up @@ -2430,27 +2430,24 @@ void CustomProperties::setPropertyValue(const QStringList &path, const QVariant

void PropertiesWidget::updateActions()
{
#if 0
const QList<QtBrowserItem*> items = mPropertyBrowser->selectedItems();
bool allCustomProperties = !items.isEmpty() && mPropertyBrowser->allCustomPropertyItems(items);
const auto properties = mCustomProperties->selectedProperties();
bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType;
bool isTileset = mPropertyBrowser->object() && mPropertyBrowser->object()->isPartOfTileset();
bool canModify = allCustomProperties && (!isTileset || editingTileset);
bool isTileset = mDocument && mDocument->currentObject() && mDocument->currentObject()->isPartOfTileset();
bool canModify = (!isTileset || editingTileset);

// Disable remove and rename actions when none of the selected objects
// actually have the selected property (it may be inherited).
if (canModify) {
for (QtBrowserItem *item : items) {
if (!anyObjectHasProperty(mDocument->currentObjects(), item->property()->propertyName())) {
for (auto property : properties) {
if (!anyObjectHasProperty(mDocument->currentObjects(), property->name())) {
canModify = false;
break;
}
}
}

mActionRemoveProperty->setEnabled(canModify);
mActionRenameProperty->setEnabled(canModify && items.size() == 1);
#endif
mActionRenameProperty->setEnabled(canModify && properties.size() == 1);
}

void PropertiesWidget::cutProperties()
Expand All @@ -2461,19 +2458,15 @@ void PropertiesWidget::cutProperties()

bool PropertiesWidget::copyProperties()
{
#if 0
Object *object = mPropertyBrowser->object();
Object *object = mDocument ? mDocument->currentObject() : nullptr;
if (!object)
return false;

Properties properties;

const QList<QtBrowserItem*> items = mPropertyBrowser->selectedItems();
for (QtBrowserItem *item : items) {
if (!mPropertyBrowser->isCustomPropertyItem(item))
return false;

const QString name = item->property()->propertyName();
const auto selectedProperties = mCustomProperties->selectedProperties();
for (auto property : selectedProperties) {
const QString name = property->name();
const QVariant value = object->property(name);
if (!value.isValid())
return false;
Expand All @@ -2482,7 +2475,7 @@ bool PropertiesWidget::copyProperties()
}

ClipboardManager::instance()->setProperties(properties);
#endif

return true;
}

Expand Down Expand Up @@ -2564,18 +2557,15 @@ void PropertiesWidget::addProperty(const QString &name, const QVariant &value)

void PropertiesWidget::removeProperties()
{
#if 0
Object *object = mDocument->currentObject();
if (!object)
return;

const QList<QtBrowserItem*> items = mPropertyBrowser->selectedItems();
if (items.isEmpty() || !mPropertyBrowser->allCustomPropertyItems(items))
return;
const auto properties = mCustomProperties->selectedProperties();

QStringList propertyNames;
for (QtBrowserItem *item : items)
propertyNames.append(item->property()->propertyName());
for (auto property : properties)
propertyNames.append(property->name());

QUndoStack *undoStack = mDocument->undoStack();
undoStack->beginMacro(QCoreApplication::translate("Tiled::PropertiesDock",
Expand All @@ -2590,7 +2580,15 @@ void PropertiesWidget::removeProperties()
}

undoStack->endMacro();
#endif
}

void PropertiesWidget::renameSelectedProperty()
{
const auto properties = mCustomProperties->selectedProperties();
if (properties.size() != 1)
return;

renameProperty(properties.first()->name());
}

void PropertiesWidget::renameProperty(const QString &name)
Expand Down
1 change: 1 addition & 0 deletions src/tiled/propertieswidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public slots:
void showAddValueProperty();
void addProperty(const QString &name, const QVariant &value);
void removeProperties();
void renameSelectedProperty();
void renameProperty(const QString &name);
void showContextMenu(const QPoint &pos);

Expand Down
101 changes: 88 additions & 13 deletions src/tiled/propertyeditorwidgets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,15 @@ void ElidingLabel::setToolTip(const QString &toolTip)
QLabel::setToolTip(m_toolTip);
}

void ElidingLabel::setSelected(bool selected)
{
if (m_selected == selected)
return;

m_selected = selected;
update();
}

QSize ElidingLabel::minimumSizeHint() const
{
auto hint = QLabel::minimumSizeHint();
Expand Down Expand Up @@ -688,7 +697,8 @@ void ElidingLabel::paintEvent(QPaintEvent *)
}

QStylePainter p(this);
p.drawItemText(cr, flags, opt.palette, isEnabled(), elidedText, foregroundRole());
QPalette::ColorRole role = m_selected ? QPalette::HighlightedText : foregroundRole();
p.drawItemText(cr, flags, opt.palette, isEnabled(), elidedText, role);
}


Expand Down Expand Up @@ -748,23 +758,27 @@ void PropertyLabel::setModified(bool modified)

bool PropertyLabel::event(QEvent *event)
{
switch (event->type()) {
// Handled here instead of in mousePressEvent because we want it to be
// expandable also when the label is disabled.
if (event->type() == QEvent::MouseButtonPress && m_expandable) {
auto mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::LeftButton) {
setExpanded(!m_expanded);
return true;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonDblClick:
if (m_expandable) {
auto mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::LeftButton) {
setExpanded(!m_expanded);
return true;
}
}
}

if (event->type() == QEvent::ContextMenu) {
emit contextMenuRequested(static_cast<QContextMenuEvent *>(event)->globalPos());
return true;
}
break;

if (event->type() == QEvent::LayoutDirectionChange)
case QEvent::LayoutDirectionChange:
updateContentMargins();
break;

default:
break;
}

return ElidingLabel::event(event);
}
Expand All @@ -785,6 +799,8 @@ void PropertyLabel::paintEvent(QPaintEvent *event)
else
branchOption.rect = QRect(width() - indent - branchIndicatorWidth - spacing, 0,
branchIndicatorWidth + spacing, height());
if (isSelected())
branchOption.state |= QStyle::State_Selected;
if (m_expandable)
branchOption.state |= QStyle::State_Children;
if (m_expanded)
Expand Down Expand Up @@ -836,6 +852,65 @@ QSize PropertyLabel::sizeHint() const
return style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(w, h), this);
}


PropertyWidget::PropertyWidget(QWidget *parent)
: QWidget(parent)
{
setContextMenuPolicy(Qt::CustomContextMenu);
}

void PropertyWidget::setSelectable(bool selectable)
{
if (m_selectable == selectable)
return;

m_selectable = selectable;

if (!selectable)
setSelected(false);
}

void PropertyWidget::setSelected(bool selected)
{
if (m_selected == selected)
return;

m_selected = selected;
update();
}

void PropertyWidget::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);

const auto halfSpacing = Utils::dpiScaled(2);
const QRect r = rect().adjusted(halfSpacing, 0, -halfSpacing, 0);
QStylePainter painter(this);

if (isSelected())
painter.fillRect(r, palette().highlight());

if (hasFocus()) {
QStyleOptionFocusRect option;
option.initFrom(this);
option.rect = r;
option.backgroundColor = palette().color(backgroundRole());
option.state |= QStyle::State_KeyboardFocusChange;

painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
}
}

void PropertyWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && m_selectable)
emit clicked(event->modifiers());

setFocus(Qt::MouseFocusReason);

QWidget::mousePressEvent(event);
}

} // namespace Tiled

#include "moc_propertyeditorwidgets.cpp"
Expand Down
33 changes: 32 additions & 1 deletion src/tiled/propertyeditorwidgets.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ class ElidingLabel : public QLabel

void setToolTip(const QString &toolTip);

void setSelected(bool selected);
bool isSelected() const { return m_selected; }

QSize minimumSizeHint() const override;

protected:
Expand All @@ -299,6 +302,7 @@ class ElidingLabel : public QLabel
private:
QString m_toolTip;
bool m_isElided = false;
bool m_selected = false;
};

/**
Expand Down Expand Up @@ -328,7 +332,6 @@ class PropertyLabel : public ElidingLabel

signals:
void toggled(bool expanded);
void contextMenuRequested(const QPoint &globalPos);

protected:
bool event(QEvent *event) override;
Expand All @@ -343,4 +346,32 @@ class PropertyLabel : public ElidingLabel
bool m_expanded = false;
};

/**
* A widget that represents a single property.
*/
class PropertyWidget : public QWidget
{
Q_OBJECT

public:
PropertyWidget(QWidget *parent = nullptr);

void setSelectable(bool selectable);
bool isSelectable() const { return m_selectable; }

void setSelected(bool selected);
bool isSelected() const { return m_selected; }

signals:
void clicked(Qt::KeyboardModifiers modifiers);

protected:
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;

private:
bool m_selectable = false;
bool m_selected = false;
};

} // namespace Tiled
Loading

0 comments on commit 58db1d0

Please sign in to comment.