601 lines
11 KiB
Go
601 lines
11 KiB
Go
// 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 (
|
|
"bytes"
|
|
"math"
|
|
"math/big"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/lxn/win"
|
|
)
|
|
|
|
var (
|
|
decimalSepB byte
|
|
decimalSepUint16 uint16
|
|
decimalSepS string
|
|
groupSepB byte
|
|
groupSepUint16 uint16
|
|
groupSepS string
|
|
)
|
|
|
|
func init() {
|
|
AppendToWalkInit(func() {
|
|
var buf [4]uint16
|
|
|
|
win.GetLocaleInfo(win.LOCALE_USER_DEFAULT, win.LOCALE_SDECIMAL, &buf[0], int32(len(buf)))
|
|
decimalSepB = byte(buf[0])
|
|
decimalSepS = syscall.UTF16ToString(buf[0:1])
|
|
decimalSepUint16 = buf[0]
|
|
|
|
win.GetLocaleInfo(win.LOCALE_USER_DEFAULT, win.LOCALE_STHOUSAND, &buf[0], int32(len(buf)))
|
|
groupSepB = byte(buf[0])
|
|
groupSepS = syscall.UTF16ToString(buf[0:1])
|
|
groupSepUint16 = buf[0]
|
|
})
|
|
}
|
|
|
|
func maxi(a, b int) int {
|
|
if a > b {
|
|
return a
|
|
}
|
|
|
|
return b
|
|
}
|
|
|
|
func mini(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
|
|
return b
|
|
}
|
|
|
|
func boolToInt(value bool) int {
|
|
if value {
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func uint16IndexUint16(s []uint16, v uint16) int {
|
|
for i, u := range s {
|
|
if u == v {
|
|
return i
|
|
}
|
|
}
|
|
|
|
return -1
|
|
}
|
|
|
|
func uint16ContainsUint16(s []uint16, v uint16) bool {
|
|
return uint16IndexUint16(s, v) != -1
|
|
}
|
|
|
|
func uint16CountUint16(s []uint16, v uint16) int {
|
|
var count int
|
|
|
|
for _, u := range s {
|
|
if u == v {
|
|
count++
|
|
}
|
|
}
|
|
|
|
return count
|
|
}
|
|
|
|
func uint16RemoveUint16(s []uint16, v uint16) []uint16 {
|
|
count := uint16CountUint16(s, v)
|
|
if count == 0 {
|
|
return s
|
|
}
|
|
|
|
ret := make([]uint16, 0, len(s)-count)
|
|
|
|
for _, u := range s {
|
|
if u != v {
|
|
ret = append(ret, u)
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func assertFloat64Or(value interface{}, defaultValue float64) float64 {
|
|
if f, ok := value.(float64); ok {
|
|
return f
|
|
}
|
|
|
|
return defaultValue
|
|
}
|
|
|
|
func assertIntOr(value interface{}, defaultValue int) int {
|
|
if n, ok := value.(int); ok {
|
|
return n
|
|
}
|
|
|
|
return defaultValue
|
|
}
|
|
|
|
func assertStringOr(value interface{}, defaultValue string) string {
|
|
if s, ok := value.(string); ok {
|
|
return s
|
|
}
|
|
|
|
return defaultValue
|
|
}
|
|
|
|
func assertTimeOr(value interface{}, defaultValue time.Time) time.Time {
|
|
if t, ok := value.(time.Time); ok {
|
|
return t
|
|
}
|
|
|
|
return defaultValue
|
|
}
|
|
|
|
func ParseFloat(s string) (float64, error) {
|
|
s = strings.TrimSpace(s)
|
|
|
|
t := FormatFloatGrouped(1000, 2)
|
|
|
|
replaceSep := func(new string, index func(string, func(rune) bool) int) {
|
|
i := index(t, func(r rune) bool {
|
|
return r < '0' || r > '9'
|
|
})
|
|
|
|
var sep string
|
|
if i > -1 {
|
|
sep = string(t[i])
|
|
}
|
|
if sep != "" {
|
|
s = strings.Replace(s, string(sep), new, -1)
|
|
}
|
|
}
|
|
|
|
replaceSep("", strings.IndexFunc)
|
|
replaceSep(".", strings.LastIndexFunc)
|
|
|
|
return strconv.ParseFloat(s, 64)
|
|
}
|
|
|
|
func FormatFloat(f float64, prec int) string {
|
|
return formatFloatString(strconv.FormatFloat(f, 'f', prec, 64), prec, false)
|
|
}
|
|
|
|
func FormatFloatGrouped(f float64, prec int) string {
|
|
return formatFloatString(strconv.FormatFloat(f, 'f', maxi(1, prec), 64), prec, true)
|
|
}
|
|
|
|
func formatBigRat(r *big.Rat, prec int) string {
|
|
return formatFloatString(r.FloatString(prec), prec, false)
|
|
}
|
|
|
|
func formatBigRatGrouped(r *big.Rat, prec int) string {
|
|
return formatFloatString(r.FloatString(prec), prec, true)
|
|
}
|
|
|
|
func formatFloatString(s string, prec int, grouped bool) string {
|
|
switch s {
|
|
case "NaN", "-Inf", "+Inf":
|
|
return s
|
|
}
|
|
|
|
s = strings.Replace(s, ".", decimalSepS, 1)
|
|
if !grouped {
|
|
return s
|
|
}
|
|
|
|
b := new(bytes.Buffer)
|
|
|
|
var firstDigit int
|
|
if len(s) > 0 && s[0] == '-' {
|
|
firstDigit = 1
|
|
b.WriteByte('-')
|
|
s = s[1:]
|
|
}
|
|
|
|
intLen := len(s) - maxi(1, prec) - 1
|
|
|
|
n := intLen % 3
|
|
if n != 0 {
|
|
b.WriteString(s[:n])
|
|
}
|
|
for i := n; i < intLen; i += 3 {
|
|
if b.Len() > firstDigit {
|
|
b.WriteByte(groupSepB)
|
|
}
|
|
b.WriteString(s[i : i+3])
|
|
}
|
|
|
|
b.WriteString(s[intLen:])
|
|
|
|
s = b.String()
|
|
|
|
if prec == 0 {
|
|
s = s[:len(s)-2]
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func applyEnabledToDescendants(window Window, enabled bool) {
|
|
wb := window.AsWindowBase()
|
|
wb.applyEnabled(enabled)
|
|
|
|
walkDescendants(window, func(w Window) bool {
|
|
if w.Handle() == wb.hWnd {
|
|
return true
|
|
}
|
|
|
|
if enabled && !w.AsWindowBase().enabled {
|
|
return false
|
|
}
|
|
|
|
w.(applyEnableder).applyEnabled(enabled)
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
var seenInApplyFontToDescendantsDuringDPIChange map[*WindowBase]bool
|
|
|
|
func applyFontToDescendants(window Window, font *Font) {
|
|
wb := window.AsWindowBase()
|
|
wb.applyFont(font)
|
|
|
|
walkDescendants(window, func(w Window) bool {
|
|
if w.Handle() == wb.hWnd {
|
|
return true
|
|
}
|
|
|
|
if w.AsWindowBase().font != nil {
|
|
return false
|
|
}
|
|
|
|
if seenInApplyFontToDescendantsDuringDPIChange != nil {
|
|
wb := w.AsWindowBase()
|
|
if seenInApplyFontToDescendantsDuringDPIChange[wb] {
|
|
return true
|
|
}
|
|
seenInApplyFontToDescendantsDuringDPIChange[wb] = true
|
|
}
|
|
|
|
w.(applyFonter).applyFont(font)
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
func applySysColorsToDescendants(window Window) {
|
|
wb := window.AsWindowBase()
|
|
wb.ApplySysColors()
|
|
|
|
walkDescendants(window, func(w Window) bool {
|
|
if w.Handle() == wb.hWnd {
|
|
return true
|
|
}
|
|
|
|
w.(ApplySysColorser).ApplySysColors()
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
var seenInApplyDPIToDescendantsDuringDPIChange map[*WindowBase]bool
|
|
|
|
func applyDPIToDescendants(window Window, dpi int) {
|
|
wb := window.AsWindowBase()
|
|
wb.ApplyDPI(dpi)
|
|
|
|
walkDescendants(window, func(w Window) bool {
|
|
if w.Handle() == wb.hWnd {
|
|
return true
|
|
}
|
|
|
|
if seenInApplyDPIToDescendantsDuringDPIChange != nil {
|
|
wb := w.AsWindowBase()
|
|
if seenInApplyDPIToDescendantsDuringDPIChange[wb] {
|
|
return true
|
|
}
|
|
seenInApplyDPIToDescendantsDuringDPIChange[wb] = true
|
|
}
|
|
|
|
w.(ApplyDPIer).ApplyDPI(dpi)
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
func walkDescendants(window Window, f func(w Window) bool) {
|
|
window = window.AsWindowBase().window
|
|
|
|
if window == nil || !f(window) {
|
|
return
|
|
}
|
|
|
|
var children []*WidgetBase
|
|
|
|
switch w := window.(type) {
|
|
case *NumberEdit:
|
|
if w.edit != nil {
|
|
children = append(children, w.edit.AsWidgetBase())
|
|
}
|
|
|
|
case *TabWidget:
|
|
for _, p := range w.Pages().items {
|
|
children = append(children, p.AsWidgetBase())
|
|
}
|
|
|
|
case Container:
|
|
if c := w.Children(); c != nil {
|
|
children = c.items
|
|
} else {
|
|
children = nil
|
|
}
|
|
}
|
|
|
|
for _, wb := range children {
|
|
walkDescendants(wb.window.(Widget), f)
|
|
}
|
|
}
|
|
|
|
func less(a, b interface{}, order SortOrder) bool {
|
|
if _, ok := a.(error); ok {
|
|
_, bIsErr := b.(error)
|
|
|
|
return order == SortAscending == !bIsErr
|
|
}
|
|
if _, ok := b.(error); ok {
|
|
return order == SortDescending
|
|
}
|
|
|
|
if a == nil {
|
|
return order == SortAscending == (b != nil)
|
|
}
|
|
if b == nil {
|
|
return order == SortDescending
|
|
}
|
|
|
|
switch av := a.(type) {
|
|
case string:
|
|
if bv, ok := b.(string); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case int:
|
|
if bv, ok := b.(int); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case float64:
|
|
if bv, ok := b.(float64); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case float32:
|
|
if bv, ok := b.(float32); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case int64:
|
|
if bv, ok := b.(int64); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case int32:
|
|
if bv, ok := b.(int32); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case int16:
|
|
if bv, ok := b.(int16); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case int8:
|
|
if bv, ok := b.(int8); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case uint:
|
|
if bv, ok := b.(uint); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case uint64:
|
|
if bv, ok := b.(uint64); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case uint32:
|
|
if bv, ok := b.(uint32); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case uint16:
|
|
if bv, ok := b.(uint16); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case uint8:
|
|
if bv, ok := b.(uint8); ok {
|
|
if order == SortAscending {
|
|
return av < bv
|
|
} else {
|
|
return bv < av
|
|
}
|
|
}
|
|
|
|
case time.Time:
|
|
if bv, ok := b.(time.Time); ok {
|
|
if order == SortAscending {
|
|
return av.Before(bv)
|
|
} else {
|
|
return bv.Before(av)
|
|
}
|
|
}
|
|
|
|
case bool:
|
|
if bv, ok := b.(bool); ok {
|
|
if order == SortAscending {
|
|
return !av && bv
|
|
} else {
|
|
return !bv && av
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func dpiForHDC(hdc win.HDC) int {
|
|
if hwnd := win.WindowFromDC(hdc); hwnd != 0 {
|
|
return int(win.GetDpiForWindow(hwnd))
|
|
}
|
|
|
|
return int(win.GetDeviceCaps(hdc, win.LOGPIXELSX))
|
|
}
|
|
|
|
// IntFrom96DPI converts from 1/96" units to native pixels.
|
|
func IntFrom96DPI(value, dpi int) int {
|
|
return scaleInt(value, float64(dpi)/96.0)
|
|
}
|
|
|
|
// IntTo96DPI converts from native pixels to 1/96" units.
|
|
func IntTo96DPI(value, dpi int) int {
|
|
return scaleInt(value, 96.0/float64(dpi))
|
|
}
|
|
|
|
func scaleInt(value int, scale float64) int {
|
|
return int(math.Round(float64(value) * scale))
|
|
}
|
|
|
|
// MarginsFrom96DPI converts from 1/96" units to native pixels.
|
|
func MarginsFrom96DPI(value Margins, dpi int) Margins {
|
|
return scaleMargins(value, float64(dpi)/96.0)
|
|
}
|
|
|
|
// MarginsTo96DPI converts from native pixels to 1/96" units.
|
|
func MarginsTo96DPI(value Margins, dpi int) Margins {
|
|
return scaleMargins(value, 96.0/float64(dpi))
|
|
}
|
|
|
|
func scaleMargins(value Margins, scale float64) Margins {
|
|
return Margins{
|
|
HNear: scaleInt(value.HNear, scale),
|
|
VNear: scaleInt(value.VNear, scale),
|
|
HFar: scaleInt(value.HFar, scale),
|
|
VFar: scaleInt(value.VFar, scale),
|
|
}
|
|
}
|
|
|
|
// PointFrom96DPI converts from 1/96" units to native pixels.
|
|
func PointFrom96DPI(value Point, dpi int) Point {
|
|
return scalePoint(value, float64(dpi)/96.0)
|
|
}
|
|
|
|
// PointTo96DPI converts from native pixels to 1/96" units.
|
|
func PointTo96DPI(value Point, dpi int) Point {
|
|
return scalePoint(value, 96.0/float64(dpi))
|
|
}
|
|
|
|
func scalePoint(value Point, scale float64) Point {
|
|
return Point{
|
|
X: scaleInt(value.X, scale),
|
|
Y: scaleInt(value.Y, scale),
|
|
}
|
|
}
|
|
|
|
// RectangleFrom96DPI converts from 1/96" units to native pixels.
|
|
func RectangleFrom96DPI(value Rectangle, dpi int) Rectangle {
|
|
return scaleRectangle(value, float64(dpi)/96.0)
|
|
}
|
|
|
|
// RectangleTo96DPI converts from native pixels to 1/96" units.
|
|
func RectangleTo96DPI(value Rectangle, dpi int) Rectangle {
|
|
return scaleRectangle(value, 96.0/float64(dpi))
|
|
}
|
|
|
|
func scaleRectangle(value Rectangle, scale float64) Rectangle {
|
|
return Rectangle{
|
|
X: scaleInt(value.X, scale),
|
|
Y: scaleInt(value.Y, scale),
|
|
Width: scaleInt(value.Width, scale),
|
|
Height: scaleInt(value.Height, scale),
|
|
}
|
|
}
|
|
|
|
// SizeFrom96DPI converts from 1/96" units to native pixels.
|
|
func SizeFrom96DPI(value Size, dpi int) Size {
|
|
return scaleSize(value, float64(dpi)/96.0)
|
|
}
|
|
|
|
// SizeTo96DPI converts from native pixels to 1/96" units.
|
|
func SizeTo96DPI(value Size, dpi int) Size {
|
|
return scaleSize(value, 96.0/float64(dpi))
|
|
}
|
|
|
|
func scaleSize(value Size, scale float64) Size {
|
|
return Size{
|
|
Width: scaleInt(value.Width, scale),
|
|
Height: scaleInt(value.Height, scale),
|
|
}
|
|
}
|