Skip to content

Commit

Permalink
Add ToolTipErrorPresenter
Browse files Browse the repository at this point in the history
  • Loading branch information
lxn committed Apr 11, 2017
1 parent db3b0a2 commit 7cea49e
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 4 deletions.
5 changes: 5 additions & 0 deletions application.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type Application struct {
settings Settings
exiting bool
exitCode int
activeForm Form
panickingPublisher ErrorEventPublisher
}

Expand Down Expand Up @@ -85,3 +86,7 @@ func (app *Application) ExitCode() int {
func (app *Application) Panicking() *ErrorEvent {
return app.panickingPublisher.Event()
}

func (app *Application) ActiveForm() Form {
return app.activeForm
}
6 changes: 6 additions & 0 deletions declarative/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ func (b *Builder) InitWidget(d Widget, w walk.Window, customInit func() error) e
return err
} else {
db = dataB

if ep := db.ErrorPresenter(); ep != nil {
if dep, ok := ep.(walk.Disposable); ok {
wc.AddDisposable(dep)
}
}
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions form.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,16 @@ type FormBase struct {
clientComposite *Composite
owner Form
closingPublisher CloseEventPublisher
activatingPublisher EventPublisher
deactivatingPublisher EventPublisher
startingPublisher EventPublisher
titleChangedPublisher EventPublisher
iconChangedPublisher EventPublisher
progressIndicator *ProgressIndicator
icon *Icon
prevFocusHWnd win.HWND
isInRestoreState bool
started bool
closeReason CloseReason
}

Expand Down Expand Up @@ -307,6 +310,7 @@ func (fb *FormBase) Run() int {

fb.focusFirstCandidateDescendant()

fb.started = true
fb.startingPublisher.Publish()

var msg win.MSG
Expand Down Expand Up @@ -505,9 +509,18 @@ func (fb *FormBase) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) u
win.SetFocus(fb.prevFocusHWnd)
}

appSingleton.activeForm = fb.window.(Form)

fb.activatingPublisher.Publish()

case win.WA_INACTIVE:
fb.prevFocusHWnd = win.GetFocus()

appSingleton.activeForm = nil

fb.deactivatingPublisher.Publish()
}

return 0

case win.WM_CLOSE:
Expand Down
83 changes: 79 additions & 4 deletions tooltip.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,25 @@ type ToolTip struct {
var globalToolTip *ToolTip

func NewToolTip() (*ToolTip, error) {
tt, err := newToolTip(0)
if err != nil {
return nil, err
}

win.SetWindowPos(tt.hWnd, win.HWND_TOPMOST, 0, 0, 0, 0, win.SWP_NOMOVE|win.SWP_NOSIZE|win.SWP_NOACTIVATE)

return tt, nil
}

func newToolTip(style uint32) (*ToolTip, error) {
tt := new(ToolTip)

if err := InitWindow(
tt,
nil,
"tooltips_class32",
win.WS_POPUP|win.TTS_ALWAYSTIP,
win.WS_EX_TOPMOST); err != nil {
win.WS_DISABLED|win.WS_POPUP|win.TTS_ALWAYSTIP|style,
0); err != nil {
return nil, err
}

Expand All @@ -50,7 +61,7 @@ func NewToolTip() (*ToolTip, error) {
}
}()

win.SetWindowPos(tt.hWnd, win.HWND_TOPMOST, 0, 0, 0, 0, win.SWP_NOMOVE|win.SWP_NOSIZE|win.SWP_NOACTIVATE)
tt.SendMessage(win.TTM_SETMAXTIPWIDTH, 0, 300)

succeeded = true

Expand Down Expand Up @@ -107,13 +118,77 @@ func (tt *ToolTip) setTitle(title string, icon uintptr) error {
return nil
}

func (tt *ToolTip) track(tool Widget) error {
form := tool.Form()
if form == nil {
return nil
}
// HACK: We may have to delay this until the form is fully up to avoid glitches.
if !form.AsFormBase().started {
var handle int
handle = form.Starting().Attach(func() {
form.Starting().Detach(handle)
tt.track(tool)
})
return nil
}

ti := tt.toolInfo(tool)
if ti == nil {
return newError("unknown tool")
}

tt.SendMessage(win.TTM_TRACKACTIVATE, 1, uintptr(unsafe.Pointer(ti)))

b := tool.Bounds()
p := win.POINT{X: int32(b.X + b.Width/2), Y: int32(b.Y + b.Height)}

win.ClientToScreen(tool.Parent().Handle(), &p)

tt.SendMessage(win.TTM_TRACKPOSITION, 0, uintptr(win.MAKELONG(uint16(p.X), uint16(p.Y))))

var insertAfterHWND win.HWND
if form := tool.Form(); form != nil && win.GetForegroundWindow() == form.Handle() {
insertAfterHWND = win.HWND_TOP
} else {
insertAfterHWND = tool.Handle()
}
win.SetWindowPos(tt.hWnd, insertAfterHWND, 0, 0, 0, 0, win.SWP_NOMOVE|win.SWP_NOSIZE|win.SWP_NOACTIVATE)

return nil
}

func (tt *ToolTip) untrack(tool Widget) error {
ti := tt.toolInfo(tool)
if ti == nil {
return newError("unknown tool")
}

tt.SendMessage(win.TTM_TRACKACTIVATE, 0, uintptr(unsafe.Pointer(ti)))

return nil
}

func (tt *ToolTip) AddTool(tool Widget) error {
return tt.addTool(tool, false)
}

func (tt *ToolTip) addTrackedTool(tool Widget) error {
return tt.addTool(tool, true)
}

func (tt *ToolTip) addTool(tool Widget, track bool) error {
hwnd := tool.Handle()

var ti win.TOOLINFO
ti.CbSize = uint32(unsafe.Sizeof(ti))
ti.Hwnd = hwnd
ti.UFlags = win.TTF_IDISHWND | win.TTF_SUBCLASS
ti.UFlags = win.TTF_IDISHWND
if track {
ti.UFlags |= win.TTF_TRACK
} else {
ti.UFlags |= win.TTF_SUBCLASS
}
ti.UId = uintptr(hwnd)

if win.FALSE == tt.SendMessage(win.TTM_ADDTOOL, 0, uintptr(unsafe.Pointer(&ti))) {
Expand Down
154 changes: 154 additions & 0 deletions tooltiperrorpresenter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package walk

import (
"github.com/lxn/win"
)

type ToolTipErrorPresenter struct {
toolTip *ToolTip
curWidget Widget
widget2error map[Widget]error
trackedBoundsChangedHandles map[Window]int
form Form
formActivatingHandle int
formDeactivatingHandle int
}

func NewToolTipErrorPresenter() (*ToolTipErrorPresenter, error) {
tt, err := newToolTip(win.TTS_BALLOON)
if err != nil {
return nil, err
}

succeeded := false
defer func() {
if !succeeded {
tt.Dispose()
}
}()

succeeded = true

return &ToolTipErrorPresenter{
toolTip: tt,
widget2error: make(map[Widget]error),
trackedBoundsChangedHandles: make(map[Window]int),
formActivatingHandle: -1,
formDeactivatingHandle: -1,
}, nil
}

func (ttep *ToolTipErrorPresenter) Dispose() {
if ttep.toolTip != nil {
ttep.untrack()
ttep.toolTip.Dispose()
ttep.toolTip = nil
if ttep.form != nil {
ttep.form.AsFormBase().activatingPublisher.event.Detach(ttep.formActivatingHandle)
ttep.form.AsFormBase().deactivatingPublisher.event.Detach(ttep.formDeactivatingHandle)
ttep.form = nil
}
}
}

func (ttep *ToolTipErrorPresenter) PresentError(err error, widget Widget) {
if ttep.toolTip == nil {
return
}

if err == nil && widget == ttep.curWidget {
ttep.untrack()
}

if err == nil {
ttep.toolTip.RemoveTool(widget)
delete(ttep.widget2error, widget)
} else {
ttep.toolTip.addTrackedTool(widget)
ttep.widget2error[widget] = err
}

var found bool
if widget != nil {
walkDescendants(widget.Form().AsFormBase().clientComposite, func(w Window) bool {
wt := w.(Widget)

if !found {
if e, ok := ttep.widget2error[wt]; ok {
err, widget, found = e, wt, true
}
}

return true
})
}

if found {
if widget != ttep.curWidget {
ttep.untrack()
}

if ve, ok := err.(*ValidationError); ok {
ttep.toolTip.SetErrorTitle(ve.title)
ttep.toolTip.SetText(widget, ve.message)
} else {
ttep.toolTip.SetErrorTitle(tr("Invalid Input"))
ttep.toolTip.SetText(widget, err.Error())
}

if widget != ttep.curWidget {
ttep.track(widget)
}
}
}

func (ttep *ToolTipErrorPresenter) track(widget Widget) {
var wnd Window

wnd = widget

for wnd != nil {
handle := wnd.AsWindowBase().boundsChangedPublisher.event.Attach(func() {
ttep.toolTip.track(widget)
})

ttep.trackedBoundsChangedHandles[wnd] = handle

if ttep.form == nil {
ttep.form = widget.Form()
ttep.formActivatingHandle = ttep.form.AsFormBase().activatingPublisher.event.Attach(func() {
ttep.toolTip.track(widget)
})
ttep.formDeactivatingHandle = ttep.form.AsFormBase().deactivatingPublisher.event.Attach(func() {
ttep.toolTip.track(widget)
})
}

if w, ok := wnd.(Widget); ok {
if parent := w.Parent(); parent != nil {
wnd = parent
}
} else {
break
}
}

ttep.toolTip.track(widget)

ttep.curWidget = widget
}

func (ttep *ToolTipErrorPresenter) untrack() {
if ttep.curWidget == nil {
return
}

ttep.toolTip.untrack(ttep.curWidget)

for wnd, handle := range ttep.trackedBoundsChangedHandles {
wnd.AsWindowBase().boundsChangedPublisher.event.Detach(handle)
delete(ttep.trackedBoundsChangedHandles, wnd)
}

ttep.curWidget = nil
}
4 changes: 4 additions & 0 deletions window.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ type WindowBase struct {
mouseUpPublisher MouseEventPublisher
mouseMovePublisher MouseEventPublisher
mouseWheelPublisher MouseEventPublisher
boundsChangedPublisher EventPublisher
sizeChangedPublisher EventPublisher
maxSize Size
minSize Size
Expand Down Expand Up @@ -1523,6 +1524,9 @@ func (wb *WindowBase) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr)
case win.WM_SIZE, win.WM_SIZING:
wb.sizeChangedPublisher.Publish()

case win.WM_WINDOWPOSCHANGED:
wb.boundsChangedPublisher.Publish()

case win.WM_DESTROY:
if wb.origWndProcPtr != 0 {
// As we subclass all windows of system classes, we prevented the
Expand Down

0 comments on commit 7cea49e

Please sign in to comment.