diff options
| author | Pietro Gagliardi <[email protected]> | 2015-12-11 20:37:59 -0500 |
|---|---|---|
| committer | Pietro Gagliardi <[email protected]> | 2015-12-11 20:37:59 -0500 |
| commit | f8e3f12ab02b528f2a05a4f713d7af7ea8e44b42 (patch) | |
| tree | 82dedf4d37f0f6d31e88ebb2ca1ce6499dead261 /prev/spinbox_windows.go | |
| parent | e34c561ed5bedeb180437ec165882b98d70d38c1 (diff) | |
LET'S GET THIS FINAL REWRITE EVER STARTED
Diffstat (limited to 'prev/spinbox_windows.go')
| -rw-r--r-- | prev/spinbox_windows.go | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/prev/spinbox_windows.go b/prev/spinbox_windows.go new file mode 100644 index 0000000..05dd928 --- /dev/null +++ b/prev/spinbox_windows.go @@ -0,0 +1,148 @@ +// 28 october 2014 + +package ui + +import ( + "strconv" + "unsafe" +) + +// #include "winapi_windows.h" +import "C" + +// TODO do we have to manually monitor user changes to the edit control? +// TODO WS_EX_CLIENTEDGE on the updown? + +type spinbox struct { + hwndEdit C.HWND + hwndUpDown C.HWND + changed *event + // updown state + updownVisible bool + // keep these here to avoid having to get them out + value int + min int + max int +} + +func newSpinbox(min int, max int) Spinbox { + s := new(spinbox) + s.hwndEdit = C.newControl(editclass, + C.textfieldStyle | C.ES_NUMBER, + C.textfieldExtStyle) + s.changed = newEvent() + s.updownVisible = true // initially shown + s.min = min + s.max = max + s.value = s.min + s.remakeUpDown() + C.controlSetControlFont(s.hwndEdit) + C.setSpinboxEditSubclass(s.hwndEdit, unsafe.Pointer(s)) + return s +} + +func (s *spinbox) cap() { + if s.value < s.min { + s.value = s.min + } + if s.value > s.max { + s.value = s.max + } +} + +func (s *spinbox) Value() int { + return s.value +} + +func (s *spinbox) SetValue(value int) { + // UDM_SETPOS32 is documented to do what we want, but since we're keeping a copy of value we need to do it anyway + s.value = value + s.cap() + C.SendMessageW(s.hwndUpDown, C.UDM_SETPOS32, 0, C.LPARAM(s.value)) +} + +func (s *spinbox) OnChanged(e func()) { + s.changed.set(e) +} + +//export spinboxUpDownClicked +func spinboxUpDownClicked(data unsafe.Pointer, nud *C.NMUPDOWN) { + // this is where we do custom increments + s := (*spinbox)(data) + s.value = int(nud.iPos + nud.iDelta) + // this can go above or below the bounds (the spinbox only rejects invalid values after the UDN_DELTAPOS notification is processed) + // because we have a copy of the value, we need to fix that here + s.cap() + s.changed.fire() +} + +//export spinboxEditChanged +func spinboxEditChanged(data unsafe.Pointer) { + // we're basically on our own here + s := (*spinbox)(unsafe.Pointer(data)) + // this basically does what OS X does: values too low get clamped to the minimum, values too high get clamped to the maximum, and deleting everything clamps to the minimum + value, err := strconv.Atoi(getWindowText(s.hwndEdit)) + if err != nil { + // best we can do fo rnow in this case :S + // a partial atoi() like in C would be more optimal + // it handles the deleting everything case just fine + value = s.min + } + s.value = value + s.cap() + C.SendMessageW(s.hwndUpDown, C.UDM_SETPOS32, 0, C.LPARAM(s.value)) + // TODO position the insertion caret at the end (or wherever is appropriate) + s.changed.fire() +} + +func (s *spinbox) setParent(p *controlParent) { + C.controlSetParent(s.hwndEdit, p.hwnd) + C.controlSetParent(s.hwndUpDown, p.hwnd) +} + +// an up-down control will only properly position itself the first time +// stupidly, there are no messages to force a size calculation, nor can I seem to reset the buddy window to force a new position +// alas, we have to make a new up/down control each time :( +// TODO we'll need to store a copy of the current position and range for this +func (s *spinbox) remakeUpDown() { + // destroying the previous one, setting the parent properly, and subclassing are handled here + s.hwndUpDown = C.newUpDown(s.hwndUpDown, unsafe.Pointer(s)) + // for this to work, hwndUpDown needs to have rect [0 0 0 0] + C.moveWindow(s.hwndUpDown, 0, 0, 0, 0) + C.SendMessageW(s.hwndUpDown, C.UDM_SETBUDDY, C.WPARAM(uintptr(unsafe.Pointer(s.hwndEdit))), 0) + C.SendMessageW(s.hwndUpDown, C.UDM_SETRANGE32, C.WPARAM(s.min), C.LPARAM(s.max)) + C.SendMessageW(s.hwndUpDown, C.UDM_SETPOS32, 0, C.LPARAM(s.value)) + if s.updownVisible { + C.ShowWindow(s.hwndUpDown, C.SW_SHOW) + } +} + +// use the same height as normal text fields +// TODO constrain the width somehow +func (s *spinbox) preferredSize(d *sizing) (width, height int) { + return fromdlgunitsX(textfieldWidth, d), fromdlgunitsY(textfieldHeight, d) +} + +func (s *spinbox) resize(x int, y int, width int, height int, d *sizing) { + C.moveWindow(s.hwndEdit, C.int(x), C.int(y), C.int(width), C.int(height)) + s.remakeUpDown() +} + +func (s *spinbox) nTabStops() int { + // TODO does the up-down control count? + return 1 +} + +// TODO be sure to modify this when we add Show()/Hide() +func (s *spinbox) containerShow() { + C.ShowWindow(s.hwndEdit, C.SW_SHOW) + C.ShowWindow(s.hwndUpDown, C.SW_SHOW) + s.updownVisible = true +} + +// TODO be sure to modify this when we add Show()/Hide() +func (s *spinbox) containerHide() { + C.ShowWindow(s.hwndEdit, C.SW_HIDE) + C.ShowWindow(s.hwndUpDown, C.SW_HIDE) + s.updownVisible = false +} |
