From 2b4f36e26fee16f1cea831e5f7003acd9f9115f6 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Mon, 25 Feb 2019 16:45:48 +0100 Subject: [PATCH] Add two-way binding for checked menu item to bool property via Action.Checked --- action.go | 42 +++++++++++++++++++++++++++++++++++++ declarative/action.go | 9 ++++++-- examples/actions/actions.go | 13 ++++++++++++ menu.go | 33 +++++++++++++++++++++++++---- 4 files changed, 91 insertions(+), 6 deletions(-) diff --git a/action.go b/action.go index 4c106dd9..554cdc3f 100644 --- a/action.go +++ b/action.go @@ -27,6 +27,8 @@ type Action struct { text string toolTip string image *Bitmap + checkedCondition Condition + checkedConditionChangedHandle int enabledCondition Condition enabledConditionChangedHandle int visibleCondition Condition @@ -114,6 +116,16 @@ func (a *Action) Checked() bool { } func (a *Action) SetChecked(value bool) (err error) { + if a.checkedCondition != nil { + if bp, ok := a.checkedCondition.(*boolProperty); ok { + if err := bp.Set(value); err != nil { + return err + } + } else { + return newError("CheckedCondition != nil") + } + } + if value != a.checked { old := a.checked @@ -128,6 +140,32 @@ func (a *Action) SetChecked(value bool) (err error) { return } +func (a *Action) CheckedCondition() Condition { + return a.checkedCondition +} + +func (a *Action) SetCheckedCondition(c Condition) { + if a.checkedCondition != nil { + a.checkedCondition.Changed().Detach(a.checkedConditionChangedHandle) + } + + a.checkedCondition = c + + if c != nil { + a.checked = c.Satisfied() + + a.checkedConditionChangedHandle = c.Changed().Attach(func() { + if a.checked != c.Satisfied() { + a.checked = !a.checked + + a.raiseChanged() + } + }) + } + + a.raiseChanged() +} + func (a *Action) Enabled() bool { return a.enabled } @@ -341,6 +379,10 @@ func (a *Action) Triggered() *Event { } func (a *Action) raiseTriggered() { + if a.Checkable() { + a.SetChecked(!a.Checked()) + } + a.triggeredPublisher.Publish() } diff --git a/declarative/action.go b/declarative/action.go index ef489e2d..ca8e2465 100644 --- a/declarative/action.go +++ b/declarative/action.go @@ -25,6 +25,7 @@ type Action struct { AssignTo **walk.Action Text string Image interface{} + Checked Property Enabled Property Visible Property Shortcut Shortcut @@ -45,10 +46,10 @@ func (a Action) createAction(builder *Builder, menu *walk.Menu) (*walk.Action, e if err := setActionImage(action, a.Image); err != nil { return nil, err } - if err := action.SetCheckable(a.Checkable); err != nil { + + if err := setActionBoolOrCondition(action.SetChecked, action.SetCheckedCondition, a.Checked, "Action.Checked", builder); err != nil { return nil, err } - if err := setActionBoolOrCondition(action.SetEnabled, action.SetEnabledCondition, a.Enabled, "Action.Enabled", builder); err != nil { return nil, err } @@ -56,6 +57,10 @@ func (a Action) createAction(builder *Builder, menu *walk.Menu) (*walk.Action, e return nil, err } + if err := action.SetCheckable(a.Checkable || action.CheckedCondition() != nil); err != nil { + return nil, err + } + s := a.Shortcut if err := action.SetShortcut(walk.Shortcut{s.Modifiers, s.Key}); err != nil { return nil, err diff --git a/examples/actions/actions.go b/examples/actions/actions.go index 7ed411c0..df0ff505 100644 --- a/examples/actions/actions.go +++ b/examples/actions/actions.go @@ -55,6 +55,19 @@ func main() { }, }, }, + Menu{ + Text: "&View", + Items: []MenuItem{ + Action{ + Text: "Open / Special Enabled", + Checked: Bind("enabledCB.Visible"), + }, + Action{ + Text: "Open Hidden", + Checked: Bind("openHiddenCB.Visible"), + }, + }, + }, Menu{ Text: "&Help", Items: []MenuItem{ diff --git a/menu.go b/menu.go index 54473289..58493c00 100644 --- a/menu.go +++ b/menu.go @@ -10,9 +10,7 @@ import ( "fmt" "syscall" "unsafe" -) -import ( "github.com/lxn/win" ) @@ -83,9 +81,9 @@ func (m *Menu) initMenuItemInfoFromAction(mii *win.MENUITEMINFO, action *Action) mii.HbmpItem = action.image.handle() } if action.IsSeparator() { - mii.FType = win.MFT_SEPARATOR + mii.FType |= win.MFT_SEPARATOR } else { - mii.FType = win.MFT_STRING + mii.FType |= win.MFT_STRING var text string if s := action.shortcut; s.Key != 0 { text = fmt.Sprintf("%s\t%s", action.text, s.String()) @@ -109,6 +107,9 @@ func (m *Menu) initMenuItemInfoFromAction(mii *win.MENUITEMINFO, action *Action) if action.Checked() { mii.FState |= win.MFS_CHECKED } + if action.Exclusive() { + mii.FType |= win.MFT_RADIOCHECK + } menu := action.menu if menu != nil { @@ -130,6 +131,30 @@ func (m *Menu) onActionChanged(action *Action) error { return newError("SetMenuItemInfo failed") } + if action.Exclusive() && action.Checked() { + var first, last int + + index := m.actions.Index(action) + + for i := index; i >= 0; i-- { + first = i + if !m.actions.At(i).Exclusive() { + break + } + } + + for i := index; i < m.actions.Len(); i++ { + last = i + if !m.actions.At(i).Exclusive() { + break + } + } + + if !win.CheckMenuRadioItem(m.hMenu, uint32(first), uint32(last), uint32(index), win.MF_BYPOSITION) { + return newError("CheckMenuRadioItem failed") + } + } + return nil }