Restored the previous new API. I'm going to change it so that events are callbacks rather than using a window handler, but other than that... yeah.
This commit is contained in:
parent
990d50e9a1
commit
ffa1bbe0b9
11
area.go
11
area.go
|
@ -6,7 +6,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,7 +27,6 @@ import (
|
||||||
// to lead to trouble.
|
// to lead to trouble.
|
||||||
// [FOR FUTURE PLANNING Use TextArea instead, providing a TextAreaHandler.]
|
// [FOR FUTURE PLANNING Use TextArea instead, providing a TextAreaHandler.]
|
||||||
type Area struct {
|
type Area struct {
|
||||||
lock sync.Mutex
|
|
||||||
created bool
|
created bool
|
||||||
sysData *sysData
|
sysData *sysData
|
||||||
handler AreaHandler
|
handler AreaHandler
|
||||||
|
@ -298,9 +296,6 @@ func NewArea(width int, height int, handler AreaHandler) *Area {
|
||||||
// SetSize will also signal the entirety of the Area to be redrawn as in RepaintAll.
|
// SetSize will also signal the entirety of the Area to be redrawn as in RepaintAll.
|
||||||
// It panics if width or height is zero or negative.
|
// It panics if width or height is zero or negative.
|
||||||
func (a *Area) SetSize(width int, height int) {
|
func (a *Area) SetSize(width int, height int) {
|
||||||
a.lock.Lock()
|
|
||||||
defer a.lock.Unlock()
|
|
||||||
|
|
||||||
checkAreaSize(width, height, "Area.SetSize()")
|
checkAreaSize(width, height, "Area.SetSize()")
|
||||||
if a.created {
|
if a.created {
|
||||||
a.sysData.setAreaSize(width, height)
|
a.sysData.setAreaSize(width, height)
|
||||||
|
@ -313,9 +308,6 @@ func (a *Area) SetSize(width int, height int) {
|
||||||
// RepaintAll signals the entirety of the Area for redraw.
|
// RepaintAll signals the entirety of the Area for redraw.
|
||||||
// If called before the Window containing the Area is created, RepaintAll does nothing.
|
// If called before the Window containing the Area is created, RepaintAll does nothing.
|
||||||
func (a *Area) RepaintAll() {
|
func (a *Area) RepaintAll() {
|
||||||
a.lock.Lock()
|
|
||||||
defer a.lock.Unlock()
|
|
||||||
|
|
||||||
if !a.created {
|
if !a.created {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -323,9 +315,6 @@ func (a *Area) RepaintAll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Area) make(window *sysData) error {
|
func (a *Area) make(window *sysData) error {
|
||||||
a.lock.Lock()
|
|
||||||
defer a.lock.Unlock()
|
|
||||||
|
|
||||||
a.sysData.handler = a.handler
|
a.sysData.handler = a.handler
|
||||||
err := a.sysData.make(window)
|
err := a.sysData.make(window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
24
button.go
24
button.go
|
@ -2,18 +2,8 @@
|
||||||
|
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Button represents a clickable button with some text.
|
// A Button represents a clickable button with some text.
|
||||||
type Button struct {
|
type Button struct {
|
||||||
// Clicked gets a message when the button is clicked.
|
|
||||||
// You cannot change it once the Window containing the Button has been created.
|
|
||||||
// If you do not respond to this signal, nothing will happen.
|
|
||||||
Clicked chan struct{}
|
|
||||||
|
|
||||||
lock sync.Mutex
|
|
||||||
created bool
|
created bool
|
||||||
sysData *sysData
|
sysData *sysData
|
||||||
initText string
|
initText string
|
||||||
|
@ -24,15 +14,11 @@ func NewButton(text string) (b *Button) {
|
||||||
return &Button{
|
return &Button{
|
||||||
sysData: mksysdata(c_button),
|
sysData: mksysdata(c_button),
|
||||||
initText: text,
|
initText: text,
|
||||||
Clicked: newEvent(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetText sets the button's text.
|
// SetText sets the button's text.
|
||||||
func (b *Button) SetText(text string) {
|
func (b *Button) SetText(text string) {
|
||||||
b.lock.Lock()
|
|
||||||
defer b.lock.Unlock()
|
|
||||||
|
|
||||||
if b.created {
|
if b.created {
|
||||||
b.sysData.setText(text)
|
b.sysData.setText(text)
|
||||||
return
|
return
|
||||||
|
@ -42,9 +28,6 @@ func (b *Button) SetText(text string) {
|
||||||
|
|
||||||
// Text returns the button's text.
|
// Text returns the button's text.
|
||||||
func (b *Button) Text() string {
|
func (b *Button) Text() string {
|
||||||
b.lock.Lock()
|
|
||||||
defer b.lock.Unlock()
|
|
||||||
|
|
||||||
if b.created {
|
if b.created {
|
||||||
return b.sysData.text()
|
return b.sysData.text()
|
||||||
}
|
}
|
||||||
|
@ -52,10 +35,9 @@ func (b *Button) Text() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Button) make(window *sysData) error {
|
func (b *Button) make(window *sysData) error {
|
||||||
b.lock.Lock()
|
b.sysData.event = func() {
|
||||||
defer b.lock.Unlock()
|
window.winhandler.Event(Clicked, b)
|
||||||
|
}
|
||||||
b.sysData.event = b.Clicked
|
|
||||||
err := b.sysData.make(window)
|
err := b.sysData.make(window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -27,8 +27,9 @@ import "C"
|
||||||
func our_window_delete_event_callback(widget *C.GtkWidget, event *C.GdkEvent, what C.gpointer) C.gboolean {
|
func our_window_delete_event_callback(widget *C.GtkWidget, event *C.GdkEvent, what C.gpointer) C.gboolean {
|
||||||
// called when the user tries to close the window
|
// called when the user tries to close the window
|
||||||
s := (*sysData)(unsafe.Pointer(what))
|
s := (*sysData)(unsafe.Pointer(what))
|
||||||
s.signal()
|
b := false // TODO
|
||||||
return C.TRUE // do not close the window
|
s.close(&b)
|
||||||
|
return togbool(!b) // ! because TRUE means don't close
|
||||||
}
|
}
|
||||||
|
|
||||||
var window_delete_event_callback = C.GCallback(C.our_window_delete_event_callback)
|
var window_delete_event_callback = C.GCallback(C.our_window_delete_event_callback)
|
||||||
|
@ -52,7 +53,7 @@ var window_configure_event_callback = C.GCallback(C.our_window_configure_event_c
|
||||||
func our_button_clicked_callback(button *C.GtkButton, what C.gpointer) {
|
func our_button_clicked_callback(button *C.GtkButton, what C.gpointer) {
|
||||||
// called when the user clicks a button
|
// called when the user clicks a button
|
||||||
s := (*sysData)(unsafe.Pointer(what))
|
s := (*sysData)(unsafe.Pointer(what))
|
||||||
s.signal()
|
s.event()
|
||||||
}
|
}
|
||||||
|
|
||||||
var button_clicked_callback = C.GCallback(C.our_button_clicked_callback)
|
var button_clicked_callback = C.GCallback(C.our_button_clicked_callback)
|
||||||
|
|
20
checkbox.go
20
checkbox.go
|
@ -2,13 +2,8 @@
|
||||||
|
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Checkbox is a clickable square with a label. The square can be either checked or unchecked. Checkboxes start out unchecked.
|
// A Checkbox is a clickable square with a label. The square can be either checked or unchecked. Checkboxes start out unchecked.
|
||||||
type Checkbox struct {
|
type Checkbox struct {
|
||||||
lock sync.Mutex
|
|
||||||
created bool
|
created bool
|
||||||
sysData *sysData
|
sysData *sysData
|
||||||
initText string
|
initText string
|
||||||
|
@ -25,9 +20,6 @@ func NewCheckbox(text string) (c *Checkbox) {
|
||||||
|
|
||||||
// SetText sets the checkbox's text.
|
// SetText sets the checkbox's text.
|
||||||
func (c *Checkbox) SetText(text string) {
|
func (c *Checkbox) SetText(text string) {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
if c.created {
|
if c.created {
|
||||||
c.sysData.setText(text)
|
c.sysData.setText(text)
|
||||||
return
|
return
|
||||||
|
@ -37,9 +29,6 @@ func (c *Checkbox) SetText(text string) {
|
||||||
|
|
||||||
// Text returns the checkbox's text.
|
// Text returns the checkbox's text.
|
||||||
func (c *Checkbox) Text() string {
|
func (c *Checkbox) Text() string {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
if c.created {
|
if c.created {
|
||||||
return c.sysData.text()
|
return c.sysData.text()
|
||||||
}
|
}
|
||||||
|
@ -48,9 +37,6 @@ func (c *Checkbox) Text() string {
|
||||||
|
|
||||||
// SetChecked() changes the checked state of the Checkbox.
|
// SetChecked() changes the checked state of the Checkbox.
|
||||||
func (c *Checkbox) SetChecked(checked bool) {
|
func (c *Checkbox) SetChecked(checked bool) {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
if c.created {
|
if c.created {
|
||||||
c.sysData.setChecked(checked)
|
c.sysData.setChecked(checked)
|
||||||
return
|
return
|
||||||
|
@ -60,9 +46,6 @@ func (c *Checkbox) SetChecked(checked bool) {
|
||||||
|
|
||||||
// Checked() returns whether or not the Checkbox has been checked.
|
// Checked() returns whether or not the Checkbox has been checked.
|
||||||
func (c *Checkbox) Checked() bool {
|
func (c *Checkbox) Checked() bool {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
if c.created {
|
if c.created {
|
||||||
return c.sysData.isChecked()
|
return c.sysData.isChecked()
|
||||||
}
|
}
|
||||||
|
@ -70,9 +53,6 @@ func (c *Checkbox) Checked() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checkbox) make(window *sysData) error {
|
func (c *Checkbox) make(window *sysData) error {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
err := c.sysData.make(window)
|
err := c.sysData.make(window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
23
combobox.go
23
combobox.go
|
@ -4,12 +4,10 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Combobox is a drop-down list of items, of which at most one can be selected at any given time. You may optionally make the combobox editable to allow custom items. Initially, no item will be selected (and no text entered in an editable Combobox's entry field). What happens to the text shown in a Combobox if its width is too small is implementation-defined.
|
// A Combobox is a drop-down list of items, of which at most one can be selected at any given time. You may optionally make the combobox editable to allow custom items. Initially, no item will be selected (and no text entered in an editable Combobox's entry field). What happens to the text shown in a Combobox if its width is too small is implementation-defined.
|
||||||
type Combobox struct {
|
type Combobox struct {
|
||||||
lock sync.Mutex
|
|
||||||
created bool
|
created bool
|
||||||
sysData *sysData
|
sysData *sysData
|
||||||
initItems []string
|
initItems []string
|
||||||
|
@ -37,9 +35,6 @@ func NewEditableCombobox(items ...string) *Combobox {
|
||||||
// Append adds items to the end of the Combobox's list.
|
// Append adds items to the end of the Combobox's list.
|
||||||
// Append will panic if something goes wrong on platforms that do not abort themselves.
|
// Append will panic if something goes wrong on platforms that do not abort themselves.
|
||||||
func (c *Combobox) Append(what ...string) {
|
func (c *Combobox) Append(what ...string) {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
if c.created {
|
if c.created {
|
||||||
for _, s := range what {
|
for _, s := range what {
|
||||||
c.sysData.append(s)
|
c.sysData.append(s)
|
||||||
|
@ -52,9 +47,6 @@ func (c *Combobox) Append(what ...string) {
|
||||||
// InsertBefore inserts a new item in the Combobox before the item at the given position. It panics if the given index is out of bounds.
|
// InsertBefore inserts a new item in the Combobox before the item at the given position. It panics if the given index is out of bounds.
|
||||||
// InsertBefore will also panic if something goes wrong on platforms that do not abort themselves.
|
// InsertBefore will also panic if something goes wrong on platforms that do not abort themselves.
|
||||||
func (c *Combobox) InsertBefore(what string, before int) {
|
func (c *Combobox) InsertBefore(what string, before int) {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
var m []string
|
var m []string
|
||||||
|
|
||||||
if c.created {
|
if c.created {
|
||||||
|
@ -78,9 +70,6 @@ badrange:
|
||||||
|
|
||||||
// Delete removes the given item from the Combobox. It panics if the given index is out of bounds.
|
// Delete removes the given item from the Combobox. It panics if the given index is out of bounds.
|
||||||
func (c *Combobox) Delete(index int) {
|
func (c *Combobox) Delete(index int) {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
if c.created {
|
if c.created {
|
||||||
if index < 0 || index >= c.sysData.len() {
|
if index < 0 || index >= c.sysData.len() {
|
||||||
goto badrange
|
goto badrange
|
||||||
|
@ -99,9 +88,6 @@ badrange:
|
||||||
|
|
||||||
// Selection returns the current selection.
|
// Selection returns the current selection.
|
||||||
func (c *Combobox) Selection() string {
|
func (c *Combobox) Selection() string {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
if c.created {
|
if c.created {
|
||||||
return c.sysData.text()
|
return c.sysData.text()
|
||||||
}
|
}
|
||||||
|
@ -110,9 +96,6 @@ func (c *Combobox) Selection() string {
|
||||||
|
|
||||||
// SelectedIndex returns the index of the current selection in the Combobox. It returns -1 either if no selection was made or if text was manually entered in an editable Combobox.
|
// SelectedIndex returns the index of the current selection in the Combobox. It returns -1 either if no selection was made or if text was manually entered in an editable Combobox.
|
||||||
func (c *Combobox) SelectedIndex() int {
|
func (c *Combobox) SelectedIndex() int {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
if c.created {
|
if c.created {
|
||||||
return c.sysData.selectedIndex()
|
return c.sysData.selectedIndex()
|
||||||
}
|
}
|
||||||
|
@ -123,9 +106,6 @@ func (c *Combobox) SelectedIndex() int {
|
||||||
//
|
//
|
||||||
// On platforms for which this function may return an error, it panics if one is returned.
|
// On platforms for which this function may return an error, it panics if one is returned.
|
||||||
func (c *Combobox) Len() int {
|
func (c *Combobox) Len() int {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
if c.created {
|
if c.created {
|
||||||
return c.sysData.len()
|
return c.sysData.len()
|
||||||
}
|
}
|
||||||
|
@ -133,9 +113,6 @@ func (c *Combobox) Len() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Combobox) make(window *sysData) (err error) {
|
func (c *Combobox) make(window *sysData) (err error) {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
err = c.sysData.make(window)
|
err = c.sysData.make(window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -175,7 +175,6 @@ func releaseTextDC(hwnd _HWND, dc _HANDLE) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function runs on uitask; call the functions directly.
|
|
||||||
func (s *sysData) preferredSize(d *sysSizeData) (width int, height int) {
|
func (s *sysData) preferredSize(d *sysSizeData) (width int, height int) {
|
||||||
// the preferred size of an Area is its size
|
// the preferred size of an Area is its size
|
||||||
if stdDlgSizes[s.ctype].area {
|
if stdDlgSizes[s.ctype].area {
|
||||||
|
|
|
@ -24,9 +24,11 @@ func makeAppDelegate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//export appDelegate_windowShouldClose
|
//export appDelegate_windowShouldClose
|
||||||
func appDelegate_windowShouldClose(win C.id) {
|
func appDelegate_windowShouldClose(win C.id) C.BOOL {
|
||||||
sysData := getSysData(win)
|
sysData := getSysData(win)
|
||||||
sysData.signal()
|
b := false // TODO
|
||||||
|
sysData.close(&b)
|
||||||
|
return toBOOL(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export appDelegate_windowDidResize
|
//export appDelegate_windowDidResize
|
||||||
|
@ -42,13 +44,5 @@ func appDelegate_windowDidResize(win C.id) {
|
||||||
//export appDelegate_buttonClicked
|
//export appDelegate_buttonClicked
|
||||||
func appDelegate_buttonClicked(button C.id) {
|
func appDelegate_buttonClicked(button C.id) {
|
||||||
sysData := getSysData(button)
|
sysData := getSysData(button)
|
||||||
sysData.signal()
|
sysData.event()
|
||||||
}
|
|
||||||
|
|
||||||
//export appDelegate_applicationShouldTerminate
|
|
||||||
func appDelegate_applicationShouldTerminate() {
|
|
||||||
// asynchronous so as to return control to the event loop
|
|
||||||
go func() {
|
|
||||||
AppQuit <- struct{}{}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,8 +63,7 @@ extern NSRect dummyRect;
|
||||||
|
|
||||||
- (BOOL)windowShouldClose:(id)win
|
- (BOOL)windowShouldClose:(id)win
|
||||||
{
|
{
|
||||||
appDelegate_windowShouldClose(win);
|
return appDelegate_windowShouldClose(win);
|
||||||
return NO; // don't close
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowDidResize:(NSNotification *)n
|
- (void)windowDidResize:(NSNotification *)n
|
||||||
|
@ -79,13 +78,25 @@ extern NSRect dummyRect;
|
||||||
|
|
||||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
|
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
|
||||||
{
|
{
|
||||||
appDelegate_applicationShouldTerminate();
|
NSArray *windows;
|
||||||
|
NSUInteger i;
|
||||||
|
|
||||||
|
// try to close all windows
|
||||||
|
windows = [NSApp windows];
|
||||||
|
for (i = 0; i < [windows count]; i++)
|
||||||
|
[[windows objectAtIndex:i] performClose:self];
|
||||||
|
// if any windows are left, cancel
|
||||||
|
if ([[NSApp windows] count] != 0)
|
||||||
return NSTerminateCancel;
|
return NSTerminateCancel;
|
||||||
|
// no windows are left; we're good
|
||||||
|
return NSTerminateNow;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)chan
|
- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)data
|
||||||
{
|
{
|
||||||
dialog_send(chan, (uintptr_t) returnCode);
|
NSInteger *ret = (NSInteger *) data;
|
||||||
|
|
||||||
|
*ret = returnCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -127,7 +138,7 @@ void douitask(id appDelegate, void *p)
|
||||||
fp = [NSValue valueWithPointer:p];
|
fp = [NSValue valueWithPointer:p];
|
||||||
[appDelegate performSelectorOnMainThread:@selector(uitask:)
|
[appDelegate performSelectorOnMainThread:@selector(uitask:)
|
||||||
withObject:fp
|
withObject:fp
|
||||||
waitUntilDone:YES]; // wait since that's what we want the Go uitask() to do
|
waitUntilDone:YES]; // wait so we can properly drain the autorelease pool; on other platforms we wind up waiting anyway (since the main thread can only handle one thing at a time) so
|
||||||
[pool release];
|
[pool release];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
dialog.go
22
dialog.go
|
@ -2,8 +2,16 @@
|
||||||
|
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
|
// Response denotes a response from the user to a Dialog.
|
||||||
|
type Response uint
|
||||||
|
const (
|
||||||
|
NotDone Response = iota
|
||||||
|
OK
|
||||||
|
)
|
||||||
|
|
||||||
// sentinel (not nil so programmer errors don't go undetected)
|
// sentinel (not nil so programmer errors don't go undetected)
|
||||||
// this window is invalid and cannot be used directly
|
// this window is invalid and cannot be used directly
|
||||||
|
// notice the support it uses
|
||||||
var dialogWindow = new(Window)
|
var dialogWindow = new(Window)
|
||||||
|
|
||||||
// MsgBox displays an informational message box to the user with just an OK button.
|
// MsgBox displays an informational message box to the user with just an OK button.
|
||||||
|
@ -14,16 +22,16 @@ var dialogWindow = new(Window)
|
||||||
//
|
//
|
||||||
// See "On Dialogs" in the package overview for behavioral information.
|
// See "On Dialogs" in the package overview for behavioral information.
|
||||||
func MsgBox(primaryText string, secondaryText string) {
|
func MsgBox(primaryText string, secondaryText string) {
|
||||||
<-dialogWindow.msgBox(primaryText, secondaryText)
|
dialogWindow.msgBox(primaryText, secondaryText)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MsgBox is the Window method version of the package-scope function MsgBox.
|
// MsgBox is the Window method version of the package-scope function MsgBox.
|
||||||
// See that function's documentation and "On Dialogs" in the package overview for more information.
|
// See that function's documentation and "On Dialogs" in the package overview for more information.
|
||||||
func (w *Window) MsgBox(primaryText string, secondaryText string) (done chan struct{}) {
|
func (w *Window) MsgBox(primaryText string, secondaryText string) {
|
||||||
if !w.created {
|
if !w.created {
|
||||||
panic("parent window passed to Window.MsgBox() before it was created")
|
panic("parent window passed to Window.MsgBox() before it was created")
|
||||||
}
|
}
|
||||||
return w.msgBox(primaryText, secondaryText)
|
w.msgBox(primaryText, secondaryText)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MsgBoxError displays a message box to the user with just an OK button and an icon indicating an error.
|
// MsgBoxError displays a message box to the user with just an OK button and an icon indicating an error.
|
||||||
|
@ -31,14 +39,14 @@ func (w *Window) MsgBox(primaryText string, secondaryText string) (done chan str
|
||||||
//
|
//
|
||||||
// See "On Dialogs" in the package overview for more information.
|
// See "On Dialogs" in the package overview for more information.
|
||||||
func MsgBoxError(primaryText string, secondaryText string) {
|
func MsgBoxError(primaryText string, secondaryText string) {
|
||||||
<-dialogWindow.msgBoxError(primaryText, secondaryText)
|
dialogWindow.msgBoxError(primaryText, secondaryText)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MsgBoxError is the Window method version of the package-scope function MsgBoxError.
|
// MsgBoxError is the Window method version of the package-scope function MsgBoxError.
|
||||||
// See that function's documentation and "On Dialogs" in the package overview for more information.
|
// See that function's documentation and "On Dialogs" in the package overview for more information.
|
||||||
func (w *Window) MsgBoxError(primaryText string, secondaryText string) (done chan struct{}) {
|
func (w *Window) MsgBoxError(primaryText string, secondaryText string) {
|
||||||
if !w.created {
|
if !w.created {
|
||||||
panic("parent window passed to MsgBoxError() before it was created")
|
panic("parent window passed to Window.MsgBox() before it was created")
|
||||||
}
|
}
|
||||||
return w.msgBoxError(primaryText, secondaryText)
|
w.msgBoxError(primaryText, secondaryText)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,9 +18,7 @@ func dialog_send(pchan unsafe.Pointer, res C.intptr_t) {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func _msgBox(parent *Window, primarytext string, secondarytext string, style uintptr) chan int {
|
func _msgBox(parent *Window, primarytext string, secondarytext string, style uintptr) Response {
|
||||||
ret := make(chan int)
|
|
||||||
uitask <- func() {
|
|
||||||
var pwin C.id = nil
|
var pwin C.id = nil
|
||||||
|
|
||||||
if parent != dialogWindow {
|
if parent != dialogWindow {
|
||||||
|
@ -32,28 +31,19 @@ func _msgBox(parent *Window, primarytext string, secondarytext string, style uin
|
||||||
}
|
}
|
||||||
switch style {
|
switch style {
|
||||||
case 0: // normal
|
case 0: // normal
|
||||||
C.msgBox(pwin, primary, secondary, unsafe.Pointer(&ret))
|
C.msgBox(pwin, primary, secondary)
|
||||||
|
return OK
|
||||||
case 1: // error
|
case 1: // error
|
||||||
C.msgBoxError(pwin, primary, secondary, unsafe.Pointer(&ret))
|
C.msgBoxError(pwin, primary, secondary)
|
||||||
|
return OK
|
||||||
}
|
}
|
||||||
}
|
panic(fmt.Errorf("unknown message box style %d\n", style))
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) msgBox(primarytext string, secondarytext string) (done chan struct{}) {
|
func (w *Window) msgBox(primarytext string, secondarytext string) {
|
||||||
done = make(chan struct{})
|
_msgBox(w, primarytext, secondarytext, 0)
|
||||||
go func() {
|
|
||||||
<-_msgBox(w, primarytext, secondarytext, 0)
|
|
||||||
done <- struct{}{}
|
|
||||||
}()
|
|
||||||
return done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) msgBoxError(primarytext string, secondarytext string) (done chan struct{}) {
|
func (w *Window) msgBoxError(primarytext string, secondarytext string) {
|
||||||
done = make(chan struct{})
|
_msgBox(w, primarytext, secondarytext, 1)
|
||||||
go func() {
|
|
||||||
<-_msgBox(w, primarytext, secondarytext, 1)
|
|
||||||
done <- struct{}{}
|
|
||||||
}()
|
|
||||||
return done
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#define to(T, x) ((T *) (x))
|
#define to(T, x) ((T *) (x))
|
||||||
#define toNSWindow(x) to(NSWindow, (x))
|
#define toNSWindow(x) to(NSWindow, (x))
|
||||||
|
|
||||||
static void alert(id parent, NSString *primary, NSString *secondary, NSAlertStyle style, void *chan)
|
static intptr_t alert(id parent, NSString *primary, NSString *secondary, NSAlertStyle style)
|
||||||
{
|
{
|
||||||
NSAlert *box;
|
NSAlert *box;
|
||||||
|
|
||||||
|
@ -21,20 +21,25 @@ static void alert(id parent, NSString *primary, NSString *secondary, NSAlertStyl
|
||||||
// TODO is there a named constant? will also need to be changed when we add different dialog types
|
// TODO is there a named constant? will also need to be changed when we add different dialog types
|
||||||
[box addButtonWithTitle:@"OK"];
|
[box addButtonWithTitle:@"OK"];
|
||||||
if (parent == nil)
|
if (parent == nil)
|
||||||
dialog_send(chan, (intptr_t) [box runModal]);
|
return (intptr_t) [box runModal];
|
||||||
else
|
else {
|
||||||
|
NSInteger ret;
|
||||||
|
|
||||||
[box beginSheetModalForWindow:toNSWindow(parent)
|
[box beginSheetModalForWindow:toNSWindow(parent)
|
||||||
modalDelegate:[NSApp delegate]
|
modalDelegate:[NSApp delegate]
|
||||||
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
|
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
|
||||||
contextInfo:chan];
|
contextInfo:&ret];
|
||||||
|
// TODO
|
||||||
|
return (intptr_t) ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void msgBox(id parent, id primary, id secondary, void *chan)
|
void msgBox(id parent, id primary, id secondary)
|
||||||
{
|
{
|
||||||
alert(parent, (NSString *) primary, (NSString *) secondary, NSInformationalAlertStyle, chan);
|
alert(parent, (NSString *) primary, (NSString *) secondary, NSInformationalAlertStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void msgBoxError(id parent, id primary, id secondary, void *chan)
|
void msgBoxError(id parent, id primary, id secondary)
|
||||||
{
|
{
|
||||||
alert(parent, (NSString *) primary, (NSString *) secondary, NSCriticalAlertStyle, chan);
|
alert(parent, (NSString *) primary, (NSString *) secondary, NSCriticalAlertStyle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// #include "gtk_unix.h"
|
// #include "gtk_unix.h"
|
||||||
// extern void our_dialog_response_callback(GtkDialog *, gint, gpointer);
|
|
||||||
// /* because cgo seems to choke on ... */
|
// /* because cgo seems to choke on ... */
|
||||||
// /* parent will be NULL if there is no parent, so this is fine */
|
// /* parent will be NULL if there is no parent, so this is fine */
|
||||||
// static inline GtkWidget *gtkNewMsgBox(GtkWindow *parent, GtkMessageType type, GtkButtonsType buttons, char *title, char *text)
|
// static inline GtkWidget *gtkNewMsgBox(GtkWindow *parent, GtkMessageType type, GtkButtonsType buttons, char *title, char *text)
|
||||||
|
@ -23,6 +22,11 @@ import (
|
||||||
// }
|
// }
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
// the actual type of these constants is GtkResponseType but gtk_dialog_run() returns a gint
|
||||||
|
var dialogResponses = map[C.gint]Response{
|
||||||
|
C.GTK_RESPONSE_OK: OK,
|
||||||
|
}
|
||||||
|
|
||||||
// dialog performs the bookkeeping involved for having a GtkDialog behave the way we want.
|
// dialog performs the bookkeeping involved for having a GtkDialog behave the way we want.
|
||||||
type dialog struct {
|
type dialog struct {
|
||||||
parent *Window
|
parent *Window
|
||||||
|
@ -30,13 +34,12 @@ type dialog struct {
|
||||||
hadgroup C.gboolean
|
hadgroup C.gboolean
|
||||||
prevgroup *C.GtkWindowGroup
|
prevgroup *C.GtkWindowGroup
|
||||||
newgroup *C.GtkWindowGroup
|
newgroup *C.GtkWindowGroup
|
||||||
result chan int
|
result Response
|
||||||
}
|
}
|
||||||
|
|
||||||
func mkdialog(parent *Window) *dialog {
|
func mkdialog(parent *Window) *dialog {
|
||||||
return &dialog{
|
return &dialog{
|
||||||
parent: parent,
|
parent: parent,
|
||||||
result: make(chan int),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,36 +62,6 @@ func (d *dialog) prepare() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dialog) run(mk func() *C.GtkWidget) {
|
|
||||||
d.prepare()
|
|
||||||
box := mk()
|
|
||||||
if d.parent == dialogWindow {
|
|
||||||
go func() {
|
|
||||||
res := make(chan C.gint)
|
|
||||||
defer close(res)
|
|
||||||
uitask <- func() {
|
|
||||||
r := C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(box)))
|
|
||||||
d.cleanup(box)
|
|
||||||
res <- r
|
|
||||||
}
|
|
||||||
d.send(<-res)
|
|
||||||
}()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// otherwise just connect the delete signal
|
|
||||||
g_signal_connect_pointer(box, "response", dialog_response_callback, unsafe.Pointer(d))
|
|
||||||
C.gtk_widget_show_all(box)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export our_dialog_response_callback
|
|
||||||
func our_dialog_response_callback(box *C.GtkDialog, res C.gint, data C.gpointer) {
|
|
||||||
d := (*dialog)(unsafe.Pointer(data))
|
|
||||||
d.cleanup((*C.GtkWidget)(unsafe.Pointer(box)))
|
|
||||||
go d.send(res) // send on another goroutine, like everything else
|
|
||||||
}
|
|
||||||
|
|
||||||
var dialog_response_callback = C.GCallback(C.our_dialog_response_callback)
|
|
||||||
|
|
||||||
func (d *dialog) cleanup(box *C.GtkWidget) {
|
func (d *dialog) cleanup(box *C.GtkWidget) {
|
||||||
// have to explicitly close the dialog box, otherwise wacky things will happen
|
// have to explicitly close the dialog box, otherwise wacky things will happen
|
||||||
C.gtk_widget_destroy(box)
|
C.gtk_widget_destroy(box)
|
||||||
|
@ -101,15 +74,16 @@ func (d *dialog) cleanup(box *C.GtkWidget) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dialog) send(res C.gint) {
|
func (d *dialog) run(mk func() *C.GtkWidget) {
|
||||||
// this is where processing would go
|
d.prepare()
|
||||||
d.result <- int(res)
|
box := mk()
|
||||||
|
r := C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(box)))
|
||||||
|
d.cleanup(box)
|
||||||
|
d.result = dialogResponses[r]
|
||||||
}
|
}
|
||||||
|
|
||||||
func _msgBox(parent *Window, primarytext string, secondarytext string, msgtype C.GtkMessageType, buttons C.GtkButtonsType) (result chan int) {
|
func _msgBox(parent *Window, primarytext string, secondarytext string, msgtype C.GtkMessageType, buttons C.GtkButtonsType) (response Response) {
|
||||||
result = make(chan int)
|
|
||||||
d := mkdialog(parent)
|
d := mkdialog(parent)
|
||||||
uitask <- func() {
|
|
||||||
cprimarytext := C.CString(primarytext)
|
cprimarytext := C.CString(primarytext)
|
||||||
defer C.free(unsafe.Pointer(cprimarytext))
|
defer C.free(unsafe.Pointer(cprimarytext))
|
||||||
csecondarytext := (*C.char)(nil)
|
csecondarytext := (*C.char)(nil)
|
||||||
|
@ -120,24 +94,13 @@ func _msgBox(parent *Window, primarytext string, secondarytext string, msgtype C
|
||||||
d.run(func() *C.GtkWidget {
|
d.run(func() *C.GtkWidget {
|
||||||
return C.gtkNewMsgBox(d.pwin, msgtype, buttons, cprimarytext, csecondarytext)
|
return C.gtkNewMsgBox(d.pwin, msgtype, buttons, cprimarytext, csecondarytext)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
return d.result
|
return d.result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) msgBox(primarytext string, secondarytext string) (done chan struct{}) {
|
func (w *Window) msgBox(primarytext string, secondarytext string) {
|
||||||
done = make(chan struct{})
|
_msgBox(w, primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_OTHER), C.GtkButtonsType(C.GTK_BUTTONS_OK))
|
||||||
go func() {
|
|
||||||
<-_msgBox(w, primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_OTHER), C.GtkButtonsType(C.GTK_BUTTONS_OK))
|
|
||||||
done <- struct{}{}
|
|
||||||
}()
|
|
||||||
return done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) msgBoxError(primarytext string, secondarytext string) (done chan struct{}) {
|
func (w *Window) msgBoxError(primarytext string, secondarytext string) {
|
||||||
done = make(chan struct{})
|
_msgBox(w, primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_ERROR), C.GtkButtonsType(C.GTK_BUTTONS_OK))
|
||||||
go func() {
|
|
||||||
<-_msgBox(w, primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_ERROR), C.GtkButtonsType(C.GTK_BUTTONS_OK))
|
|
||||||
done <- struct{}{}
|
|
||||||
}()
|
|
||||||
return done
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,11 @@ var (
|
||||||
_messageBox = user32.NewProc("MessageBoxW")
|
_messageBox = user32.NewProc("MessageBoxW")
|
||||||
)
|
)
|
||||||
|
|
||||||
func _msgBox(parent *Window, primarytext string, secondarytext string, uType uint32) (result chan int) {
|
var dialogResponses = map[uintptr]Response{
|
||||||
|
_IDOK: OK,
|
||||||
|
}
|
||||||
|
|
||||||
|
func _msgBox(parent *Window, primarytext string, secondarytext string, uType uint32) Response {
|
||||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa511267.aspx says "Use task dialogs whenever appropriate to achieve a consistent look and layout. Task dialogs require Windows Vista® or later, so they aren't suitable for earlier versions of Windows. If you must use a message box, separate the main instruction from the supplemental instruction with two line breaks."
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa511267.aspx says "Use task dialogs whenever appropriate to achieve a consistent look and layout. Task dialogs require Windows Vista® or later, so they aren't suitable for earlier versions of Windows. If you must use a message box, separate the main instruction from the supplemental instruction with two line breaks."
|
||||||
text := primarytext
|
text := primarytext
|
||||||
if secondarytext != "" {
|
if secondarytext != "" {
|
||||||
|
@ -26,43 +30,21 @@ func _msgBox(parent *Window, primarytext string, secondarytext string, uType uin
|
||||||
} else {
|
} else {
|
||||||
uType |= _MB_TASKMODAL // make modal to every window in the program (they're all windows of the uitask, which is a single thread)
|
uType |= _MB_TASKMODAL // make modal to every window in the program (they're all windows of the uitask, which is a single thread)
|
||||||
}
|
}
|
||||||
retchan := make(chan int)
|
r1, _, err := _messageBox.Call(
|
||||||
go func() {
|
|
||||||
ret := make(chan uiret)
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- &uimsg{
|
|
||||||
call: _messageBox,
|
|
||||||
p: []uintptr{
|
|
||||||
uintptr(parenthwnd),
|
uintptr(parenthwnd),
|
||||||
utf16ToArg(ptext),
|
utf16ToArg(ptext),
|
||||||
utf16ToArg(ptitle),
|
utf16ToArg(ptitle),
|
||||||
uintptr(uType),
|
uintptr(uType))
|
||||||
},
|
if r1 == 0 { // failure
|
||||||
ret: ret,
|
panic(fmt.Sprintf("error displaying message box to user: %v\nstyle: 0x%08X\ntitle: %q\ntext:\n%s", err, uType, os.Args[0], text))
|
||||||
}
|
}
|
||||||
r := <-ret
|
return dialogResponses[r1]
|
||||||
if r.ret == 0 { // failure
|
|
||||||
panic(fmt.Sprintf("error displaying message box to user: %v\nstyle: 0x%08X\ntitle: %q\ntext:\n%s", r.err, uType, os.Args[0], text))
|
|
||||||
}
|
|
||||||
retchan <- int(r.ret)
|
|
||||||
}()
|
|
||||||
return retchan
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) msgBox(primarytext string, secondarytext string) (done chan struct{}) {
|
func (w *Window) msgBox(primarytext string, secondarytext string) {
|
||||||
done = make(chan struct{})
|
_msgBox(w, primarytext, secondarytext, _MB_OK)
|
||||||
go func() {
|
|
||||||
<-_msgBox(w, primarytext, secondarytext, _MB_OK)
|
|
||||||
done <- struct{}{}
|
|
||||||
}()
|
|
||||||
return done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) msgBoxError(primarytext string, secondarytext string) (done chan struct{}) {
|
func (w *Window) msgBoxError(primarytext string, secondarytext string) {
|
||||||
done = make(chan struct{})
|
_msgBox(w, primarytext, secondarytext, _MB_OK|_MB_ICONERROR)
|
||||||
go func() {
|
|
||||||
<-_msgBox(w, primarytext, secondarytext, _MB_OK|_MB_ICONERROR)
|
|
||||||
done <- struct{}{}
|
|
||||||
}()
|
|
||||||
return done
|
|
||||||
}
|
}
|
||||||
|
|
11
grid.go
11
grid.go
|
@ -4,7 +4,6 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Grid arranges Controls in a two-dimensional grid.
|
// A Grid arranges Controls in a two-dimensional grid.
|
||||||
|
@ -16,7 +15,6 @@ import (
|
||||||
// A stretchy Control implicitly fills its cell.
|
// A stretchy Control implicitly fills its cell.
|
||||||
// All cooridnates in a Grid are given in (row,column) form with (0,0) being the top-left cell.
|
// All cooridnates in a Grid are given in (row,column) form with (0,0) being the top-left cell.
|
||||||
type Grid struct {
|
type Grid struct {
|
||||||
lock sync.Mutex
|
|
||||||
created bool
|
created bool
|
||||||
controls [][]Control
|
controls [][]Control
|
||||||
filling [][]bool
|
filling [][]bool
|
||||||
|
@ -69,9 +67,6 @@ func NewGrid(nPerRow int, controls ...Control) *Grid {
|
||||||
// This function cannot be called after the Window that contains the Grid has been created.
|
// This function cannot be called after the Window that contains the Grid has been created.
|
||||||
// It panics if the given coordinate is invalid.
|
// It panics if the given coordinate is invalid.
|
||||||
func (g *Grid) SetFilling(row int, column int) {
|
func (g *Grid) SetFilling(row int, column int) {
|
||||||
g.lock.Lock()
|
|
||||||
defer g.lock.Unlock()
|
|
||||||
|
|
||||||
if g.created {
|
if g.created {
|
||||||
panic(fmt.Errorf("Grid.SetFilling() called after window create"))
|
panic(fmt.Errorf("Grid.SetFilling() called after window create"))
|
||||||
}
|
}
|
||||||
|
@ -87,9 +82,6 @@ func (g *Grid) SetFilling(row int, column int) {
|
||||||
// This function cannot be called after the Window that contains the Grid has been created.
|
// This function cannot be called after the Window that contains the Grid has been created.
|
||||||
// It panics if the given coordinate is invalid.
|
// It panics if the given coordinate is invalid.
|
||||||
func (g *Grid) SetStretchy(row int, column int) {
|
func (g *Grid) SetStretchy(row int, column int) {
|
||||||
g.lock.Lock()
|
|
||||||
defer g.lock.Unlock()
|
|
||||||
|
|
||||||
if g.created {
|
if g.created {
|
||||||
panic(fmt.Errorf("Grid.SetFilling() called after window create"))
|
panic(fmt.Errorf("Grid.SetFilling() called after window create"))
|
||||||
}
|
}
|
||||||
|
@ -102,9 +94,6 @@ func (g *Grid) SetStretchy(row int, column int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Grid) make(window *sysData) error {
|
func (g *Grid) make(window *sysData) error {
|
||||||
g.lock.Lock()
|
|
||||||
defer g.lock.Unlock()
|
|
||||||
|
|
||||||
// commit filling for the stretchy control now (see SetStretchy() above)
|
// commit filling for the stretchy control now (see SetStretchy() above)
|
||||||
if g.stretchyrow != -1 && g.stretchycol != -1 {
|
if g.stretchyrow != -1 && g.stretchycol != -1 {
|
||||||
g.filling[g.stretchyrow][g.stretchycol] = true
|
g.filling[g.stretchyrow][g.stretchycol] = true
|
||||||
|
|
61
init.go
61
init.go
|
@ -2,28 +2,53 @@
|
||||||
|
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
// Go sets up the UI environment and runs main in a goroutine.
|
import (
|
||||||
// If initialization fails, Go returns an error and main is not called.
|
"runtime"
|
||||||
// Otherwise, Go does not return to its caller until main does, at which point it returns nil.
|
)
|
||||||
// After it returns, you cannot call future ui functions/methods meaningfully.
|
|
||||||
|
// Go sets up the UI environment and pulses Ready.
|
||||||
|
// If initialization fails, Go returns an error and Ready is not pulsed.
|
||||||
|
// Otherwise, Go does not return to its caller until Stop is pulsed, at which point Go() will return nil.
|
||||||
|
// After Go() returns, you cannot call future ui functions/methods meaningfully.
|
||||||
|
// Pulsing Stop will cause Go() to return immediately; the programmer is responsible for cleaning up (for instance, hiding open Windows) beforehand.
|
||||||
//
|
//
|
||||||
// It is not safe to call ui.Go() in a goroutine. It must be called directly from main().
|
// It is not safe to call ui.Go() in a goroutine. It must be called directly from main(). This means if your code calls other code-modal servers (such as http.ListenAndServe()), they must be run from goroutines. (This is due to limitations in various OSs, such as Mac OS X.)
|
||||||
//
|
//
|
||||||
// This model is undesirable, but Cocoa limitations require it.
|
// Go() does not process the command line for flags (that is, it does not call flag.Parse()), nor does package ui add any of the underlying toolkit's supported command-line flags.
|
||||||
//
|
|
||||||
// Go does not process the command line for flags (that is, it does not call flag.Parse()), nor does package ui add any of the underlying toolkit's supported command-line flags.
|
|
||||||
// If you must, and if the toolkit also has environment variable equivalents to these flags (for instance, GTK+), use those instead.
|
// If you must, and if the toolkit also has environment variable equivalents to these flags (for instance, GTK+), use those instead.
|
||||||
func Go(main func()) error {
|
func Go() error {
|
||||||
return ui(main)
|
runtime.LockOSThread()
|
||||||
|
if err := uiinit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
Ready <- struct{}{}
|
||||||
|
close(Ready)
|
||||||
|
ui()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppQuit is pulsed when the user decides to quit the program if their operating system provides a facility for quitting an entire application, rather than merely close all windows (for instance, Mac OS X via the Dock icon).
|
// Ready is pulsed when Go() is ready to begin accepting requests to the safe methods.
|
||||||
// You should assign one of your Windows's Closing to this variable so the user choosing to quit the application is treated the same as closing that window.
|
// Go() will wait for something to receive on Ready, then Ready will be closed.
|
||||||
// If you do not respond to this signal, nothing will happen; regardless of whether or not you respond to this signal, the application will not quit.
|
var Ready = make(chan struct{})
|
||||||
// Do not merely check this channel alone; it is not guaranteed to be pulsed on all systems or in all conditions.
|
|
||||||
var AppQuit chan struct{}
|
|
||||||
|
|
||||||
func init() {
|
// Stop should be pulsed when you are ready for Go() to return.
|
||||||
// don't expose this in the documentation
|
// Pulsing Stop will cause Go() to return immediately; the programmer is responsible for cleaning up (for instance, hiding open Windows) beforehand.
|
||||||
AppQuit = newEvent()
|
// Do not pulse Stop more than once.
|
||||||
|
var Stop = make(chan struct{})
|
||||||
|
|
||||||
|
// This function is a simple helper functionn that basically pushes the effect of a function call for later. This allows the selected safe Window methods to be safe.
|
||||||
|
// TODO make sure this acts sanely if called from uitask itself
|
||||||
|
func touitask(f func()) {
|
||||||
|
done := make(chan struct{})
|
||||||
|
defer close(done)
|
||||||
|
go func() { // to avoid locking uitask itself
|
||||||
|
done2 := make(chan struct{}) // make the chain uitask <- f <- uitask to avoid deadlocks
|
||||||
|
defer close(done2)
|
||||||
|
uitask <- func() {
|
||||||
|
f()
|
||||||
|
done2 <- struct{}{}
|
||||||
|
}
|
||||||
|
done <- <-done2
|
||||||
|
}()
|
||||||
|
<-done
|
||||||
}
|
}
|
||||||
|
|
14
label.go
14
label.go
|
@ -2,16 +2,11 @@
|
||||||
|
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Label is a static line of text used to mark other controls.
|
// A Label is a static line of text used to mark other controls.
|
||||||
// Label text is drawn on a single line; text that does not fit is truncated.
|
// Label text is drawn on a single line; text that does not fit is truncated.
|
||||||
// A Label can appear in one of two places: bound to a control or standalone.
|
// A Label can appear in one of two places: bound to a control or standalone.
|
||||||
// This determines the vertical alignment of the label.
|
// This determines the vertical alignment of the label.
|
||||||
type Label struct {
|
type Label struct {
|
||||||
lock sync.Mutex
|
|
||||||
created bool
|
created bool
|
||||||
sysData *sysData
|
sysData *sysData
|
||||||
initText string
|
initText string
|
||||||
|
@ -39,9 +34,6 @@ func NewStandaloneLabel(text string) *Label {
|
||||||
|
|
||||||
// SetText sets the Label's text.
|
// SetText sets the Label's text.
|
||||||
func (l *Label) SetText(text string) {
|
func (l *Label) SetText(text string) {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
if l.created {
|
if l.created {
|
||||||
l.sysData.setText(text)
|
l.sysData.setText(text)
|
||||||
return
|
return
|
||||||
|
@ -51,9 +43,6 @@ func (l *Label) SetText(text string) {
|
||||||
|
|
||||||
// Text returns the Label's text.
|
// Text returns the Label's text.
|
||||||
func (l *Label) Text() string {
|
func (l *Label) Text() string {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
if l.created {
|
if l.created {
|
||||||
return l.sysData.text()
|
return l.sysData.text()
|
||||||
}
|
}
|
||||||
|
@ -61,9 +50,6 @@ func (l *Label) Text() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Label) make(window *sysData) error {
|
func (l *Label) make(window *sysData) error {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
l.sysData.alternate = l.standalone
|
l.sysData.alternate = l.standalone
|
||||||
err := l.sysData.make(window)
|
err := l.sysData.make(window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
14
lineedit.go
14
lineedit.go
|
@ -2,13 +2,8 @@
|
||||||
|
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A LineEdit is a control which allows you to enter a single line of text.
|
// A LineEdit is a control which allows you to enter a single line of text.
|
||||||
type LineEdit struct {
|
type LineEdit struct {
|
||||||
lock sync.Mutex
|
|
||||||
created bool
|
created bool
|
||||||
sysData *sysData
|
sysData *sysData
|
||||||
initText string
|
initText string
|
||||||
|
@ -33,9 +28,6 @@ func NewPasswordEdit() *LineEdit {
|
||||||
|
|
||||||
// SetText sets the LineEdit's text.
|
// SetText sets the LineEdit's text.
|
||||||
func (l *LineEdit) SetText(text string) {
|
func (l *LineEdit) SetText(text string) {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
if l.created {
|
if l.created {
|
||||||
l.sysData.setText(text)
|
l.sysData.setText(text)
|
||||||
return
|
return
|
||||||
|
@ -45,9 +37,6 @@ func (l *LineEdit) SetText(text string) {
|
||||||
|
|
||||||
// Text returns the LineEdit's text.
|
// Text returns the LineEdit's text.
|
||||||
func (l *LineEdit) Text() string {
|
func (l *LineEdit) Text() string {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
if l.created {
|
if l.created {
|
||||||
return l.sysData.text()
|
return l.sysData.text()
|
||||||
}
|
}
|
||||||
|
@ -55,9 +44,6 @@ func (l *LineEdit) Text() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LineEdit) make(window *sysData) error {
|
func (l *LineEdit) make(window *sysData) error {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
l.sysData.alternate = l.password
|
l.sysData.alternate = l.password
|
||||||
err := l.sysData.make(window)
|
err := l.sysData.make(window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
23
listbox.go
23
listbox.go
|
@ -4,7 +4,6 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Listbox is a vertical list of items, of which either at most one or any number of items can be selected at any given time.
|
// A Listbox is a vertical list of items, of which either at most one or any number of items can be selected at any given time.
|
||||||
|
@ -12,7 +11,6 @@ import (
|
||||||
// For information on scrollbars, see "Scrollbars" in the Overview.
|
// For information on scrollbars, see "Scrollbars" in the Overview.
|
||||||
// Due to implementation issues, the presence of horizontal scrollbars is currently implementation-defined.
|
// Due to implementation issues, the presence of horizontal scrollbars is currently implementation-defined.
|
||||||
type Listbox struct {
|
type Listbox struct {
|
||||||
lock sync.Mutex
|
|
||||||
created bool
|
created bool
|
||||||
sysData *sysData
|
sysData *sysData
|
||||||
initItems []string
|
initItems []string
|
||||||
|
@ -40,9 +38,6 @@ func NewMultiSelListbox(items ...string) *Listbox {
|
||||||
// Append adds items to the end of the Listbox's list.
|
// Append adds items to the end of the Listbox's list.
|
||||||
// Append will panic if something goes wrong on platforms that do not abort themselves.
|
// Append will panic if something goes wrong on platforms that do not abort themselves.
|
||||||
func (l *Listbox) Append(what ...string) {
|
func (l *Listbox) Append(what ...string) {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
if l.created {
|
if l.created {
|
||||||
for _, s := range what {
|
for _, s := range what {
|
||||||
l.sysData.append(s)
|
l.sysData.append(s)
|
||||||
|
@ -55,9 +50,6 @@ func (l *Listbox) Append(what ...string) {
|
||||||
// InsertBefore inserts a new item in the Listbox before the item at the given position. It panics if the given index is out of bounds.
|
// InsertBefore inserts a new item in the Listbox before the item at the given position. It panics if the given index is out of bounds.
|
||||||
// InsertBefore will also panic if something goes wrong on platforms that do not abort themselves.
|
// InsertBefore will also panic if something goes wrong on platforms that do not abort themselves.
|
||||||
func (l *Listbox) InsertBefore(what string, before int) {
|
func (l *Listbox) InsertBefore(what string, before int) {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
var m []string
|
var m []string
|
||||||
|
|
||||||
if l.created {
|
if l.created {
|
||||||
|
@ -81,9 +73,6 @@ badrange:
|
||||||
|
|
||||||
// Delete removes the given item from the Listbox. It panics if the given index is out of bounds.
|
// Delete removes the given item from the Listbox. It panics if the given index is out of bounds.
|
||||||
func (l *Listbox) Delete(index int) {
|
func (l *Listbox) Delete(index int) {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
if l.created {
|
if l.created {
|
||||||
if index < 0 || index >= l.sysData.len() {
|
if index < 0 || index >= l.sysData.len() {
|
||||||
goto badrange
|
goto badrange
|
||||||
|
@ -102,9 +91,6 @@ badrange:
|
||||||
|
|
||||||
// Selection returns a list of strings currently selected in the Listbox, or an empty list if none have been selected. This list will have at most one item on a single-selection Listbox.
|
// Selection returns a list of strings currently selected in the Listbox, or an empty list if none have been selected. This list will have at most one item on a single-selection Listbox.
|
||||||
func (l *Listbox) Selection() []string {
|
func (l *Listbox) Selection() []string {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
if l.created {
|
if l.created {
|
||||||
return l.sysData.selectedTexts()
|
return l.sysData.selectedTexts()
|
||||||
}
|
}
|
||||||
|
@ -113,9 +99,6 @@ func (l *Listbox) Selection() []string {
|
||||||
|
|
||||||
// SelectedIndices returns a list of the currently selected indexes in the Listbox, or an empty list if none have been selected. This list will have at most one item on a single-selection Listbox.
|
// SelectedIndices returns a list of the currently selected indexes in the Listbox, or an empty list if none have been selected. This list will have at most one item on a single-selection Listbox.
|
||||||
func (l *Listbox) SelectedIndices() []int {
|
func (l *Listbox) SelectedIndices() []int {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
if l.created {
|
if l.created {
|
||||||
return l.sysData.selectedIndices()
|
return l.sysData.selectedIndices()
|
||||||
}
|
}
|
||||||
|
@ -126,9 +109,6 @@ func (l *Listbox) SelectedIndices() []int {
|
||||||
//
|
//
|
||||||
// On platforms for which this function may return an error, it panics if one is returned.
|
// On platforms for which this function may return an error, it panics if one is returned.
|
||||||
func (l *Listbox) Len() int {
|
func (l *Listbox) Len() int {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
if l.created {
|
if l.created {
|
||||||
return l.sysData.len()
|
return l.sysData.len()
|
||||||
}
|
}
|
||||||
|
@ -136,9 +116,6 @@ func (l *Listbox) Len() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Listbox) make(window *sysData) (err error) {
|
func (l *Listbox) make(window *sysData) (err error) {
|
||||||
l.lock.Lock()
|
|
||||||
defer l.lock.Unlock()
|
|
||||||
|
|
||||||
err = l.sysData.make(window)
|
err = l.sysData.make(window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -68,8 +68,8 @@ extern void breakMainLoop(void);
|
||||||
extern void cocoaMainLoop(void);
|
extern void cocoaMainLoop(void);
|
||||||
|
|
||||||
/* dialog_darwin.m */
|
/* dialog_darwin.m */
|
||||||
extern void msgBox(id, id, id, void *);
|
extern void msgBox(id, id, id);
|
||||||
extern void msgBoxError(id, id, id, void *);
|
extern void msgBoxError(id, id, id);
|
||||||
|
|
||||||
/* listbox_darwin.m */
|
/* listbox_darwin.m */
|
||||||
extern id toListboxItem(id, id);
|
extern id toListboxItem(id, id);
|
||||||
|
|
|
@ -2,16 +2,11 @@
|
||||||
|
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A ProgressBar is a horizontal rectangle that fills up from left to right to indicate the progress of a long-running task.
|
// A ProgressBar is a horizontal rectangle that fills up from left to right to indicate the progress of a long-running task.
|
||||||
// This progress is represented by an integer within the range [0,100], representing a percentage.
|
// This progress is represented by an integer within the range [0,100], representing a percentage.
|
||||||
// Alternatively, a progressbar can show an animation indicating that progress is being made but how much is indeterminate.
|
// Alternatively, a progressbar can show an animation indicating that progress is being made but how much is indeterminate.
|
||||||
// Newly-created ProgressBars default to showing 0% progress.
|
// Newly-created ProgressBars default to showing 0% progress.
|
||||||
type ProgressBar struct {
|
type ProgressBar struct {
|
||||||
lock sync.Mutex
|
|
||||||
created bool
|
created bool
|
||||||
sysData *sysData
|
sysData *sysData
|
||||||
initProg int
|
initProg int
|
||||||
|
@ -30,9 +25,6 @@ func NewProgressBar() *ProgressBar {
|
||||||
// Otherwise, SetProgress panics.
|
// Otherwise, SetProgress panics.
|
||||||
// Calling SetProgress(-1) repeatedly will neither leave indeterminate mode nor stop any animation involved in indeterminate mode indefinitely; any other side-effect of doing so is implementation-defined.
|
// Calling SetProgress(-1) repeatedly will neither leave indeterminate mode nor stop any animation involved in indeterminate mode indefinitely; any other side-effect of doing so is implementation-defined.
|
||||||
func (p *ProgressBar) SetProgress(percent int) {
|
func (p *ProgressBar) SetProgress(percent int) {
|
||||||
p.lock.Lock()
|
|
||||||
defer p.lock.Unlock()
|
|
||||||
|
|
||||||
if percent < -1 || percent > 100 {
|
if percent < -1 || percent > 100 {
|
||||||
panic("percent value out of range")
|
panic("percent value out of range")
|
||||||
}
|
}
|
||||||
|
@ -44,9 +36,6 @@ func (p *ProgressBar) SetProgress(percent int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ProgressBar) make(window *sysData) error {
|
func (p *ProgressBar) make(window *sysData) error {
|
||||||
p.lock.Lock()
|
|
||||||
defer p.lock.Unlock()
|
|
||||||
|
|
||||||
err := p.sysData.make(window)
|
err := p.sysData.make(window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
8
stack.go
8
stack.go
|
@ -4,7 +4,6 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type orientation bool
|
type orientation bool
|
||||||
|
@ -20,7 +19,6 @@ const (
|
||||||
// Any extra space at the end of a Stack is left blank.
|
// Any extra space at the end of a Stack is left blank.
|
||||||
// Some controls may be marked as "stretchy": when the Window they are in changes size, stretchy controls resize to take up the remaining space after non-stretchy controls are laid out. If multiple controls are marked stretchy, they are alloted equal distribution of the remaining space.
|
// Some controls may be marked as "stretchy": when the Window they are in changes size, stretchy controls resize to take up the remaining space after non-stretchy controls are laid out. If multiple controls are marked stretchy, they are alloted equal distribution of the remaining space.
|
||||||
type Stack struct {
|
type Stack struct {
|
||||||
lock sync.Mutex
|
|
||||||
created bool
|
created bool
|
||||||
orientation orientation
|
orientation orientation
|
||||||
controls []Control
|
controls []Control
|
||||||
|
@ -51,9 +49,6 @@ func NewVerticalStack(controls ...Control) *Stack {
|
||||||
// SetStretchy marks a control in a Stack as stretchy. This cannot be called once the Window containing the Stack has been created.
|
// SetStretchy marks a control in a Stack as stretchy. This cannot be called once the Window containing the Stack has been created.
|
||||||
// It panics if index is out of range.
|
// It panics if index is out of range.
|
||||||
func (s *Stack) SetStretchy(index int) {
|
func (s *Stack) SetStretchy(index int) {
|
||||||
s.lock.Lock()
|
|
||||||
defer s.lock.Unlock()
|
|
||||||
|
|
||||||
if s.created {
|
if s.created {
|
||||||
panic("call to Stack.SetStretchy() after Stack has been created")
|
panic("call to Stack.SetStretchy() after Stack has been created")
|
||||||
}
|
}
|
||||||
|
@ -64,9 +59,6 @@ func (s *Stack) SetStretchy(index int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stack) make(window *sysData) error {
|
func (s *Stack) make(window *sysData) error {
|
||||||
s.lock.Lock()
|
|
||||||
defer s.lock.Unlock()
|
|
||||||
|
|
||||||
for i, c := range s.controls {
|
for i, c := range s.controls {
|
||||||
err := c.make(window)
|
err := c.make(window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -116,7 +116,7 @@ func stdWndProc(hwnd _HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) _LRESUL
|
||||||
switch ss.ctype {
|
switch ss.ctype {
|
||||||
case c_button:
|
case c_button:
|
||||||
if wParam.HIWORD() == _BN_CLICKED {
|
if wParam.HIWORD() == _BN_CLICKED {
|
||||||
ss.signal()
|
ss.event()
|
||||||
}
|
}
|
||||||
case c_checkbox:
|
case c_checkbox:
|
||||||
// we opt into doing this ourselves because http://blogs.msdn.com/b/oldnewthing/archive/2014/05/22/10527522.aspx
|
// we opt into doing this ourselves because http://blogs.msdn.com/b/oldnewthing/archive/2014/05/22/10527522.aspx
|
||||||
|
@ -164,7 +164,11 @@ func stdWndProc(hwnd _HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) _LRESUL
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
case _WM_CLOSE:
|
case _WM_CLOSE:
|
||||||
s.signal()
|
close := false // TODO decide apt default
|
||||||
|
s.close(&close)
|
||||||
|
if close {
|
||||||
|
s.hide()
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
default:
|
default:
|
||||||
return defWindowProc(hwnd, uMsg, wParam, lParam)
|
return defWindowProc(hwnd, uMsg, wParam, lParam)
|
||||||
|
|
26
sysdata.go
26
sysdata.go
|
@ -2,21 +2,16 @@
|
||||||
|
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
const eventbufsiz = 100 // suggested by skelterjohn
|
|
||||||
|
|
||||||
// newEvent returns a new channel suitable for listening for events.
|
|
||||||
func newEvent() chan struct{} {
|
|
||||||
return make(chan struct{}, eventbufsiz)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The sysData type contains all system data. It provides the system-specific underlying implementation. It is guaranteed to have the following by embedding:
|
// The sysData type contains all system data. It provides the system-specific underlying implementation. It is guaranteed to have the following by embedding:
|
||||||
type cSysData struct {
|
type cSysData struct {
|
||||||
ctype int
|
ctype int
|
||||||
event chan struct{}
|
|
||||||
allocate func(x int, y int, width int, height int, d *sysSizeData) []*allocation
|
allocate func(x int, y int, width int, height int, d *sysSizeData) []*allocation
|
||||||
spaced bool
|
spaced bool
|
||||||
alternate bool // editable for Combobox, multi-select for listbox, password for lineedit
|
alternate bool // editable for Combobox, multi-select for listbox, password for lineedit
|
||||||
handler AreaHandler // for Areas
|
handler AreaHandler // for Areas; TODO rename to areahandler
|
||||||
|
winhandler WindowHandler // for Windows
|
||||||
|
close func(*bool) // provided by each Window
|
||||||
|
event func() // provided by each control
|
||||||
}
|
}
|
||||||
|
|
||||||
// this interface is used to make sure all sysDatas are synced
|
// this interface is used to make sure all sysDatas are synced
|
||||||
|
@ -44,19 +39,6 @@ var _xSysData interface {
|
||||||
setChecked(bool)
|
setChecked(bool)
|
||||||
} = &sysData{} // this line will error if there's an inconsistency
|
} = &sysData{} // this line will error if there's an inconsistency
|
||||||
|
|
||||||
// signal sends the event signal. This raise is done asynchronously to avoid deadlocking the UI task.
|
|
||||||
// Thanks skelterjohn for this techinque: if we can't queue any more events, drop them
|
|
||||||
func (s *cSysData) signal() {
|
|
||||||
if s.event != nil {
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case s.event <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
c_window = iota
|
c_window = iota
|
||||||
c_button
|
c_button
|
||||||
|
|
|
@ -225,17 +225,9 @@ func (s *sysData) make(window *sysData) error {
|
||||||
if window != nil {
|
if window != nil {
|
||||||
parentWindow = window.id
|
parentWindow = window.id
|
||||||
}
|
}
|
||||||
ret := make(chan C.id)
|
s.id = ct.make(parentWindow, s.alternate, s)
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- ct.make(parentWindow, s.alternate, s)
|
|
||||||
}
|
|
||||||
s.id = <-ret
|
|
||||||
if ct.getinside != nil {
|
if ct.getinside != nil {
|
||||||
uitask <- func() {
|
addSysData(ct.getinside(s.id), s)
|
||||||
ret <- ct.getinside(s.id)
|
|
||||||
}
|
|
||||||
addSysData(<-ret, s)
|
|
||||||
} else {
|
} else {
|
||||||
addSysData(s.id, s)
|
addSysData(s.id, s)
|
||||||
}
|
}
|
||||||
|
@ -249,33 +241,15 @@ func (s *sysData) firstShow() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) show() {
|
func (s *sysData) show() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
classTypes[s.ctype].show(s.id)
|
classTypes[s.ctype].show(s.id)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) hide() {
|
func (s *sysData) hide() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
classTypes[s.ctype].hide(s.id)
|
classTypes[s.ctype].hide(s.id)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setText(text string) {
|
func (s *sysData) setText(text string) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
classTypes[s.ctype].settext(s.id, toNSString(text))
|
classTypes[s.ctype].settext(s.id, toNSString(text))
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setRect(x int, y int, width int, height int, winheight int) error {
|
func (s *sysData) setRect(x int, y int, width int, height int, winheight int) error {
|
||||||
|
@ -288,147 +262,63 @@ func (s *sysData) setRect(x int, y int, width int, height int, winheight int) er
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) isChecked() bool {
|
func (s *sysData) isChecked() bool {
|
||||||
ret := make(chan bool)
|
return C.isCheckboxChecked(s.id) != C.NO
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- C.isCheckboxChecked(s.id) != C.NO
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) text() string {
|
func (s *sysData) text() string {
|
||||||
ret := make(chan string)
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
str := classTypes[s.ctype].text(s.id, s.alternate)
|
str := classTypes[s.ctype].text(s.id, s.alternate)
|
||||||
ret <- fromNSString(str)
|
return fromNSString(str)
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) append(what string) {
|
func (s *sysData) append(what string) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
classTypes[s.ctype].append(s.id, what, s.alternate)
|
classTypes[s.ctype].append(s.id, what, s.alternate)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) insertBefore(what string, before int) {
|
func (s *sysData) insertBefore(what string, before int) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
classTypes[s.ctype].insertBefore(s.id, what, before, s.alternate)
|
classTypes[s.ctype].insertBefore(s.id, what, before, s.alternate)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) selectedIndex() int {
|
func (s *sysData) selectedIndex() int {
|
||||||
ret := make(chan int)
|
return classTypes[s.ctype].selIndex(s.id)
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- classTypes[s.ctype].selIndex(s.id)
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) selectedIndices() []int {
|
func (s *sysData) selectedIndices() []int {
|
||||||
ret := make(chan []int)
|
return classTypes[s.ctype].selIndices(s.id)
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- classTypes[s.ctype].selIndices(s.id)
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) selectedTexts() []string {
|
func (s *sysData) selectedTexts() []string {
|
||||||
ret := make(chan []string)
|
return classTypes[s.ctype].selTexts(s.id)
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- classTypes[s.ctype].selTexts(s.id)
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setWindowSize(width int, height int) error {
|
func (s *sysData) setWindowSize(width int, height int) error {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
C.windowSetContentSize(s.id, C.intptr_t(width), C.intptr_t(height))
|
C.windowSetContentSize(s.id, C.intptr_t(width), C.intptr_t(height))
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) delete(index int) {
|
func (s *sysData) delete(index int) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
classTypes[s.ctype].delete(s.id, index)
|
classTypes[s.ctype].delete(s.id, index)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setProgress(percent int) {
|
func (s *sysData) setProgress(percent int) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
C.setProgress(s.id, C.intptr_t(percent))
|
C.setProgress(s.id, C.intptr_t(percent))
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) len() int {
|
func (s *sysData) len() int {
|
||||||
ret := make(chan int)
|
return classTypes[s.ctype].len(s.id)
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- classTypes[s.ctype].len(s.id)
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setAreaSize(width int, height int) {
|
func (s *sysData) setAreaSize(width int, height int) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
C.setAreaSize(s.id, C.intptr_t(width), C.intptr_t(height))
|
C.setAreaSize(s.id, C.intptr_t(width), C.intptr_t(height))
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) repaintAll() {
|
func (s *sysData) repaintAll() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
C.display(s.id)
|
C.display(s.id)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) center() {
|
func (s *sysData) center() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
C.center(s.id)
|
C.center(s.id)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setChecked(checked bool) {
|
func (s *sysData) setChecked(checked bool) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
C.setCheckboxChecked(s.id, toBOOL(checked))
|
C.setCheckboxChecked(s.id, toBOOL(checked))
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
144
sysdata_unix.go
144
sysdata_unix.go
|
@ -118,29 +118,20 @@ var classTypes = [nctypes]*classData{
|
||||||
|
|
||||||
func (s *sysData) make(window *sysData) error {
|
func (s *sysData) make(window *sysData) error {
|
||||||
ct := classTypes[s.ctype]
|
ct := classTypes[s.ctype]
|
||||||
ret := make(chan *C.GtkWidget)
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
if s.alternate {
|
if s.alternate {
|
||||||
ret <- ct.makeAlt()
|
s.widget = ct.makeAlt()
|
||||||
return
|
} else {
|
||||||
|
s.widget = ct.make()
|
||||||
}
|
}
|
||||||
ret <- ct.make()
|
|
||||||
}
|
|
||||||
s.widget = <-ret
|
|
||||||
if window == nil {
|
if window == nil {
|
||||||
uitask <- func() {
|
|
||||||
fixed := gtkNewWindowLayout()
|
fixed := gtkNewWindowLayout()
|
||||||
gtk_container_add(s.widget, fixed)
|
gtk_container_add(s.widget, fixed)
|
||||||
for signame, sigfunc := range ct.signals {
|
for signame, sigfunc := range ct.signals {
|
||||||
g_signal_connect(s.widget, signame, sigfunc, s)
|
g_signal_connect(s.widget, signame, sigfunc, s)
|
||||||
}
|
}
|
||||||
ret <- fixed
|
s.container = fixed
|
||||||
}
|
|
||||||
s.container = <-ret
|
|
||||||
} else {
|
} else {
|
||||||
s.container = window.container
|
s.container = window.container
|
||||||
uitask <- func() {
|
|
||||||
gtkAddWidgetToLayout(s.container, s.widget)
|
gtkAddWidgetToLayout(s.container, s.widget)
|
||||||
for signame, sigfunc := range ct.signals {
|
for signame, sigfunc := range ct.signals {
|
||||||
g_signal_connect(s.widget, signame, sigfunc, s)
|
g_signal_connect(s.widget, signame, sigfunc, s)
|
||||||
|
@ -151,9 +142,6 @@ func (s *sysData) make(window *sysData) error {
|
||||||
g_signal_connect(child, signame, sigfunc, s)
|
g_signal_connect(child, signame, sigfunc, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret <- nil
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -170,35 +158,17 @@ func (s *sysData) firstShow() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) show() {
|
func (s *sysData) show() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
gtk_widget_show(s.widget)
|
gtk_widget_show(s.widget)
|
||||||
s.resetposition()
|
s.resetposition()
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) hide() {
|
func (s *sysData) hide() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
gtk_widget_hide(s.widget)
|
gtk_widget_hide(s.widget)
|
||||||
s.resetposition()
|
s.resetposition()
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setText(text string) {
|
func (s *sysData) setText(text string) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
classTypes[s.ctype].setText(s.widget, text)
|
classTypes[s.ctype].setText(s.widget, text)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setRect(x int, y int, width int, height int, winheight int) error {
|
func (s *sysData) setRect(x int, y int, width int, height int, winheight int) error {
|
||||||
|
@ -208,103 +178,51 @@ func (s *sysData) setRect(x int, y int, width int, height int, winheight int) er
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) isChecked() bool {
|
func (s *sysData) isChecked() bool {
|
||||||
ret := make(chan bool)
|
return gtk_toggle_button_get_active(s.widget)
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- gtk_toggle_button_get_active(s.widget)
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) text() string {
|
func (s *sysData) text() string {
|
||||||
ret := make(chan string)
|
return classTypes[s.ctype].text(s.widget)
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- classTypes[s.ctype].text(s.widget)
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) append(what string) {
|
func (s *sysData) append(what string) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
classTypes[s.ctype].append(s.widget, what)
|
classTypes[s.ctype].append(s.widget, what)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) insertBefore(what string, before int) {
|
func (s *sysData) insertBefore(what string, before int) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
classTypes[s.ctype].insert(s.widget, before, what)
|
classTypes[s.ctype].insert(s.widget, before, what)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) selectedIndex() int {
|
func (s *sysData) selectedIndex() int {
|
||||||
ret := make(chan int)
|
return classTypes[s.ctype].selected(s.widget)
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- classTypes[s.ctype].selected(s.widget)
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) selectedIndices() []int {
|
func (s *sysData) selectedIndices() []int {
|
||||||
ret := make(chan []int)
|
return classTypes[s.ctype].selMulti(s.widget)
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- classTypes[s.ctype].selMulti(s.widget)
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) selectedTexts() []string {
|
func (s *sysData) selectedTexts() []string {
|
||||||
ret := make(chan []string)
|
return classTypes[s.ctype].smtexts(s.widget)
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- classTypes[s.ctype].smtexts(s.widget)
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setWindowSize(width int, height int) error {
|
func (s *sysData) setWindowSize(width int, height int) error {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
// does not take window geometry into account (and cannot, since the window manager won't give that info away)
|
// does not take window geometry into account (and cannot, since the window manager won't give that info away)
|
||||||
// thanks to TingPing in irc.gimp.net/#gtk+
|
// thanks to TingPing in irc.gimp.net/#gtk+
|
||||||
gtk_window_resize(s.widget, width, height)
|
gtk_window_resize(s.widget, width, height)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) delete(index int) {
|
func (s *sysData) delete(index int) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
classTypes[s.ctype].delete(s.widget, index)
|
classTypes[s.ctype].delete(s.widget, index)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// With GTK+, we must manually pulse the indeterminate progressbar ourselves. This goroutine does that.
|
// With GTK+, we must manually pulse the indeterminate progressbar ourselves. This goroutine does that.
|
||||||
func (s *sysData) progressPulse() {
|
func (s *sysData) progressPulse() {
|
||||||
|
// TODO this could probably be done differently...
|
||||||
pulse := func() {
|
pulse := func() {
|
||||||
ret := make(chan struct{})
|
touitask(func() {
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
gtk_progress_bar_pulse(s.widget)
|
gtk_progress_bar_pulse(s.widget)
|
||||||
ret <- struct{}{}
|
})
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ticker *time.Ticker
|
var ticker *time.Ticker
|
||||||
|
@ -345,53 +263,28 @@ func (s *sysData) setProgress(percent int) {
|
||||||
}
|
}
|
||||||
s.pulse <- false
|
s.pulse <- false
|
||||||
<-s.pulse // wait for sysData.progressPulse() to register that
|
<-s.pulse // wait for sysData.progressPulse() to register that
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
gtk_progress_bar_set_fraction(s.widget, percent)
|
gtk_progress_bar_set_fraction(s.widget, percent)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) len() int {
|
func (s *sysData) len() int {
|
||||||
ret := make(chan int)
|
return classTypes[s.ctype].len(s.widget)
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- classTypes[s.ctype].len(s.widget)
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setAreaSize(width int, height int) {
|
func (s *sysData) setAreaSize(width int, height int) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
c := gtkAreaGetControl(s.widget)
|
c := gtkAreaGetControl(s.widget)
|
||||||
gtk_widget_set_size_request(c, width, height)
|
gtk_widget_set_size_request(c, width, height)
|
||||||
s.areawidth = width // for sysData.preferredSize()
|
s.areawidth = width // for sysData.preferredSize()
|
||||||
s.areaheight = height
|
s.areaheight = height
|
||||||
C.gtk_widget_queue_draw(c)
|
C.gtk_widget_queue_draw(c)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO should this be made safe? (TODO move to area.go)
|
||||||
func (s *sysData) repaintAll() {
|
func (s *sysData) repaintAll() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
c := gtkAreaGetControl(s.widget)
|
c := gtkAreaGetControl(s.widget)
|
||||||
C.gtk_widget_queue_draw(c)
|
C.gtk_widget_queue_draw(c)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) center() {
|
func (s *sysData) center() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
if C.gtk_widget_get_visible(s.widget) == C.FALSE {
|
if C.gtk_widget_get_visible(s.widget) == C.FALSE {
|
||||||
// hint to the WM to make it centered when it is shown again
|
// hint to the WM to make it centered when it is shown again
|
||||||
// thanks to Jasper in irc.gimp.net/#gtk+
|
// thanks to Jasper in irc.gimp.net/#gtk+
|
||||||
|
@ -406,17 +299,8 @@ func (s *sysData) center() {
|
||||||
(C.gdk_screen_width() / 2) - (width / 2),
|
(C.gdk_screen_width() / 2) - (width / 2),
|
||||||
(C.gdk_screen_height() / 2) - (width / 2))
|
(C.gdk_screen_height() / 2) - (width / 2))
|
||||||
}
|
}
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setChecked(checked bool) {
|
func (s *sysData) setChecked(checked bool) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
gtk_toggle_button_set_active(s.widget, checked)
|
gtk_toggle_button_set_active(s.widget, checked)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,9 +148,6 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *sysData) make(window *sysData) (err error) {
|
func (s *sysData) make(window *sysData) (err error) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ct := classTypes[s.ctype]
|
ct := classTypes[s.ctype]
|
||||||
cid := _HMENU(0)
|
cid := _HMENU(0)
|
||||||
pwin := uintptr(_NULL)
|
pwin := uintptr(_NULL)
|
||||||
|
@ -197,9 +194,6 @@ func (s *sysData) make(window *sysData) (err error) {
|
||||||
uintptr(_WPARAM(controlFont)),
|
uintptr(_WPARAM(controlFont)),
|
||||||
uintptr(_LPARAM(_TRUE)))
|
uintptr(_LPARAM(_TRUE)))
|
||||||
}
|
}
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,9 +205,6 @@ var (
|
||||||
// ShowWindow(hwnd, nCmdShow);
|
// ShowWindow(hwnd, nCmdShow);
|
||||||
// UpdateWindow(hwnd);
|
// UpdateWindow(hwnd);
|
||||||
func (s *sysData) firstShow() error {
|
func (s *sysData) firstShow() error {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
_showWindow.Call(
|
_showWindow.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
uintptr(nCmdShow))
|
uintptr(nCmdShow))
|
||||||
|
@ -221,40 +212,22 @@ func (s *sysData) firstShow() error {
|
||||||
if r1 == 0 { // failure
|
if r1 == 0 { // failure
|
||||||
panic(fmt.Errorf("error updating window for the first time: %v", err))
|
panic(fmt.Errorf("error updating window for the first time: %v", err))
|
||||||
}
|
}
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) show() {
|
func (s *sysData) show() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
_showWindow.Call(
|
_showWindow.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
uintptr(_SW_SHOW))
|
uintptr(_SW_SHOW))
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) hide() {
|
func (s *sysData) hide() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
_showWindow.Call(
|
_showWindow.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
uintptr(_SW_HIDE))
|
uintptr(_SW_HIDE))
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setText(text string) {
|
func (s *sysData) setText(text string) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ptext := toUTF16(text)
|
ptext := toUTF16(text)
|
||||||
r1, _, err := _setWindowText.Call(
|
r1, _, err := _setWindowText.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
|
@ -262,12 +235,8 @@ func (s *sysData) setText(text string) {
|
||||||
if r1 == 0 { // failure
|
if r1 == 0 { // failure
|
||||||
panic(fmt.Errorf("error setting window/control text: %v", err))
|
panic(fmt.Errorf("error setting window/control text: %v", err))
|
||||||
}
|
}
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// runs on uitask
|
|
||||||
func (s *sysData) setRect(x int, y int, width int, height int, winheight int) error {
|
func (s *sysData) setRect(x int, y int, width int, height int, winheight int) error {
|
||||||
r1, _, err := _moveWindow.Call(
|
r1, _, err := _moveWindow.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
|
@ -283,23 +252,15 @@ func (s *sysData) setRect(x int, y int, width int, height int, winheight int) er
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) isChecked() bool {
|
func (s *sysData) isChecked() bool {
|
||||||
ret := make(chan bool)
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
r1, _, _ := _sendMessage.Call(
|
r1, _, _ := _sendMessage.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
uintptr(_BM_GETCHECK),
|
uintptr(_BM_GETCHECK),
|
||||||
uintptr(0),
|
uintptr(0),
|
||||||
uintptr(0))
|
uintptr(0))
|
||||||
ret <- r1 == _BST_CHECKED
|
return r1 == _BST_CHECKED
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) text() (str string) {
|
func (s *sysData) text() (str string) {
|
||||||
ret := make(chan string)
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
var tc []uint16
|
var tc []uint16
|
||||||
|
|
||||||
r1, _, _ := _sendMessage.Call(
|
r1, _, _ := _sendMessage.Call(
|
||||||
|
@ -314,15 +275,10 @@ func (s *sysData) text() (str string) {
|
||||||
uintptr(_WM_GETTEXT),
|
uintptr(_WM_GETTEXT),
|
||||||
uintptr(_WPARAM(length)),
|
uintptr(_WPARAM(length)),
|
||||||
uintptr(_LPARAM(unsafe.Pointer(&tc[0]))))
|
uintptr(_LPARAM(unsafe.Pointer(&tc[0]))))
|
||||||
ret <- syscall.UTF16ToString(tc)
|
return syscall.UTF16ToString(tc)
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) append(what string) {
|
func (s *sysData) append(what string) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
pwhat := toUTF16(what)
|
pwhat := toUTF16(what)
|
||||||
r1, _, err := _sendMessage.Call(
|
r1, _, err := _sendMessage.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
|
@ -334,15 +290,9 @@ func (s *sysData) append(what string) {
|
||||||
} else if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
} else if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
||||||
panic(fmt.Errorf("failed to add item to combobox/listbox (last error: %v)", err))
|
panic(fmt.Errorf("failed to add item to combobox/listbox (last error: %v)", err))
|
||||||
}
|
}
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) insertBefore(what string, index int) {
|
func (s *sysData) insertBefore(what string, index int) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
pwhat := toUTF16(what)
|
pwhat := toUTF16(what)
|
||||||
r1, _, err := _sendMessage.Call(
|
r1, _, err := _sendMessage.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
|
@ -354,13 +304,9 @@ func (s *sysData) insertBefore(what string, index int) {
|
||||||
} else if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
} else if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
||||||
panic(fmt.Errorf("failed to add item to combobox/listbox (last error: %v)", err))
|
panic(fmt.Errorf("failed to add item to combobox/listbox (last error: %v)", err))
|
||||||
}
|
}
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// runs on uitask
|
func (s *sysData) selectedIndex() int {
|
||||||
func (s *sysData) doSelectedIndex() int {
|
|
||||||
r1, _, _ := _sendMessage.Call(
|
r1, _, _ := _sendMessage.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
uintptr(classTypes[s.ctype].selectedIndexMsg),
|
uintptr(classTypes[s.ctype].selectedIndexMsg),
|
||||||
|
@ -372,19 +318,9 @@ func (s *sysData) doSelectedIndex() int {
|
||||||
return int(r1)
|
return int(r1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) selectedIndex() int {
|
func (s *sysData) selectedIndices() []int {
|
||||||
ret := make(chan int)
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- s.doSelectedIndex()
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// runs on uitask
|
|
||||||
func (s *sysData) doSelectedIndices() []int {
|
|
||||||
if !s.alternate { // single-selection list box; use single-selection method
|
if !s.alternate { // single-selection list box; use single-selection method
|
||||||
index := s.doSelectedIndex()
|
index := s.selectedIndex()
|
||||||
if index == -1 {
|
if index == -1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -414,20 +350,8 @@ func (s *sysData) doSelectedIndices() []int {
|
||||||
return indices
|
return indices
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) selectedIndices() []int {
|
|
||||||
ret := make(chan []int)
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
ret <- s.doSelectedIndices()
|
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sysData) selectedTexts() []string {
|
func (s *sysData) selectedTexts() []string {
|
||||||
ret := make(chan []string)
|
indices := s.selectedIndices()
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
indices := s.doSelectedIndices()
|
|
||||||
strings := make([]string, len(indices))
|
strings := make([]string, len(indices))
|
||||||
for i, v := range indices {
|
for i, v := range indices {
|
||||||
r1, _, err := _sendMessage.Call(
|
r1, _, err := _sendMessage.Call(
|
||||||
|
@ -449,15 +373,10 @@ func (s *sysData) selectedTexts() []string {
|
||||||
}
|
}
|
||||||
strings[i] = syscall.UTF16ToString(str)
|
strings[i] = syscall.UTF16ToString(str)
|
||||||
}
|
}
|
||||||
ret <- strings
|
return strings
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setWindowSize(width int, height int) error {
|
func (s *sysData) setWindowSize(width int, height int) error {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
var rect _RECT
|
var rect _RECT
|
||||||
|
|
||||||
r1, _, err := _getClientRect.Call(
|
r1, _, err := _getClientRect.Call(
|
||||||
|
@ -472,16 +391,10 @@ func (s *sysData) setWindowSize(width int, height int) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("error actually resizing window: %v", err))
|
panic(fmt.Errorf("error actually resizing window: %v", err))
|
||||||
}
|
}
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) delete(index int) {
|
func (s *sysData) delete(index int) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
r1, _, err := _sendMessage.Call(
|
r1, _, err := _sendMessage.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
uintptr(classTypes[s.ctype].deleteMsg),
|
uintptr(classTypes[s.ctype].deleteMsg),
|
||||||
|
@ -490,15 +403,9 @@ func (s *sysData) delete(index int) {
|
||||||
if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
||||||
panic(fmt.Errorf("failed to delete item from combobox/listbox (last error: %v)", err))
|
panic(fmt.Errorf("failed to delete item from combobox/listbox (last error: %v)", err))
|
||||||
}
|
}
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setIndeterminate() {
|
func (s *sysData) setIndeterminate() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
r1, _, err := _setWindowLongPtr.Call(
|
r1, _, err := _setWindowLongPtr.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
negConst(_GWL_STYLE),
|
negConst(_GWL_STYLE),
|
||||||
|
@ -512,9 +419,6 @@ func (s *sysData) setIndeterminate() {
|
||||||
uintptr(_WPARAM(_TRUE)),
|
uintptr(_WPARAM(_TRUE)),
|
||||||
uintptr(0))
|
uintptr(0))
|
||||||
s.isMarquee = true
|
s.isMarquee = true
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setProgress(percent int) {
|
func (s *sysData) setProgress(percent int) {
|
||||||
|
@ -522,9 +426,6 @@ func (s *sysData) setProgress(percent int) {
|
||||||
s.setIndeterminate()
|
s.setIndeterminate()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
if s.isMarquee {
|
if s.isMarquee {
|
||||||
// turn off marquee before switching back
|
// turn off marquee before switching back
|
||||||
_sendMessage.Call(
|
_sendMessage.Call(
|
||||||
|
@ -560,15 +461,9 @@ func (s *sysData) setProgress(percent int) {
|
||||||
if percent == 100 {
|
if percent == 100 {
|
||||||
send(_PBM_SETRANGE32, 0, 100)
|
send(_PBM_SETRANGE32, 0, 100)
|
||||||
}
|
}
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) len() int {
|
func (s *sysData) len() int {
|
||||||
ret := make(chan int)
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
r1, _, err := _sendMessage.Call(
|
r1, _, err := _sendMessage.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
uintptr(classTypes[s.ctype].lenMsg),
|
uintptr(classTypes[s.ctype].lenMsg),
|
||||||
|
@ -577,43 +472,26 @@ func (s *sysData) len() int {
|
||||||
if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) {
|
||||||
panic(fmt.Errorf("unexpected error return from sysData.len(); GetLastError() says %v", err))
|
panic(fmt.Errorf("unexpected error return from sysData.len(); GetLastError() says %v", err))
|
||||||
}
|
}
|
||||||
ret <- int(r1)
|
return int(r1)
|
||||||
}
|
|
||||||
return <-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setAreaSize(width int, height int) {
|
func (s *sysData) setAreaSize(width int, height int) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
_sendMessage.Call(
|
_sendMessage.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
uintptr(msgSetAreaSize),
|
uintptr(msgSetAreaSize),
|
||||||
uintptr(width), // WPARAM is UINT_PTR on Windows XP and newer at least, so we're good with this
|
uintptr(width), // WPARAM is UINT_PTR on Windows XP and newer at least, so we're good with this
|
||||||
uintptr(height))
|
uintptr(height))
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) repaintAll() {
|
func (s *sysData) repaintAll() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
_sendMessage.Call(
|
_sendMessage.Call(
|
||||||
uintptr(s.hwnd),
|
uintptr(s.hwnd),
|
||||||
uintptr(msgRepaintAll),
|
uintptr(msgRepaintAll),
|
||||||
uintptr(0),
|
uintptr(0),
|
||||||
uintptr(0))
|
uintptr(0))
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) center() {
|
func (s *sysData) center() {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
var ws _RECT
|
var ws _RECT
|
||||||
|
|
||||||
r1, _, err := _getWindowRect.Call(
|
r1, _, err := _getWindowRect.Call(
|
||||||
|
@ -624,6 +502,7 @@ func (s *sysData) center() {
|
||||||
}
|
}
|
||||||
// TODO should this be using the monitor functions instead? http://blogs.msdn.com/b/oldnewthing/archive/2005/05/05/414910.aspx
|
// TODO should this be using the monitor functions instead? http://blogs.msdn.com/b/oldnewthing/archive/2005/05/05/414910.aspx
|
||||||
// error returns from GetSystemMetrics() is meaningless because the return value, 0, is still valid
|
// error returns from GetSystemMetrics() is meaningless because the return value, 0, is still valid
|
||||||
|
// TODO should this be using the client rect and not the window rect?
|
||||||
dw, _, _ := _getSystemMetrics.Call(uintptr(_SM_CXFULLSCREEN))
|
dw, _, _ := _getSystemMetrics.Call(uintptr(_SM_CXFULLSCREEN))
|
||||||
dh, _, _ := _getSystemMetrics.Call(uintptr(_SM_CYFULLSCREEN))
|
dh, _, _ := _getSystemMetrics.Call(uintptr(_SM_CYFULLSCREEN))
|
||||||
ww := ws.right - ws.left
|
ww := ws.right - ws.left
|
||||||
|
@ -631,15 +510,9 @@ func (s *sysData) center() {
|
||||||
wx := (int32(dw) / 2) - (ww / 2)
|
wx := (int32(dw) / 2) - (ww / 2)
|
||||||
wy := (int32(dh) / 2) - (wh / 2)
|
wy := (int32(dh) / 2) - (wh / 2)
|
||||||
s.setRect(int(wx), int(wy), int(ww), int(wh), 0)
|
s.setRect(int(wx), int(wy), int(ww), int(wh), 0)
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sysData) setChecked(checked bool) {
|
func (s *sysData) setChecked(checked bool) {
|
||||||
ret := make(chan struct{})
|
|
||||||
defer close(ret)
|
|
||||||
uitask <- func() {
|
|
||||||
c := uintptr(_BST_CHECKED)
|
c := uintptr(_BST_CHECKED)
|
||||||
if !checked {
|
if !checked {
|
||||||
c = uintptr(_BST_UNCHECKED)
|
c = uintptr(_BST_UNCHECKED)
|
||||||
|
@ -649,7 +522,4 @@ func (s *sysData) setChecked(checked bool) {
|
||||||
uintptr(_BM_SETCHECK),
|
uintptr(_BM_SETCHECK),
|
||||||
c,
|
c,
|
||||||
uintptr(0))
|
uintptr(0))
|
||||||
ret <- struct{}{}
|
|
||||||
}
|
|
||||||
<-ret
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,13 +73,19 @@ func (a *keyboardArea) Key(e KeyEvent) (repaint bool) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type kbhandler struct{}
|
||||||
|
func (kbhandler) Event(e Event, d interface{}) {
|
||||||
|
if e == Closing {
|
||||||
|
*(d.(*bool)) = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var doKeyboard = flag.Bool("kb", false, "run keyboard test (overrides -areabounds)")
|
var doKeyboard = flag.Bool("kb", false, "run keyboard test (overrides -areabounds)")
|
||||||
func kbTest() {
|
func kbTest() {
|
||||||
wid, ht, ah := mkkbArea()
|
wid, ht, ah := mkkbArea()
|
||||||
a := NewArea(wid, ht, ah)
|
a := NewArea(wid, ht, ah)
|
||||||
w := NewWindow("Hi", wid, ht)
|
w := NewWindow("Hi", wid, ht, kbhandler{})
|
||||||
w.Open(a)
|
w.Open(a)
|
||||||
<-w.Closing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -213,6 +219,8 @@ var modpoints = map[Modifiers]image.Point{
|
||||||
Super: image.Pt(61, 199),
|
Super: image.Pt(61, 199),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO move the following to its own file
|
||||||
|
|
||||||
// source: http://openclipart.org/image/800px/svg_to_png/154537/1312973798.png (medium image) via http://openclipart.org/detail/154537/us-english-keyboard-layout-v0.1-by-nitiraseem
|
// source: http://openclipart.org/image/800px/svg_to_png/154537/1312973798.png (medium image) via http://openclipart.org/detail/154537/us-english-keyboard-layout-v0.1-by-nitiraseem
|
||||||
var kbpic = []byte{
|
var kbpic = []byte{
|
||||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||||
|
|
361
test/main.go
361
test/main.go
|
@ -15,18 +15,21 @@ import (
|
||||||
. "github.com/andlabs/ui"
|
. "github.com/andlabs/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type nullwinhandler struct{}
|
||||||
|
func (nullwinhandler) Event(Event, interface{}) {}
|
||||||
|
|
||||||
var prefsizetest = flag.Bool("prefsize", false, "")
|
var prefsizetest = flag.Bool("prefsize", false, "")
|
||||||
func listboxPreferredSizeTest() *Window {
|
func listboxPreferredSizeTest() *Window {
|
||||||
lb := NewListbox("xxxxx", "y", "zzz")
|
lb := NewListbox("xxxxx", "y", "zzz")
|
||||||
g := NewGrid(1, lb)
|
g := NewGrid(1, lb)
|
||||||
w := NewWindow("Listbox Preferred Size Test", 300, 300)
|
w := NewWindow("Listbox Preferred Size Test", 300, 300, nullwinhandler{})
|
||||||
w.Open(g)
|
w.Open(g)
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
var gridtest = flag.Bool("grid", false, "")
|
var gridtest = flag.Bool("grid", false, "")
|
||||||
func gridWindow() *Window {
|
func gridWindow() *Window {
|
||||||
w := NewWindow("Grid Test", 400, 400)
|
w := NewWindow("Grid Test", 400, 400, nullwinhandler{})
|
||||||
b00 := NewButton("0,0")
|
b00 := NewButton("0,0")
|
||||||
b01 := NewButton("0,1")
|
b01 := NewButton("0,1")
|
||||||
b02 := NewButton("0,2")
|
b02 := NewButton("0,2")
|
||||||
|
@ -44,10 +47,11 @@ func gridWindow() *Window {
|
||||||
g.SetStretchy(1, 1)
|
g.SetStretchy(1, 1)
|
||||||
w.SetSpaced(*spacingTest)
|
w.SetSpaced(*spacingTest)
|
||||||
w.Open(g)
|
w.Open(g)
|
||||||
go func() {for {select {
|
//TODO
|
||||||
case <-b12.Clicked:
|
// go func() {for {select {
|
||||||
c21.SetChecked(!c21.Checked())
|
// case <-b12.Clicked:
|
||||||
}}}()
|
// c21.SetChecked(!c21.Checked())
|
||||||
|
// }}}()
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +209,6 @@ func areaTest() {
|
||||||
}
|
}
|
||||||
img := image.NewRGBA(ximg.Bounds())
|
img := image.NewRGBA(ximg.Bounds())
|
||||||
draw.Draw(img, img.Rect, ximg, image.ZP, draw.Over)
|
draw.Draw(img, img.Rect, ximg, image.ZP, draw.Over)
|
||||||
w := NewWindow("Area Test", 100, 100)
|
|
||||||
areahandler := &areaHandler{
|
areahandler := &areaHandler{
|
||||||
img: img,
|
img: img,
|
||||||
}
|
}
|
||||||
|
@ -229,24 +232,54 @@ func areaTest() {
|
||||||
timedisp,
|
timedisp,
|
||||||
sizeStack)
|
sizeStack)
|
||||||
layout.SetStretchy(0)
|
layout.SetStretchy(0)
|
||||||
|
w := NewWindow("Area Test", 100, 100, &areatestwinhandler{
|
||||||
|
areahandler: areahandler,
|
||||||
|
a: a,
|
||||||
|
timedisp: timedisp,
|
||||||
|
widthbox: widthbox,
|
||||||
|
heightbox: heightbox,
|
||||||
|
resize: resize,
|
||||||
|
modaltest: modaltest,
|
||||||
|
repainttest: repainttest,
|
||||||
|
})
|
||||||
w.Open(layout)
|
w.Open(layout)
|
||||||
for {
|
go func() {
|
||||||
select {
|
for t := range timechan {
|
||||||
case <-w.Closing:
|
// TODO
|
||||||
return
|
_ = t
|
||||||
case t := <-timechan:
|
// timedisp.SetText(t.String())
|
||||||
timedisp.SetText(t.String())
|
}
|
||||||
case <-resize.Clicked:
|
}()
|
||||||
width, err := strconv.Atoi(widthbox.Text())
|
}
|
||||||
if err != nil { println(err); continue }
|
|
||||||
height, err := strconv.Atoi(heightbox.Text())
|
type areatestwinhandler struct {
|
||||||
if err != nil { println(err); continue }
|
areahandler *areaHandler
|
||||||
a.SetSize(width, height)
|
a *Area
|
||||||
case <-modaltest.Clicked:
|
timedisp *Label
|
||||||
|
widthbox *LineEdit
|
||||||
|
heightbox *LineEdit
|
||||||
|
resize *Button
|
||||||
|
modaltest *Button
|
||||||
|
repainttest *Button
|
||||||
|
}
|
||||||
|
func (a *areatestwinhandler) Event(e Event, d interface{}) {
|
||||||
|
switch e {
|
||||||
|
case Closing:
|
||||||
|
*(d.(*bool)) = true
|
||||||
|
Stop <- struct{}{}
|
||||||
|
case Clicked:
|
||||||
|
switch d {
|
||||||
|
case a.resize:
|
||||||
|
width, err := strconv.Atoi(a.widthbox.Text())
|
||||||
|
if err != nil { println(err); return }
|
||||||
|
height, err := strconv.Atoi(a.heightbox.Text())
|
||||||
|
if err != nil { println(err); return }
|
||||||
|
a.a.SetSize(width, height)
|
||||||
|
case a.modaltest:
|
||||||
MsgBox("Modal Test", "")
|
MsgBox("Modal Test", "")
|
||||||
case <-repainttest.Clicked:
|
case a.repainttest:
|
||||||
areahandler.mutate()
|
a.areahandler.mutate()
|
||||||
a.RepaintAll()
|
a.a.RepaintAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,9 +308,10 @@ func areaboundsTest() {
|
||||||
// middle purple
|
// middle purple
|
||||||
r.Min.Y++
|
r.Min.Y++
|
||||||
draw.Draw(img, r, u(128, 0, 128), image.ZP, draw.Over)
|
draw.Draw(img, r, u(128, 0, 128), image.ZP, draw.Over)
|
||||||
w := NewWindow("Area Bounds Test", 320, 240)
|
w := NewWindow("Area Bounds Test", 320, 240, &areatestwinhandler{
|
||||||
|
a: a,
|
||||||
|
})
|
||||||
w.Open(a)
|
w.Open(a)
|
||||||
<-w.Closing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var dialogTest = flag.Bool("dialog", false, "add Window.MsgBox() channel test window")
|
var dialogTest = flag.Bool("dialog", false, "add Window.MsgBox() channel test window")
|
||||||
|
@ -285,6 +319,7 @@ var labelAlignTest = flag.Bool("label", false, "show Label Alignment test window
|
||||||
var spacingTest = flag.Bool("spacing", false, "margins and padding on Window")
|
var spacingTest = flag.Bool("spacing", false, "margins and padding on Window")
|
||||||
|
|
||||||
func myMain() {
|
func myMain() {
|
||||||
|
<-Ready
|
||||||
if *spacetest != "" {
|
if *spacetest != "" {
|
||||||
spaceTest()
|
spaceTest()
|
||||||
return
|
return
|
||||||
|
@ -301,58 +336,88 @@ func myMain() {
|
||||||
areaboundsTest()
|
areaboundsTest()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w := NewWindow("Main Window", 320, 240)
|
runMainTest()
|
||||||
b := NewButton("Click Me")
|
|
||||||
b2 := NewButton("Or Me")
|
|
||||||
bmsg := NewButton("Or Even Me!")
|
|
||||||
s2 := NewHorizontalStack(b, b2, bmsg)
|
|
||||||
s2.SetStretchy(2)
|
|
||||||
c := NewCheckbox("Check Me")
|
|
||||||
cb1 := NewEditableCombobox("You can edit me!", "Yes you can!", "Yes you will!")
|
|
||||||
cb2 := NewCombobox("You can't edit me!", "No you can't!", "No you won't!")
|
|
||||||
e := NewLineEdit("Enter text here too")
|
|
||||||
l := NewLabel("This is a label")
|
|
||||||
resetl := func() {
|
|
||||||
l.SetText("This is a label")
|
|
||||||
}
|
}
|
||||||
b3 := NewButton("List Info")
|
|
||||||
s3 := NewHorizontalStack(l, b3)
|
type testwinhandler struct {
|
||||||
|
w *Window
|
||||||
|
b *Button
|
||||||
|
b2 *Button
|
||||||
|
bmsg *Button
|
||||||
|
c *Checkbox
|
||||||
|
cb1 *Combobox
|
||||||
|
cb2 *Combobox
|
||||||
|
e *LineEdit
|
||||||
|
l *Label
|
||||||
|
resetl func()
|
||||||
|
b3 *Button
|
||||||
|
pbar *ProgressBar
|
||||||
|
prog int
|
||||||
|
incButton *Button
|
||||||
|
decButton *Button
|
||||||
|
indetButton *Button
|
||||||
|
invalidButton *Button
|
||||||
|
password *LineEdit
|
||||||
|
lb1 *Listbox
|
||||||
|
lb2 *Listbox
|
||||||
|
i int
|
||||||
|
doAdjustments func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func runMainTest() {
|
||||||
|
handler := new(testwinhandler)
|
||||||
|
handler.w = NewWindow("Main Window", 320, 240, handler)
|
||||||
|
handler.b = NewButton("Click Me")
|
||||||
|
handler.b2 = NewButton("Or Me")
|
||||||
|
handler.bmsg = NewButton("Or Even Me!")
|
||||||
|
s2 := NewHorizontalStack(handler.b, handler.b2, handler.bmsg)
|
||||||
|
s2.SetStretchy(2)
|
||||||
|
handler.c = NewCheckbox("Check Me")
|
||||||
|
handler.cb1 = NewEditableCombobox("You can edit me!", "Yes you can!", "Yes you will!")
|
||||||
|
handler.cb2 = NewCombobox("You can't edit me!", "No you can't!", "No you won't!")
|
||||||
|
handler.e = NewLineEdit("Enter text here too")
|
||||||
|
handler.l = NewLabel("This is a label")
|
||||||
|
handler.resetl = func() {
|
||||||
|
handler.l.SetText("This is a label")
|
||||||
|
}
|
||||||
|
handler.b3 = NewButton("List Info")
|
||||||
|
s3 := NewHorizontalStack(handler.l, handler.b3)
|
||||||
s3.SetStretchy(0)
|
s3.SetStretchy(0)
|
||||||
// s3.SetStretchy(1)
|
// s3.SetStretchy(1)
|
||||||
pbar := NewProgressBar()
|
handler.pbar = NewProgressBar()
|
||||||
prog := 0
|
handler.prog = 0
|
||||||
incButton := NewButton("Inc")
|
handler.incButton = NewButton("Inc")
|
||||||
decButton := NewButton("Dec")
|
handler.decButton = NewButton("Dec")
|
||||||
indetButton := NewButton("Indeterminate")
|
handler.indetButton = NewButton("Indeterminate")
|
||||||
invalidButton := NewButton("Run Invalid Test")
|
handler.invalidButton = NewButton("Run Invalid Test")
|
||||||
sincdec := NewHorizontalStack(incButton, decButton, indetButton, invalidButton)
|
sincdec := NewHorizontalStack(handler.incButton, handler.decButton, handler.indetButton, handler.invalidButton)
|
||||||
password := NewPasswordEdit()
|
handler.password = NewPasswordEdit()
|
||||||
s0 := NewVerticalStack(s2, c, cb1, cb2, e, s3, pbar, sincdec, Space(), password)
|
s0 := NewVerticalStack(s2, handler.c, handler.cb1, handler.cb2, handler.e, s3, handler.pbar, sincdec, Space(), handler.password)
|
||||||
s0.SetStretchy(8)
|
s0.SetStretchy(8)
|
||||||
lb1 := NewMultiSelListbox("Select One", "Or More", "To Continue")
|
handler.lb1 = NewMultiSelListbox("Select One", "Or More", "To Continue")
|
||||||
lb2 := NewListbox("Select", "Only", "One", "Please")
|
handler.lb2 = NewListbox("Select", "Only", "One", "Please")
|
||||||
i := 0
|
handler.i = 0
|
||||||
doAdjustments := func() {
|
handler.doAdjustments = func() {
|
||||||
cb1.Append("append")
|
handler.cb1.Append("append")
|
||||||
cb2.InsertBefore(fmt.Sprintf("before %d", i), 1)
|
handler.cb2.InsertBefore(fmt.Sprintf("before %d", handler.i), 1)
|
||||||
lb1.InsertBefore(fmt.Sprintf("%d", i), 2)
|
handler.lb1.InsertBefore(fmt.Sprintf("%d", handler.i), 2)
|
||||||
lb2.Append("Please")
|
handler.lb2.Append("Please")
|
||||||
i++
|
handler.i++
|
||||||
}
|
}
|
||||||
doAdjustments()
|
handler.doAdjustments()
|
||||||
cb1.Append("append multi 1", "append multi 2")
|
handler.cb1.Append("append multi 1", "append multi 2")
|
||||||
lb2.Append("append multi 1", "append multi 2")
|
handler.lb2.Append("append multi 1", "append multi 2")
|
||||||
s1 := NewVerticalStack(lb2, lb1)
|
s1 := NewVerticalStack(handler.lb2, handler.lb1)
|
||||||
s1.SetStretchy(0)
|
s1.SetStretchy(0)
|
||||||
s1.SetStretchy(1)
|
s1.SetStretchy(1)
|
||||||
s := NewHorizontalStack(s1, s0)
|
s := NewHorizontalStack(s1, s0)
|
||||||
s.SetStretchy(0)
|
s.SetStretchy(0)
|
||||||
s.SetStretchy(1)
|
s.SetStretchy(1)
|
||||||
if *invalidBefore {
|
if *invalidBefore {
|
||||||
invalidTest(cb1, lb1, s, NewGrid(1, Space()))
|
invalidTest(handler.cb1, handler.lb1, s, NewGrid(1, Space()))
|
||||||
}
|
}
|
||||||
w.SetSpaced(*spacingTest)
|
handler.w.SetSpaced(*spacingTest)
|
||||||
w.Open(s)
|
handler.w.Open(s)
|
||||||
if *gridtest {
|
if *gridtest {
|
||||||
gridWindow()
|
gridWindow()
|
||||||
}
|
}
|
||||||
|
@ -360,12 +425,17 @@ func myMain() {
|
||||||
listboxPreferredSizeTest()
|
listboxPreferredSizeTest()
|
||||||
}
|
}
|
||||||
|
|
||||||
ticker := time.Tick(time.Second)
|
// == TODO ==
|
||||||
|
|
||||||
dialog_bMsgBox := NewButton("MsgBox()")
|
dialog_bMsgBox := NewButton("MsgBox()")
|
||||||
dialog_bMsgBoxError := NewButton("MsgBoxError()")
|
dialog_bMsgBoxError := NewButton("MsgBoxError()")
|
||||||
centerButton := NewButton("Center")
|
centerButton := NewButton("Center")
|
||||||
dialog_win := NewWindow("Dialogs", 200, 200)
|
dh := &dialoghandler{
|
||||||
|
bMsgBox: dialog_bMsgBox,
|
||||||
|
bMsgBoxError: dialog_bMsgBoxError,
|
||||||
|
bCenter: centerButton,
|
||||||
|
// send: w.Send,
|
||||||
|
}
|
||||||
|
dh.w = NewWindow("Dialogs", 200, 200, dh)
|
||||||
if *dialogTest {
|
if *dialogTest {
|
||||||
s := NewVerticalStack(
|
s := NewVerticalStack(
|
||||||
dialog_bMsgBox,
|
dialog_bMsgBox,
|
||||||
|
@ -373,11 +443,9 @@ func myMain() {
|
||||||
Space(),
|
Space(),
|
||||||
centerButton)
|
centerButton)
|
||||||
s.SetStretchy(2)
|
s.SetStretchy(2)
|
||||||
dialog_win.Open(s)
|
dh.w.Open(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
var dialog_sret chan struct{} = nil
|
|
||||||
|
|
||||||
if *labelAlignTest {
|
if *labelAlignTest {
|
||||||
s := NewHorizontalStack(NewStandaloneLabel("Label"), NewLineEdit("Label"))
|
s := NewHorizontalStack(NewStandaloneLabel("Label"), NewLineEdit("Label"))
|
||||||
s.SetStretchy(1)
|
s.SetStretchy(1)
|
||||||
|
@ -407,99 +475,116 @@ func myMain() {
|
||||||
NewButton("Button"),
|
NewButton("Button"),
|
||||||
s)
|
s)
|
||||||
s.SetStretchy(4)
|
s.SetStretchy(4)
|
||||||
NewWindow("Label Align Test", 500, 300).Open(s)
|
NewWindow("Label Align Test", 500, 300, nullwinhandler{}).Open(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mainloop:
|
func (handler *testwinhandler) Event(e Event, d interface{}) {
|
||||||
for {
|
switch e {
|
||||||
select {
|
case Closing:
|
||||||
case curtime := <-ticker:
|
|
||||||
// l.SetText(curtime.String())
|
|
||||||
_=curtime
|
|
||||||
case <-w.Closing:
|
|
||||||
println("window closed event received")
|
println("window closed event received")
|
||||||
break mainloop
|
Stop <- struct{}{}
|
||||||
case <-AppQuit:
|
case Clicked:
|
||||||
println("application quit event received")
|
switch d {
|
||||||
break mainloop
|
case handler.b:
|
||||||
case <-b.Clicked:
|
handler.w.SetTitle(fmt.Sprintf("%v | %s | %s | %s | %s",
|
||||||
w.SetTitle(fmt.Sprintf("%v | %s | %s | %s | %s",
|
handler.c.Checked(),
|
||||||
c.Checked(),
|
handler.cb1.Selection(),
|
||||||
cb1.Selection(),
|
handler.cb2.Selection(),
|
||||||
cb2.Selection(),
|
handler.e.Text(),
|
||||||
e.Text(),
|
handler.password.Text()))
|
||||||
password.Text()))
|
handler.doAdjustments()
|
||||||
doAdjustments()
|
case handler.b2:
|
||||||
case <-b2.Clicked:
|
if handler.cb1.Len() > 1 {
|
||||||
if cb1.Len() > 1 {
|
handler.cb1.Delete(1)
|
||||||
cb1.Delete(1)
|
|
||||||
}
|
}
|
||||||
if cb2.Len() > 2 {
|
if handler.cb2.Len() > 2 {
|
||||||
cb2.Delete(2)
|
handler.cb2.Delete(2)
|
||||||
}
|
}
|
||||||
if lb1.Len() > 3 || *macCrashTest {
|
if handler.lb1.Len() > 3 || *macCrashTest {
|
||||||
lb1.Delete(3)
|
handler.lb1.Delete(3)
|
||||||
}
|
}
|
||||||
if lb2.Len() > 4 {
|
if handler.lb2.Len() > 4 {
|
||||||
lb2.Delete(4)
|
handler.lb2.Delete(4)
|
||||||
}
|
}
|
||||||
case <-b3.Clicked:
|
case handler.b3:
|
||||||
f := MsgBox
|
f := MsgBox
|
||||||
if c.Checked() {
|
if handler.c.Checked() {
|
||||||
f = MsgBoxError
|
f = MsgBoxError
|
||||||
}
|
}
|
||||||
f("List Info",
|
f("List Info",
|
||||||
fmt.Sprintf("cb1: %d %q (len %d)\ncb2: %d %q (len %d)\nlb1: %d %q (len %d)\nlb2: %d %q (len %d)",
|
fmt.Sprintf("cb1: %d %q (len %d)\ncb2: %d %q (len %d)\nlb1: %d %q (len %d)\nlb2: %d %q (len %d)",
|
||||||
cb1.SelectedIndex(), cb1.Selection(), cb1.Len(),
|
handler.cb1.SelectedIndex(), handler.cb1.Selection(), handler.cb1.Len(),
|
||||||
cb2.SelectedIndex(), cb2.Selection(), cb2.Len(),
|
handler.cb2.SelectedIndex(), handler.cb2.Selection(), handler.cb2.Len(),
|
||||||
lb1.SelectedIndices(), lb1.Selection(), lb1.Len(),
|
handler.lb1.SelectedIndices(), handler.lb1.Selection(), handler.lb1.Len(),
|
||||||
lb2.SelectedIndices(), lb2.Selection(), lb2.Len()))
|
handler.lb2.SelectedIndices(), handler.lb2.Selection(), handler.lb2.Len()))
|
||||||
case <-incButton.Clicked:
|
case handler.incButton:
|
||||||
prog++
|
handler.prog++
|
||||||
if prog > 100 {
|
if handler.prog > 100 {
|
||||||
prog = 100
|
handler.prog = 100
|
||||||
}
|
}
|
||||||
pbar.SetProgress(prog)
|
handler.pbar.SetProgress(handler.prog)
|
||||||
cb1.Append("append multi 1", "append multi 2")
|
handler.cb1.Append("append multi 1", "append multi 2")
|
||||||
lb2.Append("append multi 1", "append multi 2")
|
handler.lb2.Append("append multi 1", "append multi 2")
|
||||||
case <-decButton.Clicked:
|
case handler.decButton:
|
||||||
prog--
|
handler.prog--
|
||||||
if prog < 0 {
|
if handler.prog < 0 {
|
||||||
prog = 0
|
handler.prog = 0
|
||||||
}
|
}
|
||||||
pbar.SetProgress(prog)
|
handler.pbar.SetProgress(handler.prog)
|
||||||
case <-indetButton.Clicked:
|
case handler.indetButton:
|
||||||
pbar.SetProgress(-1)
|
handler.pbar.SetProgress(-1)
|
||||||
case <-invalidButton.Clicked:
|
case handler.invalidButton:
|
||||||
invalidTest(cb1, lb1, nil, nil)
|
invalidTest(handler.cb1, handler.lb1, nil, nil)
|
||||||
case <-bmsg.Clicked:
|
case handler.bmsg:
|
||||||
MsgBox("Title Only, no parent", "")
|
MsgBox("Title Only, no parent", "")
|
||||||
MsgBox("Title and Text", "parent")
|
handler.w.MsgBox("Title and Text", "parent")
|
||||||
// dialogs
|
}
|
||||||
case <-dialog_bMsgBox.Clicked:
|
// == TODO ==
|
||||||
dialog_sret = dialog_win.MsgBox("Message Box", "Dismiss")
|
// case CusotmEvent:
|
||||||
l.SetText("DIALOG")
|
// l.SetText("DIALOG")
|
||||||
case <-dialog_bMsgBoxError.Clicked:
|
// case CustomEvent + 1:
|
||||||
dialog_sret = dialog_win.MsgBoxError("Message Box", "Dismiss")
|
// resetl()
|
||||||
l.SetText("DIALOG")
|
}
|
||||||
case <-dialog_sret:
|
}
|
||||||
dialog_sret = nil
|
|
||||||
resetl()
|
type dialoghandler struct {
|
||||||
case <-centerButton.Clicked:
|
bMsgBox *Button
|
||||||
dialog_win.Center()
|
bMsgBoxError *Button
|
||||||
|
w *Window
|
||||||
|
bCenter *Button
|
||||||
|
send func(Event, interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// == TODO ==
|
||||||
|
func (handler *dialoghandler) Event(e Event, d interface{}) {
|
||||||
|
if e == Clicked {
|
||||||
|
switch d {
|
||||||
|
case handler.bMsgBox:
|
||||||
|
// handler.send(CustomEvent, "DIALOG")
|
||||||
|
handler.w.MsgBox("Message Box", "Dismiss")
|
||||||
|
// handler.send(CustomEvent, nil)
|
||||||
|
case handler.bMsgBoxError:
|
||||||
|
// handler.send(CustomEvent, "DIALOG")
|
||||||
|
handler.w.MsgBoxError("Message Box", "Dismiss")
|
||||||
|
// handler.send(CustomEvent, nil)
|
||||||
|
case handler.bCenter:
|
||||||
|
handler.w.Center()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.Hide()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
err := Go(myMain)
|
go myMain()
|
||||||
|
err := Go()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO move to its own file
|
||||||
|
|
||||||
// source: https://upload.wikimedia.org/wikipedia/commons/0/06/Regensburg-donaulaende-warp-enhanced_1-320x240.jpg via https://commons.wikimedia.org/wiki/File:Regensburg-donaulaende-warp-enhanced_1-320x240.jpg (thanks Sofi)
|
// source: https://upload.wikimedia.org/wikipedia/commons/0/06/Regensburg-donaulaende-warp-enhanced_1-320x240.jpg via https://commons.wikimedia.org/wiki/File:Regensburg-donaulaende-warp-enhanced_1-320x240.jpg (thanks Sofi)
|
||||||
// converted to png with imagemagick because the JPG was throwing up an error about incomplete Huffman data (TODO)
|
// converted to png with imagemagick because the JPG was throwing up an error about incomplete Huffman data (TODO)
|
||||||
var imagedata = []byte{
|
var imagedata = []byte{
|
||||||
|
|
|
@ -41,14 +41,13 @@ func spaceTest() {
|
||||||
a2 := NewArea(w, h, ah)
|
a2 := NewArea(w, h, ah)
|
||||||
a3 := NewArea(w, h, ah)
|
a3 := NewArea(w, h, ah)
|
||||||
a4 := NewArea(w, h, ah)
|
a4 := NewArea(w, h, ah)
|
||||||
win := NewWindow("Stack", 250, 250)
|
win := NewWindow("Stack", 250, 250, nullwinhandler{})
|
||||||
win.SetSpaced(true)
|
win.SetSpaced(true)
|
||||||
win.Open(f(a1, a2))
|
win.Open(f(a1, a2))
|
||||||
win = NewWindow("Grid", 250, 250)
|
win = NewWindow("Grid", 250, 250, nullwinhandler{})
|
||||||
win.SetSpaced(true)
|
win.SetSpaced(true)
|
||||||
g := NewGrid(ng, a3, a4)
|
g := NewGrid(ng, a3, a4)
|
||||||
g.SetFilling(0, 0)
|
g.SetFilling(0, 0)
|
||||||
g.SetStretchy(gsx, gsy)
|
g.SetStretchy(gsx, gsy)
|
||||||
win.Open(g)
|
win.Open(g)
|
||||||
select {}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,32 +15,31 @@ import "C"
|
||||||
|
|
||||||
var uitask chan func()
|
var uitask chan func()
|
||||||
|
|
||||||
func ui(main func()) error {
|
func uiinit() error {
|
||||||
runtime.LockOSThread()
|
|
||||||
|
|
||||||
uitask = make(chan func())
|
|
||||||
|
|
||||||
err := initCocoa()
|
err := initCocoa()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do this at the end in case something goes wrong
|
||||||
|
uitask = make(chan func())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ui() {
|
||||||
// Cocoa must run on the first thread created by the program, so we run our dispatcher on another thread instead
|
// Cocoa must run on the first thread created by the program, so we run our dispatcher on another thread instead
|
||||||
go func() {
|
go func() {
|
||||||
for f := range uitask {
|
for {
|
||||||
|
select {
|
||||||
|
case f := <-uitask:
|
||||||
C.douitask(appDelegate, unsafe.Pointer(&f))
|
C.douitask(appDelegate, unsafe.Pointer(&f))
|
||||||
}
|
case <-Stop:
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
main()
|
|
||||||
uitask <- func() {
|
|
||||||
C.breakMainLoop()
|
C.breakMainLoop()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
C.cocoaMainLoop()
|
C.cocoaMainLoop()
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initCocoa() (err error) {
|
func initCocoa() (err error) {
|
||||||
|
|
|
@ -6,7 +6,6 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// #cgo pkg-config: gtk+-3.0
|
// #cgo pkg-config: gtk+-3.0
|
||||||
|
@ -15,19 +14,32 @@ import "C"
|
||||||
|
|
||||||
var uitask chan func()
|
var uitask chan func()
|
||||||
|
|
||||||
func ui(main func()) error {
|
func uiinit() error {
|
||||||
runtime.LockOSThread()
|
|
||||||
|
|
||||||
uitask = make(chan func())
|
|
||||||
err := gtk_init()
|
err := gtk_init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("gtk_init() failed: %v", err)
|
return fmt.Errorf("gtk_init() failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do this only on success, just to be safe
|
||||||
|
uitask = make(chan func())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ui() {
|
||||||
// thanks to tristan and Daniel_S in irc.gimp.net/#gtk
|
// thanks to tristan and Daniel_S in irc.gimp.net/#gtk
|
||||||
// see our_idle_callback in callbacks_unix.go for details
|
// see our_idle_callback in callbacks_unix.go for details
|
||||||
go func() {
|
go func() {
|
||||||
for f := range uitask {
|
for {
|
||||||
|
var f func()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case f = <-uitask:
|
||||||
|
// do nothing
|
||||||
|
case <-Stop:
|
||||||
|
f = func() {
|
||||||
|
C.gtk_main_quit()
|
||||||
|
}
|
||||||
|
}
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
gdk_threads_add_idle(>kIdleOp{
|
gdk_threads_add_idle(>kIdleOp{
|
||||||
what: f,
|
what: f,
|
||||||
|
@ -38,11 +50,5 @@ func ui(main func()) error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
|
||||||
main()
|
|
||||||
uitask <- gtk_main_quit
|
|
||||||
}()
|
|
||||||
|
|
||||||
C.gtk_main()
|
C.gtk_main()
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -46,47 +45,51 @@ var (
|
||||||
_postMessage = user32.NewProc("PostMessageW")
|
_postMessage = user32.NewProc("PostMessageW")
|
||||||
)
|
)
|
||||||
|
|
||||||
func ui(main func()) error {
|
var msghwnd _HWND
|
||||||
runtime.LockOSThread()
|
|
||||||
|
|
||||||
uitask = make(chan interface{})
|
func uiinit() error {
|
||||||
err := doWindowsInit()
|
err := doWindowsInit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error doing general Windows initialization: %v", err)
|
return fmt.Errorf("error doing general Windows initialization: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hwnd, err := makeMessageHandler()
|
msghwnd, err = makeMessageHandler()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error making invisible window for handling events: %v", err)
|
return fmt.Errorf("error making invisible window for handling events: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do this only on success just to be safe
|
||||||
|
uitask = make(chan interface{})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ui() {
|
||||||
go func() {
|
go func() {
|
||||||
for m := range uitask {
|
for {
|
||||||
|
select {
|
||||||
|
case m := <-uitask:
|
||||||
r1, _, err := _postMessage.Call(
|
r1, _, err := _postMessage.Call(
|
||||||
uintptr(hwnd),
|
uintptr(msghwnd),
|
||||||
msgRequested,
|
msgRequested,
|
||||||
uintptr(0),
|
uintptr(0),
|
||||||
uintptr(unsafe.Pointer(&m)))
|
uintptr(unsafe.Pointer(&m)))
|
||||||
if r1 == 0 { // failure
|
if r1 == 0 { // failure
|
||||||
panic("error sending message to message loop to call function: " + err.Error())
|
panic("error sending message to message loop to call function: " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
case <-Stop:
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
main()
|
|
||||||
r1, _, err := _postMessage.Call(
|
r1, _, err := _postMessage.Call(
|
||||||
uintptr(hwnd),
|
uintptr(msghwnd),
|
||||||
msgQuit,
|
msgQuit,
|
||||||
uintptr(0),
|
uintptr(0),
|
||||||
uintptr(0))
|
uintptr(0))
|
||||||
if r1 == 0 { // failure
|
if r1 == 0 { // failure
|
||||||
panic("error sending quit message to message loop: " + err.Error())
|
panic("error sending quit message to message loop: " + err.Error())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
msgloop()
|
msgloop()
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
70
window.go
70
window.go
|
@ -4,17 +4,10 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Window represents an on-screen window.
|
// Window represents an on-screen window.
|
||||||
type Window struct {
|
type Window struct {
|
||||||
// Closing gets a message when the user clicks the window's close button.
|
|
||||||
// You cannot change it once the Window has been created.
|
|
||||||
// If you do not respond to this signal, nothing will happen; regardless of whether you handle the signal or not, the window will not be closed.
|
|
||||||
Closing chan struct{}
|
|
||||||
|
|
||||||
lock sync.Mutex
|
|
||||||
created bool
|
created bool
|
||||||
sysData *sysData
|
sysData *sysData
|
||||||
initTitle string
|
initTitle string
|
||||||
|
@ -22,24 +15,44 @@ type Window struct {
|
||||||
initHeight int
|
initHeight int
|
||||||
shownOnce bool
|
shownOnce bool
|
||||||
spaced bool
|
spaced bool
|
||||||
|
handler WindowHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WindowHandler represents an event handler for a Window and all its child Controls.
|
||||||
|
//
|
||||||
|
// When an event on a Window or one of its child Controls comes in, the respect Window's handler's Event() method is called. The method call occurs on the main thread, and thus any call to any package ui method can be performed.
|
||||||
|
//
|
||||||
|
// Each Event() call takes two parameters: the event ID and a data argument. For most events, the data argument is a pointer to the Control that triggered the event.
|
||||||
|
//
|
||||||
|
// For Closing, the data argument is a pointer to a bool variable. If, after returning from Event, the value of this variable is true, the Window is closed; if false, the Window is not closed. The default value on entry to the function is [TODO].
|
||||||
|
//
|
||||||
|
// For any event >= CustomEvent, the data argument is the argument passed to the Window's SendEvent() method.
|
||||||
|
type WindowHandler interface {
|
||||||
|
Event(e Event, data interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event represents an event; see WindowHandler for details.
|
||||||
|
// All event values >= CustomEvent are available for program use.
|
||||||
|
type Event int
|
||||||
|
const (
|
||||||
|
Closing Event = iota // Window close
|
||||||
|
Clicked // Button click
|
||||||
|
CustomEvent = 5000 // very high number; higher than the package would ever need, anyway
|
||||||
|
)
|
||||||
|
|
||||||
// NewWindow allocates a new Window with the given title and size. The window is not created until a call to Create() or Open().
|
// NewWindow allocates a new Window with the given title and size. The window is not created until a call to Create() or Open().
|
||||||
func NewWindow(title string, width int, height int) *Window {
|
func NewWindow(title string, width int, height int, handler WindowHandler) *Window {
|
||||||
return &Window{
|
return &Window{
|
||||||
sysData: mksysdata(c_window),
|
sysData: mksysdata(c_window),
|
||||||
initTitle: title,
|
initTitle: title,
|
||||||
initWidth: width,
|
initWidth: width,
|
||||||
initHeight: height,
|
initHeight: height,
|
||||||
Closing: newEvent(),
|
handler: handler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTitle sets the window's title.
|
// SetTitle sets the window's title.
|
||||||
func (w *Window) SetTitle(title string) {
|
func (w *Window) SetTitle(title string) {
|
||||||
w.lock.Lock()
|
|
||||||
defer w.lock.Unlock()
|
|
||||||
|
|
||||||
if w.created {
|
if w.created {
|
||||||
w.sysData.setText(title)
|
w.sysData.setText(title)
|
||||||
return
|
return
|
||||||
|
@ -49,9 +62,6 @@ func (w *Window) SetTitle(title string) {
|
||||||
|
|
||||||
// SetSize sets the window's size.
|
// SetSize sets the window's size.
|
||||||
func (w *Window) SetSize(width int, height int) (err error) {
|
func (w *Window) SetSize(width int, height int) (err error) {
|
||||||
w.lock.Lock()
|
|
||||||
defer w.lock.Unlock()
|
|
||||||
|
|
||||||
if w.created {
|
if w.created {
|
||||||
err := w.sysData.setWindowSize(width, height)
|
err := w.sysData.setWindowSize(width, height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -70,9 +80,6 @@ func (w *Window) SetSize(width int, height int) (err error) {
|
||||||
// This property is visible recursively throughout the widget hierarchy of the Window.
|
// This property is visible recursively throughout the widget hierarchy of the Window.
|
||||||
// This property cannot be set after the Window has been created.
|
// This property cannot be set after the Window has been created.
|
||||||
func (w *Window) SetSpaced(spaced bool) {
|
func (w *Window) SetSpaced(spaced bool) {
|
||||||
w.lock.Lock()
|
|
||||||
defer w.lock.Unlock()
|
|
||||||
|
|
||||||
if w.created {
|
if w.created {
|
||||||
panic(fmt.Errorf("Window.SetSpaced() called after window created"))
|
panic(fmt.Errorf("Window.SetSpaced() called after window created"))
|
||||||
}
|
}
|
||||||
|
@ -81,20 +88,24 @@ func (w *Window) SetSpaced(spaced bool) {
|
||||||
|
|
||||||
// Open creates the Window with Create and then shows the Window with Show. As with Create, you cannot call Open more than once per window.
|
// Open creates the Window with Create and then shows the Window with Show. As with Create, you cannot call Open more than once per window.
|
||||||
func (w *Window) Open(control Control) {
|
func (w *Window) Open(control Control) {
|
||||||
w.Create(control)
|
w.create(control, true)
|
||||||
w.Show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates the Window, setting its control to the given control. It does not show the window. This can only be called once per window, and finalizes all initialization of the control.
|
// Create creates the Window, setting its control to the given control. It does not show the window. This can only be called once per window, and finalizes all initialization of the control.
|
||||||
func (w *Window) Create(control Control) {
|
func (w *Window) Create(control Control) {
|
||||||
w.lock.Lock()
|
w.create(control, false)
|
||||||
defer w.lock.Unlock()
|
}
|
||||||
|
|
||||||
|
func (w *Window) create(control Control, show bool) {
|
||||||
|
touitask(func() {
|
||||||
if w.created {
|
if w.created {
|
||||||
panic("window already open")
|
panic("window already open")
|
||||||
}
|
}
|
||||||
w.sysData.spaced = w.spaced
|
w.sysData.spaced = w.spaced
|
||||||
w.sysData.event = w.Closing
|
w.sysData.winhandler = w.handler
|
||||||
|
w.sysData.close = func(b *bool) {
|
||||||
|
w.sysData.winhandler.Event(Closing, b)
|
||||||
|
}
|
||||||
err := w.sysData.make(nil)
|
err := w.sysData.make(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("error opening window: %v", err))
|
panic(fmt.Errorf("error opening window: %v", err))
|
||||||
|
@ -112,13 +123,14 @@ func (w *Window) Create(control Control) {
|
||||||
}
|
}
|
||||||
w.sysData.setText(w.initTitle)
|
w.sysData.setText(w.initTitle)
|
||||||
w.created = true
|
w.created = true
|
||||||
|
if show {
|
||||||
|
w.Show()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show shows the window.
|
// Show shows the window.
|
||||||
func (w *Window) Show() {
|
func (w *Window) Show() {
|
||||||
w.lock.Lock()
|
|
||||||
defer w.lock.Unlock()
|
|
||||||
|
|
||||||
if !w.shownOnce {
|
if !w.shownOnce {
|
||||||
w.shownOnce = true
|
w.shownOnce = true
|
||||||
err := w.sysData.firstShow()
|
err := w.sysData.firstShow()
|
||||||
|
@ -132,9 +144,6 @@ func (w *Window) Show() {
|
||||||
|
|
||||||
// Hide hides the window.
|
// Hide hides the window.
|
||||||
func (w *Window) Hide() {
|
func (w *Window) Hide() {
|
||||||
w.lock.Lock()
|
|
||||||
defer w.lock.Unlock()
|
|
||||||
|
|
||||||
w.sysData.hide()
|
w.sysData.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,9 +151,6 @@ func (w *Window) Hide() {
|
||||||
// The concept of "screen" in the case of a multi-monitor setup is implementation-defined.
|
// The concept of "screen" in the case of a multi-monitor setup is implementation-defined.
|
||||||
// It presently panics if the Window has not been created.
|
// It presently panics if the Window has not been created.
|
||||||
func (w *Window) Center() {
|
func (w *Window) Center() {
|
||||||
w.lock.Lock()
|
|
||||||
defer w.lock.Unlock()
|
|
||||||
|
|
||||||
if !w.created {
|
if !w.created {
|
||||||
panic("attempt to center Window before it has been created")
|
panic("attempt to center Window before it has been created")
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ const _FALSE = 0
|
||||||
const _GWLP_USERDATA = -21
|
const _GWLP_USERDATA = -21
|
||||||
const _GWL_STYLE = -16
|
const _GWL_STYLE = -16
|
||||||
const _ICC_PROGRESS_CLASS = 32
|
const _ICC_PROGRESS_CLASS = 32
|
||||||
|
const _IDOK = 1
|
||||||
const _LBS_EXTENDEDSEL = 2048
|
const _LBS_EXTENDEDSEL = 2048
|
||||||
const _LBS_NOINTEGRALHEIGHT = 256
|
const _LBS_NOINTEGRALHEIGHT = 256
|
||||||
const _LBS_NOTIFY = 1
|
const _LBS_NOTIFY = 1
|
||||||
|
|
|
@ -35,6 +35,7 @@ const _FALSE = 0
|
||||||
const _GWLP_USERDATA = -21
|
const _GWLP_USERDATA = -21
|
||||||
const _GWL_STYLE = -16
|
const _GWL_STYLE = -16
|
||||||
const _ICC_PROGRESS_CLASS = 32
|
const _ICC_PROGRESS_CLASS = 32
|
||||||
|
const _IDOK = 1
|
||||||
const _LBS_EXTENDEDSEL = 2048
|
const _LBS_EXTENDEDSEL = 2048
|
||||||
const _LBS_NOINTEGRALHEIGHT = 256
|
const _LBS_NOINTEGRALHEIGHT = 256
|
||||||
const _LBS_NOTIFY = 1
|
const _LBS_NOTIFY = 1
|
||||||
|
|
Loading…
Reference in New Issue