341 lines
6.9 KiB
Go
341 lines
6.9 KiB
Go
|
// Copyright 2018 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 (
|
||
|
"syscall"
|
||
|
"unsafe"
|
||
|
|
||
|
"github.com/lxn/win"
|
||
|
)
|
||
|
|
||
|
const staticWindowClass = `\o/ Walk_Static_Class \o/`
|
||
|
|
||
|
var staticWndProcPtr uintptr
|
||
|
|
||
|
func init() {
|
||
|
AppendToWalkInit(func() {
|
||
|
MustRegisterWindowClass(staticWindowClass)
|
||
|
staticWndProcPtr = syscall.NewCallback(staticWndProc)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
type static struct {
|
||
|
WidgetBase
|
||
|
hwndStatic win.HWND
|
||
|
origStaticWndProcPtr uintptr
|
||
|
textAlignment Alignment2D
|
||
|
textColor Color
|
||
|
}
|
||
|
|
||
|
func (s *static) init(widget Widget, parent Container) error {
|
||
|
if err := InitWidget(
|
||
|
widget,
|
||
|
parent,
|
||
|
staticWindowClass,
|
||
|
win.WS_VISIBLE,
|
||
|
win.WS_EX_CONTROLPARENT); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if s.hwndStatic = win.CreateWindowEx(
|
||
|
0,
|
||
|
syscall.StringToUTF16Ptr("static"),
|
||
|
nil,
|
||
|
win.WS_CHILD|win.WS_CLIPSIBLINGS|win.WS_VISIBLE|win.SS_LEFT|win.SS_NOTIFY,
|
||
|
win.CW_USEDEFAULT,
|
||
|
win.CW_USEDEFAULT,
|
||
|
win.CW_USEDEFAULT,
|
||
|
win.CW_USEDEFAULT,
|
||
|
s.hWnd,
|
||
|
0,
|
||
|
0,
|
||
|
nil,
|
||
|
); s.hwndStatic == 0 {
|
||
|
return newError("creating static failed")
|
||
|
}
|
||
|
|
||
|
if err := s.group.toolTip.AddTool(s); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
s.origStaticWndProcPtr = win.SetWindowLongPtr(s.hwndStatic, win.GWLP_WNDPROC, staticWndProcPtr)
|
||
|
if s.origStaticWndProcPtr == 0 {
|
||
|
return lastError("SetWindowLongPtr")
|
||
|
}
|
||
|
|
||
|
s.applyFont(s.Font())
|
||
|
|
||
|
s.SetBackground(nullBrushSingleton)
|
||
|
|
||
|
s.SetAlignment(AlignHNearVCenter)
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *static) Dispose() {
|
||
|
if s.hwndStatic != 0 {
|
||
|
win.DestroyWindow(s.hwndStatic)
|
||
|
s.hwndStatic = 0
|
||
|
}
|
||
|
|
||
|
s.WidgetBase.Dispose()
|
||
|
}
|
||
|
|
||
|
func (s *static) handleForToolTip() win.HWND {
|
||
|
return s.hwndStatic
|
||
|
}
|
||
|
|
||
|
func (s *static) applyEnabled(enabled bool) {
|
||
|
s.WidgetBase.applyEnabled(enabled)
|
||
|
|
||
|
setWindowEnabled(s.hwndStatic, enabled)
|
||
|
}
|
||
|
|
||
|
func (s *static) applyFont(font *Font) {
|
||
|
s.WidgetBase.applyFont(font)
|
||
|
|
||
|
SetWindowFont(s.hwndStatic, font)
|
||
|
}
|
||
|
|
||
|
func (s *static) textAlignment1D() Alignment1D {
|
||
|
switch s.textAlignment {
|
||
|
case AlignHCenterVNear, AlignHCenterVCenter, AlignHCenterVFar:
|
||
|
return AlignCenter
|
||
|
|
||
|
case AlignHFarVNear, AlignHFarVCenter, AlignHFarVFar:
|
||
|
return AlignFar
|
||
|
|
||
|
default:
|
||
|
return AlignNear
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *static) setTextAlignment1D(alignment Alignment1D) error {
|
||
|
var align Alignment2D
|
||
|
|
||
|
switch alignment {
|
||
|
case AlignCenter:
|
||
|
align = AlignHCenterVCenter
|
||
|
|
||
|
case AlignFar:
|
||
|
align = AlignHFarVCenter
|
||
|
|
||
|
default:
|
||
|
align = AlignHNearVCenter
|
||
|
}
|
||
|
|
||
|
return s.setTextAlignment(align)
|
||
|
}
|
||
|
|
||
|
func (s *static) setTextAlignment(alignment Alignment2D) error {
|
||
|
if alignment == s.textAlignment {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
var styleBit uint32
|
||
|
|
||
|
switch alignment {
|
||
|
case AlignHNearVNear, AlignHNearVCenter, AlignHNearVFar:
|
||
|
styleBit |= win.SS_LEFT
|
||
|
|
||
|
case AlignHCenterVNear, AlignHCenterVCenter, AlignHCenterVFar:
|
||
|
styleBit |= win.SS_CENTER
|
||
|
|
||
|
case AlignHFarVNear, AlignHFarVCenter, AlignHFarVFar:
|
||
|
styleBit |= win.SS_RIGHT
|
||
|
}
|
||
|
|
||
|
if err := setAndClearWindowLongBits(s.hwndStatic, win.GWL_STYLE, styleBit, win.SS_LEFT|win.SS_CENTER|win.SS_RIGHT); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
s.textAlignment = alignment
|
||
|
|
||
|
s.Invalidate()
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *static) setText(text string) (changed bool, err error) {
|
||
|
if text == s.text() {
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
if err := s.WidgetBase.setText(text); err != nil {
|
||
|
return false, err
|
||
|
}
|
||
|
|
||
|
if err := setWindowText(s.hwndStatic, text); err != nil {
|
||
|
return false, err
|
||
|
}
|
||
|
|
||
|
s.RequestLayout()
|
||
|
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
func (s *static) TextColor() Color {
|
||
|
return s.textColor
|
||
|
}
|
||
|
|
||
|
func (s *static) SetTextColor(c Color) {
|
||
|
s.textColor = c
|
||
|
|
||
|
s.Invalidate()
|
||
|
}
|
||
|
|
||
|
func (s *static) shrinkable() bool {
|
||
|
if em, ok := s.window.(interface{ EllipsisMode() EllipsisMode }); ok {
|
||
|
return em.EllipsisMode() != EllipsisNone
|
||
|
}
|
||
|
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (s *static) updateStaticBounds() {
|
||
|
var format DrawTextFormat
|
||
|
|
||
|
switch s.textAlignment {
|
||
|
case AlignHNearVNear, AlignHNearVCenter, AlignHNearVFar:
|
||
|
format |= TextLeft
|
||
|
|
||
|
case AlignHCenterVNear, AlignHCenterVCenter, AlignHCenterVFar:
|
||
|
format |= TextCenter
|
||
|
|
||
|
case AlignHFarVNear, AlignHFarVCenter, AlignHFarVFar:
|
||
|
format |= TextRight
|
||
|
}
|
||
|
|
||
|
switch s.textAlignment {
|
||
|
case AlignHNearVNear, AlignHCenterVNear, AlignHFarVNear:
|
||
|
format |= TextTop
|
||
|
|
||
|
case AlignHNearVCenter, AlignHCenterVCenter, AlignHFarVCenter:
|
||
|
format |= TextVCenter
|
||
|
|
||
|
case AlignHNearVFar, AlignHCenterVFar, AlignHFarVFar:
|
||
|
format |= TextBottom
|
||
|
}
|
||
|
|
||
|
cb := s.ClientBoundsPixels()
|
||
|
|
||
|
if shrinkable := s.shrinkable(); shrinkable || format&TextVCenter != 0 || format&TextBottom != 0 {
|
||
|
var size Size
|
||
|
if _, ok := s.window.(HeightForWidther); ok {
|
||
|
size = s.calculateTextSizeForWidth(cb.Width)
|
||
|
} else {
|
||
|
size = s.calculateTextSize()
|
||
|
}
|
||
|
|
||
|
if shrinkable {
|
||
|
var text string
|
||
|
if size.Width > cb.Width {
|
||
|
text = s.text()
|
||
|
}
|
||
|
s.SetToolTipText(text)
|
||
|
}
|
||
|
|
||
|
if format&TextVCenter != 0 || format&TextBottom != 0 {
|
||
|
if format&TextVCenter != 0 {
|
||
|
cb.Y += (cb.Height - size.Height) / 2
|
||
|
} else {
|
||
|
cb.Y += cb.Height - size.Height
|
||
|
}
|
||
|
|
||
|
cb.Height = size.Height
|
||
|
}
|
||
|
}
|
||
|
|
||
|
win.MoveWindow(s.hwndStatic, int32(cb.X), int32(cb.Y), int32(cb.Width), int32(cb.Height), true)
|
||
|
|
||
|
s.Invalidate()
|
||
|
}
|
||
|
|
||
|
func (s *static) WndProc(hwnd win.HWND, msg uint32, wp, lp uintptr) uintptr {
|
||
|
switch msg {
|
||
|
case win.WM_CTLCOLORSTATIC:
|
||
|
if hBrush := s.handleWMCTLCOLOR(wp, uintptr(s.hWnd)); hBrush != 0 {
|
||
|
return hBrush
|
||
|
}
|
||
|
|
||
|
case win.WM_WINDOWPOSCHANGED:
|
||
|
wp := (*win.WINDOWPOS)(unsafe.Pointer(lp))
|
||
|
|
||
|
if wp.Flags&win.SWP_NOSIZE != 0 {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
s.updateStaticBounds()
|
||
|
}
|
||
|
|
||
|
return s.WidgetBase.WndProc(hwnd, msg, wp, lp)
|
||
|
}
|
||
|
|
||
|
func staticWndProc(hwnd win.HWND, msg uint32, wp, lp uintptr) uintptr {
|
||
|
as, ok := windowFromHandle(win.GetParent(hwnd)).(interface{ asStatic() *static })
|
||
|
if !ok {
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
s := as.asStatic()
|
||
|
|
||
|
switch msg {
|
||
|
case win.WM_NCHITTEST:
|
||
|
return win.HTCLIENT
|
||
|
|
||
|
case win.WM_MOUSEMOVE, win.WM_LBUTTONDOWN, win.WM_LBUTTONUP, win.WM_MBUTTONDOWN, win.WM_MBUTTONUP, win.WM_RBUTTONDOWN, win.WM_RBUTTONUP:
|
||
|
m := win.MSG{
|
||
|
HWnd: hwnd,
|
||
|
Message: msg,
|
||
|
WParam: wp,
|
||
|
LParam: lp,
|
||
|
Pt: win.POINT{int32(win.GET_X_LPARAM(lp)), int32(win.GET_Y_LPARAM(lp))},
|
||
|
}
|
||
|
|
||
|
return s.group.toolTip.SendMessage(win.TTM_RELAYEVENT, 0, uintptr(unsafe.Pointer(&m)))
|
||
|
}
|
||
|
|
||
|
return win.CallWindowProc(s.origStaticWndProcPtr, hwnd, msg, wp, lp)
|
||
|
}
|
||
|
|
||
|
func (s *static) CreateLayoutItem(ctx *LayoutContext) LayoutItem {
|
||
|
var layoutFlags LayoutFlags
|
||
|
if s.textAlignment1D() != AlignNear {
|
||
|
layoutFlags = GrowableHorz
|
||
|
} else if s.shrinkable() {
|
||
|
layoutFlags = ShrinkableHorz
|
||
|
}
|
||
|
|
||
|
return &staticLayoutItem{
|
||
|
layoutFlags: layoutFlags,
|
||
|
idealSize: s.calculateTextSize(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type staticLayoutItem struct {
|
||
|
LayoutItemBase
|
||
|
layoutFlags LayoutFlags
|
||
|
idealSize Size // in native pixels
|
||
|
}
|
||
|
|
||
|
func (li *staticLayoutItem) LayoutFlags() LayoutFlags {
|
||
|
return li.layoutFlags
|
||
|
}
|
||
|
|
||
|
func (li *staticLayoutItem) IdealSize() Size {
|
||
|
return li.idealSize
|
||
|
}
|
||
|
|
||
|
func (li *staticLayoutItem) MinSize() Size {
|
||
|
if li.layoutFlags&ShrinkableHorz != 0 {
|
||
|
return Size{Height: li.idealSize.Height}
|
||
|
}
|
||
|
|
||
|
return li.idealSize
|
||
|
}
|