Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
External App Dialog Frame with Splitters
#1
Hi Gintaras,
Rather than hanging QM dialogs/toolbars/menu's on other apps, I wanted to encase several external apps within a dialog frame with relevant buttons and easy resizing with QM splitter functions. I have something here that works but I am worried about the resource use of the rep loop function and wondering what else I could do to make it better. Don't worry about the loading of the external apps or any wintesting on them, those are just examples. I am more interested in the mechanism for maintaining them properly in their frame and handling Zorder well.
Thanks for any ideas,
S

Function dlg_ExternalAppSplitter
Code:
Copy      Help
\Dialog_Editor

function# hDlg message wParam lParam
if(hDlg) goto messages

str controls = "3 4 5 7"
str e3 e4 c5Not c7Pai
if(!ShowDialog("dlg_ExternalAppSplitter" &dlg_ExternalAppSplitter &controls)) ret

;BEGIN DIALOG
;0 "" 0x90CF0AC8 0x0 0 0 227 265 "App Frame"
;3 Edit 0x54231044 0x200 4 16 220 101 ""
;4 Edit 0x54231044 0x200 4 126 222 101 ""
;1 Button 0x54030001 0x4 62 248 48 14 "OK"
;2 Button 0x54030000 0x4 120 248 48 14 "Cancel"
;6 QM_Splitter 0x54000000 0x0 4 118 220 8 ""
;9 QM_Splitter 0x54000000 0x0 4 228 222 2 ""
;5 Button 0x54031003 0x0 44 0 48 14 "Notepad"
;7 Button 0x54031003 0x0 92 0 48 14 "Paint"
;END DIALOG
;DIALOG EDITOR: "" 0x2030408 "*" "" "" ""


;;;;;Action - m (move) or s (resize).
;;;;;Direction - h (horizontally) or v (vertically) or none (horizontally and vertically).



ret
;messages
DT_AutoSizeControls hDlg message "3s 4s 6m"
DlgSplitter ds1.Init(hDlg 6)
DlgSplitter ds2.Init(hDlg 9)
sel message
,case WM_INITDIALOG
,,ifi-(win("Paint" "MSPaintApp"))
,,,run "$system$\mspaint.exe"
,,ifi-(win("Notepad" "Notepad"))
,,,run "$system$\notepad.exe"
,,mac "AppPositioner"
,,but(id(5 hDlg))
,,but(id(7 hDlg))
,case WM_DESTROY
,case WM_COMMAND goto messages2
ret
;messages2
sel wParam
,case IDOK
,case IDCANCEL
,case [5,7]
,,if(but(id(5 hDlg)) and but(id(7 hDlg)))
,,,ds1.SetPos(ds1.GetMaxPos/2)
,,,ds2.SetPos(ds2.GetMaxPos-20)
,,if(but(id(5 hDlg)) and !but(id(7 hDlg)))            
,,,ds1.SetPos(ds1.GetMaxPos-30)
,,,ds2.SetPos(0)
,,if(!but(id(5 hDlg)) and but(id(7 hDlg)))
,,,ds1.SetPos(20)
,,,ds2.SetPos(ds2.GetMaxPos)
,,if(!but(id(5 hDlg)) and !but(id(7 hDlg)))
,,,ds1.SetPos(20)
,,,ds2.SetPos(0)

ret 1


Function ExternalAppPositioner
Code:
Copy      Help
spe
rep
,int AppHwnd1=win("Notepad" "Notepad")    
,int AppHwnd2=win("Paint" "MSPaintApp")
,int AppFrameHwnd=win("App Frame" "#32770")
,int x_1 y_1 cx_1 cy_1 x_2 y_2 cx_2 cy_2
,
,GetWinXY id(3 AppFrameHwnd) x_1 y_1 cx_1 cy_1 ;;editable text
,GetWinXY id(4 AppFrameHwnd) x_2 y_2 cx_2 cy_2 ;;editable text

,MoveWindow AppHwnd1 x_1 y_1 cx_1 cy_1 1
,MoveWindow AppHwnd2 x_2 y_2 cx_2 cy_2 1
,Zorder AppHwnd1 HWND_TOP SWP_NOACTIVATE
,Zorder AppFrameHwnd HWND_BOTTOM
,
#2
Function dlg_ExternalAppSplitter
Code:
Copy      Help
\Dialog_Editor

function# hDlg message wParam lParam
if(hDlg) goto messages

str controls = "100 101 5 7"
str e100 e101 c5Not c7Pai
c5Not=1
c7Pai=1
if(!ShowDialog("dlg_ExternalAppSplitter" &dlg_ExternalAppSplitter &controls)) ret

;BEGIN DIALOG
;0 "" 0x90CF0AC8 0x0 0 0 227 265 "App Frame"
;100 Edit 0x54231044 0x200 4 16 220 101 ""
;101 Edit 0x54231044 0x200 4 126 222 101 ""
;1 Button 0x54030001 0x4 62 248 48 14 "OK"
;2 Button 0x54030000 0x4 120 248 48 14 "Cancel"
;6 QM_Splitter 0x54000000 0x0 4 118 220 8 ""
;9 QM_Splitter 0x54000000 0x0 4 228 222 2 ""
;5 Button 0x54031003 0x0 44 0 48 14 "Notepad"
;7 Button 0x54031003 0x0 92 0 48 14 "Paint"
;END DIALOG
;DIALOG EDITOR: "" 0x2030502 "*" "" "" ""

;;;;;Action - m (move) or s (resize).
;;;;;Direction - h (horizontally) or v (vertically) or none (horizontally and vertically).

ret
;messages
DT_AutoSizeControls hDlg message "100s 101s 6mh"
DlgSplitter ds1.Init(hDlg 6)
DlgSplitter ds2.Init(hDlg 9)
ARRAY(int)- aw; int- nWindows=2
int i w
sel message
,case WM_INITDIALOG
,aw.create(nWindows)
,run "$system$\notepad.exe" "" "" "" 0x1800 win("Notepad" "Notepad") aw[0]
,run "$system$\mspaint.exe" "" "" "" 0x1800 win("Paint" "MSPaintApp") aw[1]
,for i 0 nWindows
,,SetWindowLong aw[i] GWL_HWNDPARENT hDlg ;;now should always be on top of dialog etc
,SetTimer hDlg 1 100 0
,SendMessage hDlg WM_COMMAND 5 0 ;;execute gButton code. Or use function instead.
,SendMessage hDlg WM_TIMER 1 0 ;;execute gTimer code. Or use function instead.
,
,case WM_TIMER
,sel wParam
,,case 1 goto gTimer
,
,case WM_DESTROY
,for i 0 nWindows
,,clo aw[i]; err
,
,case WM_COMMAND goto messages2
ret
;messages2
sel wParam
,case IDOK
,case IDCANCEL
,case [5,7] goto gButton
ret 1

;gButton
sel but(5 hDlg)|(but(7 hDlg)<<1)
,case 3
,ds1.SetPos(ds1.GetMaxPos/2)
,ds2.SetPos(ds2.GetMaxPos-20)
,case 1
,ds1.SetPos(ds1.GetMaxPos-30)
,ds2.SetPos(0)
,case 2
,ds1.SetPos(20)
,ds2.SetPos(ds2.GetMaxPos)
,case 0
,ds1.SetPos(20)
,ds2.SetPos(0)
ret

;gTimer
RECT r rr
for i 0 nWindows
,GetWindowRect id(100+i hDlg) &r
,GetWindowRect aw[i] &rr
,if(memcmp(&r &rr sizeof(RECT))) MoveWindow aw[i] r.left r.top r.right-r.left r.bottom-r.top 1
;speed: 6 miscroseconds when don't need to move

ret
#3
Thanks so much, Gintaras..I learned so much and tried to extend it to 3 external apps:

- the gButton construct for case was very interesting. I see that you use the the bitwise rightshift << syntax (just had to look it up). I know it is also in some of case structures in dialogs e.g. case EN_SETFOCUS<<16|14. Here it was for distinguishing between the 8 combination states of 3 buttons. Still not exactly sure how I guessed to put in a 2 before I even knew what the bitwise thing was all about but somehow it worked. (I learn so much from your code). Anyway, when do you use this construct and why?

(Also how would I search on something like "<<" in the forum - << and "<<" both come up empty in the helpfile and forum)

- Timer function - cool idea to keep all dialog functions in same thread instead of calling external rep loop function. But Timer way is a little slower than the rep loop (without any pauses, spe= 0). ie. I can see the lag in repositioning in the timer way but not in the external thread rep loop. Is this something that is adjustable. Is it too much of a performance hit? I didn't understand about ';speed: 6 miscroseconds when don't need to move" - it seems like the timer function goes all the time not just when frame is selected?



- I used the Timer function to keep the main buttons centered since it is a resizable dialog. Is there a more elegant way of doing this?

- Splitters -
I had a bit of trouble once I went up to three external app windows managing the size and bounds of the splitters. If they go to their maximum, they cover up the Title bar of the external app. I couldn't figure out how to limit their excursion to not go past the title bar so instead I made the title bar very wide. You will see it works but takes up unnecessary space. Also, if they go all the way, then they cover up the splitter so can't move away from max position by manually moving the splitter bar. Complicated!!!

Anyway, this is a lot to respond to (if you have the time) but I am really inspired by all these capabilities in QM and having fun. Thanks so much!,
S


Function dlg_3ExternalAppSplitter
Code:
Copy      Help
\Dialog_Editor

function# hDlg message wParam lParam
if(hDlg) goto messages

CloseWindowsOf("App Frame" "#32770");err

ifi(win("App Frame" "#32270")); clo win("App Frame" "#32270")
str controls = "100 101 102 5 7 3"
str e100 e101 e102 c5Not c7Pai c3Wor
c5Not=1
c7Pai=1
c3Wor=1
if(!ShowDialog("dlg_3ExternalAppSplitter" &dlg_3ExternalAppSplitter &controls)) ret

;BEGIN DIALOG
;0 "" 0x90CF0AC8 0x0 0 0 231 423 "App Frame"
;100 Edit 0x54231044 0x200 4 18 222 100 ""
;101 Edit 0x54231044 0x200 4 140 222 100 ""
;102 Edit 0x54231044 0x200 4 262 222 100 ""
;1 Button 0x54030001 0x4 64 402 48 14 "OK"
;2 Button 0x54030000 0x4 118 402 48 14 "Cancel"
;6 QM_Splitter 0x54040000 0x0 4 118 222 22 ""
;9 QM_Splitter 0x54040000 0x0 4 240 222 22 ""
;5 Button 0x54031003 0x0 38 4 48 10 "Notepad"
;7 Button 0x54031003 0x0 86 4 48 10 "Paint"
;3 Button 0x54031003 0x0 134 4 48 10 "Wordpad"
;END DIALOG
;DIALOG EDITOR: "" 0x2030408 "*" "" "" ""


;;;;;Action - m (move) or s (resize).
;;;;;Direction - h (horizontally) or v (vertically) or none (horizontally and vertically).

ret
;messages
DT_AutoSizeControls hDlg message "100sh 101sh 102sh 6mh 9mh"
DlgSplitter ds1.Init(hDlg 6)
DlgSplitter ds2.Init(hDlg 9)
ARRAY(int)- aw; int- nWindows=3
int i w
sel message
,case WM_INITDIALOG
,aw.create(nWindows)
,run "$system$\notepad.exe" "" "" "" 0x1800 win("Notepad" "Notepad") aw[0]
,run "$system$\mspaint.exe" "" "" "" 0x1800 win("Paint" "MSPaintApp") aw[1]
,run "$program files$\Windows NT\Accessories\wordpad.exe" "" "" "" 0x1800 win("WordPad" "WordPadClass") aw[2]

,for i 0 nWindows
,,SetWindowLong aw[i] GWL_HWNDPARENT hDlg ;;now should always be on top of dialog etc
,SetTimer hDlg 1 100 0
,SendMessage hDlg WM_COMMAND 5 0 ;;execute gButton code. Or use function instead.
,SendMessage hDlg WM_TIMER 1 0 ;;execute gTimer code. Or use function instead.
,
,case WM_TIMER
,sel wParam
,,case 1 goto gTimer
,
,case WM_DESTROY
,for i 0 nWindows
,,clo aw[i]; err
,
,case WM_COMMAND goto messages2
ret
;messages2
sel wParam
,case IDOK
,case IDCANCEL
,case [5,7,3] goto gButton
ret 1

;gButton
int- LastConfig
out but(5 hDlg)|(but(7 hDlg)<<1)|(but(3 hDlg)<<2)

sel but(5 hDlg)|(but(7 hDlg)<<1)|(but(3 hDlg)<<2)
,case 1;;button 5 (NotePad Only)
,,ds1.SetPos(ds1.GetMaxPos)
,,ds2.SetPos(ds2.GetMaxPos)
,,LastConfig = 1
,case 2;;button 7 (Paint Only)
,,ds1.SetPos(0)
,,ds2.SetPos(ds2.GetMaxPos)
,,LastConfig = 2
,case 3;;button 5 and 7 (NotePad and Paint)
,,ds1.SetPos(ds1.GetMaxPos/2)
,,ds2.SetPos(ds2.GetMaxPos)
,,LastConfig = 3
,case 4;;button 3 (Wordpad Only)
,,ds1.SetPos(0)
,,ds2.SetPos(0)
,,LastConfig = 4
,case 5;;button 5 and 3 NotePad and WordPad
,,ds1.SetPos(ds1.GetMaxPos)
,,ds2.SetPos(0)
,,LastConfig = 5
,case 6;;button 7 and 3 (Paint and WordPad)
,,ds1.SetPos(0)
,,ds2.SetPos(ds2.GetMaxPos/2)
,,LastConfig = 6
,case 7;;button 5, 7 and 3 (NotePad, Paint and WordPad)
,,ds1.SetPos(ds1.GetMaxPos/2)
,,ds2.SetPos(ds2.GetMaxPos/2)
,,LastConfig = 7
,case 0;;no buttons
,,out "LastConfig = %i" LastConfig
,,sel LastConfig
,,,case 1    TO_Check hDlg "5" 1
,,,case 2    TO_Check hDlg "7" 1
,,,case 4    TO_Check hDlg "3" 1
ret

;gTimer
RECT r rr
for i 0 nWindows
,GetWindowRect id(100+i hDlg) &r
,GetWindowRect aw[i] &rr
,if(memcmp(&r &rr sizeof(RECT))) MoveWindow aw[i] r.left r.top r.right-r.left r.bottom-r.top 1

;speed: 6 miscroseconds when don't need to move
RECT r_Dlg; GetWindowRect hDlg &r_Dlg
RECT r_Button; GetWindowRect id(5 hDlg) &r_Button ;;check box 'Notepad'
int ButtonWidth = r_Button.right-r_Button.left
int MidDlg = (r_Dlg.right-r_Dlg.left)/2
mov MidDlg-(ButtonWidth*3/2) 0 id(5 hDlg) 2 ;;check box 'Notepad'
mov MidDlg-(ButtonWidth/2) 0 id(7 hDlg) 2 ;;check box 'Paint'
mov MidDlg+(ButtonWidth/2) 0 id(3 hDlg) 2 ;;check box 'Wordpad'


ret
#4
<< is bitwise left-shift operator. Like in C++ and other languages. Here I used it to make code smaller, avoid multiple but(...).
1<<1 = 2
1<<2 = 4
1<<3 = 8
1<<4 = 16
...
1<<16 = 0x10000
...
1<<31 = 0x80000000

EN_SETFOCUS<<16|14 creates value where high-order word is EN_SETFOCUS and low-order word is 14.

Try smaller timer period. For example, with period 1 almost no lag. Actually OS does not allow 1 s period, smallest is maybe 10 ms. The timer goes all the time, but the code is fast, still 0 % CPU in Task Manager.

But probably better is to execute gTimer code on messages that dialog receives when splitter moved, dialog moved etc. Use OutWinMsg to see messages. For example:
,case [WM_PAINT,WM_MOVE] goto gTimer

To center buttons better use WM_SIZE, but can also do it on WM_TIMER, if the code is fast. Don't mov everytime, only when need, like I did with memcmp, then much faster.

To limit splitter range, use other smaller control by the splitter.
Function Dialog122
Code:
Copy      Help
\Dialog_Editor

if(!ShowDialog("Dialog122")) ret

;BEGIN DIALOG
;0 "" 0x90C80AC8 0x0 0 0 223 135 "Dialog"
;3 QM_Splitter 0x54030000 0x0 4 56 214 6 ""
;4 Edit 0x54030080 0x200 2 0 110 54 ""
;5 Static 0x54000000 0x0 118 26 96 28 "This smaller control limits splitter range."
;6 Static 0x54000000 0x0 2 64 212 14 "A splitter is above me. Try to drag it up."
;1 Button 0x54030001 0x4 120 116 48 14 "OK"
;2 Button 0x54030000 0x4 170 116 48 14 "Cancel"
;END DIALOG
;DIALOG EDITOR: "" 0x2030502 "" "" "" ""
#5
Thanks Gintaras,
I learned so much from this. One thing though, I set timer to lowest value
Code:
Copy      Help
SetTimer hDlg 1 1 0
but still big lag compared to running in a separate mac run from WM_INITDIALOG:

Code:
Copy      Help
mac "Repositioner" 0 hDlg nWindows

Function Repositioner
Code:
Copy      Help
function int'hDlg nWindows
OutVar hDlg
rep
,ARRAY(int)+ aw;
,RECT r rr
,int i
,for i 0 nWindows
,,GetWindowRect id(100+i hDlg) &r
,,GetWindowRect aw[i] &rr
,,if(memcmp(&r &rr sizeof(RECT))) MoveWindow aw[i] r.left r.top r.right-r.left r.bottom-r.top 1


Am I setting up the timer incorrectly. With the external rep loop, there is no lag whatsoever. With the loop, it is delayed then catches up immediately with each movement.

S
#6
With such loop uses much CPU. And the lag with timer possibly is because something uses much CPU. Almost no lag on my PC.
The correct way is to use messages, for example
,case [WM_PAINT,WM_MOVE] goto gTimer
Use timer too, with bigger period, just to make sure that everything will work in all cases.
#7
Hi Gintaras,

The solution of using

Code:
Copy      Help
SetWindowLong WordPadHwnd GWL_HWNDPARENT hDlg ;;now should always be on top of dialog etc
instead of

Code:
Copy      Help
Zorder AppHwnd1 HWND_TOP SWP_NOACTIVATE
Zorder AppFrameHwnd HWND_BOTTOM

works very well because it doesn't interfere when the user want to view windows other than the app frame or it's associated windows. However, it seems to block the ability to drag-select text (LMB down-drag-LMB up). I have tried this in my actual app as well as in wordpad. Any thoughts?
I have rummaged around on web and there is something about using SetParent instead of GWL_HWNDPARENT but not sure if this applies. Thanks again,
S
#8
Maybe something in your code repeatedly sets focus to some other window or control. Normally no problems with drag.
SetParent can be used instead. Also add WS_CHILD style. Then the window becomes a child window of the dialog. But it is not recommended. Possible problems.


Forum Jump:


Users browsing this thread: 1 Guest(s)