代码拉取完成,页面将自动刷新
// Copyright 2010 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows
package walk
import (
"fmt"
"math/big"
"syscall"
"time"
"unsafe"
)
import (
"github.com/lxn/win"
)
type ComboBox struct {
WidgetBase
bindingValueProvider BindingValueProvider
model ListModel
providedModel interface{}
bindingMember string
displayMember string
format string
precision int
itemsResetHandlerHandle int
itemChangedHandlerHandle int
maxItemTextWidth int
prevCurIndex int
selChangeIndex int
maxLength int
currentIndexChangedPublisher EventPublisher
textChangedPublisher EventPublisher
editOrigWndProcPtr uintptr
}
var comboBoxEditWndProcPtr = syscall.NewCallback(comboBoxEditWndProc)
func comboBoxEditWndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
cb := (*ComboBox)(unsafe.Pointer(win.GetWindowLongPtr(hwnd, win.GWLP_USERDATA)))
switch msg {
case win.WM_GETDLGCODE:
if form := ancestor(cb); form != nil {
if dlg, ok := form.(dialogish); ok {
if dlg.DefaultButton() != nil {
// If the ComboBox lives in a Dialog that has a
// DefaultButton, we won't swallow the return key.
break
}
}
}
if wParam == win.VK_RETURN {
return win.DLGC_WANTALLKEYS
}
case win.WM_KEYDOWN:
if wParam != win.VK_RETURN || 0 == cb.SendMessage(win.CB_GETDROPPEDSTATE, 0, 0) {
cb.handleKeyDown(wParam, lParam)
}
case win.WM_KEYUP:
if wParam != win.VK_RETURN || 0 == cb.SendMessage(win.CB_GETDROPPEDSTATE, 0, 0) {
cb.handleKeyUp(wParam, lParam)
}
}
return win.CallWindowProc(cb.editOrigWndProcPtr, hwnd, msg, wParam, lParam)
}
func NewComboBox(parent Container) (*ComboBox, error) {
cb, err := newComboBoxWithStyle(parent, win.CBS_DROPDOWN)
if err != nil {
return nil, err
}
editHwnd := win.GetWindow(cb.hWnd, win.GW_CHILD)
win.SetWindowLongPtr(editHwnd, win.GWLP_USERDATA, uintptr(unsafe.Pointer(cb)))
cb.editOrigWndProcPtr = win.SetWindowLongPtr(editHwnd, win.GWLP_WNDPROC, comboBoxEditWndProcPtr)
return cb, nil
}
func NewDropDownBox(parent Container) (*ComboBox, error) {
return newComboBoxWithStyle(parent, win.CBS_DROPDOWNLIST)
}
func newComboBoxWithStyle(parent Container, style uint32) (*ComboBox, error) {
cb := &ComboBox{prevCurIndex: -1, selChangeIndex: -1, precision: 2}
if err := InitWidget(
cb,
parent,
"COMBOBOX",
win.WS_TABSTOP|win.WS_VISIBLE|win.WS_VSCROLL|style,
0); err != nil {
return nil, err
}
succeeded := false
defer func() {
if !succeeded {
cb.Dispose()
}
}()
cb.MustRegisterProperty("CurrentIndex", NewProperty(
func() interface{} {
return cb.CurrentIndex()
},
func(v interface{}) error {
return cb.SetCurrentIndex(v.(int))
},
cb.CurrentIndexChanged()))
cb.MustRegisterProperty("Text", NewProperty(
func() interface{} {
return cb.Text()
},
func(v interface{}) error {
return cb.SetText(v.(string))
},
cb.TextChanged()))
cb.MustRegisterProperty("HasCurrentItem", NewReadOnlyBoolProperty(
func() bool {
return cb.CurrentIndex() != -1
},
cb.CurrentIndexChanged()))
cb.MustRegisterProperty("TextNotEmpty", NewReadOnlyBoolProperty(
func() bool {
return cb.Text() != ""
},
cb.CurrentIndexChanged()))
cb.MustRegisterProperty("Value", NewProperty(
func() interface{} {
if cb.Editable() {
return cb.Text()
}
index := cb.CurrentIndex()
if cb.bindingValueProvider == nil || index == -1 {
return nil
}
return cb.bindingValueProvider.BindingValue(index)
},
func(v interface{}) error {
if cb.Editable() {
return cb.SetText(v.(string))
}
if cb.bindingValueProvider == nil {
if cb.model == nil {
return nil
} else {
return newError("Data binding is only supported using a model that implements BindingValueProvider.")
}
}
index := -1
count := cb.model.ItemCount()
for i := 0; i < count; i++ {
if cb.bindingValueProvider.BindingValue(i) == v {
index = i
break
}
}
return cb.SetCurrentIndex(index)
},
cb.CurrentIndexChanged()))
succeeded = true
return cb, nil
}
func (*ComboBox) LayoutFlags() LayoutFlags {
return GrowableHorz
}
func (cb *ComboBox) MinSizeHint() Size {
defaultSize := cb.dialogBaseUnitsToPixels(Size{50, 12})
if cb.model != nil && cb.maxItemTextWidth <= 0 {
cb.maxItemTextWidth = cb.calculateMaxItemTextWidth()
}
// FIXME: Use GetThemePartSize instead of guessing
w := maxi(defaultSize.Width, cb.maxItemTextWidth+30)
h := defaultSize.Height + 1
return Size{w, h}
}
func (cb *ComboBox) SizeHint() Size {
return cb.MinSizeHint()
}
func (cb *ComboBox) applyFont(font *Font) {
cb.WidgetBase.applyFont(font)
if cb.model != nil {
cb.maxItemTextWidth = cb.calculateMaxItemTextWidth()
cb.updateParentLayout()
}
}
func (cb *ComboBox) Editable() bool {
return !cb.hasStyleBits(win.CBS_DROPDOWNLIST)
}
func (cb *ComboBox) itemString(index int) string {
switch val := cb.model.Value(index).(type) {
case string:
return val
case time.Time:
return val.Format(cb.format)
case *big.Rat:
return val.FloatString(cb.precision)
default:
return fmt.Sprintf(cb.format, val)
}
panic("unreachable")
}
func (cb *ComboBox) insertItemAt(index int) error {
str := cb.itemString(index)
lp := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))
if win.CB_ERR == cb.SendMessage(win.CB_INSERTSTRING, uintptr(index), lp) {
return newError("SendMessage(CB_INSERTSTRING)")
}
return nil
}
func (cb *ComboBox) resetItems() error {
cb.SetSuspended(true)
defer cb.SetSuspended(false)
cb.selChangeIndex = -1
if win.FALSE == cb.SendMessage(win.CB_RESETCONTENT, 0, 0) {
return newError("SendMessage(CB_RESETCONTENT)")
}
cb.maxItemTextWidth = 0
cb.SetCurrentIndex(-1)
if cb.model == nil {
return nil
}
count := cb.model.ItemCount()
for i := 0; i < count; i++ {
if err := cb.insertItemAt(i); err != nil {
return err
}
}
cb.updateParentLayout()
return nil
}
func (cb *ComboBox) attachModel() {
itemsResetHandler := func() {
cb.resetItems()
}
cb.itemsResetHandlerHandle = cb.model.ItemsReset().Attach(itemsResetHandler)
itemChangedHandler := func(index int) {
if win.CB_ERR == cb.SendMessage(win.CB_DELETESTRING, uintptr(index), 0) {
newError("SendMessage(CB_DELETESTRING)")
}
cb.insertItemAt(index)
cb.SetCurrentIndex(cb.prevCurIndex)
}
cb.itemChangedHandlerHandle = cb.model.ItemChanged().Attach(itemChangedHandler)
}
func (cb *ComboBox) detachModel() {
cb.model.ItemsReset().Detach(cb.itemsResetHandlerHandle)
cb.model.ItemChanged().Detach(cb.itemChangedHandlerHandle)
}
// Model returns the model of the ComboBox.
func (cb *ComboBox) Model() interface{} {
return cb.providedModel
}
// SetModel sets the model of the ComboBox.
//
// It is required that mdl either implements walk.ListModel or
// walk.ReflectListModel or be a slice of pointers to struct or a []string.
func (cb *ComboBox) SetModel(mdl interface{}) error {
model, ok := mdl.(ListModel)
if !ok && mdl != nil {
var err error
if model, err = newReflectListModel(mdl); err != nil {
return err
}
if _, ok := mdl.([]string); !ok {
if badms, ok := model.(bindingAndDisplayMemberSetter); ok {
var bindingMember string
if cb.Editable() {
bindingMember = cb.displayMember
} else {
bindingMember = cb.bindingMember
}
badms.setBindingMember(bindingMember)
badms.setDisplayMember(cb.displayMember)
}
}
}
cb.providedModel = mdl
if cb.model != nil {
cb.detachModel()
}
cb.model = model
cb.bindingValueProvider, _ = model.(BindingValueProvider)
if model != nil {
cb.attachModel()
}
if err := cb.resetItems(); err != nil {
return err
}
if !cb.Editable() && model != nil && model.ItemCount() == 1 {
cb.SetCurrentIndex(0)
}
return cb.Invalidate()
}
// BindingMember returns the member from the model of the ComboBox that is bound
// to a field of the data source managed by an associated DataBinder.
//
// This is only applicable to walk.ReflectListModel models and simple slices of
// pointers to struct.
func (cb *ComboBox) BindingMember() string {
return cb.bindingMember
}
// SetBindingMember sets the member from the model of the ComboBox that is bound
// to a field of the data source managed by an associated DataBinder.
//
// This is only applicable to walk.ReflectListModel models and simple slices of
// pointers to struct.
//
// For a model consisting of items of type S, data source field of type T and
// bindingMember "Foo", this can be one of the following:
//
// A field Foo T
// A method func (s S) Foo() T
// A method func (s S) Foo() (T, error)
//
// If bindingMember is not a simple member name like "Foo", but a path to a
// member like "A.B.Foo", members "A" and "B" both must be one of the options
// mentioned above, but with T having type pointer to struct.
func (cb *ComboBox) SetBindingMember(bindingMember string) error {
if bindingMember != "" {
if _, ok := cb.providedModel.([]string); ok {
return newError("invalid for []string model")
}
}
cb.bindingMember = bindingMember
if badms, ok := cb.model.(bindingAndDisplayMemberSetter); ok {
badms.setBindingMember(bindingMember)
}
return nil
}
// DisplayMember returns the member from the model of the ComboBox that is
// displayed in the ComboBox.
//
// This is only applicable to walk.ReflectListModel models and simple slices of
// pointers to struct.
func (cb *ComboBox) DisplayMember() string {
return cb.displayMember
}
// SetDisplayMember sets the member from the model of the ComboBox that is
// displayed in the ComboBox.
//
// This is only applicable to walk.ReflectListModel models and simple slices of
// pointers to struct.
//
// For a model consisting of items of type S, the type of the specified member T
// and displayMember "Foo", this can be one of the following:
//
// A field Foo T
// A method func (s S) Foo() T
// A method func (s S) Foo() (T, error)
//
// If displayMember is not a simple member name like "Foo", but a path to a
// member like "A.B.Foo", members "A" and "B" both must be one of the options
// mentioned above, but with T having type pointer to struct.
func (cb *ComboBox) SetDisplayMember(displayMember string) error {
if displayMember != "" {
if _, ok := cb.providedModel.([]string); ok {
return newError("invalid for []string model")
}
}
cb.displayMember = displayMember
if badms, ok := cb.model.(bindingAndDisplayMemberSetter); ok {
badms.setDisplayMember(displayMember)
}
return nil
}
func (cb *ComboBox) Format() string {
return cb.format
}
func (cb *ComboBox) SetFormat(value string) {
cb.format = value
}
func (cb *ComboBox) Precision() int {
return cb.precision
}
func (cb *ComboBox) SetPrecision(value int) {
cb.precision = value
}
func (cb *ComboBox) MaxLength() int {
return cb.maxLength
}
func (cb *ComboBox) SetMaxLength(value int) {
cb.SendMessage(win.CB_LIMITTEXT, uintptr(value), 0)
cb.maxLength = value
}
func (cb *ComboBox) calculateMaxItemTextWidth() int {
hdc := win.GetDC(cb.hWnd)
if hdc == 0 {
newError("GetDC failed")
return -1
}
defer win.ReleaseDC(cb.hWnd, hdc)
hFontOld := win.SelectObject(hdc, win.HGDIOBJ(cb.Font().handleForDPI(0)))
defer win.SelectObject(hdc, hFontOld)
var maxWidth int
count := cb.model.ItemCount()
for i := 0; i < count; i++ {
var s win.SIZE
str := syscall.StringToUTF16(cb.itemString(i))
if !win.GetTextExtentPoint32(hdc, &str[0], int32(len(str)-1), &s) {
newError("GetTextExtentPoint32 failed")
return -1
}
maxWidth = maxi(maxWidth, int(s.CX))
}
return maxWidth
}
func (cb *ComboBox) CurrentIndex() int {
return int(int32(cb.SendMessage(win.CB_GETCURSEL, 0, 0)))
}
func (cb *ComboBox) SetCurrentIndex(value int) error {
index := int(int32(cb.SendMessage(win.CB_SETCURSEL, uintptr(value), 0)))
if index != value {
return newError("invalid index")
}
if value != cb.prevCurIndex {
cb.prevCurIndex = value
cb.currentIndexChangedPublisher.Publish()
}
return nil
}
func (cb *ComboBox) CurrentIndexChanged() *Event {
return cb.currentIndexChangedPublisher.Event()
}
func (cb *ComboBox) Text() string {
return windowText(cb.hWnd)
}
func (cb *ComboBox) SetText(value string) error {
return setWindowText(cb.hWnd, value)
}
func (cb *ComboBox) TextSelection() (start, end int) {
cb.SendMessage(win.CB_GETEDITSEL, uintptr(unsafe.Pointer(&start)), uintptr(unsafe.Pointer(&end)))
return
}
func (cb *ComboBox) SetTextSelection(start, end int) {
cb.SendMessage(win.CB_SETEDITSEL, 0, uintptr(win.MAKELONG(uint16(start), uint16(end))))
}
func (cb *ComboBox) TextChanged() *Event {
return cb.textChangedPublisher.Event()
}
func (cb *ComboBox) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
switch msg {
case win.WM_COMMAND:
code := win.HIWORD(uint32(wParam))
selIndex := cb.CurrentIndex()
switch code {
case win.CBN_EDITCHANGE:
cb.selChangeIndex = -1
cb.textChangedPublisher.Publish()
cb.currentIndexChangedPublisher.Publish()
case win.CBN_SELCHANGE:
cb.selChangeIndex = selIndex
case win.CBN_SELENDCANCEL:
if cb.selChangeIndex != -1 {
if cb.selChangeIndex < cb.model.ItemCount() {
cb.SetCurrentIndex(cb.selChangeIndex)
}
cb.selChangeIndex = -1
}
case win.CBN_SELENDOK:
if editable := cb.Editable(); editable || selIndex != cb.prevCurIndex {
if editable && selIndex > -1 {
cb.Property("Value").Set(cb.model.Value(selIndex))
}
cb.currentIndexChangedPublisher.Publish()
cb.prevCurIndex = selIndex
return 0
}
cb.selChangeIndex = -1
}
case win.WM_MOUSEWHEEL:
if !cb.Enabled() {
return 0
}
}
return cb.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。