paste code register
bold search result part of display names
fix magic search list
fix navigation
This commit is contained in:
Gaelle Braud 2024-09-20 15:07:40 +02:00
parent 1f764df150
commit e07cd93fad
46 changed files with 404 additions and 226 deletions

View file

@ -553,6 +553,7 @@ void App::initCppInterfaces() {
qmlRegisterType<FriendGui>(Constants::MainQmlUri, 1, 0, "FriendGui"); qmlRegisterType<FriendGui>(Constants::MainQmlUri, 1, 0, "FriendGui");
qmlRegisterUncreatableType<FriendCore>(Constants::MainQmlUri, 1, 0, "FriendCore", QLatin1String("Uncreatable")); qmlRegisterUncreatableType<FriendCore>(Constants::MainQmlUri, 1, 0, "FriendCore", QLatin1String("Uncreatable"));
qmlRegisterType<MagicSearchProxy>(Constants::MainQmlUri, 1, 0, "MagicSearchProxy"); qmlRegisterType<MagicSearchProxy>(Constants::MainQmlUri, 1, 0, "MagicSearchProxy");
qmlRegisterType<MagicSearchList>(Constants::MainQmlUri, 1, 0, "MagicSearchList");
qmlRegisterType<CameraGui>(Constants::MainQmlUri, 1, 0, "CameraGui"); qmlRegisterType<CameraGui>(Constants::MainQmlUri, 1, 0, "CameraGui");
qmlRegisterType<FPSCounter>(Constants::MainQmlUri, 1, 0, "FPSCounter"); qmlRegisterType<FPSCounter>(Constants::MainQmlUri, 1, 0, "FPSCounter");

View file

@ -35,9 +35,9 @@ QSharedPointer<AccountDeviceList> AccountDeviceList::create() {
return model; return model;
} }
QSharedPointer<AccountDeviceList> AccountDeviceList::create(const std::shared_ptr<AccountModel> &accountModel) { QSharedPointer<AccountDeviceList> AccountDeviceList::create(const QSharedPointer<AccountCore> &account) {
auto model = create(); auto model = create();
model->setAccountModel(accountModel); model->setAccount(account);
return model; return model;
} }
@ -59,15 +59,15 @@ AccountDeviceList::buildDevices(const std::list<std::shared_ptr<linphone::Accoun
return devices; return devices;
} }
const std::shared_ptr<AccountModel> &AccountDeviceList::getAccountModel() const { const QSharedPointer<AccountCore> &AccountDeviceList::getAccount() const {
return mAccountModel; return mAccountCore;
} }
void AccountDeviceList::setAccountModel(const std::shared_ptr<AccountModel> &accountModel) { void AccountDeviceList::setAccount(const QSharedPointer<AccountCore> &accountCore) {
mustBeInMainThread(log().arg(Q_FUNC_INFO)); mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mAccountModel != accountModel) { if (mAccountCore != accountCore) {
mAccountModel = accountModel; mAccountCore = accountCore;
lDebug() << log().arg("Set account model") << mAccountModel.get(); lDebug() << log().arg("Set account model") << mAccountCore.get();
// oldConnect.unlock(); // oldConnect.unlock();
refreshDevices(); refreshDevices();
// } // }
@ -79,12 +79,12 @@ void AccountDeviceList::refreshDevices() {
beginResetModel(); beginResetModel();
clearData(); clearData();
endResetModel(); endResetModel();
if (mAccountModel) { if (mAccountCore) {
auto requestDeviceList = [this] { auto requestDeviceList = [this] {
if (!mAccountManagerServicesModelConnection) return; if (!mAccountManagerServicesModelConnection) return;
mAccountManagerServicesModelConnection->invokeToModel([this]() { mAccountManagerServicesModelConnection->invokeToModel([this]() {
auto identityAddress = mAccountModel->getMonitor()->getParams()->getIdentityAddress(); auto identityAddress = mAccountCore->getModel()->getMonitor()->getParams()->getIdentityAddress();
auto authinfo = mAccountModel->getMonitor()->findAuthInfo(); auto authinfo = mAccountCore->getModel()->getMonitor()->findAuthInfo();
qDebug() << "[AccountDeviceList] request devices for address" << identityAddress->asStringUriOnly(); qDebug() << "[AccountDeviceList] request devices for address" << identityAddress->asStringUriOnly();
mAccountManagerServicesModel->getDeviceList(identityAddress); mAccountManagerServicesModel->getDeviceList(identityAddress);
}); });
@ -110,8 +110,8 @@ void AccountDeviceList::deleteDevice(AccountDeviceGui *deviceGui) {
auto deviceModel = deviceCore->getModel(); auto deviceModel = deviceCore->getModel();
mAccountManagerServicesModelConnection->invokeToModel([this, deviceModel]() { mAccountManagerServicesModelConnection->invokeToModel([this, deviceModel]() {
auto linphoneDevice = deviceModel->getDevice(); auto linphoneDevice = deviceModel->getDevice();
auto identityAddress = mAccountModel->getMonitor()->getParams()->getIdentityAddress(); auto identityAddress = mAccountCore->getModel()->getMonitor()->getParams()->getIdentityAddress();
auto authinfo = mAccountModel->getMonitor()->findAuthInfo(); auto authinfo = mAccountCore->getModel()->getMonitor()->findAuthInfo();
qDebug() << "[AccountDeviceList] delete device" << linphoneDevice->getName() << "of address" qDebug() << "[AccountDeviceList] delete device" << linphoneDevice->getName() << "of address"
<< identityAddress->asStringUriOnly(); << identityAddress->asStringUriOnly();
mAccountManagerServicesModel->deleteDevice(identityAddress, linphoneDevice); mAccountManagerServicesModel->deleteDevice(identityAddress, linphoneDevice);

View file

@ -25,7 +25,6 @@
#include "AccountDeviceCore.hpp" #include "AccountDeviceCore.hpp"
#include "core/account/AccountGui.hpp" #include "core/account/AccountGui.hpp"
#include "model/account/AccountManagerServicesModel.hpp" #include "model/account/AccountManagerServicesModel.hpp"
#include "model/account/AccountModel.hpp"
#include "tool/AbstractObject.hpp" #include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp" #include "tool/thread/SafeConnection.hpp"
@ -35,7 +34,7 @@ class AccountDeviceList : public ListProxy, public AbstractObject {
public: public:
static QSharedPointer<AccountDeviceList> create(); static QSharedPointer<AccountDeviceList> create();
static QSharedPointer<AccountDeviceList> create(const std::shared_ptr<AccountModel> &accountModel); static QSharedPointer<AccountDeviceList> create(const QSharedPointer<AccountCore> &accountCore);
AccountDeviceList(); AccountDeviceList();
~AccountDeviceList(); ~AccountDeviceList();
@ -43,8 +42,8 @@ public:
QList<QSharedPointer<AccountDeviceCore>> QList<QSharedPointer<AccountDeviceCore>>
buildDevices(const std::list<std::shared_ptr<linphone::AccountDevice>> &devicesList); buildDevices(const std::list<std::shared_ptr<linphone::AccountDevice>> &devicesList);
const std::shared_ptr<AccountModel> &getAccountModel() const; const QSharedPointer<AccountCore> &getAccount() const;
void setAccountModel(const std::shared_ptr<AccountModel> &accountModel); void setAccount(const QSharedPointer<AccountCore> &accountCore);
void refreshDevices(); void refreshDevices();
void setDevices(QList<QSharedPointer<AccountDeviceCore>> devices); void setDevices(QList<QSharedPointer<AccountDeviceCore>> devices);
@ -58,7 +57,7 @@ signals:
void componentReady(); void componentReady();
private: private:
std::shared_ptr<AccountModel> mAccountModel; QSharedPointer<AccountCore> mAccountCore;
std::shared_ptr<AccountManagerServicesModel> mAccountManagerServicesModel; std::shared_ptr<AccountManagerServicesModel> mAccountManagerServicesModel;
QSharedPointer<SafeConnection<AccountDeviceList, AccountManagerServicesModel>> QSharedPointer<SafeConnection<AccountDeviceList, AccountManagerServicesModel>>
mAccountManagerServicesModelConnection; mAccountManagerServicesModelConnection;

View file

@ -37,22 +37,16 @@ AccountDeviceProxy::AccountDeviceProxy(QObject *parent) : SortFilterProxy(parent
} }
AccountDeviceProxy::~AccountDeviceProxy() { AccountDeviceProxy::~AccountDeviceProxy() {
setSourceModel(nullptr); // setSourceModel(nullptr);
} }
AccountGui *AccountDeviceProxy::getAccount() const { AccountGui *AccountDeviceProxy::getAccount() const {
return mAccount ? new AccountGui(mAccount) : nullptr; auto account = mAccountDeviceList->getAccount();
return account ? new AccountGui(account) : nullptr;
} }
void AccountDeviceProxy::setAccount(AccountGui *accountGui) { void AccountDeviceProxy::setAccount(AccountGui *accountGui) {
auto currentAccountModel = mAccountDeviceList->getAccountModel(); mAccountDeviceList->setAccount(accountGui ? accountGui->mCore : nullptr);
auto accountCore = accountGui ? QSharedPointer<AccountCore>(accountGui->getCore()) : nullptr;
auto newModel = accountCore ? accountCore->getModel() : nullptr;
if (newModel != currentAccountModel) {
mAccount = accountCore;
mAccountDeviceList->setAccountModel(accountGui->getCore()->getModel());
emit accountChanged();
}
} }
void AccountDeviceProxy::deleteDevice(AccountDeviceGui *device) { void AccountDeviceProxy::deleteDevice(AccountDeviceGui *device) {

View file

@ -52,7 +52,6 @@ signals:
void accountChanged(); void accountChanged();
private: private:
QSharedPointer<AccountCore> mAccount;
QString mSearchText; QString mSearchText;
QSharedPointer<AccountDeviceList> mAccountDeviceList; QSharedPointer<AccountDeviceList> mAccountDeviceList;
QSharedPointer<SafeConnection<AccountDeviceProxy, CoreModel>> mCoreModelConnection; QSharedPointer<SafeConnection<AccountDeviceProxy, CoreModel>> mCoreModelConnection;

View file

@ -73,7 +73,8 @@ bool CallHistoryProxy::filterAcceptsRow(int sourceRow, const QModelIndex &source
QRegularExpression::CaseInsensitiveOption | QRegularExpression::CaseInsensitiveOption |
QRegularExpression::UseUnicodePropertiesOption); QRegularExpression::UseUnicodePropertiesOption);
auto callLog = qobject_cast<CallHistoryList *>(sourceModel())->getAt<CallHistoryCore>(sourceRow); auto callLog = qobject_cast<CallHistoryList *>(sourceModel())->getAt<CallHistoryCore>(sourceRow);
show = callLog->mRemoteAddress.contains(search) || callLog->mDisplayName.contains(search); show =
callLog->mIsConference ? callLog->mDisplayName.contains(search) : callLog->mRemoteAddress.contains(search);
} }
return show; return show;

View file

@ -37,6 +37,13 @@ QSharedPointer<MagicSearchList> MagicSearchList::create() {
return model; return model;
} }
QSharedPointer<MagicSearchList> MagicSearchList::create(MagicSearchList *magicSearchList) {
auto model = QSharedPointer<MagicSearchList>(magicSearchList, &QObject::deleteLater);
model->moveToThread(App::getInstance()->thread());
model->setSelf(model);
return model;
}
MagicSearchList::MagicSearchList(QObject *parent) : ListProxy(parent) { MagicSearchList::MagicSearchList(QObject *parent) : ListProxy(parent) {
mustBeInMainThread(getClassName()); mustBeInMainThread(getClassName());
mSourceFlags = (int)linphone::MagicSearch::Source::Friends | (int)linphone::MagicSearch::Source::LdapServers; mSourceFlags = (int)linphone::MagicSearch::Source::Friends | (int)linphone::MagicSearch::Source::LdapServers;
@ -82,6 +89,10 @@ void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) {
mModelConnection->makeConnectToCore(&MagicSearchList::lSetSourceFlags, [this](int flags) { mModelConnection->makeConnectToCore(&MagicSearchList::lSetSourceFlags, [this](int flags) {
mModelConnection->invokeToModel([this, flags]() { mMagicSearch->setSourceFlags(flags); }); mModelConnection->invokeToModel([this, flags]() { mMagicSearch->setSourceFlags(flags); });
}); });
mModelConnection->makeConnectToCore(
&MagicSearchList::lSetAggregationFlag, [this](LinphoneEnums::MagicSearchAggregation flag) {
mModelConnection->invokeToModel([this, flag]() { mMagicSearch->setAggregationFlag(flag); });
});
mModelConnection->makeConnectToModel(&MagicSearchModel::sourceFlagsChanged, [this](int flags) { mModelConnection->makeConnectToModel(&MagicSearchModel::sourceFlagsChanged, [this](int flags) {
mModelConnection->invokeToCore([this, flags]() { setSourceFlags(flags); }); mModelConnection->invokeToCore([this, flags]() { setSourceFlags(flags); });
}); });
@ -124,9 +135,15 @@ void MagicSearchList::setSelf(QSharedPointer<MagicSearchList> me) {
} }
void MagicSearchList::setResults(const QList<QSharedPointer<FriendCore>> &contacts) { void MagicSearchList::setResults(const QList<QSharedPointer<FriendCore>> &contacts) {
for (auto item : mList) {
auto isFriendCore = item.objectCast<FriendCore>();
if (!isFriendCore) continue;
disconnect(isFriendCore.get());
}
resetData(); resetData();
for (auto it : contacts) { for (auto it : contacts) {
connect(it.get(), &FriendCore::removed, this, qOverload<QObject *>(&MagicSearchList::remove)); connect(it.get(), &FriendCore::removed, this, qOverload<QObject *>(&MagicSearchList::remove));
connect(it.get(), &FriendCore::starredChanged, this, [this] { lSearch(mSearchFilter); });
} }
add(contacts); add(contacts);
} }

View file

@ -36,6 +36,7 @@ class MagicSearchList : public ListProxy, public AbstractObject {
Q_OBJECT Q_OBJECT
public: public:
static QSharedPointer<MagicSearchList> create(); static QSharedPointer<MagicSearchList> create();
static QSharedPointer<MagicSearchList> create(MagicSearchList *magicSearchList);
MagicSearchList(QObject *parent = Q_NULLPTR); MagicSearchList(QObject *parent = Q_NULLPTR);
~MagicSearchList(); ~MagicSearchList();

View file

@ -23,29 +23,48 @@
#include "core/friend/FriendGui.hpp" #include "core/friend/FriendGui.hpp"
MagicSearchProxy::MagicSearchProxy(QObject *parent) : SortFilterProxy(parent) { MagicSearchProxy::MagicSearchProxy(QObject *parent) : SortFilterProxy(parent) {
mList = MagicSearchList::create();
mSourceFlags = (int)LinphoneEnums::MagicSearchSource::Friends | (int)LinphoneEnums::MagicSearchSource::LdapServers; mSourceFlags = (int)LinphoneEnums::MagicSearchSource::Friends | (int)LinphoneEnums::MagicSearchSource::LdapServers;
mAggregationFlag = LinphoneEnums::MagicSearchAggregation::Friend; mAggregationFlag = LinphoneEnums::MagicSearchAggregation::Friend;
(mList.get(), &MagicSearchList::sourceFlagsChanged, this, &MagicSearchProxy::sourceFlagsChanged); setSourceModel(new MagicSearchList());
connect(mList.get(), &MagicSearchList::aggregationFlagChanged, this, &MagicSearchProxy::aggregationFlagChanged);
connect(mList.get(), &MagicSearchList::friendCreated, this, [this](int index) {
auto proxyIndex = mapFromSource(sourceModel()->index(index, 0));
emit friendCreated(proxyIndex.row());
});
setSourceModel(mList.get());
connect(this, &MagicSearchProxy::forceUpdate, [this] { emit mList->lSearch(mSearchText); });
sort(0); sort(0);
connect(mList.get(), &MagicSearchList::initialized, this, [this] { connect(this, &MagicSearchProxy::forceUpdate, [this] {
emit mList->lSetSourceFlags(mSourceFlags); auto magicSearchList = qobject_cast<MagicSearchList *>(sourceModel());
emit mList->lSetAggregationFlag(mAggregationFlag); if (magicSearchList) emit magicSearchList->lSearch(mSearchText);
}); });
} }
MagicSearchProxy::~MagicSearchProxy() { MagicSearchProxy::~MagicSearchProxy() {
setSourceModel(nullptr);
}
void MagicSearchProxy::setSourceModel(QAbstractItemModel *model) {
auto oldMagicSearchList = dynamic_cast<MagicSearchList *>(sourceModel());
if (oldMagicSearchList) {
disconnect(oldMagicSearchList);
}
auto newMagicSearchList = dynamic_cast<MagicSearchList *>(model);
if (newMagicSearchList) {
mList = MagicSearchList::create(newMagicSearchList);
connect(newMagicSearchList, &MagicSearchList::sourceFlagsChanged, this, &MagicSearchProxy::sourceFlagsChanged);
connect(newMagicSearchList, &MagicSearchList::aggregationFlagChanged, this,
&MagicSearchProxy::aggregationFlagChanged);
connect(newMagicSearchList, &MagicSearchList::friendCreated, this, [this](int index) {
auto proxyIndex = mapFromSource(sourceModel()->index(index, 0));
emit friendCreated(proxyIndex.row());
});
connect(newMagicSearchList, &MagicSearchList::initialized, this, [this, newMagicSearchList] {
emit newMagicSearchList->lSetSourceFlags(mSourceFlags);
emit newMagicSearchList->lSetAggregationFlag(mAggregationFlag);
});
}
QSortFilterProxyModel::setSourceModel(model);
} }
int MagicSearchProxy::findFriendIndexByAddress(const QString &address) { int MagicSearchProxy::findFriendIndexByAddress(const QString &address) {
return mapFromSource(mList->index(mList->findFriendIndexByAddress(address), 0)).row(); auto magicSearchList = qobject_cast<MagicSearchList *>(sourceModel());
if (magicSearchList)
return mapFromSource(magicSearchList->index(magicSearchList->findFriendIndexByAddress(address), 0)).row();
else return -1;
} }
QString MagicSearchProxy::getSearchText() const { QString MagicSearchProxy::getSearchText() const {
@ -53,8 +72,10 @@ QString MagicSearchProxy::getSearchText() const {
} }
void MagicSearchProxy::setSearchText(const QString &search) { void MagicSearchProxy::setSearchText(const QString &search) {
mSearchText = search; if (mSearchText != search) {
mList->setSearch(mSearchText); mSearchText = search;
mList->setSearch(mSearchText);
}
} }
int MagicSearchProxy::getSourceFlags() const { int MagicSearchProxy::getSourceFlags() const {
@ -68,6 +89,17 @@ void MagicSearchProxy::setSourceFlags(int flags) {
} }
} }
bool MagicSearchProxy::showFavouritesOnly() const {
return mShowFavouritesOnly;
}
void MagicSearchProxy::setShowFavouritesOnly(bool show) {
if (mShowFavouritesOnly != show) {
mShowFavouritesOnly = show;
emit showFavouriteOnlyChanged();
}
}
LinphoneEnums::MagicSearchAggregation MagicSearchProxy::getAggregationFlag() const { LinphoneEnums::MagicSearchAggregation MagicSearchProxy::getAggregationFlag() const {
return mAggregationFlag; return mAggregationFlag;
} }
@ -79,6 +111,17 @@ void MagicSearchProxy::setAggregationFlag(LinphoneEnums::MagicSearchAggregation
} }
} }
bool MagicSearchProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
auto model = sourceModel()->data(index);
auto friendGui = model.value<FriendGui *>();
auto friendCore = friendGui->getCore();
if (friendCore) {
return !mShowFavouritesOnly || friendCore->getStarred();
}
return false;
}
bool MagicSearchProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const { bool MagicSearchProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const {
auto l = sourceModel()->data(left); auto l = sourceModel()->data(left);
auto r = sourceModel()->data(right); auto r = sourceModel()->data(right);
@ -92,4 +135,4 @@ bool MagicSearchProxy::lessThan(const QModelIndex &left, const QModelIndex &righ
return lName < rName; return lName < rName;
} }
return true; return true;
} }

View file

@ -34,6 +34,8 @@ class MagicSearchProxy : public SortFilterProxy {
Q_PROPERTY(int sourceFlags READ getSourceFlags WRITE setSourceFlags NOTIFY sourceFlagsChanged) Q_PROPERTY(int sourceFlags READ getSourceFlags WRITE setSourceFlags NOTIFY sourceFlagsChanged)
Q_PROPERTY(LinphoneEnums::MagicSearchAggregation aggregationFlag READ getAggregationFlag WRITE setAggregationFlag Q_PROPERTY(LinphoneEnums::MagicSearchAggregation aggregationFlag READ getAggregationFlag WRITE setAggregationFlag
NOTIFY aggregationFlagChanged) NOTIFY aggregationFlagChanged)
Q_PROPERTY(
bool showFavouritesOnly READ showFavouritesOnly WRITE setShowFavouritesOnly NOTIFY showFavouriteOnlyChanged)
public: public:
MagicSearchProxy(QObject *parent = Q_NULLPTR); MagicSearchProxy(QObject *parent = Q_NULLPTR);
@ -48,6 +50,11 @@ public:
LinphoneEnums::MagicSearchAggregation getAggregationFlag() const; LinphoneEnums::MagicSearchAggregation getAggregationFlag() const;
void setAggregationFlag(LinphoneEnums::MagicSearchAggregation flag); void setAggregationFlag(LinphoneEnums::MagicSearchAggregation flag);
bool showFavouritesOnly() const;
void setShowFavouritesOnly(bool show);
void setSourceModel(QAbstractItemModel *sourceModel) override;
// Q_INVOKABLE forceUpdate(); // Q_INVOKABLE forceUpdate();
Q_INVOKABLE int findFriendIndexByAddress(const QString &address); Q_INVOKABLE int findFriendIndexByAddress(const QString &address);
@ -57,12 +64,15 @@ signals:
void aggregationFlagChanged(LinphoneEnums::MagicSearchAggregation aggregationFlag); void aggregationFlagChanged(LinphoneEnums::MagicSearchAggregation aggregationFlag);
void forceUpdate(); void forceUpdate();
void friendCreated(int index); void friendCreated(int index);
void showFavouriteOnlyChanged();
protected: protected:
QString mSearchText; QString mSearchText;
int mSourceFlags; int mSourceFlags;
bool mShowFavouritesOnly = false;
LinphoneEnums::MagicSearchAggregation mAggregationFlag; LinphoneEnums::MagicSearchAggregation mAggregationFlag;
QSharedPointer<MagicSearchList> mList; QSharedPointer<MagicSearchList> mList;
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
}; };

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -302,7 +302,7 @@ QString Utils::interpretUrl(const QString &uri) {
QString address = uri; QString address = uri;
if (!address.contains('@')) { if (!address.contains('@')) {
App::postModelBlock([address, uri]() mutable { App::postModelBlock([&address, uri]() mutable {
auto addr = ToolModel::interpretUrl(uri); auto addr = ToolModel::interpretUrl(uri);
if (addr) address = Utils::coreStringToAppString(addr->asStringUriOnly()); if (addr) address = Utils::coreStringToAppString(addr->asStringUriOnly());
}); });
@ -1357,4 +1357,25 @@ void Utils::useFetchConfig(const QString &configUrl) {
void Utils::playDtmf(const QString &dtmf) { void Utils::playDtmf(const QString &dtmf) {
const char key = dtmf.constData()[0].toLatin1(); const char key = dtmf.constData()[0].toLatin1();
App::postModelSync([key]() { CoreModel::getInstance()->getCore()->playDtmf(key, 200); }); App::postModelSync([key]() { CoreModel::getInstance()->getCore()->playDtmf(key, 200); });
}
bool Utils::isInteger(const QString &text) {
QRegularExpression re(QRegularExpression::anchoredPattern("\\d+"));
if (re.match(text).hasMatch()) {
return true;
}
return false;
}
QString Utils::boldTextPart(const QString &text, const QString &regex) {
int regexIndex = text.indexOf(regex, 0, Qt::CaseInsensitive);
if (regex.isEmpty() || regexIndex == -1) return text;
QString result;
QStringList splittedText = text.split(regex, Qt::KeepEmptyParts, Qt::CaseInsensitive);
for (int i = 0; i < splittedText.size() - 1; ++i) {
result.append(splittedText[i]);
result.append("<b>" + regex + "</b>");
}
if (splittedText.size() > 0) result.append(splittedText[splittedText.size() - 1]);
return result;
} }

View file

@ -128,6 +128,8 @@ public:
static QString getCountryName(const QLocale::Territory &p_country); static QString getCountryName(const QLocale::Territory &p_country);
Q_INVOKABLE static void useFetchConfig(const QString &configUrl); Q_INVOKABLE static void useFetchConfig(const QString &configUrl);
Q_INVOKABLE void playDtmf(const QString &dtmf); Q_INVOKABLE void playDtmf(const QString &dtmf);
Q_INVOKABLE bool isInteger(const QString &text);
Q_INVOKABLE QString boldTextPart(const QString &text, const QString &regex);
static QString getApplicationProduct(); static QString getApplicationProduct();
static QString getOsProduct(); static QString getOsProduct();

View file

@ -65,7 +65,7 @@ Control.Button {
// Crash : https://bugreports.qt.io/browse/QTBUG-124730 // Crash : https://bugreports.qt.io/browse/QTBUG-124730
shadowEnabled: true //mainItem.shadowEnabled shadowEnabled: true //mainItem.shadowEnabled
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0 shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0
} }
} }

View file

@ -47,7 +47,7 @@ ComboBox {
anchors.fill: calendarBg anchors.fill: calendarBg
source: calendarBg source: calendarBg
shadowEnabled: true shadowEnabled: true
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: 0.1 shadowOpacity: 0.1
} }
} }

View file

@ -33,7 +33,7 @@ Control.CheckBox {
// Crash : https://bugreports.qt.io/browse/QTBUG-124730 // Crash : https://bugreports.qt.io/browse/QTBUG-124730
shadowEnabled: true //mainItem.shadowEnabled shadowEnabled: true //mainItem.shadowEnabled
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0 shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0
} }
} }

View file

@ -238,7 +238,7 @@ Control.ComboBox {
source: cboxBg source: cboxBg
shadowEnabled: true shadowEnabled: true
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: 0.1 shadowOpacity: 0.1
} }
} }

View file

@ -112,6 +112,7 @@ ColumnLayout {
keyNavigationEnabled: true keyNavigationEnabled: true
keyNavigationWraps: true keyNavigationWraps: true
maximumFlickVelocity: 1500 maximumFlickVelocity: 1500
spacing: 10 * DefaultStyle.dp
highlight: Rectangle { highlight: Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -205,7 +206,7 @@ ColumnLayout {
source: popupBg source: popupBg
shadowEnabled: true shadowEnabled: true
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: 0.1 shadowOpacity: 0.1
} }
} }

View file

@ -64,7 +64,7 @@ MouseArea {
// Crash : https://bugreports.qt.io/browse/QTBUG-124730 // Crash : https://bugreports.qt.io/browse/QTBUG-124730
shadowEnabled: true //mainItem.shadowEnabled shadowEnabled: true //mainItem.shadowEnabled
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0 shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0
} }
} }

View file

@ -42,7 +42,7 @@ Item{
// Crash : https://bugreports.qt.io/browse/QTBUG-124730 // Crash : https://bugreports.qt.io/browse/QTBUG-124730
shadowEnabled: true //mainItem.shadowEnabled shadowEnabled: true //mainItem.shadowEnabled
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0 shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0
}*/ }*/

View file

@ -51,7 +51,7 @@ Button {
// Crash : https://bugreports.qt.io/browse/QTBUG-124730 // Crash : https://bugreports.qt.io/browse/QTBUG-124730
shadowEnabled: true //mainItem.shadowEnabled shadowEnabled: true //mainItem.shadowEnabled
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0 shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0
} }
} }
@ -102,8 +102,8 @@ Button {
source: popupBackground source: popupBackground
anchors.fill: popupBackground anchors.fill: popupBackground
shadowEnabled: true shadowEnabled: true
shadowBlur: 1 shadowBlur: 0.1
shadowColor: DefaultStyle.grey_900 shadowColor: DefaultStyle.grey_1000
shadowOpacity: 0.4 shadowOpacity: 0.4
} }
} }

View file

@ -52,7 +52,7 @@ Control.RadioButton {
// Crash : https://bugreports.qt.io/browse/QTBUG-124730 // Crash : https://bugreports.qt.io/browse/QTBUG-124730
shadowEnabled: true //mainItem.shadowEnabled shadowEnabled: true //mainItem.shadowEnabled
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0 shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0
} }
} }

View file

@ -41,7 +41,7 @@ Control.Slider {
// Crash : https://bugreports.qt.io/browse/QTBUG-124730 // Crash : https://bugreports.qt.io/browse/QTBUG-124730
shadowEnabled: true //mainItem.shadowEnabled shadowEnabled: true //mainItem.shadowEnabled
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0 shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0
} }
} }
@ -62,7 +62,7 @@ Control.Slider {
anchors.fill: handleRect anchors.fill: handleRect
shadowEnabled: true shadowEnabled: true
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: 0.1 shadowOpacity: 0.1
} }
} }

View file

@ -44,7 +44,7 @@ Control.Switch {
// Crash : https://bugreports.qt.io/browse/QTBUG-124730 // Crash : https://bugreports.qt.io/browse/QTBUG-124730
shadowEnabled: true //mainItem.shadowEnabled shadowEnabled: true //mainItem.shadowEnabled
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0 shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0
} }
} }

View file

@ -78,7 +78,7 @@ Control.TabBar {
// Crash : https://bugreports.qt.io/browse/QTBUG-124730 // Crash : https://bugreports.qt.io/browse/QTBUG-124730
shadowEnabled: true //mainItem.shadowEnabled shadowEnabled: true //mainItem.shadowEnabled
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: tabButton.shadowEnabled ? 0.5 : 0.0 shadowOpacity: tabButton.shadowEnabled ? 0.5 : 0.0
} }
} }

View file

@ -143,7 +143,7 @@ StackView {
source: initialItem source: initialItem
anchors.fill: initialItem anchors.fill: initialItem
shadowEnabled: true shadowEnabled: true
shadowBlur: 1 shadowBlur: 0.1
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowOpacity: 0.1 shadowOpacity: 0.1
} }

View file

@ -14,7 +14,6 @@ ListView {
//keyNavigationWraps: true //keyNavigationWraps: true
// rightMargin: 5 * DefaultStyle.dp // rightMargin: 5 * DefaultStyle.dp
property string searchBarText
property bool selectionEnabled: true property bool selectionEnabled: true
property bool hoverEnabled: true property bool hoverEnabled: true
@ -24,7 +23,18 @@ ListView {
property bool actionLayoutVisible: false property bool actionLayoutVisible: false
property bool initialHeadersVisible: true property bool initialHeadersVisible: true
property bool displayNameCapitalization: true property bool displayNameCapitalization: true
property bool showOnlyFavourites: false property bool showFavouritesOnly: false
property bool showDefaultAddress: false
property var sourceModel: MagicSearchList{}
// Model properties
// set searchBarText without specifying a model to bold
// matching names
property string searchBarText
property string searchText: searchBarText.length === 0 ? "*" : searchBarText
property var aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend
property var sourceFlags: LinphoneEnums.MagicSearchSource.Friends | LinphoneEnums.MagicSearchSource.LdapServers
property ConferenceInfoGui confInfoGui property ConferenceInfoGui confInfoGui
@ -76,12 +86,19 @@ ListView {
}else { }else {
currentIndex = -1 currentIndex = -1
} }
model: MagicSearchProxy { model: MagicSearchProxy {
id: magicSearchProxy id: magicSearchProxy
searchText: searchBarText.length === 0 ? "*" : searchBarText searchText: mainItem.searchText
// This property is needed instead of playing on the delegate visibility
// considering its starred status. Otherwise, the row in the list still
// exists even if its delegate is not visible, and creates navigation issues
showFavouritesOnly: mainItem.showFavouritesOnly
onFriendCreated: (index) => { onFriendCreated: (index) => {
mainItem.currentIndex = index mainItem.currentIndex = index
} }
aggregationFlag: mainItem.aggregationFlag
sourceModel: mainItem.sourceModel
} }
Control.ScrollBar.vertical: ScrollBar { Control.ScrollBar.vertical: ScrollBar {
@ -99,13 +116,11 @@ ListView {
} }
delegate: FocusScope { delegate: FocusScope {
id: itemDelegate id: itemDelegate
height: display ? 56 * DefaultStyle.dp : 0 height: 56 * DefaultStyle.dp
width: mainItem.width width: mainItem.width
property var previousItem : mainItem.model.count > 0 && index > 0 ? mainItem.model.getAt(index-1) : null property var previousItem : mainItem.model.count > 0 && index > 0 ? mainItem.model.getAt(index-1) : null
property var previousDisplayName: previousItem ? previousItem.core.displayName : "" property var previousDisplayName: previousItem ? previousItem.core.displayName : ""
property var displayName: modelData.core.displayName property var displayName: modelData.core.displayName
property bool display: !mainItem.showOnlyFavourites || modelData.core.starred
visible: display
Connections { Connections {
enabled: modelData.core enabled: modelData.core
@ -134,19 +149,34 @@ ListView {
anchors.left: initial.visible ? initial.right : parent.left anchors.left: initial.visible ? initial.right : parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
spacing: 10 * DefaultStyle.dp spacing: 16 * DefaultStyle.dp
z: 1 z: 1
Avatar { Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp Layout.preferredHeight: 45 * DefaultStyle.dp
contact: modelData contact: modelData
} }
Text { ColumnLayout {
text: itemDelegate.displayName spacing: 0
font.pixelSize: 14 * DefaultStyle.dp Text {
font.capitalization: mainItem.displayNameCapitalization ? Font.Capitalize : Font.MixedCase text: UtilsCpp.boldTextPart(itemDelegate.displayName, mainItem.searchBarText)
maximumLineCount: 1 font{
Layout.fillWidth: true pixelSize: mainItem.showDefaultAddress ? 16 * DefaultStyle.dp : 14 * DefaultStyle.dp
capitalization: mainItem.displayNameCapitalization ? Font.Capitalize : Font.MixedCase
weight: mainItem.showDefaultAddress ? 800 * DefaultStyle.dp : 400 * DefaultStyle.dp
}
maximumLineCount: 1
Layout.fillWidth: true
}
Text {
Layout.topMargin: 2 * DefaultStyle.dp
visible: mainItem.showDefaultAddress
text: modelData.core.defaultAddress
font {
weight: 300 * DefaultStyle.dp
pixelSize: 12 * DefaultStyle.dp
}
}
} }
EffectImage { EffectImage {
id: isSelectedCheck id: isSelectedCheck
@ -169,7 +199,7 @@ ListView {
// anchors.right: parent.right // anchors.right: parent.right
Layout.rightMargin: 5 * DefaultStyle.dp Layout.rightMargin: 5 * DefaultStyle.dp
// anchors.verticalCenter: parent.verticalCenter // anchors.verticalCenter: parent.verticalCenter
spacing: 10 * DefaultStyle.dp // TODO : change when mockup ready spacing: 16 * DefaultStyle.dp
RowLayout{ RowLayout{
id: actionButtons id: actionButtons
visible: mainItem.actionLayoutVisible visible: mainItem.actionLayoutVisible
@ -304,7 +334,7 @@ ListView {
if (mouse && mouse.button == Qt.RightButton) { if (mouse && mouse.button == Qt.RightButton) {
friendPopup.open() friendPopup.open()
} else { } else {
mainItem.currentIndex = -1 mainItem.forceActiveFocus()
mainItem.currentIndex = index mainItem.currentIndex = index
if (mainItem.multiSelectionEnabled) { if (mainItem.multiSelectionEnabled) {
var indexInSelection = mainItem.selectedContacts.indexOf(modelData.core.defaultAddress) var indexInSelection = mainItem.selectedContacts.indexOf(modelData.core.defaultAddress)

View file

@ -49,7 +49,7 @@ Item {
// // Crash : https://bugreports.qt.io/browse/QTBUG-124730 // // Crash : https://bugreports.qt.io/browse/QTBUG-124730
// shadowEnabled: true //mainItem.shadowEnabled // shadowEnabled: true //mainItem.shadowEnabled
// shadowColor: DefaultStyle.grey_1000 // shadowColor: DefaultStyle.grey_1000
// shadowBlur: 1 // shadowBlur: 0.1
// shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0 // shadowOpacity: mainItem.shadowEnabled ? 0.5 : 0.0
// } // }
onClicked: { onClicked: {

View file

@ -267,7 +267,7 @@ Item {
anchors.fill: background anchors.fill: background
shadowEnabled: true shadowEnabled: true
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1 shadowBlur: 0.1
shadowOpacity: 0.4 shadowOpacity: 0.4
} }
RowLayout{ RowLayout{

View file

@ -7,12 +7,19 @@ import Linphone
Text { Text {
id: mainItem id: mainItem
color: DefaultStyle.danger_500main color: DefaultStyle.danger_500main
visible: false
function clear() { function clear() {
autoHideErrorMessage.stop() autoHideErrorMessage.stop()
text = "" text = ""
mainItem.visible = false
} }
function setText(text) { function setText(text) {
mainItem.text = text if (text.length === 0) {
clear()
} else {
mainItem.visible = true
mainItem.text = text
}
} }
font { font {
pixelSize: 12 * DefaultStyle.dp pixelSize: 12 * DefaultStyle.dp
@ -21,7 +28,9 @@ Text {
Timer { Timer {
id: autoHideErrorMessage id: autoHideErrorMessage
interval: 5000 interval: 5000
onTriggered: mainItem.text = "" onTriggered: {
mainItem.clear()
}
} }
onTextChanged: if (mainItem.text.length > 0) autoHideErrorMessage.restart() onTextChanged: if (mainItem.text.length > 0) autoHideErrorMessage.restart()

View file

@ -16,7 +16,7 @@ ColumnLayout {
enableErrorText: true enableErrorText: true
contentItem: TextField { contentItem: TextField {
id: usernameEdit id: usernameEdit
isError: username.errorTextVisible isError: username.errorTextVisible || errorText.visible
Layout.preferredWidth: 360 * DefaultStyle.dp Layout.preferredWidth: 360 * DefaultStyle.dp
Layout.preferredHeight: 49 * DefaultStyle.dp Layout.preferredHeight: 49 * DefaultStyle.dp
} }
@ -30,7 +30,7 @@ ColumnLayout {
enableErrorText: true enableErrorText: true
contentItem: TextField { contentItem: TextField {
id: passwordEdit id: passwordEdit
isError: password.errorTextVisible isError: password.errorTextVisible || errorText.visible
Layout.preferredWidth: 360 * DefaultStyle.dp Layout.preferredWidth: 360 * DefaultStyle.dp
Layout.preferredHeight: 49 * DefaultStyle.dp Layout.preferredHeight: 49 * DefaultStyle.dp
hidden: true hidden: true
@ -130,7 +130,7 @@ ColumnLayout {
color: DefaultStyle.main2_500main color: DefaultStyle.main2_500main
text: qsTr("Mot de passe oublié ?") text: qsTr("Mot de passe oublié ?")
font{ font{
underline: forgottenButton.underline underline: true
pixelSize: 13 * DefaultStyle.dp pixelSize: 13 * DefaultStyle.dp
weight: 600 * DefaultStyle.dp weight: 600 * DefaultStyle.dp
} }

View file

@ -42,7 +42,7 @@ Control.TextField {
return true return true
} }
property int idleTimeOut: 200 property int idleTimeOut: 200
property bool empty: mainItem.propertyOwner[mainItem.propertyName]?.length == 0 property bool empty: mainItem.propertyOwner!= undefined && mainItem.propertyOwner[mainItem.propertyName]?.length == 0
property bool canBeEmpty: true property bool canBeEmpty: true
signal validationChecked(bool valid) signal validationChecked(bool valid)
@ -144,7 +144,7 @@ Control.TextField {
running: false running: false
interval: mainItem.idleTimeOut interval: mainItem.idleTimeOut
repeat: false repeat: false
onTriggered: textField.editingFinished() onTriggered: mainItem.editingFinished()
} }
onEditingFinished: { onEditingFinished: {
updateText() updateText()
@ -162,7 +162,7 @@ Control.TextField {
mainItem.validationChecked(false) mainItem.validationChecked(false)
return return
} }
if (isValid(text)) { if (isValid(text) && mainItem.propertyOwner && mainItem.propertyName) {
if (mainItem.propertyOwner[mainItem.propertyName] != text) if (mainItem.propertyOwner[mainItem.propertyName] != text)
mainItem.propertyOwner[mainItem.propertyName] = text mainItem.propertyOwner[mainItem.propertyName] = text
mainItem.validationChecked(true) mainItem.validationChecked(true)

View file

@ -111,7 +111,7 @@ Dialog {
source: backgroundItem source: backgroundItem
shadowEnabled: true shadowEnabled: true
shadowColor: DefaultStyle.grey_900 shadowColor: DefaultStyle.grey_900
shadowBlur: 1.0 shadowBlur: 0.1
shadowOpacity: 0.1 shadowOpacity: 0.1
} }
} }
@ -215,7 +215,7 @@ Dialog {
anchors.fill: code anchors.fill: code
shadowEnabled: true shadowEnabled: true
shadowOpacity: 0.1 shadowOpacity: 0.1
shadowBlur: 1.0 shadowBlur: 0.1
} }
} }
} }

View file

@ -65,6 +65,6 @@ DesktopPopup {
shadowEnabled: true shadowEnabled: true
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowOpacity: 0.1 shadowOpacity: 0.1
shadowBlur: 1 shadowBlur: 0.1
} }
} }

View file

@ -37,7 +37,7 @@ Control.Popup {
shadowEnabled: true shadowEnabled: true
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowOpacity: 0.1 shadowOpacity: 0.1
shadowBlur: 1 shadowBlur: 0.1
z: -1 z: -1
} }
Rectangle { Rectangle {

View file

@ -30,8 +30,8 @@ Control.Popup{
anchors.fill: backgroundItem anchors.fill: backgroundItem
source: backgroundItem source: backgroundItem
shadowEnabled: true shadowEnabled: true
shadowColor: DefaultStyle.grey_900 shadowColor: DefaultStyle.grey_1000
shadowBlur: 1.0 shadowBlur: 0.1
shadowOpacity: 0.1 shadowOpacity: 0.1
} }
MouseArea { MouseArea {

View file

@ -126,9 +126,7 @@ FocusScope {
Layout.preferredHeight: contentHeight Layout.preferredHeight: contentHeight
Control.ScrollBar.vertical.visible: false Control.ScrollBar.vertical.visible: false
contactMenuVisible: false contactMenuVisible: false
model: MagicSearchProxy { searchBarText: searchBar.text
searchText: searchBar.text.length === 0 ? "*" : searchBar.text
}
onSelectedContactChanged: mainItem.selectedContact = selectedContact onSelectedContactChanged: mainItem.selectedContact = selectedContact
onClicked: mainItem.callSelectedContact() onClicked: mainItem.callSelectedContact()
} }
@ -151,11 +149,8 @@ FocusScope {
Layout.preferredHeight: contentHeight Layout.preferredHeight: contentHeight
initialHeadersVisible: false initialHeadersVisible: false
displayNameCapitalization: false displayNameCapitalization: false
model: MagicSearchProxy { searchBarText: searchBar.text
searchText: searchBar.text.length === 0 ? "*" : searchBar.text sourceFlags: LinphoneEnums.MagicSearchSource.All
sourceFlags: LinphoneEnums.MagicSearchSource.All
aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend
}
onSelectedContactChanged: mainItem.selectedContact = selectedContact onSelectedContactChanged: mainItem.selectedContact = selectedContact
onClicked: mainItem.callSelectedContact() onClicked: mainItem.callSelectedContact()
} }

View file

@ -83,89 +83,96 @@ LoginLayout {
Component { Component {
id: firstItem id: firstItem
ColumnLayout { ColumnLayout {
spacing: 0 spacing: 85 * DefaultStyle.dp
Layout.maximumHeight: 420 * DefaultStyle.dp
ColumnLayout { ColumnLayout {
Text { spacing: 0
Layout.fillWidth: true ColumnLayout {
Layout.preferredWidth: rootStackView.width spacing: 28 * DefaultStyle.dp
wrapMode: Text.WordWrap Text {
color: DefaultStyle.main2_600 Layout.fillWidth: true
font { Layout.preferredWidth: rootStackView.width
pixelSize: 14 * DefaultStyle.dp wrapMode: Text.WordWrap
weight: 400* DefaultStyle.dp color: DefaultStyle.main2_900
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400* DefaultStyle.dp
}
text: "Certaines fonctionnalités nécessitent un compte Linphone, comme la messagerie de groupe, les vidéoconférences..."
}
Text {
Layout.fillWidth: true
Layout.preferredWidth: rootStackView.width
wrapMode: Text.WordWrap
color: DefaultStyle.main2_900
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400* DefaultStyle.dp
}
text:"Ces fonctionnalités sont cachées lorsque vous vous enregistrez avec un compte SIP tiers."
}
Text {
Layout.fillWidth: true
Layout.preferredWidth: rootStackView.width
wrapMode: Text.WordWrap
color: DefaultStyle.main2_900
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400* DefaultStyle.dp
}
text: "Pour les activer dans un projet commercial, veuillez nous contacter. "
} }
text: "Certaines fonctionnalités nécessitent un compte Linphone, comme la messagerie de groupe, les vidéoconférences..."
} }
Text { Button {
Layout.fillWidth: true id: openLinkButton
Layout.preferredWidth: rootStackView.width Layout.alignment: Qt.AlignCenter
wrapMode: Text.WordWrap Layout.topMargin: 18 * DefaultStyle.dp
color: DefaultStyle.main2_600 text: "linphone.org/contact"
font { textSize: 13 * DefaultStyle.dp
pixelSize: 14 * DefaultStyle.dp inversedColors: true
weight: 400* DefaultStyle.dp leftPadding: 12 * DefaultStyle.dp
rightPadding: 12 * DefaultStyle.dp
topPadding: 6 * DefaultStyle.dp
bottomPadding: 6 * DefaultStyle.dp
onClicked: {
Qt.openUrlExternally(ConstantsCpp.ContactUrl)
} }
text:"Ces fonctionnalités sont cachées lorsque vous vous enregistrez avec un compte SIP tiers." KeyNavigation.up: backButton
} KeyNavigation.down: createAccountButton
Text {
Layout.fillWidth: true
Layout.preferredWidth: rootStackView.width
wrapMode: Text.WordWrap
color: DefaultStyle.main2_600
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400* DefaultStyle.dp
}
text: "Pour les activer dans un projet commercial, veuillez nous contacter. "
} }
} }
Button { ColumnLayout {
id: openLinkButton spacing: 20 * DefaultStyle.dp
Layout.alignment: Qt.AlignCenter Button {
Layout.topMargin: 18 * DefaultStyle.dp id: createAccountButton
text: "linphone.org/contact" // Layout.topMargin: 85 * DefaultStyle.dp
textSize: 13 * DefaultStyle.dp Layout.fillWidth: true
inversedColors: true inversedColors: true
leftPadding: 12 * DefaultStyle.dp text: qsTr("Créer un compte linphone")
rightPadding: 12 * DefaultStyle.dp leftPadding: 20 * DefaultStyle.dp
topPadding: 6 * DefaultStyle.dp rightPadding: 20 * DefaultStyle.dp
bottomPadding: 6 * DefaultStyle.dp topPadding: 11 * DefaultStyle.dp
onClicked: { bottomPadding: 11 * DefaultStyle.dp
Qt.openUrlExternally(ConstantsCpp.ContactUrl) onClicked: {
console.debug("[SIPLoginPage] User: click register")
mainItem.goToRegister()
}
KeyNavigation.up: openLinkButton
KeyNavigation.down: continueButton
} }
KeyNavigation.up: backButton Button {
KeyNavigation.down: createAccountButton id: continueButton
} Layout.fillWidth: true
Button { text: qsTr("Je comprends")
id: createAccountButton leftPadding: 20 * DefaultStyle.dp
Layout.topMargin: 85 * DefaultStyle.dp rightPadding: 20 * DefaultStyle.dp
Layout.fillWidth: true topPadding: 11 * DefaultStyle.dp
inversedColors: true bottomPadding: 11 * DefaultStyle.dp
text: qsTr("Créer un compte linphone") onClicked: {
leftPadding: 20 * DefaultStyle.dp rootStackView.replace(secondItem)
rightPadding: 20 * DefaultStyle.dp }
topPadding: 11 * DefaultStyle.dp KeyNavigation.up: createAccountButton
bottomPadding: 11 * DefaultStyle.dp
onClicked: {
console.debug("[SIPLoginPage] User: click register")
mainItem.goToRegister()
} }
KeyNavigation.up: openLinkButton
KeyNavigation.down: continueButton
}
Button {
id: continueButton
Layout.topMargin: 20 * DefaultStyle.dp
Layout.fillWidth: true
text: qsTr("Je comprends")
leftPadding: 20 * DefaultStyle.dp
rightPadding: 20 * DefaultStyle.dp
topPadding: 11 * DefaultStyle.dp
bottomPadding: 11 * DefaultStyle.dp
onClicked: {
rootStackView.replace(secondItem)
}
KeyNavigation.up: createAccountButton
} }
Item { Item {
Layout.fillHeight: true Layout.fillHeight: true

View file

@ -2,6 +2,7 @@ import QtQuick 2.15
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtQuick.Controls as Control import QtQuick.Controls as Control
import Linphone import Linphone
import UtilsCpp
LoginLayout { LoginLayout {
id: mainItem id: mainItem
@ -11,6 +12,8 @@ LoginLayout {
property string address property string address
property string sipIdentityAddress property string sipIdentityAddress
property string code property string code
property bool ctrlIsPressed
onCtrlIsPressedChanged: console.log("ctrl is pressed", ctrlIsPressed)
titleContent: [ titleContent: [
RowLayout { RowLayout {
spacing: 21 * DefaultStyle.dp spacing: 21 * DefaultStyle.dp
@ -74,11 +77,26 @@ LoginLayout {
spacing: 45 * DefaultStyle.dp spacing: 45 * DefaultStyle.dp
Repeater { Repeater {
model: 4 model: 4
id: repeater
signal pasteRequested(string text)
DigitInput { DigitInput {
id: digitInput
required property int index required property int index
Layout.preferredWidth: width Layout.preferredWidth: width
Layout.preferredHeight: height Layout.preferredHeight: height
onTextEdited: { Connections {
target: repeater
function onPasteRequested(text) {
console.log("paste requested", text[digitInput.index])
var test= text;
if (UtilsCpp.isInteger(text))
{
digitInput.text = text[digitInput.index]
}
}
}
onTextChanged: {
console.log("text edited", text)
if (text.length > 0 ) { if (text.length > 0 ) {
mainItem.code = mainItem.code.slice(0, index) + text + mainItem.code.slice(index) mainItem.code = mainItem.code.slice(0, index) + text + mainItem.code.slice(index)
if (index < 3) if (index < 3)
@ -90,7 +108,32 @@ LoginLayout {
} else { } else {
if (index > 0) if (index > 0)
nextItemInFocusChain(false).forceActiveFocus() nextItemInFocusChain(false).forceActiveFocus()
} }
}
Keys.onPressed: (event) => {
if (event.key == Qt.Key_Backspace) {
if (text.length === 0) {
nextItemInFocusChain(false).forceActiveFocus()
event.accepted = true
} else {
event.accepted = false
}
} else if (event.key == Qt.Key_Control) {
mainItem.ctrlIsPressed = true
event.accepted = false
} else if (mainItem.ctrlIsPressed && event.key == Qt.Key_V) {
var clipboard = UtilsCpp.getClipboardText()
console.log("paste", clipboard)
repeater.pasteRequested(clipboard)
} else {
event.accepted = false
}
}
Keys.onReleased: (event) => {
if (event.key == Qt.Key_Control) {
mainItem.ctrlIsPressed = false
event.accepted = true
}
} }
} }
} }

View file

@ -15,7 +15,7 @@ AbstractMainPage {
signal goBack() signal goBack()
function layoutUrl(name) { function layoutUrl(name) {
return layoutsPath+"/"+name+".qml" return layoutsPath+"/"+name+".qml"
} }

View file

@ -233,7 +233,7 @@ Item {
source: popupBg source: popupBg
anchors.fill: popupBg anchors.fill: popupBg
shadowEnabled: true shadowEnabled: true
shadowBlur: 1 shadowBlur: 0.1
shadowColor: DefaultStyle.grey_1000 shadowColor: DefaultStyle.grey_1000
shadowOpacity: 0.1 shadowOpacity: 0.1
} }
@ -260,11 +260,9 @@ Item {
contactMenuVisible: false contactMenuVisible: false
actionLayoutVisible: true actionLayoutVisible: true
selectionEnabled: false selectionEnabled: false
showDefaultAddress: true
Control.ScrollBar.vertical: scrollbar Control.ScrollBar.vertical: scrollbar
model: MagicSearchProxy { searchText: magicSearchBar.text
searchText: magicSearchBar.text.length === 0 ? "*" : magicSearchBar.text
aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend
}
Keys.onPressed: (event) => { Keys.onPressed: (event) => {
if(event.key == Qt.Key_Down){ if(event.key == Qt.Key_Down){
@ -335,13 +333,11 @@ Item {
Layout.preferredHeight: 45 * DefaultStyle.dp Layout.preferredHeight: 45 * DefaultStyle.dp
_address: magicSearchBar.text _address: magicSearchBar.text
} }
ColumnLayout { Text {
Text { text: UtilsCpp.interpretUrl(magicSearchBar.text)
text: magicSearchBar.text font {
font { pixelSize: 12 * DefaultStyle.dp
pixelSize: 12 * DefaultStyle.dp weight: 300 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
} }
} }
Item { Item {

View file

@ -46,7 +46,7 @@ AbstractSettingsLayout {
Layout.rightMargin: 44 * DefaultStyle.dp Layout.rightMargin: 44 * DefaultStyle.dp
Layout.leftMargin: 64 * DefaultStyle.dp Layout.leftMargin: 64 * DefaultStyle.dp
Layout.topMargin: 20 * DefaultStyle.dp Layout.topMargin: 20 * DefaultStyle.dp
ValidatedTextField { DecoratedTextField {
propertyName: "mwiServerAddress" propertyName: "mwiServerAddress"
propertyOwner: account.core propertyOwner: account.core
title: qsTr("URI du serveur de messagerie vocale") title: qsTr("URI du serveur de messagerie vocale")
@ -105,7 +105,7 @@ AbstractSettingsLayout {
propertyName: "transport" propertyName: "transport"
propertyOwner: account.core propertyOwner: account.core
} }
ValidatedTextField { DecoratedTextField {
title: qsTr("URL du serveur mandataire") title: qsTr("URL du serveur mandataire")
propertyName: "serverAddress" propertyName: "serverAddress"
propertyOwner: account.core propertyOwner: account.core
@ -116,7 +116,7 @@ AbstractSettingsLayout {
propertyName: "outboundProxyEnabled" propertyName: "outboundProxyEnabled"
propertyOwner: account.core propertyOwner: account.core
} }
ValidatedTextField { DecoratedTextField {
propertyName: "stunServer" propertyName: "stunServer"
propertyOwner: account.core propertyOwner: account.core
title: qsTr("Adresse du serveur STUN") title: qsTr("Adresse du serveur STUN")
@ -137,26 +137,26 @@ AbstractSettingsLayout {
propertyName: "bundleModeEnabled" propertyName: "bundleModeEnabled"
propertyOwner: account.core propertyOwner: account.core
} }
ValidatedTextField { DecoratedTextField {
propertyName: "expire" propertyName: "expire"
propertyOwner: account.core propertyOwner: account.core
title: qsTr("Expiration (en seconde)") title: qsTr("Expiration (en seconde)")
canBeEmpty: false canBeEmpty: false
isValid: function(text) { return !isNaN(Number(text)); } isValid: function(text) { return !isNaN(Number(text)); }
} }
ValidatedTextField { DecoratedTextField {
title: qsTr("URI de lusine à conversations") title: qsTr("URI de lusine à conversations")
propertyName: "conferenceFactoryAddress" propertyName: "conferenceFactoryAddress"
propertyOwner: account.core propertyOwner: account.core
isValid: function(text) { return UtilsCpp.isValidSIPAddress(text); } isValid: function(text) { return UtilsCpp.isValidSIPAddress(text); }
} }
ValidatedTextField { DecoratedTextField {
title: qsTr("URI de lusine à réunions") title: qsTr("URI de lusine à réunions")
propertyName: "audioVideoConferenceFactoryAddress" propertyName: "audioVideoConferenceFactoryAddress"
propertyOwner: account.core propertyOwner: account.core
isValid: function(text) { return UtilsCpp.isValidSIPAddress(text); } isValid: function(text) { return UtilsCpp.isValidSIPAddress(text); }
} }
ValidatedTextField { DecoratedTextField {
title: qsTr("URL du serveur déchange de clés de chiffrement") title: qsTr("URL du serveur déchange de clés de chiffrement")
propertyName: "limeServerUrl" propertyName: "limeServerUrl"
propertyOwner: account.core propertyOwner: account.core

View file

@ -93,18 +93,18 @@ AbstractSettingsLayout {
Layout.rightMargin: 44 * DefaultStyle.dp Layout.rightMargin: 44 * DefaultStyle.dp
Layout.topMargin: 20 * DefaultStyle.dp Layout.topMargin: 20 * DefaultStyle.dp
Layout.leftMargin: 64 * DefaultStyle.dp Layout.leftMargin: 64 * DefaultStyle.dp
ValidatedTextField { DecoratedTextField {
id: server id: server
propertyName: "server" propertyName: "server"
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
title: qsTr("URL du serveur (ne peut être vide)") title: qsTr("URL du serveur (ne peut être vide)")
} }
ValidatedTextField { DecoratedTextField {
propertyName: "bindDn" propertyName: "bindDn"
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
title: qsTr("Bind DN") title: qsTr("Bind DN")
} }
ValidatedTextField { DecoratedTextField {
propertyName: "password" propertyName: "password"
hidden: true hidden: true
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
@ -115,51 +115,51 @@ AbstractSettingsLayout {
propertyName: "tls" propertyName: "tls"
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
} }
ValidatedTextField { DecoratedTextField {
propertyName: "baseObject" propertyName: "baseObject"
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
title: qsTr("Base de recherche (ne peut être vide)") title: qsTr("Base de recherche (ne peut être vide)")
} }
ValidatedTextField { DecoratedTextField {
propertyName: "filter" propertyName: "filter"
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
title: qsTr("Filtre") title: qsTr("Filtre")
} }
ValidatedTextField { DecoratedTextField {
propertyName: "maxResults" propertyName: "maxResults"
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
validator: RegularExpressionValidator { regularExpression: /[0-9]+/ } validator: RegularExpressionValidator { regularExpression: /[0-9]+/ }
title: qsTr("Nombre maximum de résultats") title: qsTr("Nombre maximum de résultats")
} }
ValidatedTextField { DecoratedTextField {
propertyName: "delay" propertyName: "delay"
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
validator: RegularExpressionValidator { regularExpression: /[0-9]+/ } validator: RegularExpressionValidator { regularExpression: /[0-9]+/ }
title: qsTr("Délai entre 2 requêtes (en millisecondes)") title: qsTr("Délai entre 2 requêtes (en millisecondes)")
} }
ValidatedTextField { DecoratedTextField {
propertyName: "timeout" propertyName: "timeout"
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
title: qsTr("Durée maximun (en secondes)") title: qsTr("Durée maximun (en secondes)")
validator: RegularExpressionValidator { regularExpression: /[0-9]+/ } validator: RegularExpressionValidator { regularExpression: /[0-9]+/ }
} }
ValidatedTextField { DecoratedTextField {
propertyName: "minChars" propertyName: "minChars"
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
title: qsTr("Nombre minimum de caractères pour la requête") title: qsTr("Nombre minimum de caractères pour la requête")
validator: RegularExpressionValidator { regularExpression: /[0-9]+/ } validator: RegularExpressionValidator { regularExpression: /[0-9]+/ }
} }
ValidatedTextField { DecoratedTextField {
propertyName: "nameAttribute" propertyName: "nameAttribute"
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
title: qsTr("Attributs de nom") title: qsTr("Attributs de nom")
} }
ValidatedTextField { DecoratedTextField {
propertyName: "sipAttribute" propertyName: "sipAttribute"
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
title: qsTr("Attributs SIP") title: qsTr("Attributs SIP")
} }
ValidatedTextField { DecoratedTextField {
propertyName: "sipDomain" propertyName: "sipDomain"
propertyOwner: ldapGui.core propertyOwner: ldapGui.core
title: qsTr("Domaine SIP") title: qsTr("Domaine SIP")

View file

@ -283,6 +283,7 @@ AbstractMainPage {
RowLayout { RowLayout {
z: 1 z: 1
anchors.fill: parent anchors.fill: parent
spacing: 10 * DefaultStyle.dp
Item { Item {
Layout.preferredWidth: historyAvatar.width Layout.preferredWidth: historyAvatar.width
Layout.preferredHeight: historyAvatar.height Layout.preferredHeight: historyAvatar.height
@ -291,8 +292,8 @@ AbstractMainPage {
source: historyAvatar source: historyAvatar
anchors.fill: historyAvatar anchors.fill: historyAvatar
shadowEnabled: true shadowEnabled: true
shadowBlur: 1 shadowBlur: 0.1
shadowColor: DefaultStyle.grey_900 shadowColor: DefaultStyle.grey_1000
shadowOpacity: 0.1 shadowOpacity: 0.1
} }
Avatar { Avatar {
@ -317,7 +318,7 @@ AbstractMainPage {
} }
} }
RowLayout { RowLayout {
spacing: 3 * DefaultStyle.dp spacing: 6 * DefaultStyle.dp
EffectImage { EffectImage {
id: statusIcon id: statusIcon
imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined
@ -403,7 +404,6 @@ AbstractMainPage {
onPressed: { onPressed: {
historyListView.currentIndex = model.index historyListView.currentIndex = model.index
historyListView.forceActiveFocus() historyListView.forceActiveFocus()
} }
} }
} }

View file

@ -52,10 +52,9 @@ AbstractMainPage {
} }
showDefaultItem: contactList.model.sourceModel.count === 0 showDefaultItem: contactList.model.sourceModel.count === 0
property MagicSearchProxy allFriends: MagicSearchProxy { MagicSearchList {
searchText: searchBar.text.length === 0 ? "*" : searchBar.text id: allFriends
aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend
} }
function deleteContact(contact) { function deleteContact(contact) {
@ -268,14 +267,15 @@ AbstractMainPage {
} }
ContactListView{ ContactListView{
id: favoriteList id: favoriteList
onActiveFocusChanged: if (activeFocus) console.log("favorite list focus")
hoverEnabled: mainItem.leftPanelEnabled hoverEnabled: mainItem.leftPanelEnabled
highlightFollowsCurrentItem: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: contentHeight Layout.preferredHeight: contentHeight
Control.ScrollBar.vertical.visible: false Control.ScrollBar.vertical.visible: false
showOnlyFavourites: true showFavouritesOnly: true
contactMenuVisible: true contactMenuVisible: true
model: allFriends searchBarText: searchBar.text
sourceModel: allFriends
onSelectedContactChanged: { onSelectedContactChanged: {
if (selectedContact) { if (selectedContact) {
contactList.currentIndex = -1 contactList.currentIndex = -1
@ -320,7 +320,6 @@ AbstractMainPage {
} }
ContactListView{ ContactListView{
id: contactList id: contactList
onActiveFocusChanged: if (activeFocus) console.log("contact list focus")
onCountChanged: { onCountChanged: {
if (initialFriendToDisplay.length !== 0) { if (initialFriendToDisplay.length !== 0) {
if (selectContact(initialFriendToDisplay) != -1) initialFriendToDisplay = "" if (selectContact(initialFriendToDisplay) != -1) initialFriendToDisplay = ""
@ -328,14 +327,14 @@ AbstractMainPage {
} }
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: contentHeight Layout.preferredHeight: contentHeight
interactive: false
Control.ScrollBar.vertical.visible: false Control.ScrollBar.vertical.visible: false
hoverEnabled: mainItem.leftPanelEnabled hoverEnabled: mainItem.leftPanelEnabled
contactMenuVisible: true contactMenuVisible: true
highlightFollowsCurrentItem: true
searchBarText: searchBar.text searchBarText: searchBar.text
model: allFriends sourceModel: allFriends
Connections { Connections {
target: allFriends target: contactList.model
function onFriendCreated(index) { function onFriendCreated(index) {
contactList.currentIndex = index contactList.currentIndex = index
} }

View file

@ -14,6 +14,7 @@ QtObject {
property color main2_600: "#4E6074" property color main2_600: "#4E6074"
property color main2_700: "#364860" property color main2_700: "#364860"
property color main2_800: "#22334D" property color main2_800: "#22334D"
property color main2_900: "#2D3648"
property color grey_0: "#FFFFFF" property color grey_0: "#FFFFFF"
property color grey_100: "#F9F9F9" property color grey_100: "#F9F9F9"