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()()
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 {
return nil, err
}

View file

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

View file

@ -13,8 +13,26 @@ var (
getOpenFileName = comdlg32.NewProc("GetOpenFileNameW")
getSaveFileName = comdlg32.NewProc("GetSaveFileNameW")
shBrowseForFolder = shell32.NewProc("SHBrowseForFolderW")
shGetPathFromIDListEx = shell32.NewProc("SHGetPathFromIDListEx")
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) {
@ -25,13 +43,13 @@ func selectFile(opts options) (string, error) {
var args _OPENFILENAME
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 {
args.Title = syscall.StringToUTF16Ptr(*opts.title)
}
if opts.showHidden {
args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN
args.Flags |= _OFN_FORCESHOWHIDDEN
}
if opts.fileFilters != nil {
args.Filter = &initFilters(opts.fileFilters)[0]
@ -45,7 +63,7 @@ func selectFile(opts options) (string, error) {
defer setup()()
if opts.ctx != nil {
unhook, err := hookDialog(opts.ctx, nil)
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil {
return "", err
}
@ -70,13 +88,13 @@ func selectFileMultiple(opts options) ([]string, error) {
var args _OPENFILENAME
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 {
args.Title = syscall.StringToUTF16Ptr(*opts.title)
}
if opts.showHidden {
args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN
args.Flags |= _OFN_FORCESHOWHIDDEN
}
if opts.fileFilters != nil {
args.Filter = &initFilters(opts.fileFilters)[0]
@ -90,7 +108,7 @@ func selectFileMultiple(opts options) ([]string, error) {
defer setup()()
if opts.ctx != nil {
unhook, err := hookDialog(opts.ctx, nil)
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil {
return nil, err
}
@ -140,19 +158,19 @@ func selectFileSave(opts options) (string, error) {
var args _OPENFILENAME
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 {
args.Title = syscall.StringToUTF16Ptr(*opts.title)
}
if opts.confirmOverwrite {
args.Flags |= 0x2 // OFN_OVERWRITEPROMPT
args.Flags |= _OFN_OVERWRITEPROMPT
}
if opts.confirmCreate {
args.Flags |= 0x2000 // OFN_CREATEPROMPT
args.Flags |= _OFN_CREATEPROMPT
}
if opts.showHidden {
args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN
args.Flags |= _OFN_FORCESHOWHIDDEN
}
if opts.fileFilters != nil {
args.Filter = &initFilters(opts.fileFilters)[0]
@ -166,7 +184,7 @@ func selectFileSave(opts options) (string, error) {
defer setup()()
if opts.ctx != nil {
unhook, err := hookDialog(opts.ctx, nil)
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil {
return "", err
}
@ -211,13 +229,14 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
if int32(hr) < 0 {
return "", nil, syscall.Errno(hr)
}
flgs |= _FOS_NOCHANGEDIR | _FOS_PICKFOLDERS | _FOS_FORCEFILESYSTEM
if multi {
flgs |= 0x200 // FOS_ALLOWMULTISELECT
flgs |= _FOS_ALLOWMULTISELECT
}
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 {
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 {
unhook, err := hookDialog(opts.ctx, nil)
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil {
return "", nil, err
}
@ -320,7 +339,7 @@ func browseForFolder(opts options) (string, []string, error) {
}
if opts.ctx != nil {
unhook, err := hookDialog(opts.ctx, nil)
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil {
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) {
return hookDialog(opts.ctx, 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)
}
}
return hookDialog(opts.ctx, opts.windowIcon, nil, func(wnd uintptr) {
enumChildWindows.Call(wnd,
syscall.NewCallback(hookMessageDialogCallback),
uintptr(unsafe.Pointer(&opts)))
@ -122,7 +115,7 @@ func hookMessageDialogCallback(wnd uintptr, lparam *options) uintptr {
icon := getIcon(lparam.icon)
if icon.handle != 0 {
defer icon.delete()
sendMessage.Call(wnd, 0x0170 /*STM_SETICON*/, icon.handle, 0)
sendMessage.Call(wnd, _STM_SETICON, icon.handle, 0)
}
}
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 {
return nil, ctx.Err()
}
hook, err := newDialogHook(ctx, initDialog)
hook, err := newDialogHook(ctx, icon, title, init)
if err != nil {
return nil, err
}
@ -168,15 +168,17 @@ func hookDialog(ctx context.Context, initDialog func(wnd uintptr)) (unhook conte
}
type dialogHook struct {
ctx context.Context
tid uintptr
wnd uintptr
hook uintptr
done chan struct{}
init func(wnd uintptr)
ctx context.Context
tid uintptr
wnd uintptr
hook uintptr
done chan struct{}
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()
hk, _, err := setWindowsHookEx.Call(5, // WH_CBT
syscall.NewCallback(dialogHookProc), 0, tid)
@ -185,10 +187,12 @@ func newDialogHook(ctx context.Context, initDialog func(wnd uintptr)) (*dialogHo
}
hook := dialogHook{
ctx: ctx,
tid: tid,
hook: hk,
init: initDialog,
ctx: ctx,
tid: tid,
hook: hk,
icon: icon,
title: title,
init: init,
}
if ctx != nil {
hook.done = make(chan struct{})
@ -206,8 +210,20 @@ func dialogHookProc(code int32, wparam, lparam uintptr) uintptr {
atomic.StoreUintptr(&hook.wnd, wparam)
if hook.ctx != nil && hook.ctx.Err() != nil {
sendMessage.Call(wparam, _WM_SYSCOMMAND, _SC_CLOSE, 0)
} else if hook.init != nil {
hook.init(wparam)
} else {
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(
@ -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 {
sync.Mutex
m map[uintptr]unsafe.Pointer