Redesign my profile widget to match design. It dynamically resizes and
the build was successful Details

shapes depending if the side pane is open. Fixes for ellipse label and
portrait size, positioning, margins
This commit is contained in:
Dan Ballard 2020-04-08 15:30:10 -07:00
parent 2f251aa4da
commit 5343348aa9
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)" : "")
}
}