diff options
-rw-r--r-- | boxlayout.go | 32 | ||||
-rw-r--r-- | button.go | 9 | ||||
-rw-r--r-- | canvas.go | 10 | ||||
-rw-r--r-- | checkbox.go | 34 | ||||
-rw-r--r-- | combobox.go | 4 | ||||
-rw-r--r-- | container.go | 5 | ||||
-rw-r--r-- | flowlayout.go | 55 | ||||
-rw-r--r-- | form.go | 14 | ||||
-rw-r--r-- | gridlayout.go | 67 | ||||
-rw-r--r-- | lineedit.go | 3 | ||||
-rw-r--r-- | pushbutton.go | 12 | ||||
-rw-r--r-- | radiobutton.go | 11 | ||||
-rw-r--r-- | tableview.go | 2 | ||||
-rw-r--r-- | toolbar.go | 14 | ||||
-rw-r--r-- | widget.go | 14 | ||||
-rw-r--r-- | window.go | 130 |
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 { @@ -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 } @@ -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 @@ -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) @@ -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)") } @@ -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) } } @@ -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 } |