mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2024-11-01 12:04:15 +00:00
117 lines
2.4 KiB
Go
117 lines
2.4 KiB
Go
|
package attachment
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"io"
|
||
|
|
||
|
"github.com/diamondburned/cchat-gtk/internal/gts"
|
||
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
||
|
"github.com/gotk3/gotk3/gtk"
|
||
|
"github.com/gotk3/gotk3/pango"
|
||
|
)
|
||
|
|
||
|
type MessageUploader struct {
|
||
|
*gtk.Grid
|
||
|
}
|
||
|
|
||
|
// NewMessageUploader creates a new MessageUploader. It returns nil if there are
|
||
|
// no files.
|
||
|
func NewMessageUploader(files []File) *MessageUploader {
|
||
|
m := &MessageUploader{}
|
||
|
|
||
|
m.Grid, _ = gtk.GridNew()
|
||
|
m.Grid.SetHExpand(true)
|
||
|
m.Grid.SetColumnSpacing(4)
|
||
|
m.Grid.SetRowSpacing(2)
|
||
|
m.Grid.SetRowHomogeneous(true)
|
||
|
|
||
|
primitives.AddClass(m.Grid, "upload-progress")
|
||
|
|
||
|
for i, file := range files {
|
||
|
var pbar = NewProgressBar(file)
|
||
|
|
||
|
m.Grid.Attach(pbar.Name, 0, i, 1, 1)
|
||
|
m.Grid.Attach(pbar.PBar, 1, i, 1, 1)
|
||
|
}
|
||
|
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
type ProgressBar struct {
|
||
|
PBar *gtk.ProgressBar
|
||
|
Name *gtk.Label
|
||
|
}
|
||
|
|
||
|
func NewProgressBar(file File) *ProgressBar {
|
||
|
bar, _ := gtk.ProgressBarNew()
|
||
|
bar.SetVAlign(gtk.ALIGN_CENTER)
|
||
|
bar.Show()
|
||
|
|
||
|
name, _ := gtk.LabelNew(file.Name)
|
||
|
name.SetMaxWidthChars(45)
|
||
|
name.SetSingleLineMode(true)
|
||
|
name.SetEllipsize(pango.ELLIPSIZE_MIDDLE)
|
||
|
name.SetXAlign(1)
|
||
|
name.Show()
|
||
|
|
||
|
// Override the upload read callback.
|
||
|
file.Prog.u = func(fraction float64) {
|
||
|
gts.ExecAsync(func() {
|
||
|
if fraction == -1 {
|
||
|
// Pulse the bar around, as we don't know the total bytes.
|
||
|
bar.Pulse()
|
||
|
} else {
|
||
|
// We know the progress, so use the percentage.
|
||
|
bar.SetFraction(fraction)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return &ProgressBar{bar, name}
|
||
|
}
|
||
|
|
||
|
// Progress wraps around a ReadCloser and implements a progress state for a
|
||
|
// reader.
|
||
|
type Progress struct {
|
||
|
u func(float64) // read callback, arg is percentage
|
||
|
r io.Reader
|
||
|
s float64 // total, const
|
||
|
n uint64 // cumulative
|
||
|
}
|
||
|
|
||
|
// NewProgress creates a new upload progress state.
|
||
|
func NewProgress(r io.Reader, size int64) *Progress {
|
||
|
return &Progress{
|
||
|
r: r,
|
||
|
s: float64(size),
|
||
|
n: 0,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// frac returns the current percentage, or -1 is there is no total.
|
||
|
func (p *Progress) frac() float64 {
|
||
|
if p.s > 0 {
|
||
|
return float64(p.n) / p.s
|
||
|
}
|
||
|
return -1
|
||
|
}
|
||
|
|
||
|
func (p *Progress) Read(b []byte) (int, error) {
|
||
|
// Read and cumulate total bytes read if there are no errors or if the error
|
||
|
// is not fatal (EOF).
|
||
|
n, err := p.r.Read(b)
|
||
|
if err == nil || errors.Is(err, io.EOF) {
|
||
|
p.n += uint64(n)
|
||
|
} else {
|
||
|
// If we have an unexpected error, then we should reset the bytes read
|
||
|
// to 0.
|
||
|
p.n = 0
|
||
|
}
|
||
|
|
||
|
if p.u != nil {
|
||
|
p.u(p.frac())
|
||
|
}
|
||
|
|
||
|
return n, err
|
||
|
}
|