Feature : display accounts.

- Implement shaders to make round images and use qsb --qt6.
- Add picture to Friend.
- Display username if displayname is not found.
- Compute initials from C++ with emojis.
- Add Accounts list in a popup from main window.
- Add a hack on account to update avatar on all AcountModel.
- Add Avatar item for initials/picture.
- Add Contact description item.
- Make sizes proportionals to match designs.
- Add image colorization.
This commit is contained in:
Julien Wadel 2023-12-01 11:21:50 +01:00
parent 41ee79c070
commit a1d72e6382
24 changed files with 537 additions and 30 deletions

View file

@ -40,6 +40,7 @@ FriendCore::FriendCore(const std::shared_ptr<linphone::Friend> &contact) : QObje
mFriendModel->setSelf(mFriendModel); mFriendModel->setSelf(mFriendModel);
mConsolidatedPresence = LinphoneEnums::fromLinphone(contact->getConsolidatedPresence()); mConsolidatedPresence = LinphoneEnums::fromLinphone(contact->getConsolidatedPresence());
mPresenceTimestamp = mFriendModel->getPresenceTimestamp(); mPresenceTimestamp = mFriendModel->getPresenceTimestamp();
mPictureUri = Utils::coreStringToAppString(contact->getPhoto());
auto address = contact->getAddress(); auto address = contact->getAddress();
mAddress = address ? Utils::coreStringToAppString(contact->getAddress()->asString()) : "NoAddress"; mAddress = address ? Utils::coreStringToAppString(contact->getAddress()->asString()) : "NoAddress";
mIsSaved = true; mIsSaved = true;
@ -71,6 +72,14 @@ void FriendCore::setSelf(SafeSharedPointer<QObject> me) {
setPresenceTimestamp(presenceTimestamp); setPresenceTimestamp(presenceTimestamp);
}); });
}); });
mFriendModelConnection->makeConnect(mFriendModel.get(), &FriendModel::pictureUriChanged, [this](QString uri) {
mFriendModelConnection->invokeToCore([this, uri]() { this->onPictureUriChanged(uri); });
});
// From GUI
mFriendModelConnection->makeConnect(this, &FriendCore::lSetPictureUri, [this](QString uri) {
mFriendModelConnection->invokeToModel([this, uri]() { mFriendModel->setPictureUri(uri); });
});
} else { // Create } else { // Create
mFriendModelConnection = QSharedPointer<SafeConnection>( mFriendModelConnection = QSharedPointer<SafeConnection>(
@ -133,6 +142,15 @@ void FriendCore::setPresenceTimestamp(QDateTime presenceTimestamp) {
} }
} }
QString FriendCore::getPictureUri() const {
return mPictureUri;
}
void FriendCore::onPictureUriChanged(QString uri) {
mPictureUri = uri;
emit pictureUriChanged();
}
bool FriendCore::getIsSaved() const { bool FriendCore::getIsSaved() const {
return mIsSaved; return mIsSaved;
} }

View file

@ -44,6 +44,7 @@ class FriendCore : public QObject, public AbstractObject {
Q_PROPERTY(LinphoneEnums::ConsolidatedPresence consolidatedPresence READ getConsolidatedPresence NOTIFY Q_PROPERTY(LinphoneEnums::ConsolidatedPresence consolidatedPresence READ getConsolidatedPresence NOTIFY
consolidatedPresenceChanged) consolidatedPresenceChanged)
Q_PROPERTY(bool isSaved READ getIsSaved NOTIFY isSavedChanged) Q_PROPERTY(bool isSaved READ getIsSaved NOTIFY isSavedChanged)
Q_PROPERTY(QString pictureUri READ getPictureUri WRITE lSetPictureUri NOTIFY pictureUriChanged)
public: public:
// Should be call from model Thread. Will be automatically in App thread after initialization // Should be call from model Thread. Will be automatically in App thread after initialization
@ -70,6 +71,9 @@ public:
bool getIsSaved() const; bool getIsSaved() const;
void setIsSaved(bool isSaved); void setIsSaved(bool isSaved);
QString getPictureUri() const;
void onPictureUriChanged(QString uri);
void onPresenceReceived(LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp); void onPresenceReceived(LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp);
Q_INVOKABLE void remove(); Q_INVOKABLE void remove();
@ -84,10 +88,13 @@ signals:
void presenceTimestampChanged(QDateTime presenceTimestamp); void presenceTimestampChanged(QDateTime presenceTimestamp);
void sipAddressAdded(const QString &sipAddress); void sipAddressAdded(const QString &sipAddress);
void sipAddressRemoved(const QString &sipAddress); void sipAddressRemoved(const QString &sipAddress);
void pictureUriChanged();
void saved(); void saved();
void isSavedChanged(bool isSaved); void isSavedChanged(bool isSaved);
void removed(FriendCore *contact); void removed(FriendCore *contact);
void lSetPictureUri(QString pictureUri);
protected: protected:
void writeInto(std::shared_ptr<linphone::Friend> contact) const; void writeInto(std::shared_ptr<linphone::Friend> contact) const;
void writeFrom(const std::shared_ptr<linphone::Friend> &contact); void writeFrom(const std::shared_ptr<linphone::Friend> &contact);
@ -96,6 +103,7 @@ protected:
QDateTime mPresenceTimestamp; QDateTime mPresenceTimestamp;
QString mName; QString mName;
QString mAddress; QString mAddress;
QString mPictureUri;
bool mIsSaved; bool mIsSaved;
std::shared_ptr<FriendModel> mFriendModel; std::shared_ptr<FriendModel> mFriendModel;
QSharedPointer<SafeConnection> mFriendModelConnection; QSharedPointer<SafeConnection> mFriendModelConnection;

View file

@ -4,6 +4,7 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc
"data/image/info.svg" "data/image/info.svg"
"data/image/belledonne.svg" "data/image/belledonne.svg"
"data/image/user-circle.svg" "data/image/user-circle.svg"
"data/image/user-circle-gear.svg"
"data/image/logo.svg" "data/image/logo.svg"
"data/image/login_image.svg" "data/image/login_image.svg"
"data/image/eye-slash.svg" "data/image/eye-slash.svg"
@ -45,6 +46,9 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc
"data/image/outgoing_call_missed.svg" "data/image/outgoing_call_missed.svg"
"data/image/outgoing_call_rejected.svg" "data/image/outgoing_call_rejected.svg"
data/shaders/roundEffect.vert.qsb
data/shaders/roundEffect.frag.qsb
) )
set(_LINPHONEAPP_RC_FILES ${_LINPHONEAPP_RC_FILES} PARENT_SCOPE) set(_LINPHONEAPP_RC_FILES ${_LINPHONEAPP_RC_FILES} PARENT_SCOPE)

View file

@ -0,0 +1,18 @@
#version 440
layout(location = 0) in vec2 coord;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float edge;
};
layout(binding = 1) uniform sampler2D src;
void main() {
float dist = distance(coord, vec2( 0.5 ));
float delta = fwidth(dist);
float alpha = smoothstep( mix(clamp(edge, 0.0, 1.0), 0.0, 0.5) - delta, 0.5, dist );
vec4 tex = texture(src, coord);
fragColor = mix( tex, vec4(0.0), alpha) * qt_Opacity;
}

Binary file not shown.

View file

@ -0,0 +1,13 @@
#version 440
layout(location = 0) in vec4 qt_Vertex;
layout(location = 1) in vec2 qt_MultiTextCoord0;
layout(location = 0) out vec2 coord;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
};
void main() {
coord = qt_MultiTextCoord0;
gl_Position = qt_Matrix * qt_Vertex;
}

Binary file not shown.

View file

@ -34,6 +34,10 @@ AccountModel::AccountModel(const std::shared_ptr<linphone::Account> &account, QO
mustBeInLinphoneThread(getClassName()); mustBeInLinphoneThread(getClassName());
connect(CoreModel::getInstance().get(), &CoreModel::defaultAccountChanged, this, connect(CoreModel::getInstance().get(), &CoreModel::defaultAccountChanged, this,
&AccountModel::onDefaultAccountChanged); &AccountModel::onDefaultAccountChanged);
// Hack because Account doesn't provide callbacks on updated data
connect(this, &AccountModel::defaultAccountChanged, this,
[this]() { emit pictureUriChanged(Utils::coreStringToAppString(mMonitor->getParams()->getPictureUri())); });
} }
AccountModel::~AccountModel() { AccountModel::~AccountModel() {
@ -61,7 +65,10 @@ void AccountModel::setPictureUri(QString uri) {
} }
params->setPictureUri(Utils::appStringToCoreString(uri)); params->setPictureUri(Utils::appStringToCoreString(uri));
account->setParams(params); account->setParams(params);
emit pictureUriChanged(uri); // Hack because Account doesn't provide callbacks on updated data
// emit pictureUriChanged(uri);
emit CoreModel::getInstance()->defaultAccountChanged(CoreModel::getInstance()->getCore(),
CoreModel::getInstance()->getCore()->getDefaultAccount());
} }
void AccountModel::onDefaultAccountChanged() { void AccountModel::onDefaultAccountChanged() {

View file

@ -20,7 +20,11 @@
#include "FriendModel.hpp" #include "FriendModel.hpp"
#include "core/path/Paths.hpp"
#include "tool/Utils.hpp"
#include "tool/providers/AvatarProvider.hpp"
#include <QDebug> #include <QDebug>
#include <QUrl>
DEFINE_ABSTRACT_OBJECT(FriendModel) DEFINE_ABSTRACT_OBJECT(FriendModel)
@ -48,3 +52,21 @@ QDateTime FriendModel::getPresenceTimestamp() const {
void FriendModel::onPresenceReceived(const std::shared_ptr<linphone::Friend> &contact) { void FriendModel::onPresenceReceived(const std::shared_ptr<linphone::Friend> &contact) {
emit presenceReceived(LinphoneEnums::fromLinphone(contact->getConsolidatedPresence()), getPresenceTimestamp()); emit presenceReceived(LinphoneEnums::fromLinphone(contact->getConsolidatedPresence()), getPresenceTimestamp());
} }
void FriendModel::setPictureUri(QString uri) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto account = std::dynamic_pointer_cast<linphone::Account>(mMonitor);
auto params = account->getParams()->clone();
auto oldPictureUri = Utils::coreStringToAppString(params->getPictureUri());
if (!oldPictureUri.isEmpty()) {
QString appPrefix = QStringLiteral("image://%1/").arg(AvatarProvider::ProviderId);
if (oldPictureUri.startsWith(appPrefix)) {
oldPictureUri = Paths::getAvatarsDirPath() + oldPictureUri.mid(appPrefix.length());
}
QFile oldPicture(oldPictureUri);
if (!oldPicture.remove()) qWarning() << log().arg("Cannot delete old avatar file at " + oldPictureUri);
}
params->setPictureUri(Utils::appStringToCoreString(uri));
account->setParams(params);
emit pictureUriChanged(uri);
}

View file

@ -39,9 +39,13 @@ public:
~FriendModel(); ~FriendModel();
QDateTime getPresenceTimestamp() const; QDateTime getPresenceTimestamp() const;
std::shared_ptr<linphone::Friend> getFriend() const; std::shared_ptr<linphone::Friend> getFriend() const;
void setPictureUri(QString uri);
signals:
void pictureUriChanged(QString uri);
private: private:
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT

View file

@ -51,6 +51,7 @@ QString ToolModel::getDisplayName(const std::shared_ptr<const linphone::Address>
QString displayName; QString displayName;
if (address) { if (address) {
displayName = Utils::coreStringToAppString(address->getDisplayName()); displayName = Utils::coreStringToAppString(address->getDisplayName());
if (displayName.isEmpty()) displayName = Utils::coreStringToAppString(address->getUsername());
// TODO // TODO
// std::shared_ptr<linphone::Address> cleanAddress = address->clone(); // std::shared_ptr<linphone::Address> cleanAddress = address->clone();
// cleanAddress->clean(); // cleanAddress->clean();

View file

@ -53,6 +53,26 @@ VariantObject *Utils::getDisplayName(const QString &address) {
return data; return data;
} }
QString Utils::getInitials(const QString &username) {
if (username.isEmpty()) return "";
QRegularExpression regex("[\\s\\.]+");
QStringList words = username.split(regex); // Qt 5.14: Qt::SkipEmptyParts
QStringList initials;
auto str32 = words[0].toStdU32String();
std::u32string char32;
char32 += str32[0];
initials << QString::fromStdU32String(char32);
for (int i = 1; i < words.size() && initials.size() <= 1; ++i) {
if (words[i].size() > 0) {
str32 = words[i].toStdU32String();
char32[0] = str32[0];
initials << QString::fromStdU32String(char32);
}
}
return QLocale().toUpper(initials.join(""));
}
VariantObject *Utils::createCall(const QString &sipAddress, VariantObject *Utils::createCall(const QString &sipAddress,
const QString &prepareTransfertAddress, const QString &prepareTransfertAddress,
const QHash<QString, QString> &headers) { const QHash<QString, QString> &headers) {

View file

@ -49,6 +49,7 @@ public:
} }
Q_INVOKABLE static VariantObject *getDisplayName(const QString &address); Q_INVOKABLE static VariantObject *getDisplayName(const QString &address);
Q_INVOKABLE static QString getInitials(const QString &username); // Support UTF32
Q_INVOKABLE static VariantObject *createCall(const QString &sipAddress, Q_INVOKABLE static VariantObject *createCall(const QString &sipAddress,
const QString &prepareTransfertAddress = "", const QString &prepareTransfertAddress = "",
const QHash<QString, QString> &headers = {}); const QHash<QString, QString> &headers = {});

View file

@ -6,7 +6,7 @@ import QtCore
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2 as Control import QtQuick.Controls 2.2 as Control
import QtQuick.Dialogs import QtQuick.Effects
import Linphone import Linphone
import UtilsCpp import UtilsCpp
@ -17,7 +17,7 @@ Item {
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
// spacing: 30 // spacing: 30
anchors.topMargin: 18 anchors.topMargin: 18 * DefaultStyle.dp
VerticalTabBar { VerticalTabBar {
id: tabbar id: tabbar
Layout.fillHeight: true Layout.fillHeight: true
@ -32,7 +32,9 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
RowLayout { RowLayout {
Layout.leftMargin: 25 id: topRow
Layout.leftMargin: 25 * DefaultStyle.dp
Layout.rightMargin: 41 * DefaultStyle.dp
TextInput { TextInput {
fillWidth: true fillWidth: true
placeholderText: qsTr("Rechercher un contact, appeler ou envoyer un message...") placeholderText: qsTr("Rechercher un contact, appeler ou envoyer un message...")
@ -41,42 +43,44 @@ Item {
id: avatarButton id: avatarButton
AccountProxy{ AccountProxy{
id: accountProxy id: accountProxy
property bool haveAvatar: defaultAccount && defaultAccount.core.pictureUri || false //property bool haveAvatar: defaultAccount && defaultAccount.core.pictureUri || false
} }
Layout.preferredWidth: 30 Layout.preferredWidth: 54 * DefaultStyle.dp
Layout.preferredHeight: 30 Layout.preferredHeight: width
background: Item { background: Item {
visible: false visible: false
} }
contentItem: Image { contentItem: Avatar {
id: avatar id: avatar
source: accountProxy.haveAvatar ? accountProxy.defaultAccount.core.pictureUri : AppIcons.welcomeLinphoneLogo height: avatarButton.height
fillMode: Image.PreserveAspectFit width: avatarButton.width
account: accountProxy.defaultAccount
} }
onClicked: { onClicked: {
fileDialog.open() accountList.open()
} }
FileDialog {
id: fileDialog
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
onAccepted: {
var avatarPath = UtilsCpp.createAvatar( selectedFile )
if(avatarPath){
accountProxy.defaultAccount.core.pictureUri = avatarPath
}
}
}
} }
Control.Button { Control.Button {
id: settingsButton
enabled: false enabled: false
Layout.preferredWidth: 30 Layout.preferredWidth: 30 * DefaultStyle.dp
Layout.preferredHeight: 30 Layout.preferredHeight: 30 * DefaultStyle.dp
background: Item { background: Item {
} }
contentItem: Image { contentItem: Image {
source: AppIcons.verticalDots source: AppIcons.verticalDots
} }
Popup{
id: accountList
x: -width + parent.width
y: settingsButton.height + (10 * DefaultStyle.dp)
contentWidth: accounts.width
contentHeight: accounts.height
Accounts{
id: accounts
}
}
} }
} }
StackLayout { StackLayout {

View file

@ -6,8 +6,8 @@ import Linphone
Window { Window {
id: mainWindow id: mainWindow
width: 1025 width: 1512 * DefaultStyle.dp
height: 641 height: 930 * DefaultStyle.dp
visible: true visible: true
title: qsTr("Linphone") title: qsTr("Linphone")
property bool firstConnection: true property bool firstConnection: true

View file

@ -4,10 +4,17 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/App/Layout/LoginLayout.qml view/App/Layout/LoginLayout.qml
view/App/Layout/MainLayout.qml view/App/Layout/MainLayout.qml
view/Item/Account/Accounts.qml
view/Item/Button.qml view/Item/Button.qml
view/Item/Carousel.qml view/Item/Carousel.qml
view/Item/CheckBox.qml view/Item/CheckBox.qml
view/Item/ComboBox.qml view/Item/ComboBox.qml
view/Item/Contact/Avatar.qml
view/Item/Contact/Contact.qml
view/Item/Contact/ContactDescription.qml
view/Item/DesktopPopup.qml view/Item/DesktopPopup.qml
view/Item/DigitInput.qml view/Item/DigitInput.qml
@ -18,6 +25,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Item/NumericPad.qml view/Item/NumericPad.qml
view/Item/PhoneNumberComboBox.qml view/Item/PhoneNumberComboBox.qml
view/Item/PhoneNumberInput.qml view/Item/PhoneNumberInput.qml
view/Item/Popup.qml
view/Item/RadioButton.qml view/Item/RadioButton.qml
view/Item/RectangleTest.qml view/Item/RectangleTest.qml
view/Item/SearchBar.qml view/Item/SearchBar.qml

View file

@ -0,0 +1,73 @@
import QtQuick 2.15
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2 as Control
import Linphone
import UtilsCpp
Item {
id: mainItem
width: 517 * DefaultStyle.dp
readonly property int topPadding: 23 * DefaultStyle.dp
readonly property int bottomPadding: 18 * DefaultStyle.dp
readonly property int leftPadding: 32 * DefaultStyle.dp
readonly property int rightPadding: 32 * DefaultStyle.dp
readonly property int spacing: 16 * DefaultStyle.dp
implicitHeight: list.contentHeight + topPadding + bottomPadding + 32 * DefaultStyle.dp + 1 + newAccountArea.height
ColumnLayout{
anchors.top: parent.top
anchors.topMargin: mainItem.topPadding
anchors.left: parent.left
anchors.leftMargin: mainItem.leftPadding
anchors.right: parent.right
anchors.rightMargin: mainItem.rightPadding
ListView{
id: list
Layout.preferredHeight: contentHeight
Layout.fillWidth: true
spacing: mainItem.spacing
model: AccountProxy{}
delegate: Contact{
width: list.width
account: modelData
}
}
Rectangle{
id: separator
Layout.fillWidth: true
Layout.topMargin: mainItem.spacing
Layout.bottomMargin: mainItem.spacing
height: 1
color: DefaultStyle.main2_300
}
MouseArea{ // TODO
Layout.fillWidth: true
Layout.preferredHeight: 32 * DefaultStyle.dp
onClicked: console.log('New!')
RowLayout{
id: newAccountArea
anchors.fill: parent
spacing: 5 * DefaultStyle.dp
EffectImage {
id: newAccount
image.source: AppIcons.plusCircle
Layout.fillHeight: true
Layout.preferredWidth: height
Layout.alignment: Qt.AlignHCenter
image.fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.main2_500main
}
Text{
Layout.fillHeight: true
Layout.fillWidth: true
verticalAlignment: Text.AlignVCenter
font.weight: 400 * DefaultStyle.dp
font.pixelSize: 14 * DefaultStyle.dp
color: DefaultStyle.main2_500main
text: 'Ajouter un compte'
}
}
}
}
}

View file

@ -0,0 +1,77 @@
import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2
import QtQuick.Effects
import Linphone
import UtilsCpp
// Avatar using initial of the username in case
// they don't have any profile picture
StackView{
id: mainItem
property FriendGui contact
property AccountGui account
onAccountChanged: if(account) replace(avatar, StackView.Immediate)
property string address: account ? account.core.identityAddress : ''
property var displayNameObj: UtilsCpp.getDisplayName(address)
property bool haveAvatar: (account && account.core.pictureUri )
|| (contact && contact.core.pictureUri)
initialItem: haveAvatar ? avatar : initials
Component{
id: initials
Rectangle {
id: initialItem
property string initials: UtilsCpp.getInitials(mainItem.displayNameObj.value)
onInitialsChanged: console.log("newInit:"+initials)
radius: width / 2
color: DefaultStyle.main2_200
height: mainItem.height
width: height
Component.onCompleted: console.log("init:"+initials)
Text {
anchors.fill: parent
anchors.centerIn: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: initialItem.initials
font {
pixelSize: initialItem.height * 36 / 120
weight: 800 * DefaultStyle.dp
capitalization: Font.AllUppercase
}
}
}
}
Component{
id: avatar
Item {
id: avatarItem
height: mainItem.height
width: height
Image {
id: image
visible: false
width: parent.width
height: parent.height
sourceSize.width: avatarItem.width
sourceSize.height: avatarItem.height
fillMode: Image.PreserveAspectCrop
anchors.centerIn: parent
source: mainItem.account ? mainItem.account.core.pictureUri : mainItem.contact.core.pictureUri
mipmap: true
}
ShaderEffect {
id: roundEffect
property variant src: image
property double edge: 0.9
anchors.fill: parent
vertexShader: 'qrc:/data/shaders/roundEffect.vert.qsb'
fragmentShader: 'qrc:/data/shaders/roundEffect.frag.qsb'
}
}
}
}

View file

@ -0,0 +1,137 @@
import QtCore
import QtQuick
import QtQuick.Effects
import QtQuick.Dialogs
import QtQuick.Layouts
import Linphone
import UtilsCpp
Rectangle{
id: mainItem
property AccountGui account
height: 45 * DefaultStyle.dp
RowLayout{
anchors.fill: parent
spacing: 0
Avatar{
id: avatar
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
account: mainItem.account
MouseArea{
anchors.fill: parent
onClicked: fileDialog.open()
}
FileDialog {
id: fileDialog
currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]
onAccepted: {
var avatarPath = UtilsCpp.createAvatar( selectedFile )
if(avatarPath){
mainItem.account.core.pictureUri = avatarPath
}
}
}
}
ContactDescription{
id: description
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: 10 * DefaultStyle.dp
account: mainItem.account
}
Item{
id: registrationStatusItem
Layout.preferredWidth: 97 * DefaultStyle.dp
Layout.fillHeight: true
Rectangle{
id: registrationStatus
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: Math.min(text.implicitWidth + (2 * 8 * DefaultStyle.dp), registrationStatusItem.width)
height: 24 * DefaultStyle.dp
color: DefaultStyle.main2_200
radius: 90 * DefaultStyle.dp
Text{
id: text
anchors.fill: parent
anchors.leftMargin: 8 * DefaultStyle.dp
anchors.rightMargin: 8 * DefaultStyle.dp
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
visible: mainItem.account
readonly property int mode : !mainItem.account || mainItem.account.core.registrationState == LinphoneEnums.RegistrationState.Ok
? 0
: mainItem.account.core.registrationState == LinphoneEnums.RegistrationState.Cleared || mainItem.account.core.registrationState == LinphoneEnums.RegistrationState.None
? 1
: mainItem.account.core.registrationState == LinphoneEnums.RegistrationState.Progress || mainItem.account.core.registrationState == LinphoneEnums.RegistrationState.Refreshing
? 2
: 3
// Test texts
//Timer{
// running: true
// interval: 1000
// repeat: true
// onTriggered: text.mode = (++text.mode) % 4
//}
font.weight: 300 * DefaultStyle.dp
font.pixelSize: 12 * DefaultStyle.dp
color: mode == 0
? DefaultStyle.success_500main
: mode == 1
? DefaultStyle.warning_600
: mode == 2
? DefaultStyle.main2_500main
: DefaultStyle.danger_500main
text: mode == 0
? 'Connecté'
: mode == 1
? 'Désactivé'
: mode == 2
? 'Connexion...'
: 'Erreur'
}
}
}
Item{ // TODO
Layout.preferredWidth: 100 * DefaultStyle.dp
Layout.fillHeight: true
Rectangle{
id: unreadNotifications
anchors.left: parent.left
anchors.leftMargin: 10 * DefaultStyle.dp
anchors.verticalCenter: parent.verticalCenter
width: 22 * DefaultStyle.dp
height: 22 * DefaultStyle.dp
radius: width/2
color: DefaultStyle.danger_500main
border.color: DefaultStyle.grey_0
border.width: 2 * DefaultStyle.dp
Text{
id: unreadCount
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: DefaultStyle.grey_0
text: '2'
}
}
}
EffectImage {
id: manageAccount
image.source: AppIcons.manageProfile
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
Layout.alignment: Qt.AlignHCenter
image.sourceSize.width: 24 * DefaultStyle.dp
image.fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.main2_500main
MouseArea{ // TODO
anchors.fill: parent
onClicked: console.log('Manage!')
}
}
}
}

View file

@ -0,0 +1,37 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Linphone
import UtilsCpp
ColumnLayout{
id: mainItem
property AccountGui account: null
property var displayName: account ? UtilsCpp.getDisplayName(account.core.identityAddress) : ''
property string topText: displayName ? displayName.value : ''
property string bottomText: account ? account.core.identityAddress : ''
spacing: 0
Text{
id: topTextItem
Layout.fillWidth: true
Layout.fillHeight: true
verticalAlignment: (bottomTextItem.visible?Text.AlignBottom:Text.AlignVCenter)
visible: text != ''
font.weight: 400 * DefaultStyle.dp
font.pixelSize: 14 * DefaultStyle.dp
color: DefaultStyle.main2_700
text: mainItem.topText
}
Text{
id: bottomTextItem
Layout.fillWidth: true
Layout.fillHeight: true
verticalAlignment: (topTextItem.visible?Text.AlignTop:Text.AlignVCenter)
visible: text != ''
font.weight: 300 * DefaultStyle.dp
font.pixelSize: 12 * DefaultStyle.dp
color: DefaultStyle.main2_400
text: mainItem.bottomText
}
}

View file

@ -9,21 +9,36 @@ Item {
id: mainItem id: mainItem
property alias image: image property alias image: image
property alias effect: effect property alias effect: effect
property alias effect2: effect2
property var colorizationColor
readonly property bool useColor: colorizationColor != undefined
width: image.width width: image.width
height: image.height height: image.height
Image { Image {
id: image id: image
width: 20 width: parent.width
height: 20 height: parent.height
sourceSize.width: 20 //sourceSize.width: parent.width
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
anchors.centerIn: parent anchors.centerIn: parent
visible: !effect2.enabled
} }
MultiEffect { MultiEffect {
id: effect id: effect
anchors.fill: image anchors.fill: image
source: image source: image
maskSource: image maskSource: image
visible: !effect2.enabled
brightness: effect2.enabled ? 1.0 : 0.0
} }
} MultiEffect {
id: effect2
enabled: mainItem.useColor
anchors.fill: effect
source: effect
maskSource: effect
colorizationColor: effect2.enabled ? mainItem.colorizationColor : 'black'
colorization: effect2.enabled ? 1.0 : 0.0
}
}

View file

@ -0,0 +1,27 @@
import QtQuick
import QtQuick.Controls as Control
import QtQuick.Effects
import Linphone
Control.Popup{
id: mainItem
padding: 0
background: Item{
Rectangle{
id: backgroundItem
width: mainItem.width
height: mainItem.height
radius: 16 * DefaultStyle.dp
border.color: DefaultStyle.grey_0
border.width: 1
}
MultiEffect {
anchors.fill: backgroundItem
source: backgroundItem
maskSource: backgroundItem
shadowEnabled: true
shadowBlur: 1.0
shadowOpacity: 0.1
}
}
}

View file

@ -14,6 +14,7 @@ QtObject {
property string loginImage: "image://internal/login_image.svg" property string loginImage: "image://internal/login_image.svg"
property string belledonne: "image://internal/belledonne.svg" property string belledonne: "image://internal/belledonne.svg"
property string profile: "image://internal/user-circle.svg" property string profile: "image://internal/user-circle.svg"
property string manageProfile: "image://internal/user-circle-gear.svg"
property string verif_page_image: "image://internal/verif_page_image.svg" property string verif_page_image: "image://internal/verif_page_image.svg"
property string check: "image://internal/check.svg" property string check: "image://internal/check.svg"
property string dialer: "image://internal/dialer.svg" property string dialer: "image://internal/dialer.svg"

View file

@ -78,4 +78,16 @@ QtObject {
property color splitViewHandleColor: "#F9F9F9" property color splitViewHandleColor: "#F9F9F9"
property color splitViewHoveredHandleColor: "#EDEDED" property color splitViewHoveredHandleColor: "#EDEDED"
property color danger_500main: "#DD5F5F"
property color grey_0: "#FFFFFF"
property color success_500main: "#4FAE80"
property color warning_600: "#DBB820"
property color main2_200: "#DFECF2"
property color main2_300: "#C0D1D9"
property color main2_400: "#9AABB5"
property color main2_700: "#364860"
property color main2_500main: "#6C7A87"
property double dp: 1.0//0.66
} }