Merge pull request 'Redesign my profile widget to match design. It dynamically resizes and' (#277) from dan/ui:04-widgets into master
the build was successful Details

This commit is contained in:
Sarah Jamie Lewis 2020-04-23 11:42:59 -07:00
commit 0a9b2ed6f5
12 changed files with 240 additions and 270 deletions

View File

@ -108,9 +108,11 @@ func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) {
if exists && scope == attr.PublicScope {
switch path {
case constants.Name:
uiManager.UpdateContactDisplayName(onion, val)
peer.SetContactAttribute(onion, attr.GetPublicScope(constants.Name), val)
uiManager.UpdateContactDisplayName(onion)
case constants.Picture:
uiManager.UpdateContactPicture(onion, val)
peer.SetContactAttribute(onion, attr.GetPublicScope(constants.Picture), val)
uiManager.UpdateContactPicture(onion)
}
}

View File

@ -68,8 +68,8 @@ type GrandCentralDispatcher struct {
_ func(loading bool) `signal:"SetLoadingState"`
// profile-area stuff
_ func(name, onion, image string) `signal:"UpdateMyProfile"`
_ func(status int, str string) `signal:"TorStatus"`
_ func(name, onion, image, tag string) `signal:"UpdateMyProfile"`
_ func(status int, str string) `signal:"TorStatus"`
// settings helpers
_ func(str string) `signal:"InvokePopup"`
@ -605,7 +605,8 @@ func (this *GrandCentralDispatcher) loadProfile(onion string) {
pic = NewImage(RandomProfileImage(onion), TypeImageDistro)
the.Peer.SetAttribute(attr.GetPublicScope(constants.Picture), ImageToString(pic))
}
this.UpdateMyProfile(the.Peer.GetName(), the.Peer.GetOnion(), getPicturePath(pic))
tag, _ := the.Peer.GetAttribute(app.AttributeTag)
this.UpdateMyProfile(the.Peer.GetName(), the.Peer.GetOnion(), getPicturePath(pic), tag)
contacts := the.Peer.GetContacts()
for i := range contacts {

View File

@ -67,7 +67,7 @@ func getNick(id string) string {
return nick
} else {
nick, exists := the.Peer.GetContactAttribute(id, attr.GetLocalScope(constants.Name))
if !exists {
if !exists || nick == "" {
nick, exists = the.Peer.GetContactAttribute(id, attr.GetPeerScope(constants.Name))
if !exists {
nick = id
@ -203,8 +203,8 @@ type Manager interface {
ReloadProfiles()
UpdateContactDisplayName(handle string, name string)
UpdateContactPicture(handle string, picVal string)
UpdateContactDisplayName(handle string)
UpdateContactPicture(handle string)
UpdateContactStatus(handle string, status int, loading bool)
UpdateContactAttribute(handle, key, value string)
@ -304,19 +304,16 @@ func (this *manager) ReloadProfiles() {
}
// UpdateContactDisplayName updates a contact's display name in the contact list and conversations
func (this *manager) UpdateContactDisplayName(handle string, name string) {
func (this *manager) UpdateContactDisplayName(handle string) {
this.gcd.DoIfProfile(this.profile, func() {
this.gcd.UpdateContactDisplayName(handle, name)
this.gcd.UpdateContactDisplayName(handle, getNick(handle))
})
}
// UpdateContactPicture updates a contact's picture in the contact list and conversations
func (this *manager) UpdateContactPicture(handle string, picVal string) {
func (this *manager) UpdateContactPicture(handle string) {
this.gcd.DoIfProfile(this.profile, func() {
pic, err := StringToImage(picVal)
if err == nil {
this.gcd.UpdateContactPicture(handle, getPicturePath(pic))
}
this.gcd.UpdateContactPicture(handle, getProfilePic(handle))
})
}

View File

@ -28,6 +28,7 @@
<file>qml/widgets/InplaceEditText.qml</file>
<file>qml/widgets/Message.qml</file>
<file>qml/widgets/ScalingLabel.qml</file>
<file>qml/widgets/EllipsisLabel.qml</file>
<file>qml/widgets/MyProfile.qml</file>
<file>qml/widgets/ProfileList.qml</file>
<file>qml/widgets/RadioButton.qml</file>

View File

@ -87,7 +87,7 @@ ApplicationWindow {
property alias pane: parentStack.currentIndex
Rectangle { // Splash pane
color: "#FFFFFF"
color: Theme.backgroundMainColor
//Layout.fillHeight: true
//Layout.minimumWidth: Layout.maximumWidth
//Layout.minimumHeight: parent.height
@ -130,7 +130,7 @@ ApplicationWindow {
spacing: 0
Rectangle { // THE LEFT PANE WITH TOOLS AND CONTACTS
color: "#D2C0DD"
color: Theme.backgroundMainColor
Layout.fillHeight: true
Layout.minimumWidth: Layout.maximumWidth
Layout.maximumWidth: theStack.pane == theStack.emptyPane ? parent.width : Theme.sidePaneMinSize
@ -139,6 +139,7 @@ ApplicationWindow {
ContactList{
anchors.fill: parent
dualPane: theStack.pane != theStack.emptyPane || theStack.pane == undefined
}
}

View File

@ -5,7 +5,7 @@ TextFieldStyle {
id: root
textColor: "black"
font.pointSize: 10 * gcd.themeScale
property int width: 100
property int width: parent.width
background: Rectangle {
radius: 2

View File

@ -7,6 +7,8 @@ import QtQuick.Layouts 1.3
ColumnLayout {
id: root
property alias dualPane: myprof.dualPane
MouseArea {
anchors.fill: parent
@ -16,7 +18,7 @@ ColumnLayout {
}
}
MyProfile{ // CURRENT PROFILE INFO AND CONTROL BAR
MyProfile { // CURRENT PROFILE INFO AND CONTROL BAR
id: myprof
}

View File

@ -0,0 +1,56 @@
import QtQuick 2.7
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.0
import QtQuick.Layouts 1.3
import CustomQmlTypes 1.0
import "../widgets" as Widgets
import "../theme"
Item {
//anchors.right: parent.right
anchors.left: parent.left
property string text
property alias color: label.color
property alias pixelSize: label.font.pixelSize
property alias weight: label.font.weight
property alias strikeout: label.font.strikeout
height: textMetric.height
width: textMetric.width + 10
anchors.leftMargin: 10
Label {
id: label
textFormat: Text.PlainText
elide: Text.ElideRight
text: textMetric.text
}
TextMetrics {
id: textMetric
text: text
font: label.font
}
/*onWidthChanged: {
setTextResize()
}*/
onTextChanged: {
setTextResize()
}
function setTextResize() {
textMetric.text = text
var i = 2
// - 30 for padding
while (textMetric.width > parent.width - (30 * gcd.themeScale) && parent.width > 50) {
textMetric.text = text.slice(0, text.length - (i * 3)) + "..."
i++
}
}
}

View File

@ -45,7 +45,8 @@ Item {
Portrait {
id: imgProfile
anchors.left: parent.left
handle: root.from
// TODO: currently unused?
//handle: root.from
visible: !fromMe
//showStatus: false
//highlight: ima.containsMouse

View File

@ -9,224 +9,149 @@ import QtQuick.Controls 1.4
import "." as Widgets
import "../styles"
import "../theme"
ColumnLayout {
Item {
id: root
anchors.fill: parent
width: parent.width
height: profile.height + searchAddText.height + 10
implicitHeight: profile.height + searchAddText.height + 10
property string image
property string nick
property string onion
property string tag
property bool dualPane: false
property real logscale: 4 * Math.log10(gcd.themeScale + 1)
onDualPaneChanged: { realignProfile() }
function realignProfile() {
if (dualPane) {
profile.height = 78 * logscale
portrait.baseWidth = 78 * logscale
portrait.height = 78 * logscale
portrait.anchors.horizontalCenter = undefined
portrait.anchors.left = profile.left
portrait.anchors.leftMargin = 25 * logscale
nameRect.anchors.horizontalCenter = undefined
nameRect.anchors.left = portrait.right
nameRect.anchors.top = undefined
nameRect.anchors.verticalCenter = portrait.verticalCenter
} else {
profile.height = (150 * logscale)
portrait.baseWidth = 100 * logscale
portrait.height = 100 * logscale
portrait.anchors.left = undefined
portrait.anchors.leftMargin = undefined
portrait.anchors.horizontalCenter = profile.horizontalCenter
nameRect.anchors.left = undefined
nameRect.anchors.horizontalCenter = profile.horizontalCenter
nameRect.anchors.verticalCenter = undefined
nameRect.anchors.top = portrait.bottom
}
}
Component.onCompleted: { realignProfile() }
Widgets.Button {// BACK BUTTON
id: btnBack
icon: "solid/arrow-circle-left"
anchors.left: parent.left
//anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 2
anchors.top: parent.top
anchors.topMargin: 2
onClicked: function() {
gcd.selectedProfile = "none"
gcd.reloadProfileList()
parentStack.pane = parentStack.managementPane
theStack.pane = theStack.emptyPane
}
}
Item{ height: 6 }
Rectangle {
Item { // PROFILE IMAGE
id: imgProfile
implicitWidth: 96
implicitHeight: 96
anchors.horizontalCenter: parent.horizontalCenter
anchors.left: parent.left
anchors.right: parent.right
width: parent.width
id: profile
Rectangle { // WHITE CIRCLE BORDER
width: 96
height: 96
color: "#FFFFFF"
radius: width / 2
Portrait {
id: portrait
Image { // ACTUAL IMAGE
id: imgProfileImg
source: gcd.assetPath + root.image
anchors.fill: parent
fillMode: Image.PreserveAspectFit
visible: false
}
source: root.image
Image { // INNER CIRCLE MASK
id: mask
fillMode: Image.PreserveAspectFit
visible: false
source: "qrc:/qml/images/extra/clipcircle.png"
}
badgeColor: Theme.portraitProfileBadgeColor
portraitBorderColor: Theme.portraitOnlineBorderColor
portraitColor: Theme.portraitOnlineBackgroundColor
OpacityMask { // WE PUT IT ALL TOGETHER ANNND
anchors.fill: imgProfileImg
source: imgProfileImg
maskSource: mask
}
badgeContent: Image {// Profle Type
id: profiletype
source: tag == "v1-userPassword" ? gcd.assetPath + "/fontawesome/solid/lock.svg" : gcd.assetPath + "/fontawesome/solid/lock-open.svg"
height: Theme.badgeTextSize * gcd.themeScale
width: height
}
}
Rectangle {
id: nameRect
height: name.height
width: name.width + addBtn.width
EllipsisLabel {
id: name
anchors.right: undefined
anchors.left: undefined
color: Theme.portraitOnlineTextColor
pixelSize: Theme.usernameSize * gcd.themeScale
weight: Font.Bold
text: nick
}
Widgets.Button { // Add Button
id: addBtn
anchors.left: name.right //name.left + name.textWidth
anchors.top: name.top
icon: "solid/plus"
height: name.height
width: height
radius: width * 0.3
onClicked: {
}
}
}
}
// TODO Remove for new global topbar
Widgets.Button {// BACK BUTTON
id: btnBack
icon: "solid/arrow-circle-left"
anchors.left: parent.left
anchors.leftMargin: 2
anchors.top: parent.top
anchors.topMargin: 2
onClicked: function() {
gcd.selectedProfile = "none"
gcd.reloadProfileList()
parentStack.pane = parentStack.managementPane
theStack.pane = theStack.emptyPane
}
}
Rectangle { // TOR STATUS INDICATOR
color: "#FFFFFF"
width: 12
height: 12
radius: 3
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 8
Rectangle { //0: no tor, 1: progress 0%, 2: progress 1-99%, 3: DONE
id: rectTorStatus
color: code == 3 ? "green": code == 2 ? "yellow" : code == 1 ? "orange" : "red"
width: 8
height: 8
radius: 2
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 2
property int code
property string message
property bool hovered
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: rectTorStatus.hovered = true
onExited: rectTorStatus.hovered = false
}
ToolTip.visible: hovered
ToolTip.delay: 400
ToolTip.timeout: 5000
ToolTip.text: message
}
}
}
}
InplaceEditText { // USER NICKNAME
id: lblNick
anchors.horizontalCenter: parent.horizontalCenter
Layout.minimumWidth: parent.width
Layout.alignment: Qt.AlignHCenter
onUpdated: {
gcd.updateNick(onion, lblNick.text)
}
}
/*
Text {
id: txtNick
fontSizeMode: Text.HorizontalFit
minimumPixelSize: 10
font.pixelSize: 20
width: 195
anchors.horizontalCenter: parent.horizontalCenter
text: cbNick.editText
MouseArea {
anchors.fill: parent
onClicked: {
parent.visible = false
cbNick.visible = true
}
}
}
ComboBox { // USER NICKNAME
id: cbNick
anchors.horizontalCenter: parent.horizontalCenter
popup.font.pixelSize: 12
width: 200
font.pixelSize: 20
model: ["erinn", "erinn (open privacy)", "supergirl", "add new profile..."]
visible: false
onCurrentTextChanged: {
visible = false
txtNick.visible = true
}
}
*/
Text { // ONION ADDRESS
id: lblOnion
fontSizeMode: Text.HorizontalFit
minimumPointSize: 2
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
text: onion
}
Row { // TOOLS FOR EDITING PROFILE
anchors.horizontalCenter: parent.horizontalCenter
spacing: gcd.themeScale * 2
TextEdit { // USED TO POWER THE COPY/PASTE BUTTON
id: txtHidden
visible: false
}
Widgets.Button { // COPY ONION ADDRESS BUTTON
icon: "regular/clipboard"
//: Button for copying profile onion address to clipboard
text: qsTr("copy-btn")
onClicked: {
//: Copied to clipboard
gcd.popup(qsTr("copied-clipboard-notification"))
txtHidden.text = nick.replace(" ", "~") + "~" + onion
txtHidden.selectAll()
txtHidden.copy()
}
}
Widgets.Button { // SETTINGS BUTTON
icon: "solid/cog"
onClicked: theStack.pane = theStack.settingsPane
}
Widgets.Button { // SIGN OUT BUTTON
icon: "solid/sign-out-alt"
onClicked: {
gcd.popup("not yet implemented, sorry :(")
}
}
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: gcd.themeScale * 2
Widgets.Button { // CREATE GROUP BUTTON
icon: "regular/clipboard"
//: create new group button
text: qsTr("new-group-btn")
onClicked: theStack.pane = theStack.addGroupPane
}
}
TextField {
TextField {
id: searchAddText
anchors.top: profile.bottom
anchors.horizontalCenter: parent.horizontalCenter
style: CwtchTextFieldStyle{ width: 400 }
style: CwtchTextFieldStyle{ }
width: parent.width - 30
//: ex: "... paste an address here to add a contact ..."
placeholderText: qsTr("paste-address-to-add-contact")
horizontalAlignment: TextInput.AlignHCenter
@ -242,16 +167,16 @@ ColumnLayout {
Connections {
target: gcd
onUpdateMyProfile: function(_nick, _onion, _image) {
onUpdateMyProfile: function(_nick, _onion, _image, _tag) {
nick = _nick
lblNick.text = _nick
onion = _onion
image = _image
tag = _tag
}
onTorStatus: function(code, str) {
/*onTorStatus: function(code, str) {
rectTorStatus.code = code
rectTorStatus.message = str
}
}*/
}
}

View File

@ -11,20 +11,23 @@ Item {
implicitWidth: baseWidth
implicitHeight: baseWidth
property string handle
property string source
property alias badgeColor: badge.color
property real logscale: 4 * Math.log10(gcd.themeScale + 1)
property int baseWidth: parent.height
property int baseWidth: 78 * logscale
height: 78 * logscale
property alias portraitBorderColor: mainImage.color
property alias portraitColor: imageInner.color
property alias badgeVisible: badge.visible
property alias badgeContent: badge.content
Rectangle {
id: mainImage
//anchors.leftMargin: baseWidth * 0.1
anchors.horizontalCenter: parent.horizontalCenter
width: baseWidth * 0.8
height: width
anchors.verticalCenter: parent.verticalCenter

View File

@ -18,7 +18,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
implicitHeight: 78 * logscale + 3 //height
property real logscale: 4 * Math.log10(gcd.themeScale + 1)
property string displayName //: nameTxtmetric.text
property string displayName
property alias image: portrait.source
property string handle
property bool isActive
@ -30,9 +30,12 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
property alias portraitColor: portrait.portraitColor
property alias nameColor: cn.color
property alias onionColor: onion.color
property alias onionVisible: onion.visible
property alias badgeVisible: portrait.badgeVisible
property alias badgeContent: portrait.badgeContent
property alias hoverEnabled: buttonMA.hoverEnabled
property alias content: extraMeta.children
// TODO: should be in ContactRow
property bool blocked
@ -55,64 +58,42 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
}
ColumnLayout {
id: portraitMeta
anchors.left: portrait.right
anchors.right: parent.right
anchors.leftMargin: 4 * logscale
anchors.verticalCenter: parent.verticalCenter
Label { // CONTACT NAME
spacing: 2 * gcd.themeScale
EllipsisLabel { // CONTACT NAME
id: cn
leftPadding: 10
rightPadding: 10
//wrapMode: Text.WordWrap
font.pixelSize: Theme.usernameSize * gcd.themeScale
font.weight: Font.Bold
font.strikeout: blocked
elide: Text.ElideRight
text: nameTxtmetric.text
}
TextMetrics {
id: nameTxtmetric
pixelSize: Theme.usernameSize * gcd.themeScale
weight: Font.Bold
strikeout: blocked
text: displayName
font: cn.font
}
Label { // Onion
EllipsisLabel { // Onion
id: onion
text: onionTxtmetric.text
leftPadding: 10
rightPadding: 10
font.pixelSize: Theme.secondaryTextSize * gcd.themeScale
font.strikeout: blocked
textFormat: Text.PlainText
elide: Text.ElideRight
text: handle
pixelSize: Theme.secondaryTextSize * gcd.themeScale
strikeout: blocked
}
TextMetrics {
id: onionTxtmetric
text: handle
font: onion.font
}
onWidthChanged: {
cn.setTextResize()
onion.setTextResize()
}
}
onWidthChanged: {
nameTxtmetric.text = displayName
onionTxtmetric.text = handle
var i = 2
var maxWidth = Math.max(200, width - portrait.width - (50 * logscale))
while (nameTxtmetric.width > maxWidth) {
nameTxtmetric.text = displayName.slice(0, displayName.length - (i * 3)) + "..."
i++
}
i = 2
while (onionTxtmetric.width > maxWidth) {
onionTxtmetric.text = handle.slice(0, handle.length - (i * 3)) + "..."
i++
}
Column {
id: extraMeta
anchors.left: portraitMeta.right
anchors.verticalCenter: parent.verticalCenter
}
}
@ -147,7 +128,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
onUpdateContactDisplayName: function(_handle, _displayName) {
if (handle == _handle) {
displayName = _displayName + (_blocked == true ? " (blocked)" : "")
displayName = _displayName + (blocked == true ? " (blocked)" : "")
}
}