Skip to content
Snippets Groups Projects
  • Sébastien Blin's avatar
    e073c6f8
    i18n: support RTL languages · e073c6f8
    Sébastien Blin authored and Aline Gondim Santos's avatar Aline Gondim Santos committed
    This patch introduces some mechanisms to fully support RTL languages:
    1. RTL detection via UtilsAdapter::isRTL() checking the locale name
    2. Using QML mirroring when needed based on UtilsAdapter.isRTL
    3. Inverting panels in DualPaneView and JamiListView when needed
    with SinglePane support
    
    Moreover, lot of anchors are added to automatically manage layout
    updates.
    
    GitLab: #235
    Change-Id: I40c245f2d7fae96d796c53505af5403f5e813e7f
    e073c6f8
    History
    i18n: support RTL languages
    Sébastien Blin authored and Aline Gondim Santos's avatar Aline Gondim Santos committed
    This patch introduces some mechanisms to fully support RTL languages:
    1. RTL detection via UtilsAdapter::isRTL() checking the locale name
    2. Using QML mirroring when needed based on UtilsAdapter.isRTL
    3. Inverting panels in DualPaneView and JamiListView when needed
    with SinglePane support
    
    Moreover, lot of anchors are added to automatically manage layout
    updates.
    
    GitLab: #235
    Change-Id: I40c245f2d7fae96d796c53505af5403f5e813e7f
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
SmartListItemDelegate.qml 10.24 KiB
/*
 * Copyright (C) 2020-2023 Savoir-faire Linux Inc.
 * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
 * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects

import net.jami.Adapters 1.1
import net.jami.Constants 1.1
import net.jami.Enums 1.1
import net.jami.Models 1.1

import "../../commoncomponents"

ItemDelegate {
    id: root

    width: ListView.view.width
    height: JamiTheme.smartListItemHeight

    property string accountId: ""
    property string convId: ""

    highlighted: ListView.isCurrentItem
    property bool interactive: true
    property string lastInteractionDate: LastInteractionTimeStamp === undefined
                                         ? ""
                                         : LastInteractionTimeStamp

    property string lastInteractionFormattedDate: MessagesAdapter.getBestFormattedDate(lastInteractionDate)

    Connections {
        target: UtilsAdapter
        function onChangeLanguage() {
            UtilsAdapter.clearInteractionsCache(root.accountId, root.convId)
        }
    }

    property bool showSharePositionIndicator: PositionManager.isPositionSharedToConv(accountId, UID)
    property bool showSharedPositionIndicator: PositionManager.isConvSharingPosition(accountId, UID)

    Connections {
        target: PositionManager
        function onPositionShareConvIdsCountChanged () {
            root.showSharePositionIndicator = PositionManager.isPositionSharedToConv(accountId, UID)
        }
        function onSharingUrisCountChanged () {
            root.showSharedPositionIndicator = PositionManager.isConvSharingPosition(accountId, UID)
        }
    }

    Connections {
        target: MessagesAdapter
        function onTimestampUpdated() {
            lastInteractionFormattedDate = MessagesAdapter.getBestFormattedDate(lastInteractionDate)
        }
    }

    Component.onCompleted: {
        // Store to avoid undefined at the end
        root.accountId = Qt.binding(() => CurrentAccount.id)
        root.convId = UID
    }

    Component.onDestruction: {
        UtilsAdapter.clearInteractionsCache(root.accountId, root.convId)
    }

    RowLayout {
        anchors.fill: parent
        anchors.leftMargin: 15
        anchors.rightMargin: 15
        spacing: 10

        ConversationAvatar {
            id: avatar

            imageId: UID
            showPresenceIndicator: Presence !== undefined ? Presence : false

            Layout.preferredWidth: JamiTheme.smartListAvatarSize
            Layout.preferredHeight: JamiTheme.smartListAvatarSize

            Rectangle {
                id: overlayHighlighted
                visible: highlighted && !interactive

                anchors.fill: parent
                color: Qt.rgba(0, 0, 0, 0.5)
                radius: JamiTheme.smartListAvatarSize / 2

                Image {
                    id: highlightedImage

                    width: JamiTheme.smartListAvatarSize / 2
                    height: JamiTheme.smartListAvatarSize / 2
                    anchors.centerIn: parent

                    layer {
                        enabled: true
                        effect: ColorOverlay {
                            color: "white"
                        }
                    }
                    source: JamiResources.check_black_24dp_svg
                }
            }

        }

        ColumnLayout {
            Layout.fillWidth: true
            Layout.fillHeight: true
            spacing: 0

            // best name
            Text {
                Layout.fillWidth: true
                Layout.minimumHeight: 20
                Layout.alignment: Qt.AlignVCenter
                horizontalAlignment: Text.AlignLeft
                elide: Text.ElideMiddle
                text: Title === undefined ? "" : Title
                textFormat: TextEdit.PlainText
                font.pointSize: JamiTheme.smartlistItemFontSize
                font.weight: UnreadMessagesCount ? Font.Bold : Font.Normal
                color: JamiTheme.textColor
            }
            RowLayout {
                visible: ContactType !== Profile.Type.TEMPORARY
                         && !IsBanned
                         && lastInteractionFormattedDate !== undefined
                         && interactive
                Layout.fillWidth: true
                Layout.minimumHeight: 20
                Layout.alignment: Qt.AlignTop

                // last Interaction date
                Text {
                    Layout.alignment: Qt.AlignVCenter
                    text: lastInteractionFormattedDate === undefined ? "" : lastInteractionFormattedDate
                    textFormat: TextEdit.PlainText
                    font.pointSize: JamiTheme.smartlistItemInfoFontSize
                    font.weight: UnreadMessagesCount ? Font.DemiBold : Font.Normal
                    color: JamiTheme.textColor
                }

                // last Interaction
                Text {
                    elide: Text.ElideRight
                    Layout.fillWidth: true
                    Layout.alignment: Qt.AlignVCenter
                    horizontalAlignment: Text.AlignLeft
                    text: Draft ?
                              Draft :
                              (LastInteraction === undefined ? "" : LastInteraction)
                    textFormat: TextEdit.PlainText
                    font.pointSize: JamiTheme.smartlistItemInfoFontSize
                    font.weight: UnreadMessagesCount ? Font.Normal : Font.Light
                    font.hintingPreference: Font.PreferNoHinting
                    maximumLineCount: 1
                    color: JamiTheme.textColor
                }
            }
            Text {
                Layout.fillWidth: true
                Layout.minimumHeight: 20
                Layout.alignment: Qt.AlignVCenter
                text: JamiStrings.banned
                textFormat: TextEdit.PlainText
                visible: IsBanned
                font.pointSize: JamiTheme.smartlistItemFontSize
                font.weight: Font.Bold
                color: JamiTheme.textColor
            }
        }

        // Draft indicator
        ResponsiveImage {
            visible: Draft && !root.highlighted
            source: JamiResources.round_edit_24dp_svg
            color: JamiTheme.primaryForegroundColor
        }

        // Show that a call is ongoing for groups indicator
        ResponsiveImage {
            visible: ActiveCallsCount && !root.highlighted
            source: JamiResources.videocam_24dp_svg
            color: JamiTheme.primaryForegroundColor
        }

        BlinkingLocationIcon {
            isSharing: true
            visible: showSharePositionIndicator
            arrowTimerVisibility: locationIconTimer.showIconArrow
            color: JamiTheme.sharePositionIndicatorColor
        }

        BlinkingLocationIcon {
            isSharing: false
            visible: showSharedPositionIndicator
            arrowTimerVisibility: locationIconTimer.showIconArrow
            color: JamiTheme.sharedPositionIndicatorColor
        }

        ColumnLayout {
            Layout.fillHeight: true
            spacing: 2

            // call status
            Text {
                id: callStatusText

                Layout.minimumHeight: 20
                Layout.alignment: Qt.AlignRight
                text: InCall ? UtilsAdapter.getCallStatusStr(CallState) : ""
                textFormat: TextEdit.PlainText
                font.pointSize: JamiTheme.smartlistItemInfoFontSize
                font.weight: Font.Medium
                color: JamiTheme.textColor
            }

            // unread message count
            Item {
                Layout.preferredWidth: childrenRect.width
                Layout.preferredHeight: childrenRect.height
                Layout.alignment: Qt.AlignTop | Qt.AlignRight
                BadgeNotifier {
                    size: 20
                    count: UnreadMessagesCount
                    animate: index === 0
                }
            }
        }

        Accessible.role: Accessible.Button
        Accessible.name: Title === undefined? "" : Title
        Accessible.description: LastInteraction === undefined? "" : LastInteraction
    }

    background: Rectangle {
        color: {
            if (root.pressed || root.highlighted)
                return JamiTheme.smartListSelectedColor
            else if (root.hovered)
                return JamiTheme.smartListHoveredColor
            else
                return "transparent"
        }
    }

    onClicked: {
        if (!interactive) {
            highlighted = !highlighted
            return;
        }
        ListView.view.model.select(index)
    }
    onDoubleClicked: {
        if (!interactive)
            return;
        ListView.view.model.select(index)
        if (CurrentConversation.isSwarm && !CurrentConversation.isCoreDialog && !UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
            return; // For now disable calls for swarm with multiple participants
        if (LRCInstance.currentAccountType === Profile.Type.SIP || !CurrentAccount.videoEnabled_Video)
            CallAdapter.placeAudioOnlyCall()
        else {
            if (!CurrentConversation.readOnly) {
                CallAdapter.placeCall()
            }
        }
    }
    onPressAndHold: {
        if (!interactive)
            return;
        ListView.view.openContextMenuAt(pressX, pressY, root)
    }

    MouseArea {
        anchors.fill: parent
        enabled: interactive
        acceptedButtons: Qt.RightButton
        onClicked: function (mouse) {
            root.ListView.view.openContextMenuAt(mouse.x, mouse.y, root)
        }
    }
}