cchat-gtk/internal/ui/messages/sadface/sadface.go

148 lines
3.1 KiB
Go

// Package sadface provides different views for the message container.
package sadface
import (
"strings"
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
"github.com/gotk3/gotk3/gtk"
)
const FaceSize = 56
type FaceView struct {
gtk.Stack
placeholder gtk.IWidget
face *Container
loading *Spinner
parent gtk.IWidget
empty gtk.IWidget
}
func New(parent gtk.IWidget, placeholder gtk.IWidget) *FaceView {
c := NewContainer()
c.Show()
s := NewSpinner()
s.Show()
// make an empty box for an empty page.
b, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
stack, _ := gtk.StackNew()
stack.SetTransitionDuration(55)
stack.SetTransitionType(gtk.STACK_TRANSITION_TYPE_CROSSFADE)
stack.Add(parent)
stack.Add(c)
stack.Add(s)
stack.Add(b)
// Show placeholder by default.
stack.AddNamed(placeholder, "placeholder")
stack.SetVisibleChild(placeholder)
return &FaceView{
Stack: *stack,
placeholder: placeholder,
face: c,
loading: s,
parent: parent,
empty: b,
}
}
// Reset brings the view to an empty box.
func (v *FaceView) Reset() {
v.loading.Spinner.Stop()
v.Stack.SetVisibleChild(v.empty)
v.ensurePlaceholderDestroyed()
}
func (v *FaceView) SetMain() {
v.loading.Spinner.Stop()
v.Stack.SetVisibleChild(v.parent)
v.ensurePlaceholderDestroyed()
}
func (v *FaceView) SetLoading() {
v.loading.Spinner.Start()
v.Stack.SetVisibleChild(v.loading)
v.ensurePlaceholderDestroyed()
}
func (v *FaceView) SetError(err error) {
v.face.SetError(err)
v.Stack.SetVisibleChild(v.face)
v.ensurePlaceholderDestroyed()
v.loading.Spinner.Stop()
}
func (v *FaceView) ensurePlaceholderDestroyed() {
// If the placeholder is still there:
if v.placeholder != nil {
// Safely remove the placeholder from the stack.
if v.Stack.GetVisibleChildName() == "placeholder" {
v.Stack.SetVisibleChild(v.empty)
}
// Remove the placeholder widget.
v.Stack.Remove(v.placeholder)
v.placeholder = nil
}
}
type Spinner struct {
gtk.Box
Spinner *gtk.Spinner
}
func NewSpinner() *Spinner {
s, _ := gtk.SpinnerNew()
s.SetSizeRequest(FaceSize, FaceSize)
s.Show()
b, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
b.Add(s)
b.SetHAlign(gtk.ALIGN_CENTER)
b.SetVAlign(gtk.ALIGN_CENTER)
return &Spinner{*b, s}
}
type Container struct {
gtk.Box
Face *gtk.Image
Error *gtk.Label
}
func NewContainer() *Container {
face, _ := gtk.ImageNew()
face.SetSizeRequest(FaceSize, FaceSize)
face.Show()
primitives.SetImageIcon(face, "face-sad-symbolic", FaceSize)
errlabel, _ := gtk.LabelNew("")
errlabel.SetOpacity(0.75) // low contrast good because unreadable
errlabel.Show()
box, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 15)
box.SetVAlign(gtk.ALIGN_CENTER)
box.SetHAlign(gtk.ALIGN_CENTER)
box.PackStart(face, false, false, 0)
box.PackStart(errlabel, false, false, 0)
return &Container{*box, face, errlabel}
}
// SetError sets the view to display the error. Error must not be nil.
func (v *Container) SetError(err error) {
// Split the error.
parts := strings.Split(err.Error(), ": ")
v.Error.SetLabel("Error: " + parts[len(parts)-1])
// Use the full error for the tooltip.
v.Box.SetTooltipText(err.Error())
}