mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 22:13:36 +08:00
Dark theme menus for windows
This commit is contained in:
parent
8a3d7eb12a
commit
2faf8533e0
@ -247,8 +247,6 @@ func (m *windowsApp) wndProc(hwnd w32.HWND, msg uint32, wParam, lParam uintptr)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
case w32.WM_UAHDRAWMENU:
|
|
||||||
return w32.UAHDrawMenu(hwnd, wParam, lParam)
|
|
||||||
case w32.WM_SETTINGCHANGE:
|
case w32.WM_SETTINGCHANGE:
|
||||||
settingChanged := w32.UTF16PtrToString((*uint16)(unsafe.Pointer(lParam)))
|
settingChanged := w32.UTF16PtrToString((*uint16)(unsafe.Pointer(lParam)))
|
||||||
if settingChanged == "ImmersiveColorSet" {
|
if settingChanged == "ImmersiveColorSet" {
|
||||||
|
@ -324,6 +324,7 @@ type ThemeSettings struct {
|
|||||||
LightModeTitleTextInactive int32
|
LightModeTitleTextInactive int32
|
||||||
LightModeBorder int32
|
LightModeBorder int32
|
||||||
LightModeBorderInactive int32
|
LightModeBorderInactive int32
|
||||||
|
DarkModeMenuBar int32
|
||||||
}
|
}
|
||||||
|
|
||||||
/****** Mac Options *******/
|
/****** Mac Options *******/
|
||||||
|
@ -1056,6 +1056,7 @@ func (w *windowsWebviewWindow) updateTheme(isDarkMode bool) {
|
|||||||
customTheme := w.parent.options.Windows.CustomTheme
|
customTheme := w.parent.options.Windows.CustomTheme
|
||||||
// Custom theme
|
// Custom theme
|
||||||
if w32.SupportsCustomThemes() && customTheme != nil {
|
if w32.SupportsCustomThemes() && customTheme != nil {
|
||||||
|
w32.InitDarkMode(customTheme.DarkModeMenuBar)
|
||||||
if w.isActive() {
|
if w.isActive() {
|
||||||
if isDarkMode {
|
if isDarkMode {
|
||||||
w32.SetTitleBarColour(w.hwnd, customTheme.DarkModeTitleBar)
|
w32.SetTitleBarColour(w.hwnd, customTheme.DarkModeTitleBar)
|
||||||
@ -1087,6 +1088,12 @@ func (w *windowsWebviewWindow) isActive() bool {
|
|||||||
var resizePending int32
|
var resizePending int32
|
||||||
|
|
||||||
func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
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 {
|
switch msg {
|
||||||
case w32.WM_ACTIVATE:
|
case w32.WM_ACTIVATE:
|
||||||
if int(wparam&0xffff) == w32.WA_INACTIVE {
|
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{})
|
w.setPadding(edge.Rect{})
|
||||||
} else {
|
} 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
|
// See: https://stackoverflow.com/a/6558508
|
||||||
// The workaround from the SO answer suggests to reduce the bottom of the window by 1px.
|
// 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.
|
// However, this would result in losing 1px of the WebView content.
|
||||||
// Increasing the bottom also worksaround the flickering but we would loose 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.
|
// therefore let's pad the content with 1px at the bottom.
|
||||||
rgrc.Bottom += 1
|
rgrc.Bottom += 1
|
||||||
w.setPadding(edge.Rect{Bottom: 1})
|
w.setPadding(edge.Rect{Bottom: 1})
|
||||||
|
@ -608,6 +608,7 @@ const (
|
|||||||
WM_DPICHANGED = 0x02E0
|
WM_DPICHANGED = 0x02E0
|
||||||
WM_UAHDRAWMENU = 0x0091
|
WM_UAHDRAWMENU = 0x0091
|
||||||
WM_UAHDRAWMENUITEM = 0x0092
|
WM_UAHDRAWMENUITEM = 0x0092
|
||||||
|
WM_UAHMEASUREMENUITEM = 0x012E
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -1,11 +1,121 @@
|
|||||||
package w32
|
package w32
|
||||||
|
|
||||||
import "unsafe"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
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 {
|
type UAHMENU struct {
|
||||||
Hmenu HMENU
|
Hmenu HMENU
|
||||||
Hdc HDC
|
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))
|
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 {
|
type UAHMENUPOPUPMETRICS struct {
|
||||||
Rgcx [4]uint32 // Array of 4 DWORDs
|
Rgcx [4]uint32 // Array of 4 DWORDs
|
||||||
FUpdateMaxWidths uint32 // Bit-field represented as a uint32
|
FUpdateMaxWidths uint32 // Bit-field represented as a uint32
|
||||||
@ -75,7 +200,13 @@ var darkModeTitleBarBrush HBRUSH
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
darkModeTitleBarBrush, _, _ = procCreateSolidBrush.Call(
|
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)
|
return HBRUSH(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UAHDrawMenu(hwnd HWND, wParam uintptr, lParam uintptr) uintptr {
|
func RGB(r, g, b byte) uint32 {
|
||||||
if !IsCurrentlyDarkMode() {
|
return uint32(r) | uint32(g)<<8 | uint32(b)<<16
|
||||||
return 0
|
}
|
||||||
}
|
|
||||||
udm := (*UAHMENU)(unsafe.Pointer(lParam))
|
|
||||||
var rc RECT
|
|
||||||
|
|
||||||
// get the menubar rect
|
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 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)
|
menuBarInfo, err := GetMenuBarInfo(hwnd, OBJID_MENU, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return false, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
winRect := GetWindowRect(hwnd)
|
winRect := GetWindowRect(hwnd)
|
||||||
@ -105,88 +254,210 @@ func UAHDrawMenu(hwnd HWND, wParam uintptr, lParam uintptr) uintptr {
|
|||||||
// the rcBar is offset by the window rect
|
// the rcBar is offset by the window rect
|
||||||
rc = menuBarInfo.Bar
|
rc = menuBarInfo.Bar
|
||||||
OffsetRect(&rc, int(-winRect.Left), int(-winRect.Top))
|
OffsetRect(&rc, int(-winRect.Left), int(-winRect.Top))
|
||||||
|
|
||||||
|
FillRect(udm.Hdc, &rc, darkModeTitleBarBrush)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
FillRect(udm.Hdc, &rc, darkModeTitleBarBrush)
|
func MapWindowPoints(hWndFrom HWND, hWndTo HWND, points uintptr, numPoints uint32) {
|
||||||
|
|
||||||
return 1
|
// 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 {
|
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
|
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
|
var mi MENUBARINFO
|
||||||
|
|
||||||
mi.CbSize = uint32(unsafe.Sizeof(&mi))
|
mi.CbSize = uint32(unsafe.Sizeof(mi))
|
||||||
ret, _, err := procGetMenuBarInfo.Call(
|
ret, _, err := procGetMenuBarInfo.Call(
|
||||||
hwnd,
|
hwnd,
|
||||||
uintptr(idObject),
|
uintptr(idObject),
|
||||||
|
@ -24,6 +24,8 @@ const HCF_HIGHCONTRASTON = 0x00000001
|
|||||||
|
|
||||||
type WINDOWCOMPOSITIONATTRIB DWORD
|
type WINDOWCOMPOSITIONATTRIB DWORD
|
||||||
|
|
||||||
|
type HTHEME HANDLE
|
||||||
|
|
||||||
const (
|
const (
|
||||||
WCA_UNDEFINED WINDOWCOMPOSITIONATTRIB = 0
|
WCA_UNDEFINED WINDOWCOMPOSITIONATTRIB = 0
|
||||||
WCA_NCRENDERING_ENABLED WINDOWCOMPOSITIONATTRIB = 1
|
WCA_NCRENDERING_ENABLED WINDOWCOMPOSITIONATTRIB = 1
|
||||||
@ -67,7 +69,10 @@ type WINDOWCOMPOSITIONATTRIBDATA struct {
|
|||||||
var (
|
var (
|
||||||
uxtheme = syscall.NewLazyDLL("uxtheme.dll")
|
uxtheme = syscall.NewLazyDLL("uxtheme.dll")
|
||||||
procSetWindowTheme = uxtheme.NewProc("SetWindowTheme")
|
procSetWindowTheme = uxtheme.NewProc("SetWindowTheme")
|
||||||
|
procOpenThemeData = uxtheme.NewProc("OpenThemeData")
|
||||||
|
procDrawThemeBackground = uxtheme.NewProc("DrawThemeBackground")
|
||||||
procAllowDarkModeForApplication = uxtheme.NewProc("AllowDarkModeForApp")
|
procAllowDarkModeForApplication = uxtheme.NewProc("AllowDarkModeForApp")
|
||||||
|
procDrawThemeTextEx = uxtheme.NewProc("DrawThemeTextEx")
|
||||||
)
|
)
|
||||||
|
|
||||||
type PreferredAppMode = int32
|
type PreferredAppMode = int32
|
||||||
@ -206,7 +211,7 @@ func SetMenuTheme(hwnd uintptr, useDarkMode bool) {
|
|||||||
// Set the window theme
|
// Set the window theme
|
||||||
themeName := "Explorer"
|
themeName := "Explorer"
|
||||||
if useDarkMode {
|
if useDarkMode {
|
||||||
themeName = "DarkMode"
|
themeName = "DarkMode_Explorer"
|
||||||
}
|
}
|
||||||
procSetWindowTheme.Call(HWND(hwnd), uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(themeName))), 0)
|
procSetWindowTheme.Call(HWND(hwnd), uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(themeName))), 0)
|
||||||
|
|
||||||
|
@ -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)
|
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
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms633577.aspx
|
||||||
type WNDCLASSEX struct {
|
type WNDCLASSEX struct {
|
||||||
Size uint32
|
Size uint32
|
||||||
|
@ -162,12 +162,17 @@ var (
|
|||||||
procAppendMenu = moduser32.NewProc("AppendMenuW")
|
procAppendMenu = moduser32.NewProc("AppendMenuW")
|
||||||
procSetMenuItemInfo = moduser32.NewProc("SetMenuItemInfoW")
|
procSetMenuItemInfo = moduser32.NewProc("SetMenuItemInfoW")
|
||||||
procDrawMenuBar = moduser32.NewProc("DrawMenuBar")
|
procDrawMenuBar = moduser32.NewProc("DrawMenuBar")
|
||||||
|
procTrackPopupMenu = moduser32.NewProc("TrackPopupMenu")
|
||||||
procTrackPopupMenuEx = moduser32.NewProc("TrackPopupMenuEx")
|
procTrackPopupMenuEx = moduser32.NewProc("TrackPopupMenuEx")
|
||||||
procGetKeyState = moduser32.NewProc("GetKeyState")
|
procGetKeyState = moduser32.NewProc("GetKeyState")
|
||||||
procGetSysColorBrush = moduser32.NewProc("GetSysColorBrush")
|
procGetSysColorBrush = moduser32.NewProc("GetSysColorBrush")
|
||||||
|
procGetSysColor = moduser32.NewProc("GetSysColor")
|
||||||
|
|
||||||
|
procMapWindowPoints = moduser32.NewProc("MapWindowPoints")
|
||||||
|
|
||||||
procGetWindowPlacement = moduser32.NewProc("GetWindowPlacement")
|
procGetWindowPlacement = moduser32.NewProc("GetWindowPlacement")
|
||||||
procSetWindowPlacement = moduser32.NewProc("SetWindowPlacement")
|
procSetWindowPlacement = moduser32.NewProc("SetWindowPlacement")
|
||||||
|
procGetWindowDC = moduser32.NewProc("GetWindowDC")
|
||||||
|
|
||||||
procGetScrollInfo = moduser32.NewProc("GetScrollInfo")
|
procGetScrollInfo = moduser32.NewProc("GetScrollInfo")
|
||||||
procSetScrollInfo = moduser32.NewProc("SetScrollInfo")
|
procSetScrollInfo = moduser32.NewProc("SetScrollInfo")
|
||||||
@ -187,6 +192,11 @@ func init() {
|
|||||||
mainThread = GetCurrentThreadId()
|
mainThread = GetCurrentThreadId()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetWindowDC(hwnd HWND) HDC {
|
||||||
|
ret, _, _ := procGetWindowDC.Call(hwnd)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func GET_X_LPARAM(lp uintptr) int32 {
|
func GET_X_LPARAM(lp uintptr) int32 {
|
||||||
return int32(int16(LOWORD(uint32(lp))))
|
return int32(int16(LOWORD(uint32(lp))))
|
||||||
}
|
}
|
||||||
@ -1058,10 +1068,14 @@ func FillRect(hDC HDC, lprc *RECT, hbr HBRUSH) bool {
|
|||||||
return ret != 0
|
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(
|
ret, _, _ := procDrawText.Call(
|
||||||
uintptr(hDC),
|
uintptr(hDC),
|
||||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))),
|
uintptr(unsafe.Pointer(&text[0])),
|
||||||
uintptr(uCount),
|
uintptr(uCount),
|
||||||
uintptr(unsafe.Pointer(lpRect)),
|
uintptr(unsafe.Pointer(lpRect)),
|
||||||
uintptr(uFormat))
|
uintptr(uFormat))
|
||||||
|
@ -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)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user