• Welcome to Jose's Read Only Forum 2023.
 

SDK modal window

Started by James C. Fuller, July 21, 2014, 01:25:08 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

James C. Fuller

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


Patrice Terrier

#1
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.

...
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

José Roca

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.

Patrice Terrier

#3
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


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.

...
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Frederick J. Harris

#4
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


Patrice Terrier

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.

...
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com