erinn
/
ui
forked from cwtch.im/ui
1
0
Fork 0

initial commit

This commit is contained in:
erinn 2018-10-29 11:00:21 -07:00
parent 65c50a50b0
commit 1011d4a4f9
15 changed files with 331 additions and 73 deletions

View File

@ -32,7 +32,11 @@ func (this *ChatChannelListener) OpenInbound() {
}
func (this *ChatChannelListener) ChatMessage(messageID uint32, when time.Time, message string) bool {
DeliverMessageToUI(this.rai.RemoteHostname, message, uint(messageID), false, when)
DeliverMessageToUI(this.rai.RemoteHostname, this.rai.RemoteHostname, "", message, uint(messageID), false, when)
go func() {
time.Sleep(time.Second)
peer.Save()
}()
return true
}

24
gcd.go
View File

@ -18,7 +18,7 @@ type GrandCentralDispatcher struct {
currentOpenConversation string
// messages pane stuff
_ func(from, message string, mID uint, ts string) `signal:"AppendMessage"`
_ func(from, message, displayname string, mID uint, ts, source string) `signal:"AppendMessage"`
_ func() `signal:"ClearMessages"`
_ func() `signal:"ResetMessagePane"`
_ func(uint) `signal:"Acknowledged"`
@ -78,7 +78,7 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID uint) {
default:
}
DeliverMessageToUI(gcd.currentOpenConversation, message, mID, true, time.Now())
DeliverMessageToUI(gcd.currentOpenConversation, gcd.currentOpenConversation, "", message, mID, true, time.Now())
}
func (this *GrandCentralDispatcher) loadMessagesPane(onion string) {
@ -86,15 +86,27 @@ func (this *GrandCentralDispatcher) loadMessagesPane(onion string) {
gcd.currentOpenConversation = onion
gcd.SetUnread(onion, 0)
if len(onion) == 32 { // eg 48e7dcfc353e6d77da2c31d63654fd19
if len(onion) == 32 { // LOAD GROUP
log.Printf("LOADING GROUP %s", onion)
tl := peer.GetGroup(onion).GetTimeline()
log.Printf("messages: %d", len(tl))
for i := range tl {
gcd.AppendMessage(tl[i].PeerID, tl[i].Message, 0, tl[i].Timestamp.Format(TIME_FORMAT))
var handle string
if tl[i].PeerID == peer.GetProfile().Onion {
handle = "me"
} else {
handle = tl[i].PeerID
}
var name string
var exists bool
name, exists = peer.GetProfile().GetCustomAttribute(tl[i].PeerID + "_name")
if !exists || name == "" {
name = tl[i].PeerID[:16] + "..."
}
gcd.AppendMessage(handle, tl[i].Message, name, 0, tl[i].Timestamp.Format(TIME_FORMAT), randomProfileImage(tl[i].PeerID))
}
return
}
} // ELSE LOAD CONTACT
_, exists := contactMgr[onion]
if exists { // (if not, they haven't been accepted as a contact yet)
@ -106,7 +118,7 @@ func (this *GrandCentralDispatcher) loadMessagesPane(onion string) {
if messages[i].FromMe {
from = "me"
}
gcd.AppendMessage(from, messages[i].Message, messages[i].MessageID, messages[i].Timestamp.Format(TIME_FORMAT))
gcd.AppendMessage(from, messages[i].Message, "", messages[i].MessageID, messages[i].Timestamp.Format(TIME_FORMAT), randomProfileImage(onion))
}
}
}

57
main.go
View File

@ -52,21 +52,21 @@ type Message struct {
Timestamp time.Time
}
func DeliverMessageToUI(from, message string, mID uint, fromMe bool, ts time.Time) {
_, found := contactMgr[from]
func DeliverMessageToUI(handle, from, displayname, message string, mID uint, fromMe bool, ts time.Time) {
_, found := contactMgr[handle]
if !found {
contactMgr[from] = &Contact{[]Message{}, 0, 0}
contactMgr[handle] = &Contact{[]Message{}, 0, 0}
}
contactMgr[from].AddMessage(Message{from, message, fromMe, mID, ts})
if gcd.currentOpenConversation == from {
contactMgr[handle].AddMessage(Message{from, message, fromMe, mID, ts})
if gcd.currentOpenConversation == handle {
if fromMe {
from = "me"
}
gcd.AppendMessage(from, message, mID, ts.Format(TIME_FORMAT))
gcd.AppendMessage(from, message, displayname, mID, ts.Format(TIME_FORMAT), randomProfileImage(from))
} else {
contactMgr[from].Unread++
gcd.SetUnread(from, contactMgr[from].Unread)
contactMgr[handle].Unread++
gcd.SetUnread(handle, contactMgr[handle].Unread)
}
}
@ -83,7 +83,9 @@ func main() {
quickcontrols2.QQuickStyle_SetStyle("Universe")
view := quick.NewQQuickView(nil)
view.SetResizeMode(quick.QQuickView__SizeRootObjectToView)
view.SetTitle("bounce")
view.SetMinimumHeight(280)
view.SetMinimumWidth(300)
view.SetTitle("cwtch")
gcd = NewGrandCentralDispatcher(nil)
view.RootContext().SetContextProperty("gcd", gcd)
@ -113,11 +115,12 @@ func groupPoller() {
groups := peer.GetGroups()
for i := range groups {
group := peer.GetGroup(groups[i])
log.Printf("setting group %s to status %d", groups[i], int(servers[group.GroupServer]))
//log.Printf("setting group %s to status %d", groups[i], int(servers[group.GroupServer]))
gcd.SetConnectionStatus(groups[i], int(servers[group.GroupServer]))
}
}
}
func presencePoller() { // TODO: make this subscribe-able in ricochet
time.Sleep(time.Second * 4)
for {
@ -181,8 +184,22 @@ func torStatusPoller() {
func cwtchListener(groupID string, channel chan model.Message) {
for {
m := <-channel
log.Printf("GROUPMSG %s", m.Message)
DeliverMessageToUI(groupID, m.Message, 0, m.PeerID == peer.GetProfile().Onion, m.Timestamp)
log.Printf("GROUPMSG %s %s", m.Message, m.PeerID)
name := m.PeerID
if name == peer.GetProfile().Onion {
name = "me"
} else {
var exists bool // lol this is a golang antifeature
name, exists = peer.GetProfile().GetCustomAttribute(m.PeerID + "_name")
if !exists {
name = ""
}
}
if name == "" {
name = m.PeerID[:16] + "..."
}
DeliverMessageToUI(groupID, m.PeerID, name, m.Message, 0, m.PeerID == peer.GetProfile().Onion, m.Timestamp)
peer.Save()
}
}
@ -225,19 +242,20 @@ func andHisBlackAndWhiteCat(incomingMessages chan Message) {
}
func initialize(view *quick.QQuickView) {
log.Printf(os.Args[0])
//TODO: this section is ported over and has a lot of printf errors, need to show them in the ui
var dirname, filename string
if os.Getenv("SENDAFRIEND_FOLDER") != "" {
dirname = os.Getenv("SENDAFRIEND_FOLDER")
filename = path.Join(dirname, "identity.private")
if os.Getenv("CWTCH_FOLDER") != "" {
dirname = os.Getenv("CWTCH_FOLDER")
filename = path.Join(dirname, "keep-this-file-private")
} else {
usr, err := user.Current()
if err != nil {
fmt.Printf("\nerror: could not load current user: %v\n", err)
os.Exit(1)
}
dirname = path.Join(usr.HomeDir, ".sendafriend")
filename = path.Join(dirname, "identity.private")
dirname = path.Join(usr.HomeDir, ".cwtch")
filename = path.Join(dirname, "keep-this-file-private")
}
os.MkdirAll(dirname, 0700)
@ -293,6 +311,11 @@ func initialize(view *quick.QQuickView) {
// temporary until we do real picture selection
func randomProfileImage(onion string) string {
//TODO: this is a hack, fix ever passing this in
if onion == "me" {
onion = peer.GetProfile().Onion
}
choices := []string{"001-centaur", "002-kraken", "003-dinosaur", "004-tree-1", "005-hand", "006-echidna", "007-robot", "008-mushroom", "009-harpy", "010-phoenix", "011-dragon-1", "012-devil", "013-troll", "014-alien", "015-minotaur", "016-madre-monte", "017-satyr", "018-karakasakozou", "019-pirate", "020-werewolf", "021-scarecrow", "022-valkyrie", "023-curupira", "024-loch-ness-monster", "025-tree", "026-cerberus", "027-gryphon", "028-mermaid", "029-vampire", "030-goblin", "031-yeti", "032-leprechaun", "033-medusa", "034-chimera", "035-elf", "036-hydra", "037-cyclops", "038-pegasus", "039-narwhal", "040-woodcutter", "041-zombie", "042-dragon", "043-frankenstein", "044-witch", "045-fairy", "046-genie", "047-pinocchio", "048-ghost", "049-wizard", "050-unicorn"}
barr, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
if err != nil || len(barr) != 35 {

View File

@ -8,17 +8,18 @@ import "fonts/Twemoji.js" as T
import "widgets"
Item {
id: windowItem
width: 525
height: 500
id: root
readonly property real ratio: height / width
FontAwesome {
FontAwesome { // PRETTY BUTTON ICONS
id: awesome
resource: "qrc:/qml/fonts/fontawesome.ttf"
}
function parse(text, size) {
function parse(text, size) { // REPLACE EMOJI WITH <IMG> TAGS
T.twemoji.base = "qrc:/qml/fonts/twemoji/"
T.twemoji.ext = ".png"
T.twemoji.size = "72x72"
@ -26,7 +27,7 @@ Item {
return T.twemoji.parse(text)
}
function restoreEmoji(text) {
function restoreEmoji(text) { // REPLACE <IMG> TAGS WITH EMOJI
var re = /<img src="qrc:\/qml\/fonts\/twemoji\/72x72\/([^"]*?)\.png" width="10" height="10" \/>/g
var arr
var newtext = text
@ -42,7 +43,57 @@ Item {
}
RowLayout {
/* Rectangle { // THE TOOLBAR
id: toolbar
anchors.top: parent.top
anchors.left: parent.left
width: ratio >= 0.92 ? parent.width : 70
height: ratio >= 0.92 ? 70 : parent.height
color: "#4B3557"
GridLayout {
width: parent.width
height: parent.height
columns: ratio >= 0.92 ? children.length : 1
ContactPicture {
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
source: "qrc:/qml/images/profiles/001-centaur.png"
status: -2
}
ContactPicture {
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
source: "qrc:/qml/images/profiles/002-kraken.png"
status: -2
}
ContactPicture {
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
source: "qrc:/qml/images/profiles/003-dinosaur.png"
status: -2
}
ContactPicture {
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
source: "qrc:/qml/images/profiles/004-tree-1.png"
status: -2
}
ContactPicture {
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
source: "qrc:/qml/images/profiles/005-hand.png"
status: -2
}
}
}*/
RowLayout { // CONTAINS EVERYTHING EXCEPT THE TOOLBAR
/* anchors.left: ratio >= 0.92 ? parent.left : toolbar.right
anchors.top: ratio >= 0.92 ? toolbar.bottom : parent.top
anchors.right: parent.right
anchors.bottom: parent.bottom */
anchors.fill: parent
spacing: 0
@ -50,8 +101,9 @@ Item {
Rectangle { // THE LEFT PANE WITH TOOLS AND CONTACTS
color: "#D2C0DD"
Layout.fillHeight: true
Layout.minimumWidth: 200
Layout.maximumWidth: 200
Layout.minimumWidth: Layout.maximumWidth
Layout.maximumWidth: theStack.pane == theStack.emptyPane ? parent.width : 200
visible: (ratio <= 1.08 && windowItem.width >= 500) || theStack.pane == theStack.emptyPane
ContactList{
@ -59,21 +111,79 @@ Item {
}
}
Rectangle { // THE RIGHT PANE WHERE THE MESSAGES GO
Rectangle { // THE RIGHT PANE WHERE THE MESSAGES AND STuFF GO
color: "#EEEEFF"
Layout.fillWidth: true
Layout.fillHeight: true
MessageList{
StackLayout {
id: theStack
anchors.fill: parent
currentIndex: 0
property alias pane: theStack.currentIndex
readonly property int emptyPane: 0
readonly property int messagePane: 1
readonly property int settingsPane: 2
readonly property int userProfilePane: 3
readonly property int groupProfilePane: 4
Item {} // empty
MessageList { // messagePane
anchors.fill: parent
}
ColumnLayout { // settingsPane
anchors.fill: parent
StackToolbar {
text: "Cwtch Settings"
aux.visible: false
}
Label { text: "welcome to the global app settings page!" }
}
ColumnLayout { // userProfilePane
anchors.fill: parent
StackToolbar {
text: "Settings for Sarah"
aux.visible: false
back.onClicked: theStack.pane = theStack.messagePane
}
Label { text: "per-user things like contact name and picture will be edited here" }
}
Label { // groupProfilePane
font.pixelSize: 12
text: "invite new people or change the group name here"
StackToolbar { text: "Group settings" }
}
Label { // addGroupPane
font.pixelSize: 12
text: "add a new group"
StackToolbar { text: "Create group" }
}
}
}
}
PropertyAnimation { id: anmPopup; easing.type: Easing.InQuart; duration: 7000; target: popup; property: "opacity"; to: 0; }
Rectangle {
Rectangle { // THE ERROR MESSAGE POPUP
id: popup
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
@ -96,7 +206,7 @@ Item {
}
Connections {
Connections { // POPUPS ARE INVOKED BY GO FUNCS
target: gcd
onInvokePopup: function(str) {

View File

@ -57,7 +57,7 @@ RowLayout { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
id: rectUnread
anchors.right: parent.right
height: 16
width: childrenRect.width + 10
width: lblUnread.width + 10
radius: 8
color: "#4B3557"
visible: badge != "0"
@ -65,6 +65,7 @@ RowLayout { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
Label {
id: lblUnread
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
color: "#FFFFFF"
@ -82,6 +83,7 @@ RowLayout { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
onClicked: {
gcd.broadcast("ResetMessagePane")
isActive = true
theStack.pane = theStack.messagePane
gcd.loadMessagesPane(onion)
}
@ -108,7 +110,6 @@ RowLayout { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
}
onSetConnectionStatus: function(foronion, x) {
if (foronion.length == 32) console.log(onion+" setting status "+x+" for contact "+foronion)
if (onion == foronion) {
status = x
}

Binary file not shown.

Binary file not shown.

View File

@ -9,7 +9,6 @@ Item {
id: imgProfile
implicitWidth: 48
implicitHeight: 48
anchors.left: parent.left
anchors.margins: 5
property alias source: img.source

View File

@ -8,12 +8,18 @@ import "controls" as Awesome
RowLayout {
id: root
Layout.alignment: from == "me" ? Qt.AlignRight : Qt.AlignLeft
//Layout.alignment: from == "me" ? Qt.AlignRight : Qt.AlignLeft
anchors.left: from == "me" ? undefined : parent.left
anchors.right: from == "me" ? parent.right : undefined
height: Math.max(imgProfile.height, rectMessageBubble.height)
property alias message: lbl.text
property string from
property string displayname
property int messageID
property alias timestamp: ts.text
property alias source: imgProfile.source
property alias status: imgProfile.status
Connections {
@ -27,15 +33,22 @@ RowLayout {
}
ContactPicture {
id: imgProfile
anchors.left: parent.left
visible: from != "me"
}
Rectangle { // THIS IS JUST A PRETTY MESSAGE-HOLDING RECTANGLE
height: childrenRect.height + 3
width: childrenRect.width + 6
id: rectMessageBubble
height: lbl.height + ts.height + 4
width: colMessageBubble.width + 6
color: from == "me" ? "#B09CBC" : "#4B3557"
radius: 5
// the console will complain constantly about me setting these anchors, but qt only allows margins if they've been set to something
// a kludge to fix this would be to have spacers before/after and set the widths according to the side they're on ^ea
anchors.left: from == "me" ? undefined : parent.left
anchors.left: from == "me" ? undefined : imgProfile.right //parent.left
anchors.right: from == "me" ? parent.right : undefined
anchors.leftMargin: 5
anchors.rightMargin: 9
@ -43,6 +56,9 @@ RowLayout {
ColumnLayout {
id: colMessageBubble
Column { // combine these into one element or else childrenRect won't play nicely
TextEdit { // this is used as a helper to calculate the message box width
id: dummy
@ -63,18 +79,19 @@ RowLayout {
font.pixelSize: 12
selectByMouse: true
readOnly: true
width: dummy.width > root.parent.width - 70 ? root.parent.width - 70 : dummy.width
width: Math.min(dummy.width, root.parent.width - (imgProfile.visible ? imgProfile.width : 0) - 40)
wrapMode: TextEdit.Wrap
textFormat: Text.RichText
}
}
RowLayout {
id: rowBottom
anchors.left: parent.left
anchors.right: parent.right
Label {
Label { // TIMESTAMP
id: ts
color: "#FFFFFF"
font.pixelSize: 10
@ -82,13 +99,15 @@ RowLayout {
leftPadding: 10
}
Label {
Label { text: dummy.width+", "+root.width }
Label { // MESSAGE ACKNOWLEDGMENT
id: ack
color: "#FFFFFF"
font.pixelSize: 10
anchors.right: parent.right
font.family: "FontAwesome"
text: from == "me" ? awesome.icons.fa_ellipsis_h : ""
text: from == "me" ? awesome.icons.fa_ellipsis_h : displayname
}
}
}

Binary file not shown.

View File

@ -11,7 +11,13 @@ ColumnLayout {
Layout.fillWidth: true
Flickable {
StackToolbar {
text: "Sarah Jamie Lewis"
aux.onClicked: theStack.pane = theStack.userProfilePane
}
Flickable { // THE MESSAGE LIST ITSELF
id: sv
clip: true
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
@ -32,12 +38,14 @@ ColumnLayout {
messagesModel.clear()
}
onAppendMessage: function(from, message, mid, ts) {
onAppendMessage: function(from, message, displayname, mid, ts, source) {
messagesModel.append({
"f": from,
"m": parse(message, 12),
"d": displayname,
"i": mid,
"t": ts
"t": ts,
"src": source
})
if (sv.contentY + sv.height >= sv.contentHeight - colMessages.height && sv.contentHeight > sv.height) {
@ -67,8 +75,10 @@ ColumnLayout {
delegate: Message {
from: f
message: m
displayname: d
messageID: i
timestamp: t
source: src
}
}
}
@ -98,9 +108,7 @@ ColumnLayout {
maximumFlickVelocity: 300
ScrollBar.vertical: ScrollBar{
policy: ScrollBar.AlwaysOn
}
ScrollBar.vertical: ScrollBar{}
TextEdit {
id: txtMessage
@ -116,7 +124,7 @@ ColumnLayout {
Keys.onReturnPressed: { // CTRL+ENTER = LINEBREAK
if (event.modifiers & Qt.ControlModifier) {
txtMessage.insert(txtMessage.cursorPosition, "\n")
txtMessage.insert(txtMessage.cursorPosition, "<br>")
} else if (event.modifiers == Qt.NoModifier) {
btnSend.clicked()
}
@ -126,7 +134,7 @@ ColumnLayout {
// while also stripping any other tag, including other images.
// TODO: this probably breaks if people actually do want to paste html
onTextChanged: {
console.log("onTextChanged()")
//console.log("onTextChanged()")
// we're taking advantage of TextEdit.getText()'s parsing capability, which means occasionally
// passing text into it to be filtered. this prevents recursive calls putting us into an
@ -137,7 +145,7 @@ ColumnLayout {
return
}
console.log("1: " + txtMessage.getText(0, txtMessage.text.length))
//console.log("1: " + txtMessage.getText(0, txtMessage.text.length))
// convert <img> tags back to their emoji form
var nt = restoreEmoji(txtMessage.text)
@ -146,15 +154,15 @@ ColumnLayout {
txtMessage.text = nt
}
console.log("2: " + txtMessage.getText(0, txtMessage.text.length))
//console.log("2: " + txtMessage.getText(0, txtMessage.text.length))
// strip all HTML tags
var theText = txtMessage.getText(0, txtMessage.text.length)
console.log("3: " + theText)
//console.log("3: " + theText)
// convert emoji back to <img> tags
nt = parse(theText, 10)
console.log("4: " + nt)
//console.log("4: " + nt)
// if there were changes...
if (nt != txtMessage.getText(0, txtMessage.text.length)) {

Binary file not shown.

View File

@ -2,6 +2,7 @@ import QtGraphicalEffects 1.0
import QtQuick 2.7
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.0
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
ColumnLayout {
@ -10,8 +11,8 @@ ColumnLayout {
width: parent.width
property alias image: imgProfileImg.source
property alias nick: lblNick.text
property alias onion: lblOnion.text
property string nick
property string onion
Item{ height: 6 }
@ -93,23 +94,56 @@ ColumnLayout {
}
}
InplaceEditText { // USER NICKNAME
id: lblNick
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
//InplaceEditText { // USER NICKNAME
// id: lblNick
// anchors.horizontalCenter: parent.horizontalCenter
// width: parent.width
//
// onUpdated: {
// gcd.updateNick(lblNick.text)
// }
//}
onUpdated: {
gcd.updateNick(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
}
}
// TODO: this doesnt quite fit everything :{
Label { // ONION ADDRESS
id: lblOnion
font.pixelSize: 6
Layout.fillWidth: true
padding: 3
horizontalAlignment: Text.AlignHCenter
text: "This is your address. You can give it out to people!\n" + onion
}
RowLayout { // TOOLS FOR EDITING PROFILE
@ -121,7 +155,7 @@ ColumnLayout {
visible: false
}
IconButton {
IconButton { // COPY ONION ADDRESS BUTTON
icon: awesome.icons.fa_clipboard
label: "copy"
@ -133,20 +167,19 @@ ColumnLayout {
}
}
IconButton {
IconButton { // SETTINGS BUTTON
icon: awesome.icons.fa_cog
//label: "settings"
onClicked: gcd.popup("not yet implemented, sorry :(")
onClicked: theStack.pane = theStack.settingsPane
}
IconButton {
IconButton { // SIGN OUT BUTTON
icon: awesome.icons.fa_sign_out
//label: "sign out"
onClicked: {
gcd.popup("not yet implemented, sorry :(")
console.log(parse("💜", 20))
}
}
}
@ -199,7 +232,7 @@ ColumnLayout {
onUpdateMyProfile: function(_nick, _onion, _image) {
nick = _nick
onion = "This is your address. You should give it out to people!\n" + _onion
onion = _onion
image = _image
}

Binary file not shown.

View File

@ -0,0 +1,49 @@
import QtGraphicalEffects 1.0
import QtQuick 2.7
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.0
import QtQuick.Layouts 1.3
import "controls" as Awesome
import "../fonts/Twemoji.js" as T
Rectangle { // OVERHEAD BAR ON STACK PANE
id: toolbar
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: 28
color: "#EDEDED"
property alias text: lbl.text
property alias aux: btnAux
property alias back: btnBack
IconButton {// BACK BUTTON
id: btnBack
icon: awesome.icons.fa_arrow_circle_o_left
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 6
visible: ratio >= 1.08 || windowItem.width < 500
onClicked: theStack.pane = theStack.emptyPane
}
Label { // TEXT
id: lbl
font.pixelSize: 16
text: "Sarah Jamie Lewis"
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
IconButton { // COG BUTTON
id: btnAux
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 6
icon: awesome.icons.fa_cog
}
}