summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorAlexander Neumann <alexander.neumann@picos-software.com>2019-04-30 17:47:37 +0200
committerAlexander Neumann <alexander.neumann@picos-software.com>2019-04-30 17:47:37 +0200
commit5fe20850cc25a0340c6f9f0c75a73566c68e047b (patch)
tree229c426b057e2fde750bf720c03df2a43fd04bfe
parentGroupBox: Use left margin from layout for check box offset (diff)
downloadwireguard-windows-5fe20850cc25a0340c6f9f0c75a73566c68e047b.tar.xz
wireguard-windows-5fe20850cc25a0340c6f9f0c75a73566c68e047b.zip
Initial multi monitor HIDPI support
-rw-r--r--boxlayout.go32
-rw-r--r--button.go9
-rw-r--r--canvas.go10
-rw-r--r--checkbox.go34
-rw-r--r--combobox.go4
-rw-r--r--container.go5
-rw-r--r--flowlayout.go55
-rw-r--r--form.go14
-rw-r--r--gridlayout.go67
-rw-r--r--lineedit.go3
-rw-r--r--pushbutton.go12
-rw-r--r--radiobutton.go11
-rw-r--r--tableview.go2
-rw-r--r--toolbar.go14
-rw-r--r--widget.go14
-rw-r--r--window.go130
16 files changed, 275 insertions, 141 deletions
diff --git a/boxlayout.go b/boxlayout.go
index 0c261726..96d8afd2 100644
--- a/boxlayout.go
+++ b/boxlayout.go
@@ -26,7 +26,7 @@ type BoxLayout struct {
orientation Orientation
alignment Alignment2D
hwnd2StretchFactor map[win.HWND]int
- size2MinSize map[Size]Size
+ sizeAndDPI2MinSize map[sizeAndDPI]Size
resetNeeded bool
}
@@ -34,7 +34,7 @@ func newBoxLayout(orientation Orientation) *BoxLayout {
return &BoxLayout{
orientation: orientation,
hwnd2StretchFactor: make(map[win.HWND]int),
- size2MinSize: make(map[Size]Size),
+ sizeAndDPI2MinSize: make(map[sizeAndDPI]Size),
margins: Margins{9, 9, 9, 9},
spacing: 6,
}
@@ -243,18 +243,24 @@ func (l *BoxLayout) MinSizeForSize(size Size) Size {
return Size{}
}
- if min, ok := l.size2MinSize[size]; ok {
+ dpi := l.container.DPI()
+
+ if min, ok := l.sizeAndDPI2MinSize[sizeAndDPI{size, dpi}]; ok {
return min
}
bounds := Rectangle{Width: size.Width, Height: size.Height}
- items, err := boxLayoutItems(widgetsToLayout(l.Container().Children()), l.orientation, l.alignment, bounds, l.margins, l.spacing, l.hwnd2StretchFactor)
+ wb := l.container.AsWindowBase()
+ margins := wb.marginsFrom96DPI(l.margins)
+ spacing := wb.intFrom96DPI(l.spacing)
+
+ items, err := boxLayoutItems(widgetsToLayout(l.Container().Children()), l.orientation, l.alignment, bounds, margins, spacing, l.hwnd2StretchFactor)
if err != nil {
return Size{}
}
- s := Size{l.margins.HNear + l.margins.HFar, l.margins.VNear + l.margins.VFar}
+ s := Size{margins.HNear + margins.HFar, margins.VNear + margins.VFar}
var maxSecondary int
@@ -280,15 +286,15 @@ func (l *BoxLayout) MinSizeForSize(size Size) Size {
}
if l.orientation == Horizontal {
- s.Width += (len(items) - 1) * l.spacing
+ s.Width += (len(items) - 1) * spacing
s.Height += maxSecondary
} else {
- s.Height += (len(items) - 1) * l.spacing
+ s.Height += (len(items) - 1) * spacing
s.Width += maxSecondary
}
if s.Width > 0 && s.Height > 0 {
- l.size2MinSize[size] = s
+ l.sizeAndDPI2MinSize[sizeAndDPI{size, dpi}] = s
}
return s
@@ -299,7 +305,7 @@ func (l *BoxLayout) Update(reset bool) error {
return nil
}
- l.size2MinSize = make(map[Size]Size)
+ l.sizeAndDPI2MinSize = make(map[sizeAndDPI]Size)
if reset {
l.resetNeeded = true
@@ -321,12 +327,16 @@ func (l *BoxLayout) Update(reset bool) error {
ifContainerIsScrollViewDoCoolSpecialLayoutStuff(l)
- items, err := boxLayoutItems(widgetsToLayout(l.Container().Children()), l.orientation, l.alignment, l.container.ClientBounds(), l.margins, l.spacing, l.hwnd2StretchFactor)
+ items, err := boxLayoutItems(widgetsToLayout(l.Container().Children()), l.orientation, l.alignment, l.container.ClientBounds(), l.container.AsWindowBase().marginsFrom96DPI(l.margins), l.container.AsWindowBase().intFrom96DPI(l.spacing), l.hwnd2StretchFactor)
if err != nil {
return err
}
- return applyLayoutResults(l.container, items)
+ if err := applyLayoutResults(l.container, items); err != nil {
+ return err
+ }
+
+ return nil
}
func boxLayoutFlags(orientation Orientation, children *WidgetList) LayoutFlags {
diff --git a/button.go b/button.go
index 53c07f74..83dac2c7 100644
--- a/button.go
+++ b/button.go
@@ -9,6 +9,7 @@ package walk
import (
"fmt"
"strconv"
+ "unsafe"
"github.com/lxn/win"
)
@@ -85,6 +86,14 @@ func (b *Button) init() {
b.textChangedPublisher.Event()))
}
+func (b *Button) MinSizeHint() Size {
+ var s win.SIZE
+
+ b.SendMessage(win.BCM_GETIDEALSIZE, 0, uintptr(unsafe.Pointer(&s)))
+
+ return maxSize(Size{int(s.CX), int(s.CY)}, b.dialogBaseUnitsToPixels(Size{50, 14}))
+}
+
func (b *Button) Image() Image {
return b.image
}
diff --git a/canvas.go b/canvas.go
index 872bec7e..ef7709a5 100644
--- a/canvas.go
+++ b/canvas.go
@@ -101,7 +101,9 @@ func newCanvasFromHWND(hwnd win.HWND) (*Canvas, error) {
return nil, newError("GetDC failed")
}
- return (&Canvas{hdc: hdc, hwnd: hwnd}).init()
+ dpi := int(win.GetDpiForWindow(hwnd))
+
+ return (&Canvas{hdc: hdc, hwnd: hwnd, dpix: dpi, dpiy: dpi}).init()
}
func newCanvasFromHDC(hdc win.HDC) (*Canvas, error) {
@@ -113,8 +115,10 @@ func newCanvasFromHDC(hdc win.HDC) (*Canvas, error) {
}
func (c *Canvas) init() (*Canvas, error) {
- c.dpix = int(win.GetDeviceCaps(c.hdc, win.LOGPIXELSX))
- c.dpiy = int(win.GetDeviceCaps(c.hdc, win.LOGPIXELSY))
+ if c.dpix == 0 || c.dpiy == 0 {
+ c.dpix = int(win.GetDeviceCaps(c.hdc, win.LOGPIXELSX))
+ c.dpiy = int(win.GetDeviceCaps(c.hdc, win.LOGPIXELSY))
+ }
if win.SetBkMode(c.hdc, win.TRANSPARENT) == 0 {
return nil, newError("SetBkMode failed")
diff --git a/checkbox.go b/checkbox.go
index 778ecbbe..dbfc3a76 100644
--- a/checkbox.go
+++ b/checkbox.go
@@ -8,7 +8,6 @@ package walk
import (
"strconv"
- "syscall"
"github.com/lxn/win"
)
@@ -65,39 +64,6 @@ func (*CheckBox) LayoutFlags() LayoutFlags {
return 0
}
-func (cb *CheckBox) MinSizeHint() Size {
- if checkBoxCheckSize.Width == 0 {
- if win.IsAppThemed() {
- hTheme := win.OpenThemeData(cb.hWnd, syscall.StringToUTF16Ptr("Button"))
- defer win.CloseThemeData(hTheme)
-
- hdc := win.GetDC(cb.hWnd)
- defer win.ReleaseDC(cb.hWnd, hdc)
-
- var s win.SIZE
- if win.S_OK == win.GetThemePartSize(hTheme, hdc, win.BP_CHECKBOX, win.CBS_UNCHECKEDNORMAL, nil, win.TS_TRUE, &s) {
- checkBoxCheckSize.Width = int(s.CX)
- checkBoxCheckSize.Height = int(s.CY)
- }
- } else {
- checkBoxCheckSize.Width = 12
- checkBoxCheckSize.Height = 12
- }
- }
-
- if cb.Text() == "" {
- return checkBoxCheckSize
- }
-
- defaultSize := cb.dialogBaseUnitsToPixels(Size{50, 10})
- textSize := cb.calculateTextSizeImpl("n" + cb.text())
-
- w := textSize.Width + checkBoxCheckSize.Width
- h := maxi(defaultSize.Height, textSize.Height)
-
- return Size{w, h}
-}
-
func (cb *CheckBox) SizeHint() Size {
return cb.MinSizeHint()
}
diff --git a/combobox.go b/combobox.go
index 0548d30b..ff70d7f5 100644
--- a/combobox.go
+++ b/combobox.go
@@ -244,7 +244,7 @@ func (cb *ComboBox) MinSizeHint() Size {
}
// FIXME: Use GetThemePartSize instead of guessing
- w := maxi(defaultSize.Width, cb.maxItemTextWidth+30)
+ w := maxi(defaultSize.Width, cb.maxItemTextWidth+int(win.GetSystemMetricsForDpi(win.SM_CXVSCROLL, uint32(cb.DPI())))+8)
h := defaultSize.Height + 1
return Size{w, h}
@@ -542,7 +542,7 @@ func (cb *ComboBox) calculateMaxItemTextWidth() int {
}
defer win.ReleaseDC(cb.hWnd, hdc)
- hFontOld := win.SelectObject(hdc, win.HGDIOBJ(cb.Font().handleForDPI(0)))
+ hFontOld := win.SelectObject(hdc, win.HGDIOBJ(cb.Font().handleForDPI(cb.DPI())))
defer win.SelectObject(hdc, hFontOld)
var maxWidth int
diff --git a/container.go b/container.go
index 4af7f28c..b94bccda 100644
--- a/container.go
+++ b/container.go
@@ -100,6 +100,11 @@ type Layout interface {
Update(reset bool) error
}
+type sizeAndDPI struct {
+ size Size
+ dpi int
+}
+
type HeightForWidther interface {
HeightForWidth(width int) int
}
diff --git a/flowlayout.go b/flowlayout.go
index 1cf2ec4e..e5c79cce 100644
--- a/flowlayout.go
+++ b/flowlayout.go
@@ -14,7 +14,7 @@ type FlowLayout struct {
container Container
hwnd2StretchFactor map[win.HWND]int
margins Margins
- size2MinSize map[Size]Size
+ sizeAndDPI2MinSize map[sizeAndDPI]Size
spacing int
alignment Alignment2D
resetNeeded bool
@@ -32,7 +32,7 @@ type flowLayoutSectionItem struct {
}
func NewFlowLayout() *FlowLayout {
- return &FlowLayout{size2MinSize: make(map[Size]Size)}
+ return &FlowLayout{sizeAndDPI2MinSize: make(map[sizeAndDPI]Size)}
}
func (l *FlowLayout) Container() Container {
@@ -163,7 +163,9 @@ func (l *FlowLayout) MinSizeForSize(size Size) Size {
return Size{}
}
- if min, ok := l.size2MinSize[size]; ok {
+ dpi := l.container.DPI()
+
+ if min, ok := l.sizeAndDPI2MinSize[sizeAndDPI{size, dpi}]; ok {
return min
}
@@ -174,6 +176,9 @@ func (l *FlowLayout) MinSizeForSize(size Size) Size {
var s Size
var maxPrimary int
+ wb := l.container.AsWindowBase()
+ spacing := wb.intFrom96DPI(l.spacing)
+
for i, section := range sections {
var widgets []Widget
var sectionMinWidth int
@@ -182,12 +187,12 @@ func (l *FlowLayout) MinSizeForSize(size Size) Size {
sectionMinWidth += item.minSize.Width
}
- sectionMinWidth += (len(section.items) - 1) * l.spacing
+ sectionMinWidth += (len(section.items) - 1) * spacing
maxPrimary = maxi(maxPrimary, sectionMinWidth)
bounds.Height = section.secondaryMinSize
- margins := l.margins
+ margins := wb.marginsFrom96DPI(l.margins)
if i > 0 {
margins.VNear = 0
}
@@ -195,7 +200,8 @@ func (l *FlowLayout) MinSizeForSize(size Size) Size {
margins.VFar = 0
}
- layoutItems, err := boxLayoutItems(widgets, Horizontal, l.alignment, bounds, margins, l.spacing, l.hwnd2StretchFactor)
+
+ layoutItems, err := boxLayoutItems(widgets, Horizontal, l.alignment, bounds, margins, spacing, l.hwnd2StretchFactor)
if err != nil {
return Size{}
}
@@ -216,16 +222,16 @@ func (l *FlowLayout) MinSizeForSize(size Size) Size {
s.Height += maxSecondary
- bounds.Y += maxSecondary + l.spacing
+ bounds.Y += maxSecondary + spacing
}
s.Width = maxPrimary
- s.Width += l.margins.HNear + l.margins.HFar
- s.Height += l.margins.VNear + l.margins.VFar + (len(sections)-1)*l.spacing
+ s.Width += wb.intFrom96DPI(l.margins.HNear + l.margins.HFar)
+ s.Height += wb.intFrom96DPI(l.margins.VNear + l.margins.VFar + (len(sections)-1)*l.spacing)
if s.Width > 0 && s.Height > 0 {
- l.size2MinSize[size] = s
+ l.sizeAndDPI2MinSize[sizeAndDPI{size, dpi}] = s
}
return s
@@ -236,7 +242,7 @@ func (l *FlowLayout) Update(reset bool) error {
return nil
}
- l.size2MinSize = make(map[Size]Size)
+ l.sizeAndDPI2MinSize = make(map[sizeAndDPI]Size)
if reset {
l.resetNeeded = true
@@ -261,6 +267,8 @@ func (l *FlowLayout) Update(reset bool) error {
bounds := l.container.ClientBounds()
sections := l.sectionsForPrimarySize(bounds.Width)
+ wb := l.container.AsWindowBase()
+
for i, section := range sections {
var widgets []Widget
for _, item := range section.items {
@@ -269,7 +277,7 @@ func (l *FlowLayout) Update(reset bool) error {
bounds.Height = section.secondaryMinSize
- margins := l.margins
+ margins := wb.marginsFrom96DPI(l.margins)
if i > 0 {
margins.VNear = 0
}
@@ -277,7 +285,9 @@ func (l *FlowLayout) Update(reset bool) error {
margins.VFar = 0
}
- layoutItems, err := boxLayoutItems(widgets, Horizontal, l.alignment, bounds, margins, l.spacing, l.hwnd2StretchFactor)
+ spacing := wb.intFrom96DPI(l.spacing)
+
+ layoutItems, err := boxLayoutItems(widgets, Horizontal, l.alignment, bounds, margins, spacing, l.hwnd2StretchFactor)
if err != nil {
return err
}
@@ -296,7 +306,7 @@ func (l *FlowLayout) Update(reset bool) error {
bounds.Height = maxSecondary + margins.VNear + margins.VFar
- if layoutItems, err = boxLayoutItems(widgets, Horizontal, l.alignment, bounds, margins, l.spacing, l.hwnd2StretchFactor); err != nil {
+ if layoutItems, err = boxLayoutItems(widgets, Horizontal, l.alignment, bounds, margins, spacing, l.hwnd2StretchFactor); err != nil {
return err
}
@@ -304,7 +314,7 @@ func (l *FlowLayout) Update(reset bool) error {
return err
}
- bounds.Y += bounds.Height + l.spacing
+ bounds.Y += bounds.Height + spacing
}
return nil
@@ -316,14 +326,17 @@ func (l *FlowLayout) sectionsForPrimarySize(primarySize int) []flowLayoutSection
var sections []flowLayoutSection
+ margins := l.container.AsWindowBase().marginsFrom96DPI(l.margins)
+ spacing := l.container.AsWindowBase().intFrom96DPI(l.spacing)
+
section := flowLayoutSection{
- primarySpaceLeft: primarySize - l.margins.HNear - l.margins.HFar,
+ primarySpaceLeft: primarySize - margins.HNear - margins.HFar,
}
addSection := func() {
sections = append(sections, section)
section.items = nil
- section.primarySpaceLeft = primarySize - l.margins.HNear - l.margins.HFar
+ section.primarySpaceLeft = primarySize - margins.HNear - margins.HFar
section.secondaryMinSize = 0
}
@@ -341,7 +354,7 @@ func (l *FlowLayout) sectionsForPrimarySize(primarySize int) []flowLayoutSection
addItem := func() {
section.items = append(section.items, item)
if len(section.items) > 1 {
- section.primarySpaceLeft -= l.spacing
+ section.primarySpaceLeft -= spacing
}
section.primarySpaceLeft -= item.minSize.Width
@@ -351,7 +364,7 @@ func (l *FlowLayout) sectionsForPrimarySize(primarySize int) []flowLayoutSection
if section.primarySpaceLeft < item.minSize.Width && len(section.items) == 0 {
addItem()
addSection()
- } else if section.primarySpaceLeft < l.spacing+item.minSize.Width && len(section.items) > 0 {
+ } else if section.primarySpaceLeft < spacing+item.minSize.Width && len(section.items) > 0 {
addSection()
addItem()
} else {
@@ -364,8 +377,8 @@ func (l *FlowLayout) sectionsForPrimarySize(primarySize int) []flowLayoutSection
}
if len(sections) > 0 {
- sections[0].secondaryMinSize += l.margins.VNear
- sections[len(sections)-1].secondaryMinSize += l.margins.VFar
+ sections[0].secondaryMinSize += margins.VNear
+ sections[len(sections)-1].secondaryMinSize += margins.VFar
}
return sections
diff --git a/form.go b/form.go
index 6b7e3e0c..ba4cdda2 100644
--- a/form.go
+++ b/form.go
@@ -683,8 +683,8 @@ func (fb *FormBase) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) u
}
mmi.PtMinTrackSize = win.POINT{
- int32(maxi(min.Width, fb.minSize.Width)),
- int32(maxi(min.Height, fb.minSize.Height)),
+ int32(fb.intFrom96DPI(maxi(min.Width, fb.minSize.Width))),
+ int32(fb.intFrom96DPI(maxi(min.Height, fb.minSize.Height))),
}
return 0
@@ -704,6 +704,16 @@ func (fb *FormBase) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) u
case win.WM_SIZE:
fb.clientComposite.SetBounds(fb.window.ClientBounds())
+ case win.WM_DPICHANGED:
+ wasSuspended := fb.Suspended()
+ fb.SetSuspended(true)
+ defer fb.SetSuspended(wasSuspended)
+
+ rc := (*win.RECT)(unsafe.Pointer(lParam))
+ fb.window.SetBounds(rectangleFromRECT(*rc))
+
+ fb.applyFont(fb.Font())
+
case win.WM_SYSCOMMAND:
if wParam == win.SC_CLOSE {
fb.closeReason = CloseReasonUser
diff --git a/gridlayout.go b/gridlayout.go
index a4e1a826..82938c81 100644
--- a/gridlayout.go
+++ b/gridlayout.go
@@ -34,18 +34,18 @@ type GridLayout struct {
margins Margins
spacing int
alignment Alignment2D
- resetNeeded bool
rowStretchFactors []int
columnStretchFactors []int
widgetBase2Info map[*WidgetBase]*gridLayoutWidgetInfo
cells [][]gridLayoutCell
- size2MinSize map[Size]Size
+ sizeAndDPI2MinSize map[sizeAndDPI]Size
+ resetNeeded bool
}
func NewGridLayout() *GridLayout {
l := &GridLayout{
- widgetBase2Info: make(map[*WidgetBase]*gridLayoutWidgetInfo),
- size2MinSize: make(map[Size]Size),
+ widgetBase2Info: make(map[*WidgetBase]*gridLayoutWidgetInfo),
+ sizeAndDPI2MinSize: make(map[sizeAndDPI]Size),
}
return l
@@ -397,7 +397,9 @@ func (l *GridLayout) MinSizeForSize(size Size) Size {
return Size{}
}
- if min, ok := l.size2MinSize[size]; ok {
+ dpi := l.container.DPI()
+
+ if min, ok := l.sizeAndDPI2MinSize[sizeAndDPI{size, dpi}]; ok {
return min
}
@@ -465,13 +467,17 @@ func (l *GridLayout) MinSizeForSize(size Size) Size {
heights[row] = maxHeight
}
- width := l.margins.HNear + l.margins.HFar
- height := l.margins.VNear + l.margins.VFar
+ margins := l.container.AsWindowBase().marginsFrom96DPI(l.margins)
+
+ width := margins.HNear + margins.HFar
+ height := margins.VNear + margins.VFar
+
+ spacing := l.container.AsWindowBase().intFrom96DPI(l.spacing)
for i, w := range ws {
if w > 0 {
if i > 0 {
- width += l.spacing
+ width += spacing
}
width += w
}
@@ -479,14 +485,14 @@ func (l *GridLayout) MinSizeForSize(size Size) Size {
for i, h := range heights {
if h > 0 {
if i > 0 {
- height += l.spacing
+ height += spacing
}
height += h
}
}
if width > 0 && height > 0 {
- l.size2MinSize[size] = Size{width, height}
+ l.sizeAndDPI2MinSize[sizeAndDPI{size, dpi}] = Size{width, height}
}
return Size{width, height}
@@ -494,12 +500,13 @@ func (l *GridLayout) MinSizeForSize(size Size) Size {
func (l *GridLayout) spannedWidth(info *gridLayoutWidgetInfo, widths []int) int {
width := 0
+ spacing := l.container.AsWindowBase().intFrom96DPI(l.spacing)
for i := info.cell.column; i < info.cell.column+info.spanHorz; i++ {
if w := widths[i]; w > 0 {
width += w
if i > info.cell.column {
- width += l.spacing
+ width += spacing
}
}
}
@@ -509,12 +516,13 @@ func (l *GridLayout) spannedWidth(info *gridLayoutWidgetInfo, widths []int) int
func (l *GridLayout) spannedHeight(info *gridLayoutWidgetInfo, heights []int) int {
height := 0
+ spacing := l.container.AsWindowBase().intFrom96DPI(l.spacing)
for i := info.cell.row; i < info.cell.row+info.spanVert; i++ {
if h := heights[i]; h > 0 {
height += h
if i > info.cell.row {
- height += l.spacing
+ height += spacing
}
}
}
@@ -564,7 +572,7 @@ func (l *GridLayout) Update(reset bool) error {
return nil
}
- l.size2MinSize = make(map[Size]Size)
+ l.sizeAndDPI2MinSize = make(map[sizeAndDPI]Size)
if reset {
l.resetNeeded = true
@@ -593,6 +601,9 @@ func (l *GridLayout) Update(reset bool) error {
items := make([]layoutResultItem, 0, len(l.widgetBase2Info))
+ margins := l.container.AsWindowBase().marginsFrom96DPI(l.margins)
+ spacing := l.container.AsWindowBase().intFrom96DPI(l.spacing)
+
for wb, info := range l.widgetBase2Info {
widget := wb.window.(Widget)
@@ -600,17 +611,17 @@ func (l *GridLayout) Update(reset bool) error {
continue
}
- x := l.margins.HNear
+ x := margins.HNear
for i := 0; i < info.cell.column; i++ {
if w := widths[i]; w > 0 {
- x += w + l.spacing
+ x += w + spacing
}
}
- y := l.margins.VNear
+ y := margins.VNear
for i := 0; i < info.cell.row; i++ {
if h := heights[i]; h > 0 {
- y += h + l.spacing
+ y += h + spacing
}
}
@@ -666,7 +677,11 @@ func (l *GridLayout) Update(reset bool) error {
items = append(items, layoutResultItem{widget: widget, bounds: Rectangle{X: x, Y: y, Width: w, Height: h}})
}
- return applyLayoutResults(l.container, items)
+ if err := applyLayoutResults(l.container, items); err != nil {
+ return err
+ }
+
+ return nil
}
func (l *GridLayout) sectionSizesForSpace(orientation Orientation, space int, widths []int) []int {
@@ -785,20 +800,24 @@ func (l *GridLayout) sectionSizesForSpace(orientation Orientation, space int, wi
sort.Stable(sortedSections)
+ margins := l.container.AsWindowBase().marginsFrom96DPI(l.margins)
+
if orientation == Horizontal {
- space -= l.margins.HNear + l.margins.HFar
+ space -= margins.HNear + margins.HFar
} else {
- space -= l.margins.VNear + l.margins.VFar
+ space -= margins.VNear + margins.VFar
}
+ spacing := l.container.AsWindowBase().intFrom96DPI(l.spacing)
+
var spacingRemaining int
for _, max := range maxSizes {
if max > 0 {
- spacingRemaining += l.spacing
+ spacingRemaining += spacing
}
}
if spacingRemaining > 0 {
- spacingRemaining -= l.spacing
+ spacingRemaining -= spacing
}
offsets := [3]int{0, sectionCountWithGreedyNonSpacer, sectionCountWithGreedyNonSpacer + sectionCountWithGreedySpacer}
@@ -832,8 +851,8 @@ func (l *GridLayout) sectionSizesForSpace(orientation Orientation, space int, wi
minSizesRemaining -= min
stretchFactorsRemaining -= stretch
- space -= (size + l.spacing)
- spacingRemaining -= l.spacing
+ space -= (size + spacing)
+ spacingRemaining -= spacing
}
}
diff --git a/lineedit.go b/lineedit.go
index da4c1251..7016bdc7 100644
--- a/lineedit.go
+++ b/lineedit.go
@@ -270,7 +270,6 @@ func (le *LineEdit) sizeHintForLimit(limit int) (size Size) {
}
func (le *LineEdit) initCharWidth() {
-
font := le.Font()
if font == le.charWidthFont {
return
@@ -285,7 +284,7 @@ func (le *LineEdit) initCharWidth() {
}
defer win.ReleaseDC(le.hWnd, hdc)
- defer win.SelectObject(hdc, win.SelectObject(hdc, win.HGDIOBJ(font.handleForDPI(0))))
+ defer win.SelectObject(hdc, win.SelectObject(hdc, win.HGDIOBJ(font.handleForDPI(le.DPI()))))
buf := []uint16{'M'}
diff --git a/pushbutton.go b/pushbutton.go
index 088dbe4e..f16c21cd 100644
--- a/pushbutton.go
+++ b/pushbutton.go
@@ -7,10 +7,6 @@
package walk
import (
- "unsafe"
-)
-
-import (
"github.com/lxn/win"
)
@@ -42,14 +38,6 @@ func (*PushButton) LayoutFlags() LayoutFlags {
return GrowableHorz
}
-func (pb *PushButton) MinSizeHint() Size {
- var s win.SIZE
-
- pb.SendMessage(win.BCM_GETIDEALSIZE, 0, uintptr(unsafe.Pointer(&s)))
-
- return maxSize(Size{int(s.CX), int(s.CY)}, pb.dialogBaseUnitsToPixels(Size{50, 14}))
-}
-
func (pb *PushButton) SizeHint() Size {
return pb.MinSizeHint()
}
diff --git a/radiobutton.go b/radiobutton.go
index 3fb36dc2..59947b5e 100644
--- a/radiobutton.go
+++ b/radiobutton.go
@@ -97,17 +97,6 @@ func (*RadioButton) LayoutFlags() LayoutFlags {
return 0
}
-func (rb *RadioButton) MinSizeHint() Size {
- defaultSize := rb.dialogBaseUnitsToPixels(Size{50, 10})
- textSize := rb.calculateTextSizeImpl("n" + rb.text())
-
- // FIXME: Use GetThemePartSize instead of GetSystemMetrics?
- w := textSize.Width + int(win.GetSystemMetrics(win.SM_CXMENUCHECK))
- h := maxi(defaultSize.Height, textSize.Height)
-
- return Size{w, h}
-}
-
func (rb *RadioButton) SizeHint() Size {
return rb.MinSizeHint()
}
diff --git a/tableview.go b/tableview.go
index d7140990..e84ec667 100644
--- a/tableview.go
+++ b/tableview.go
@@ -366,7 +366,7 @@ func (tv *TableView) applyEnabled(enabled bool) {
func (tv *TableView) applyFont(font *Font) {
tv.WidgetBase.applyFont(font)
- hFont := uintptr(font.handleForDPI(0))
+ hFont := uintptr(font.handleForDPI(tv.DPI()))
win.SendMessage(tv.hwndFrozenLV, win.WM_SETFONT, hFont, 0)
win.SendMessage(tv.hwndNormalLV, win.WM_SETFONT, hFont, 0)
diff --git a/toolbar.go b/toolbar.go
index 8d5f735c..82570646 100644
--- a/toolbar.go
+++ b/toolbar.go
@@ -124,6 +124,15 @@ func (tb *ToolBar) SizeHint() Size {
return Size{width, height}
}
+
+func (tb *ToolBar) applyFont(font *Font) {
+ tb.WidgetBase.applyFont(font)
+
+ tb.applyDefaultButtonWidth()
+
+ tb.updateParentLayout()
+}
+
func (tb *ToolBar) Orientation() Orientation {
style := win.GetWindowLong(tb.hWnd, win.GWL_STYLE)
@@ -143,8 +152,9 @@ func (tb *ToolBar) applyDefaultButtonWidth() error {
return nil
}
- lParam := uintptr(
- win.MAKELONG(uint16(tb.defaultButtonWidth), uint16(tb.defaultButtonWidth)))
+ width := tb.intFrom96DPI(tb.defaultButtonWidth)
+
+ lParam := uintptr(win.MAKELONG(uint16(width), uint16(width)))
if 0 == tb.SendMessage(win.TB_SETBUTTONWIDTH, 0, lParam) {
return newError("SendMessage(TB_SETBUTTONWIDTH)")
}
diff --git a/widget.go b/widget.go
index 13b76b90..e67e90bd 100644
--- a/widget.go
+++ b/widget.go
@@ -470,20 +470,20 @@ func (wb *WidgetBase) updateParentLayoutWithReset(reset bool) error {
layout := parent.Layout()
- var size2MinSize map[Size]Size
+ var sizeAndDPI2MinSize map[sizeAndDPI]Size
switch l := layout.(type) {
case *BoxLayout:
- size2MinSize = l.size2MinSize
+ sizeAndDPI2MinSize = l.sizeAndDPI2MinSize
case *GridLayout:
- size2MinSize = l.size2MinSize
+ sizeAndDPI2MinSize = l.sizeAndDPI2MinSize
case *FlowLayout:
- size2MinSize = l.size2MinSize
+ sizeAndDPI2MinSize = l.sizeAndDPI2MinSize
}
- if size2MinSize != nil {
- for k := range size2MinSize {
- delete(size2MinSize, k)
+ if sizeAndDPI2MinSize != nil {
+ for k := range sizeAndDPI2MinSize {
+ delete(sizeAndDPI2MinSize, k)
}
}
diff --git a/window.go b/window.go
index 9f030a07..b15b79a1 100644
--- a/window.go
+++ b/window.go
@@ -86,6 +86,9 @@ type Window interface {
// of.
Disposing() *Event
+ // DPI returns the current DPI value of the Window.
+ DPI() int
+
// Enabled returns if the Window is enabled for user interaction.
Enabled() bool
@@ -292,6 +295,7 @@ type calcTextSizeInfo struct {
font fontInfo
text string
size Size
+ dpi int
}
// WindowBase implements many operations common to all Windows.
@@ -776,6 +780,87 @@ func (wb *WindowBase) SetDoubleBuffering(enabled bool) error {
return wb.ensureExtendedStyleBits(win.WS_EX_COMPOSITED, enabled)
}
+// DPI returns the current DPI value of the WindowBase.
+func (wb *WindowBase) DPI() int {
+ return int(win.GetDpiForWindow(wb.hWnd))
+}
+
+func (wb *WindowBase) intFrom96DPI(value int) int {
+ return wb.scaleInt(value, float64(wb.DPI()) / 96.0)
+}
+
+func (wb *WindowBase) intTo96DPI(value int) int {
+ return wb.scaleInt(value, 96.0 / float64(wb.DPI()))
+}
+
+func (wb *WindowBase) scaleInt(value int, scale float64) int {
+ return int(float64(value) * scale)
+}
+
+func (wb *WindowBase) marginsFrom96DPI(value Margins) Margins {
+ return wb.scaleMargins(value, float64(wb.DPI()) / 96.0)
+}
+
+func (wb *WindowBase) marginsTo96DPI(value Margins) Margins {
+ return wb.scaleMargins(value, 96.0 / float64(wb.DPI()))
+}
+
+func (wb *WindowBase) scaleMargins(value Margins, scale float64) Margins {
+ return Margins{
+ HNear: int(float64(value.HNear) * scale),
+ VNear: int(float64(value.VNear) * scale),
+ HFar: int(float64(value.HFar) * scale),
+ VFar: int(float64(value.VFar) * scale),
+ }
+}
+
+func (wb *WindowBase) pointFrom96DPI(value Point) Point {
+ return wb.scalePoint(value, float64(wb.DPI()) / 96.0)
+}
+
+func (wb *WindowBase) pointTo96DPI(value Point) Point {
+ return wb.scalePoint(value, 96.0 / float64(wb.DPI()))
+}
+
+func (wb *WindowBase) scalePoint(value Point, scale float64) Point {
+ return Point{
+ X: int(float64(value.X) * scale),
+ Y: int(float64(value.Y) * scale),
+ }
+}
+
+func (wb *WindowBase) rectangleFrom96DPI(value Rectangle) Rectangle {
+ return wb.scaleRectangle(value, float64(wb.DPI()) / 96.0)
+}
+
+func (wb *WindowBase) rectangleTo96DPI(value Rectangle) Rectangle {
+ return wb.scaleRectangle(value, 96.0 / float64(wb.DPI()))
+}
+
+func (wb *WindowBase) scaleRectangle(value Rectangle, scale float64) Rectangle {
+ return Rectangle{
+ X: int(float64(value.X) * scale),
+ Y: int(float64(value.Y) * scale),
+ Width: int(float64(value.Width) * scale),
+ Height: int(float64(value.Height) * scale),
+ }
+}
+
+func (wb *WindowBase) sizeFrom96DPI(value Size) Size {
+ return wb.scaleSize(value, float64(wb.DPI()) / 96.0)
+}
+
+func (wb *WindowBase) sizeTo96DPI(value Size) Size {
+ return wb.scaleSize(value, 96.0 / float64(wb.DPI()))
+}
+
+func (wb *WindowBase) scaleSize(value Size, scale float64) Size {
+ return Size{
+ Width: int(float64(value.Width) * scale),
+ Height: int(float64(value.Height) * scale),
+ }
+}
+
// Enabled returns if the *WindowBase is enabled for user interaction.
func (wb *WindowBase) Enabled() bool {
return wb.enabled
@@ -845,11 +930,19 @@ func (wb *WindowBase) applyFont(font *Font) {
}
func SetWindowFont(hwnd win.HWND, font *Font) {
- win.SendMessage(hwnd, win.WM_SETFONT, uintptr(font.handleForDPI(0)), 1)
+ setWindowFont(hwnd, font)
}
func setWindowFont(hwnd win.HWND, font *Font) {
- win.SendMessage(hwnd, win.WM_SETFONT, uintptr(font.handleForDPI(0)), 1)
+ dpi := int(win.GetDpiForWindow(hwnd))
+
+ win.SendMessage(hwnd, win.WM_SETFONT, uintptr(font.handleForDPI(dpi)), 1)
+
+ if window := windowFromHandle(hwnd); window != nil {
+ if widget, ok := window.(Widget); ok {
+ widget.AsWidgetBase().updateParentLayoutWithReset(false)
+ }
+ }
}
// Suspended returns if the *WindowBase is suspended for layout and repainting
@@ -1119,24 +1212,35 @@ func (wb *WindowBase) SetMinMaxSize(min, max Size) error {
return nil
}
+type fontInfoAndDPI struct {
+ fontInfo
+ dpi int
+}
+
var (
dialogBaseUnitsUTF16StringPtr = syscall.StringToUTF16Ptr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
- fontInfo2DialogBaseUnits = make(map[fontInfo]Size)
+ fontInfoAndDPI2DialogBaseUnits = make(map[fontInfoAndDPI]Size)
)
func (wb *WindowBase) dialogBaseUnits() Size {
// The window may use a font different from that in WindowBase,
// like e.g. NumberEdit does, so we try to use the right one.
font := wb.window.Font()
- fi := fontInfo{family: font.Family(), pointSize: font.PointSize(), style: font.Style()}
- if s, ok := fontInfo2DialogBaseUnits[fi]; ok {
+ fi := fontInfoAndDPI{
+ fontInfo: fontInfo{
+ family: font.Family(),
+ pointSize: font.PointSize(),
+ style: font.Style(),
+ },
+ dpi: wb.DPI()}
+ if s, ok := fontInfoAndDPI2DialogBaseUnits[fi]; ok {
return s
}
hdc := win.GetDC(wb.hWnd)
defer win.ReleaseDC(wb.hWnd, hdc)
- hFont := font.handleForDPI(0)
+ hFont := font.handleForDPI(wb.DPI())
hFontOld := win.SelectObject(hdc, win.HGDIOBJ(hFont))
defer win.SelectObject(hdc, win.HGDIOBJ(hFontOld))
@@ -1156,7 +1260,7 @@ func (wb *WindowBase) dialogBaseUnits() Size {
s := Size{int((size.CX/26 + 1) / 2), int(tm.TmHeight)}
- fontInfo2DialogBaseUnits[fi] = s
+ fontInfoAndDPI2DialogBaseUnits[fi] = s
return s
}
@@ -1177,15 +1281,22 @@ func (wb *WindowBase) calculateTextSizeImpl(text string) Size {
func (wb *WindowBase) calculateTextSizeImplForWidth(text string, width int) Size {
font := wb.window.Font()
+ dpi := wb.DPI()
+
if wb.calcTextSizeInfoPrev != nil &&
width == wb.calcTextSizeInfoPrev.width &&
font.family == wb.calcTextSizeInfoPrev.font.family &&
font.pointSize == wb.calcTextSizeInfoPrev.font.pointSize &&
font.style == wb.calcTextSizeInfoPrev.font.style &&
- text == wb.calcTextSizeInfoPrev.text {
+ text == wb.calcTextSizeInfoPrev.text &&
+ dpi == wb.calcTextSizeInfoPrev.dpi {
return wb.calcTextSizeInfoPrev.size
}
+ if wb.calcTextSizeInfoPrev != nil && dpi != wb.calcTextSizeInfoPrev.dpi {
+ width = int(float64(width) * float64(dpi) / float64(wb.calcTextSizeInfoPrev.dpi))
+ }
+
var size Size
if width > 0 {
canvas, err := wb.CreateCanvas()
@@ -1208,7 +1319,7 @@ func (wb *WindowBase) calculateTextSizeImplForWidth(text string, width int) Size
}
defer win.ReleaseDC(wb.hWnd, hdc)
- hFontOld := win.SelectObject(hdc, win.HGDIOBJ(font.handleForDPI(0)))
+ hFontOld := win.SelectObject(hdc, win.HGDIOBJ(font.handleForDPI(dpi)))
defer win.SelectObject(hdc, hFontOld)
lines := strings.Split(text, "\n")
@@ -1237,6 +1348,7 @@ func (wb *WindowBase) calculateTextSizeImplForWidth(text string, width int) Size
wb.calcTextSizeInfoPrev.font.style = font.style
wb.calcTextSizeInfoPrev.text = text
wb.calcTextSizeInfoPrev.size = size
+ wb.calcTextSizeInfoPrev.dpi = dpi
return size
}