Several minor changes and bug fixes

This commit added a confirmation dialog when clicking on links. It also
fixes some bugs.

I forgot the rest.
This commit is contained in:
diamondburned 2020-07-08 02:07:00 -07:00
parent 2fae6ffbb3
commit 76d4b8d69a
8 changed files with 247 additions and 28 deletions

8
go.mod
View File

@ -4,13 +4,15 @@ 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.40
github.com/diamondburned/cchat-discord v0.0.0-20200703190659-fbf95b9b6c03
github.com/diamondburned/cchat v0.0.42
github.com/diamondburned/cchat-discord v0.0.0-20200708083530-d0e43cc63b03
github.com/diamondburned/cchat-mock v0.0.0-20200704044009-f587c4904aa3
github.com/diamondburned/imgutil v0.0.0-20200704034004-40dbfc732516
github.com/diamondburned/imgutil v0.0.0-20200708012333-53c9e45dd28b
github.com/goodsign/monday v1.0.0
github.com/gotk3/gotk3 v0.4.1-0.20200524052254-cb2aa31c6194
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79

14
go.sum
View File

@ -46,22 +46,36 @@ github.com/diamondburned/aqs v0.0.0-20200704043812-99b676ee44eb h1:Ja/niwykeFoSk
github.com/diamondburned/aqs v0.0.0-20200704043812-99b676ee44eb/go.mod h1:q1MbMBfZrv7xqV8n7LgMwhHs3oBbNwWJes8exs2AmDs=
github.com/diamondburned/arikawa v0.9.5 h1:P1ffsp+NHT22wWKYFVC8CdlGRLzPuUV9FcCBKOCJpCI=
github.com/diamondburned/arikawa v0.9.5/go.mod h1:nIhVIatzTQhPUa7NB8w4koG1RF9gYbpAr8Fj8sKq660=
github.com/diamondburned/arikawa v0.9.6 h1:6TpfTKa2btoVQGxojNqv8g2YC0tIc/tX5w/OCVZPF5Q=
github.com/diamondburned/arikawa v0.9.6/go.mod h1:nIhVIatzTQhPUa7NB8w4koG1RF9gYbpAr8Fj8sKq660=
github.com/diamondburned/cchat v0.0.40 h1:38gPyJnnDoNDHrXcV8Qchfv3y6jlS3Fzz/6FY0BPH6I=
github.com/diamondburned/cchat v0.0.40/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
github.com/diamondburned/cchat v0.0.41 h1:6y32s2wWTiDw4hWN/Gna6ay3uUrRAW5V8Cj0/xLKovw=
github.com/diamondburned/cchat v0.0.41/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
github.com/diamondburned/cchat v0.0.42 h1:FVMLy9hOTxKju8OWDBIStrekbgTHCaH8+GVnV4LOByg=
github.com/diamondburned/cchat v0.0.42/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
github.com/diamondburned/cchat-discord v0.0.0-20200703190659-fbf95b9b6c03 h1:F5TL7GPRU/D4ldVkS0haY3SiHPtf1Kby/4nbYpm//MQ=
github.com/diamondburned/cchat-discord v0.0.0-20200703190659-fbf95b9b6c03/go.mod h1:p0X6QUH0mxK8yEW0+a4QA77ClAmoxz8CvgbnobMtWQA=
github.com/diamondburned/cchat-discord v0.0.0-20200708083530-d0e43cc63b03 h1:Xx4ioFTurT6qTxzTL8QlsH3E5VskLxHPJ8RwmaKhObA=
github.com/diamondburned/cchat-discord v0.0.0-20200708083530-d0e43cc63b03/go.mod h1:hjaCeQfhSqANH+ZtxXPMWzpgobb4syy2VcqDK4PXcPU=
github.com/diamondburned/cchat-mock v0.0.0-20200704044009-f587c4904aa3 h1:xr07/2cwINyrMqh92pQQJVDfQqG0u6gHAK+ZcGfpSew=
github.com/diamondburned/cchat-mock v0.0.0-20200704044009-f587c4904aa3/go.mod h1:SRu3OOeggELFr2Wd3/+SpYV1eNcvSk2LBhM70NOZSG8=
github.com/diamondburned/gotk3 v0.0.0-20200630065217-97aeb06d705d h1:Ha/I6PMKi+B4hpWclwlXj0tUMehR7Q0TNxPczzBwzPI=
github.com/diamondburned/gotk3 v0.0.0-20200630065217-97aeb06d705d/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
github.com/diamondburned/imgutil v0.0.0-20200704034004-40dbfc732516 h1:6j4oZahbNdVhSEInRfeYbgDpx1FXDfJy6CcUVyWOuVY=
github.com/diamondburned/imgutil v0.0.0-20200704034004-40dbfc732516/go.mod h1:kBQKaukR/LyCfhED99/T4/XxUMDNEEzf1Fx6vreD3RQ=
github.com/diamondburned/imgutil v0.0.0-20200708012333-53c9e45dd28b h1:iYKHGvWzNFBIRTSY8Pd5g301YDGWMfs3fh1VS0iBSj0=
github.com/diamondburned/imgutil v0.0.0-20200708012333-53c9e45dd28b/go.mod h1:kBQKaukR/LyCfhED99/T4/XxUMDNEEzf1Fx6vreD3RQ=
github.com/diamondburned/ningen v0.1.1-0.20200621014632-6babb812b249 h1:yP7kJ+xCGpDz6XbcfACJcju4SH1XDPwlrvbofz3lP8I=
github.com/diamondburned/ningen v0.1.1-0.20200621014632-6babb812b249/go.mod h1:xW9hpBZsGi8KpAh10TyP+YQlYBo+Xc+2w4TR6N0951A=
github.com/diamondburned/ningen v0.1.1-0.20200708090333-227e90d19851 h1:xf1aLPnwK/Yn2z7dBIgQROSVOEc2wtivgnnwBItdEVM=
github.com/diamondburned/ningen v0.1.1-0.20200708090333-227e90d19851/go.mod h1:FNezDLQIhoDS+RkXLSQ7dJNrt6BW/nVl1krzDgWMQwg=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=

6
internal/c/README.md Normal file
View File

@ -0,0 +1,6 @@
# c
**HERE BE DRAGONS!!**
This package and all its subpackages contain either C code or Cgo code. This
bugs me and I hate it. PROCEED WITH CAUTION!

23
internal/c/gtkp/gtkp.go Normal file
View File

@ -0,0 +1,23 @@
package gtkp
// #cgo pkg-config: gdk-3.0 gio-2.0 glib-2.0 gobject-2.0 gtk+-3.0
// #include <glib.h>
// #include <gtk/gtk.h>
// #include <pango/pango.h>
import "C"
import (
"unsafe"
"github.com/gotk3/gotk3/gtk"
)
func LabelNoHyphens(l *gtk.Label) {
attrlist := C.pango_attr_list_new()
defer C.pango_attr_list_unref(attrlist)
C.pango_attr_list_insert(attrlist, C.pango_attr_insert_hyphens_new(C.FALSE))
v := (*C.GtkLabel)(unsafe.Pointer(l.Native()))
C.gtk_label_set_attributes(v, attrlist)
}

View File

@ -0,0 +1,101 @@
package labelutils
// #cgo pkg-config: gdk-3.0 gio-2.0 glib-2.0 gobject-2.0 gtk+-3.0
// #include <glib.h>
// #include <gtk/gtk.h>
// #include <pango/pango.h>
import "C"
import (
"unsafe"
"github.com/gotk3/gotk3/gtk"
"github.com/gotk3/gotk3/pango"
)
func gbool(b bool) C.gboolean {
if b {
return C.gboolean(C.TRUE)
} else {
return C.gboolean(C.FALSE)
}
}
type Attribute = func() *C.PangoAttribute
func InsertHyphens(hyphens bool) Attribute {
return func() *C.PangoAttribute {
return C.pango_attr_insert_hyphens_new(gbool(hyphens))
}
}
func Scale(factor float64) Attribute {
return func() *C.PangoAttribute {
return C.pango_attr_scale_new(C.double(factor))
}
}
func Underline(underline pango.Underline) Attribute {
return func() *C.PangoAttribute {
return C.pango_attr_underline_new(C.PangoUnderline(underline))
}
}
func Strikethrough(strikethrough bool) Attribute {
return func() *C.PangoAttribute {
return C.pango_attr_strikethrough_new(gbool(strikethrough))
}
}
const u16divu8 = 65535 / 255
func rgb(hex uint32) (r, g, b uint16) {
r = uint16(hex>>16&255) * u16divu8
g = uint16(hex>>8&255) * u16divu8
b = uint16(hex&255) * u16divu8
return
}
func Background(hex uint32) Attribute {
r, g, b := rgb(hex)
return func() *C.PangoAttribute {
return C.pango_attr_background_new(C.guint16(r), C.guint16(g), C.guint16(b))
}
}
func Foreground(hex uint32) Attribute {
r, g, b := rgb(hex)
return func() *C.PangoAttribute {
return C.pango_attr_foreground_new(C.guint16(r), C.guint16(g), C.guint16(b))
}
}
func Style(style pango.Style) Attribute {
return func() *C.PangoAttribute {
return C.pango_attr_style_new(C.PangoStyle(style))
}
}
func Family(family string) Attribute {
return func() *C.PangoAttribute {
str := C.CString(family)
defer C.free(unsafe.Pointer(str))
return C.pango_attr_family_new(str)
}
}
func AddAttr(l *gtk.Label, attrs ...Attribute) {
attrlist := C.pango_attr_list_new()
defer C.pango_attr_list_unref(attrlist)
for _, attr := range attrs {
// attr() should not unref; insert is transfer-full
// https://discourse.gnome.org/t/pango-how-to-turn-off-hyphenation-for-char-wrapping/2101/2
C.pango_attr_list_insert(attrlist, attr())
}
v := (*C.GtkLabel)(unsafe.Pointer(l.Native()))
C.gtk_label_set_attributes(v, attrlist)
}

View File

@ -6,7 +6,11 @@ import (
"github.com/gotk3/gotk3/gtk"
)
func NewModal(body gtk.IWidget, title, label string, callback func()) *gtk.Dialog {
func ShowModal(body gtk.IWidget, title, button string, callback func()) {
NewModal(body, title, title, callback).Show()
}
func NewModal(body gtk.IWidget, title, button string, callback func()) *gtk.Dialog {
cancel, _ := gtk.ButtonNew()
cancel.Show()
cancel.SetHAlign(gtk.ALIGN_START)
@ -17,10 +21,12 @@ func NewModal(body gtk.IWidget, title, label string, callback func()) *gtk.Dialo
action.Show()
action.SetHAlign(gtk.ALIGN_END)
action.SetRelief(gtk.RELIEF_NONE)
action.SetLabel(label)
action.SetLabel(button)
header, _ := gtk.HeaderBarNew()
header.Show()
header.SetMarginStart(5)
header.SetMarginEnd(5)
header.SetTitle(title)
header.PackStart(cancel)
header.PackEnd(action)
@ -44,20 +50,13 @@ func NewCSD(body, header gtk.IWidget) *gtk.Dialog {
}
func newCSD(body, header gtk.IWidget) *gtk.Dialog {
dialog, _ := gtk.DialogNew()
dialog.SetModal(true)
dialog.SetTransientFor(gts.App.Window)
if area, _ := dialog.GetContentArea(); area != nil {
dialog.Remove(area)
}
dialog, _ := gts.NewEmptyModalDialog()
dialog.SetDefaultSize(450, 300)
dialog.Add(body)
if oldh, _ := dialog.GetHeaderBar(); oldh != nil {
dialog.Remove(oldh)
}
dialog.Add(body)
dialog.SetTitlebar(header)
return dialog

View File

@ -1,15 +1,23 @@
package imgview
import (
"fmt"
"html"
"net/url"
"path"
"strings"
"github.com/diamondburned/cchat-gtk/internal/c/labelutils"
"github.com/diamondburned/cchat-gtk/internal/gts/httputil"
"github.com/diamondburned/cchat-gtk/internal/log"
"github.com/diamondburned/cchat-gtk/internal/ui/dialog"
"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"
"github.com/gotk3/gotk3/pango"
"github.com/pkg/errors"
"github.com/skratchdot/open-golang/open"
)
const (
@ -44,7 +52,8 @@ func BindTooltip(connector WidgetConnector) {
r.SetX(int(x))
r.SetY(int(y))
// Make a new image that's asynchronously fetched.
// Make a new image that's asynchronously fetched inside a button.
// This allows us to make it clickable.
img, _ := gtk.ImageNewFromIconName("image-loading", gtk.ICON_SIZE_BUTTON)
img.SetMarginStart(5)
img.SetMarginEnd(5)
@ -56,20 +65,80 @@ func BindTooltip(connector WidgetConnector) {
var w, h = parser.FragmentImageSize(uri, MaxWidth, MaxHeight)
httputil.AsyncImageSized(img, uri, w, h)
btn, _ := gtk.ButtonNew()
btn.Add(img)
btn.SetRelief(gtk.RELIEF_NONE)
btn.Connect("clicked", func() { PromptOpen(uri) })
btn.Show()
p, _ := gtk.PopoverNew(c)
p.SetPointingTo(r)
p.Connect("closed", img.Destroy) // on close, destroy image
p.Add(img)
p.Add(btn)
p.Popup()
return true
default:
return false
PromptOpen(uri)
}
// Never let Gtk open the dialog.
return true
})
}
const urlPrompt = `This link leads to the following URL:
<span weight="bold"><a href="%[1]s">%[1]s</a></span>
Click <b>Open</b> to proceed.`
var warnLabelCSS = primitives.PrepareCSS(`
label {
padding: 4px 8px;
}
`)
// PromptOpen shows a dialog asking if the URL should be opened.
func PromptOpen(uri string) {
// Format the prompt body.
l, _ := gtk.LabelNew("")
l.SetJustify(gtk.JUSTIFY_CENTER)
l.SetLineWrap(true)
l.SetLineWrapMode(pango.WRAP_WORD_CHAR)
l.Show()
l.SetMarkup(fmt.Sprintf(urlPrompt, html.EscapeString(uri)))
// Style the label.
primitives.AttachCSS(l, warnLabelCSS)
// Disable hyphens on line wraps.
labelutils.AddAttr(l, labelutils.InsertHyphens(false))
open := func() {
if err := open.Start(uri); err != nil {
log.Error(errors.Wrap(err, "Failed to open URL after confirm"))
}
}
// Prompt the user if they want to open the URL.
dlg := dialog.NewModal(l, "Caution", "Open", open)
dlg.SetSizeRequest(350, 100)
// Add a class to the dialog to allow theming.
primitives.AddClass(dlg, "url-warning")
// On link click, close the dialog.
l.Connect("activate-link", func(l *gtk.Label, uri string) bool {
// Close the dialog.
dlg.Destroy()
// Open the link anyway.
open()
// Return true since we handled the event.
return true
})
// Show the dialog.
dlg.Show()
}
// ext parses and sanitizes the extension to something comparable.
func ext(uri string) string {
u, err := url.Parse(uri)

View File

@ -64,27 +64,32 @@ func RenderMarkup(content text.Rich) string {
for _, segment := range content.Segments {
start, end := segment.Bounds()
switch segment := segment.(type) {
case text.Linker:
if segment, ok := segment.(text.Linker); ok {
appended.Addf(start, `<a href="%s">`, html.EscapeString(segment.Link()))
appended.Add(end, "</a>")
}
case text.Imager:
if segment, ok := segment.(text.Imager); ok {
// Ends don't matter with images.
appended.Add(start, composeImageMarkup(segment))
}
case text.Colorer:
if segment, ok := segment.(text.Colorer); ok {
appended.Span(start, end, fmt.Sprintf(`color="#%06X"`, segment.Color()))
}
case text.Attributor:
if segment, ok := segment.(text.Attributor); ok {
appended.Span(start, end, markupAttr(segment.Attribute()))
}
case text.Codeblocker:
if segment, ok := segment.(text.Codeblocker); ok {
// Syntax highlight the codeblock.
hl.Segments(&appended, content.Content, segment)
}
case text.Quoteblocker:
// TODO: pls.
// TODO: make this not shit. Maybe make it somehow not rely on green
// arrows. Or maybe.
if _, ok := segment.(text.Quoteblocker); ok {
appended.Span(start, end, `color="#789922"`)
}
}