Previous page Select page Next page

Improving the "peg" handler - step 3

We already know how we want to improve the "page" handler - we want to be able to specify one of 4 possible position for the object within the client area of the window. Start by creating a new project. Use the same names as before and add 4 buttons to the window. If you call the first one "btn0" and name the others accordingly, it'll make the code much more readable. Place the buttons anywhere you like on the screen but the result, when you run the program, should be to have them end up as shown in the llustration here.

The first thing we need to change is the actual call to the "pegObject" routine. Not only does it now have an additional parameter but there are 4 objects to "peg" and not just one as before, so our "OnResize" handler should look like this:


  OnResize:

	UseData winMainProcedure

	Invoke pegObject, [hWnd], IDC_WINMAIN_BTN0, 0

	Invoke pegObject, [hWnd], IDC_WINMAIN_BTN2, 2

	Invoke pegObject, [hWnd], IDC_WINMAIN_BTN6, 6

	Invoke pegObject, [hWnd], IDC_WINMAIN_BTN8, 8

	Return (TRUE)

  ; End OnResize

  EndU

There should be no big surprises here - we invoke the "pegObject" routine, pass it the address of the window (using the square brackets convention), give the name of the object to be pegged (using the "IDC" reference) and then tell it at which of the four corners that particular object is to be pegged. That's the easy bit - now we have to work out how to handle each of these instructions!

Let's start with the actual "signature" of the routine - only one thing has changed, remember. We'll call this new value "pegPosition" but the initial steps of setting up the local variables, getting the object's handle and then the size of the window's client area remain the same:


  pegObject Frame hWnd, idControl, pegPosition

    Local hButton, pBox : RECT



    ; 1. Get a handle on the button

    Invoke GetWindowItem, [hWnd], [idControl]

    Mov [hButton], Eax



    ; 2. Get the new size of the window's client area

    Invoke GetClientRect, [hWnd], Addr pBox

OK, now we turn to the new parameter. We need to move the peg position value into one of the registers and, since Eax is currently employed, we should move the contents of "pegPosition" into Ebx:


    ; 3. Get the required peg position

    Mov Ebx, [pegPosition]

We're now in a position to do something about the user's request to position an object at one of the four positions. What we are going to do is to repeatedly compare Ebx with the known values 0, 2, 6 and 8. Each time, if we find the zero flag set, we will jump to an appropriate label where the code for that pegging operation will be found. That means the JZ mnemonic.

Now, here's the thing - in assembler, we can either jump forward or backwards and, in general, backwards jumps are more efficient. For that reason, if we simply say Jz .thisLabel, goAsm will assume that there is a label with that name earlier in the code and it will fail for that reason. We get around this by means of the "greater than" and "less than" symbols to specify explicitly whether the jump is backwards or forwards, respectively. It's optional, in the case of backwards jumps, whether you use the symbol but it is mandatory when you want to jump forwards.

So, let's make 4 labels in our code at the end of the "OnResize" handler:

Go back to the point just after the point where we store the pegPosition in Ebx and put in the Cmp and the corresponding Jz jumps to those labels:


    ; 4.  Jump to the relevant code

    Cmp Ebx, 0

      Jz >.position_0

    Cmp Ebx, 2

      Jz >.position_2

    Cmp Ebx, 6

      Jz >.position_6

    Cmp Ebx, 8

      Jz >.position_8

You don't actually have to indent the jump statements. I did that to make the relationship between the compares and the associated jumps.

Let's now turn to an important point. What would happen if the user passed the value "7" or "9" to the pegPosition routine? It would fail each one of these comparisons which means that after the last "Jz" it would move on to the next statement, which would be the first of the labels we defined above. That means it would execute the code for pegging the object at position "0". This is obviously not what we would want so we really need to put a statement after the last jump which would cause us to leave the routine if such a condition occurred:


    ; An incorrect "pegPosition" parameter has been supplied.

    ; Return to the main code immediately with a False flag.

    Return (FALSE)

Note that you should return a "False" value because now that we understand comparisons and jumping, we can check for this flag whenever we have called the pegPosition routine to see if it succeeded or not and to handle a failure in some elegant fashion.

OK. Let's now turn our attention to the chunks of code which actually do the pegging.

Position 0

Pegging an object to Position 0 is really simple - it's at the top left of the client area so we simply have to offset it from 0,0 by the amount of border we want, say 4:


  .position_0

    Invoke SetLeft, [hButton], 4

    Invoke SetTop, [hButton], 4

    Return(TRUE)

None of that should pose problems for you but note the "True" in the Return statement to indicate success. Properly speaking, this is not really good enough. Either (or both) the SetLeft or the SetTop could conceivably fail. If you check EasyCode's documentation, you would see that failure of these methods causes a "False" to be returned in Eax, so really we should check that register after each call to these two methods and only if both returned a True should we return from pegPosition with a True value. However, I want to keep things simple, so let's leave things as they are.

Position 2

Position 2 is at the top right of the window. That means that the "y" value of the object is the same as if it had been pegged to position 0. The "x" value is just as we had it in the previous version of pegPosition - we take the width of the object, add the border value to it and then subtract the result from the width of the client area. So, taking the "x" or "left" position first, we would have:


  .position_2

    Invoke GetWidth, [hButton]      ; Get the button's current width

    Add Eax, [pegBorder]	    ; Add a border of 4

    Mov Ebx, [pBox.right]	    ; Retrieve the parent's width

    Sub Ebx, Eax		    ; Subtract the button's (plus border) 

                                    ; width from it.

    Invoke SetLeft, [hButton], Ebx  ; Set the button's left position 

                                    ; to the new value

    Invoke SetTop, [hButton], 4     ; Set the top position

    Return(TRUE)

Position 6

What do we have to do to place something at position 6? Since it is at the bottom left of the window, the "left" co-ordinate is the same as for position 0 and the "top" position of the object is as we had it in the previous version of the routine - take the height of the object, add the border and subtract the result from the height of the client area:


  .position_6

    Invoke SetLeft, [hButton], 4

    Invoke GetHeight, [hButton]		; Get the button's current height

    Add Eax, [pegBorder]		; Add a border of 4

    Mov Ebx, [pBox.bottom]		; Retrieve the parent's height

    Sub Ebx, Eax			; Subtract the button's (plus border) height

    Invoke SetTop, [hButton], Ebx	; Set the button's top position to the new value

    Return(TRUE)

Position 8

And lastly we come to our old friend - position 8. This is the one we used in our previous version of pegPosition. It's at the bottom right of the screen, so we have to work out both the "left" and "top" positions arithmetically:


  .position_8

    Invoke GetWidth, [hButton]		; Get the button's current width

    Add Eax, [pegBorder]		; A border of 4

    Mov Ebx, [pBox.right]		; Retrieve the parent's width

    Sub Ebx, Eax			; Subtract the button's (plus border) width

    Invoke SetLeft, [hButton], Ebx	; Set the button's left position to the new value



    Invoke GetHeight, [hButton]		; Get the button's current height

    Add Eax, [pegBorder]		; Add a border of 4

    Mov Ebx, [pBox.bottom]		; Retrieve the parent's height

    Sub Ebx, Eax			; Subtract the button's (plus border) height

    Invoke SetTop, [hButton], Ebx	; Set the button's top position to the new value

    Return(TRUE)

Putting it all together

And that's it, except for the EndF you need to close the Frame. With the four invocations in the "OnResize" handler and the new code in pegPosition, we are good to go. Compile it and run it, after ensuring that you have 4 buttons in winMain.

Previous page Select page Next page