1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-08-04 23:30:26 +00:00

Merge pull request #245 from mkilgore/dialog-fixes

Improve dialog window association and _InputBox$ support on Windows
This commit is contained in:
Matt Kilgore 2022-11-13 15:41:24 -05:00 committed by GitHub
commit 855eaac864
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 166 additions and 116 deletions

View file

@ -59,6 +59,7 @@
//#include <math.h> //<-causes overloading abs conflicts in Windows
# include <cmath>
# endif
# include <stdint.h>
# include <errno.h>
# include <fcntl.h>
# include <fstream>
@ -82,7 +83,6 @@
# else
# include <pthread.h>
# include <stdint.h>
# include <stdlib.h>
# include <sys/stat.h>
# include <sys/types.h>

View file

@ -7,25 +7,14 @@
* Should this be adapted to check for each type before defining?
*/
#ifndef QB64_OS_H_NO_TYPES
# ifdef QB64_WINDOWS
# define uint64 unsigned __int64
# define uint32 unsigned __int32
# define uint16 unsigned __int16
# define uint8 unsigned __int8
# define int64 __int64
# define int32 __int32
# define int16 __int16
# define int8 __int8
# else
# define int64 int64_t
# define int32 int32_t
# define int16 int16_t
# define int8 int8_t
# define uint64 uint64_t
# define uint32 uint32_t
# define uint16 uint16_t
# define uint8 uint8_t
# endif
# define int64 int64_t
# define int32 int32_t
# define int16 int16_t
# define int8 int8_t
# define uint64 uint64_t
# define uint32 uint32_t
# define uint16 uint16_t
# define uint8 uint8_t
# define ptrszint intptr_t
# define uptrszint uintptr_t

View file

@ -918,6 +918,120 @@ static int fileExists(char const * aFilePathAndName)
}
}
// Checks that it's the top-level Window for the process
static BOOL isMainWindow(HWND handle)
{
return GetWindow(handle, GW_OWNER) == (HWND)0 && IsWindowVisible(handle);
}
struct handle_data {
unsigned long process_id;
HWND window_handle;
};
static BOOL CALLBACK enumCallback(HWND handle, LPARAM lParam)
{
struct handle_data *data = (struct handle_data*)lParam;
unsigned long process_id = 0;
GetWindowThreadProcessId(handle, &process_id);
if (data->process_id != process_id || !isMainWindow(handle))
return TRUE; // Keep going to the next window
data->window_handle = handle;
return FALSE; // End the enumeration
}
static HWND TryGetMainWindow()
{
struct handle_data data = {
.process_id = GetCurrentProcessId(),
.window_handle = 0,
};
EnumWindows(enumCallback, (LPARAM)&data);
return data.window_handle;
}
static LRESULT CALLBACK hiddenWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
static HWND CreateHiddenWindow()
{
WNDCLASSEX wndclass = {
sizeof(WNDCLASSEX),
CS_DBLCLKS,
hiddenWindowProc,
0,
0,
GetModuleHandle(0),
LoadIcon(0, IDI_APPLICATION),
LoadCursor(0, IDC_ARROW),
(HBRUSH)(COLOR_WINDOW + 1),
0,
"hiddenWindowClass",
LoadIcon(0, IDI_APPLICATION)
};
if (!RegisterClassEx(&wndclass))
return 0;
return CreateWindowEx(
0,
"hiddenWindowClass",
"hiddenWindow",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
GetModuleHandle(NULL),
0
);
}
static HWND windowForDialogs = 0;
// Returns a window handle sutable for displaying dialogs and notifications
//
// We attempt to find a window associated with the program, if none exists then
// we'll create a hidden one.
static HWND GetDialogWindow()
{
if (windowForDialogs)
return windowForDialogs;
// First see if our process has a window already
windowForDialogs = TryGetMainWindow();
if (windowForDialogs)
return windowForDialogs;
// Check if we have an associated console
windowForDialogs = GetConsoleWindow();
if (windowForDialogs)
return windowForDialogs;
// No sutable window could be found, create a hidden one for us to use
windowForDialogs = CreateHiddenWindow();
return windowForDialogs;
}
#endif /* _WIN32 */
/* source and destination can be the same or ovelap*/
@ -1036,7 +1150,7 @@ int tinyfd_messageBoxW(
aCode += MB_TOPMOST;
lBoxReturnValue = MessageBoxW(GetForegroundWindow(), aMessage, aTitle, aCode);
lBoxReturnValue = MessageBoxW(GetDialogWindow(), aMessage, aTitle, aCode);
if ( (lBoxReturnValue == IDNO) && (aDialogType && !wcscmp(L"yesnocancel", aDialogType)) )
{
@ -1052,64 +1166,12 @@ int tinyfd_messageBoxW(
}
}
static LRESULT CALLBACK hiddenNotifyWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
static HWND hiddenWindow = NULL;
static HICON notifyIcon = NULL;
// Creates a hidden window to tie the Notifications too
static void setupHiddenWindowHandles()
static void setupNotifyIconHandle()
{
if (hiddenWindow || notifyIcon)
return;
WNDCLASSEX wndclass = {
sizeof(WNDCLASSEX),
CS_DBLCLKS,
hiddenNotifyWindowProc,
0,
0,
GetModuleHandle(0),
LoadIcon(0,IDI_APPLICATION),
LoadCursor(0,IDC_ARROW),
(HBRUSH)(COLOR_WINDOW + 1),
0,
"notifyIconClass",
LoadIcon(0,IDI_APPLICATION)
};
if (!RegisterClassEx(&wndclass))
return;
hiddenWindow = CreateWindowEx(
0,
"notifyIconClass",
"title",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
GetModuleHandle(NULL),
0
);
if (!hiddenWindow)
if (notifyIcon)
return;
// Attempt to load 32x32 image
@ -1131,7 +1193,7 @@ static void createWinNotificationEntry()
memset(&notificationData, 0, sizeof(notificationData));
notificationData.cbSize = sizeof(notificationData);
notificationData.hWnd = hiddenWindow;
notificationData.hWnd = GetDialogWindow();
notificationData.uID = 0;
notificationData.hIcon = notifyIcon;
@ -1146,7 +1208,7 @@ static void sendWinNotification(const wchar_t *title, const wchar_t *message, co
NOTIFYICONDATAW notificationData;
memset(&notificationData, 0, sizeof(notificationData));
notificationData.cbSize = sizeof(notificationData);
notificationData.hWnd = hiddenWindow;
notificationData.hWnd = GetDialogWindow();
notificationData.uID = 0;
notificationData.hIcon = notifyIcon;
@ -1175,10 +1237,10 @@ int tinyfd_notifyPopupW(
{
if (aTitle && !wcscmp(aTitle, L"tinyfd_query")) { strcpy(tinyfd_response, "windows_wchar"); return 1; }
setupHiddenWindowHandles();
setupNotifyIconHandle();
// Can't send notification if the handles aren't setup
if (!hiddenWindow || !notifyIcon)
if (!notifyIcon)
return 0;
createWinNotificationEntry();
@ -1228,7 +1290,7 @@ static BOOL CALLBACK dialogBoxCallback(HWND hwndDlg, UINT message, WPARAM wParam
return FALSE;
}
static LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, const wchar_t *title, const wchar_t *lpszMessage, const wchar_t *defaultText)
static LRESULT displayInputBox(HINSTANCE hinst, HWND hwndOwner, const wchar_t *title, const wchar_t *lpszMessage, const wchar_t *defaultText)
{
HGLOBAL hgbl;
LPDLGTEMPLATE lpdt;
@ -1252,7 +1314,7 @@ static LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, const wchar_t *
// Define a dialog box.
lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION | DS_SETFONT;
lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION | DS_SETFONT | DS_SYSMODAL;
lpdt->cdit = 4; // Number of controls
lpdt->x = 0; lpdt->y = 0;
lpdt->cx = 200; lpdt->cy = 70;
@ -1271,6 +1333,27 @@ static LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, const wchar_t *
for (lpwsz = (LPWSTR)lpw; ((*lpwsz++) = *font++);); // Font name
lpw = (LPWORD)lpwsz;
//-----------------------
// Define a edit text control.
//-----------------------
lpw = lpwAlignDWORD(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 5; lpdit->y = 53;
lpdit->cx = 190; lpdit->cy = 12;
lpdit->id = ID_TEXT; // Text identifier
lpdit->style = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL | ES_LEFT;
if (wcslen(defaultText) == 0)
lpdit->style |= ES_PASSWORD;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0081; // Edit class
for (lpwsz = (LPWSTR)lpw; ((*lpwsz++) = *defaultText++););
lpw = (LPWORD)lpwsz;
*lpw++ = 0; // No creation data
//-----------------------
// Define an OK button.
//-----------------------
@ -1279,7 +1362,7 @@ static LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, const wchar_t *
lpdit->x = 160; lpdit->y = 6;
lpdit->cx = 35; lpdit->cy = 12;
lpdit->id = IDOK; // OK button identifier
lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;
lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
@ -1298,7 +1381,7 @@ static LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, const wchar_t *
lpdit->x = 160; lpdit->y = 21;
lpdit->cx = 35; lpdit->cy = 12;
lpdit->id = IDCANCEL; // Cancel button identifier
lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
@ -1309,27 +1392,6 @@ static LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, const wchar_t *
lpw += nchar;
*lpw++ = 0; // No creation data
//-----------------------
// Define a edit text control.
//-----------------------
lpw = lpwAlignDWORD(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 5; lpdit->y = 53;
lpdit->cx = 190; lpdit->cy = 12;
lpdit->id = ID_TEXT; // Text identifier
lpdit->style = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | ES_LEFT;
if (wcslen(defaultText) == 0)
lpdit->style |= ES_PASSWORD;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0081; // Edit class
for (lpwsz = (LPWSTR)lpw; ((*lpwsz++) = *defaultText++););
lpw = (LPWORD)lpwsz;
*lpw++ = 0; // No creation data
//-----------------------
// Define a static text control.
//-----------------------
@ -1373,8 +1435,7 @@ wchar_t * tinyfd_inputBoxW(
aMessage = aMessage? aMessage: L"";
aDefaultInput = aDefaultInput? aDefaultInput: L"";
setupHiddenWindowHandles();
DisplayMyMessage(GetModuleHandle(NULL), hiddenWindow, aTitle, aMessage, aDefaultInput);
displayInputBox(GetModuleHandle(NULL), GetDialogWindow(), aTitle, aMessage, aDefaultInput);
mbstowcs(lBuff, dialogContents, MAX_PATH_OR_CMD);
return lBuff;
@ -1436,7 +1497,7 @@ wchar_t * tinyfd_saveFileDialogW(
}
ofn.lStructSize = sizeof(OPENFILENAMEW);
ofn.hwndOwner = GetForegroundWindow();
ofn.hwndOwner = GetDialogWindow();
ofn.hInstance = 0;
ofn.lpstrFilter = wcslen(lFilterPatterns) ? lFilterPatterns : NULL;
ofn.lpstrCustomFilter = NULL;
@ -1552,7 +1613,7 @@ wchar_t * tinyfd_openFileDialogW(
}
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = GetForegroundWindow();
ofn.hwndOwner = GetDialogWindow();
ofn.hInstance = 0;
ofn.lpstrFilter = wcslen(lFilterPatterns) ? lFilterPatterns : NULL;
ofn.lpstrCustomFilter = NULL;
@ -1672,7 +1733,7 @@ wchar_t * tinyfd_selectFolderDialogW(
lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
bInfo.hwndOwner = GetForegroundWindow();
bInfo.hwndOwner = GetDialogWindow();
bInfo.pidlRoot = NULL;
bInfo.pszDisplayName = lBuff;
bInfo.lpszTitle = aTitle && wcslen(aTitle) ? aTitle : NULL;
@ -1734,7 +1795,7 @@ wchar_t * tinyfd_colorChooserW(
/* we can't use aTitle */
cc.lStructSize = sizeof(CHOOSECOLOR);
cc.hwndOwner = GetForegroundWindow();
cc.hwndOwner = GetDialogWindow();
cc.hInstance = NULL;
cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]);
cc.lpCustColors = crCustColors;

View file

@ -13,7 +13,7 @@ _Dest test1&
Circle (64, 25), 25, 7
Paint (64, 25), TILE$, 7
AssertImage test1&, "tile_border_black_bg.bmp"
AssertImage test1&, "tile_border_black_bg.png"
System
'$include:'../utilities/imageassert.bm'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

View file

@ -17,7 +17,7 @@ Line (0, 0)-(127, 49), 7, BF
Circle (64, 25), 25, 0
Paint (64, 25), TILE$, 7
AssertImage test1&, "tile_border_black_nocolor.bmp"
AssertImage test1&, "tile_border_black_nocolor.png"
System
'$include:'../utilities/imageassert.bm'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

View file

@ -22,7 +22,7 @@ Pset (64, 25), 15 ' Set the starting location a third color
Paint (64, 25), TILE$, 0
AssertImage test1&, "tile_border_white_bg.bmp"
AssertImage test1&, "tile_border_white_bg.png"
System
'$include:'../utilities/imageassert.bm'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

View file

@ -17,7 +17,7 @@ Line (0, 0)-(127, 49), 7, BF
Circle (64, 25), 25, 0
Paint (64, 25), TILE$, 7
AssertImage test1&, "tile_border_white_nocolor.bmp"
AssertImage test1&, "tile_border_white_nocolor.png"
System
'$include:'../utilities/imageassert.bm'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

View file

@ -13,7 +13,7 @@ _Dest test1&
Circle (64, 25), 25, 7
Paint (64, 25), TILE$
AssertImage test1&, "tile_noborder_black_bg.bmp"
AssertImage test1&, "tile_noborder_black_bg.png"
System
'$include:'../utilities/imageassert.bm'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

View file

@ -16,7 +16,7 @@ Line (0, 0)-(127, 49), 7, BF
Circle (64, 25), 25, 0
Paint (64, 25), TILE$
AssertImage test1&, "tile_noborder_white_bg.bmp"
AssertImage test1&, "tile_noborder_white_bg.png"
System
'$include:'../utilities/imageassert.bm'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B