Skip to content

Commit

Permalink
Don't ask...
Browse files Browse the repository at this point in the history
  • Loading branch information
lxn committed Jun 25, 2015
1 parent a4d0287 commit 49c0764
Show file tree
Hide file tree
Showing 58 changed files with 1,515 additions and 772 deletions.
31 changes: 31 additions & 0 deletions button.go
Expand Up @@ -17,6 +17,7 @@ type Button struct {
checkedChangedPublisher EventPublisher
clickedPublisher EventPublisher
textChangedPublisher EventPublisher
image Image
}

func (b *Button) init() {
Expand All @@ -40,6 +41,36 @@ func (b *Button) init() {
b.textChangedPublisher.Event()))
}

func (b *Button) Image() Image {
return b.image
}

func (b *Button) SetImage(image Image) error {
var typ uintptr
var handle uintptr
switch img := image.(type) {
case nil:
// zeroes are good

case *Bitmap:
typ = win.IMAGE_BITMAP
handle = uintptr(img.hBmp)

case *Icon:
typ = win.IMAGE_ICON
handle = uintptr(img.hIcon)

default:
return newError("image must be either *walk.Bitmap or *walk.Icon")
}

b.SendMessage(win.BM_SETIMAGE, typ, handle)

b.image = image

return b.updateParentLayout()
}

func (b *Button) Text() string {
return windowText(b.hWnd)
}
Expand Down
33 changes: 30 additions & 3 deletions combobox.go
Expand Up @@ -126,6 +126,18 @@ func newComboBoxWithStyle(parent Container, style uint32) (*ComboBox, error) {
},
cb.TextChanged()))

cb.MustRegisterProperty("HasCurrentItem", NewReadOnlyBoolProperty(
func() bool {
return cb.CurrentIndex() != -1
},
cb.CurrentIndexChanged()))

cb.MustRegisterProperty("TextNotEmpty", NewReadOnlyBoolProperty(
func() bool {
return cb.Text() != ""
},
cb.CurrentIndexChanged()))

cb.MustRegisterProperty("Value", NewProperty(
func() interface{} {
if cb.Editable() {
Expand All @@ -146,7 +158,11 @@ func newComboBoxWithStyle(parent Container, style uint32) (*ComboBox, error) {
}

if cb.bindingValueProvider == nil {
return newError("Data binding is only supported using a model that implements BindingValueProvider.")
if cb.model == nil {
return nil
} else {
return newError("Data binding is only supported using a model that implements BindingValueProvider.")
}
}

index := -1
Expand Down Expand Up @@ -180,7 +196,7 @@ func (cb *ComboBox) MinSizeHint() Size {
}

// FIXME: Use GetThemePartSize instead of guessing
w := maxi(defaultSize.Width, cb.maxItemTextWidth+26)
w := maxi(defaultSize.Width, cb.maxItemTextWidth+30)
h := defaultSize.Height + 1

return Size{w, h}
Expand Down Expand Up @@ -227,6 +243,8 @@ func (cb *ComboBox) resetItems() error {
cb.SetSuspended(true)
defer cb.SetSuspended(false)

cb.selChangeIndex = -1

if win.FALSE == cb.SendMessage(win.CB_RESETCONTENT, 0, 0) {
return newError("SendMessage(CB_RESETCONTENT)")
}
Expand All @@ -247,6 +265,8 @@ func (cb *ComboBox) resetItems() error {
}
}

cb.updateParentLayout()

return nil
}

Expand Down Expand Up @@ -525,7 +545,9 @@ func (cb *ComboBox) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) u

case win.CBN_SELENDCANCEL:
if cb.selChangeIndex != -1 {
cb.SetCurrentIndex(cb.selChangeIndex)
if cb.selChangeIndex < cb.model.ItemCount() {
cb.SetCurrentIndex(cb.selChangeIndex)
}

cb.selChangeIndex = -1
}
Expand All @@ -542,6 +564,11 @@ func (cb *ComboBox) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) u

cb.selChangeIndex = -1
}

case win.WM_MOUSEWHEEL:
if !cb.Enabled() {
return 0
}
}

return cb.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
Expand Down
2 changes: 1 addition & 1 deletion container.go
Expand Up @@ -35,7 +35,7 @@ func shouldLayoutWidget(widget Widget) bool {

_, isSpacer := widget.(*Spacer)

return isSpacer || widget.AsWindowBase().visible
return isSpacer || widget.AsWindowBase().visible || widget.AlwaysConsumeSpace()
}

func DescendantByName(container Container, name string) Widget {
Expand Down
114 changes: 84 additions & 30 deletions databinding.go
Expand Up @@ -31,6 +31,7 @@ type DataBinder struct {
autoSubmit bool
canSubmit bool
inReset bool
dirty bool
}

func NewDataBinder() *DataBinder {
Expand Down Expand Up @@ -93,13 +94,15 @@ func (db *DataBinder) SetBoundWidgets(boundWidgets []Widget) {
db.property2Widget[prop] = widget

db.property2ChangedHandle[prop] = prop.Changed().Attach(func() {
db.dirty = true

if db.autoSubmit {
if prop.Get() == nil {
return
}

p, s := db.reflectValuesFromDataSource()
field := db.fieldBoundToProperty(p, s, prop)
v := reflect.ValueOf(db.dataSource)
field := db.fieldBoundToProperty(v, prop)
if !field.IsValid() {
return
}
Expand Down Expand Up @@ -230,6 +233,8 @@ func (db *DataBinder) Reset() error {

db.validateProperties()

db.dirty = false

return nil
}

Expand All @@ -244,12 +249,23 @@ func (db *DataBinder) Submit() error {
return err
}

db.dirty = false

db.submittedPublisher.Publish()

return nil
}

func (db *DataBinder) Dirty() bool {
return db.dirty
}

func (db *DataBinder) submitProperty(prop Property, field reflect.Value) error {
if !field.CanSet() {
// FIXME: handle properly
return nil
}

value := prop.Get()
if value == nil {
// This happens e.g. if CurrentIndex() of a ComboBox returns -1.
Expand Down Expand Up @@ -284,13 +300,13 @@ func (db *DataBinder) submitProperty(prop Property, field reflect.Value) error {
}

func (db *DataBinder) forEach(f func(prop Property, field reflect.Value) error) error {
p, s := db.reflectValuesFromDataSource()
if p.IsNil() {
dsv := reflect.ValueOf(db.dataSource)
if dsv.Kind() == reflect.Ptr && dsv.IsNil() {
return nil
}

for _, prop := range db.properties {
field := db.fieldBoundToProperty(p, s, prop)
field := db.fieldBoundToProperty(dsv, prop)

if err := f(prop, field); err != nil {
return err
Expand All @@ -300,40 +316,78 @@ func (db *DataBinder) forEach(f func(prop Property, field reflect.Value) error)
return nil
}

func (db *DataBinder) reflectValuesFromDataSource() (p, s reflect.Value) {
p = reflect.ValueOf(db.dataSource)
if p.IsNil() {
return
func (db *DataBinder) fieldBoundToProperty(v reflect.Value, prop Property) reflect.Value {
source := prop.Source().(string)
path := strings.Split(source, ".")

vv, err := reflectValueFromPath(v, path)
if err != nil {
panic(fmt.Sprintf("invalid source '%s'", source))
}

s = p.Elem()
return vv
}

return
func validateBindingMemberSyntax(member string) error {
// FIXME
return nil
}

func (db *DataBinder) fieldBoundToProperty(p, s reflect.Value, prop Property) reflect.Value {
var v reflect.Value
source := prop.Source().(string)
path := strings.Split(source, ".")
func reflectValueFromPath(root reflect.Value, path []string) (reflect.Value, error) {
v := root

for i, name := range path {
v = s.FieldByName(name)
if !v.IsValid() {
panic(fmt.Sprintf("invalid element '%s' in source '%s'", name, source))
}
if i < len(path)-1 {
for _, name := range path {
var p reflect.Value
for v.Kind() == reflect.Ptr {
p = v
if p.IsNil() {
return reflect.Value{}
v = v.Elem()
}

// Try as field first.
var f reflect.Value
if v.Kind() == reflect.Struct {
f = v.FieldByName(name)
}
if f.IsValid() {
v = f
} else {
// No field, so let's see if we got a method.
var m reflect.Value
if p.IsValid() {
// Try pointer receiver first.
m = p.MethodByName(name)
}

if !m.IsValid() {
// No pointer, try directly.
m = v.MethodByName(name)
}
if !m.IsValid() {
return v, fmt.Errorf("bad member: '%s'", strings.Join(path, "."))
}

// We assume it takes no args and returns one mandatory value plus
// maybe an error.
rvs := m.Call(nil)
switch len(rvs) {
case 1:
v = rvs[0]

case 2:
rv2 := rvs[1].Interface()
if err, ok := rv2.(error); ok {
return v, err
} else if rv2 != nil {
return v, fmt.Errorf("Second method return value must implement error.")
}

v = rvs[0]

default:
return v, fmt.Errorf("Method must return a value plus optionally an error: %s", name)
}
s = p.Elem()
}
}

return v
}

func validateBindingMemberSyntax(member string) error {
// FIXME
return nil
return v, nil
}

0 comments on commit 49c0764

Please sign in to comment.