5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 22:31:06 +08:00

Dark theme menus for windows

This commit is contained in:
Lea Anthony 2025-02-09 21:59:50 +11:00
parent 8a3d7eb12a
commit 2faf8533e0
No known key found for this signature in database
GPG Key ID: 33DAF7BB90A58405
9 changed files with 395 additions and 243 deletions

View File

@ -247,8 +247,6 @@ func (m *windowsApp) wndProc(hwnd w32.HWND, msg uint32, wParam, lParam uintptr)
return true;
}
*/
case w32.WM_UAHDRAWMENU:
return w32.UAHDrawMenu(hwnd, wParam, lParam)
case w32.WM_SETTINGCHANGE:
settingChanged := w32.UTF16PtrToString((*uint16)(unsafe.Pointer(lParam)))
if settingChanged == "ImmersiveColorSet" {

View File

@ -324,6 +324,7 @@ type ThemeSettings struct {
LightModeTitleTextInactive int32
LightModeBorder int32
LightModeBorderInactive int32
DarkModeMenuBar int32
}
/****** Mac Options *******/

View File

@ -1056,6 +1056,7 @@ func (w *windowsWebviewWindow) updateTheme(isDarkMode bool) {
customTheme := w.parent.options.Windows.CustomTheme
// Custom theme
if w32.SupportsCustomThemes() && customTheme != nil {
w32.InitDarkMode(customTheme.DarkModeMenuBar)
if w.isActive() {
if isDarkMode {
w32.SetTitleBarColour(w.hwnd, customTheme.DarkModeTitleBar)
@ -1087,6 +1088,12 @@ func (w *windowsWebviewWindow) isActive() bool {
var resizePending int32
func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
processed, code := w32.MenuBarWndProc(w.hwnd, msg, wparam, lparam)
if processed {
return code
}
switch msg {
case w32.WM_ACTIVATE:
if int(wparam&0xffff) == w32.WA_INACTIVE {
@ -1404,11 +1411,11 @@ func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintp
}
w.setPadding(edge.Rect{})
} else {
// This is needed to workaround the resize flickering in frameless mode with WindowDecorations
// This is needed to work around the resize flickering in frameless mode with WindowDecorations
// See: https://stackoverflow.com/a/6558508
// The workaround from the SO answer suggests to reduce the bottom of the window by 1px.
// However this would result in loosing 1px of the WebView content.
// Increasing the bottom also worksaround the flickering but we would loose 1px of the WebView content
// However, this would result in losing 1px of the WebView content.
// Increasing the bottom also worksaround the flickering, but we would lose 1px of the WebView content
// therefore let's pad the content with 1px at the bottom.
rgrc.Bottom += 1
w.setPadding(edge.Rect{Bottom: 1})

View File

@ -608,6 +608,7 @@ const (
WM_DPICHANGED = 0x02E0
WM_UAHDRAWMENU = 0x0091
WM_UAHDRAWMENUITEM = 0x0092
WM_UAHMEASUREMENUITEM = 0x012E
)
const (

View File

@ -1,11 +1,121 @@
package w32
import "unsafe"
import (
"fmt"
"unsafe"
)
const (
OBJID_MENU = 3
OBJID_MENU = -3
)
var (
menuTheme HTHEME
)
type DTTOPTS struct {
DwSize uint32
DwFlags uint32
CrText uint32
CrBorder uint32
CrShadow uint32
ITextShadowType int32
PtShadowOffset POINT
iBorderSize int32
iFontPropId int32
IColorPropId int32
IStateId int32
FApplyOverlay int32
IGlowSize int32
PfnDrawTextCallback uintptr
LParam uintptr
}
const (
MENU_POPUPITEM = 14
DTT_TEXTCOLOR = 1
)
// Menu item states
const (
ODS_SELECTED = 0x0001
ODS_GRAYED = 0x0002
ODS_DISABLED = 0x0004
ODS_CHECKED = 0x0008
ODS_FOCUS = 0x0010
ODS_DEFAULT = 0x0020
ODS_HOTLIGHT = 0x0040
ODS_INACTIVE = 0x0080
ODS_NOACCEL = 0x0100
ODS_NOFOCUSRECT = 0x0200
)
// Menu Button Image states
const (
MBI_NORMAL = 1
MBI_HOT = 2
MBI_PUSHED = 3
MBI_DISABLED = 4
)
var (
procGetMenuItemInfo = moduser32.NewProc("GetMenuItemInfoW")
)
func GetMenuItemInfo(hmenu HMENU, item uint32, fByPosition bool, lpmii *MENUITEMINFO) bool {
ret, _, _ := procGetMenuItemInfo.Call(
uintptr(hmenu),
uintptr(item),
uintptr(boolToUint(fByPosition)),
uintptr(unsafe.Pointer(lpmii)),
)
return ret != 0
}
// Helper function to convert bool to uint
func boolToUint(b bool) uint {
if b {
return 1
}
return 0
}
// Add these function declarations
func OpenThemeData(hwnd HWND, pszClassList *uint16) HTHEME {
ret, _, _ := procOpenThemeData.Call(
uintptr(hwnd),
uintptr(unsafe.Pointer(pszClassList)),
)
return HTHEME(ret)
}
func DrawThemeBackground(hTheme HTHEME, hdc HDC, iPartId, iStateId int32, pRect *RECT, pClipRect *RECT) HRESULT {
ret, _, _ := procDrawThemeBackground.Call(
uintptr(hTheme),
uintptr(hdc),
uintptr(iPartId),
uintptr(iStateId),
uintptr(unsafe.Pointer(pRect)),
uintptr(unsafe.Pointer(pClipRect)),
)
return HRESULT(ret)
}
func DrawThemeTextEx(hTheme HTHEME, hdc HDC, iPartId, iStateId int32, text *uint16, textLen int32, dwFlags uint32, pRect *RECT, pOptions *DTTOPTS) HRESULT {
ret, _, _ := procDrawThemeTextEx.Call(
uintptr(hTheme),
uintptr(hdc),
uintptr(iPartId),
uintptr(iStateId),
uintptr(unsafe.Pointer(text)),
uintptr(textLen),
uintptr(dwFlags),
uintptr(unsafe.Pointer(pRect)),
uintptr(unsafe.Pointer(pOptions)),
)
return HRESULT(ret)
}
type UAHMENU struct {
Hmenu HMENU
Hdc HDC
@ -56,6 +166,21 @@ func (u *UAHMENUITEMMETRICS) RgsizePopup() *[4]struct{ cx, cy uint32 } {
return (*[4]struct{ cx, cy uint32 })(unsafe.Pointer(&u.data))
}
type UAHMEASUREMENUITEM struct {
UM UAHMENU
UAMI UAHMENUITEM
Mis MEASUREITEMSTRUCT
}
type MEASUREITEMSTRUCT struct {
CtlType uint32
CtlID uint32
ItemID uint32
ItemWidth uint32
ItemHeight uint32
ItemData uintptr
}
type UAHMENUPOPUPMETRICS struct {
Rgcx [4]uint32 // Array of 4 DWORDs
FUpdateMaxWidths uint32 // Bit-field represented as a uint32
@ -75,7 +200,13 @@ var darkModeTitleBarBrush HBRUSH
func init() {
darkModeTitleBarBrush, _, _ = procCreateSolidBrush.Call(
uintptr(0x8080FF),
uintptr(0x00262525),
)
}
func InitDarkMode(darkModeMenuBarColour int32) {
darkModeTitleBarBrush, _, _ = procCreateSolidBrush.Call(
uintptr(darkModeMenuBarColour),
)
}
@ -86,18 +217,36 @@ func CreateSolidBrush(color COLORREF) HBRUSH {
return HBRUSH(ret)
}
func UAHDrawMenu(hwnd HWND, wParam uintptr, lParam uintptr) uintptr {
func RGB(r, g, b byte) uint32 {
return uint32(r) | uint32(g)<<8 | uint32(b)<<16
}
func TrackPopupMenu(hmenu HMENU, flags uint32, x, y int32, reserved int32, hwnd HWND, prcRect *RECT) bool {
ret, _, _ := procTrackPopupMenu.Call(
uintptr(hmenu),
uintptr(flags),
uintptr(x),
uintptr(y),
uintptr(reserved),
uintptr(hwnd),
uintptr(unsafe.Pointer(prcRect)),
)
return ret != 0
}
func MenuBarWndProc(hwnd HWND, msg uint32, wParam WPARAM, lParam LPARAM) (bool, LRESULT) {
if !IsCurrentlyDarkMode() {
return 0
return false, 0
}
switch msg {
case WM_UAHDRAWMENU:
udm := (*UAHMENU)(unsafe.Pointer(lParam))
var rc RECT
// get the menubar rect
{
menuBarInfo, err := GetMenuBarInfo(hwnd, OBJID_MENU, 0)
if err != nil {
return 0
return false, 0
}
winRect := GetWindowRect(hwnd)
@ -105,88 +254,210 @@ func UAHDrawMenu(hwnd HWND, wParam uintptr, lParam uintptr) uintptr {
// the rcBar is offset by the window rect
rc = menuBarInfo.Bar
OffsetRect(&rc, int(-winRect.Left), int(-winRect.Top))
}
FillRect(udm.Hdc, &rc, darkModeTitleBarBrush)
return 1
return true, 0
case WM_UAHDRAWMENUITEM:
udmi := (*UAHDRAWMENUITEM)(unsafe.Pointer(lParam))
// Create buffer for menu text
menuString := make([]uint16, 256)
// Setup menu item info structure
mii := MENUITEMINFO{
CbSize: uint32(unsafe.Sizeof(MENUITEMINFO{})),
FMask: MIIM_STRING,
DwTypeData: &menuString[0],
Cch: uint32(len(menuString) - 1),
}
GetMenuItemInfo(udmi.UM.Hmenu, uint32(udmi.UAMI.Position), true, &mii)
if udmi.DIS.ItemState&ODS_HOTLIGHT != 0 && mii.HSubMenu != 0 {
// If this is a menu item with a submenu and we're hovering,
// tell the menu to track
TrackPopupMenu(mii.HSubMenu,
TPM_LEFTALIGN|TPM_TOPALIGN,
int32(udmi.DIS.RcItem.Left),
int32(udmi.DIS.RcItem.Bottom),
0, hwnd, nil)
}
dwFlags := uint32(DT_CENTER | DT_SINGLELINE | DT_VCENTER)
// Use different colors for menubar vs popup items
var bgBrush HBRUSH
var textColor uint32
if udmi.DIS.ItemState&ODS_HOTLIGHT != 0 {
// Hot state - use a specific color for hover
bgBrush = CreateSolidBrush(RGB(45, 45, 45)) // Dark gray for hover
textColor = RGB(255, 255, 255) // White text
} else if udmi.DIS.ItemState&ODS_SELECTED != 0 {
// Selected state
bgBrush = CreateSolidBrush(RGB(60, 60, 60)) // Slightly lighter for selected
textColor = RGB(255, 255, 255)
} else {
// Normal state
bgBrush = darkModeTitleBarBrush
textColor = RGB(255, 255, 255)
}
// Fill background
FillRect(udmi.UM.Hdc, &udmi.DIS.RcItem, bgBrush)
// Delete the temporary brush if we created one
if bgBrush != darkModeTitleBarBrush {
DeleteObject(bgBrush)
}
// Draw text
SetTextColor(udmi.UM.Hdc, textColor)
SetBkMode(udmi.UM.Hdc, TRANSPARENT)
DrawText(udmi.UM.Hdc, menuString, -1, &udmi.DIS.RcItem, dwFlags)
return true, 1
case WM_UAHMEASUREMENUITEM:
// Cast lParam to UAHMEASUREMENUITEM pointer
mmi := (*UAHMEASUREMENUITEM)(unsafe.Pointer(lParam))
// Let the default window procedure handle the basic measurement
result := DefWindowProc(hwnd, msg, wParam, lParam)
// Modify the width to be 1/3rd wider
mmi.Mis.ItemWidth = (mmi.Mis.ItemWidth * 4) / 3
return true, result
case WM_NCPAINT, WM_NCACTIVATE:
result := DefWindowProc(hwnd, msg, wParam, lParam)
_, err := GetMenuBarInfo(hwnd, OBJID_MENU, 0)
if err != nil {
return false, 0
}
clientRect := GetClientRect(hwnd)
points := []POINT{
{
X: clientRect.Left,
Y: clientRect.Top,
},
{
X: clientRect.Right,
Y: clientRect.Bottom,
},
}
MapWindowPoints(hwnd, 0, uintptr(unsafe.Pointer(&points[0])), 2)
clientRect.Left = points[0].X
clientRect.Top = points[0].Y
clientRect.Right = points[1].X
clientRect.Bottom = points[1].Y
winRect := GetWindowRect(hwnd)
OffsetRect(clientRect, int(-winRect.Left), int(-winRect.Top))
line := *clientRect
line.Bottom = line.Top
line.Top = line.Top - 1
hdc := GetWindowDC(hwnd)
FillRect(hdc, &line, darkModeTitleBarBrush)
ReleaseDC(hwnd, hdc)
return true, result
}
return false, 0
}
func MapWindowPoints(hWndFrom HWND, hWndTo HWND, points uintptr, numPoints uint32) {
// Call the MapWindowPoints function
ret, _, _ := procMapWindowPoints.Call(
uintptr(hWndFrom),
uintptr(hWndTo),
points,
uintptr(numPoints),
)
// Check for errors
if ret == 0 {
fmt.Println("MapWindowPoints failed")
}
}
func UAHDrawMenuItem(hwnd HWND, wParam uintptr, lParam uintptr) uintptr {
pUDMI := (*UAHDRAWMENUITEM)(unsafe.Pointer(lParam))
var pbrBackground, pbrBorder *HBRUSH
pbrBackground = &brItemBackground
pbrBorder = &brItemBackground
// get the menu item string
menuString := make([]uint16, 256)
mii := MENUITEMINFO{
CbSize: uint32(unsafe.Sizeof(mii)),
FMask: MIIM_STRING,
DwTypeData: uintptr(unsafe.Pointer(&menuString[0])),
Cch: uint32(len(menuString) - 1),
}
GetMenuItemInfo(pUDMI.um.hmenu, pUDMI.umi.iPosition, true, &mii)
// get the item state for drawing
dwFlags := DT_CENTER | DT_SINGLELINE | DT_VCENTER
iTextStateID := 0
iBackgroundStateID := 0
switch {
case pUDMI.dis.itemState&ODS_INACTIVE != 0 || pUDMI.dis.itemState&ODS_DEFAULT != 0:
// normal display
iTextStateID = MBI_NORMAL
iBackgroundStateID = MBI_NORMAL
case pUDMI.dis.itemState&ODS_HOTLIGHT != 0:
// hot tracking
iTextStateID = MBI_HOT
iBackgroundStateID = MBI_HOT
pbrBackground = &brItemBackgroundHot
pbrBorder = &brItemBorder
case pUDMI.dis.itemState&ODS_SELECTED != 0:
// clicked
iTextStateID = MBI_PUSHED
iBackgroundStateID = MBI_PUSHED
pbrBackground = &brItemBackgroundSelected
pbrBorder = &brItemBorder
case pUDMI.dis.itemState&ODS_GRAYED != 0 || pUDMI.dis.itemState&ODS_DISABLED != 0:
// disabled / grey text
iTextStateID = MBI_DISABLED
iBackgroundStateID = MBI_DISABLED
case pUDMI.dis.itemState&ODS_NOACCEL != 0:
// hide prefix
dwFlags |= DT_HIDEPREFIX
}
if g_menuTheme == 0 {
g_menuTheme = OpenThemeData(hwnd, "Menu")
}
opts := DTTOPTS{
DtSize: uint32(unsafe.Sizeof(opts)),
DwFlags: DTT_TEXTCOLOR,
CrText: RGB(0x00, 0x00, 0x20),
ITextState: iTextStateID,
}
if iTextStateID == MBI_DISABLED {
opts.CrText = RGB(0x40, 0x40, 0x40)
}
FillRect(pUDMI.um.hdc, &pUDMI.dis.rcItem, *pbrBackground)
FrameRect(pUDMI.um.hdc, &pUDMI.dis.rcItem, *pbrBorder)
DrawThemeTextEx(g_menuTheme, pUDMI.um.hdc, MENU_BARITEM, MBI_NORMAL, uintptr(unsafe.Pointer(&menuString[0])), mii.cch, dwFlags, &pUDMI.dis.rcItem, &opts)
return 1
//pUDMI := (*UAHDRAWMENUITEM)(unsafe.Pointer(lParam))
//
//var pbrBackground, pbrBorder *HBRUSH
//pbrBackground = &brItemBackground
//pbrBorder = &brItemBackground
//
//// get the menu item string
//menuString := make([]uint16, 256)
//mii := MENUITEMINFO{
// CbSize: uint32(unsafe.Sizeof(mii)),
// FMask: MIIM_STRING,
// DwTypeData: uintptr(unsafe.Pointer(&menuString[0])),
// Cch: uint32(len(menuString) - 1),
//}
//GetMenuItemInfo(pUDMI.um.hmenu, pUDMI.umi.iPosition, true, &mii)
//
//// get the item state for drawing
//dwFlags := DT_CENTER | DT_SINGLELINE | DT_VCENTER
//
//iTextStateID := 0
//iBackgroundStateID := 0
//switch {
//case pUDMI.dis.itemState&ODS_INACTIVE != 0 || pUDMI.dis.itemState&ODS_DEFAULT != 0:
// // normal display
// iTextStateID = MBI_NORMAL
// iBackgroundStateID = MBI_NORMAL
//case pUDMI.dis.itemState&ODS_HOTLIGHT != 0:
// // hot tracking
// iTextStateID = MBI_HOT
// iBackgroundStateID = MBI_HOT
//
// pbrBackground = &brItemBackgroundHot
// pbrBorder = &brItemBorder
//case pUDMI.dis.itemState&ODS_SELECTED != 0:
// // clicked
// iTextStateID = MBI_PUSHED
// iBackgroundStateID = MBI_PUSHED
//
// pbrBackground = &brItemBackgroundSelected
// pbrBorder = &brItemBorder
//case pUDMI.dis.itemState&ODS_GRAYED != 0 || pUDMI.dis.itemState&ODS_DISABLED != 0:
// // disabled / grey text
// iTextStateID = MBI_DISABLED
// iBackgroundStateID = MBI_DISABLED
//case pUDMI.dis.itemState&ODS_NOACCEL != 0:
// // hide prefix
// dwFlags |= DT_HIDEPREFIX
//}
//
//if g_menuTheme == 0 {
// g_menuTheme = OpenThemeData(hwnd, "Menu")
//}
//
//opts := DTTOPTS{
// DtSize: uint32(unsafe.Sizeof(opts)),
// DwFlags: DTT_TEXTCOLOR,
// CrText: RGB(0x00, 0x00, 0x20),
// ITextState: iTextStateID,
//}
//if iTextStateID == MBI_DISABLED {
// opts.CrText = RGB(0x40, 0x40, 0x40)
//}
//
//FillRect(pUDMI.um.hdc, &pUDMI.dis.rcItem, *pbrBackground)
//FrameRect(pUDMI.um.hdc, &pUDMI.dis.rcItem, *pbrBorder)
//DrawThemeTextEx(g_menuTheme, pUDMI.um.hdc, MENU_BARITEM, MBI_NORMAL, uintptr(unsafe.Pointer(&menuString[0])), mii.cch, dwFlags, &pUDMI.dis.rcItem, &opts)
//return 1
}
func GetMenuBarInfo(hwnd HWND, idObject uint32, idItem uint32) (*MENUBARINFO, error) {
func GetMenuBarInfo(hwnd HWND, idObject int32, idItem uint32) (*MENUBARINFO, error) {
var mi MENUBARINFO
mi.CbSize = uint32(unsafe.Sizeof(&mi))
mi.CbSize = uint32(unsafe.Sizeof(mi))
ret, _, err := procGetMenuBarInfo.Call(
hwnd,
uintptr(idObject),

View File

@ -24,6 +24,8 @@ const HCF_HIGHCONTRASTON = 0x00000001
type WINDOWCOMPOSITIONATTRIB DWORD
type HTHEME HANDLE
const (
WCA_UNDEFINED WINDOWCOMPOSITIONATTRIB = 0
WCA_NCRENDERING_ENABLED WINDOWCOMPOSITIONATTRIB = 1
@ -67,7 +69,10 @@ type WINDOWCOMPOSITIONATTRIBDATA struct {
var (
uxtheme = syscall.NewLazyDLL("uxtheme.dll")
procSetWindowTheme = uxtheme.NewProc("SetWindowTheme")
procOpenThemeData = uxtheme.NewProc("OpenThemeData")
procDrawThemeBackground = uxtheme.NewProc("DrawThemeBackground")
procAllowDarkModeForApplication = uxtheme.NewProc("AllowDarkModeForApp")
procDrawThemeTextEx = uxtheme.NewProc("DrawThemeTextEx")
)
type PreferredAppMode = int32
@ -206,7 +211,7 @@ func SetMenuTheme(hwnd uintptr, useDarkMode bool) {
// Set the window theme
themeName := "Explorer"
if useDarkMode {
themeName = "DarkMode"
themeName = "DarkMode_Explorer"
}
procSetWindowTheme.Call(HWND(hwnd), uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(themeName))), 0)

View File

@ -241,6 +241,13 @@ func (r *RECT) String() string {
return fmt.Sprintf("RECT (%p): Left: %d, Top: %d, Right: %d, Bottom: %d", r, r.Left, r.Top, r.Right, r.Bottom)
}
func RectToPoints(rect *RECT) []POINT {
return []POINT{
{X: rect.Left, Y: rect.Top}, // Top-left
{X: rect.Right, Y: rect.Bottom}, // Bottom-right
}
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms633577.aspx
type WNDCLASSEX struct {
Size uint32

View File

@ -162,12 +162,17 @@ var (
procAppendMenu = moduser32.NewProc("AppendMenuW")
procSetMenuItemInfo = moduser32.NewProc("SetMenuItemInfoW")
procDrawMenuBar = moduser32.NewProc("DrawMenuBar")
procTrackPopupMenu = moduser32.NewProc("TrackPopupMenu")
procTrackPopupMenuEx = moduser32.NewProc("TrackPopupMenuEx")
procGetKeyState = moduser32.NewProc("GetKeyState")
procGetSysColorBrush = moduser32.NewProc("GetSysColorBrush")
procGetSysColor = moduser32.NewProc("GetSysColor")
procMapWindowPoints = moduser32.NewProc("MapWindowPoints")
procGetWindowPlacement = moduser32.NewProc("GetWindowPlacement")
procSetWindowPlacement = moduser32.NewProc("SetWindowPlacement")
procGetWindowDC = moduser32.NewProc("GetWindowDC")
procGetScrollInfo = moduser32.NewProc("GetScrollInfo")
procSetScrollInfo = moduser32.NewProc("SetScrollInfo")
@ -187,6 +192,11 @@ func init() {
mainThread = GetCurrentThreadId()
}
func GetWindowDC(hwnd HWND) HDC {
ret, _, _ := procGetWindowDC.Call(hwnd)
return ret
}
func GET_X_LPARAM(lp uintptr) int32 {
return int32(int16(LOWORD(uint32(lp))))
}
@ -1058,10 +1068,14 @@ func FillRect(hDC HDC, lprc *RECT, hbr HBRUSH) bool {
return ret != 0
}
func DrawText(hDC HDC, text string, uCount int, lpRect *RECT, uFormat uint) int {
func DrawText(hDC HDC, text []uint16, uCount int, lpRect *RECT, uFormat uint32) int {
// Convert the string to a UTF16 pointer
// This is necessary because the DrawText function expects a UTF16 pointer
ret, _, _ := procDrawText.Call(
uintptr(hDC),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))),
uintptr(unsafe.Pointer(&text[0])),
uintptr(uCount),
uintptr(unsafe.Pointer(lpRect)),
uintptr(uFormat))

View File

@ -1,152 +0,0 @@
//go:build windows
/*
* Copyright (C) 2019 Tad Vizbaras. All Rights Reserved.
* Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved.
*/
package w32
import (
"syscall"
"unsafe"
)
// LISTVIEW parts
const (
LVP_LISTITEM = 1
LVP_LISTGROUP = 2
LVP_LISTDETAIL = 3
LVP_LISTSORTEDDETAIL = 4
LVP_EMPTYTEXT = 5
LVP_GROUPHEADER = 6
LVP_GROUPHEADERLINE = 7
LVP_EXPANDBUTTON = 8
LVP_COLLAPSEBUTTON = 9
LVP_COLUMNDETAIL = 10
)
// LVP_LISTITEM states
const (
LISS_NORMAL = 1
LISS_HOT = 2
LISS_SELECTED = 3
LISS_DISABLED = 4
LISS_SELECTEDNOTFOCUS = 5
LISS_HOTSELECTED = 6
)
// TREEVIEW parts
const (
TVP_TREEITEM = 1
TVP_GLYPH = 2
TVP_BRANCH = 3
TVP_HOTGLYPH = 4
)
// TVP_TREEITEM states
const (
TREIS_NORMAL = 1
TREIS_HOT = 2
TREIS_SELECTED = 3
TREIS_DISABLED = 4
TREIS_SELECTEDNOTFOCUS = 5
TREIS_HOTSELECTED = 6
)
type HTHEME HANDLE
var (
// Library
libuxtheme uintptr
// Functions
closeThemeData uintptr
drawThemeBackground uintptr
drawThemeText uintptr
getThemeTextExtent uintptr
openThemeData uintptr
setWindowTheme uintptr
)
func init() {
// Library
libuxtheme = MustLoadLibrary("uxtheme.dll")
// Functions
closeThemeData = MustGetProcAddress(libuxtheme, "CloseThemeData")
drawThemeBackground = MustGetProcAddress(libuxtheme, "DrawThemeBackground")
drawThemeText = MustGetProcAddress(libuxtheme, "DrawThemeText")
getThemeTextExtent = MustGetProcAddress(libuxtheme, "GetThemeTextExtent")
openThemeData = MustGetProcAddress(libuxtheme, "OpenThemeData")
setWindowTheme = MustGetProcAddress(libuxtheme, "SetWindowTheme")
}
func CloseThemeData(hTheme HTHEME) HRESULT {
ret, _, _ := syscall.SyscallN(closeThemeData,
uintptr(hTheme),
0,
0)
return HRESULT(ret)
}
func DrawThemeBackground(hTheme HTHEME, hdc HDC, iPartId, iStateId int32, pRect, pClipRect *RECT) HRESULT {
ret, _, _ := syscall.Syscall6(drawThemeBackground, 6,
uintptr(hTheme),
uintptr(hdc),
uintptr(iPartId),
uintptr(iStateId),
uintptr(unsafe.Pointer(pRect)),
uintptr(unsafe.Pointer(pClipRect)))
return HRESULT(ret)
}
func DrawThemeText(hTheme HTHEME, hdc HDC, iPartId, iStateId int32, pszText *uint16, iCharCount int32, dwTextFlags, dwTextFlags2 uint32, pRect *RECT) HRESULT {
ret, _, _ := syscall.Syscall9(drawThemeText, 9,
uintptr(hTheme),
uintptr(hdc),
uintptr(iPartId),
uintptr(iStateId),
uintptr(unsafe.Pointer(pszText)),
uintptr(iCharCount),
uintptr(dwTextFlags),
uintptr(dwTextFlags2),
uintptr(unsafe.Pointer(pRect)))
return HRESULT(ret)
}
func GetThemeTextExtent(hTheme HTHEME, hdc HDC, iPartId, iStateId int32, pszText *uint16, iCharCount int32, dwTextFlags uint32, pBoundingRect, pExtentRect *RECT) HRESULT {
ret, _, _ := syscall.Syscall9(getThemeTextExtent, 9,
uintptr(hTheme),
uintptr(hdc),
uintptr(iPartId),
uintptr(iStateId),
uintptr(unsafe.Pointer(pszText)),
uintptr(iCharCount),
uintptr(dwTextFlags),
uintptr(unsafe.Pointer(pBoundingRect)),
uintptr(unsafe.Pointer(pExtentRect)))
return HRESULT(ret)
}
func OpenThemeData(hwnd HWND, pszClassList *uint16) HTHEME {
ret, _, _ := syscall.SyscallN(openThemeData,
uintptr(hwnd),
uintptr(unsafe.Pointer(pszClassList)),
0)
return HTHEME(ret)
}
func SetWindowTheme(hwnd HWND, pszSubAppName, pszSubIdList *uint16) HRESULT {
ret, _, _ := syscall.SyscallN(setWindowTheme,
uintptr(hwnd),
uintptr(unsafe.Pointer(pszSubAppName)),
uintptr(unsafe.Pointer(pszSubIdList)))
return HRESULT(ret)
}