This may have been covered but my memeory gets worse every day.
Most of my PowerBASIC GUI coding has used a combination of an SDK Window as a main window (could be invisible) and resource dialogs (could be DDT) for the majority of the application. I had no need for eye candy as most were data input, calculation set up, and calculation rendering.
José and Patrice advocate using only SDK windows.
I use Modal dialogs in situations where data is required to proceed with the flow of the program.
How would one handle these situations with SDK only?
James
You do it just the same than for a regular popup window, except that the popup input box will use your main window for parent.
I like to use a DDW to disable the parent window, as long as the child popup has not been completed.
More about a DDW here. (http://www.jose.it-berater.org/smfforum/index.php?topic=3178.msg10093#msg10093)
...
I use my CWindow class, that simplifies the syntax while remaining pure SDK.
This is the code for the "Go to line" popup dialog of my CSED editor:
' ########################################################################################
' CSED Editor
' File: CSED_GOTOLINE.INC
' Contents: Go to line dialog
' Copyright (c) 2011 José Roca
' All Rights Reserved.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################
' ========================================================================================
' Go to line popup dialog
' ========================================================================================
SUB CSED_GotoLineDialog (BYVAL hParent AS LONG)
' // Create an instance of the class
LOCAL pWindow AS IWindow
pWindow = CLASS "CWindow"
IF ISNOTHING(pWindow) THEN EXIT SUB
' // Create the main window
LOCAL hwnd AS DWORD
hwnd = pWindow.CreateWindow(hParent, "Go to line", 0, 0, 0, 0, _
%WS_VISIBLE OR %WS_CAPTION OR %WS_POPUPWINDOW, %WS_EX_WINDOWEDGE, _
CODEPTR(CSED_GotoLine_WindowProc))
pWindow.SetClientSize 254, 97
pWindow.CenterWindow(hwnd, hParent)
' // Retrieve the position and number of lines
LOCAL curPos AS LONG
LOCAL nLine AS LONG
LOCAL nLines AS LONG
curPos = SCI_GetCurrentPos(pSed.hEdit)
nLine = SCI_LineFromPosition(pSed.hEdit, curPos) + 1
nLines = SCI_GetLineCount(pSed.hEdit)
' // Add the controls
LOCAL hCtl AS DWORD
hCtl = pWindow.AddGroupBox(hwnd, -1, "", 15, 8, 135, 74, -1)
hCtl = pWindow.AddLabel(hwnd, -1, "Last line:", 18, 20, 68, 13, %WS_CHILD OR %WS_VISIBLE OR %SS_RIGHT)
hCtl = pWindow.AddLabel(hwnd, -1, "", 90, 20, 48, 13, %WS_CHILD OR %WS_VISIBLE OR %SS_RIGHT)
SetWindowText hCtl, FORMAT$(nLines)
hCtl = pWindow.AddLabel(hwnd, -1, "Current line:", 18, 37, 68, 13, %WS_CHILD OR %WS_VISIBLE OR %SS_RIGHT)
hCtl = pWindow.AddLabel(hwnd, -1, "", 90, 37, 48, 13, %WS_CHILD OR %WS_VISIBLE OR %SS_RIGHT)
SetWindowText hCtl, FORMAT$(nLine)
hCtl = pWindow.AddLabel(hwnd, -1, "&Go to line:", 18, 57, 68, 13, %WS_CHILD OR %WS_VISIBLE OR %SS_RIGHT)
hCtl = pWindow.AddTextBox(hwnd, 100, "", 90, 55, 51, 20, %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %ES_NUMBER OR %ES_AUTOHSCROLL, %WS_EX_CLIENTEDGE)
Edit_LimitText(hCtl, LEN(FORMAT$(nLines)))
SetFocus hCtl
hCtl = pWindow.AddButton(hwnd, %IDOK, "&Ok", 165, 13, 75, 23, %BS_DEFPUSHBUTTON)
AfxDisableWindow hCtl
hCtl = pWindow.AddButton(hwnd, %IDCANCEL, "&Cancel", 165, 41, 75, 23, -1)
' // Default message pump
pWindow.DoEvents
END SUB
' ========================================================================================
' ========================================================================================
' Go to line window procedure
' ========================================================================================
FUNCTION CSED_GotoLine_WindowProc (BYVAL hwnd AS DWORD, BYVAL wMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
LOCAL szBuffer AS ASCIIZ * 255
LOCAL nLine AS LONG
SELECT CASE wMsg
CASE %WM_CREATE ' Entrance
EnableWindow GetParent(hwnd), %FALSE ' To make popup dialog modal
CASE %WM_COMMAND
SELECT CASE LO(WORD, wParam)
CASE %IDCANCEL
IF HI(WORD, wParam) = %BN_CLICKED THEN
SendMessage hwnd, %WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
CASE %IDOK
IF HI(WORD, wParam) = %BN_CLICKED THEN
szBuffer = AfxGetWindowText(GetDlgItem(hwnd, 100))
nLine = VAL(szBuffer) - 1
SCI_GotoLine(pSed.hEdit, nLine)
SendMessage hwnd, %WM_CLOSE, 0, 0
EXIT FUNCTION
END IF
CASE 100 ' // Go to line edit control
IF HI(WORD, wParam) = %EN_CHANGE THEN
szBuffer = AfxGetWindowText(GetDlgItem(hwnd, 100))
IF LEN(szBuffer) THEN
IF VAL(szBuffer) > SCI_GetLineCount(pSed.hEdit) THEN
EnableWindow GetDlgItem(hwnd, %IDOK), %FALSE
ELSE
EnableWindow GetDlgItem(hwnd, %IDOK), %TRUE
END IF
ELSE
EnableWindow GetDlgItem(hwnd, %IDOK), %FALSE
END IF
END IF
END SELECT
CASE %WM_CLOSE
' // Maintains parent's zorder
EnableWindow GetParent(hwnd), %TRUE
CASE %WM_DESTROY
' // Close the main window
PostQuitMessage 0
EXIT FUNCTION
END SELECT
FUNCTION = DefWindowProc(hwnd, wMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
A syntax as easy as DDT, but with some goodies as being High DPI aware and smaller executable size.
James--
While the EnableWindow(hParent, OnOffMode) is all you have to do to turn a child popup into a modal one.
I like the WinDev concept of a DDW (Dim Disabled Window) or GFI (Grayed For Input) that has been duplicated and extended into WinLIFT.
More details here (http://doc.windev.com/?9000071)
To see it in action you can try any of these WinLIFT's API
DDW/GFI function :
skCreateDW (IN HWND hParent)
skDestroyDW (OUT HWND &hDW)
Standard modal dialogs
skDialogAlert (IN WCHAR* zCaption, IN WCHAR* zMessage, IN WCHAR* zButton)
skDialogError (IN WCHAR* zCaption, IN WCHAR* zMessage, IN WCHAR* zButton)
skDialogInfo (IN WCHAR* zCaption, IN WCHAR* zMessage, IN WCHAR* zButton)
skDialogYesNo (IN WCHAR* zCaption, IN WCHAR* zMessage, IN WCHAR* zButton)
skDialogInput (IN WCHAR* zCaption, IN WCHAR* zMessage, IN WCHAR* zButton)
And here is the C++ generic SDK code used to create any of the above skDialog Windows.
long skDialog(IN WCHAR* zCaption, IN WCHAR* zMessage, IN WCHAR* zButton, IN long nDialog) {
long nRet = 0;
HWND hWnd = 0, hCtl = 0, hParent = 0, hFocus = 0, hGFI = 0;
long x = 0, K = 0, nWidth = 0, nHeight = 0, nButID = 0, nButtonCount = 0;
HBITMAP hBack = 0, hIcon = 0;
WNDCLASSEX wcx;
wstring sCaption, sButton, sButItem;
tagMSG uMsg = {0};
RECT rc = {0}, rw = {0};
WCHAR zClass[16] = {0};
wcscpy_s(zClass, L"ZDIALOG");
wcx.cbSize = sizeof(wcx);
long IsInitialized = GetClassInfoEx(skInstance(), zClass, &wcx);
if (IsInitialized == 0) {
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = &AlertProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = EXTEND_EXTRA * sizeof(LONG_PTR);
wcx.hInstance = skInstance();
wcx.hIcon = NULL; // LoadIcon(wcx.hInstance, "PROGRAM")
wcx.hCursor = NULL;
wcx.hbrBackground = NULL; // Don't paint the class window background
wcx.lpszMenuName = NULL;
wcx.lpszClassName = zClass;
wcx.hIconSm = NULL;
if (RegisterClassEx(&wcx)) { IsInitialized = TRUE; }
}
if (IsInitialized) {
DlgChoice(0, 1); // Reset the choice.
DlgStringInput($NULL, 1); // Reset string input.
hParent = zMainWindow(0);
if (zCaption) { sCaption = zCaption; }
if (sCaption.length() == 0) { sCaption = zGetCTLText(hParent); }
EnableWindow(hParent, 0); // Disable Mouse and keyboard input
hGFI = skCreateDW(hParent);
SetRect(&rc, 0, 0, CLIENT_SIZEX, CLIENT_SIZEY);
// Note: indeed we don't need AdjustWindowRectEx, because we do not use a non-client area
// but it won't hurt anything to keep it, in case we change our mind ;)
DWORD dwStyle = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION;
DWORD dwExStyle = 0;
AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle);
rc.right = rc.right - rc.left; rc.bottom = rc.bottom - rc.top;
hWnd = CreateWindowEx(dwExStyle, zClass, (WCHAR*) sCaption.c_str(), dwStyle,
0, - 1000, rc.right, rc.bottom, hParent, 0, skInstance(), NULL);
zSetProperty(hWnd, PROP_DLGTYPE, nDialog);
switch (nDialog) {
case DLG_ALERT:
case DLG_ERROR:
hBack = g_DlgAlert;
if (nDialog == DLG_ALERT) { hIcon = g_IconAlert; } else { hIcon = g_IconError; }
break;
case DLG_INFO:
hBack = g_DlgInfo; hIcon = g_IconInfo;
break;
case DLG_YESNO:
case DLG_INPUT:
hBack = g_DlgInput; hIcon = g_IconInput;
break;
}
zSetProperty(hWnd, PROP_BACK, (LONG_PTR) hBack);
zSetProperty(hWnd, PROP_ICON, (LONG_PTR) hIcon);
if (zButton) { sButton = zButton; }
if (sButton.length() == 0) { sButton = L"Ok"; }
nButtonCount = PARSECOUNT(sButton, $COMMA);
if ((nButtonCount == 1) || (nDialog < DLG_YESNO)) {
if (nDialog == DLG_INPUT) {
x = CLIENT_SIZEX - 87; }
else {
x = (CLIENT_SIZEX - 80) / 2;
}
hCtl = CreateWindowEx(0, L"Button", (WCHAR*) PARSE$(sButton, $COMMA, 1).c_str(),
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_CENTER | BS_VCENTER | BS_NOTIFY,
x, 218, 80, 27, hWnd, (HMENU) BTN_OK, skInstance(), NULL);
hFocus = hCtl; }
else if (nButtonCount == 2) {
// if (an option is prefixed with @) { it becomes the default.
x = CLIENT_SIZEX - (87);
nButID = BTN_NO;
long I; K = nButtonCount;
for (I = 0; I < nButtonCount; I++) {
sButItem = PARSE$(sButton, $COMMA, K);
hCtl = CreateWindowEx(0, L"Button", (WCHAR*) LTRIM$(sButItem, L"@").c_str(),
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_CENTER | BS_VCENTER | BS_NOTIFY,
x, 218, 80, 27, hWnd, (HMENU) nButID, skInstance(), NULL);
if (ASC(sButItem, 1) == 64) { hFocus = hCtl; }
x -= 86;
nButID -= 1;
K -= 1;
}
}
if (nDialog == DLG_INPUT) {
dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL;
x = CLIENT_SIZEX - (80 + 13 + 216);
hCtl = CreateWindowEx(0, L"EDIT", NULL, dwStyle, x, 220, 216, 22, hWnd, (HMENU) TXT_INPUT, skInstance(), NULL);
hFocus = hCtl;
}
hCtl = CreateWindowEx(0, L"STATIC", zMessage, WS_CHILD, 0, -1000, 0, 0, hWnd, (HMENU) LIB_Message, skInstance(), NULL);
skSkinWindow(hWnd, L"Dock|Undock|Minimize|Maximize|Restore|Close");
if (nDialog == DLG_INPUT) { SendMessage(hFocus, WM_SETFONT, (WPARAM) skFontDlg(), 0); }
if (hFocus) { SetFocus(hFocus); }
// Center the g_Child popup in the parent window.
GetWindowRect(hParent, &rw); nWidth = rw.right - rw.left; nHeight = rw.bottom - rw.top;
GetWindowRect(hWnd, &rc); rc.right = rc.right - rc.left; rc.bottom = rc.bottom - rc.top;
MoveWindow(hWnd, rw.left + (nWidth - rc.right) / 2, rw.top + (nHeight - rc.bottom) / 2, rc.right, rc.bottom, 0);
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
while (GetMessage(&uMsg, NULL, 0, 0)) {
TranslateMessage(&uMsg);
DispatchMessage(&uMsg);
}
skDestroyDW(hGFI);
if (nDialog == DLG_YESNO) {
if (DlgChoice(0, 0) == BTN_YES) { nRet = -1; } else { nRet = 0; }
}
else {
nRet = (long) uMsg.wParam;
}
}
return nRet;
}
The beauty of the DDW is that it works with any window shape (region) and even DirectX and OpenGL.
...
Mixing Sdk and resource script dialogs is what I do. I almost never create menus or pure input box type dialogs with just code. So most of my apps have a *.rc file. Don't know if that helps James.
You know, it was several years ago I found some code of Jose's that really interested me, where he showed how to fully create a modal dialog box with just code. It wasn't his CWindow Class based code, but older Sdk. It may have been 5 years ago or more. What I found most interesting about it was a second Message Pump in the code to strictly handle the Dialog Box.
I believe I used an adaptation of that (Jose's code) in my C/C++ Tutorial thingie on command line compiling with the GNU compilers. Yes, I just checked. Its here ...
http://www.jose.it-berater.org/smfforum/index.php?topic=4611.0
QuoteWhat I found most interesting about it was a second Message Pump in the code to strictly handle the Dialog Box.
Each modal child popup should always have their own message pump altogether with the use of EnableWindow to disable/enable their parent, that's the easiest way of doing it.
...