928 lines
20 KiB
Go
928 lines
20 KiB
Go
// Copyright 2011 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 (
|
|
"bytes"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
"github.com/lxn/win"
|
|
)
|
|
|
|
const numberEditWindowClass = `\o/ Walk_NumberEdit_Class \o/`
|
|
|
|
func init() {
|
|
AppendToWalkInit(func() {
|
|
MustRegisterWindowClass(numberEditWindowClass)
|
|
})
|
|
}
|
|
|
|
// NumberEdit is a widget that is suited to edit numeric values.
|
|
type NumberEdit struct {
|
|
WidgetBase
|
|
edit *numberLineEdit
|
|
maxValueChangedPublisher EventPublisher
|
|
minValueChangedPublisher EventPublisher
|
|
prefixChangedPublisher EventPublisher
|
|
suffixChangedPublisher EventPublisher
|
|
}
|
|
|
|
// NewNumberEdit returns a new NumberEdit widget as child of parent.
|
|
func NewNumberEdit(parent Container) (*NumberEdit, error) {
|
|
ne := new(NumberEdit)
|
|
|
|
if err := InitWidget(
|
|
ne,
|
|
parent,
|
|
numberEditWindowClass,
|
|
win.WS_VISIBLE,
|
|
win.WS_EX_CONTROLPARENT); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var succeeded bool
|
|
defer func() {
|
|
if !succeeded {
|
|
ne.Dispose()
|
|
}
|
|
}()
|
|
|
|
var err error
|
|
if ne.edit, err = newNumberLineEdit(ne); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ne.edit.applyFont(ne.Font())
|
|
|
|
ne.SetRange(-math.MaxFloat64, math.MaxFloat64)
|
|
|
|
if err = ne.SetValue(0); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ne.GraphicsEffects().Add(InteractionEffect)
|
|
ne.GraphicsEffects().Add(FocusEffect)
|
|
|
|
ne.MustRegisterProperty("MaxValue", NewProperty(
|
|
func() interface{} {
|
|
return ne.MaxValue()
|
|
},
|
|
func(v interface{}) error {
|
|
return ne.SetRange(ne.MinValue(), assertFloat64Or(v, 0.0))
|
|
},
|
|
ne.minValueChangedPublisher.Event()))
|
|
|
|
ne.MustRegisterProperty("MinValue", NewProperty(
|
|
func() interface{} {
|
|
return ne.MinValue()
|
|
},
|
|
func(v interface{}) error {
|
|
return ne.SetRange(assertFloat64Or(v, 0.0), ne.MaxValue())
|
|
},
|
|
ne.maxValueChangedPublisher.Event()))
|
|
|
|
ne.MustRegisterProperty("Prefix", NewProperty(
|
|
func() interface{} {
|
|
return ne.Prefix()
|
|
},
|
|
func(v interface{}) error {
|
|
return ne.SetPrefix(assertStringOr(v, ""))
|
|
},
|
|
ne.prefixChangedPublisher.Event()))
|
|
|
|
ne.MustRegisterProperty("ReadOnly", NewProperty(
|
|
func() interface{} {
|
|
return ne.ReadOnly()
|
|
},
|
|
func(v interface{}) error {
|
|
return ne.SetReadOnly(v.(bool))
|
|
},
|
|
ne.edit.readOnlyChangedPublisher.Event()))
|
|
|
|
ne.MustRegisterProperty("Suffix", NewProperty(
|
|
func() interface{} {
|
|
return ne.Suffix()
|
|
},
|
|
func(v interface{}) error {
|
|
return ne.SetSuffix(assertStringOr(v, ""))
|
|
},
|
|
ne.suffixChangedPublisher.Event()))
|
|
|
|
ne.MustRegisterProperty("Value", NewProperty(
|
|
func() interface{} {
|
|
return ne.Value()
|
|
},
|
|
func(v interface{}) error {
|
|
return ne.SetValue(assertFloat64Or(v, 0.0))
|
|
},
|
|
ne.edit.valueChangedPublisher.Event()))
|
|
|
|
succeeded = true
|
|
|
|
return ne, nil
|
|
}
|
|
|
|
func (ne *NumberEdit) applyEnabled(enabled bool) {
|
|
ne.WidgetBase.applyEnabled(enabled)
|
|
|
|
if ne.edit == nil {
|
|
return
|
|
}
|
|
|
|
ne.edit.applyEnabled(enabled)
|
|
}
|
|
|
|
func (ne *NumberEdit) applyFont(font *Font) {
|
|
ne.WidgetBase.applyFont(font)
|
|
|
|
if ne.edit == nil {
|
|
return
|
|
}
|
|
|
|
ne.edit.applyFont(font)
|
|
}
|
|
|
|
// Decimals returns the number of decimal places in the NumberEdit.
|
|
func (ne *NumberEdit) Decimals() int {
|
|
return ne.edit.decimals
|
|
}
|
|
|
|
// SetDecimals sets the number of decimal places in the NumberEdit.
|
|
func (ne *NumberEdit) SetDecimals(decimals int) error {
|
|
if decimals < 0 || decimals > 8 {
|
|
return newError("decimals must >= 0 && <= 8")
|
|
}
|
|
|
|
ne.edit.decimals = decimals
|
|
|
|
return ne.SetValue(ne.edit.value)
|
|
}
|
|
|
|
// Prefix returns the text that appears in the NumberEdit before the number.
|
|
func (ne *NumberEdit) Prefix() string {
|
|
return syscall.UTF16ToString(ne.edit.prefix)
|
|
}
|
|
|
|
// SetPrefix sets the text that appears in the NumberEdit before the number.
|
|
func (ne *NumberEdit) SetPrefix(prefix string) error {
|
|
if prefix == ne.Prefix() {
|
|
return nil
|
|
}
|
|
|
|
p, err := syscall.UTF16FromString(prefix)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
old := ne.edit.prefix
|
|
ne.edit.prefix = p[:len(p)-1]
|
|
|
|
if err := ne.edit.setTextFromValue(ne.edit.value); err != nil {
|
|
ne.edit.prefix = old
|
|
return err
|
|
}
|
|
|
|
ne.prefixChangedPublisher.Publish()
|
|
|
|
return nil
|
|
}
|
|
|
|
// PrefixChanged returns the event that is published when the prefix changed.
|
|
func (ne *NumberEdit) PrefixChanged() *Event {
|
|
return ne.prefixChangedPublisher.Event()
|
|
}
|
|
|
|
// Suffix returns the text that appears in the NumberEdit after the number.
|
|
func (ne *NumberEdit) Suffix() string {
|
|
return syscall.UTF16ToString(ne.edit.suffix)
|
|
}
|
|
|
|
// SetSuffix sets the text that appears in the NumberEdit after the number.
|
|
func (ne *NumberEdit) SetSuffix(suffix string) error {
|
|
if suffix == ne.Suffix() {
|
|
return nil
|
|
}
|
|
|
|
s, err := syscall.UTF16FromString(suffix)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
old := ne.edit.suffix
|
|
ne.edit.suffix = s[:len(s)-1]
|
|
|
|
if err := ne.edit.setTextFromValue(ne.edit.value); err != nil {
|
|
ne.edit.suffix = old
|
|
return err
|
|
}
|
|
|
|
ne.suffixChangedPublisher.Publish()
|
|
|
|
return nil
|
|
}
|
|
|
|
// SuffixChanged returns the event that is published when the suffix changed.
|
|
func (ne *NumberEdit) SuffixChanged() *Event {
|
|
return ne.suffixChangedPublisher.Event()
|
|
}
|
|
|
|
// Increment returns the amount by which the NumberEdit increments or decrements
|
|
// its value, when the user presses the KeyDown or KeyUp keys, or when the mouse
|
|
// wheel is rotated.
|
|
func (ne *NumberEdit) Increment() float64 {
|
|
return ne.edit.increment
|
|
}
|
|
|
|
// SetIncrement sets the amount by which the NumberEdit increments or decrements
|
|
// its value, when the user presses the KeyDown or KeyUp keys, or when the mouse
|
|
// wheel is rotated.
|
|
func (ne *NumberEdit) SetIncrement(increment float64) error {
|
|
ne.edit.increment = increment
|
|
|
|
return nil
|
|
}
|
|
|
|
// MinValue returns the minimum value the NumberEdit will accept.
|
|
func (ne *NumberEdit) MinValue() float64 {
|
|
return ne.edit.minValue
|
|
}
|
|
|
|
// MinValue returns the maximum value the NumberEdit will accept.
|
|
func (ne *NumberEdit) MaxValue() float64 {
|
|
return ne.edit.maxValue
|
|
}
|
|
|
|
// SetRange sets the minimum and maximum values the NumberEdit will accept.
|
|
//
|
|
// If the current value is out of this range, it will be adjusted.
|
|
func (ne *NumberEdit) SetRange(min, max float64) error {
|
|
if min > max {
|
|
return newError(fmt.Sprintf("invalid range - min: %f, max: %f", min, max))
|
|
}
|
|
|
|
minChanged := min != ne.edit.minValue
|
|
maxChanged := max != ne.edit.maxValue
|
|
|
|
ne.edit.minValue = min
|
|
ne.edit.maxValue = max
|
|
if min != max {
|
|
if ne.edit.value < min {
|
|
if err := ne.edit.setValue(min, true); err != nil {
|
|
return err
|
|
}
|
|
} else if ne.edit.value > max {
|
|
if err := ne.edit.setValue(max, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if minChanged {
|
|
ne.minValueChangedPublisher.Publish()
|
|
}
|
|
if maxChanged {
|
|
ne.maxValueChangedPublisher.Publish()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Value returns the value of the NumberEdit.
|
|
func (ne *NumberEdit) Value() float64 {
|
|
return ne.edit.value
|
|
}
|
|
|
|
// SetValue sets the value of the NumberEdit.
|
|
func (ne *NumberEdit) SetValue(value float64) error {
|
|
if ne.edit.minValue != ne.edit.maxValue &&
|
|
(value < ne.edit.minValue || value > ne.edit.maxValue) {
|
|
|
|
return newError("value out of range")
|
|
}
|
|
|
|
return ne.edit.setValue(value, true)
|
|
}
|
|
|
|
// ValueChanged returns an Event that can be used to track changes to Value.
|
|
func (ne *NumberEdit) ValueChanged() *Event {
|
|
return ne.edit.valueChangedPublisher.Event()
|
|
}
|
|
|
|
// SetFocus sets the keyboard input focus to the NumberEdit.
|
|
func (ne *NumberEdit) SetFocus() error {
|
|
if win.SetFocus(ne.edit.hWnd) == 0 {
|
|
return lastError("SetFocus")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TextSelection returns the range of the current text selection of the
|
|
// NumberEdit.
|
|
func (ne *NumberEdit) TextSelection() (start, end int) {
|
|
return ne.edit.TextSelection()
|
|
}
|
|
|
|
// SetTextSelection sets the range of the current text selection of the
|
|
// NumberEdit.
|
|
func (ne *NumberEdit) SetTextSelection(start, end int) {
|
|
ne.edit.SetTextSelection(start, end)
|
|
}
|
|
|
|
// ReadOnly returns whether the NumberEdit is in read-only mode.
|
|
func (ne *NumberEdit) ReadOnly() bool {
|
|
return ne.edit.ReadOnly()
|
|
}
|
|
|
|
// SetReadOnly sets whether the NumberEdit is in read-only mode.
|
|
func (ne *NumberEdit) SetReadOnly(readOnly bool) error {
|
|
if readOnly != ne.ReadOnly() {
|
|
ne.invalidateBorderInParent()
|
|
}
|
|
|
|
return ne.edit.SetReadOnly(readOnly)
|
|
}
|
|
|
|
// Background returns the background Brush of the NumberEdit.
|
|
//
|
|
// By default this is nil.
|
|
func (ne *NumberEdit) Background() Brush {
|
|
return ne.edit.Background()
|
|
}
|
|
|
|
// SetBackground sets the background Brush of the NumberEdit.
|
|
func (ne *NumberEdit) SetBackground(bg Brush) {
|
|
ne.edit.SetBackground(bg)
|
|
}
|
|
|
|
// TextColor returns the Color used to draw the text of the NumberEdit.
|
|
func (ne *NumberEdit) TextColor() Color {
|
|
return ne.edit.TextColor()
|
|
}
|
|
|
|
// TextColor sets the Color used to draw the text of the NumberEdit.
|
|
func (ne *NumberEdit) SetTextColor(c Color) {
|
|
ne.edit.SetTextColor(c)
|
|
}
|
|
|
|
func (*NumberEdit) NeedsWmSize() bool {
|
|
return true
|
|
}
|
|
|
|
// WndProc is the window procedure of the NumberEdit.
|
|
//
|
|
// When implementing your own WndProc to add or modify behavior, call the
|
|
// WndProc of the embedded NumberEdit for messages you don't handle yourself.
|
|
func (ne *NumberEdit) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
|
|
switch msg {
|
|
case win.WM_CTLCOLOREDIT, win.WM_CTLCOLORSTATIC:
|
|
if hBrush := ne.handleWMCTLCOLOR(wParam, lParam); hBrush != 0 {
|
|
return hBrush
|
|
}
|
|
|
|
case win.WM_WINDOWPOSCHANGED:
|
|
wp := (*win.WINDOWPOS)(unsafe.Pointer(lParam))
|
|
|
|
if wp.Flags&win.SWP_NOSIZE != 0 {
|
|
break
|
|
}
|
|
|
|
if ne.edit == nil {
|
|
break
|
|
}
|
|
|
|
cb := ne.ClientBoundsPixels()
|
|
if err := ne.edit.SetBoundsPixels(cb); err != nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
return ne.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
|
|
}
|
|
|
|
func (ne *NumberEdit) CreateLayoutItem(ctx *LayoutContext) LayoutItem {
|
|
return &numberEditLayoutItem{
|
|
idealSize: ne.dialogBaseUnitsToPixels(Size{50, 12}),
|
|
minSize: ne.dialogBaseUnitsToPixels(Size{20, 12}),
|
|
}
|
|
}
|
|
|
|
type numberEditLayoutItem struct {
|
|
LayoutItemBase
|
|
idealSize Size // in native pixels
|
|
minSize Size // in native pixels
|
|
}
|
|
|
|
func (*numberEditLayoutItem) LayoutFlags() LayoutFlags {
|
|
return ShrinkableHorz | GrowableHorz
|
|
}
|
|
|
|
func (li *numberEditLayoutItem) IdealSize() Size {
|
|
return li.idealSize
|
|
}
|
|
|
|
func (li *numberEditLayoutItem) MinSize() Size {
|
|
return li.minSize
|
|
}
|
|
|
|
type numberLineEdit struct {
|
|
*LineEdit
|
|
buf *bytes.Buffer
|
|
prefix []uint16
|
|
suffix []uint16
|
|
value float64
|
|
minValue float64
|
|
maxValue float64
|
|
increment float64
|
|
decimals int
|
|
valueChangedPublisher EventPublisher
|
|
inEditMode bool
|
|
}
|
|
|
|
func newNumberLineEdit(parent Widget) (*numberLineEdit, error) {
|
|
nle := &numberLineEdit{
|
|
buf: new(bytes.Buffer),
|
|
increment: 1,
|
|
}
|
|
|
|
var err error
|
|
if nle.LineEdit, err = newLineEdit(parent); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
succeeded := false
|
|
defer func() {
|
|
if !succeeded {
|
|
nle.Dispose()
|
|
}
|
|
}()
|
|
|
|
if err := nle.LineEdit.setAndClearStyleBits(win.ES_RIGHT, win.ES_LEFT|win.ES_CENTER); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := InitWrapperWindow(nle); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
succeeded = true
|
|
|
|
return nle, nil
|
|
}
|
|
|
|
func (nle *numberLineEdit) TextColor() Color {
|
|
return nle.LineEdit.TextColor()
|
|
}
|
|
|
|
func (nle *numberLineEdit) SetTextColor(c Color) {
|
|
nle.LineEdit.SetTextColor(c)
|
|
}
|
|
|
|
func (nle *numberLineEdit) setValue(value float64, setText bool) error {
|
|
if setText {
|
|
if err := nle.setTextFromValue(value); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if value == nle.value {
|
|
return nil
|
|
}
|
|
|
|
nle.value = value
|
|
|
|
nle.valueChangedPublisher.Publish()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (nle *numberLineEdit) setTextFromValue(value float64) error {
|
|
nle.buf.Reset()
|
|
|
|
nle.buf.WriteString(syscall.UTF16ToString(nle.prefix))
|
|
|
|
if nle.decimals > 0 {
|
|
nle.buf.WriteString(FormatFloatGrouped(value, nle.decimals))
|
|
} else {
|
|
nle.buf.WriteString(FormatFloat(value, nle.decimals))
|
|
}
|
|
|
|
nle.buf.WriteString(syscall.UTF16ToString(nle.suffix))
|
|
|
|
return nle.SetText(nle.buf.String())
|
|
}
|
|
|
|
func (nle *numberLineEdit) endEdit() error {
|
|
if err := nle.setTextFromValue(nle.value); err != nil {
|
|
return err
|
|
}
|
|
|
|
nle.inEditMode = false
|
|
|
|
return nil
|
|
}
|
|
|
|
func (nle *numberLineEdit) processChar(text []uint16, start, end int, key Key, char uint16) {
|
|
hadSelection := start != end
|
|
|
|
if !nle.inEditMode {
|
|
var groupSepsBeforeStart int
|
|
if nle.decimals > 0 {
|
|
groupSepsBeforeStart = uint16CountUint16(text[:start], groupSepUint16)
|
|
}
|
|
|
|
if hadSelection {
|
|
text = append(text[:start], text[end:]...)
|
|
}
|
|
|
|
if nle.decimals > 0 {
|
|
text = uint16RemoveUint16(text, groupSepUint16)
|
|
start -= groupSepsBeforeStart
|
|
}
|
|
|
|
nle.inEditMode = true
|
|
} else {
|
|
if hadSelection {
|
|
text = append(text[:start], text[end:]...)
|
|
}
|
|
}
|
|
|
|
end = start
|
|
|
|
switch key {
|
|
case KeyBack:
|
|
if !hadSelection && start > 0 {
|
|
start -= 1
|
|
text = append(text[:start], text[start+1:]...)
|
|
}
|
|
|
|
case KeyDelete:
|
|
if !hadSelection && start < len(text) {
|
|
text = append(text[:start], text[start+1:]...)
|
|
}
|
|
|
|
default:
|
|
t := make([]uint16, len(text[:start]), len(text)+1)
|
|
copy(t, text[:start])
|
|
t = append(t, char)
|
|
text = append(t, text[start:]...)
|
|
start += 1
|
|
}
|
|
|
|
nle.buf.Reset()
|
|
|
|
str := syscall.UTF16ToString(text)
|
|
|
|
nle.buf.WriteString(syscall.UTF16ToString(nle.prefix))
|
|
nle.buf.WriteString(str)
|
|
nle.buf.WriteString(syscall.UTF16ToString(nle.suffix))
|
|
|
|
nle.SetText(nle.buf.String())
|
|
|
|
start += len(nle.prefix)
|
|
nle.SetTextSelection(start, start)
|
|
|
|
nle.tryUpdateValue(false)
|
|
}
|
|
|
|
func (nle *numberLineEdit) tryUpdateValue(setText bool) bool {
|
|
t := nle.textUTF16()
|
|
t = t[len(nle.prefix) : len(t)-len(nle.suffix)]
|
|
|
|
text := strings.Replace(syscall.UTF16ToString(t), decimalSepS, ".", 1)
|
|
|
|
switch text {
|
|
case "", ".":
|
|
text = "0"
|
|
}
|
|
|
|
if value, err := strconv.ParseFloat(text, 64); err == nil {
|
|
if nle.minValue == nle.maxValue || value >= nle.minValue && value <= nle.maxValue {
|
|
return nle.setValue(value, setText) == nil
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (nle *numberLineEdit) selectNumber() {
|
|
nle.SetTextSelection(len(nle.prefix), len(nle.textUTF16())-len(nle.suffix))
|
|
}
|
|
|
|
func (nle *numberLineEdit) textUTF16() []uint16 {
|
|
textLength := nle.SendMessage(win.WM_GETTEXTLENGTH, 0, 0)
|
|
buf := make([]uint16, textLength+1)
|
|
nle.SendMessage(win.WM_GETTEXT, uintptr(textLength+1), uintptr(unsafe.Pointer(&buf[0])))
|
|
|
|
return buf[:len(buf)-1]
|
|
}
|
|
|
|
func (nle *numberLineEdit) incrementValue(delta float64) {
|
|
value := nle.value + delta
|
|
|
|
if nle.minValue != nle.maxValue {
|
|
if value < nle.minValue {
|
|
value = nle.minValue
|
|
} else if value > nle.maxValue {
|
|
value = nle.maxValue
|
|
}
|
|
}
|
|
|
|
nle.setValue(value, true)
|
|
nle.selectNumber()
|
|
}
|
|
|
|
func (nle *numberLineEdit) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
|
|
switch msg {
|
|
case win.WM_CHAR:
|
|
if nle.ReadOnly() {
|
|
break
|
|
}
|
|
|
|
if AltDown() {
|
|
return 0
|
|
}
|
|
|
|
if ControlDown() {
|
|
if wParam == 1 {
|
|
// Ctrl+A
|
|
return 0
|
|
}
|
|
break
|
|
}
|
|
|
|
char := uint16(wParam)
|
|
|
|
text := nle.textUTF16()
|
|
text = text[len(nle.prefix) : len(text)-len(nle.suffix)]
|
|
start, end := nle.TextSelection()
|
|
start -= len(nle.prefix)
|
|
end -= len(nle.prefix)
|
|
|
|
if Key(wParam) == KeyBack {
|
|
nle.processChar(text, start, end, KeyBack, 0)
|
|
return 0
|
|
}
|
|
|
|
switch char {
|
|
case uint16('0'), uint16('1'), uint16('2'), uint16('3'), uint16('4'), uint16('5'), uint16('6'), uint16('7'), uint16('8'), uint16('9'):
|
|
if start == end && nle.decimals > 0 {
|
|
if i := uint16IndexUint16(text, decimalSepUint16); i > -1 && i < len(text)-nle.decimals && start > i {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
nle.processChar(text, start, end, 0, char)
|
|
return 0
|
|
|
|
case uint16('-'):
|
|
if nle.minValue != nle.maxValue && nle.minValue >= 0 {
|
|
return 0
|
|
}
|
|
|
|
if start > 0 || uint16ContainsUint16(text, uint16('-')) && end == 0 {
|
|
return 0
|
|
}
|
|
|
|
nle.processChar(text, start, end, 0, char)
|
|
return 0
|
|
|
|
case decimalSepUint16:
|
|
if nle.decimals == 0 {
|
|
return 0
|
|
}
|
|
|
|
if start == 0 && end == 0 && len(text) > 0 && text[0] == '-' {
|
|
return 0
|
|
}
|
|
|
|
if end < len(text)-nle.decimals {
|
|
return 0
|
|
}
|
|
|
|
if i := uint16IndexUint16(text, decimalSepUint16); i > -1 && i <= start || i > end {
|
|
return 0
|
|
}
|
|
|
|
nle.processChar(text, start, end, 0, char)
|
|
return 0
|
|
|
|
default:
|
|
return 0
|
|
}
|
|
|
|
case win.WM_KEYDOWN:
|
|
switch Key(wParam) {
|
|
case KeyA:
|
|
if ControlDown() {
|
|
nle.selectNumber()
|
|
return 0
|
|
}
|
|
|
|
case KeyDelete:
|
|
if nle.ReadOnly() {
|
|
break
|
|
}
|
|
|
|
text := nle.textUTF16()
|
|
text = text[len(nle.prefix) : len(text)-len(nle.suffix)]
|
|
start, end := nle.TextSelection()
|
|
start -= len(nle.prefix)
|
|
end -= len(nle.prefix)
|
|
|
|
nle.processChar(text, start, end, KeyDelete, 0)
|
|
return 0
|
|
|
|
case KeyDown:
|
|
if nle.ReadOnly() || nle.increment <= 0 {
|
|
return 0
|
|
}
|
|
|
|
nle.incrementValue(-nle.increment)
|
|
return 0
|
|
|
|
case KeyEnd:
|
|
start, end := nle.TextSelection()
|
|
end = len(nle.textUTF16()) - len(nle.suffix)
|
|
if !ShiftDown() {
|
|
start = end
|
|
}
|
|
nle.SetTextSelection(start, end)
|
|
return 0
|
|
|
|
case KeyHome:
|
|
start, end := nle.TextSelection()
|
|
start = len(nle.prefix)
|
|
if !ShiftDown() {
|
|
end = start
|
|
}
|
|
nle.SetTextSelection(start, end)
|
|
return 0
|
|
|
|
case KeyLeft:
|
|
var pos win.POINT
|
|
win.GetCaretPos(&pos)
|
|
|
|
lParam := uintptr(win.MAKELONG(uint16(pos.X), uint16(pos.Y)))
|
|
i := int(win.LOWORD(uint32(nle.SendMessage(win.EM_CHARFROMPOS, 0, lParam))))
|
|
|
|
if min := len(nle.prefix); i <= min {
|
|
if !ShiftDown() {
|
|
nle.SetTextSelection(min, min)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
case KeyReturn:
|
|
if nle.ReadOnly() {
|
|
break
|
|
}
|
|
|
|
if nle.inEditMode {
|
|
nle.endEdit()
|
|
nle.selectNumber()
|
|
return 0
|
|
}
|
|
|
|
case KeyRight:
|
|
var pos win.POINT
|
|
win.GetCaretPos(&pos)
|
|
|
|
lParam := uintptr(win.MAKELONG(uint16(pos.X), uint16(pos.Y)))
|
|
i := int(win.LOWORD(uint32(nle.SendMessage(win.EM_CHARFROMPOS, 0, lParam))))
|
|
|
|
if max := len(nle.textUTF16()) - len(nle.suffix); i >= max {
|
|
if !ShiftDown() {
|
|
nle.SetTextSelection(max, max)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
case KeyUp:
|
|
if nle.ReadOnly() || nle.increment <= 0 {
|
|
return 0
|
|
}
|
|
|
|
nle.incrementValue(nle.increment)
|
|
return 0
|
|
}
|
|
|
|
case win.WM_GETDLGCODE:
|
|
if !nle.inEditMode {
|
|
if form := ancestor(nle); form != nil {
|
|
if dlg, ok := form.(dialogish); ok {
|
|
if dlg.DefaultButton() != nil {
|
|
// If the NumberEdit 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_KILLFOCUS:
|
|
nle.onFocusChanged()
|
|
nle.endEdit()
|
|
|
|
case win.WM_LBUTTONDOWN:
|
|
i := int(win.LOWORD(uint32(nle.SendMessage(win.EM_CHARFROMPOS, 0, lParam))))
|
|
|
|
if min := len(nle.prefix); i < min {
|
|
nle.SetFocus()
|
|
nle.SetTextSelection(min, min)
|
|
return 0
|
|
}
|
|
if max := len(nle.textUTF16()) - len(nle.suffix); i > max {
|
|
nle.SetFocus()
|
|
nle.SetTextSelection(max, max)
|
|
return 0
|
|
}
|
|
|
|
case win.WM_LBUTTONDBLCLK:
|
|
nle.selectNumber()
|
|
return 0
|
|
|
|
case win.WM_MOUSEMOVE:
|
|
i := int(win.LOWORD(uint32(nle.SendMessage(win.EM_CHARFROMPOS, 0, lParam))))
|
|
|
|
if min := len(nle.prefix); i < min {
|
|
return 0
|
|
}
|
|
if max := len(nle.textUTF16()) - len(nle.suffix); i > max {
|
|
return 0
|
|
}
|
|
|
|
case win.WM_MOUSEWHEEL:
|
|
if nle.ReadOnly() || nle.increment <= 0 {
|
|
break
|
|
}
|
|
|
|
delta := float64(int16(win.HIWORD(uint32(wParam))))
|
|
nle.incrementValue(delta / 120 * nle.increment)
|
|
return 0
|
|
|
|
case win.WM_PASTE:
|
|
if nle.ReadOnly() {
|
|
break
|
|
}
|
|
|
|
ret := nle.LineEdit.WndProc(hwnd, msg, wParam, lParam)
|
|
if !nle.tryUpdateValue(true) {
|
|
nle.setTextFromValue(nle.value)
|
|
}
|
|
nle.selectNumber()
|
|
return ret
|
|
|
|
case win.WM_SETFOCUS:
|
|
nle.onFocusChanged()
|
|
nle.selectNumber()
|
|
|
|
case win.EM_SETSEL:
|
|
start := int(wParam)
|
|
end := int(lParam)
|
|
adjusted := false
|
|
if min := len(nle.prefix); start < min {
|
|
start = min
|
|
adjusted = true
|
|
}
|
|
if max := len(nle.textUTF16()) - len(nle.suffix); end < 0 || end > max {
|
|
end = max
|
|
adjusted = true
|
|
}
|
|
|
|
if adjusted {
|
|
nle.SetTextSelection(start, end)
|
|
return 0
|
|
}
|
|
}
|
|
|
|
return nle.LineEdit.WndProc(hwnd, msg, wParam, lParam)
|
|
}
|
|
|
|
func (nle *numberLineEdit) onFocusChanged() {
|
|
if ne := windowFromHandle(win.GetParent(nle.hWnd)); ne != nil {
|
|
if wnd := windowFromHandle(win.GetParent(ne.Handle())); wnd != nil {
|
|
if _, ok := wnd.(Container); ok {
|
|
ne.(Widget).AsWidgetBase().invalidateBorderInParent()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ne *NumberEdit) SetToolTipText(s string) error {
|
|
return ne.edit.SetToolTipText(s)
|
|
}
|