WIP: progress (unix).
This commit is contained in:
parent
374ba8a90a
commit
10c3d63ca5
10 changed files with 209 additions and 82 deletions
|
@ -32,7 +32,7 @@ func selectColor(opts options) (color.Color, error) {
|
|||
if opts.color != nil {
|
||||
args.Flags |= 0x1 // CC_RGBINIT
|
||||
n := color.NRGBAModel.Convert(opts.color).(color.NRGBA)
|
||||
args.RgbResult = uint32(n.R) | (uint32(n.G) << 8) | (uint32(n.B) << 16)
|
||||
args.RgbResult = uint32(n.R) | uint32(n.G)<<8 | uint32(n.B)<<16
|
||||
}
|
||||
if opts.showPalette {
|
||||
args.Flags |= 0x4 // CC_PREVENTFULLOPEN
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
@ -51,7 +50,8 @@ func Run(ctx context.Context, script string, data interface{}) ([]byte, error) {
|
|||
return cmd.Output()
|
||||
}
|
||||
|
||||
func RunProgress(ctx context.Context, env []string) (m *progressDialog, err error) {
|
||||
// RunProgress is internal.
|
||||
func RunProgress(ctx context.Context, max int, env []string) (m *progressDialog, err error) {
|
||||
t, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -108,10 +108,9 @@ func RunProgress(ctx context.Context, env []string) (m *progressDialog, err erro
|
|||
m = &progressDialog{
|
||||
done: make(chan struct{}),
|
||||
lines: make(chan string),
|
||||
max: max,
|
||||
}
|
||||
go func() {
|
||||
defer func() {
|
||||
pipe.Close()
|
||||
err := cmd.Wait()
|
||||
if cerr := ctx.Err(); cerr != nil {
|
||||
err = cerr
|
||||
|
@ -120,6 +119,8 @@ func RunProgress(ctx context.Context, env []string) (m *progressDialog, err erro
|
|||
close(m.done)
|
||||
os.RemoveAll(t)
|
||||
}()
|
||||
go func() {
|
||||
defer pipe.Close()
|
||||
for {
|
||||
var line string
|
||||
select {
|
||||
|
@ -140,52 +141,6 @@ func RunProgress(ctx context.Context, env []string) (m *progressDialog, err erro
|
|||
return
|
||||
}
|
||||
|
||||
type progressDialog struct {
|
||||
err error
|
||||
done chan struct{}
|
||||
lines chan string
|
||||
}
|
||||
|
||||
func (m *progressDialog) send(line string) error {
|
||||
select {
|
||||
case m.lines <- line:
|
||||
return nil
|
||||
case <-m.done:
|
||||
return m.err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *progressDialog) Close() error {
|
||||
close(m.lines)
|
||||
<-m.done
|
||||
return m.err
|
||||
}
|
||||
|
||||
func (m *progressDialog) Text(text string) error {
|
||||
return m.send("#" + text)
|
||||
}
|
||||
|
||||
func (m *progressDialog) Value(value int) error {
|
||||
return m.send(strconv.Itoa(value))
|
||||
}
|
||||
|
||||
// File is internal.
|
||||
type File struct {
|
||||
Operation string
|
||||
Separator string
|
||||
Options FileOptions
|
||||
}
|
||||
|
||||
// FileOptions is internal.
|
||||
type FileOptions struct {
|
||||
Prompt *string `json:"withPrompt,omitempty"`
|
||||
Type []string `json:"ofType,omitempty"`
|
||||
Name string `json:"defaultName,omitempty"`
|
||||
Location string `json:"defaultLocation,omitempty"`
|
||||
Multiple bool `json:"multipleSelectionsAllowed,omitempty"`
|
||||
Invisibles bool `json:"invisibles,omitempty"`
|
||||
}
|
||||
|
||||
// Dialog is internal.
|
||||
type Dialog struct {
|
||||
Operation string
|
||||
|
@ -208,6 +163,25 @@ type DialogOptions struct {
|
|||
Timeout int `json:"givingUpAfter,omitempty"`
|
||||
}
|
||||
|
||||
// DialogButtons is internal.
|
||||
type DialogButtons struct {
|
||||
Buttons []string
|
||||
Default int
|
||||
Cancel int
|
||||
Extra int
|
||||
}
|
||||
|
||||
// SetButtons is internal.
|
||||
func (d *Dialog) SetButtons(btns DialogButtons) {
|
||||
d.Options.Buttons = btns.Buttons
|
||||
d.Options.Default = btns.Default
|
||||
d.Options.Cancel = btns.Cancel
|
||||
if btns.Extra > 0 {
|
||||
name := btns.Buttons[btns.Extra-1]
|
||||
d.Extra = &name
|
||||
}
|
||||
}
|
||||
|
||||
// List is internal.
|
||||
type List struct {
|
||||
Items []string
|
||||
|
@ -226,6 +200,23 @@ type ListOptions struct {
|
|||
Empty bool `json:"emptySelectionAllowed,omitempty"`
|
||||
}
|
||||
|
||||
// File is internal.
|
||||
type File struct {
|
||||
Operation string
|
||||
Separator string
|
||||
Options FileOptions
|
||||
}
|
||||
|
||||
// FileOptions is internal.
|
||||
type FileOptions struct {
|
||||
Prompt *string `json:"withPrompt,omitempty"`
|
||||
Type []string `json:"ofType,omitempty"`
|
||||
Name string `json:"defaultName,omitempty"`
|
||||
Location string `json:"defaultLocation,omitempty"`
|
||||
Multiple bool `json:"multipleSelectionsAllowed,omitempty"`
|
||||
Invisibles bool `json:"invisibles,omitempty"`
|
||||
}
|
||||
|
||||
// Notify is internal.
|
||||
type Notify struct {
|
||||
Text string
|
||||
|
@ -237,20 +228,3 @@ type NotifyOptions struct {
|
|||
Title *string `json:"withTitle,omitempty"`
|
||||
Subtitle string `json:"subtitle,omitempty"`
|
||||
}
|
||||
|
||||
type Buttons struct {
|
||||
Buttons []string
|
||||
Default int
|
||||
Cancel int
|
||||
Extra int
|
||||
}
|
||||
|
||||
func (d *Dialog) SetButtons(btns Buttons) {
|
||||
d.Options.Buttons = btns.Buttons
|
||||
d.Options.Default = btns.Default
|
||||
d.Options.Cancel = btns.Cancel
|
||||
if btns.Extra > 0 {
|
||||
name := btns.Buttons[btns.Extra-1]
|
||||
d.Extra = &name
|
||||
}
|
||||
}
|
||||
|
|
50
internal/zenutil/run_progress.go
Normal file
50
internal/zenutil/run_progress.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
// +build !windows,!js
|
||||
|
||||
package zenutil
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type progressDialog struct {
|
||||
err error
|
||||
done chan struct{}
|
||||
lines chan string
|
||||
percent bool
|
||||
max int
|
||||
}
|
||||
|
||||
func (m *progressDialog) send(line string) error {
|
||||
select {
|
||||
case m.lines <- line:
|
||||
return nil
|
||||
case <-m.done:
|
||||
return m.err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *progressDialog) Close() error {
|
||||
close(m.lines)
|
||||
<-m.done
|
||||
return m.err
|
||||
}
|
||||
|
||||
func (m *progressDialog) Text(text string) error {
|
||||
return m.send("#" + text)
|
||||
}
|
||||
|
||||
func (m *progressDialog) Value(value int) error {
|
||||
if m.percent {
|
||||
return m.send(strconv.FormatFloat(100*float64(value)/float64(m.max), 'f', -1, 64))
|
||||
} else {
|
||||
return m.send(strconv.Itoa(value))
|
||||
}
|
||||
}
|
||||
|
||||
func (m *progressDialog) MaxValue() int {
|
||||
return m.max
|
||||
}
|
||||
|
||||
func (m *progressDialog) Done() <-chan struct{} {
|
||||
return m.done
|
||||
}
|
|
@ -40,3 +40,66 @@ func Run(ctx context.Context, args []string) ([]byte, error) {
|
|||
}
|
||||
return exec.Command(tool, args...).Output()
|
||||
}
|
||||
|
||||
// RunProgress is internal.
|
||||
func RunProgress(ctx context.Context, max int, args []string) (m *progressDialog, err error) {
|
||||
if Command && path != "" {
|
||||
if Timeout > 0 {
|
||||
args = append(args, "--timeout", strconv.Itoa(Timeout))
|
||||
}
|
||||
syscall.Exec(path, append([]string{tool}, args...), os.Environ())
|
||||
}
|
||||
|
||||
cmd := exec.Command(tool, args...)
|
||||
pipe, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = cmd.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
m = &progressDialog{
|
||||
done: make(chan struct{}),
|
||||
lines: make(chan string),
|
||||
percent: true,
|
||||
max: max,
|
||||
}
|
||||
go func() {
|
||||
err := cmd.Wait()
|
||||
select {
|
||||
case _, ok := <-m.lines:
|
||||
if !ok {
|
||||
err = nil
|
||||
}
|
||||
default:
|
||||
}
|
||||
if cerr := ctx.Err(); cerr != nil {
|
||||
err = cerr
|
||||
}
|
||||
m.err = err
|
||||
close(m.done)
|
||||
}()
|
||||
go func() {
|
||||
defer cmd.Process.Signal(syscall.SIGTERM)
|
||||
for {
|
||||
var line string
|
||||
select {
|
||||
case s, ok := <-m.lines:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
line = s
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
if _, err := pipe.Write([]byte(line + "\n")); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
|
14
progress.go
14
progress.go
|
@ -8,14 +8,26 @@ func Progress(options ...Option) (ProgressDialog, error) {
|
|||
return progress(applyOptions(options))
|
||||
}
|
||||
|
||||
// ProgressDialog allows you to interact with the progress indication dialog.
|
||||
type ProgressDialog interface {
|
||||
// Text sets the dialog text.
|
||||
Text(string) error
|
||||
|
||||
// Value sets how much of the task has been completed.
|
||||
Value(int) error
|
||||
|
||||
// MaxValue gets how much work the task requires in total.
|
||||
MaxValue() int
|
||||
|
||||
// Close closes the dialog.
|
||||
Close() error
|
||||
|
||||
// Done returns a channel that's closed when the dialog is closed.
|
||||
Done() <-chan struct{}
|
||||
}
|
||||
|
||||
// MaxValue returns an Option to set the maximum value (macOS only).
|
||||
// The default value is 100.
|
||||
// The default maximum value is 100.
|
||||
func MaxValue(value int) Option {
|
||||
return funcOption(func(o *options) { o.maxValue = value })
|
||||
}
|
||||
|
|
|
@ -17,5 +17,5 @@ func progress(opts options) (ProgressDialog, error) {
|
|||
if opts.maxValue >= 0 {
|
||||
env = append(env, "total="+strconv.Itoa(opts.maxValue))
|
||||
}
|
||||
return zenutil.RunProgress(opts.ctx, env)
|
||||
return zenutil.RunProgress(opts.ctx, opts.maxValue, env)
|
||||
}
|
||||
|
|
28
progress_unix.go
Normal file
28
progress_unix.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
// +build !windows,!darwin,!js
|
||||
|
||||
package zenity
|
||||
|
||||
import (
|
||||
"github.com/ncruces/zenity/internal/zenutil"
|
||||
)
|
||||
|
||||
func progress(opts options) (ProgressDialog, error) {
|
||||
args := []string{"--progress"}
|
||||
args = appendTitle(args, opts)
|
||||
args = appendButtons(args, opts)
|
||||
args = appendWidthHeight(args, opts)
|
||||
args = appendIcon(args, opts)
|
||||
if opts.maxValue == 0 {
|
||||
opts.maxValue = 100
|
||||
}
|
||||
if opts.maxValue < 0 {
|
||||
args = append(args, "--pulsate")
|
||||
}
|
||||
if opts.noCancel {
|
||||
args = append(args, "--no-cancel")
|
||||
}
|
||||
if opts.timeRemaining {
|
||||
args = append(args, "--time-remaining")
|
||||
}
|
||||
return zenutil.RunProgress(opts.ctx, opts.maxValue, args)
|
||||
}
|
|
@ -2,13 +2,13 @@ package zenity
|
|||
|
||||
import "github.com/ncruces/zenity/internal/zenutil"
|
||||
|
||||
func getButtons(dialog, okcancel bool, opts options) (btns zenutil.Buttons) {
|
||||
func getButtons(dialog, okcancel bool, opts options) (btns zenutil.DialogButtons) {
|
||||
if !okcancel {
|
||||
opts.cancelLabel = nil
|
||||
opts.defaultCancel = false
|
||||
}
|
||||
|
||||
if opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil || (dialog != okcancel) {
|
||||
if opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil || dialog != okcancel {
|
||||
if opts.okLabel == nil {
|
||||
opts.okLabel = stringPtr("OK")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue