mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2024-12-26 14:06:53 +00:00
diamondburned (Forefront)
f10aa71003
This commit refactors the input container's UI as well as fixing some bugs related to asynchronous fetching of images. It also adds complete typing indicator capabilities, all without using a single mutex!
148 lines
3.6 KiB
Go
148 lines
3.6 KiB
Go
package username
|
|
|
|
import (
|
|
"github.com/diamondburned/cchat"
|
|
"github.com/diamondburned/cchat-gtk/internal/gts"
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/config"
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
|
"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)
|
|
)
|
|
|
|
var usernameCSS = primitives.PrepareCSS(`
|
|
.username-view {
|
|
margin: 8px 10px;
|
|
}
|
|
`)
|
|
|
|
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()
|
|
|
|
primitives.AddClass(box, "username-view")
|
|
primitives.AttachCSS(box, usernameCSS)
|
|
|
|
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()
|
|
}
|