From fdede5fc131dfeb59922924d450288c5142469de Mon Sep 17 00:00:00 2001 From: diamondburned Date: Thu, 13 Aug 2020 15:50:51 -0700 Subject: [PATCH] Improvements on popovers and mentions --- go.mod | 6 +- go.sum | 6 + .../messages/container/cozy/message_full.go | 2 +- internal/ui/rich/labeluri/labeluri.go | 104 ++++++++++++++++-- internal/ui/rich/parser/markup/markup.go | 7 +- 5 files changed, 110 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index ef116c9..db546a3 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,11 @@ go 1.14 replace github.com/gotk3/gotk3 => github.com/diamondburned/gotk3 v0.0.0-20200630065217-97aeb06d705d -replace github.com/diamondburned/cchat-discord => ../cchat-discord/ - require ( github.com/Xuanwo/go-locale v0.2.0 github.com/alecthomas/chroma v0.7.3 - github.com/diamondburned/cchat v0.0.45 - github.com/diamondburned/cchat-discord v0.0.0-20200718071554-360b34de2a71 + github.com/diamondburned/cchat v0.0.46 + github.com/diamondburned/cchat-discord v0.0.0-20200730000036-2c93cdc1974e github.com/diamondburned/cchat-mock v0.0.0-20200709231652-ad222ce5a74b github.com/diamondburned/imgutil v0.0.0-20200710174014-8a3be144a972 github.com/disintegration/imaging v1.6.2 diff --git a/go.sum b/go.sum index 0bb0c94..a0031a2 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,12 @@ github.com/diamondburned/cchat v0.0.43 h1:HetAujSaUSdnQgAUZgprNLARjf/MSWXpCfWdvX github.com/diamondburned/cchat v0.0.43/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU= github.com/diamondburned/cchat v0.0.45 h1:HMVSKx1h6lh2OenWaBTvMSK531hWaXAW7I0tKZepYug= github.com/diamondburned/cchat v0.0.45/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU= +github.com/diamondburned/cchat v0.0.46 h1:fzm2XA9uGasX0uaic1AFfUMGA53PlO+GGmkYbx49A5k= +github.com/diamondburned/cchat v0.0.46/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU= +github.com/diamondburned/cchat-discord v0.0.0-20200719175346-af912db55401 h1:llmx/8UiJoTcHUw+GE5/rESVVmmnLh1HEPx3wRj+oQY= +github.com/diamondburned/cchat-discord v0.0.0-20200719175346-af912db55401/go.mod h1:+hSrIVYj5tIPLAorDsHj2Tbt2fWlZtOanzfEUHX53HM= +github.com/diamondburned/cchat-discord v0.0.0-20200730000036-2c93cdc1974e h1:EA5Vg0x57qLURJP80XhABBW+X0sbQSh2gw5qvPbZTs4= +github.com/diamondburned/cchat-discord v0.0.0-20200730000036-2c93cdc1974e/go.mod h1:+hSrIVYj5tIPLAorDsHj2Tbt2fWlZtOanzfEUHX53HM= github.com/diamondburned/cchat-mock v0.0.0-20200709231652-ad222ce5a74b h1:sq0MXjJc3yAOZvuolRxOpKQNvpMLyTmsECxQqdYgF5E= github.com/diamondburned/cchat-mock v0.0.0-20200709231652-ad222ce5a74b/go.mod h1:+bAf0m2o5qH54DmYJ/lR1HeITV53ol0JaoKyFFx3m3E= github.com/diamondburned/gotk3 v0.0.0-20200630065217-97aeb06d705d h1:Ha/I6PMKi+B4hpWclwlXj0tUMehR7Q0TNxPczzBwzPI= diff --git a/internal/ui/messages/container/cozy/message_full.go b/internal/ui/messages/container/cozy/message_full.go index aa2882f..d3c95c4 100644 --- a/internal/ui/messages/container/cozy/message_full.go +++ b/internal/ui/messages/container/cozy/message_full.go @@ -67,7 +67,7 @@ func WrapFullMessage(gc *message.GenericContainer) *FullMessage { avatar.SetMarginStart(container.ColumnSpacing * 2) avatar.Connect("clicked", func() { if output := gc.Username.Output(); len(output.Mentions) > 0 { - labeluri.PopoverMentioner(avatar, output.Mentions[0]) + labeluri.PopoverMentioner(avatar, output.Input, output.Mentions[0]) } }) // We don't call avatar.Show(). That's called in Attach. diff --git a/internal/ui/rich/labeluri/labeluri.go b/internal/ui/rich/labeluri/labeluri.go index 2db837d..0ac15da 100644 --- a/internal/ui/rich/labeluri/labeluri.go +++ b/internal/ui/rich/labeluri/labeluri.go @@ -12,6 +12,7 @@ import ( "github.com/diamondburned/cchat-gtk/internal/ui/dialog" "github.com/diamondburned/cchat-gtk/internal/ui/primitives" "github.com/diamondburned/cchat-gtk/internal/ui/primitives/roundimage" + "github.com/diamondburned/cchat-gtk/internal/ui/primitives/scrollinput" "github.com/diamondburned/cchat-gtk/internal/ui/rich" "github.com/diamondburned/cchat-gtk/internal/ui/rich/parser/markup" "github.com/diamondburned/cchat/text" @@ -23,8 +24,10 @@ import ( ) const ( - MaxWidth = 350 - MaxHeight = 350 + AvatarSize = 96 + PopoverWidth = 250 + MaxWidth = 350 + MaxHeight = 350 ) type WidgetConnector interface { @@ -88,7 +91,7 @@ func BindRichLabel(label Labeler) { var output = label.Output() if mention := output.IsMention(uri); mention != nil { - if p := popoverMentioner(label, mention); p != nil { + if p := popoverMentioner(label, output.Input, mention); p != nil { p.SetPointingTo(ptr) p.Popup() } @@ -100,32 +103,117 @@ func BindRichLabel(label Labeler) { }) } -func PopoverMentioner(rel gtk.IWidget, mention text.Mentioner) { - if p := popoverMentioner(rel, mention); p != nil { +func PopoverMentioner(rel gtk.IWidget, input string, mention text.Mentioner) { + if p := popoverMentioner(rel, input, mention); p != nil { p.Popup() } } -func popoverMentioner(rel gtk.IWidget, mention text.Mentioner) *gtk.Popover { +func popoverMentioner(rel gtk.IWidget, input string, mention text.Mentioner) *gtk.Popover { var info = mention.MentionInfo() if info.Empty() { return nil } + start, end := mention.Bounds() + h := input[start:end] + + box, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) + box.Show() + + // Do we have an image or an avatar? + var url string + var round bool + + switch v := mention.(type) { + case text.MentionerImage: + url = v.Image() + case text.MentionerAvatar: + url = v.Avatar() + round = true + } + + if url != "" { + box.PackStart(popoverImg(url, round), false, false, 8) + } + + head, _ := gtk.LabelNew(largeText(h)) + head.SetUseMarkup(true) + head.SetLineWrap(true) + head.SetLineWrapMode(pango.WRAP_WORD_CHAR) + head.SetMarginStart(8) + head.SetMarginEnd(8) + head.Show() + box.PackStart(head, false, false, 0) + + // Left-align the label if we don't have an image. + if url == "" { + head.SetXAlign(0) + } + l, _ := gtk.LabelNew(markup.Render(info)) l.SetUseMarkup(true) + l.SetLineWrapMode(pango.WRAP_WORD_CHAR) + l.SetLineWrap(true) l.SetXAlign(0) + l.SetMarginStart(8) + l.SetMarginEnd(8) + l.SetMarginTop(8) + l.SetMarginBottom(8) l.Show() // Enable images??? BindActivator(l) + // Make a scrolling text. + scr := scrollinput.NewVScroll(PopoverWidth) + scr.Show() + scr.Add(l) + box.PackStart(scr, false, false, 0) + p, _ := gtk.PopoverNew(rel) - p.Add(l) - p.Connect("destroy", l.Destroy) + p.Add(box) + p.SetSizeRequest(PopoverWidth, -1) + p.Connect("destroy", box.Destroy) return p } +func largeText(text string) string { + return fmt.Sprintf( + `%s`, html.EscapeString(text), + ) +} + +// popoverImg creates a new button with an image for it, which is used for the +// avatar in the user popover. +func popoverImg(url string, round bool) gtk.IWidget { + var img *gtk.Image + var btn *gtk.Button + + if round { + b, _ := roundimage.NewButton() + img = b.Image.Image + btn = b.Button + } else { + img, _ = gtk.ImageNew() + btn, _ = gtk.ButtonNew() + btn.Add(img) + } + + img.SetSizeRequest(AvatarSize, AvatarSize) + img.SetHAlign(gtk.ALIGN_CENTER) + img.Show() + + httputil.AsyncImageSized(img, url, AvatarSize, AvatarSize) + + btn.SetHAlign(gtk.ALIGN_CENTER) + btn.SetRelief(gtk.RELIEF_NONE) + btn.Connect("clicked", func() { PromptOpen(url) }) + btn.Show() + + return btn +} + func BindActivator(connector WidgetConnector) { bind(connector, nil) } diff --git a/internal/ui/rich/parser/markup/markup.go b/internal/ui/rich/parser/markup/markup.go index 877be78..1aa3a63 100644 --- a/internal/ui/rich/parser/markup/markup.go +++ b/internal/ui/rich/parser/markup/markup.go @@ -24,11 +24,12 @@ func hyphenate(text string) string { // RenderOutput is the output of a render. type RenderOutput struct { Markup string + Input string // useless to keep parts, as Go will keep all alive anyway Mentions []text.Mentioner } // f_Mention is used to print and parse mention URIs. -const f_Mention = "cchat://mention:%d" // %d == Mentions[i] +const f_Mention = "cchat://mention/%d" // %d == Mentions[i] // IsMention returns the mention if the URI is correct, or nil if none. func (r RenderOutput) IsMention(uri string) text.Mentioner { @@ -65,6 +66,7 @@ func RenderCmplxWithConfig(content text.Rich, cfg RenderConfig) RenderOutput { if len(content.Segments) == 0 { return RenderOutput{ Markup: hyphenate(html.EscapeString(content.Content)), + Input: content.Content, } } @@ -120,7 +122,7 @@ func RenderCmplxWithConfig(content text.Rich, cfg RenderConfig) RenderOutput { if segment, ok := segment.(text.Mentioner); ok { // Render the mention into "cchat://mention:0" or such. Other // components will take care of showing the information. - if cfg.NoMentionLinks { + if !cfg.NoMentionLinks { appended.AnchorNU(start, end, fmt.Sprintf(f_Mention, len(mentions))) } @@ -168,6 +170,7 @@ func RenderCmplxWithConfig(content text.Rich, cfg RenderConfig) RenderOutput { return RenderOutput{ Markup: hyphenate(buf.String()), + Input: content.Content, Mentions: mentions, } }