Author Topic: Using Dynamic Dialogs  (Read 3197 times)

0 Members and 1 Guest are viewing this topic.

Arnold

  • Hero Member
  • *****
  • Posts: 700
Using Dynamic Dialogs
« on: January 02, 2018, 01:53:37 AM »
Hi Charles,

the new year has begun just in time to start a new project. Maybe it will be good enough some time to get a home in the Oxygenbasic\inc folder.
When I found that Oxygenbasic can deal very well with resources from a .rc file, I got interested in dialogs created during runtime in memory using templates. I had access to these readings:

Win32.chm (Dialog Boxes)
MASM32 SDK (dialogs.inc)
and this link of the Freebasic Forum was very helpful:
https://www.freebasic.net/forum/viewtopic.php?t=5667

The trick is to find a way to create a sequence which contains various items and arrays of variable length  and to respect some word and dword boundaries. I have started to create a basic dialogs.inc for Oxygenbasic based on the code of MichaelW, which should be improved and completed with some more constants and winapi functions but I think it already works quite nice.

Perhaps there is a more elegant way to create the sequence with the members and strings of variable length? This is something of general interest, not only for DLGTEMPLATE and DLGITEMTEMPLATE extended structures.
I used getmemory for allocating space. Would it be possible to allocate the memory space in sub Dialog()? I did not manage to do this. Would it also be possible to resize the allocated space, similar to the WinApi function GlobalReAlloc? This would be interesting in function CreateModalDialog, CreateModelessDialog.

Dealing with in-memory dialogs is a very interesting matter and there would be some advantages. I am curious how far dialogs.inc could be extended.

Roland
« Last Edit: March 19, 2018, 12:57:03 AM by Arnold »

Arnold

  • Hero Member
  • *****
  • Posts: 700
Re: Using Dynamic Dialogs
« Reply #1 on: January 02, 2018, 02:03:48 AM »
Here are some small examples for dialogs.inc (will be continued)

Edit: adapted for modified dialogs.inc

SimpleModal.o2bas:
Code: OxygenBasic
  1. '====================================================================
  2. ' Simple modal dialog as main.
  3. '====================================================================
  4.  
  5. '% review
  6.  
  7. uses dialogs
  8. namespace
  9.  
  10. ==============================================
  11.  
  12. 'MAIN CODE
  13. =============================================
  14.  
  15. 'dim nCmdline as asciiz ptr, hInstance as sys
  16. '&nCmdline = GetCommandLine
  17. 'hInstance = GetModuleHandle(NULL)
  18.  
  19.  
  20. function DialogProc( sys hDlg, uint uMsg, sys wParam, lParam ) as int callback
  21.   select case uMsg
  22.  
  23.     case WM_INITDIALOG
  24.       return true
  25.  
  26.     case WM_COMMAND
  27.       select case loword(wParam)
  28.         case IDCANCEL
  29.            EndDialog( hDlg, null )
  30.       end select
  31.      
  32.     case WM_CLOSE
  33.       EndDialog( hDlg, null )
  34.                
  35.   end select
  36.  
  37.   return 0
  38. end function
  39.  
  40. sub winmain()
  41.  
  42.   sys lpdt
  43.  
  44.   'provide memory for DLGTEMPLATE structure etc    
  45. '  dyn::init(lpdt,nBytes)
  46.  dyn::init(lpdt) '1024
  47.  
  48.   dyn::Dialog( 1,  0, 0, 200, 100, "Memory based modal dialog using OxygenBasic", lpdt,
  49.           WS_OVERLAPPEDWINDOW or DS_CENTER )
  50.   dyn::PushButton( "Close" , IDCANCEL, 80, 70, 40, 12 )
  51.  
  52.   dyn::CreateModalDialog( null, @DialogProc, 0, lpdt )
  53. end sub
  54.  
  55. winmain()
  56.  

NestedModal.o2bas:
Code: OxygenBasic
  1. '====================================================================
  2. ' Nested modal dialog demo, modal dialog as main.
  3. '====================================================================
  4.  
  5. '%review
  6.  
  7. uses dialogs
  8. namespace
  9.  
  10.  
  11. function NestedDialogProc( sys hDlg, uint uMsg, sys wParam, lParam) as sys callback
  12.   select case uMsg
  13.  
  14.     case WM_COMMAND
  15.       if loword(wParam) = IDCANCEL then
  16.         EndDialog( hDlg, null )
  17.       end if
  18.  
  19.     case WM_CLOSE
  20.       EndDialog( hDlg, null )
  21.  
  22.   end select
  23.  
  24.   return 0
  25. end function
  26.  
  27. '====================================================================
  28.  
  29. function MainDialogProc( sys hDlg, uint uMsg, sys wParam, lParam ) as sys callback
  30.  
  31.   select case uMsg
  32.  
  33.     case WM_COMMAND
  34.  
  35.       select case loword(wParam)
  36.         case 100
  37.           sys lpdt
  38.           dyn::init(lpdt)
  39.          
  40.           dyn::Dialog( 1, 0, 0, 120, 90, "Modal Nested Dialog", lpdt,
  41.                        WS_OVERLAPPED or WS_SYSMENU or DS_CENTER )
  42.           dyn::DefPushButton( "Close", IDCANCEL, -1, 60, 40, 12  )
  43.           dyn::CreateModalDialog( hDlg, @NestedDialogProc, 0, lpdt )
  44.  
  45.         case IDCANCEL
  46.           EndDialog( hDlg, null )
  47.  
  48.       end select
  49.  
  50.     case WM_CLOSE
  51.       EndDialog( hDlg, null )
  52.  
  53.   end select
  54.  
  55.   return 0
  56. end function
  57.  
  58. '====================================================================
  59.  
  60. sys lpdt
  61. dyn::init(lpdt)
  62.  
  63. dyn::Dialog( 2,  0, 0, 150, 100, "Main Dialog", lpdt,
  64.              WS_OVERLAPPED or WS_SYSMENU or DS_CENTER )
  65.  
  66. dyn::DefPushButton( "Go", 100, 30, 70, 40, 12 )
  67. dyn::PushButton( "Close", IDCANCEL, 80, 70, 40, 12 )
  68.  
  69. dyn::CreateModalDialog( 0, @MainDialogProc, 0, lpdt )
  70.  
  71. '====================================================================
  72.  

NestedModeless.o2bas:
Code: OxygenBasic
  1. '====================================================================
  2. ' Nested modeless dialog demo, modeless dialog as main.
  3. '====================================================================
  4.  
  5. '#define review
  6.  
  7. uses dialogs
  8. namespace
  9.  
  10.  
  11. sys g_hNestedDlg
  12.  
  13.  
  14. function NestedDialogProc( sys hDlg, uint uMsg, sys wParam, lParam) as sys callback
  15.   select case uMsg
  16.  
  17.     case WM_COMMAND
  18.       if loword(wParam) = IDCANCEL then
  19.         DestroyWindow( hDlg )
  20.       end if
  21.  
  22.     case WM_CLOSE
  23.       DestroyWindow( hDlg )
  24.  
  25.       'Reset so main can determine if nested dialog is open.
  26.      g_hNestedDlg = 0
  27.  
  28.   end select
  29.  
  30.   return 0
  31. end function
  32.  
  33. '====================================================================
  34.  
  35. function MainDialogProc( sys hDlg, uint uMsg, sys wParam, lParam ) as sys callback
  36.   select case uMsg
  37.  
  38.     case WM_COMMAND
  39.  
  40.       select case loword(wParam)
  41.         case 100
  42.           'Open nested dialog only if not already open.
  43.          if IsWindow( g_hNestedDlg ) = 0 then
  44.             sys lpdt
  45.             dyn::init(lpdt)
  46.            
  47.             dyn::Dialog( 1, 0, 0, 120, 90, "Modeless Nested Dialog", lpdt,
  48.                          WS_OVERLAPPED or WS_SYSMENU or DS_CENTER or WS_VISIBLE )
  49.             dyn::DefPushButton( "Close", IDCANCEL, -1, 60, 40, 12 )
  50.             g_hNestedDlg = dyn::CreateModelessDialog( hDlg, @NestedDialogProc, 0, lpdt )
  51.           end if
  52.  
  53.         case IDCANCEL
  54.           DestroyWindow( hDlg )
  55.  
  56.       end select
  57.  
  58.     case WM_CLOSE
  59.       DestroyWindow( hDlg )
  60.  
  61.     case WM_DESTROY
  62.       PostQuitMessage( null )
  63.  
  64.   end select
  65.  
  66.   return 0
  67. end function
  68.  
  69. '====================================================================
  70.  
  71. sys lpdt
  72. dyn::init(lpdt)
  73.  
  74. sys hDlg
  75. MSG wMsg
  76.  
  77. dyn::Dialog( 2, 0, 0, 150, 100, "Main Dialog", lpdt,
  78.         WS_OVERLAPPED or WS_SYSMENU or DS_CENTER or WS_VISIBLE )
  79.  
  80. dyn::DefPushButton(  "Go", 100, 30, 70, 40, 12 )
  81. dyn::PushButton( "Close", IDCANCEL, 80, 70, 40, 12 )
  82.  
  83. hDlg = dyn::CreateModelessDialog( 0, @MainDialogProc, 0, lpdt )
  84.  
  85. while GetMessage( @wMsg, null, 0, 0 ) <> 0
  86.   if IsDialogMessage( hDlg,  @wMsg ) = 0 then
  87.     if IsDialogMessage( g_hNestedDlg,  @wMsg ) = 0 then
  88.       TranslateMessage( @wMsg )
  89.       DispatchMessage( @wMsg )
  90.     end if
  91.   end if
  92. wend
  93.  
  94. '====================================================================
  95.  

ButtonGrid.o2bas:
Code: OxygenBasic
  1. '====================================================================
  2. ' Button grid demo, modal dialog as main.
  3. '====================================================================
  4. ' Minesweeper is next
  5.  
  6. '% review
  7.  
  8. uses dialogs
  9. namespace
  10.  
  11.  
  12. function DialogProc( sys hDlg, uint uMsg, sys wParam, lParam) as sys callback
  13.  
  14.   sys id, event
  15.   static int state[100]
  16.  
  17.   select case uMsg
  18.            
  19.     case WM_COMMAND
  20.       id=loword(wParam)
  21.       event=hiword(wParam)
  22.  
  23.       if id=IDCANCEL then EndDialog( hDlg, null)
  24.          
  25.       if event = BN_CLICKED then      
  26.         if state[id+100] then
  27.           SetDlgItemText( hDlg, id, "" )
  28.           state[id+100] = 0
  29.         else
  30.           SetDlgItemText( hDlg, id, "X" )
  31.           state[id+100] = 1
  32.         end if
  33.       end if
  34.        
  35.     case WM_CLOSE
  36.       EndDialog( hDlg, null )
  37.    
  38.   end select
  39.  
  40.   return 0
  41. end function
  42.  
  43. '====================================================================
  44.  
  45. 'must supply sufficient space
  46. sys lpdt
  47. 'dyn::init(lpdt)
  48. dyn::init(lpdt, 3650)
  49.  
  50. short id, r, c
  51.  
  52. dyn::Dialog( 100, 0, 0, 122, 130, "Button Grid Demo", lpdt,
  53.              WS_OVERLAPPED or WS_SYSMENU or DS_CENTER )
  54.  
  55. id = 100
  56. for c = 10 to 100 step 10
  57.   for r = 10 to 100 step 10
  58.     if id > 100 then
  59.       dyn::PushButton( "", id, r, c, 9, 9)
  60.     else
  61.       dyn::DefPushButton( "", id, r, c, 9, 9)
  62.     end if
  63.     id += 1
  64.   next
  65. next
  66.  
  67. dyn::CreateModalDialog( 0, @DialogProc, 0, lpdt )
  68.  
  69. '====================================================================
  70.  
« Last Edit: January 20, 2018, 01:06:53 AM by Arnold »

Arnold

  • Hero Member
  • *****
  • Posts: 700
Re: Using Dynamic Dialogs
« Reply #2 on: January 02, 2018, 02:36:35 AM »
These are some more extended examples:

Edit: adapted for modified dialogs.inc

Statusbar.o2bas:
Code: OxygenBasic
  1. '====================================================================
  2. ' Status bar demo, modal dialog as main.
  3. '====================================================================
  4.  
  5. '% review
  6.  
  7. uses dialogs
  8. namespace
  9.  
  10.  
  11. function DialogProc( sys hDlg, uint uMsg, sys wParam, lParam) as sys callback
  12. indexbase 0
  13.  
  14.   static sys hWndSB
  15.   static RECT rcDlg, rcSB
  16.  
  17.       hWndSB = GetDlgItem( hDlg, 100 )
  18.      
  19.   select case uMsg
  20.  
  21.     case WM_INITDIALOG
  22.  
  23.       int widths[3]
  24.  
  25.       hWndSB = GetDlgItem( hDlg, 100 )
  26.  
  27.       GetClientRect( hDlg, @rcDlg )
  28.       widths[0] = rcDlg.right \ 4
  29.       widths[1] = rcDlg.right * 2 \ 4
  30.       widths[2] = rcDlg.right * 3 \ 4
  31.       widths[3] = -1              '' part extends to window border
  32.  
  33.       SendMessage( hWndSB, SB_SETPARTS, 4, @widths )
  34.  
  35.       return true
  36.  
  37.     case WM_SIZE
  38.  
  39.       int sbHeight
  40.       string status
  41.  
  42.       GetClientRect( hDlg, @rcDlg )
  43.       GetWindowRect( hWndSB, @rcSB )
  44.       sbHeight = rcSB.bottom - rcSB.top + 1
  45.  
  46.       MoveWindow( hWndSB, 0, rcDlg.bottom - sbHeight ,
  47.                   loword(lParam), rcDlg.bottom - sbHeight, false )
  48.  
  49.       '' For a simple status bar with just one part,
  50.      '' the text can be set with WM_SETTEXT.
  51.      ''
  52.  
  53.       status = " " & str(rcDlg.right) & "x" & str(rcDlg.bottom)
  54.       SendMessage( hWndSB,SB_SETTEXT,0, status)
  55.       SendMessage( hWndSB,SB_SETTEXT,1," part 1")
  56.       SendMessage( hWndSB,SB_SETTEXT,2," part 2")
  57.       SendMessage( hWndSB,SB_SETTEXT,3," part 3 ")
  58.  
  59.       rcDlg.bottom -= sbHeight
  60.       InvalidateRect( hDlg, @rcDlg, true )
  61.  
  62.     case WM_CLOSE
  63.  
  64.       EndDialog( hDlg, null )
  65.  
  66.   end select
  67.  
  68.   return 0
  69. end function
  70.  
  71. '====================================================================
  72.  
  73. sys lpdt : dyn::init(lpdt)
  74.  
  75. dyn::init_common_controls()
  76.  
  77. dyn::Dialog( 1, 0, 0, 120, 90, "Status Bar Demo", lpdt,
  78.         WS_OVERLAPPEDWINDOW or DS_CENTER )
  79.  
  80. ' Instead of trying to anticipate the position and/or size of
  81. ' the controls, just use zeros and set the correct values in
  82. ' the WM_SIZE handler.
  83. '
  84. dyn::Control( "", 100, STATUSCLASSNAME, 0, 0, 0, 0 )
  85.  
  86. dyn::CreateModalDialog( null, @DialogProc, 0, lpdt )
  87.  
  88. '====================================================================
  89.  

Toolbar.o2bas:
Code: OxygenBasic
  1. '====================================================================
  2. ' Toolbar demo, with tooltips, modeless dialog as main.
  3. '====================================================================
  4.  
  5. '% review
  6.  
  7. uses dialogs
  8. namespace
  9.  
  10.  
  11. % HINST_COMMCTRL=-1
  12. % IDB_STD_SMALL_COLOR=0
  13. % STD_FILENEW=6
  14. % STD_FILEOPEN=7
  15. % STD_FILESAVE=8
  16. % STD_FIND=12
  17. % STD_COPY=1
  18. % STD_CUT=0
  19. % STD_PASTE=2
  20. % STD_PRINT=14
  21. % TBSTATE_ENABLED=4
  22. % TBSTYLE_BUTTON=0
  23. % TBSTYLE_SEP=1
  24. % TBSTYLE_TOOLTIPS=0x100
  25. % TBSTYLE_FLAT=0x800
  26. % TTF_DI_SETITEM=0x8000
  27. % TB_ADDBITMAP=0x413
  28. % TB_ADDBUTTONS=0x414
  29. % TB_BUTTONSTRUCTSIZE=0x41E
  30. % TB_GETTOOLTIPS=0x423
  31. % TTN_GETDISPINFO=  -520
  32.  
  33.  
  34. type TBADDBITMAP
  35.   sys       hInst
  36.   sys  nID
  37. end type
  38.  
  39. type TBBUTTON
  40.   int       iBitmap
  41.   int       idCommand
  42.   BYTE      fsState
  43.   BYTE      fsStyle
  44.   dword     dwData
  45.   sys       iString
  46. end type
  47.  
  48. type NMTTDISPINFO
  49.   NMHDR     hdr
  50.   char*     lpszText
  51.   zstring   szText[80]
  52.   sys       hinst
  53.   uint      uFlags
  54.   sys       lParam
  55. end type
  56. typedef NMTTDISPINFO TOOLTIPTEXT  
  57.  
  58.  
  59. '====================================================================
  60.  
  61. function DialogProc( sys hDlg, uint uMsg, sys wParam, lParam) as sys callback
  62. indexbase 0
  63.  
  64.   static sys hTT
  65.   static sys hWndTB
  66.  
  67.   select case uMsg
  68.  
  69.     case WM_INITDIALOG
  70.  
  71.       dim as TBADDBITMAP tbab
  72.       dim as TBBUTTON tbb[6]
  73.  
  74.       hWndTB = GetDlgItem( hDlg, 100 )
  75.  
  76.       ' Specify the structure size so the system can determine
  77.      ' which version of the common control DLL is being used.
  78.      '
  79.      SendMessage( hWndTB,TB_BUTTONSTRUCTSIZE,sizeof(TBBUTTON),0 )
  80.  
  81.       ' Add the system-defined bitmap button images to the
  82.      ' list of available images.
  83.      '
  84.      tbab.hInst = HINST_COMMCTRL
  85.       tbab.nID = IDB_STD_SMALL_COLOR
  86.       SendMessage( hWndTB, TB_ADDBITMAP, 0, @tbab)
  87.  
  88.       ' For each button, specify the button image index,
  89.      ' the associated command identifier, and the button
  90.      ' state and style.
  91.      '
  92.      tbb(0).iBitmap = STD_FILENEW
  93.       tbb(0).idCommand = 1000
  94.       tbb(0).fsState = TBSTATE_ENABLED
  95.       tbb(0).fsStyle = TBSTYLE_BUTTON
  96.       tbb(1).iBitmap = STD_FILEOPEN
  97.       tbb(1).idCommand = 1001
  98.       tbb(1).fsState = TBSTATE_ENABLED
  99.       tbb(1).fsStyle = TBSTYLE_BUTTON
  100.       tbb(2).iBitmap = STD_FILESAVE
  101.       tbb(2).idCommand = 1002
  102.       tbb(2).fsState = TBSTATE_ENABLED
  103.       tbb(2).fsStyle = TBSTYLE_BUTTON
  104.       tbb(3).iBitmap = 0
  105.       tbb(3).fsState = TBSTATE_ENABLED
  106.       tbb(3).fsStyle = TBSTYLE_SEP
  107.       tbb(4).iBitmap = STD_CUT
  108.       tbb(4).idCommand = 1003
  109.       tbb(4).fsState = TBSTATE_ENABLED
  110.       tbb(4).fsStyle = TBSTYLE_BUTTON
  111.       tbb(5).iBitmap = STD_COPY
  112.       tbb(5).idCommand = 1004
  113.       tbb(5).fsState = TBSTATE_ENABLED
  114.       tbb(5).fsStyle = TBSTYLE_BUTTON
  115.       tbb(6).iBitmap = STD_PASTE
  116.       tbb(6).idCommand = 1005
  117.       tbb(6).fsState = TBSTATE_ENABLED
  118.       tbb(6).fsStyle = TBSTYLE_BUTTON
  119.  
  120.       ' Add the buttons to the toolbar.
  121.      '
  122.      SendMessage( hWndTB,TB_ADDBUTTONS,7, @tbb )
  123.  
  124.       ' Get the handle to the ToolTip control associated
  125.      ' with the toolbar (by the TBSTYLE_TOOLTIPS style).
  126.      '
  127.      hTT = SendMessage( hWndTB,TB_GETTOOLTIPS,0,0 )
  128.  
  129.       return true
  130.  
  131.     case WM_COMMAND
  132.  
  133.       select case loword(wParam)
  134.         case IDCANCEL
  135.            DestroyWindow(hDlg)
  136.         case 1000
  137.           MessageBox( hDlg, "New", "", 0 )
  138.         case 1001
  139.           MessageBox( hDlg, "Open", "", 0 )
  140.         case 1002
  141.           MessageBox( hDlg, "Save", "", 0 )
  142.         case 1003
  143.           MessageBox( hDlg, "Cut", "", 0 )
  144.         case 1004
  145.           MessageBox( hDlg, "Copy", "", 0 )
  146.         case 1005
  147.           MessageBox( hDlg, "Paste", "", 0 )
  148.       end select
  149.  
  150.     case WM_NOTIFY
  151.  
  152.       NMHDR pnm at lParam
  153.  
  154.       ' This necessary because TTN_GETDISPINFO is not
  155.      ' fully defined in commctrl.bi (0.16b stable).
  156.      '
  157. '      #define _TTN_GETDISPINFO -520
  158.  
  159.       if pnm.hwndFrom = hTT then
  160.         if pnm.code = TTN_GETDISPINFO then
  161.  
  162.           TOOLTIPTEXT pdi at lParam
  163. '          TOOLTIPTEXT *pdi
  164. '          @pdi = lParam
  165.  
  166.           ' Now know that pnm is actually pdi, and the ToolTip
  167.          ' control is requesting information that it needs to
  168.          ' display a tooltip. Note that the hdr member of the
  169.          ' TOOLTIPTEXT structure is a NMHDR structure.
  170.          '
  171.          select case pdi.hdr.idFrom
  172.             case 1000
  173.               pdi.szText = "New"
  174.             case 1001
  175.               pdi.szText = "Open"
  176.             case 1002
  177.               pdi.szText = "Save"
  178.             case 1003
  179.               pdi.szText = "Cut"
  180.             case 1004
  181.               pdi.szText = "Copy"
  182.             case 1005
  183.               pdi.szText = "Paste"
  184.           end select
  185.  
  186.           ' This causes the ToolTip control to retain
  187.          ' the information after the first request.
  188.          '
  189.          pdi.uFlags = pdi.uFlags or TTF_DI_SETITEM
  190.  
  191.         end if
  192.       end if
  193.  
  194.     case WM_SIZE
  195.  
  196.       RECT rcTB
  197.  
  198.       GetWindowRect( hWndTB, @rcTB )
  199.  
  200.       MoveWindow( hWndTB, 0, 0, loword(lParam), _
  201.                   rcTB.bottom - rcTB.top + 1, false )
  202.  
  203.     case WM_CLOSE
  204.  
  205.       DestroyWindow( hDlg )
  206.  
  207.     case WM_DESTROY
  208.  
  209.       PostQuitMessage( null )
  210.  
  211.   end select
  212.  
  213.   return 0
  214.  
  215. end function
  216.  
  217. '====================================================================
  218.  
  219. sys lpdt : dyn::init(lpdt)
  220.  
  221. sys hDlg
  222. MSG wMsg
  223.  
  224. dyn::init_common_controls()
  225.  
  226. dyn::Dialog( 1, 0, 0, 120, 90, "Toolbar Demo", lpdt,
  227.         WS_OVERLAPPEDWINDOW or DS_CENTER or WS_VISIBLE )
  228.  
  229. ' Instead of trying to anticipate the position and/or size of
  230. ' the controls, just use zeros and set the correct values in
  231. ' the WM_SIZE handler.
  232. '
  233. dyn::Control( "", 100, TOOLBARCLASSNAME, TBSTYLE_TOOLTIPS or TBSTYLE_FLAT, 0, 0, 0, 0 )
  234.  
  235. hDlg = dyn::CreateModelessDialog( 0, @DialogProc, 0, lpdt )
  236.  
  237. while GetMessage( @wMsg, null, 0, 0 ) <> 0
  238.   if IsDialogMessage( hDlg,  @wMsg ) = 0 then
  239.     TranslateMessage( @wMsg )
  240.     DispatchMessage( @wMsg )
  241.   end if
  242. wend
  243.  
  244. '====================================================================
  245.  

TabControl.o2bas:
Code: OxygenBasic
  1. 'modified from a fsw example in the FBEdit samples
  2.  
  3. '% review
  4.  
  5. uses dialogs
  6. namespace
  7.  
  8. % IDD_DLG0 1000
  9. % IDC_BTN1 1002
  10. % IDC_TAB1 1001
  11. % IDD_TAB1 1100
  12. % CHK_1 1101
  13. % EDT_1 1102
  14. % IDD_TAB2 1200
  15. % CHK_2 1201
  16. % CHK_3 1202
  17. % EDT_2 1001
  18. % EDT_3 1002
  19. % EDT_4 1003
  20.  
  21.  
  22. % TCIF_PARAM=8
  23. % TCIF_TEXT=1
  24. % TCM_GETITEM=0x1305
  25. % TCM_INSERTITEM=0x1307
  26. % TCM_GETCURSEL=0x130B
  27. % TCN_SELCHANGE=   -551
  28. % TCN_SELCHANGING= -552
  29.  
  30. type TCITEM
  31.   int   mask,dwState,dwStateMask
  32.   char* pszText
  33.   int   cchTextMax,iImage
  34.   sys   lParam
  35. end type
  36. typedef TCITEM TC_ITEM
  37.  
  38. '=================================================
  39.  
  40. sys hInstance
  41.  
  42.  
  43. function DialogProc( sys hDlg, uint uMsg, sys wParam, lParam) as sys callback
  44.     return FALSE
  45. end function
  46.  
  47. function Tab1Proc( sys hDlg, uint uMsg, sys wParam, lParam) as bool callback
  48.     return FALSE
  49. end function
  50.  
  51. function Tab2Proc( sys hDlg, uint uMsg, sys wParam, lParam) as bool callback
  52.     return FALSE
  53. end function
  54.  
  55.  
  56. function DlgProc( sys hDlg, uint uMsg, sys wParam, lParam) as bool callback
  57.     sys id, event
  58.         TCITEM ts
  59.     NMHDR lpNMHDR
  60.     sys hTab
  61.  
  62.     select case uMsg
  63.        case WM_INITDIALOG
  64.           sys lpdt1 : dyn::init(lpdt1)
  65.           sys lpdt2 : dyn::init(lpdt2)
  66.  
  67.           'Get handle of tabstrip
  68.          hTab=GetDlgItem(hDlg,IDC_TAB1)
  69.           ts.mask=TCIF_TEXT Or TCIF_PARAM
  70.  
  71.           'Create Tab1 child dialog
  72.          ts.pszText="Tab1"
  73.           dyn::Dialog ( 2, 6,25,200,103, "Tab1", lpdt1, WS_CHILD or WS_VISIBLE)
  74.           dyn::Autocheckbox ("Checkbox", CHK_1, 14,38,172,9, &h50010003)
  75.           dyn::Edittext     ("Edit", EDT_1, 14,49,172,13, &h50010000, WS_EX_CLIENTEDGE)
  76.           ts.lparam = dyn::Createmodelessdialog(hdlg,@Tab1Proc,0, lpdt1)
  77.  
  78.           SendMessage(hTab,TCM_INSERTITEM,0, @ts)
  79.  
  80.           ' Create Tab2 child dialog
  81.          ts.pszText="Tab2"
  82.           dyn::Dialog ( 3, 6,25,200,103, "Tab2", lpdt2,WS_CHILD)
  83.           dyn::Edittext ("edit1", EDT_2, 8,55,174,13, &h50010000, WS_EX_CLIENTEDGE)
  84.           dyn::Edittext ("edit2", EDT_3, 8,73,174,13, &h50010000)
  85.           dyn::Edittext ("edit3", EDT_4, 8,92,174,13, &h50010000, WS_EX_CLIENTEDGE)
  86.           ts.lParam= dyn::Createmodelessdialog(hdlg,@Tab2Proc,0, lpdt2)
  87.           SendMessage(htab,TCM_INSERTITEM,1, @ts)
  88.  
  89.        case WM_NOTIFY
  90.           NMHDR lpNMHDR at lParam
  91.              if lpNMHDR.code=TCN_SELCHANGING Then
  92.                ' Hide the currently selected dialog
  93.               id=SendMessage(lpNMHDR.hwndFrom,TCM_GETCURSEL,0,0)
  94.                ts.mask=TCIF_PARAM
  95.                SendMessage(lpNMHDR.hwndFrom,TCM_GETITEM,id, @ts)
  96.                ShowWindow(ts.lParam,SW_HIDE)
  97.              elseif lpNMHDR.code=TCN_SELCHANGE Then
  98.                ' Show the currently selected dialog
  99.               id=SendMessage(lpNMHDR.hwndFrom,TCM_GETCURSEL,0,0)
  100.                ts.mask=TCIF_PARAM
  101.                SendMessage(lpNMHDR.hwndFrom,TCM_GETITEM,id, @ts)
  102.                ShowWindow(ts.lParam,SW_SHOW)
  103.              endif
  104.      '
  105.       case WM_CLOSE
  106.           EndDialog(hDlg, 0)
  107.      '
  108.       case WM_COMMAND
  109.           id=loword(wParam)
  110.           event=hiword(wParam)
  111.           select case id
  112.              case IDC_BTN1
  113.                 EndDialog(hDlg, 0)
  114.  
  115.              case IDCANCEL
  116.                 ' This allows user to close dialog
  117.                ' with Escape key;
  118.                EndDialog(hDlg, 0)
  119.           end select
  120.      '
  121.       case else
  122.           return FALSE
  123.  
  124.     end select
  125.  
  126.     return TRUE
  127. end function
  128.  
  129. '============================================================
  130.  
  131. dyn::init_common_controls()
  132.  
  133. 'hInstance=GetModuleHandle(NULL)
  134.  
  135. sys lpdt : dyn::init(lpdt)
  136.  
  137. 'WS_CLIPSIBLINGS required for parent window and tab control.
  138. dyn::Dialog(2, 6,6,248,188, "Tab Demo",  lpdt,
  139.        WS_OVERLAPPED Or WS_SYSMENU Or DS_CENTER or WS_CLIPSIBLINGS or DS_SETFONT, 10, "MS Sans Serif" )
  140.  
  141. ' Base style defined in Control procedure is WS_CHILD or WS_VISIBLE.
  142. dyn::Control( "", IDC_TAB1,  WC_TABCONTROL, WS_CLIPSIBLINGS, 2, 3, 244, 157 )
  143.  
  144. dyn::PushButton( "Cancel", IDC_BTN1, 176,160,64,17, WS_TABSTOP )
  145.  
  146. dyn::CreateModalDialog( 0, @DlgProc, 0, lpdt)
  147.  
  148. '============================================================
  149.  

Calendar.o2bas:
Code: OxygenBasic
  1. '====================================================================
  2. ' MONTHCAL_CLASS common control demo, modal dialog as main.
  3. '====================================================================
  4. ' a little bit modified
  5.  
  6. '% review
  7.  
  8. uses dialogs
  9. namespace
  10.  
  11. macro MonthCal_SetColor(hmc,iColor,clr) (SendMessaGE(hmc,MCM_SETCOLOR,iColor,clr))
  12.  
  13. % MCM_SETCOLOR=0x100A
  14. % MCSC_BACKGROUND=0
  15. % MCSC_MONTHBK=4
  16. % MCSC_TEXT=1
  17. % MCSC_TITLEBK=2
  18. % MCSC_TITLETEXT=3
  19. % MCSC_TRAILINGTEXT=5
  20.  
  21. '====================================================================
  22.  
  23. function DialogProc( sys hDlg, uint uMsg, sys wParam, lParam ) as int callback
  24.   sys calendar = GetDlgItem(hDlg,100)
  25.  
  26.   select case uMsg
  27.  
  28.     case WM_INITDIALOG
  29.        MonthCal_SetColor(calendar, MCSC_TITLETEXT    , RGB(0,0,0))
  30.        MonthCal_SetColor(calendar, MCSC_TITLEBK      , RGB(245,175,0))
  31.        MonthCal_SetColor(calendar, MCSC_BACKGROUND   , RGB(175,175,175))
  32.        MonthCal_SetColor(calendar, MCSC_MONTHBK      , RGB(248,245,225))
  33.        MonthCal_SetColor(calendar, MCSC_TEXT         , RGB(0,0,225))
  34.        MonthCal_SetColor(calendar, MCSC_TRAILINGTEXT , RGB(0,225,0))
  35.    
  36.     case WM_COMMAND
  37.        if loword(wParam) = IDCANCEL then
  38.          EndDialog( hDlg, null )
  39.        end if
  40.  
  41.     case WM_CLOSE
  42.        EndDialog( hDlg, null )
  43.  
  44.     case WM_SIZE
  45.        cxClient = LOWORD (lParam)
  46.        cyClient = HIWORD (lParam)
  47.        SetWindowPos( calendar, 0, 0, 0, cxClient, cyClient, SWP_NOMOVE or SWP_NOREDRAW )
  48.  
  49.   end select
  50.  
  51.   return 0
  52. end function
  53.  
  54. '====================================================================
  55.  
  56. sys lpdt
  57. dyn::init(lpdt)
  58.  
  59. dyn::init_common_controls()
  60.  
  61. dyn::Dialog( 1, 0, 0, 120, 110, "Nice Calendar", lpdt,
  62.         WS_OVERLAPPEDWINDOW or DS_CENTER )
  63. dyn::Control( "", 100,  MONTHCAL_CLASS, 0, -1, 0, 120, 100 )
  64.  
  65. dyn::CreateModalDialog( 0, @DialogProc, 0, lpdt )
  66.  
  67. '====================================================================
  68.  
« Last Edit: January 20, 2018, 01:09:42 AM by Arnold »

Arnold

  • Hero Member
  • *****
  • Posts: 700
Re: Using Dynamic Dialogs
« Reply #3 on: January 03, 2018, 02:47:53 AM »
Hi Charles,

I assume the new release of Oxygen is a bit more stringent with syntax / semantics checking which is certainly helpful. If I modify in dialogs.inc:

macro make_ustring(text,memptr) 
  int count = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED,
                               text,
                               -1,
                               memptr,
                               len(text)+1 )
  memptr += count*2
end macro

everything would work as expected. Is this approach ok?

Roland

Charles Pegge

  • Admin Support Member
  • *****
  • Posts: 3837
    • Oxygen Basic
Re: Using Dynamic Dialogs
« Reply #4 on: January 03, 2018, 05:03:04 AM »
Hi Roland,

I have not had the opportunitiy to try your code yet, but I am sure it will be a valuable addition to our code distribution.

To extend or reduce memory, strings make very useful buffers. They use the same system as getmemory/freememory but they are also garbage-collected when out of scope, and can be manipulated with all the string functions. Redim is a macro that uses them, overlaying dynamic arrays onto varname_buffer.

I have added this macro to inc/generics.inc

it takes the name of any pointered variable, and the number of bytes to allocate, as arguments:

Code: [Select]

  def buffer string
  =================


  def bufptr strptr
  =================

  macro SetBufMem( v,n,  ,de,le)
  ==============================
  #ifndef v##_buffer
    buffer v##_buffer
  #endif
  int le=len(v##_buffer)
  int de=n-le
  if de>0
    v##_buffer += nuls(de)
  elseif de<0
    v##_buffer = left( v##_buffer,le+de )
  end if
  @v=bufptr(v##_buffer)
  end macro


Arnold

  • Hero Member
  • *****
  • Posts: 700
Re: Using Dynamic Dialogs
« Reply #5 on: January 05, 2018, 07:32:13 AM »
This is a small test, which creates a main Window via CreateWindowEx and then creates an in-memory Dialog.

DlgFromWin.o2bas:
Code: OxygenBasic
  1. '====================================================================
  2. ' Create a Main Window in Winmain() and call Modeless Dialog
  3. '====================================================================
  4.  
  5. '% review
  6.  
  7. uses dialogs
  8. namespace
  9.  
  10.  
  11. hInstance = GetModuleHandle(0)
  12.  
  13.  
  14. % ID_BUTTON1=100
  15. % ID_BUTTON2=101
  16.  
  17. string g_szClassName = "WinDemo"
  18.  
  19. sys hButton1, hButton2
  20. sys g_hDialog
  21.  
  22. function DialogDlgProc(sys hwnd, Message, wParam, lParam) as bool callback
  23.  
  24.    select Message  
  25.         case WM_COMMAND
  26.  
  27.             select loword(wParam)        
  28.                 case IDOK
  29.                     MessageBox(hwnd, "Oh yeah!", "This feels good!",
  30.                         MB_OK or MB_ICONASTERISK)
  31.                
  32.                 case IDCANCEL
  33.                     MessageBox(hwnd, "Outch!", "This hurts!",
  34.                         MB_OK or MB_ICONEXCLAMATION)                
  35.             end select
  36.        
  37.         case else
  38.             return FALSE
  39.  
  40.     end select
  41.  
  42.     return TRUE
  43. end function    
  44.  
  45.  
  46. function WndProc(sys hwnd, Message, wParam, lParam) as sys callback
  47.  
  48.     select Message    
  49.         case WM_COMMAND
  50.             select loword(wParam)
  51.                            
  52.                 case ID_BUTTON1                
  53.                     ShowWindow(g_hDialog, SW_SHOW)
  54.                
  55.                 case ID_BUTTON2                
  56.                     ShowWindow(g_hDialog, SW_HIDE)              
  57.             end select
  58.        
  59.         case WM_CLOSE
  60.             DestroyWindow(hwnd)
  61.        
  62.         case WM_DESTROY
  63.             DestroyWindow(g_hDialog)
  64.             PostQuitMessage(0)
  65.        
  66.         case else
  67.             return DefWindowProc(hwnd, Message, wParam, lParam)
  68.    
  69.     end select
  70.    
  71.     return 0
  72. end function
  73.  
  74.  
  75. function WinMain(sys nCmdShow) as sys
  76.  
  77.     WNDCLASSEX wc
  78.     MSG Msg
  79.  
  80.     sys hwnd
  81.  
  82.     wc.cbSize        = sizeof(WNDCLASSEX)
  83.     wc.style         = 0
  84.     wc.lpfnWndProc   = @WndProc
  85.     wc.cbClsExtra    = 0
  86.     wc.cbWndExtra    = 0
  87.     wc.hInstance     = hInstance
  88.     wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION)
  89.     wc.hCursor       = LoadCursor(NULL, IDC_ARROW)
  90.     wc.hbrBackground = COLOR_WINDOW+1
  91.     wc.lpszMenuName  = NULL
  92.     wc.lpszClassName = strptr(g_szClassName)
  93.     wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION)
  94.  
  95.     if not RegisterClassEx(&wc) then
  96.         MessageBox(NULL, "Window Registration Failed!", "Error!",
  97.             MB_ICONEXCLAMATION or MB_OK)
  98.         return 0
  99.     end if
  100.  
  101.     hwnd = CreateWindowEx(
  102.         WS_EX_CLIENTEDGE,
  103.         g_szClassName,
  104.         "Call dynamic Dialog",
  105.         WS_OVERLAPPEDWINDOW,
  106.         300, 300, 450, 140,
  107.         NULL,
  108.         NULL,
  109.         hInstance, NULL)
  110.  
  111.     if hwnd = NULL then
  112.         MessageBox(NULL, "Window Creation Failed!", "Error!",
  113.                    MB_ICONEXCLAMATION or MB_OK)
  114.         return 0
  115.     end if        
  116.  
  117.     hButton1 = CreateWindowEx (0,"button", "Show Dialog",
  118.                                WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
  119.                                40, 20, 100, 24, hwnd, ID_BUTTON1,
  120.                                hInstance, NULL)
  121.     hButton2 = CreateWindowEx (0,"button", "Hide Dialog",
  122.                                WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
  123.                                40, 50, 100, 24, hwnd, ID_BUTTON2,
  124.                                hInstance, NULL)
  125.     sys dt
  126.     dyn::init(dt)
  127.  
  128.     dyn::Dialog( 2, 100, 0, 98, 52, "Simple Dialog", dt,
  129.                  WS_CHILD)
  130.     dyn::PushButton( "Press this button", IDOK, 7, 7, 84, 14 )
  131.     dyn::PushButton( "Don't press this button", IDCANCEL, 7,31, 84, 14 )
  132.  
  133.     g_hDialog = dyn::CreateModelessDialog( hwnd, @DialogDlgProc, 0, dt )
  134.     if g_hDialog!=null  then          
  135.       ShowWindow(g_hDialog, SW_SHOW)            
  136.     else            
  137.       MessageBox(hwnd, "CreateDialog returned NULL", "Warning!",    
  138.                  MB_OK or MB_ICONINFORMATION)
  139.     end if
  140.                          
  141.     ShowWindow(hwnd, nCmdShow)
  142.     UpdateWindow(hwnd)
  143.  
  144.     sys bRet
  145.  
  146.     do while (bRet := GetMessage(&Msg, NULL, 0, 0)) != 0
  147.       if bRet = -1 then
  148.         'show an error message
  149.        print "Error in Message Loop"
  150.         end        
  151.       else
  152.           if not IsDialogMessage(g_hDialog, &Msg) then    
  153.             TranslateMessage(&Msg)
  154.             DispatchMessage(&Msg)
  155.         end if
  156.       end if        
  157.     wend
  158.    
  159.     return Msg.wParam
  160. end function
  161.  
  162. 'WINDOWS START
  163. '=============
  164.  
  165. WinMain(SW_NORMAL)
  166.  
« Last Edit: January 20, 2018, 01:11:39 AM by Arnold »

Arnold

  • Hero Member
  • *****
  • Posts: 700
Re: Using Dynamic Dialogs
« Reply #6 on: January 05, 2018, 08:50:29 AM »
Hi Charles,

I experimented a bit with SetBufMem and I managed to use the macro with dialogs.inc but after all I found that using getmemory will be perfect to achieve my goals. I used a macro init to allocate memory space of 1024 bytes and if I would need more memory I can still use getmemory.

I also applied namespace to hide some variables in dialogs.inc a little bit. Hopefully I did everything properly. So far the examples work quite well. There are some more examples in the Freebasic Forum, and I am sure they can be easily ported to Oxygenbasic and extended further.

I am curious if it is possible to add menus/submenues and accelerators too in some simplified way to dialogs.inc. (similar to a .rc resource file). At the moment I have no idea how this should work, but if it is possible, then dialogs.inc would be a really powerful tool.

Roland
« Last Edit: January 14, 2018, 11:00:42 AM by Arnold »

Arnold

  • Hero Member
  • *****
  • Posts: 700
Re: Using Dynamic Dialogs - macros
« Reply #7 on: January 07, 2018, 11:56:08 PM »
Hi Charles,

in dialogs.inc I use a macro to allocate 1024 bytes for an DLGTEMPLATE structure. I now tried to overlay the macro:

macro init(tpl) {tpl=getmemory 1024}
macro init(tpl,b) {tpl=getmemory b}

and this seems to work (see ButtonGrid.o2bas):

'must supply sufficient space
sys lpdt
dyn::init(lpdt, 5000)

But I am not sure if I did this correctly. Would there be another way, e.g. could I use an optional parameter with a macro?

Roland

Edit: I can see that in DlgFromWin.o2bas this does not work. I would need:
    dyn::init(dt,1024)
to run the program.  So I do not know what would be the best way.

« Last Edit: January 08, 2018, 12:03:35 AM by Arnold »

Charles Pegge

  • Admin Support Member
  • *****
  • Posts: 3837
    • Oxygen Basic
Re: Using Dynamic Dialogs
« Reply #8 on: January 08, 2018, 01:34:42 AM »

Macros do not support overloading but you can check for optional params quite easily, and the macro will deliver the right code:

Code: [Select]
macro init(tpl,b)
#ifdef b
  tpl=getmemory(b)
#else
  tpl=getmemory(1024)
#endif
end macro

sys v,w
#show init(v)
#show init(v,2048)

One further enhancement to prevent memory leaks when a variable is repeatedly init:

Code: [Select]
macro init(tpl,b)
if tpl then freememory tpl
#ifdef b
  tpl=getmemory(b)
#else
  tpl=getmemory(1024)
#endif
end macro

sys v,w
#show init(v)
#show init(v,2048)



Arnold

  • Hero Member
  • *****
  • Posts: 700
Re: Using Dynamic Dialogs
« Reply #9 on: January 08, 2018, 02:25:20 AM »
Thank you Charles, for your help. The macro works really great. In the meantime I have started to revise the default styles for the controls a little bit and to add some common controls which are also often used. Extended styles can have an impact to controls sometimes too. It is experimenting a little bit but it is also fun.

Roland

John

  • Hero Member
  • *****
  • Posts: 3389
Re: Using Dynamic Dialogs
« Reply #10 on: January 12, 2018, 08:02:45 AM »
O2 is like a Lego 3D printer.  :D

Arnold

  • Hero Member
  • *****
  • Posts: 700
Re: Using Dynamic Dialogs
« Reply #11 on: January 14, 2018, 09:07:39 AM »
Yes, it is amazing what nowadays can be done with LEGO and with Oxygen. But Oxygen is less expensive.

This is what O2 can do with OpenGl in a dialog.
Unfortunately at the moment it is only 2D.  :(
But soon it will be 3D.  :)

Arnold

  • Hero Member
  • *****
  • Posts: 700
Re: Using Dynamic Dialogs
« Reply #12 on: January 14, 2018, 10:58:55 AM »
I have added some small modifications to dialogs.inc:

Revised the default styles for some controls a little bit.
Added some names for controls with special styles. At the moment these are: TriCheckBox, MultiLineText, Icon, Bitmap, SortedCombo, SimpleCombo, DropDownList, HScroll, VScroll.
I also added some warning messages in dialogs.inc which should help if CreateModalDialog or CreateModelessDialog will fail. If the symbol: review is defined, a console will open to show a little bit what happens and which can be used to add own printl statements to the code of the application.

Attached is the zip file with the 15 examples which I use for testing at the moment. Some are samples from the Freebasic Forum, extended a little bit, some are new (at least with dialog templates using O2).

The next step will be to concentrate on menus. I learned that there are several ways to create menus and submenus, but I only need a procedure which should be practicable and not too laborious.

Arnold

  • Hero Member
  • *****
  • Posts: 700
Re: Using Dynamic Dialogs
« Reply #13 on: January 16, 2018, 05:13:20 PM »
Hi Charles,

somehow I am in a strange situation and I am a bit confused.

This project is a learning process for me, like everything I did with Oxygenbasic. When I started it, my goal was to achieve the results of the Freebasic or MASM examples and I used the existing representation of the function's arguments. But when I tried to convert James' example .rc files to dialog templates I found that I could use the syntax of the .rc file directly, only by adjusting the order of the arguments. This way I could simply copy the code for the controls of the resource file (in 99 % of the cases).
At the moment I only use a different name for the procedures. The difference would be e.g:
Old:
   dyn::CONTROL IDC_STC20,303,5,60,8,"Record No.",0,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_CENTERIMAGE|SS_RIGHT
   dyn::CONTROL IDC_INDEX,366,4,25,10,"0 / 0",0,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_SUNKEN|SS_CENTERIMAGE|SS_CENTER
New:
  dyn::CONTROL_rc "Record No.",IDC_STC20,"Static",0x50000202,303,5,60,8
  dyn::CONTROL_rc "0 / 0",IDC_INDEX,"Static",0x50001201,366,4,25,10

James applies the resulting hex codes of the styles in the .rc file, but this is no problem.

Another example would be:

Old:     dyn::LText       ( IDC_STATIC,164,18,46,9,"Current time is:",SS_CENTERIMAGE, WS_EX_TRANSPARENT )
New:   dyn::LTEXT_rc    "Current time is:",IDC_STATIC,164,18,46,9,SS_CENTERIMAGE, WS_EX_TRANSPARENT

I am not sure if there is much interest in using dialog templates. Personally, I think it is a fascinating subject. Would there be any objection if I modify the arguments of the subroutines to the more .rc like syntax? I would not need different namings and could save about 90 lines of code in the include file. The code of the applications would look more solid. And I would not need to modify the created code of the .rc file.

Roland

Charles Pegge

  • Admin Support Member
  • *****
  • Posts: 3837
    • Oxygen Basic
Re: Using Dynamic Dialogs
« Reply #14 on: January 18, 2018, 02:31:33 AM »
Hi Roland,

Many thanks for those comprehensive dialog examples. We can include them in the examples folder, if that is okay with you. I think this will be of great interest to anyone developing GUI-based Windows applications. I think the same kind of strategy could be used for other kinds of objects - in games and simulations for example.

If it makes the code clearer, then by all means go ahead and restructure the functions to match rc format.