1
0
Fork 0
mirror of https://github.com/diamondburned/cchat-gtk.git synced 2024-11-19 12:43:15 +00:00
cchat-gtk/internal/ui/rich/image.go

220 lines
4.6 KiB
Go
Raw Normal View History

2020-06-06 07:44:36 +00:00
package rich
import (
"context"
2020-06-06 07:44:36 +00:00
"github.com/diamondburned/cchat"
"github.com/diamondburned/cchat-gtk/internal/gts"
"github.com/diamondburned/cchat-gtk/internal/gts/httputil"
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
2020-07-11 06:48:44 +00:00
"github.com/diamondburned/cchat-gtk/internal/ui/primitives/roundimage"
2020-06-06 07:44:36 +00:00
"github.com/diamondburned/cchat/text"
2020-08-17 00:13:47 +00:00
"github.com/gotk3/gotk3/gdk"
2020-06-06 07:44:36 +00:00
"github.com/gotk3/gotk3/gtk"
"github.com/pkg/errors"
2020-06-06 07:44:36 +00:00
)
2020-06-30 03:39:42 +00:00
type IconerFn = func(context.Context, cchat.IconContainer) (func(), error)
2020-08-17 00:13:47 +00:00
type RoundIconContainer interface {
gtk.IWidget
2020-08-17 00:13:47 +00:00
primitives.ImageIconSetter
roundimage.RadiusSetter
SetImageURL(url string)
2020-08-17 00:13:47 +00:00
GetStorageType() gtk.ImageType
GetPixbuf() *gdk.Pixbuf
GetAnimation() *gdk.PixbufAnimation
}
var (
_ RoundIconContainer = (*roundimage.Image)(nil)
_ RoundIconContainer = (*roundimage.StaticImage)(nil)
)
2020-07-11 06:48:44 +00:00
// Icon represents a rounded image container.
2020-06-06 07:44:36 +00:00
type Icon struct {
*gtk.Revealer // TODO move out
Image RoundIconContainer
2020-06-07 07:06:13 +00:00
// state
url string
2020-06-06 07:44:36 +00:00
}
const DefaultIconSize = 16
var _ cchat.IconContainer = (*Icon)(nil)
func NewIcon(sizepx int) *Icon {
2020-08-17 00:13:47 +00:00
img, _ := roundimage.NewImage(0)
img.Show()
return NewCustomIcon(img, sizepx)
2020-08-17 00:13:47 +00:00
}
func NewCustomIcon(img RoundIconContainer, sizepx int) *Icon {
2020-06-06 07:44:36 +00:00
if sizepx == 0 {
sizepx = DefaultIconSize
}
rev, _ := gtk.RevealerNew()
rev.Add(img)
rev.SetRevealChild(false)
rev.SetTransitionType(gtk.REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
rev.SetTransitionDuration(50)
i := &Icon{
2020-06-06 07:44:36 +00:00
Revealer: rev,
Image: img,
}
i.SetSize(sizepx)
return i
2020-06-06 07:44:36 +00:00
}
2020-06-07 04:27:28 +00:00
// URL is not thread-safe.
func (i *Icon) URL() string {
return i.url
}
func (i *Icon) CopyPixbuf(dst httputil.ImageContainer) {
switch i.Image.GetStorageType() {
case gtk.IMAGE_PIXBUF:
dst.SetFromPixbuf(i.Image.GetPixbuf())
case gtk.IMAGE_ANIMATION:
dst.SetFromAnimation(i.Image.GetAnimation())
}
}
// Thread-unsafe setter methods should only be called right after construction.
2020-06-06 07:44:36 +00:00
// SetPlaceholderIcon is not thread-safe.
func (i *Icon) SetPlaceholderIcon(iconName string, iconSzPx int) {
i.Image.SetRadius(-1) // square
2020-06-06 07:44:36 +00:00
i.SetRevealChild(true)
if iconName != "" {
2020-08-17 00:13:47 +00:00
primitives.SetImageIcon(i.Image, iconName, iconSzPx)
2020-06-06 07:44:36 +00:00
}
}
// SetSize is not thread-safe.
func (i *Icon) SetSize(szpx int) {
i.Image.SetSizeRequest(szpx, szpx)
}
2020-12-20 08:18:23 +00:00
// Size returns the minimum of the image size. It is not thread-safe.
func (i *Icon) Size() int {
w, h := i.Image.GetSizeRequest()
if h < w {
return h
}
return w
}
2020-06-06 07:44:36 +00:00
// SetIcon is thread-safe.
func (i *Icon) SetIcon(url string) {
gts.ExecAsync(func() { i.SetIconUnsafe(url) })
}
2020-10-15 06:32:11 +00:00
func (i *Icon) AsyncSetIconer(iconer cchat.Iconer, errwrap string) {
// Reveal to show the placeholder.
i.SetRevealChild(true)
// I have a hunch this will never work; as long as Go keeps a reference with
// iconer.Icon, then destroy will never be triggered.
ctx := primitives.HandleDestroyCtx(context.Background(), i)
gts.Async(func() (func(), error) {
f, err := iconer.Icon(ctx, i)
if err != nil {
return nil, errors.Wrap(err, "failed to load iconer")
}
return func() { i.Connect("destroy", func(interface{}) { f() }) }, nil
})
}
2020-06-07 04:27:28 +00:00
// SetIconUnsafe is not thread-safe.
func (i *Icon) SetIconUnsafe(url string) {
// Setting the radius here since we resetted it for a placeholder icon.
i.Image.SetRadius(0)
2020-06-07 04:27:28 +00:00
i.SetRevealChild(true)
2020-06-06 07:44:36 +00:00
i.url = url
i.Image.SetImageURL(i.url)
2020-06-06 07:44:36 +00:00
}
// type EventIcon struct {
// *gtk.EventBox
// Icon *Icon
// }
2020-06-06 07:44:36 +00:00
// func NewEventIcon(sizepx int) *EventIcon {
// icn := NewIcon(sizepx)
// return WrapEventIcon(icn)
// }
2020-07-16 05:41:21 +00:00
// func WrapEventIcon(icn *Icon) *EventIcon {
// icn.Show()
// evb, _ := gtk.EventBoxNew()
// evb.Add(icn)
2020-07-16 05:41:21 +00:00
// return &EventIcon{
// EventBox: evb,
// Icon: icn,
// }
// }
2020-07-16 05:41:21 +00:00
2020-06-06 07:44:36 +00:00
type ToggleButtonImage struct {
gtk.ToggleButton
Labeler
cchat.IconContainer
Label *gtk.Label
Image *Icon
Box *gtk.Box
}
var (
_ gtk.IWidget = (*ToggleButton)(nil)
_ cchat.LabelContainer = (*ToggleButton)(nil)
)
func NewToggleButtonImage(content text.Rich) *ToggleButtonImage {
img, _ := roundimage.NewStaticImage(nil, 0)
img.Show()
return NewCustomToggleButtonImage(img, content)
}
func NewCustomToggleButtonImage(img RoundIconContainer, content text.Rich) *ToggleButtonImage {
2020-06-06 07:44:36 +00:00
l := NewLabel(content)
l.Show()
2020-08-17 00:13:47 +00:00
i := NewCustomIcon(img, 0)
2020-06-06 07:44:36 +00:00
i.Show()
box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
box.PackStart(i, false, false, 0)
box.PackStart(l, true, true, 5)
box.Show()
b, _ := gtk.ToggleButtonNew()
b.Add(box)
if connector, ok := img.(roundimage.Connector); ok {
connector.ConnectHandlers(b)
}
2020-08-17 00:13:47 +00:00
2020-06-06 07:44:36 +00:00
return &ToggleButtonImage{
ToggleButton: *b,
Labeler: l, // easy inheritance of methods
IconContainer: i,
Label: &l.Label,
Image: i,
Box: box,
}
}