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:
parent
8a3d7eb12a
commit
2faf8533e0
@ -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" {
|
||||
|
@ -324,6 +324,7 @@ type ThemeSettings struct {
|
||||
LightModeTitleTextInactive int32
|
||||
LightModeBorder int32
|
||||
LightModeBorderInactive int32
|
||||
DarkModeMenuBar int32
|
||||
}
|
||||
|
||||
/****** Mac Options *******/
|
||||
|
@ -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})
|
||||
|
@ -608,6 +608,7 @@ const (
|
||||
WM_DPICHANGED = 0x02E0
|
||||
WM_UAHDRAWMENU = 0x0091
|
||||
WM_UAHDRAWMENUITEM = 0x0092
|
||||
WM_UAHMEASUREMENUITEM = 0x012E
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -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 {
|
||||
if !IsCurrentlyDarkMode() {
|
||||
return 0
|
||||
}
|
||||
udm := (*UAHMENU)(unsafe.Pointer(lParam))
|
||||
var rc RECT
|
||||
func RGB(r, g, b byte) uint32 {
|
||||
return uint32(r) | uint32(g)<<8 | uint32(b)<<16
|
||||
}
|
||||
|
||||
// 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)
|
||||
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 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 {
|
||||
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),
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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