summaryrefslogtreecommitdiff
path: root/prev/spinbox_windows.go
diff options
context:
space:
mode:
authorPietro Gagliardi <[email protected]>2015-12-11 20:37:59 -0500
committerPietro Gagliardi <[email protected]>2015-12-11 20:37:59 -0500
commitf8e3f12ab02b528f2a05a4f713d7af7ea8e44b42 (patch)
tree82dedf4d37f0f6d31e88ebb2ca1ce6499dead261 /prev/spinbox_windows.go
parente34c561ed5bedeb180437ec165882b98d70d38c1 (diff)
LET'S GET THIS FINAL REWRITE EVER STARTED
Diffstat (limited to 'prev/spinbox_windows.go')
-rw-r--r--prev/spinbox_windows.go148
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
+}