Window icons (windows).

This commit is contained in:
Nuno Cruces 2022-05-19 23:39:21 +01:00
parent af8627dee2
commit b988db5bd2
5 changed files with 72 additions and 52 deletions

View file

@ -49,7 +49,7 @@ func selectColor(opts options) (color.Color, error) {
defer setup()() defer setup()()
if opts.ctx != nil || opts.title != nil { if opts.ctx != nil || opts.title != nil {
unhook, err := hookDialogTitle(opts.ctx, opts.title) unhook, err := hookDialog(opts.ctx, opts.windowIcon, opts.title, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -58,6 +58,7 @@ const (
_WM_DESTROY = 0x0002 _WM_DESTROY = 0x0002
_WM_CLOSE = 0x0010 _WM_CLOSE = 0x0010
_WM_SETFONT = 0x0030 _WM_SETFONT = 0x0030
_WM_SETICON = 0x0080
_WM_NCCREATE = 0x0081 _WM_NCCREATE = 0x0081
_WM_NCDESTROY = 0x0082 _WM_NCDESTROY = 0x0082
_WM_COMMAND = 0x0111 _WM_COMMAND = 0x0111
@ -74,6 +75,7 @@ const (
_PBM_SETPOS = _WM_USER + 2 _PBM_SETPOS = _WM_USER + 2
_PBM_SETRANGE32 = _WM_USER + 6 _PBM_SETRANGE32 = _WM_USER + 6
_PBM_SETMARQUEE = _WM_USER + 10 _PBM_SETMARQUEE = _WM_USER + 10
_STM_SETICON = 0x0170
_GWL_STYLE = -16 _GWL_STYLE = -16

View file

@ -13,8 +13,26 @@ var (
getOpenFileName = comdlg32.NewProc("GetOpenFileNameW") getOpenFileName = comdlg32.NewProc("GetOpenFileNameW")
getSaveFileName = comdlg32.NewProc("GetSaveFileNameW") getSaveFileName = comdlg32.NewProc("GetSaveFileNameW")
shBrowseForFolder = shell32.NewProc("SHBrowseForFolderW") shBrowseForFolder = shell32.NewProc("SHBrowseForFolderW")
shGetPathFromIDListEx = shell32.NewProc("SHGetPathFromIDListEx")
shCreateItemFromParsingName = shell32.NewProc("SHCreateItemFromParsingName") shCreateItemFromParsingName = shell32.NewProc("SHCreateItemFromParsingName")
shGetPathFromIDListEx = shell32.NewProc("SHGetPathFromIDListEx")
)
const (
_OFN_OVERWRITEPROMPT = 0x00000002
_OFN_NOCHANGEDIR = 0x00000008
_OFN_ALLOWMULTISELECT = 0x00000200
_OFN_PATHMUSTEXIST = 0x00000800
_OFN_FILEMUSTEXIST = 0x00001000
_OFN_CREATEPROMPT = 0x00002000
_OFN_NOREADONLYRETURN = 0x00008000
_OFN_EXPLORER = 0x00080000
_OFN_FORCESHOWHIDDEN = 0x10000000
_FOS_NOCHANGEDIR = 0x00000008
_FOS_PICKFOLDERS = 0x00000020
_FOS_FORCEFILESYSTEM = 0x00000040
_FOS_ALLOWMULTISELECT = 0x00000200
_FOS_FORCESHOWHIDDEN = 0x10000000
) )
func selectFile(opts options) (string, error) { func selectFile(opts options) (string, error) {
@ -25,13 +43,13 @@ func selectFile(opts options) (string, error) {
var args _OPENFILENAME var args _OPENFILENAME
args.StructSize = uint32(unsafe.Sizeof(args)) args.StructSize = uint32(unsafe.Sizeof(args))
args.Flags = 0x81008 // OFN_NOCHANGEDIR|OFN_FILEMUSTEXIST|OFN_EXPLORER args.Flags = _OFN_NOCHANGEDIR | _OFN_FILEMUSTEXIST | _OFN_EXPLORER
if opts.title != nil { if opts.title != nil {
args.Title = syscall.StringToUTF16Ptr(*opts.title) args.Title = syscall.StringToUTF16Ptr(*opts.title)
} }
if opts.showHidden { if opts.showHidden {
args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN args.Flags |= _OFN_FORCESHOWHIDDEN
} }
if opts.fileFilters != nil { if opts.fileFilters != nil {
args.Filter = &initFilters(opts.fileFilters)[0] args.Filter = &initFilters(opts.fileFilters)[0]
@ -45,7 +63,7 @@ func selectFile(opts options) (string, error) {
defer setup()() defer setup()()
if opts.ctx != nil { if opts.ctx != nil {
unhook, err := hookDialog(opts.ctx, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -70,13 +88,13 @@ func selectFileMultiple(opts options) ([]string, error) {
var args _OPENFILENAME var args _OPENFILENAME
args.StructSize = uint32(unsafe.Sizeof(args)) args.StructSize = uint32(unsafe.Sizeof(args))
args.Flags = 0x81208 // OFN_NOCHANGEDIR|OFN_ALLOWMULTISELECT|OFN_FILEMUSTEXIST|OFN_EXPLORER args.Flags = _OFN_NOCHANGEDIR | _OFN_ALLOWMULTISELECT | _OFN_FILEMUSTEXIST | _OFN_EXPLORER
if opts.title != nil { if opts.title != nil {
args.Title = syscall.StringToUTF16Ptr(*opts.title) args.Title = syscall.StringToUTF16Ptr(*opts.title)
} }
if opts.showHidden { if opts.showHidden {
args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN args.Flags |= _OFN_FORCESHOWHIDDEN
} }
if opts.fileFilters != nil { if opts.fileFilters != nil {
args.Filter = &initFilters(opts.fileFilters)[0] args.Filter = &initFilters(opts.fileFilters)[0]
@ -90,7 +108,7 @@ func selectFileMultiple(opts options) ([]string, error) {
defer setup()() defer setup()()
if opts.ctx != nil { if opts.ctx != nil {
unhook, err := hookDialog(opts.ctx, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -140,19 +158,19 @@ func selectFileSave(opts options) (string, error) {
var args _OPENFILENAME var args _OPENFILENAME
args.StructSize = uint32(unsafe.Sizeof(args)) args.StructSize = uint32(unsafe.Sizeof(args))
args.Flags = 0x88808 // OFN_NOCHANGEDIR|OFN_PATHMUSTEXIST|OFN_NOREADONLYRETURN|OFN_EXPLORER args.Flags = _OFN_NOCHANGEDIR | _OFN_PATHMUSTEXIST | _OFN_NOREADONLYRETURN | _OFN_EXPLORER
if opts.title != nil { if opts.title != nil {
args.Title = syscall.StringToUTF16Ptr(*opts.title) args.Title = syscall.StringToUTF16Ptr(*opts.title)
} }
if opts.confirmOverwrite { if opts.confirmOverwrite {
args.Flags |= 0x2 // OFN_OVERWRITEPROMPT args.Flags |= _OFN_OVERWRITEPROMPT
} }
if opts.confirmCreate { if opts.confirmCreate {
args.Flags |= 0x2000 // OFN_CREATEPROMPT args.Flags |= _OFN_CREATEPROMPT
} }
if opts.showHidden { if opts.showHidden {
args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN args.Flags |= _OFN_FORCESHOWHIDDEN
} }
if opts.fileFilters != nil { if opts.fileFilters != nil {
args.Filter = &initFilters(opts.fileFilters)[0] args.Filter = &initFilters(opts.fileFilters)[0]
@ -166,7 +184,7 @@ func selectFileSave(opts options) (string, error) {
defer setup()() defer setup()()
if opts.ctx != nil { if opts.ctx != nil {
unhook, err := hookDialog(opts.ctx, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -211,13 +229,14 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
if int32(hr) < 0 { if int32(hr) < 0 {
return "", nil, syscall.Errno(hr) return "", nil, syscall.Errno(hr)
} }
flgs |= _FOS_NOCHANGEDIR | _FOS_PICKFOLDERS | _FOS_FORCEFILESYSTEM
if multi { if multi {
flgs |= 0x200 // FOS_ALLOWMULTISELECT flgs |= _FOS_ALLOWMULTISELECT
} }
if opts.showHidden { if opts.showHidden {
flgs |= 0x10000000 // FOS_FORCESHOWHIDDEN flgs |= _FOS_FORCESHOWHIDDEN
} }
hr, _, _ = dialog.Call(dialog.SetOptions, uintptr(flgs|0x68)) // FOS_NOCHANGEDIR|FOS_PICKFOLDERS|FOS_FORCEFILESYSTEM hr, _, _ = dialog.Call(dialog.SetOptions, uintptr(flgs))
if int32(hr) < 0 { if int32(hr) < 0 {
return "", nil, syscall.Errno(hr) return "", nil, syscall.Errno(hr)
} }
@ -242,7 +261,7 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
} }
if opts.ctx != nil { if opts.ctx != nil {
unhook, err := hookDialog(opts.ctx, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
@ -320,7 +339,7 @@ func browseForFolder(opts options) (string, []string, error) {
} }
if opts.ctx != nil { if opts.ctx != nil {
unhook, err := hookDialog(opts.ctx, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }

View file

@ -88,14 +88,7 @@ func message(kind messageKind, text string, opts options) error {
} }
func hookMessageDialog(kind messageKind, opts options) (unhook context.CancelFunc, err error) { func hookMessageDialog(kind messageKind, opts options) (unhook context.CancelFunc, err error) {
return hookDialog(opts.ctx, func(wnd uintptr) { return hookDialog(opts.ctx, opts.windowIcon, nil, func(wnd uintptr) {
if opts.windowIcon != nil {
icon := getIcon(opts.windowIcon)
if icon.handle != 0 {
defer icon.delete()
sendMessage.Call(wnd, 0x0080 /*WM_SETICON*/, 0, icon.handle)
}
}
enumChildWindows.Call(wnd, enumChildWindows.Call(wnd,
syscall.NewCallback(hookMessageDialogCallback), syscall.NewCallback(hookMessageDialogCallback),
uintptr(unsafe.Pointer(&opts))) uintptr(unsafe.Pointer(&opts)))
@ -122,7 +115,7 @@ func hookMessageDialogCallback(wnd uintptr, lparam *options) uintptr {
icon := getIcon(lparam.icon) icon := getIcon(lparam.icon)
if icon.handle != 0 { if icon.handle != 0 {
defer icon.delete() defer icon.delete()
sendMessage.Call(wnd, 0x0170 /*STM_SETICON*/, icon.handle, 0) sendMessage.Call(wnd, _STM_SETICON, icon.handle, 0)
} }
} }
return 1 return 1

View file

@ -156,11 +156,11 @@ func commDlgError() error {
} }
} }
func hookDialog(ctx context.Context, initDialog func(wnd uintptr)) (unhook context.CancelFunc, err error) { func hookDialog(ctx context.Context, icon any, title *string, init func(wnd uintptr)) (unhook context.CancelFunc, err error) {
if ctx != nil && ctx.Err() != nil { if ctx != nil && ctx.Err() != nil {
return nil, ctx.Err() return nil, ctx.Err()
} }
hook, err := newDialogHook(ctx, initDialog) hook, err := newDialogHook(ctx, icon, title, init)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -168,15 +168,17 @@ func hookDialog(ctx context.Context, initDialog func(wnd uintptr)) (unhook conte
} }
type dialogHook struct { type dialogHook struct {
ctx context.Context ctx context.Context
tid uintptr tid uintptr
wnd uintptr wnd uintptr
hook uintptr hook uintptr
done chan struct{} done chan struct{}
init func(wnd uintptr) icon any
title *string
init func(wnd uintptr)
} }
func newDialogHook(ctx context.Context, initDialog func(wnd uintptr)) (*dialogHook, error) { func newDialogHook(ctx context.Context, icon any, title *string, init func(wnd uintptr)) (*dialogHook, error) {
tid, _, _ := getCurrentThreadId.Call() tid, _, _ := getCurrentThreadId.Call()
hk, _, err := setWindowsHookEx.Call(5, // WH_CBT hk, _, err := setWindowsHookEx.Call(5, // WH_CBT
syscall.NewCallback(dialogHookProc), 0, tid) syscall.NewCallback(dialogHookProc), 0, tid)
@ -185,10 +187,12 @@ func newDialogHook(ctx context.Context, initDialog func(wnd uintptr)) (*dialogHo
} }
hook := dialogHook{ hook := dialogHook{
ctx: ctx, ctx: ctx,
tid: tid, tid: tid,
hook: hk, hook: hk,
init: initDialog, icon: icon,
title: title,
init: init,
} }
if ctx != nil { if ctx != nil {
hook.done = make(chan struct{}) hook.done = make(chan struct{})
@ -206,8 +210,20 @@ func dialogHookProc(code int32, wparam, lparam uintptr) uintptr {
atomic.StoreUintptr(&hook.wnd, wparam) atomic.StoreUintptr(&hook.wnd, wparam)
if hook.ctx != nil && hook.ctx.Err() != nil { if hook.ctx != nil && hook.ctx.Err() != nil {
sendMessage.Call(wparam, _WM_SYSCOMMAND, _SC_CLOSE, 0) sendMessage.Call(wparam, _WM_SYSCOMMAND, _SC_CLOSE, 0)
} else if hook.init != nil { } else {
hook.init(wparam) if hook.icon != nil {
icon := getIcon(hook.icon)
if icon.handle != 0 {
defer icon.delete()
sendMessage.Call(wparam, _WM_SETICON, 0, icon.handle)
}
}
if hook.title != nil {
setWindowText.Call(wparam, strptr(*hook.title))
}
if hook.init != nil {
hook.init(wparam)
}
} }
} }
next, _, _ := callNextHookEx.Call( next, _, _ := callNextHookEx.Call(
@ -233,16 +249,6 @@ func (h *dialogHook) wait() {
} }
} }
func hookDialogTitle(ctx context.Context, title *string) (unhook context.CancelFunc, err error) {
var init func(wnd uintptr)
if title != nil {
init = func(wnd uintptr) {
setWindowText.Call(wnd, strptr(*title))
}
}
return hookDialog(ctx, init)
}
var backRefs struct { var backRefs struct {
sync.Mutex sync.Mutex
m map[uintptr]unsafe.Pointer m map[uintptr]unsafe.Pointer