package imgview import ( "net/url" "path" "strings" "github.com/diamondburned/cchat-gtk/internal/gts/httputil" "github.com/diamondburned/cchat-gtk/internal/ui/primitives" "github.com/diamondburned/cchat-gtk/internal/ui/rich/parser" "github.com/gotk3/gotk3/gdk" "github.com/gotk3/gotk3/gtk" ) const ( MaxWidth = 350 MaxHeight = 350 ) type WidgetConnector interface { gtk.IWidget primitives.Connector } var _ WidgetConnector = (*gtk.Label)(nil) func BindTooltip(connector WidgetConnector) { // This implementation doesn't seem like a good idea. First off, is the // closure really garbage collected? If it's not, then we have some huge // issues. Second, if the closure is garbage collected, then when? If it's // not garbage collecteed, then not only are we leaking 2 float64s per // message, but we're also keeping alive the widget. var x, y float64 connector.Connect("motion-notify-event", func(w gtk.IWidget, ev *gdk.Event) { mev := gdk.EventMotionNewFromEvent(ev) x, y = mev.MotionVal() }) connector.Connect("activate-link", func(c WidgetConnector, uri string) bool { switch ext(uri) { case ".jpg", ".jpeg", ".png", ".webp", ".gif": // Make a new rectangle to use in the popover. r := gdk.Rectangle{} r.SetX(int(x)) r.SetY(int(y)) // Make a new image that's asynchronously fetched. img, _ := gtk.ImageNewFromIconName("image-loading", gtk.ICON_SIZE_BUTTON) img.SetMarginStart(5) img.SetMarginEnd(5) img.SetMarginTop(5) img.SetMarginBottom(5) img.Show() // Cap the width and height if requested. var w, h = parser.FragmentImageSize(uri, MaxWidth, MaxHeight) httputil.AsyncImageSized(img, uri, w, h) p, _ := gtk.PopoverNew(c) p.SetPointingTo(r) p.Connect("closed", img.Destroy) // on close, destroy image p.Add(img) p.Popup() return true default: return false } }) } // ext parses and sanitizes the extension to something comparable. func ext(uri string) string { u, err := url.Parse(uri) if err != nil { return strings.ToLower(path.Ext(uri)) } return strings.ToLower(path.Ext(u.Path)) }