Added GTK+ indeterminate ProgressBars.

This commit is contained in:
Pietro Gagliardi 2014-03-12 17:31:13 -04:00
parent bf093d534c
commit 6ee8d96a6e
5 changed files with 61 additions and 2 deletions

View File

@ -216,3 +216,7 @@ func gtk_progress_bar_set_fraction(w *gtkWidget, percent int) {
p := C.gdouble(percent) / 100 p := C.gdouble(percent) / 100
C.gtk_progress_bar_set_fraction(togtkprogressbar(w), p) C.gtk_progress_bar_set_fraction(togtkprogressbar(w), p)
} }
func gtk_progress_bar_pulse(w *gtkWidget) {
C.gtk_progress_bar_pulse(togtkprogressbar(w))
}

View File

@ -27,6 +27,7 @@ func NewProgressBar() *ProgressBar {
// If percent is in the range [0,100], the progressBar shows that much percent complete. // If percent is in the range [0,100], the progressBar shows that much percent complete.
// If percent is -1, the ProgressBar is made indeterminate. // If percent is -1, the ProgressBar is made indeterminate.
// Otherwise, SetProgress panics. // Otherwise, SetProgress panics.
// TODO what happens if you repeatedly call SetProgress(-1)?
func (p *ProgressBar) SetProgress(percent int) { func (p *ProgressBar) SetProgress(percent int) {
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()

View File

@ -4,7 +4,7 @@
package ui package ui
import ( import (
// ... "time"
) )
type sysData struct { type sysData struct {
@ -12,6 +12,7 @@ type sysData struct {
widget *gtkWidget widget *gtkWidget
container *gtkWidget // for moving container *gtkWidget // for moving
pulse chan bool // for sysData.progressPulse()
} }
type classData struct { type classData struct {
@ -285,7 +286,56 @@ func (s *sysData) delete(index int) {
<-ret <-ret
} }
// With GTK+, we must manually pulse the indeterminate progressbar ourselves. This goroutine does that.
func (s *sysData) progressPulse() {
pulse := func() {
ret := make(chan struct{})
defer close(ret)
uitask <- func() {
gtk_progress_bar_pulse(s.widget)
ret <- struct{}{}
}
<-ret
}
var ticker *time.Ticker
var tickchan <-chan time.Time
// the default on Windows
const pulseRate = 30 * time.Millisecond
for {
select {
case start := <-s.pulse:
if start {
ticker = time.NewTicker(pulseRate)
tickchan = ticker.C
pulse() // start the pulse animation now, not 30ms later
} else {
if ticker != nil {
ticker.Stop()
}
ticker = nil
tickchan = nil
s.pulse <- true // notify sysData.setProgress()
}
case <-tickchan:
pulse()
}
}
}
func (s *sysData) setProgress(percent int) { func (s *sysData) setProgress(percent int) {
if s.pulse == nil {
s.pulse = make(chan bool)
go s.progressPulse()
}
if percent == -1 {
s.pulse <- true
return
}
s.pulse <- false
<-s.pulse // wait for sysData.progressPulse() to register that
ret := make(chan struct{}) ret := make(chan struct{})
defer close(ret) defer close(ret)
uitask <- func() { uitask <- func() {

View File

@ -129,8 +129,9 @@ func myMain() {
prog := 0 prog := 0
incButton := NewButton("Inc") incButton := NewButton("Inc")
decButton := NewButton("Dec") decButton := NewButton("Dec")
indetButton := NewButton("Indeterminate")
invalidButton := NewButton("Run Invalid Test") invalidButton := NewButton("Run Invalid Test")
sincdec := NewHorizontalStack(incButton, decButton, invalidButton) sincdec := NewHorizontalStack(incButton, decButton, indetButton, invalidButton)
password := NewPasswordEdit() password := NewPasswordEdit()
s0 := NewVerticalStack(s2, c, cb1, cb2, e, s3, pbar, sincdec, Space(), password) s0 := NewVerticalStack(s2, c, cb1, cb2, e, s3, pbar, sincdec, Space(), password)
s0.SetStretchy(8) s0.SetStretchy(8)
@ -224,6 +225,8 @@ mainloop:
prog = 0 prog = 0
} }
pbar.SetProgress(prog) pbar.SetProgress(prog)
case <-indetButton.Clicked:
pbar.SetProgress(-1)
case <-invalidButton.Clicked: case <-invalidButton.Clicked:
invalidTest(cb1, lb1, nil, nil) invalidTest(cb1, lb1, nil, nil)
} }

View File

@ -23,6 +23,7 @@ so I don't forget:
- change sysData.make() so it does not take the initial window text as an argument and instead have the respective Control/Window.make() call sysData.setText() expressly; this would allow me to remove the "no such concept of text" checks from the GTK+ and Mac OS X backends - change sysData.make() so it does not take the initial window text as an argument and instead have the respective Control/Window.make() call sysData.setText() expressly; this would allow me to remove the "no such concept of text" checks from the GTK+ and Mac OS X backends
important things: important things:
- GTK+ ProgressBar indeterminate animation is running like mad; pretty sure it's our idle task being evil
- because the main event loop is not called if initialization fails, it is presently impossible for MsgBoxError() to work if UI initialization fails; this basically means we cannot allow initializiation to fail on Mac OS X if we want to be able to report UI init failures to the user with one (which would be desirable, maybe (would violate Windows HIG?)) - because the main event loop is not called if initialization fails, it is presently impossible for MsgBoxError() to work if UI initialization fails; this basically means we cannot allow initializiation to fail on Mac OS X if we want to be able to report UI init failures to the user with one (which would be desirable, maybe (would violate Windows HIG?))
- figure out where to auto-place windows in Cocoa (also window coordinates are still not flipped properly so (0,0) on screen is the bottom-left) - figure out where to auto-place windows in Cocoa (also window coordinates are still not flipped properly so (0,0) on screen is the bottom-left)
- also provide a method to center windows; Cocoa provides one for us but - also provide a method to center windows; Cocoa provides one for us but