2020-07-01 19:56:32 +00:00
|
|
|
package username
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/diamondburned/cchat"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/gts"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/config"
|
2020-07-04 04:41:12 +00:00
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
2020-07-01 19:56:32 +00:00
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/rich"
|
|
|
|
"github.com/diamondburned/cchat/text"
|
|
|
|
"github.com/diamondburned/imgutil"
|
|
|
|
"github.com/gotk3/gotk3/gtk"
|
|
|
|
)
|
|
|
|
|
|
|
|
const AvatarSize = 24
|
|
|
|
|
|
|
|
var showUser = true
|
|
|
|
var currentRevealer = func(bool) {} // noop by default
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
// Bind this revealer in settings.
|
|
|
|
config.AppearanceAdd("Show Username in Input", config.Switch(
|
|
|
|
&showUser,
|
|
|
|
func(b bool) { currentRevealer(b) },
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
type Container struct {
|
|
|
|
*gtk.Revealer
|
|
|
|
main *gtk.Box
|
|
|
|
avatar *rich.Icon
|
|
|
|
label *rich.Label
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
_ cchat.LabelContainer = (*Container)(nil)
|
|
|
|
_ cchat.IconContainer = (*Container)(nil)
|
|
|
|
)
|
|
|
|
|
2020-07-04 04:41:12 +00:00
|
|
|
var usernameCSS = primitives.PrepareCSS(`
|
|
|
|
.username-view {
|
|
|
|
margin: 8px 10px;
|
|
|
|
}
|
|
|
|
`)
|
|
|
|
|
2020-07-01 19:56:32 +00:00
|
|
|
func NewContainer() *Container {
|
|
|
|
avatar := rich.NewIcon(AvatarSize, imgutil.Round(true))
|
|
|
|
avatar.SetPlaceholderIcon("user-available-symbolic", AvatarSize)
|
|
|
|
avatar.Show()
|
|
|
|
|
|
|
|
label := rich.NewLabel(text.Rich{})
|
|
|
|
label.SetMaxWidthChars(35)
|
|
|
|
label.Show()
|
|
|
|
|
|
|
|
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 5)
|
|
|
|
box.PackStart(avatar, false, false, 0)
|
|
|
|
box.PackStart(label, false, false, 0)
|
|
|
|
box.SetVAlign(gtk.ALIGN_START)
|
|
|
|
box.Show()
|
|
|
|
|
2020-07-04 04:41:12 +00:00
|
|
|
primitives.AddClass(box, "username-view")
|
|
|
|
primitives.AttachCSS(box, usernameCSS)
|
|
|
|
|
2020-07-01 19:56:32 +00:00
|
|
|
rev, _ := gtk.RevealerNew()
|
|
|
|
rev.SetRevealChild(false)
|
|
|
|
rev.SetTransitionType(gtk.REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
|
|
|
|
rev.SetTransitionDuration(50)
|
|
|
|
rev.Add(box)
|
|
|
|
|
|
|
|
// Bind the current global revealer to this revealer for settings. This
|
|
|
|
// operation should be thread-safe, as everything is being done in the main
|
|
|
|
// thread.
|
|
|
|
currentRevealer = rev.SetRevealChild
|
|
|
|
|
|
|
|
return &Container{
|
|
|
|
Revealer: rev,
|
|
|
|
main: box,
|
|
|
|
avatar: avatar,
|
|
|
|
label: label,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *Container) SetRevealChild(reveal bool) {
|
|
|
|
// Only reveal if showUser is true.
|
|
|
|
u.Revealer.SetRevealChild(reveal && showUser)
|
|
|
|
}
|
|
|
|
|
|
|
|
// shouldReveal returns whether or not the container should reveal.
|
|
|
|
func (u *Container) shouldReveal() bool {
|
|
|
|
return !u.label.GetLabel().Empty() && showUser
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *Container) Reset() {
|
|
|
|
u.SetRevealChild(false)
|
|
|
|
u.avatar.Reset()
|
|
|
|
u.label.Reset()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update is not thread-safe.
|
|
|
|
func (u *Container) Update(session cchat.Session, sender cchat.ServerMessageSender) {
|
|
|
|
// Set the fallback username.
|
|
|
|
u.label.SetLabelUnsafe(session.Name())
|
|
|
|
// Reveal the name if it's not empty.
|
|
|
|
u.SetRevealChild(u.shouldReveal())
|
|
|
|
|
|
|
|
// Does sender (aka Server) implement ServerNickname? If yes, use it.
|
|
|
|
if nicknamer, ok := sender.(cchat.ServerNickname); ok {
|
|
|
|
u.label.AsyncSetLabel(nicknamer.Nickname, "Error fetching server nickname")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Does session implement an icon? Update if yes.
|
|
|
|
if iconer, ok := session.(cchat.Icon); ok {
|
|
|
|
u.avatar.AsyncSetIconer(iconer, "Error fetching session icon URL")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetLabel is not thread-safe.
|
|
|
|
func (u *Container) GetLabel() text.Rich {
|
|
|
|
return u.label.GetLabel()
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetLabel is thread-safe.
|
|
|
|
func (u *Container) SetLabel(content text.Rich) {
|
|
|
|
gts.ExecAsync(func() {
|
|
|
|
u.label.SetLabelUnsafe(content)
|
|
|
|
|
|
|
|
// Reveal if the name is not empty.
|
|
|
|
u.SetRevealChild(u.shouldReveal())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetIcon is thread-safe.
|
|
|
|
func (u *Container) SetIcon(url string) {
|
|
|
|
gts.ExecAsync(func() {
|
|
|
|
u.avatar.SetIconUnsafe(url)
|
|
|
|
|
|
|
|
// Reveal if the icon URL is not empty. We don't touch anything if the
|
|
|
|
// URL is empty, as the name might not be.
|
|
|
|
if url != "" {
|
|
|
|
u.SetRevealChild(true)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetIconURL is not thread-safe.
|
|
|
|
func (u *Container) GetIconURL() string {
|
|
|
|
return u.avatar.URL()
|
|
|
|
}
|