Symbian OS C++ for Mobile Phones VOL 1 PHẦN 5 pdf

73 363 0
Symbian OS C++ for Mobile Phones VOL 1 PHẦN 5 pdf

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Figure 11.7 I started out with code that looked something like this: void CFleetView::Draw(const TRect&) const { DrawBoard(); DrawBorders(); DrawTiles(); } This code:  draws a black square over the region of the board including both the border area and the sea area,  draws the letters and numbers for the top, bottom, left, and right borders,  draws the 64 tiles in the sea area. This is a classic flickery-draw function, in which the backgrounds are drawn first and then overpainted by the foreground. It looks especially bad towards the bottom right of the sea area, because there is a significant delay between the first function call (which painted the whole board black) and the last one (which finally painted the 64th tile). Whiting out the background There is another problem, which I'll demonstrate in Chapter 15, because I had not whited out the background area between the board and the edge of the control. I could have tackled that easily enough using, say, void CFleetView::Draw(const TRect&) const { ClearBackground(); DrawBoard(); DrawBorders(); DrawTiles(); } but that would have made the flicker even worse. Important The general solution to flicker problems is to avoid painting large areas twice. And so to my code in its present form: void CFleetView::Draw(const TRect&) const { DrawOutside(); DrawBorders(); DrawTiles(); } This code:  whites out the area of the control between the board rectangle and the border of the control – it doesn't touch the board area itself,  draws the whole top, bottom, left, and right borders – without affecting the sea area,  draws each of the 64 tiles in the sea area. My new draw-border code draws the border background and then overpaints it with the letters or numbers, which is a potential source of flicker. But the border is small and the time interval between drawing the background and overpainting the eighth letter or number is too short to notice any flicker. Likewise, the code I use to draw each tile starts by drawing the tile with its letter and then, if it's the cursor tile, overpaints the cursor. Again, this is OK – it happens so quickly that no one notices. Don't overpaint on a large scale This example emphasizes the point about the general rule for avoiding flicker: don't overpaint on a large scale. In some circumstances, redraws need to be optimized much more than I've done here. You can use many techniques for optimizing drawing to eliminate flicker:  Draw all the interesting content first – that is, draw the tiles, then the borders, and then the legend. This means that the things the user is interested in get drawn first.  Optimize the drawing order so that the tile at the cursor position is drawn first. Again, this is what the user is most interested in.  Draw subsequent tiles in order of increasing distance from the cursor tile, rather than scanning row-by-row and column-by-column.  Use active objects to allow view drawing to be mixed with user interaction – cursor movement or hit requests, for example – so that the application becomes responsive immediately.  Draw to an off-screen bitmap and bitblitt that bitmap to the screen. Each level of increased redraw optimization adds to program complexity. Fortunately, none of this was necessary for the fleet view. In some Symbian OS application views, however, these techniques make the difference between an application that is pleasant to use and one that can barely be used at all. The Agenda year view, for instance, would use all the techniques mentioned above. 11.4.2 Status View Update The status view update didn't need any optimization, even though the status view draw function appears to be quite complicated with lots of detailed coordinate calculations, font selection, and string assembly. The status view actually benefited from the buffering performed by the graphics system. As I mentioned above, drawing commands are buffered and only sent from the client application to the window server when necessary. They are executed very rapidly indeed, by the window server – typically, within a single screen refresh interval. This is too fast for a user to notice any flicker. The status view update uses only around 10 draw function calls, which probably all fit within a single buffer and so are executed all together. If the status view had been more complicated (which it would have been, had I used a suitably professional graphic design), then it might have been more flicker-prone and I would have had to take more precautions when redrawing it. Important In any professional application, the aesthetics of a view are more important than the ease with which that view can be programmed. In this book, I've paid enough attention to aesthetics to make the points I need to make, but no more. I don't really think any of my graphics are satisfactory for serious use and the status view is a prime example. In a real application, it would have to be better and if this meant the redraw code would need optimizing, then that would have to be done. Good status views are particularly demanding. On the one hand, a rich status view conveys very useful information to the user. On the other hand, the user isn't looking at the status view all the time and it must not compromise the application's responsiveness. For these reasons, status views are often updated using background active objects. A good example of a status view from the Symbian OS standard application suite would be the toolband at the top of a Word view. Of most interest to us here is that it shows the font, paragraph formatting, and other information associated with the current cursor position. Its implementation is highly optimized using background active objects and a careful drawing order so that document editing is not compromised at all. 11.4.3 Hit Reports When a hit report comes in from the opponent's fleet, the fleet view is updated to show the affected tile. Calling DrawNow() would have done the job, but it would have involved drawing the board and its borders, which is slow and completely unnecessary as these could not possibly have changed. Looking for a better approach, I considered redrawing only the tiles that were affected by the hit. These are as follows:  The tile that was hit.  If that tile was a ship, then the squares diagonally adjacent to it (provided they're on the board, and provided they haven't already been hit), because we now know that these tiles must be sea.  If the tile was the final tile in a ship, then we know that all the tiles surrounding the ship must be sea, so we have to redraw them. It turns out that working out exactly the tiles that are affected and doing a minimal redraw is nontrivial – though we could do it if it was really necessary. Instead, I decided that I would redraw all the tiles. The code would be quick enough and wouldn't cause perceived flicker because there would be no change to tiles that weren't affected. I wrote a DrawTilesNow() function to do this: void CFleetView::DrawTilesNow() const { Window().Invalidate(iSeaArea); ActivateGc(); Window().BeginRedraw(iSeaArea); DrawTiles(); Window().EndRedraw(); DeactivateGc(); } This function contains the logic needed to start and end the drawing operation and, in the middle, the same DrawTiles() function that I use to draw the board in the first place. During system-initiated redraw, the window server preparation is handled by the CONE framework. During application-initiated redraw, we have to do it ourselves before we can call DrawTiles(). The DrawXxxNow() pattern Important You can easily copy this DrawXxxNow() pattern for any selective redraws in your own applications. It's useful to pause to note a few rules about application-initiated redraw here:  Application-initiated redraw is usually done using a function whose name is DrawXxxNow().  A DrawXxx() function (without the Now) expects to be called from within an activate- GC and begin-redraw bracket, and to draw to an area that was invalid.  A simple DrawXxxNow() will invalidate activate-GC, begin-redraw, call DrawXxx(), and then end-redraw and deactivate-GC.  A more complex DrawXxxNow() function may need to call many DrawXxx() functions.  You should avoid calling multiple consecutive DrawXxxNow() functions if you can because this involves (typically) wasteful invalidation, activate-GC, and begin-redraw brackets.  You must, in any case, avoid calling a DrawXxxNow() function from within an activate-GC/begin-redraw bracket, since it will cause a panic if you repeat these functions when a bracket is already active. Later, I'll explain what the activation and begin-redraw functions actually do. Mixing draw and update functions Important Don't mix (view-related) draw functions with (model-related) update functions. For example, don't specify a function such as MoveCursor() to move the cursor and redraw the two affected squares. If you write all your model-update functions to update the view as well, you won't be able to issue a sequence of model updates without also causing many wasted view updates. The crime is compounded if your view update after, say, MoveCursor() is not optimized so that it updates the whole view. Instead, make MoveCursor() move the cursor and nothing else.You can call lots of model- update functions like this, calling an appropriate DrawXxxNow() function to update the view only when they have all executed. After a really complicated sequence of model updates, you might simply call DrawNow() to redraw the entire control. If you must combine model updates with redrawing, make it clear in your function name that you are doing so – MoveCursorAnd-DrawNow(), for example. Then your users will know that such functions should not be called during optimized update processing. 11.4.4 Cursor Movement Cursor movement is highly interactive, and must perform supremely. When writing the application, I was prepared to optimize this seriously if necessary, and that would not have been difficult to do. When you move the cursor, by keyboard or by pointer, at most two tiles are affected – the old and new cursor positions. It would have been easy to write a function to draw just the two affected tiles. But it turned out to be unnecessary. Early in development, I experimented with DrawTilesNow(), which draws all 64 tiles. That turned out to be fast enough and sufficiently flicker-free. In more demanding applications, cursor movement can become very highly optimized. A common technique is to invert the affected pixels so that no real drawing code is invoked at all – all you need to know is which region is affected and use the logical operations of the GDI to invert the colors in the affected region. However, although this technique can be very fast, it needs careful attention to detail:  Color inversion is good for black and white, but for color or more subtle shades of gray, it doesn't always produce visually acceptable results.  You still have to be able to handle system-initiated redraws, which means that you must be able to draw with the inverted color scheme on the affected region. It's insufficient simply to draw the view and then to invert the cursor region. This would produce flicker precisely in the region in which it is least acceptable. You must draw the view and cursor in one fell swoop.  In fact, you have to combine system-initiated redraws with very high application responsiveness so that the cursor can move even while a redraw is taking place. This simply amplifies the difficulties referred to, above. In general, cursor-movement optimization is nontrivial. In almost every PC application I've used (including the word processor I'm using to write this book), I've noticed bugs associated with cursor redrawing. It's the age-old lesson again: reuse existing code if you can and don't optimize unless you have to. If you do have to optimize, choose your technique very carefully. 11.5 Sharing the Screen Until now, I've covered the basics of drawing and in many cases I've had to tell you to do something without explaining why – for instance, the ActivateGc() and BeginRedraw() functions in DrawTilesNow(). Now it's time to be precise about how windows and controls work together to enable your application to share the screen with other applications and to enable the different parts of your application to work together. Symbian OS is a full multitasking system in which multiple applications may run concurrently. The screen is a single resource that must be shared among all these applications. Symbian OS implements this sharing using the window server. Each application draws to one or more windows; the window server manages the windows, ensuring that the correct window or windows are displayed, exposing and hiding windows as necessary, and managing overlaps (Figure 11.8). Figure 11.8 An application must also share the screen effectively between its own components. These components include the main application view, the button bar, and other ornaments: dialogs, menus, and the like. An application uses controls for its components. Some controls – dialogs, for instance – use an entire window, but many others simply reside alongside other controls on an existing window. The buttons on a button bar behave this way, as do the fleet views in the main application view of Battleships. 11.5.1 CONE Every GUI client uses CONE, the control environment, to provide the basic framework for controls and for communication with the window server in Figure 11.9: Figure 11.9 The window server maintains the windows used by all applications. It keeps track of their (x, y) positions and sizes, and also their front-to-back order, which is referred to as a z coordinate. As windows are moved and their z order changes, parts of them are exposed and need to be redrawn. For each window, the window server maintains an invalid region. When part of a window is invalid, the window server creates a redraw event, which is sent to the window's owning application so that the application can redraw it. Every application is a client of the window server (we'll be describing the client-server framework in detail in Chapter 18). Happily, though, it's not necessary to understand the client-server framework in enormous detail for basic GUI programming because the client interface is encapsulated by CONE. CONE associates one or more controls with each window and handles window server events. For instance, it handles a redraw event by calling the Draw() function for all controls that use the window indicated and fall within the bounding rectangle of the invalid region. 11.5.2 Window-owning and Lodger Controls I introduced you to the concept of controls at the start of this chapter. There are two types of control:  A control that requires a whole window is called a window- owning control.  A control that requires only part of a window, on the other hand, is a lodger control or (more clumsily) a non-window-owning control. Consider the dialog in Figure 11.10: Figure 11.10 It has a single window, but 12 controls, as shown in Figure 11.11. Figure 11.11 Advantages of lodgers Although a window can have many controls, a control has only one window. Every control, whether it is a window-owning control or a lodger, ultimately occupies a rectangle on just one window, and the control draws to that rectangle on that window. A control's window is available via the Window() function in CCoeControl. There are certain advantages in using lodgers:  Reduced traffic: Lodgers vastly reduce the client-server traffic between an application and the window server. Only one client-server message is needed to create an entire dialog since it includes only one window. Only one event is needed to redraw the whole dialog, no matter how many of its controls are affected. Dialogs are created and destroyed frequently in application use, so these optimizations make a significant difference.  Reduced overheads: Lodgers also reduce the overheads associated with complex entities such as a dialog because controls are much more compact in memory than windows.  Less processing : Lodgers have less demanding processing requirements. Windows may move, change z order, and overlap arbitrarily. Lodgers at peer level on the same window never intersect and they only occupy a subregion of their owning window or control. This makes the logic for detecting intersections much easier than that required for the arbitrarily complex regions managed by the window server. When you need a window All these factors improve the system efficiency of Symbian OS, compared to a scenario with no lodger controls. In order to take advantage of these features, most controls should be coded as lodgers, but there are a few circumstances in which you need a window:  When there is no window to lodge in – this is the case for the application view.  When you need shadows, as described later in this chapter. Shadows are used by dialogs, popup menus, popup list-boxes, menu panes, and the menu bar.  When you need a backed-up window – we'll come back to these later.  When you need to overlap peer controls in an arbitrary way – not according to lodger controls' stricter nesting rules.  When you need the backup-behind property (see below), which is used by dialogs and menu panes to hold a bitmap of the window behind them. Being window-owning is a fairly fundamental property of a control. There isn't much point in coding a control bimodally – that is, to be either a lodger or to be window-owning. Decide which it should be and commit to it. On the other hand, only small parts of your control's code will be affected by the decision. So, if you find out later that (for instance) your control that was previously a stand-alone app view now has a window to lodge in, then you should be able to modify your control quite easily. For instance, in the drawing example in Chapter 15, the CExample-HelloControl class adapts hellogui's CHelloGuiAppView to turn it into a lodger. The class declaration changes from: class CHelloGuiAppView : public CCoeControl { public: static CHelloGuiAppView* NewL(const TRect& aRect); ~CHelloGuiAppView(); void ConstructL(const TRect& /*aRect*/); private: void Draw(const TRect& /* aRect */) const; private: HBufC* iHelloText; }; to: class CExampleHelloControl : public CCoeControl { public: static CExampleHelloControl* NewL(const CCoeControl& aContainer, const TRect& aRect); ~CExampleHelloControl(); . . . private: void ConstructL(const CCoeControl& aContainer, const TRect& aRect); private: // From CCoeControl void Draw(const TRect&) const; private: HBufC* iText; . . . }; The essential change here is that I have to pass a CCoeControl& parameter to the control to tell it which CCoeControl to lodge in. The construction changes from: void CHelloGuiAppView::ConstructL(const TRect& aRect) { CreateWindowL(); SetRectL(aRect); ActivateL(); iHelloWorld = iEikonEnv- >AllocReadResourceL(R_HELLOGUI_TEXT_HELLO); } to: void CExampleHelloControl::ConstructL(const CCoeControl& aContainer, const TRect& aRect) { SetContainerWindowL(aContainer); SetRect(aRect); iView=CExampleHelloView::NewL(); iText=iEikonEnv- >AllocReadResourceL(R_EXAMPLE_TEXT_HELLO_WORLD); iView->SetTextL(*iText); . . . ActivateL(); } [...]... ComponentControl() as I did above, CEikDialog uses a variable-sized array to store dialog lines and calculates the answers for these functions 11 .5. 4 More on Drawing Drawing to a window is easy for programs but involves complex processing by Symbian OS as you can see in Figure 11 .12 Figure 11 .12 On the client side, an application uses a CWindowGc to draw to a window CWindowGc's functions are implemented by encoding... functions for creating new graphics contexts and fonts on the screen device Support for multiple resource files and many functions to read resources (see Chapter 7) See the definition of CCoeEnv in coemain.h for the full list 11 .6.2 Window-owning and Lodging A control may be either window-owning or a lodger A window-owning control has-a window: a lodger simply uses-a window (Figure 11 .14 ) Figure 11 .14 Either... joins 11 .8 Summary In this chapter, I've concentrated on graphics for display – how to draw and how to share the screen More specifically, we've seen the following: The CGraphicsContext class and its API for drawing, getting position, and bounding rectangles The MVC pattern, which is the natural paradigm for most Symbian OS applications MVC updates and redraws work well with standard RWindows For non-MVC... iScanCode for the extremely rare cases when scan codes are of interest The scan codes are defined by the historical evolution of IBM PC keyboards since 19 81 They originally represented the physical position of a key on the 81- key IBM PC keyboard, and have evolved since then to support new keyboards and preserve compatibility with older ones Since keyboards and keypads used on Symbian OS phones are... Finally, and most obviously, the use of lodger controls helps here too because it means the window server buffer contains only a single ResetGc() command between controls, rather than a whole end bracket for redraw and GC deactivation, followed by a begin bracket for GC activation and redraw 11 .5. 5 Backed-up Windows In the window server, a standard window is represented by information about its position,... hand, if your container is a general-purpose container such as a dialog, you may wish to implement a general-purpose array to hold your component controls 11 .6.4 Position and Size You can set a control's position and size Here are the declarations related to position and size: class CCoeControl : public CBase { m public: IMPORT_C void SetExtentL(const TPoint& aPosition, const TSize& aSize); IMPORT_C... auto repeats generated }; The legal values for iCode, iScanCode, and iModifiers are in e32keys.h Use values from the TKeyCode enumeration to interpret iCode The values are =0xf800 for the usual function, arrow, menu, and other keys found on PC keyboards Although Symbian OS phones may have no keyboard, many of these... override this function to return a component for each possible value of n Here is a generic example implementation of these functions Most Symbian OS applications use enums for their controls like: enum { EMyFirstControl, EMySecondControl, EAmountOfControls } This enables you to simply return EAmountOfControls in the CountComponentControls This ensures that you do not forget to change your return value when... { EButton1Down, EButton1Up, EButton2Down, EButton2Up, EButton3Down, EButton3Up, EDrag, EMove, EButtonRepeat, ESwitchOn, }; TType iType; TUint iModifiers; associated // Type of pointer event // State of pointing device and buttons TPoint iPosition; // Window co-ordinates of mouse event TPoint iParentPosition; // Position relative to parent window }; You can check for the following: Button 1 down (pen... protected: TPoint iPosition; TSize iSize; }; Position and size are stored in iPosition and iSize You can interrogate them with Position(), Size(), or Rect() and change them with SetExtentL(), SetPosition(), SetSizeL(), and SetRectL() Changing the size of a control could, in rare cases, cause memory to be allocated, which could fail – so all functions that change size are potentially leaving SetPosition() does . non-window-owning control. Consider the dialog in Figure 11 .10 : Figure 11 .10 It has a single window, but 12 controls, as shown in Figure 11 .11 . Figure 11 .11 Advantages of lodgers Although a window. answers for these functions. 11 .5. 4 More on Drawing Drawing to a window is easy for programs but involves complex processing by Symbian OS as you can see in Figure 11 .12 . Figure 11 .12 On. Battleships. 11 .5 .1 CONE Every GUI client uses CONE, the control environment, to provide the basic framework for controls and for communication with the window server in Figure 11 .9: Figure 11 .9

Ngày đăng: 13/08/2014, 08:21

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan