Add an option to define new power levels

pull/1111/head
Nicolas Werner 3 years ago
parent c25aeac4ca
commit dc4a06517c
No known key found for this signature in database
GPG Key ID: C8D75E610773F2D9
  1. 298
      resources/qml/dialogs/PowerLevelEditor.qml
  2. 77
      src/PowerlevelsEditModels.cpp
  3. 23
      src/PowerlevelsEditModels.h

@ -20,13 +20,14 @@ ApplicationWindow {
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
minimumWidth: 300 minimumWidth: 300
minimumHeight: 400 minimumHeight: 400
height: 600
title: qsTr("Permissions in %1").arg(roomSettings.roomName); title: qsTr("Permissions in %1").arg(roomSettings.roomName);
// Shortcut { // Shortcut {
// sequence: StandardKey.Cancel // sequence: StandardKey.Cancel
// onActivated: dbb.rejected() // onActivated: dbb.rejected()
// } // }
ColumnLayout { ColumnLayout {
anchors.margins: Nheko.paddingMedium anchors.margins: Nheko.paddingMedium
@ -117,6 +118,8 @@ ApplicationWindow {
return qsTr("Administrator (%1)").arg(model.powerlevel) return qsTr("Administrator (%1)").arg(model.powerlevel)
else if (editingModel.moderatorLevel == model.powerlevel) else if (editingModel.moderatorLevel == model.powerlevel)
return qsTr("Moderator (%1)").arg(model.powerlevel) return qsTr("Moderator (%1)").arg(model.powerlevel)
else if (editingModel.defaultUserLevel == model.powerlevel)
return qsTr("User (%1)").arg(model.powerlevel)
else else
return qsTr("Custom (%1)").arg(model.powerlevel) return qsTr("Custom (%1)").arg(model.powerlevel)
} }
@ -144,34 +147,85 @@ ApplicationWindow {
} }
} }
} }
MatrixTextField { MatrixTextField {
id: typeEntry id: typeEntry
property int index property int index
width: parent.width width: parent.width
z: 5 z: 5
visible: false visible: false
color: Nheko.colors.text color: Nheko.colors.text
Keys.onPressed: { Keys.onPressed: {
if (typeEntry.text.includes('.') && event.matches(StandardKey.InsertParagraphSeparator)) { if (typeEntry.text.includes('.') && event.matches(StandardKey.InsertParagraphSeparator)) {
editingModel.types.add(typeEntry.index, typeEntry.text) editingModel.types.add(typeEntry.index, typeEntry.text)
typeEntry.visible = false; typeEntry.visible = false;
typeEntry.clear(); typeEntry.clear();
event.accepted = true; event.accepted = true;
}
else if (event.matches(StandardKey.Cancel)) {
typeEntry.visible = false;
typeEntry.clear();
event.accepted = true;
}
}
}
}
Button {
Layout.fillWidth: true
text: qsTr("Add new role")
onClicked: newPLLay.visible = true
Rectangle {
id: newPLLay
anchors.fill: parent
visible: false
color: Nheko.colors.alternateBase
RowLayout {
spacing: Nheko.paddingMedium
anchors.fill: parent
SpinBox {
id: newPLVal
Layout.fillWidth: true
Layout.fillHeight: true
editable: true
//from: -9007199254740991
//to: 9007199254740991
// max qml values
from: -2000000000
to: 2000000000
Keys.onPressed: {
if (event.matches(StandardKey.InsertParagraphSeparator)) {
editingModel.addRole(newPLVal.value);
newPLLay.visible = false;
}
} }
else if (event.matches(StandardKey.Cancel)) { }
typeEntry.visible = false;
typeEntry.clear(); Button {
event.accepted = true; text: qsTr("Add")
Layout.preferredWidth: 100
onClicked: {
editingModel.addRole(newPLVal.value);
newPLLay.visible = false;
} }
} }
} }
}
} }
} }
ColumnLayout { ColumnLayout {
spacing: Nheko.paddingMedium spacing: Nheko.paddingMedium
@ -188,16 +242,16 @@ ApplicationWindow {
model: editingModel.users model: editingModel.users
Column{ Column{
id: userEntryCompleter id: userEntryCompleter
property int index: 0 property int index: 0
visible: false visible: false
width: parent.width width: parent.width
spacing: 1 spacing: 1
z: 5 z: 5
MatrixTextField { MatrixTextField {
id: userEntry id: userEntry
@ -229,119 +283,119 @@ ApplicationWindow {
} }
Completer { Completer {
id: userCompleter id: userCompleter
visible: userEntry.text.length > 0
width: parent.width
roomId: plEditorW.roomSettings.roomId
completerName: "user"
bottomToTop: false
fullWidth: true
avatarHeight: Nheko.avatarSize / 2
avatarWidth: Nheko.avatarSize / 2
centerRowContent: false
rowMargin: 2
rowSpacing: 2
}
}
Connections { visible: userEntry.text.length > 0
function onCompletionSelected(id) { width: parent.width
console.log("selected: " + id); roomId: plEditorW.roomSettings.roomId
editingModel.users.add(userEntryCompleter.index, id); completerName: "user"
userEntry.clear(); bottomToTop: false
userEntryCompleter.visible = false; fullWidth: true
} avatarHeight: Nheko.avatarSize / 2
avatarWidth: Nheko.avatarSize / 2
centerRowContent: false
rowMargin: 2
rowSpacing: 2
}
}
function onCountChanged() { Connections {
if (userCompleter.count > 0 && (userCompleter.currentIndex < 0 || userCompleter.currentIndex >= userCompleter.count)) function onCompletionSelected(id) {
userCompleter.currentIndex = 0; console.log("selected: " + id);
editingModel.users.add(userEntryCompleter.index, id);
userEntry.clear();
userEntryCompleter.visible = false;
}
} function onCountChanged() {
if (userCompleter.count > 0 && (userCompleter.currentIndex < 0 || userCompleter.currentIndex >= userCompleter.count))
userCompleter.currentIndex = 0;
target: userCompleter
} }
delegate: RowLayout { target: userCompleter
//anchors { fill: parent; margins: 2 } }
id: row
delegate: RowLayout {
Avatar { //anchors { fill: parent; margins: 2 }
id: avatar id: row
Layout.preferredHeight: Nheko.avatarSize / 2 Avatar {
Layout.preferredWidth: Nheko.avatarSize / 2 id: avatar
Layout.leftMargin: 2
userid: model.mxid Layout.preferredHeight: Nheko.avatarSize / 2
url: { Layout.preferredWidth: Nheko.avatarSize / 2
if (model.isUser) Layout.leftMargin: 2
return model.avatarUrl.replace("mxc://", "image://MxcImage/") userid: model.mxid
else if (editingModel.adminLevel >= model.powerlevel) url: {
return "image://colorimage/:/icons/icons/ui/ribbon_star.svg?" + Nheko.colors.buttonText; if (model.isUser)
else if (editingModel.moderatorLevel >= model.powerlevel) return model.avatarUrl.replace("mxc://", "image://MxcImage/")
return "image://colorimage/:/icons/icons/ui/ribbon.svg?" + Nheko.colors.buttonText; else if (editingModel.adminLevel >= model.powerlevel)
else return "image://colorimage/:/icons/icons/ui/ribbon_star.svg?" + Nheko.colors.buttonText;
return "image://colorimage/:/icons/icons/ui/person.svg?" + Nheko.colors.buttonText; else if (editingModel.moderatorLevel >= model.powerlevel)
} return "image://colorimage/:/icons/icons/ui/ribbon.svg?" + Nheko.colors.buttonText;
displayName: model.displayName else
enabled: false return "image://colorimage/:/icons/icons/ui/person.svg?" + Nheko.colors.buttonText;
} }
Column { displayName: model.displayName
Layout.fillWidth: true enabled: false
}
Column {
Layout.fillWidth: true
Text { visible: model.isUser; text: model.displayName; color: Nheko.colors.text} Text { visible: model.isUser; text: model.displayName; color: Nheko.colors.text}
Text { visible: model.isUser; text: model.mxid; color: Nheko.colors.text} Text { visible: model.isUser; text: model.mxid; color: Nheko.colors.text}
Text { Text {
visible: !model.isUser; visible: !model.isUser;
text: { text: {
if (editingModel.adminLevel == model.powerlevel) if (editingModel.adminLevel == model.powerlevel)
return qsTr("Administrator (%1)").arg(model.powerlevel) return qsTr("Administrator (%1)").arg(model.powerlevel)
else if (editingModel.moderatorLevel == model.powerlevel) else if (editingModel.moderatorLevel == model.powerlevel)
return qsTr("Moderator (%1)").arg(model.powerlevel) return qsTr("Moderator (%1)").arg(model.powerlevel)
else else
return qsTr("Custom (%1)").arg(model.powerlevel) return qsTr("Custom (%1)").arg(model.powerlevel)
}
color: Nheko.colors.text
} }
color: Nheko.colors.text
} }
}
ImageButton { ImageButton {
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
Layout.rightMargin: 2 Layout.rightMargin: 2
image: model.isUser ? ":/icons/icons/ui/dismiss.svg" : ":/icons/icons/ui/add-square-button.svg" image: model.isUser ? ":/icons/icons/ui/dismiss.svg" : ":/icons/icons/ui/add-square-button.svg"
visible: !model.isUser || model.removeable visible: !model.isUser || model.removeable
hoverEnabled: true hoverEnabled: true
ToolTip.visible: hovered ToolTip.visible: hovered
ToolTip.text: model.isUser ? qsTr("Remove user") : qsTr("Add user") ToolTip.text: model.isUser ? qsTr("Remove user") : qsTr("Add user")
onClicked: { onClicked: {
if (model.isUser) { if (model.isUser) {
editingModel.users.remove(index); editingModel.users.remove(index);
} else { } else {
userEntryCompleter.y = offset userEntryCompleter.y = offset
userEntryCompleter.visible = true userEntryCompleter.visible = true
userEntryCompleter.index = index; userEntryCompleter.index = index;
userEntry.forceActiveFocus() userEntry.forceActiveFocus()
}
} }
} }
} }
} }
} }
} }
} }
} }
}
footer: DialogButtonBox { footer: DialogButtonBox {
id: dbb id: dbb
standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
onAccepted: { onAccepted: {
editingModel.commit(); editingModel.commit();
plEditorW.close(); plEditorW.close();
}
onRejected: plEditorW.close();
} }
onRejected: plEditorW.close();
} }
}

@ -239,6 +239,7 @@ PowerlevelsTypeListModel::remove(int row)
return true; return true;
} }
void void
PowerlevelsTypeListModel::add(int row, QString type) PowerlevelsTypeListModel::add(int row, QString type)
{ {
@ -261,6 +262,23 @@ PowerlevelsTypeListModel::add(int row, QString type)
endInsertRows(); endInsertRows();
} }
void
PowerlevelsTypeListModel::addRole(int64_t role)
{
for (int i = 0; i < types.size(); i++) {
if (types[i].pl < role) {
beginInsertRows(QModelIndex(), i, i);
types.insert(i, Entry{"", role});
endInsertRows();
return;
}
}
beginInsertRows(QModelIndex(), types.size(), types.size());
types.push_back(Entry{"", role});
endInsertRows();
}
bool bool
PowerlevelsTypeListModel::move(int from, int to) PowerlevelsTypeListModel::move(int from, int to)
{ {
@ -299,10 +317,19 @@ PowerlevelsTypeListModel::moveRows(const QModelIndex &,
auto pl = types.at(destinationChild > 0 ? destinationChild - 1 : 0).pl; auto pl = types.at(destinationChild > 0 ? destinationChild - 1 : 0).pl;
auto sourceItem = types.takeAt(sourceRow); auto sourceItem = types.takeAt(sourceRow);
sourceItem.pl = pl; sourceItem.pl = pl;
auto movedType = sourceItem.type;
if (destinationChild < sourceRow) if (destinationChild < sourceRow)
types.insert(destinationChild, std::move(sourceItem)); types.insert(destinationChild, std::move(sourceItem));
else else
types.insert(destinationChild - 1, std::move(sourceItem)); types.insert(destinationChild - 1, std::move(sourceItem));
if (movedType == "m.room.power_levels")
emit adminLevelChanged();
else if (movedType == "redact")
emit moderatorLevelChanged();
return true; return true;
} }
@ -454,6 +481,23 @@ PowerlevelsUserListModel::add(int row, QString user)
endInsertRows(); endInsertRows();
} }
void
PowerlevelsUserListModel::addRole(int64_t role)
{
for (int i = 0; i < users.size(); i++) {
if (users[i].pl < role) {
beginInsertRows(QModelIndex(), i, i);
users.insert(i, Entry{"", role});
endInsertRows();
return;
}
}
beginInsertRows(QModelIndex(), users.size(), users.size());
users.push_back(Entry{"", role});
endInsertRows();
}
bool bool
PowerlevelsUserListModel::move(int from, int to) PowerlevelsUserListModel::move(int from, int to)
{ {
@ -492,10 +536,17 @@ PowerlevelsUserListModel::moveRows(const QModelIndex &,
auto pl = users.at(destinationChild > 0 ? destinationChild - 1 : 0).pl; auto pl = users.at(destinationChild > 0 ? destinationChild - 1 : 0).pl;
auto sourceItem = users.takeAt(sourceRow); auto sourceItem = users.takeAt(sourceRow);
sourceItem.pl = pl; sourceItem.pl = pl;
auto movedType = sourceItem.mxid;
if (destinationChild < sourceRow) if (destinationChild < sourceRow)
users.insert(destinationChild, std::move(sourceItem)); users.insert(destinationChild, std::move(sourceItem));
else else
users.insert(destinationChild - 1, std::move(sourceItem)); users.insert(destinationChild - 1, std::move(sourceItem));
if (movedType == "default")
emit defaultUserLevelChanged();
return true; return true;
} }
@ -508,7 +559,20 @@ PowerlevelEditingModels::PowerlevelEditingModels(QString room_id, QObject *paren
, types_(room_id.toStdString(), powerLevels_, this) , types_(room_id.toStdString(), powerLevels_, this)
, users_(room_id.toStdString(), powerLevels_, this) , users_(room_id.toStdString(), powerLevels_, this)
, room_id_(room_id.toStdString()) , room_id_(room_id.toStdString())
{} {
connect(&types_,
&PowerlevelsTypeListModel::adminLevelChanged,
this,
&PowerlevelEditingModels::adminLevelChanged);
connect(&types_,
&PowerlevelsTypeListModel::moderatorLevelChanged,
this,
&PowerlevelEditingModels::moderatorLevelChanged);
connect(&users_,
&PowerlevelsUserListModel::defaultUserLevelChanged,
this,
&PowerlevelEditingModels::defaultUserLevelChanged);
}
void void
PowerlevelEditingModels::commit() PowerlevelEditingModels::commit()
@ -532,3 +596,14 @@ PowerlevelEditingModels::commit()
} }
}); });
} }
void
PowerlevelEditingModels::addRole(int pl)
{
for (const auto &e : types_.types)
if (pl == int(e.pl))
return;
types_.addRole(pl);
users_.addRole(pl);
}

@ -15,6 +15,10 @@ class PowerlevelsTypeListModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
signals:
void adminLevelChanged();
void moderatorLevelChanged();
public: public:
enum Roles enum Roles
{ {
@ -36,6 +40,7 @@ public:
Q_INVOKABLE bool remove(int row); Q_INVOKABLE bool remove(int row);
Q_INVOKABLE bool move(int from, int to); Q_INVOKABLE bool move(int from, int to);
Q_INVOKABLE void add(int index, QString type); Q_INVOKABLE void add(int index, QString type);
void addRole(int64_t role);
bool moveRows(const QModelIndex &sourceParent, bool moveRows(const QModelIndex &sourceParent,
int sourceRow, int sourceRow,
@ -50,7 +55,6 @@ public:
mtx::events::state::power_level_t eventsDefault(); mtx::events::state::power_level_t eventsDefault();
mtx::events::state::power_level_t stateDefault(); mtx::events::state::power_level_t stateDefault();
private:
struct Entry struct Entry
{ {
~Entry() = default; ~Entry() = default;
@ -68,6 +72,9 @@ class PowerlevelsUserListModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
signals:
void defaultUserLevelChanged();
public: public:
enum Roles enum Roles
{ {
@ -91,6 +98,7 @@ public:
Q_INVOKABLE bool remove(int row); Q_INVOKABLE bool remove(int row);
Q_INVOKABLE bool move(int from, int to); Q_INVOKABLE bool move(int from, int to);
Q_INVOKABLE void add(int index, QString user); Q_INVOKABLE void add(int index, QString user);
void addRole(int64_t role);
bool moveRows(const QModelIndex &sourceParent, bool moveRows(const QModelIndex &sourceParent,
int sourceRow, int sourceRow,
@ -101,7 +109,6 @@ public:
std::map<std::string, mtx::events::state::power_level_t, std::less<>> toUsers(); std::map<std::string, mtx::events::state::power_level_t, std::less<>> toUsers();
mtx::events::state::power_level_t usersDefault(); mtx::events::state::power_level_t usersDefault();
private:
struct Entry struct Entry
{ {
~Entry() = default; ~Entry() = default;
@ -121,8 +128,14 @@ class PowerlevelEditingModels : public QObject
Q_PROPERTY(PowerlevelsUserListModel *users READ users CONSTANT) Q_PROPERTY(PowerlevelsUserListModel *users READ users CONSTANT)
Q_PROPERTY(PowerlevelsTypeListModel *types READ types CONSTANT) Q_PROPERTY(PowerlevelsTypeListModel *types READ types CONSTANT)
Q_PROPERTY(qlonglong adminLevel READ adminLevel CONSTANT) Q_PROPERTY(qlonglong adminLevel READ adminLevel NOTIFY adminLevelChanged)
Q_PROPERTY(qlonglong moderatorLevel READ moderatorLevel CONSTANT) Q_PROPERTY(qlonglong moderatorLevel READ moderatorLevel NOTIFY moderatorLevelChanged)
Q_PROPERTY(qlonglong defaultUserLevel READ defaultUserLevel NOTIFY defaultUserLevelChanged)
signals:
void adminLevelChanged();
void moderatorLevelChanged();
void defaultUserLevelChanged();
public: public:
explicit PowerlevelEditingModels(QString room_id, QObject *parent = nullptr); explicit PowerlevelEditingModels(QString room_id, QObject *parent = nullptr);
@ -134,8 +147,10 @@ public:
return powerLevels_.state_level(to_string(mtx::events::EventType::RoomPowerLevels)); return powerLevels_.state_level(to_string(mtx::events::EventType::RoomPowerLevels));
} }
qlonglong moderatorLevel() const { return powerLevels_.redact; } qlonglong moderatorLevel() const { return powerLevels_.redact; }
qlonglong defaultUserLevel() const { return powerLevels_.users_default; }
Q_INVOKABLE void commit(); Q_INVOKABLE void commit();
Q_INVOKABLE void addRole(int pl);
mtx::events::state::PowerLevels powerLevels_; mtx::events::state::PowerLevels powerLevels_;
PowerlevelsTypeListModel types_; PowerlevelsTypeListModel types_;

Loading…
Cancel
Save