error text display
show welcome page on first launch only
try to fix crash in variant object (to fix properly)
forbid connection if account already connected
contacts list first row size + contact selected signal
accounts layout
rm layout rearrange warning
login error messages layout
trust circle avatar
no error message on status ok
busy indicator on login
This commit is contained in:
Gaelle Braud 2024-01-10 17:39:21 +01:00
parent f9abfb9fbc
commit 517c6b96a5
23 changed files with 304 additions and 144 deletions

View file

@ -81,6 +81,7 @@ void App::init() {
// Core. Manage the logger so it must be instantiate at first.
auto coreModel = CoreModel::create("", mLinphoneThread);
connect(mLinphoneThread, &QThread::started, coreModel.get(), &CoreModel::start);
mFirstLaunch = mSettings.value("firstLaunch", 1).toInt();
// Console Commands
createCommandParser();
mParser->parse(this->arguments());
@ -264,6 +265,17 @@ void App::closeCallsWindow() {
}
}
void App::setFirstLaunch(bool first) {
if (mFirstLaunch != first) {
mFirstLaunch = first;
mSettings.setValue("firstLaunch", first);
}
}
bool App::getFirstLaunch() const {
return mFirstLaunch;
}
QQuickWindow *App::getMainWindow() {
return mMainWindow;
}
}

View file

@ -20,6 +20,7 @@
#include <QCommandLineParser>
#include <QQmlApplicationEngine>
#include <QSettings>
#include <QSharedPointer>
#include "core/singleapplication/singleapplication.h"
@ -93,6 +94,9 @@ public:
QQuickWindow *getCallsWindow(QVariant callGui);
void closeCallsWindow();
bool getFirstLaunch() const;
void setFirstLaunch(bool first);
QQuickWindow *getMainWindow();
QQmlApplicationEngine *mEngine = nullptr;
@ -108,6 +112,8 @@ private:
Notifier *mNotifier = nullptr;
QQuickWindow *mMainWindow = nullptr;
QQuickWindow *mCallsWindow = nullptr;
QSettings mSettings;
bool mFirstLaunch = true;
// TODO : changer ce count lorsqu'on aura liste d'appels
int callsCount = 0;

View file

@ -49,28 +49,48 @@ void LoginPage::setRegistrationState(linphone::RegistrationState status) {
}
}
QString LoginPage::getErrorMessage() const {
return mErrorMessage;
}
void LoginPage::setErrorMessage(const QString &error) {
// force signal emission to display the error even if it doesn't change
mErrorMessage = error;
emit errorMessageChanged();
}
void LoginPage::login(const QString &username, const QString &password) {
App::postModelAsync([=]() {
QString *error = new QString(tr("Le couple identifiant mot de passe ne correspont pas"));
// Create on Model thread.
AccountManager *accountManager = new AccountManager();
connect(accountManager, &AccountManager::registrationStateChanged, this,
[accountManager, this](linphone::RegistrationState state) mutable {
[accountManager, this, error](linphone::RegistrationState state) mutable {
// View thread
setRegistrationState(state);
switch (state) {
case linphone::RegistrationState::Ok:
case linphone::RegistrationState::Cleared:
case linphone::RegistrationState::Failed: {
emit accountManager->errorMessageChanged(*error);
accountManager->deleteLater();
break;
}
case linphone::RegistrationState::Ok: {
emit accountManager->errorMessageChanged("");
break;
}
case linphone::RegistrationState::Cleared:
case linphone::RegistrationState::None:
case linphone::RegistrationState::Progress:
case linphone::RegistrationState::Refreshing:
break;
}
});
if (!accountManager->login(username, password)) {
connect(accountManager, &AccountManager::errorMessageChanged, this,
[this](QString errorMessage) { setErrorMessage(errorMessage); });
connect(accountManager, &AccountManager::destroyed, [error]() { delete error; });
if (!accountManager->login(username, password, error)) {
emit accountManager->registrationStateChanged(linphone::RegistrationState::Failed);
}
});

View file

@ -33,17 +33,23 @@ public:
~LoginPage();
Q_PROPERTY(linphone::RegistrationState registrationState READ getRegistrationState NOTIFY registrationStateChanged)
Q_PROPERTY(QString errorMessage READ getErrorMessage NOTIFY errorMessageChanged)
Q_INVOKABLE void login(const QString &username, const QString &password);
linphone::RegistrationState getRegistrationState() const;
void setRegistrationState(linphone::RegistrationState status);
QString getErrorMessage() const;
void setErrorMessage(const QString &error);
signals:
void registrationStateChanged();
void errorMessageChanged();
private:
linphone::RegistrationState mRegistrationState = linphone::RegistrationState::None;
QString mErrorMessage;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -48,7 +48,7 @@ std::shared_ptr<linphone::Account> AccountManager::createAccount(const QString &
return core->createAccount(core->createAccountParams());
}
bool AccountManager::login(QString username, QString password) {
bool AccountManager::login(QString username, QString password, QString *errorMessage) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
auto core = CoreModel::getInstance()->getCore();
auto factory = linphone::Factory::get();
@ -56,13 +56,23 @@ bool AccountManager::login(QString username, QString password) {
auto params = account->getParams()->clone();
// Sip address.
auto identity = params->getIdentityAddress()->clone();
if (mAccountModel) return false;
auto accounts = core->getAccountList();
for (auto account : accounts) {
if (account->getParams()->getIdentityAddress()->getUsername() == Utils::appStringToCoreString(username)) {
*errorMessage = tr("Le compte est déjà connecté");
return false;
}
}
identity->setUsername(Utils::appStringToCoreString(username));
if (params->setIdentityAddress(identity)) {
qWarning() << log()
.arg(QStringLiteral("Unable to set identity address: `%1`."))
.arg(Utils::coreStringToAppString(identity->asStringUriOnly()));
*errorMessage =
tr("Unable to set identity address: `%1`.").arg(Utils::coreStringToAppString(identity->asStringUriOnly()));
return false;
}

View file

@ -33,7 +33,7 @@ public:
AccountManager(QObject *parent = nullptr);
~AccountManager();
bool login(QString username, QString password);
bool login(QString username, QString password, QString *errorMessage = nullptr);
std::shared_ptr<linphone::Account> createAccount(const QString &assistantFile);
@ -42,6 +42,7 @@ public:
const std::string &message);
signals:
void registrationStateChanged(linphone::RegistrationState state);
void errorMessageChanged(const QString &errorMessage);
private:
std::shared_ptr<AccountModel> mAccountModel;

View file

@ -37,10 +37,16 @@ VariantObject::VariantObject(QVariant defaultValue, QObject *parent) {
new SafeConnection<SafeObject, SafeObject>(mCoreObject, mModelObject), &QObject::deleteLater);
mConnection->makeConnectToCore(&SafeObject::setValue, [this](QVariant value) {
mConnection->invokeToModel([this, value]() { mModelObject->onSetValue(value); });
mConnection->invokeToModel([this, value]() {
// TODO : fix this properly
if (mModelObject) mModelObject->onSetValue(value);
});
});
mConnection->makeConnectToModel(&SafeObject::setValue, [this](QVariant value) {
mConnection->invokeToCore([this, value]() { mCoreObject->onSetValue(value); });
mConnection->invokeToCore([this, value]() {
// TODO : fix this properly
if (mCoreObject) mCoreObject->onSetValue(value);
});
});
mConnection->makeConnectToModel(&SafeObject::valueChanged, [this](QVariant value) {
mConnection->invokeToCore([this, value]() { mCoreObject->valueChanged(value); });

View file

@ -315,7 +315,6 @@ Q_DECLARE_METATYPE(LinphoneEnums::FriendCapability)
Q_DECLARE_METATYPE(LinphoneEnums::MediaEncryption)
Q_DECLARE_METATYPE(LinphoneEnums::ParticipantDeviceState)
Q_DECLARE_METATYPE(LinphoneEnums::RecorderState)
Q_DECLARE_METATYPE(LinphoneEnums::RegistrationState)
Q_DECLARE_METATYPE(LinphoneEnums::TunnelMode)
Q_DECLARE_METATYPE(LinphoneEnums::TransportType)
*/

View file

@ -98,6 +98,15 @@ VariantObject *Utils::createCall(const QString &sipAddress,
return data;
}
void Utils::setFirstLaunch(bool first) {
App::getInstance()->setFirstLaunch(first);
}
bool Utils::getFirstLaunch() {
return App::getInstance()->getFirstLaunch();
}
void Utils::openCallsWindow(CallGui *call) {
if (call) App::getInstance()->getCallsWindow(QVariant::fromValue(call))->show();
}

View file

@ -57,6 +57,8 @@ public:
Q_INVOKABLE static VariantObject *createCall(const QString &sipAddress,
const QString &prepareTransfertAddress = "",
const QHash<QString, QString> &headers = {});
Q_INVOKABLE static void setFirstLaunch(bool first);
Q_INVOKABLE static bool getFirstLaunch();
Q_INVOKABLE static void openCallsWindow(CallGui *call);
Q_INVOKABLE static QQuickWindow *getMainWindow();
Q_INVOKABLE static QQuickWindow *getCallsWindow(CallGui *callGui);

View file

@ -2,7 +2,7 @@ import QtQuick 2.15
import QtQuick.Layouts 1.3
import QtQuick.Controls
import Linphone
//import UI 1.0
import UtilsCpp 1.0
Window {
id: mainWindow
@ -10,6 +10,7 @@ Window {
height: 982 * DefaultStyle.dp
visible: true
title: qsTr("Linphone")
// TODO : handle this bool when security mode is implemented
property bool firstConnection: true
function goToNewCall() {
@ -21,25 +22,23 @@ Window {
mainWindowStackView.currentItem.transferCallSucceed()
}
AccountProxy{
AccountProxy {
// TODO : change this so it does not display the main page for one second
// when we fail trying to connect the first account (account is added and
// removed shortly after)
id: accountProxy
onHaveAccountChanged: {
if(haveAccount)
mainWindowStackView.replace(mainPage, StackView.Immediate)
else
mainWindowStackView.replace(loginPage, StackView.Immediate)
}
}
StackView {
id: mainWindowStackView
anchors.fill: parent
initialItem: accountProxy.haveAccount ? mainPage : welcomePage
initialItem: accountProxy.haveAccount ? mainPage : UtilsCpp.getFirstLaunch() ? welcomePage : loginPage
}
Component {
id: welcomePage
WelcomePage {
onStartButtonPressed: {
mainWindowStackView.replace(loginPage)// Replacing the first item will destroy the old.
UtilsCpp.setFirstLaunch(false)
}
}
}
@ -51,11 +50,7 @@ Window {
onUseSIPButtonClicked: mainWindowStackView.push(sipLoginPage)
onGoToRegister: mainWindowStackView.replace(registerPage)
onConnectionSucceed: {
if (mainWindow.firstConnection) {
mainWindowStackView.replace(securityModePage)
} else {
mainWindowStackView.replace(mainPage)
}
mainWindowStackView.replace(mainPage)
}
}
}
@ -66,11 +61,7 @@ Window {
onGoToRegister: mainWindowStackView.replace(registerPage)
onConnectionSucceed: {
if (mainWindow.firstConnection) {
mainWindowStackView.replace(securityModePage)
} else {
mainWindowStackView.replace(mainPage)
}
mainWindowStackView.replace(mainPage)
}
}
}
@ -92,7 +83,9 @@ Window {
Component {
id: securityModePage
SecurityModePage {
id: securePage
onModeSelected: (index) => {
// TODO : connect to cpp part when ready
var selectedMode = index == 0 ? "chiffrement" : "interoperable"
console.debug("[SelectMode]User: User selected mode " + selectedMode)
mainWindowStackView.replace(mainPage)
@ -103,7 +96,7 @@ Window {
id: mainPage
MainLayout {
onAddAccountRequest: mainWindowStackView.replace(loginPage)
// StackView.onActivated: connectionSecured(0) // TODO : connect to cpp part when ready
}
}
}
}

View file

@ -11,21 +11,23 @@ Item {
id: mainItem
width: 517 * DefaultStyle.dp
readonly property int topPadding: 23 * DefaultStyle.dp
readonly property int bottomPadding: 18 * DefaultStyle.dp
readonly property int bottomPadding: 23 * DefaultStyle.dp
readonly property int leftPadding: 32 * DefaultStyle.dp
readonly property int rightPadding: 32 * DefaultStyle.dp
readonly property int spacing: 16 * DefaultStyle.dp
signal addAccountRequest()
implicitHeight: list.contentHeight + topPadding + bottomPadding + 32 * DefaultStyle.dp + 1 + newAccountArea.height
ColumnLayout{
id: childLayout
anchors.top: parent.top
anchors.topMargin: mainItem.topPadding
anchors.left: parent.left
anchors.leftMargin: mainItem.leftPadding
anchors.right: parent.right
anchors.rightMargin: mainItem.rightPadding
anchors.bottom: parent.bottom
anchors.bottomMargin: mainItem.bottomPadding
ListView{
id: list
Layout.preferredHeight: contentHeight
@ -69,8 +71,10 @@ Item {
EffectImage {
id: newAccount
source: AppIcons.plusCircle
Layout.fillHeight: true
Layout.preferredWidth: height
width: 32 * DefaultStyle.dp
height: 32 * DefaultStyle.dp
Layout.preferredWidth: 32 * DefaultStyle.dp
Layout.preferredHeight: 32 * DefaultStyle.dp
Layout.alignment: Qt.AlignHCenter
fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.main2_500main

View file

@ -12,6 +12,7 @@ Item {
Control.BusyIndicator {
id: busyIndicator
running: mainItem.visible
anchors.centerIn: mainItem
}
MultiEffect {
source: busyIndicator

View file

@ -55,9 +55,9 @@ Item {
}
Repeater {
id: adresses
model: [{label: "SIP", address: startCallPopup.contact ? startCallPopup.contact.core.address : ""},
{label: "Work", address: "06000000000"},
{label: "Personal", address: "060000000"}
model: [{label: "SIP", address: startCallPopup.contact ? startCallPopup.contact.core.address : ""}
// {label: "Work", address: "06000000000"},
// {label: "Personal", address: "060000000"}
] //account.adresses
Button {
id: channel
@ -235,6 +235,10 @@ Item {
Layout.fillWidth: true
id: contactList
searchBarText: searchBar.text
onContactSelected: (contact) => {
startCallPopup.contact = contact
startCallPopup.open()
}
}
}
ColumnLayout {
@ -254,6 +258,10 @@ Item {
sourceFlags: LinphoneEnums.MagicSearchSource.FavoriteFriends
aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend
}
onContactSelected: (contact) => {
startCallPopup.contact = contact
startCallPopup.open()
}
}
}
Item {

View file

@ -27,8 +27,32 @@ StackView{
|| (contact && contact.core.pictureUri)
onHaveAvatarChanged: replace(haveAvatar ? avatar : initials, StackView.Immediate)
property bool secured: false
initialItem: haveAvatar ? avatar : initials
Rectangle {
visible: mainItem.secured
anchors.fill: mainItem.currentItem
radius: mainItem.width / 2
z: 1
color: "transparent"
border {
width: 3 * DefaultStyle.dp
color: DefaultStyle.info_500_main
}
Image {
source: AppIcons.trusted
x: mainItem.width / 7
width: mainItem.width / 4.5
height: width
sourceSize.width: width
sourceSize.height: height
fillMode: Image.PreserveAspectFit
anchors.bottom: parent.bottom
}
}
Component{
id: initials
Rectangle {

View file

@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls as Control
import Linphone
@ -31,68 +32,76 @@ Rectangle{
onClicked: mainItem.avatarClicked()
}
}
ContactDescription{
id: description
Layout.fillWidth: true
Item {
Layout.preferredWidth: 200 * DefaultStyle.dp
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'
}
Layout.rightMargin: 10 * DefaultStyle.dp
ContactDescription{
id: description
anchors.fill: parent
account: mainItem.account
}
}
Control.Control {
id: registrationStatusItem
Layout.minimumWidth: 49 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
topPadding: 4 * DefaultStyle.dp
bottomPadding: 4 * DefaultStyle.dp
leftPadding: 8 * DefaultStyle.dp
rightPadding: 8 * DefaultStyle.dp
Layout.preferredWidth: text.implicitWidth + (2 * 8 * DefaultStyle.dp)
background: Rectangle{
id: registrationStatus
anchors.fill: parent
color: DefaultStyle.main2_200
radius: 90 * DefaultStyle.dp
}
contentItem: Text {
id: text
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
visible: mainItem.account
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
? qsTr("Connecté")
: mode == 1
? qsTr("Désactivé")
: mode == 2
? qsTr("Connexion...")
: qsTr("Erreur")
}
}
Item {
Layout.fillWidth: true
}
Item{
Layout.preferredWidth: 100 * DefaultStyle.dp
Layout.preferredWidth: 22 * DefaultStyle.dp
Layout.preferredHeight: 22 * DefaultStyle.dp
Layout.fillHeight: true
Rectangle{
id: unreadNotifications

View file

@ -12,9 +12,9 @@ ColumnLayout{
property string topText: displayName ? displayName.value : ''
property string bottomText: account ? account.core.identityAddress : ''
spacing: 0
Text{
width: topTextItem.implicitWidth
Text {
id: topTextItem
Layout.fillWidth: true
Layout.fillHeight: true
verticalAlignment: (bottomTextItem.visible?Text.AlignBottom:Text.AlignVCenter)
visible: text != ''
@ -22,10 +22,13 @@ ColumnLayout{
font.pixelSize: 14 * DefaultStyle.dp
color: DefaultStyle.main2_700
text: mainItem.topText
width: mainItem.width
Layout.preferredWidth: mainItem.width
wrapMode: Text.WrapAnywhere
maximumLineCount: 1
}
Text{
Text {
id: bottomTextItem
Layout.fillWidth: true
Layout.fillHeight: true
verticalAlignment: (topTextItem.visible?Text.AlignTop:Text.AlignVCenter)
visible: text != ''
@ -33,5 +36,8 @@ ColumnLayout{
font.pixelSize: 12 * DefaultStyle.dp
color: DefaultStyle.main2_400
text: mainItem.bottomText
Layout.preferredWidth: mainItem.width
maximumLineCount: 1
wrapMode: Text.WrapAnywhere
}
}

View file

@ -16,6 +16,8 @@ ListView {
property bool contactMenuVisible: true
property bool initialHeadersVisible: true
signal contactSelected(var contact)
model: MagicSearchProxy {
searchText: searchBarText.length === 0 ? "*" : searchBarText
}
@ -40,6 +42,7 @@ ListView {
}
Item {
width: mainItem.width
Layout.preferredWidth: mainItem.width
height: 56 * DefaultStyle.dp
RowLayout {
anchors.fill: parent
@ -129,9 +132,7 @@ ListView {
visible: contactArea.containsMouse || friendPopup.hovered
}
onClicked: {
startCallPopup.contact = modelData
startCallPopup.open()
// mainItem.callButtonPressed(modelData.core.address)
mainItem.contactSelected(modelData)
}
}
}

View file

@ -8,6 +8,12 @@ Text {
id: mainItem
color: DefaultStyle.danger_500main
opacity: 0
function displayText() {
mainItem.state = "Visible"
}
function hideText() {
mainItem.state = "Invisible"
}
font {
pixelSize: 13 * DefaultStyle.dp
weight: 600 * DefaultStyle.dp
@ -30,10 +36,6 @@ Text {
property: "opacity"
duration: 1000
}
// NumberAnimation {
// property: "visible"
// duration: 1100
// }
}
]
Timer {
@ -48,7 +50,7 @@ Text {
target: mainItem
onTextChanged: {
if (mainItem.text.length > 0) {
errorText.state = "Visible"
mainItem.state = "Visible"
}
}
}

View file

@ -1,5 +1,5 @@
import QtQuick 2.15
import QtQuick.Layouts 1.0
import QtQuick.Layouts
import QtQuick.Controls as Control
import Linphone
@ -24,32 +24,38 @@ ColumnLayout {
value: DefaultStyle.danger_500main
}
}
TextInput {
id: password
label: "Password"
mandatory: true
hidden: true
enableErrorText: true
Binding on background.border.color {
when: errorText.opacity != 0
value: DefaultStyle.danger_500main
}
Binding on textField.color {
when: errorText.opacity != 0
value: DefaultStyle.danger_500main
}
}
Item {
Layout.preferredHeight: password.implicitHeight
TextInput {
id: password
label: "Password"
mandatory: true
hidden: true
enableErrorText: true
ErrorText {
id: errorText
Connections {
target: LoginPageCpp
onRegistrationStateChanged: {
if (LoginPageCpp.registrationState === LinphoneEnums.RegistrationState.Failed) {
errorText.text = qsTr("Le couple identifiant mot de passe ne correspont pas")
} else if (LoginPageCpp.registrationState === LinphoneEnums.RegistrationState.Ok) {
mainItem.connectionSucceed()
Binding on background.border.color {
when: errorText.opacity != 0
value: DefaultStyle.danger_500main
}
Binding on textField.color {
when: errorText.opacity != 0
value: DefaultStyle.danger_500main
}
}
ErrorText {
anchors.bottom: password.bottom
id: errorText
Connections {
target: LoginPageCpp
onErrorMessageChanged: {
errorText.text = LoginPageCpp.errorMessage
}
onRegistrationStateChanged: {
if (LoginPageCpp.registrationState === LinphoneEnums.RegistrationState.Ok) {
mainItem.connectionSucceed()
}
}
}
}
@ -58,11 +64,43 @@ ColumnLayout {
RowLayout {
id: lastFormLineLayout
Button {
text: qsTr("Connexion")
contentItem: StackLayout {
id: connectionButton
currentIndex: 0
Text {
text: qsTr("Connexion")
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font {
pixelSize: 18 * DefaultStyle.dp
weight: 600 * DefaultStyle.dp
}
color: DefaultStyle.grey_0
}
BusyIndicator {
width: parent.height
height: parent.height
Layout.alignment: Qt.AlignCenter
indicatorColor: DefaultStyle.grey_0
}
Connections {
target: LoginPageCpp
onRegistrationStateChanged: {
if (LoginPageCpp.registrationState != LinphoneEnums.RegistrationState.Progress) {
connectionButton.currentIndex = 0
}
}
onErrorMessageChanged: {
connectionButton.currentIndex = 0
}
}
}
Layout.rightMargin: 20 * DefaultStyle.dp
onClicked: {
username.errorMessage = ""
password.errorMessage = ""
errorText.text = ""
if (username.text.length == 0 || password.text.length == 0) {
if (username.text.length == 0)
@ -72,6 +110,7 @@ ColumnLayout {
return
}
LoginPageCpp.login(username.text, password.text)
connectionButton.currentIndex = 1
}
}
Button {

View file

@ -18,6 +18,7 @@ ColumnLayout {
property string initialText
property bool enableErrorText: false
property bool errorTextVisible: errorText.opacity > 0
property alias textField: textField
property alias background: input
@ -61,11 +62,11 @@ ColumnLayout {
radius: 79 * DefaultStyle.dp
color: mainItem.enableBackgroundColors ? DefaultStyle.grey_100 : "transparent"
border.color: mainItem.enableBackgroundColors
? errorText.opacity === 0
? textField.activeFocus
? mainItem.errorTextVisible
? DefaultStyle.danger_500main
: textField.activeFocus
? DefaultStyle.main1_500_main
: DefaultStyle.grey_200
: DefaultStyle.danger_500main
: "transparent"
Control.TextField {
@ -82,7 +83,7 @@ ColumnLayout {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
color: errorText.opacity === 0 ? DefaultStyle.main2_600 : DefaultStyle.danger_500main
color: mainItem.errorTextVisible ? DefaultStyle.danger_500main : DefaultStyle.main2_600
selectByMouse: true
validator: mainItem.validator
background: Item {
@ -117,6 +118,6 @@ ColumnLayout {
id: errorText
visible: mainItem.enableErrorText
text: mainItem.errorMessage
Layout.preferredWidth: mainItem.textInputWidth
Layout.preferredWidth: implicitWidth
}
}

View file

@ -76,7 +76,6 @@ LoginLayout {
required property int index
Layout.preferredWidth: width
Layout.preferredHeight: height
Component.onCompleted: console.log("completed", width, Layout.preferredWidth)
onTextEdited: {
console.log("textfield text", text, index)
if (text.length > 0 ) {

View file

@ -54,7 +54,7 @@ LoginLayout {
}
}
}
centerContent: ColumnLayout {
centerContent: Item {
id: centerLayout
Layout.bottomMargin: 20 * DefaultStyle.dp
Layout.fillWidth: false
@ -62,6 +62,7 @@ LoginLayout {
Layout.leftMargin: 250 * DefaultStyle.dp
Layout.topMargin: 165 * DefaultStyle.dp
RowLayout {
id: carouselLayout
Image {
id: carouselImg
Layout.rightMargin: 40 * DefaultStyle.dp
@ -104,10 +105,11 @@ LoginLayout {
}
Button {
Layout.topMargin: 20 * DefaultStyle.dp
Layout.bottomMargin: 20 * DefaultStyle.dp
Layout.leftMargin: (centerLayout.width - width) * DefaultStyle.dp
Layout.alignment: Qt.AlignBottom | Qt.AlignRight
anchors.top: carouselLayout.bottom
anchors.right: carouselLayout.right
anchors.topMargin: 20 * DefaultStyle.dp
anchors.bottomMargin: 20 * DefaultStyle.dp
anchors.leftMargin: (centerLayout.width - width) * DefaultStyle.dp
y: centerLayout.implicitWidth - width
text: carousel.currentIndex < (carousel.itemsCount - 1) ? qsTr("Suivant") : qsTr("Commencer")
onClicked: {