forked from quickshell/quickshell
core/qsmenu!: improve menu layout change UX
Exposes QsMenuOpener.children as an ObjectModel instead of a list to allow smoother layout change handling in custom menu renderers. Fixes QsMenuAnchor/platform menus closing whenever menu content changes.
This commit is contained in:
parent
3fc1c914c7
commit
a053373d57
7 changed files with 61 additions and 67 deletions
|
@ -71,6 +71,22 @@ bool UntypedObjectModel::removeObject(const QObject* object) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void UntypedObjectModel::diffUpdate(const QVector<QObject*>& newValues) {
|
||||
for (qsizetype i = 0; i < this->valuesList.length();) {
|
||||
if (newValues.contains(this->valuesList.at(i))) i++;
|
||||
else this->removeAt(i);
|
||||
}
|
||||
|
||||
qsizetype oi = 0;
|
||||
for (auto* object: newValues) {
|
||||
if (this->valuesList.length() == oi || this->valuesList.at(oi) != object) {
|
||||
this->insertObject(object, oi);
|
||||
}
|
||||
|
||||
oi++;
|
||||
}
|
||||
}
|
||||
|
||||
qsizetype UntypedObjectModel::indexOf(QObject* object) { return this->valuesList.indexOf(object); }
|
||||
|
||||
UntypedObjectModel* UntypedObjectModel::emptyInstance() {
|
||||
|
|
|
@ -73,6 +73,9 @@ protected:
|
|||
void insertObject(QObject* object, qsizetype index = -1);
|
||||
bool removeObject(const QObject* object);
|
||||
|
||||
// Assumes only one instance of a specific value
|
||||
void diffUpdate(const QVector<QObject*>& newValues);
|
||||
|
||||
QVector<QObject*> valuesList;
|
||||
|
||||
private:
|
||||
|
@ -97,6 +100,11 @@ public:
|
|||
|
||||
void removeObject(const T* object) { this->UntypedObjectModel::removeObject(object); }
|
||||
|
||||
// Assumes only one instance of a specific value
|
||||
void diffUpdate(const QVector<T*>& newValues) {
|
||||
this->UntypedObjectModel::diffUpdate(*std::bit_cast<const QVector<QObject*>*>(&newValues));
|
||||
}
|
||||
|
||||
static ObjectModel<T>* emptyInstance() {
|
||||
return static_cast<ObjectModel<T>*>(UntypedObjectModel::emptyInstance());
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "../window/proxywindow.hpp"
|
||||
#include "../window/windowinterface.hpp"
|
||||
#include "iconprovider.hpp"
|
||||
#include "model.hpp"
|
||||
#include "platformmenu_p.hpp"
|
||||
#include "popupanchor.hpp"
|
||||
#include "qsmenu.hpp"
|
||||
|
@ -61,6 +62,7 @@ PlatformMenuEntry::PlatformMenuEntry(QsMenuEntry* menu): QObject(menu), menu(men
|
|||
QObject::connect(menu, &QsMenuEntry::buttonTypeChanged, this, &PlatformMenuEntry::onButtonTypeChanged);
|
||||
QObject::connect(menu, &QsMenuEntry::checkStateChanged, this, &PlatformMenuEntry::onCheckStateChanged);
|
||||
QObject::connect(menu, &QsMenuEntry::hasChildrenChanged, this, &PlatformMenuEntry::relayoutParent);
|
||||
QObject::connect(menu->children(), &UntypedObjectModel::valuesChanged, this, &PlatformMenuEntry::relayout);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
|
@ -178,10 +180,10 @@ void PlatformMenuEntry::relayout() {
|
|||
this->qmenu->setIcon(getCurrentEngineImageAsIcon(icon));
|
||||
}
|
||||
|
||||
auto children = this->menu->children();
|
||||
auto len = children.count(&children);
|
||||
const auto& children = this->menu->children()->valueList();
|
||||
auto len = children.count();
|
||||
for (auto i = 0; i < len; i++) {
|
||||
auto* child = children.at(&children, i);
|
||||
auto* child = children.at(i);
|
||||
|
||||
auto* instance = new PlatformMenuEntry(child);
|
||||
QObject::connect(instance, &QObject::destroyed, this, &PlatformMenuEntry::onChildDestroyed);
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include <qobject.h>
|
||||
#include <qqmllist.h>
|
||||
#include <qtmetamacros.h>
|
||||
#include <qtypes.h>
|
||||
|
||||
#include "model.hpp"
|
||||
#include "platformmenu.hpp"
|
||||
|
||||
using namespace qs::menu::platform;
|
||||
|
@ -34,15 +34,6 @@ void QsMenuEntry::display(QObject* parentWindow, int relativeX, int relativeY) {
|
|||
if (!success) delete platform;
|
||||
}
|
||||
|
||||
QQmlListProperty<QsMenuEntry> QsMenuEntry::emptyChildren(QObject* parent) {
|
||||
return QQmlListProperty<QsMenuEntry>(
|
||||
parent,
|
||||
nullptr,
|
||||
&QsMenuEntry::childCount,
|
||||
&QsMenuEntry::childAt
|
||||
);
|
||||
}
|
||||
|
||||
void QsMenuEntry::ref() {
|
||||
this->refcount++;
|
||||
if (this->refcount == 1) emit this->opened();
|
||||
|
@ -53,7 +44,9 @@ void QsMenuEntry::unref() {
|
|||
if (this->refcount == 0) emit this->closed();
|
||||
}
|
||||
|
||||
QQmlListProperty<QsMenuEntry> QsMenuEntry::children() { return QsMenuEntry::emptyChildren(this); }
|
||||
ObjectModel<QsMenuEntry>* QsMenuEntry::children() {
|
||||
return ObjectModel<QsMenuEntry>::emptyInstance();
|
||||
}
|
||||
|
||||
QsMenuOpener::~QsMenuOpener() {
|
||||
if (this->mMenu) {
|
||||
|
@ -83,13 +76,6 @@ void QsMenuOpener::setMenu(QsMenuHandle* menu) {
|
|||
if (menu != nullptr) {
|
||||
auto onMenuChanged = [this, menu]() {
|
||||
if (menu->menu()) {
|
||||
QObject::connect(
|
||||
menu->menu(),
|
||||
&QsMenuEntry::childrenChanged,
|
||||
this,
|
||||
&QsMenuOpener::childrenChanged
|
||||
);
|
||||
|
||||
menu->menu()->ref();
|
||||
}
|
||||
|
||||
|
@ -113,19 +99,12 @@ void QsMenuOpener::onMenuDestroyed() {
|
|||
emit this->childrenChanged();
|
||||
}
|
||||
|
||||
QQmlListProperty<QsMenuEntry> QsMenuOpener::children() {
|
||||
ObjectModel<QsMenuEntry>* QsMenuOpener::children() {
|
||||
if (this->mMenu && this->mMenu->menu()) {
|
||||
return this->mMenu->menu()->children();
|
||||
} else {
|
||||
return QsMenuEntry::emptyChildren(this);
|
||||
return ObjectModel<QsMenuEntry>::emptyInstance();
|
||||
}
|
||||
}
|
||||
|
||||
qsizetype QsMenuEntry::childCount(QQmlListProperty<QsMenuEntry>* /*property*/) { return 0; }
|
||||
|
||||
QsMenuEntry*
|
||||
QsMenuEntry::childAt(QQmlListProperty<QsMenuEntry>* /*property*/, qsizetype /*index*/) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace qs::menu
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <qtypes.h>
|
||||
|
||||
#include "doc.hpp"
|
||||
#include "model.hpp"
|
||||
|
||||
namespace qs::menu {
|
||||
|
||||
|
@ -107,9 +108,7 @@ public:
|
|||
void ref();
|
||||
void unref();
|
||||
|
||||
[[nodiscard]] virtual QQmlListProperty<QsMenuEntry> children();
|
||||
|
||||
static QQmlListProperty<QsMenuEntry> emptyChildren(QObject* parent);
|
||||
[[nodiscard]] virtual ObjectModel<QsMenuEntry>* children();
|
||||
|
||||
signals:
|
||||
/// Send a trigger/click signal to the menu entry.
|
||||
|
@ -125,12 +124,8 @@ signals:
|
|||
void buttonTypeChanged();
|
||||
void checkStateChanged();
|
||||
void hasChildrenChanged();
|
||||
QSDOC_HIDE void childrenChanged();
|
||||
|
||||
private:
|
||||
static qsizetype childCount(QQmlListProperty<QsMenuEntry>* property);
|
||||
static QsMenuEntry* childAt(QQmlListProperty<QsMenuEntry>* property, qsizetype index);
|
||||
|
||||
qsizetype refcount = 0;
|
||||
};
|
||||
|
||||
|
@ -140,7 +135,8 @@ class QsMenuOpener: public QObject {
|
|||
/// The menu to retrieve children from.
|
||||
Q_PROPERTY(qs::menu::QsMenuHandle* menu READ menu WRITE setMenu NOTIFY menuChanged);
|
||||
/// The children of the given menu.
|
||||
Q_PROPERTY(QQmlListProperty<qs::menu::QsMenuEntry> children READ children NOTIFY childrenChanged);
|
||||
QSDOC_TYPE_OVERRIDE(ObjectModel<qs::menu::QsMenuEntry>*);
|
||||
Q_PROPERTY(UntypedObjectModel* children READ children NOTIFY childrenChanged);
|
||||
QML_ELEMENT;
|
||||
|
||||
public:
|
||||
|
@ -151,7 +147,7 @@ public:
|
|||
[[nodiscard]] QsMenuHandle* menu() const;
|
||||
void setMenu(QsMenuHandle* menu);
|
||||
|
||||
[[nodiscard]] QQmlListProperty<QsMenuEntry> children();
|
||||
[[nodiscard]] ObjectModel<QsMenuEntry>* children();
|
||||
|
||||
signals:
|
||||
void menuChanged();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue