// 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) } }
func finishNewTable(b *tablebase, ty reflect.Type) Table { hwnd := C.newControl(C.xtableWindowClass, C.WS_HSCROLL|C.WS_VSCROLL|C.WS_TABSTOP, C.WS_EX_CLIENTEDGE) // WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog) t := &table{ controlSingleHWND: newControlSingleHWND(hwnd), tablebase: b, selected: newEvent(), free: make(map[C.uintptr_t]bool), } t.fpreferredSize = t.xpreferredSize t.chainresize = t.fresize t.fresize = t.xresize C.setTableSubclass(t.hwnd, unsafe.Pointer(t)) // TODO listview didn't need this; someone mentioned (TODO) it uses the small caption font??? C.controlSetControlFont(t.hwnd) for i := 0; i < ty.NumField(); i++ { coltype := C.WPARAM(C.tableColumnText) switch { case ty.Field(i).Type == reflect.TypeOf((*image.RGBA)(nil)): coltype = C.tableColumnImage case ty.Field(i).Type.Kind() == reflect.Bool: coltype = C.tableColumnCheckbox } colname := ty.Field(i).Tag.Get("uicolumn") if colname == "" { colname = ty.Field(i).Name } ccolname := toUTF16(colname) C.SendMessageW(t.hwnd, C.tableAddColumn, coltype, C.LPARAM(uintptr(unsafe.Pointer(ccolname)))) // TODO free ccolname } t.colcount = C.int(ty.NumField()) return t }
func (a *area) Repaint(r image.Rectangle) { var hscroll, vscroll C.int var rect C.RECT C.SendMessageW(a.hwnd, C.msgAreaGetScroll, C.WPARAM(uintptr(unsafe.Pointer(&hscroll))), C.LPARAM(uintptr(unsafe.Pointer(&vscroll)))) r = r.Add(image.Pt(int(hscroll), int(vscroll))) // adjust by scroll position r = image.Rect(0, 0, a.width, a.height).Intersect(r) if r.Empty() { return } rect.left = C.LONG(r.Min.X) rect.top = C.LONG(r.Min.Y) rect.right = C.LONG(r.Max.X) rect.bottom = C.LONG(r.Max.Y) C.SendMessageW(a.hwnd, C.msgAreaRepaint, 0, C.LPARAM(uintptr(unsafe.Pointer(&rect)))) }
func (p *progressbar) SetPercent(percent int) { if percent < 0 || percent > 100 { panic(fmt.Errorf("given ProgressBar percentage %d out of range", percent)) } // TODO circumvent aero C.SendMessageW(p.hwnd, C.PBM_SETPOS, C.WPARAM(percent), 0) }
func (i *imagelist) apply(hwnd C.HWND, uMsg C.UINT) { width := C.GetSystemMetrics(C.SM_CXSMICON) height := C.GetSystemMetrics(C.SM_CYSMICON) il := C.newImageList(width, height) for index := range i.list { C.addImage(il, hwnd, i.list[index], C.int(i.width[index]), C.int(i.height[index]), width, height) } C.SendMessageW(hwnd, uMsg, 0, C.LPARAM(uintptr(unsafe.Pointer(il)))) }
func (b *button) preferredSize(d *sizing) (width, height int) { // comctl32.dll version 6 thankfully provides a method to grab this... var size C.SIZE size.cx = 0 // explicitly ask for ideal size size.cy = 0 if C.SendMessageW(b._hwnd, C.BCM_GETIDEALSIZE, 0, C.LPARAM(uintptr(unsafe.Pointer(&size)))) != C.FALSE { return int(size.cx), int(size.cy) } // that failed, fall back println("message failed; falling back") // don't worry about the error return from GetSystemMetrics(); there's no way to tell (explicitly documented as such) xmargins := 2 * int(C.GetSystemMetrics(C.SM_CXEDGE)) return xmargins + int(b._textlen), fromdlgunitsY(buttonHeight, d) }
//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() }
// a tab control contains other controls; size appropriately func (t *tab) commitResize(c *allocation, d *sizing) { var r C.RECT // figure out what the rect for each child is... // the tab contents are children of the tab itself, so ignore c.x and c.y, which are relative to the window! r.left = C.LONG(0) r.top = C.LONG(0) r.right = C.LONG(c.width) r.bottom = C.LONG(c.height) C.tabGetContentRect(t._hwnd, &r) // and resize tabs // resize only the current tab; we trigger a resize on a tab change to make sure things look correct if len(t.tabs) > 0 { t.tabs[C.SendMessageW(t._hwnd, C.TCM_GETCURSEL, 0, 0)].move(&r) } // save the tab size so we can t.switchrect = r // and now resize the tab control itself basecommitResize(t, c, d) }
func finishNewTable(b *tablebase, ty reflect.Type) Table { t := &table{ _hwnd: C.newControl(C.xWC_LISTVIEW, C.LVS_REPORT|C.LVS_OWNERDATA|C.LVS_NOSORTHEADER|C.LVS_SHOWSELALWAYS|C.LVS_SINGLESEL|C.WS_HSCROLL|C.WS_VSCROLL|C.WS_TABSTOP, C.WS_EX_CLIENTEDGE), // WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog) tablebase: b, hotrow: -1, hotcol: -1, pushedrow: -1, pushedcol: -1, selected: newEvent(), } C.setTableSubclass(t._hwnd, unsafe.Pointer(t)) // LVS_EX_FULLROWSELECT gives us selection across the whole row, not just the leftmost column; this makes the list view work like on other platforms // LVS_EX_SUBITEMIMAGES gives us images in subitems, which will be important when both images and checkboxes are added C.tableAddExtendedStyles(t._hwnd, C.LVS_EX_FULLROWSELECT|C.LVS_EX_SUBITEMIMAGES) // this must come after the subclass because it uses one of our private messages C.SendMessageW(t._hwnd, C.msgTableMakeInitialCheckboxImageList, 0, 0) for i := 0; i < ty.NumField(); i++ { C.tableAppendColumn(t._hwnd, C.int(i), toUTF16(ty.Field(i).Name)) } t.colcount = C.int(ty.NumField()) return t }
func (a *area) RepaintAll() { C.SendMessageW(a.hwnd, C.msgAreaRepaintAll, 0, 0) }
func (a *area) SetSize(width, height int) { a.width = width a.height = height C.SendMessageW(a.hwnd, C.msgAreaSizeChanged, 0, 0) }
func (p *progressbar) Percent() int { return int(C.SendMessageW(p.hwnd, C.PBM_GETPOS, 0, 0)) }
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)) }