2020-06-13 07:29:32 +00:00
|
|
|
// 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
|
2020-08-29 01:42:28 +00:00
|
|
|
placeholder gtk.IWidget
|
2020-06-13 07:29:32 +00:00
|
|
|
|
|
|
|
Face *Container
|
|
|
|
Loading *Spinner
|
|
|
|
}
|
|
|
|
|
2020-08-29 01:42:28 +00:00
|
|
|
func New(parent gtk.IWidget, placeholder gtk.IWidget) *FaceView {
|
2020-06-13 07:29:32 +00:00
|
|
|
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.SetTransitionType(gtk.STACK_TRANSITION_TYPE_CROSSFADE)
|
|
|
|
stack.AddNamed(parent, "main")
|
|
|
|
stack.AddNamed(placeholder, "placeholder")
|
|
|
|
stack.AddNamed(c, "face")
|
|
|
|
stack.AddNamed(s, "loading")
|
|
|
|
stack.AddNamed(b, "empty")
|
|
|
|
|
|
|
|
// Show placeholder by default.
|
|
|
|
stack.SetVisibleChildName("placeholder")
|
|
|
|
|
|
|
|
return &FaceView{*stack, placeholder, c, s}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset brings the view to an empty box.
|
|
|
|
func (v *FaceView) Reset() {
|
|
|
|
v.Loading.Spinner.Stop()
|
2020-07-14 07:24:55 +00:00
|
|
|
v.Stack.SetVisibleChildName("empty")
|
2020-11-06 04:01:00 +00:00
|
|
|
v.ensurePlaceholderDestroyed()
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (v *FaceView) SetMain() {
|
|
|
|
v.Loading.Spinner.Stop()
|
2020-07-14 07:24:55 +00:00
|
|
|
v.Stack.SetVisibleChildName("main")
|
2020-11-06 04:01:00 +00:00
|
|
|
v.ensurePlaceholderDestroyed()
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (v *FaceView) SetLoading() {
|
|
|
|
v.Loading.Spinner.Start()
|
|
|
|
v.Stack.SetVisibleChildName("loading")
|
2020-11-06 04:01:00 +00:00
|
|
|
v.ensurePlaceholderDestroyed()
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (v *FaceView) SetError(err error) {
|
2020-06-14 18:19:06 +00:00
|
|
|
v.Face.SetError(err)
|
|
|
|
v.Stack.SetVisibleChildName("face")
|
2020-06-13 07:29:32 +00:00
|
|
|
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" {
|
2020-07-14 07:24:55 +00:00
|
|
|
v.Stack.SetVisibleChildName("empty")
|
2020-06-13 07:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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())
|
|
|
|
}
|