News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Moving child windows, revisited

Started by NoCforMe, October 16, 2011, 10:11:00 AM

Previous topic - Next topic

NoCforMe

A follow-up to my earlier thread about my troubles getting child windows to be able to be dragged around inside another window.

The attached code & executable shows what I've been able to do. My original aim was to try to replicate some of the functionality of the typical resource editor. Along the way someone turned me on to ResEd (by KetilO). I've managed to figure out how he did some things.

The attached program (resizing) doesn't actually resize anything (yet), so I guess the name is a lie. But it does what I think are some pretty cool things. I've created a child window with 8 attached resize handles (each a small sibling window), just like in his editor (same size, too, 6x6, a nice-looking size). You can drag the child around.

You can also right-click (outside the child, since the code is in the parent's window proc) and add a control from a pop-up menu. (The only thing that doesn't work right is the static text choice: for some reason, it doesn't show up in the window at all.) You'll notice that the controls are actually semi-functional; you can click the checkbox and type into the edit control.

There's a bug (at least I'd call it that) in his editor that I've fixed in this little program. If you fire up RedEd and create a dialog with a control in it, try dragging the control out of the dialog; it lets you do that. With the result that you cannot grab it back (if you let go of the mouse button while you're outside the dialog); the control is just stranded. I fixed this by capturing the mouse (using SetCapture() - ReleaseCapture() ) to confine the cursor to the containing client area. I'm surprised he didn't do that. Also, his controls move kind of jerkily, sometime leaving lots of nasty artifacts behind; perhaps he's doing a lot of processing that's slowing things down.

Anyhow, just wanted to put this out there. Comments welcome as always. (Dave, check out my table-driven handle-window moving & cursor-setting code; notice how you get the correct resizing arrows, even though they don't actually do anything yet.)

==========================================================

Aaaargh. I can see the dangers of capturing the mouse. I don't know if this version of the program does this, but I made a few changes, and now the mouse is always confined to the program window, even after releasing the mouse button after dragging the child. When this happened before, I couldn't reach any controls to kill the program, so I had to Alt-Tab to another app to restore the cursor! At least now I can reach the window's close button.

Further research is needed ...

dedndave

i figured out wht i need to do on my little version
but, i had to sit it aside to take care of some other things
at any rate, i also wanted to add the dialog box, which would be sizable, as well

not sure how you confine the cursor, but i use ClipCursor (during WM_LBUTTONDOWN)
to release that rectangle, i use ClipCursor,NULL during WM_LBUTTONUP
thing is - it has to do the same in the parent window, in case the cursor is outside the box

NoCforMe

Well, yes, that's the way to confine the cursor. I fixed that problem: I was forgetting to call ClipCursor() again to clip it back to the original setting. The proper sequence is


INVOKE SetCapture, hWin
INVOKE GetClipCursor, ADDR PrevRect ;Get current clip rectangle
INVOKE GetWindowRect, hWin, ADDR gpRect
INVOKE ClipCursor, ADDR gpRect


to capture the mouse, followed by


INVOKE ReleaseCapture
INVOKE ClipCursor, ADDR PrevRect ;Un-clip to our window.


to return things to normal.

So there's still a basic problem with this program: if you run it, you'll see that the child window is only "sensitive" at its boundaries. You can't click in its interior and move it: the cursor must be moved to any edge, at which point it starts moving.

It's a little tricky, because you need to track the origins of left-button-down and mouse movement in the child, but you have to do the mouse capture in the parent. What I'm doing is setting a global flag in the child and checking it in the parent.

Similar thing for implementing the pop-up ("shortcut") menu: the version I posted doesn't work correctly, in that the position of the menu is where I want it if you click outside the child, but wrong if you click inside the child. This has to do with the X and Y positions in the mouse-button messages (in lParam). If the user clicks in the child, I send a message (the same message I receive, WM_RBUTTONDOWN) to the parent. But since the child and parent see X/Y coordinates from different points of view, I had to adjust things for the child. This ended up doing the trick:

In the child:

MOV EAX, lParam
MOV EDX, EAX
AND EAX, 0FFFFH
MOV pt.x, EAX
SHR EDX, 16
MOV pt.y, EDX
INVOKE ClientToScreen, hWin, ADDR pt ;Where is our client in the "real world"?
INVOKE ScreenToClient, MainWinHandle, ADDR pt
MOV EAX, pt.y
SHL EAX, 16
MOV AX, WORD PTR pt.x
INVOKE PostMessage, MainWinHandle, WM_RBUTTONDOWN, wParam, EAX


and in the parent

MOV EAX, lParam
MOV EDX, EAX
AND EAX, 0FFFFH
CWDE
MOV pt.x, EAX
SHR EDX, 16
CWDE
MOV pt.y, EDX
INVOKE ClientToScreen, hWin, ADDR pt ;Where is our client in the "real world"?
INVOKE TrackPopupMenuEx, hMenu,
TPM_LEFTALIGN OR TPM_TOPALIGN OR TPM_RETURNCMD OR TPM_RIGHTBUTTON OR TPM_VERNEGANIMATION OR TPM_VERTICAL,
pt.x, pt.y, hWin, NULL


TrackPopupMenuEx() displays and gets a selection from the popup menu. It works correctly no matter where you click.

(Yeah, I know, I should use MOVZX or MOVSX to save an operation up there. What can I say?--those instructions are new to me.

dedndave

yah - if you put a control in the box, the WndProc for the control gets the messages
you can get around that by subclassing the control - give it your own WndProc
use SetWindowLong to set the address of the new WndProc and get the old one (the old one is the return value)
then, you can intercept mouse activity for the control, and send the other messages to the original WndProc

NoCforMe

That doesn't matter here, as I'm not actually using the controls (which work, by the way). I'm using the same class for the child window and the controls, so all the messages go to the same window proc. I can easily distinguish who the message is for by looking at handles.

dedndave

well - having controls that function like controls is a bit much to ask for - lol
you have to think about how the program is going to be used
the programmer is going to use it to set up a dialog box - functional controls is somewhat superfluous
he wants to see how it looks - not test it   :bg

NoCforMe

Right. You seem to have missed the point; I didn't want the controls to work, only to look like what they actually look like. They're just place-holders. That they work is just a little added bonus. It's not like I'm actually handling any messages from them.

In any case, I don't think that has anything to do with the latest problem we were discussing, that you can't "grab" the child window in its interior. It happens even if there's no control in it. (Maybe I need to add a "delete" function to that little pop-up menu.) Any ideas?

qWord

hi,

I've create an advanced example using subclasses. AFAICS, you can use any control and add the 'container'-subclass to it (function: BindContainer)
The controls can be resized and moved (hold the CONTROL-key down and click the little white boxes for moving ;-) )



FPU in a trice: SmplMath
It's that simple!

NoCforMe

Interesting; I'll have to study that.

About subclassing: that's basically Windows jargon for "intercepting calls to objects for the purpose of manipulating them", isn't it?

Question about your code:


fn CreateWindowEx,0,"Edit","xyz",WS_CHILD or WS_VISIBLE,120,10,100,100,hWnd,0,hInstance,0
invoke BindContainer,eax,HANDHOLDS_SIZE,hInstance


1. Dunno what "fn" does. Is this a MASM32 macro or something?
2. I couldn't find any documentation on MSDN for BindContainer(). Is this also MASM32? What does it do? What does it bind to what?

qWord

Quote from: NoCforMe on October 16, 2011, 07:49:25 PMAbout subclassing: that's basically Windows jargon for "intercepting calls to objects for the purpose of manipulating them", isn't it?
'yes' - subcalssing means to replace the original windows procedure with your own. This allows to get all messages that the window gets, thus you can use, redirect or filter them.
Quote from: NoCforMe on October 16, 2011, 07:49:25 PM1. Dunno what "fn" does. Is this a MASM32 macro or something?
2. I couldn't find any documentation on MSDN for BindContainer(). Is this also MASM32? What does it do? What does it bind to what?
1. This macro extents the INVOKE-directive - it adds support for string literals (e.g. "edit") -> see \masm32\help\hlhelp.chm
2. You will find this function in the source code - this function create the subclass (ok - may be the name is inappropriate)
FPU in a trice: SmplMath
It's that simple!

NoCforMe

Hmm:


SubCalssTrampoline:
mov edx,02BADF00Dh ; edx = pOrgWndProc
mov ecx,02BADF00Dh  ; ecx = ptr CSCI
mov eax,02BADF00Dh ; eax = pWndProcSubCalss
jmp eax
SubCalssTrampoline_end:


Isn't this what's called a "thunik"? Sure looks like one. (Jiump address is plugged in at run time; kind of like the dreaded "computed GOTO" of olden days ...)

dedndave

QuoteAbout subclassing: that's basically Windows jargon for "intercepting calls to objects for the purpose of manipulating them", isn't it?

that's more-or-less correct
to put it simply, you are replacing the standard WndProc function with your own

Quote1. Dunno what "fn" does. Is this a MASM32 macro or something?

yah - it's a fancy macro for INVOKE   :P
you can see what it does by looking in masm32\macros\macros.asm

Quote2. I couldn't find any documentation on MSDN for BindContainer(). Is this also MASM32? What does it do? What does it bind to what?

i'll let qWord get that one - lol
new to me   :P
i was just going to subclass the control and take over the mouse and keyboard messages, rendering it non-functional
but still leave the other messages "as is" so it gets painted, sized, moved, and so on

NoCforMe

Thanks for all the info on "subcalssing", folks. But for the time being I think I'll concentrate on doing things the plain old boring conventional Windows way. I think there must be an answer to my problem that doesn't involve this level of complexity.

Later on, of course, I want to learn all about it ...

So Dave, I assume this is in reference to my problem? Why do you think it's necessary to render the controls non-functional? After all, as I already stated, the problem occurs even with no controls in the child window, so I don't think they're to blame. (Unless you just want to figure out how to do this anyhow.)

dedndave

well - that has to do with how you capture the mouse
i wasn't going to use that function, nor DragDetect, either   :P

my game-plan was to just...
1) set the clip rectangle on left button down
this step takes a little preperation, as you want to clip so the window stays inside the boundry - not just the mouse
2) move the window with the mouse cursor as long as the left button is down
3) reset the clip rectangle on left button up

that method works, unless the cursor happens to get ahead of the moving window
a problem that can be overcome by catching the parent window mouse messages when the left button is down

i posted that code in the other thread

NoCforMe

Quote from: dedndave on October 16, 2011, 08:28:44 PM
my game-plan was to just...
1) set the clip rectangle on left button down
this step takes a little preperation, as you want to clip so the window stays inside the boundry - not just the mouse
2) move the window with the mouse cursor as long as the left button is down
3) reset the clip rectangle on left button up

Actually, I've changed my requirements a bit. Remember how I was bothered by the child window being able to be partially drug outside the client area? how I wanted it to "bump up against" the edge of the client rectangle? Well, after looking at lots of other apps, nobody does it that way, so I'm OK with it being able to "go under" the client border. That should simplify things.

So I have that part (mouse capture/client clipping) pretty well down. Can you suggest why I can't drag the child from its interior? That's my main problem here. (To be clear: If I click inside the child and try to drag, nothing happens until the mouse cursor hits the nearest edge, at which point it bounces back to the initial drag point and dragging commences. Works, but it's kind of annoying, and not at all how other apps work.)