Talk about writing GUI frames with c+

Source: Internet
Author: User

Long time no use c+ write code
I like to write the GUI framework in C + + because that sense of accomplishment is tangible to see. From graduation to now wrote a lot of, are experimental nature. What fists and knives poison concealed weapon, laser nuclear antimatter, whether it is the dark trick of Heterodoxy, or the brilliant way of the noble, as long as the C + + technology has been tried. There are many low-level or advanced techniques, such as compile-time type detection, runtime code modification, and so on, which are not marginal in terms of what is implemented by different GUIs. Learn a lot from the implementation of the first imitation Mfc,atl, and then start to look at some open source of the famous GUI framework, like MFC,WTL,SMARTWIN++,WIN32GUI,JLIB2, VCF get a lot of inspiration, to now seems to have a look at the world GUI feel. In the learning of other people's framework and their own implementation process, real real-world feel that they have grown a lot, there are many sentiment.

Here I am as a wheel maker, here to admit the mistakes of critics who shout "do not make wheels". In the case of so many good wheels, I am not worth wasting the Earth's resources, wasting time and energy to build it by hand. But it is not worth it, between the worthy and the likes I choose the latter. And in life, what is worth? I think it is not to save mankind, to contribute to world peace, nor to strive for the benefit of the people of the Earth, but simply to do what they like.

Many of those codes have been lost in the ocean of hard drives, but the sentiment of those midnight oil is still there. Before they disappeared, I wanted to use my free time to write some of the experiences that felt useful, just as the blog had not been updated for almost a year. It's also a response to friends who send me emails.

My idea is to use a series of logs, according to the realization of a GUI framework of the concrete thinking progression process to illustrate the realization of a GUI framework concrete thinking progression process. This seems a bit recursive, simply explained that this series of logs is not to use the "memory fragment" as the chaotic narrative way to illustrate a more interesting story, but as far as possible to record a simple and natural writing GUI framework process of my thinking. This progressive process is the process of implementing a GUI framework, and after a series of logs, we will see a GUI framework that looks beautiful, elastic, capable, and frugal.

Although the content is written on the Windows GUI system, but the principle is comprehend by analogy, other message-based GUI systems are similar. The code used is also the principle of elaboration, self-knowledge is absolutely not up to the level of commercial masterpiece, so please do not criticize, you know I just want to share. The reason to say this first, is very afraid of the kind of "how not cross-platform ah?" "How can you still see the HWND?" "," Why can't I use the member function to process messages? "The comrade. Do not like to stand in the high place to point at someone's heads to speak. To know that the wheel is also a step-by-step, do not want to start with a mm on the high-speed road car like a teenager.

I think that writing technology blog has three kinds of realm, one is always in that vividly describe their fish how delicious and delicious, so that readers can only covet sigh, one is to give people to fish, the bulkhead on a variety of raw seafood, so that readers are difficult to digest, there is a realm is to give people to fish, how to cook fish are carefully taught to readers The person who reads the blog has two realm, one is eats the fish only, one comes up as long as the code, one is learns the fishing, wants to know it to want to know its why. Read the blog when I try to learn the type of fishing, I write a blog when I will try to give people to the realm of fishing.

It is also to be stated that, as a small individual in the earthly world, I am also struggling for survival most of the time. In addition to the remaining most of the time, but also to play games, karaoke, watch movies, accompany mm, eat and drink. The rest of the time to write this is not a lot, there may be a series of logs a night write, it is possible to delete five years begun ten, the children are called Dad has not finished. So please do not have a great expectation of this blog, when I was the roadside performance, you soy sauce pass occasionally glanced at a glance on the good.

The nonsense to say finally finished, the following start the subject.

2 Basic Concepts

The encapsulation of the message-based GUI framework, everything is expanded around the message. Complex framework design, after defining the requirements, the first step is to divide the module. Therefore, to elaborate a design process, the first step should also be to first clear the most basic concepts and module division, rather than the general theory of relativity to the reader all down. What the GUI framework does is of course the people of the Earth know, but the GUI framework has nothing to do with the standard concepts that have been partitioned, and I'm dividing it according to the needs of the design. If you think of the GUI framework as a unit, then the most important characters in this unit are the following:

? message Sender (Sender)
? Message Listener (MSG listener)
? Message Checker (msg checker)
? Message Processor (MSG handler)
? message decomposition person (msg cracker)
? message Mapper (MSG mapper)

described separately below.

2.1 Message senders and messages (message sender,message)

The sender of the message is actually just here, it is not within the framework design, the operating system plays this toiled role, its job is to send messages to the message listener. The most important role, the message, is implied here. In fact, all the remaining characters are just dead extras, the real lead is the message itself, such as the window size changed the message, the button was clicked on the message, and so on, everyone aloft the banner tightly united around it to work. But the message itself is a very simple data structure, because the complexity of the GUI system, its message is just a few parameters, so the implementation of the framework is focused on other roles. Before you can simply encapsulate the message, one of the simplest packages might look like this:

1://Message Encapsulation class

2: class Message3: {4: public:5:     Message( UINT id_=0,WPARAM wparam_=0,LPARAM lparam_=0 )6:         :id( id_ )7:         ,wparam ( wparam_ )8:         ,lparam ( lparam_ )9:         ,result ( 0 )

10: {}

11:

12:uint ID;

13:wparam WPARAM;

14:lparam LPARAM;

15:lresult result;

16:};

In this way, our company has a core role. Conceptually, our message-based GUI framework has been completed by 99%. Then we can take it as the center, according to the functional division of the detailed discussion, step by step to complete the remaining 1% of the most creative and challenging work. Before you do that, let's briefly explain what these characters are all about. The message carrier, as described above, will not be within the scope of the discussion.

2.2 Messages Listener (Message listener)

The message listener completes the work of receiving a message from the operating system, where the message is actually reached within the framework. The simplest message listener is a callback function that is provided to the operating system, such as this function on the Windows platform:

1://I am the most pristine recipient of messages

2: LRESULT CALLBACK windowProc( HWND window,UINT id,WPARAM wparam,LPARAM lparam );

A good GUI framework, of course, does not use this thing for naked naked, and we want to do object-oriented encapsulation on top of it. The most natural encapsulation pattern the message listener can think of is the Observer pattern (Observer), in which case the listener implementation looks like this:

1://I am a beautiful observer of the pattern of messages to the listener

2: class MessageListener3: {4: public:5:     virtual LRESULT onMessage( Message* message ) = 0;6: };7:8: //监听者这样工作9: MessageListener* listener;

10:window->addlistener (listener);

11:

The implementation of JLIB2 and VCF is this pattern. But most of the frameworks in reality don't use this pattern, such as smartwin++ and Win32gui, and don't even use any patterns such as MFC and WTL. I think they do not adopt the observer pattern, some because the framework of the overall implementation of the containment, and some may be because of the failed to solve some technical problems. Our GUI framework will implement the observer pattern of the message listener, so these questions we will also encounter later, and then detailed.

2.3 Message Checker (msg checker)

The work done by the message checker is simple. When the message is received, the framework invokes the message checker to check whether the message conforms to a certain condition, and if so, the framework calls the message processor to process the message, so it's a bit like a translator, input (message), and output a (yes/no) value. The simplest checker may be a comparison of a message value, such as:

1:

2: /最简单的消息检查者3: essage.id == /*消息值*/4:5: /比如6: essage.id == WM_CREATE

Expand the message map macros for MFC and ATL, and you can see that their message checking is done with a stacked message value comparison statement. This is the most primitive and natural simplest way to implement a message checker, but there are too many flaws in this way. Our framework will implement an automated, extensible message Checker, which is discussed in detail later in this article.

2.4 Messages Processor (message handler)

The Message Processor is our ultimate goal. All the efforts made by the GUI framework are only pre-prepared until the moment the message processor runs, and the whole company is really working. The specific implementation of a message processor may be a free function, a member function or other callable body, or even an external script, which may need to return a result to the operating system. The simplest message processor can be a statement, such as:

1://Message Handling

2: alert( "窗口创建成功了!" );3:4: //返回结果5: message.result = TRUE;

The "Show message box" action in the code above is a message processing, and the above two lines of code can be treated as message handlers. The most common message handlers are functions, such as:

1://Message Handling

2: _handleCreated( message );

The function _handlecreated in the code is a typical message processor. The difficulty of the message processor's implementation is to support a variety of invocation interfaces and to support a unified approach. Our framework implements a message processor that supports free functions, member functions, function objects, or other callable bodies, and these callable bodies can have different parameter lists. A detailed discussion of the message processor will be done later in this article.

It is necessary to explain it here again. Before the curly brace of a judgment statement (part of the judgment) is the action of the message check, the curly braces (the execution part) are the actual message handling. So a judgment statement is simple, but contains the message checker and the message processor, as well as another mysterious part (see below), altogether three parts. The code looks like this:

1:if (//Message Checker)

2: {3:     //消息处理者4: }

For example, the following code:

1://Message.id = = Wm_create is a message checker

2: // _handleCreated( message )是消息处理者3:4: if ( message.id == WM_CREATE )5: {6:     _handleCreated( message );7: }8:

2.5 Message Decomposition person (msg cracker)

The message decomposition is served for the message processor. Different message handlers need the same information, for example a message processor that draws a message (WM_PAINT) might need a context handle (HDC) for a graphics device, and a message processor with a button click Message (Bn_click) might need the ID of the button, None of them want to see a red naked naked in the news. The information that the message carries is decomposed from the message, which is the work of the message decomposition person. The simplest message-decomposing person could be a cast, such as:

1://wm_create message parameter decomposition

2: CREATESTRUCT* createStruct = (CREATESTRUCT*)message.lparam;3:4: // WM_SIZE 消息参数分解5: long width  = LOWORD( message.lparam );6: long height = HIWORD( message.lparam );

Although the above code is simple but 100% completes the task of message decomposition, it is also a qualified message decomposition person. My framework will implement an automated, extensible message-decomposing person. This will be discussed in detail later in this article.

2.6 Message Mapper (MSG mapper)

The message mapper is the most straightforward part of dealing with the outside of the framework, as its name implies, and it is responsible for mapping the message checker to the message processor. The simplest mapper can be a judgment statement, as shown in the code:

1://The framework of the IF statement is a message mapper

2:3: // 消息映射者4: if ( /*消息检查者*/ )5: {6:     /*消息处理者*/7: }

1: the//If statement links the message Inspector Message.id==wm_create and the Message Processor _handlecreated (msg)

2: if ( message.id == WM_CREATE )3: {4:     _handleCreated( message );5: }

In the IF statement of the above code, the part that is judged is the message inspector, and the part that executes is the message processor. The IF statement makes these two parts a mapping, which is the simplest message mapper. Here you can find out how simple this simple if statement is. It's low-key humility but has done a lot of work alone, like the company's small Zhang to write procedures, but also sweeping the tea, and the obligation to the female colleagues to tell jokes. The message map macro expansion of MFC and WTL is just such an if statement. A framework like JLIB2, although the processor is a virtual function, but at the bottom is also using the IF statement to determine the message and then call. And of course there's a gorgeous bit of message mapper, like this:

1://Gorgeous bit of message mapper

2: window.onCreated( &_handledCreated );

The oncreated is also a message mapper, which maps Wm_creae messages and _handlecreated functions together in its own way, which is most resilient, but is much more difficult to implement than macros and virtual functions. smarwin++ is used in this way, its message mapper version looks the same as the sun handsome, but the internal implementation of some of the details slightly too wretched. Our GUI framework will implement a seemingly more beautiful, well-used message mapper like this:

1://Empty the list of message handlers, set to a processor

2: // 可以这样3: window.onCreated  = &_handleCreated;4: // 或者这样5: window.onCreated.add( &_handleCreated );6:7: // 在消息处理者列表中添加一个处理者8: // 可以这样9: window.onCreated += &_handleCreated;

10://or This

11:window.oncreated.add (&_handlecreated);

12:

13://Empty Message Processor list

14://Can do this

15:window.oncreated–;

16://or This

17:window.oncreated.clear ();

It is worth saying that this magical mapper is nearly 0 cost, and it has no data members, no virtual functions, nothing, just a simple empty object. Like the legendary work ability is super strong, but do not take wages, not bubble company mm, even lunch lunch is not the ideal staff. The implementation of this message mapper is described in detail later in this article.

3 End Nonsense

So far our framework has been completed by 99%. The next article is ready to start writing the simplest message checker, but honestly I don't know when the next one will start. Look at the previous log, it was written a year ago, this year, a lot of things happened, but their own muddle as if the blink of an eye to the present, looking at Cppblog many other brothers out of a lot of very standard things, in the heart is really ashamed. Yesterday saw "2012", now there is still remnants of the world in a moment of the shock of the Ashes, 2012 is not far away, I also hurriedly in the earth before the destruction of the oil put the log finished it. Code is difficult to write.

The avoidance is a virtual function. As an example,
Class Base
{
public void Needtoinvokeondraw ()
{
((derived*) this)->ondraw ();
}
public void OnDraw () {}//NULL implementation
}

Class Derived1:public Base
{
public void OnDraw () {}//I have a new OnDraw
}

Class Derived2:public Base
{
I used the old OnDraw.
}
Execute code compilation.

Talk about writing GUI frames with c+

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.