Resizing Windows Programmatically

We’re not done with our Calculator — we still haven’t built a system for entering the hexadecimal “numbers” A, B, C, D, E, and F. Probably the easiest way to enter these hex numbers is to add another six buttons to the keypad and put the letters on them. (Naturally, these buttons will have the tags 10-15.) Because we don’t need these buttons to be displayed all the time, our Cocoa Calculator will do something that no physical calculator can do: it will make itself bigger when it is in hex mode (to make room for the extra buttons), and then make itself smaller when they are no longer needed (i.e., in other bases).

To accomplish this magic, we need to learn more about how the NSMatrix, NSCell, and NSWindow classes work:

  • When we want to make our Calculator window bigger, the first question to ask is “How much bigger?” We’ll need to insert space for two more rows of buttons (six new hex-only buttons in total). Each NSMatrix knows the size of its cells as well as the spacing between cells. We’ll need to query our NSMatrix to find out how much larger the NSWindow needs to be in order to hold two more rows of buttons.

  • After we know how much space to add, we’ll need to resize the window and make sure that every object in the window moves to the appropriate place during the resize operation.

  • After the window is resized, we’ll need to create the six additional buttons that we want and set their tags appropriately.

  • Finally, when we make the Calculator window smaller, we’ll need to arrange to remove the two rows of buttons that we just added.

Modifying the Calculator’s Interface

The first thing that we’ll do is modify the Calculator’s interface so that the resize operation happens seamlessly for the interface objects in the Calculator window.

The Cocoa autosizing system determines how objects shrink, expand, or move on a window when that window is resized. A nice example of autosizing can be seen in the Mac OS X Mail application, which stretches the messages list and message preview areas when you resize the main window, but does not stretch the buttons or other controls. In our Calculator application, resizing is relatively simple, because there are only two cases we need to be concerned with:

  • When the user switches to base 16 (the Calculator window must get bigger to accommodate the two extra rows of buttons)

  • When the user switches from base 16 (the Calculator window must get smaller as the extra rows of buttons are removed)

When either of these things happens, we want the text area display and some of the buttons to stay near the top of the Calculator’s window, but we want the keypad of digit buttons to stick to the bottom. We will insert the new hex-only buttons A-F above the digit buttons 7, 8, and 9.

  1. Back in IB, select the NSTextField that is the Calculator’s text display readout.

  2. Type Command-3 to bring up the NSTextField Size Info dialog.

The Size Info dialog, which is available for all subclasses of the NSView class, enables you to control an object’s position and resizing within a window. We’ll start by focusing on the autosizing box at the bottom of the Size Info dialog:

  1. Click the bottom-most part of the cross in the Autosizing box of the Size inspector so that the vertical line at the very bottom turns into a spring-like wire, as shown in Figure 7-13.

Setting the autosizing for the NSTextField display readout

Figure 7-13. Setting the autosizing for the NSTextField display readout

The autosizing we’ve set in Figure 7-13 means that the NSTextField display area will “give” at the bottom of the window and “stick” to the top of the window when the window is resized. Thus, during autosizing, we are allowing the NSTextField to change its position relative to the bottom boundary of the window, but not relative to the top boundary.

  1. Set the autosizing for the NSMatrix containing the C and CA buttons to match the autosizing for the NSTextField display readout.

  2. Set the autosizing for the radix pop-up menu and the function keys (e.g., +, /) matrix to match the autosizing of the display readout as well.

At this point, all of the interface objects in the Calculator window have the same autosizing setting, except for the keypad, or digit-button matrix. The keypad needs a different autosizing setting because we want it to stick to the bottom of the window, not to the top of it. The reason is that we will insert the six new hex-only buttons above the digit buttons 7, 8, and 9.

  1. Select the keyPad NSMatrix and set its autosizing to look like that in Figure 7-14.

Setting the autosizing for the NSMatrix of digit buttons

Figure 7-14. Setting the autosizing for the NSMatrix of digit buttons

We’ll make only a general statement about autosizing now, and then we’ll move on. You can control the selected object’s position within a window by clicking the lines outside the inner box in the Size Info dialog’s Autosizing area, and you can control the object’s size by clicking the lines inside the inner box.

Notice that we haven’t entered any sizes such as how big the matrix is, how big the Calculator window is, or how big the window has to grow. We don’t need to find out this information ahead of time and hardcode it into our program. Instead, we’ll arrange for the Controller object to send messages to the NSMatrix and NSWindow objects to find out this information. The Controller will then calculate how much larger the window needs to grow in order to make the additional hex buttons visible and will send a message to the NSWindow object to change its size accordingly.

Modifying the Controller Class

Next, we need to modify the Controller.h and Controller.m files to make the window bigger when we switch to base 16 and smaller when we switch from base 16 to a different base.

  1. Back in PB, replace Controller.m’s setRadix: method with the much longer version that follows. In contrast to the way we implemented the previous version of this method, we’ll code the method the object-oriented way this time.

                            - (IBAction)setRadix:(id)sender
                            {
                                NSEnumerator *enumerator;
                                NSCell *cell;
                                int oldRadix = radix;
        radix = [ [sender selectedCell] tag];
        if (radix!=oldRadix && (radix==16 || oldRadix==16) ) {
            double ysize = [keyPad cellSize].height * 2 
                                                     + [keyPad intercellSpacing].height * 2;
                                    int row,col;
                                    NSWindow *win = [keyPad window];
                                    NSRect frame = [win frame];
            // If switching to radix 16, grow the window,
                                    // and keep the title bar in the same place
                                    if (radix==16) {
                                        frame.size.height += ysize;
                                        frame.origin.y    -= ysize; 
                                        [win setFrame:frame display:YES animate:YES];
                for (row=0;row<2;row++) {
                                            [keyPad insertRow:0];
                    for (col=0;col<3;col++) {
                                                int val = 10 + row*3 + col;
                                                cell = [keyPad cellAtRow:0 column:col];
                                                [cell setTag:val];
                                                [cell setTitle:[NSString
                                                                stringWithFormat:@"%X",val]];
                                            }
                                        }
                [keyPad sizeToCells];
                                        [keyPad setNeedsDisplay];
                                    }
            // If switching away from base 16, shrink the window
                                    // (keeping the title bar in the same place)
                                    else {
                                        frame.size.height -= ysize;
                                        frame.origin.y    += ysize;
                                        [keyPad removeRow:0];
                                        [keyPad removeRow:0];
                                        [keyPad sizeToCells];
                                        [keyPad setNeedsDisplay];
                                        [win setFrame:frame display:YES animate:YES];
                                    }
                                }
        // Disable the buttons that are higher than selected radix
                                enumerator = [ [keyPad cells] objectEnumerator];
        while (cell = [enumerator nextObject]) {
                                    [cell setEnabled: ([cell tag] < radix) ];
                                }
                                [self displayX];
                            }

Don’t worry if this code seems a bit complicated — it is a jump beyond what we’ve seen before! It uses a few methods from the NSWindow and NSMatrix classes that have yet to be described, but we’ll get to them before the end of the chapter.

The first new line sets up the oldRadix variable to contain the old radix. We use this to see if the user has changed the radix. If the user is changing the radix, and either the old or the new radix is base 16 (hex), the window needs to be resized.

The first part of the resizing code fills in ysize , a variable that stores the amount of vertical space that the window needs to grow or shrink. Because we are adding two new rows, ysize is exactly equal to twice the height of the keyPad cells and twice the intercell spacing. We also ask the keyPad for the id of its NSWindow object, so that we can eventually send a “resize” message to the window. We also get the frame of the window, which is its current location and size on the screen.

If we are switching to radix 16, the window needs to get bigger. We add ysize to the window frame’s height, then use the setFrame:display:animate: method to make the NSWindow bigger. This method’s animate: argument, which we set to YES, is responsible for animating the stretching, which makes our Calculator look like other Cocoa applications that animate their resizing.

Next, we insert the two rows of new buttons, using nested for loops. Each row is inserted at position 0, which is at the top of the matrix. Each row will have three buttons: A, B, and C for the row immediately above the row with 7, 8, and 9, and D, E, and F for the new top row in the matrix. The inner loop sets the tag and title of each of the new buttons as appropriate for its position.

We follow by sending the matrix the sizeToCells message, which causes the NSMatrix to recalculate its size given the fact that it now has two additional rows of cells. Finally, we send the matrix the setNeedsDisplay message, so that the matrix automatically redisplays itself when the window is updated.

The second case — making the window smaller — is much simpler. We calculate the new smaller size of the window, remove the top two rows, resize the matrix, note that the matrix needs to be redisplayed, and finally resize the window.

  1. Back in PB, build and run your upgraded Calculator, saving all files first.

  2. Click the 2, 6, and 7 digit buttons, then switch to Hex via the pop-up menu. The window should resize downward, and six new buttons should appear, as in the window on the right in Figure 7-15.

Calculator without (left) and with (right) hex buttons

Figure 7-15. Calculator without (left) and with (right) hex buttons

  1. Now switch to Binary, Octal, or Dec. The window should return to its original size.

  2. Quit Calculator when you’re done playing with your nifty new creation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.131.38.210