| Napalm: | ok people |
| Napalm: | who do we have here |
| Napalm: | come on, we are going to make this an interactive lecture :) |
| Uranium-239: | "/names" |
| eth0: | woot! |
| Unknown: | yo |
| wubbster: | :D |
| src: | :O |
| Napalm: | hey Bob_Plonker |
| Xorvats: | :P |
| Bob_Plonker: | Howdy bud |
| Echo: | im here of course |
| Echo: | :) |
| Uranium-239: | Naltax isn't |
| eth0: | lol |
| Echo: | good |
| Napalm: | ok |
| tktech: | Readies his X Cannon to take on the GDI MG's |
| Napalm: | now here is the source we will be discussing |
| Napalm: | http://www.netcore2k.net/downloads/glassbuttons_src.zip |
| Napalm: | help yourselfs |
| Napalm: | MSVC6 project, Executable included |
| Napalm: | resize click and play with it |
| Napalm: | while i am doing the talk you can all read the comments and i will be taking questions at the end |
| Uranium-239: | sweet |
| eth0: | goes ooooo ahhhhhh |
| Napalm: | shit |
| Napalm: | one momenyt |
| Napalm: | looks like im missing a file |
| Bob_Plonker: | lol |
| Napalm: | ok zip is updated |
| Napalm: | redownload |
| Xorvats: | look nice |
| Unknown: | aye |
| Napalm: | for those of you on linux at the moment here is a screen-shot http://www.netcore2k.net/images/glassbuttons.jpg |
| Napalm: | its slightly older than the final |
| Napalm: | Ok. on with the talk |
| eth0: | the type* |
| Napalm: | i will first cover some of the basics, but as i said before this is really for the intermediate windows programmer |
| Napalm: | to create GUI apps in windows we call a subset of the windows api |
| Napalm: | the two main librarys we deal with are kernel32 and user32 |
| Napalm: | these form the user-mode side of the win32 subsystem |
| Napalm: | we receive GUI notifications of user input and response via a messaging system |
| Napalm: | if you look in GlassButtons.cpp I will describe the control flow as the programs load |
| Napalm: | *program |
| Napalm: | i assume you all have had time to take a quick glance over the other files in the project |
| Bob_Plonker: | quick, yup |
| Napalm: | Once windows starts our program and initalizes our process it gives our application control |
| Napalm: | the function which windows calls is WinMain |
| Napalm: | here we startup and initalise everything we need for our program to execute correctly |
| Napalm: | as you can see in the source we start the GDI+ library and create a new class instance for our window |
| Napalm: | for those of you that are not all to familar with C++ the TestWindow class is derived from BaseWindow |
| Napalm: | this means all of the members that is to say methods and variables of BaseWindow become part of TestWindow |
| Napalm: | they are not copied to the new class but mapped |
| Napalm: | i wont go into detail about this but you can find info here: http://en.wikipedia.org/wiki/Virtual_table |
| Napalm: | now |
| Napalm: | since our GlassButton class and TestWindow class share some common factors, they are both Windows |
| Napalm: | both have to have a Window Handle Value (unique id) to communicate our intentions to the win32 api |
| Napalm: | you will find this stored as HWND m_hWnd; in BaseWindow |
| Napalm: | As i said before we recieve messages from widows |
| Napalm: | windows |
| Napalm: | but hows does windows know what to tell us about |
| Napalm: | well everything in the windows GUI system is a 'window' |
| Napalm: | that means a Button/Scrollbar/Menu/Custom-Control/Anything are all 'windows' |
| Napalm: | these are seperated by what we call a Class |
| Napalm: | we define a class structure and register it with windows so that when we decide to create a window it knows what base properties to give it |
| Napalm: | in this class structure we also tell windows what function we want to recieve messages on |
| Napalm: | this is of type WNDPROC |
| Napalm: | now |
| Napalm: | the idea behind using the C++ classes is so that we can dispatch messages and managed window state |
| Napalm: | you will see in WinMain we attempt to create an instance of TestWindow |
| Napalm: | and after that we can use the HWND cast operator to perform actions on our window by simply directing it to the class |
| Napalm: | to start with we animate the window to show it on the screen |
| Napalm: | http://www.netcore2k.net/downloads/glassbuttons_src.zip |
| Napalm: | after that in WinMain we use the GetMessage and friends to hand our the messages we receive from windows to our functions |
| Napalm: | by default the windows api tells us that any message we dont want to handle should ideally be passed on to DefWindowProc so it can choose to do the default action |
| Napalm: | this is so that when a user uses things like hot keys to minimize the window, it will actually work. if you did not pass the WM_SYSCOMMAND message on to the default handler then you would need to provide the code to action these events |
| Napalm: | ok, so now lets take a look at BaseWindow.h |
| Napalm: | here we define the class and give it some protected variables |
| Napalm: | if the C++ newbs are out there here is a quick definition of the class qualifiers |
| Napalm: | protected: private to this class and derived classes |
| Napalm: | private: private to this class and can only be used by its members |
| Napalm: | public: is accessable from outside the class with global access |
| Napalm: | these are the only access schemes we have, so we need to really use them to great effect |
| Napalm: | for instance we dont want to give public write access to the m_hWnd variable since its managed by BaseWindow |
| Napalm: | we wouldnt want an another piece of code to accidentally change its value and mess up message handling in our program |
| Napalm: | another value we are always going to use is the client-rect |
| Napalm: | i suppose i better explain what this is |
| Uranium-239: | could another piece of code modify it by writing to its memory address? |
| Uranium-239: | or would that raise an exception |
| Napalm: | it would be very difficult to access it |
| Napalm: | since its a member variable is dynamic in nature so is the address |
| Uranium-239: | ok |
| Napalm: | it would not raise an exception, but it would be hard to access |
| Napalm: | back to the talk |
| Napalm: | ok, client-rect |
| Napalm: | each window/button/control |
| Napalm: | whatever |
| Napalm: | has two main areas it defines |
| Napalm: | 'non-client area' and 'client area' the two restrictions are that they can only be rectangles and the 'client area' must fit inside the 'non-client area' |
| Napalm: | rohitab: http://www.netcore2k.net/downloads/glassbuttons_src.zip |
| rohitab: | thanks |
| Napalm: | the non-client area is where a overlapped (standard windows) has its window caption bar with the minimize/max/close buttons |
| Napalm: | the non-client area also encompasses the window frame aka border |
| Napalm: | now everything you see on y |
| Napalm: | your windows machine is actually drawn/painted |
| Napalm: | there is no real level of control |
| Napalm: | all windows handle mouse and keyboard input and react accordingly |
| Napalm: | so the standard windows push button simply 'feels' like a button because it looks and acts like one |
| Napalm: | its really the same as any other window |
| Napalm: | now even child-windows (controls on windows) can have a non-client area |
| Napalm: | for instance the standard edit-box (text entry field) has a non-client area where it draws the window edge |
| Napalm: | the old-classic windows look used two styles |
| Napalm: | WS_BORDER and WS_EX_CLIENTEDGE |
| Napalm: | the client-edge gives the window the sunken look |
| Napalm: | the border style gives it a single pixel black border |
| Napalm: | ok, so we have established some basic facts |
| Napalm: | we have a non-client area and client-area |
| Napalm: | now, we recieve a WM_PAINT and WM_ERASEBKGND messages to draw the content in windows |
| Napalm: | windows uses a two stage system, mainly as a fall back from the days of old and windows was 3.1 |
| Napalm: | simply because you wouldnt ever do any drawing operations you could avoid back then because the system was task-swapping and not pre-empting |
| Napalm: | so the longer you took drawing the less time other applications got to do their thing |
| Napalm: | the erase background uses our windows-class's background brush to fill the window |
| Napalm: | by default (DefWindowProc) |
| Napalm: | the flashing/flickering you see for instance when you resize an instance of notepad.exe is because it recieves a WM_ERASEBKGND message which paints the entire client-area white and then it draws the text back in WM_PAINT |
| Napalm: | would could intercept the WM_ERASEBKGND message and stop it being passed to DefWindowProc this would stop that flickering |
| Napalm: | but this also means we now have to paint the entire window in WM_PAINT |
| Napalm: | or we would simple get "copies" of the text repeating on o |
| Napalm: | us |
| Napalm: | this still has an issue |
| Napalm: | if we were to paint our window white and then draw text on it the time in-between these two things would show a flicker |
| eth0: | woops |
| Napalm: | this is because we actually draw to the bitmap memory of the screen |
| Napalm: | and the screen refreshes very quickly at say 70Hz (thats 70 times a second) |
| Napalm: | this is why we see a flicker |
| Napalm: | since the screen refreshes inbetween us painting to the background and then painting say text on top |
| Napalm: | so, to fix this we are going to need to paint our window to a memory bitmap and then copy that bitmap in one foul swoop to the screen |
| Napalm: | this as a disadvantage in the fact that is memory expensive |
| X-N2O: | Back-buffer HDC? |
| Uranium-239: | im guessing a double buffer |
| Napalm: | say a 1024x768x24bit is 2.3MB just for the bitmap data |
| Uranium-239: | and copy it over on WM_PAINT messages |
| Napalm: | shhh |
| Napalm: | si |
| Napalm: | so |
| Napalm: | it solves it and with todays memory and speeds its not a real issue |
| Napalm: | since the memory bitmap is only uses for a split second during the painting its not too expensive |
| Napalm: | but to allocate and then free that every time is |
| Napalm: | so, what we want to do is to create a static bitmap outside of the message handling so we can resuse the same bitmap |
| Napalm: | this solves flickering |
| eth0: | hold on |
| Napalm: | and the term is also called double-buffering |
| eth0: | so just to be clear, copying it from memory paints it at a faster screen rate then ... ? |
| Uranium-239: | nope |
| Uranium-239: | its about copying it over in one swoop |
| Napalm: | ltos of editing operations on the bitmap are going to be slower than one single block copy |
| Napalm: | *lots |
| eth0: | ahh ok thank you |
| Napalm: | now |
| Napalm: | its always better to try and not over-paint pixels |
| Napalm: | this as i said is what causes the flicker |
| Napalm: | so instead of a memory bitmap we could always try to avoid paint over previously painted pixels |
| Napalm: | this is quite impossible with say text |
| Napalm: | but if you wanted to display a grid, white background with grey lines |
| Napalm: | the grey lines would flash everytime the white background is painted |
| Napalm: | but if we were to say draw lots of small white rectangles a pixel apart and then fill the gaps with grey lines |
| Napalm: | there would be no flicker |
| Napalm: | so, think about what your going to paint before you paint it |
| Napalm: | it helps |
| Napalm: | so, we've kinda delved in to the land of GDI |
| Napalm: | Graphics Device Interface |
| Napalm: | needed to look that one up, long time |
| Napalm: | so |
| Napalm: | GDI is like a sub-section of win32 |
| Napalm: | it serves as a system for drawing |
| Napalm: | now as for windows have HWND (Handle Window) on GDI we also have HDC |
| Napalm: | Handle to Device Context |
| Napalm: | now a HDC is a representation of a group of objects |
| Napalm: | these objects are what the windows GDI functions use to draw with |
| Napalm: | for instance lets say we want to draw a rectangle |
| Napalm: | we would use the following API |
| Napalm: | BOOL Rectangle(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect); |
| Napalm: | now as you can see, how come we cant specify a color for our rectange? |
| Napalm: | how about if we want to fill our rectangle? |
| Napalm: | well this is because the Rectangle API will do it for us |
| Napalm: | it uses the currently "selected" objects in the HDC |
| Napalm: | so lets say we want to draw a 10x10 pixel rectangle with a 2 pixel red border with a yellow inside |
| Napalm: | we would do this |
| Napalm: | ok i think i shall explain this step by step |
| Napalm: | first we need to create a [paint]brush to fill the rect with |
| Napalm: | HBRUSH hbYellow = CreateSolidBrush(RGB(255, 255, 0)); |
| Napalm: | quite straight forward |
| Napalm: | we now need to select it into the HDC |
| Napalm: | so we call SelectObject |
| Napalm: | HBRUSH hbOld = (HBRUSH)SelectObject(hdc, hbYellow); |
| Napalm: | if you select in a brush it will return the old brush |
| Napalm: | now before you destroy a HDC you need to make sure it was in its original state |
| Napalm: | so we save the old brush |
| Napalm: | and once we are done painting we select the old brush back in |
| Napalm: | HPEN hpRed2 = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); |
| Napalm: | HPEN hpOld = (HPEN)SelectObject(hdc, hpRed2); |
| Napalm: | so that would create a 1 pixel solid red pen |
| Napalm: | which will be used to draw the border to our rectangle |
| Napalm: | so we |
| Napalm: | Rectangle(10, 10, 20, 20); |
| Napalm: | to perform the action |
| Napalm: | this paints to the HDC |
| Napalm: | ths means it actually modifies the pixels in the currently selected bitmap |
| Napalm: | as i said before HDC is a collection of objects, this also means it contains a link to the bitmap where you want to perform your modifications |
| Napalm: | after we have called Rectangle we want to reset the state of the HDC and return things to normal |
| Napalm: | DeleteObject(SelectObject(hdc, hpRed2)); |
| Napalm: | DeleteObject(SelectObject(hdc, hbOld)); |
| Napalm: | hpOld |
| Napalm: | my bad |
| Napalm: | so as you can see the SelectObject does a nice thing here |
| Napalm: | it returns the current brush/pen/object handle to the us which we pass back on to DeleteObject |
| Napalm: | we could also call SelectObject and ignore the return value |
| Napalm: | DeleteObject(hpRed2); DeleteObject(hbYellow); |
| Napalm: | and do that |
| Napalm: | but either way, no difference |
| Napalm: | any questions so far? or has everyone died of bordem? |
| eth0: | BRILLIANT! |
| Unknown: | good stuff |
| TeraBiT: | nope not died of boredom, really interesting stuff this is, and you're good at explaining them ^^ |
| src: | yeah learning alot |
| TeraBiT: | explaining it* |
| Unknown: | yeah but gonna haev to read the logs... |
| Napalm: | ok, so |
| TeraBiT: | wee vista back on |
| Napalm: | lets see now |
| Napalm: | so back to the example program http://www.netcore2k.net/downloads/glassbuttons_src.zip |
| Napalm: | wait, hmm.. going to light up a smoke |
| Napalm: | so wow ive been talking awhile |
| ABK: | btw before you kick off again |
| ABK: | 31<ABK>30 what compiler? MSVC++? |
| Napalm: | MSVC6 |
| ABK: | ahh 2008 version won't work? |
| Napalm: | but any MSVC compiler will work, also the intel compiler should work |
| ABK: | it DID compile it |
| Napalm: | yes ABK, start a unmanaged C++ project |
| ABK: | okay |
| Napalm: | windows application |
| Napalm: | and then add the source files to the blank project |
| Napalm: | should compile |
| ABK: | unamanged? |
| ABK: | anad what kinda windows app? sorry for beign such a newbie at this but it's not jsut one windows app template |
| ABK: | and* |
| ABK: | 'Empty Project'? |
| ABK: | that one? |
| Napalm: | you'll just have to try and see |
| Napalm: | i dont have 2008 in front of me and cant say |
| ABK: | ahh |
| Napalm: | ok, any questions about GDI? |
| Napalm: | anyone |
| Napalm: | ok, onward |
| Napalm: | so.. i suppose we can call this the second half |
| Napalm: | so back to WinMain and we call Create |
| Napalm: | of our new TestWindow class |
| Napalm: | this creates a actual window on the screen we can communiate with |
| Napalm: | the Create function is on BaseWindow |
| Napalm: | lets take a look |
| Napalm: | so, we call RegisterClassEx to register the base properties of our new window |
| Napalm: | but, the class-names are application or system global |
| Napalm: | so what happens when we have already registered it, RegisterClassEx fails |
| Napalm: | so we check to see if the class exists with GetClassInfoEx |
| Napalm: | if it doesnt exist things when bad so we return NULL and this will cause our code in WinMain to bring up a MessageBox to the user to let them know of that fact |
| Napalm: | if all went well we call CreateWindowEx |
| Napalm: | this function does not return until your window has processed the WM_NCCREATE and WM_CREATE (NC = non-client) |
| Napalm: | the CreateWindowEx function itself contains a "message pump" itself to handle the starting messages for the window |
| Napalm: | so if we scroll back to the top of BaseWindow to its constructor we can see the class properties |
| Napalm: | as you can see we define BaseWindow::WndProc as our procedure and we also say that each window is given some extra bytes of storage space |
| Napalm: | by the sizeof(LONG) |
| Napalm: | this will let us store a pointer of our class instance, more on that later |
| Napalm: | we setup some basic things like default icon and mouse cursor |
| Napalm: | the sizeof(LONG) is 32bit so this wont work on 64bit systems |
| Napalm: | we need to update the code to work on both, we can do that by replacing LONG with LONG_PTR |
| Napalm: | windows sets the correct type size when being compiled as 64bit then |
| Napalm: | the reason i have not done this in the code is that a lot of you dont have the PSDK (Platform Software Development Kit) which i suggest you all get and stop relying on the MSDN for API doc |
| Napalm: | since the PSDK also gives you updated librarys and headers |
| Napalm: | so back to the top of this source files BaseWindow.h |
| Napalm: | and youi can see we define a couple of virtual functions |
| eth0: | so will your code work on a 64bit system |
| Napalm: | yes, if compiled as 32bit app |
| eth0: | ahh ok |
| Napalm: | you need to change the types so that we have room to store the 48bit addressing used by windows x64 |
| Napalm: | so back to the virtual functions |
| Napalm: | these can be overridden by derived classes |
| Napalm: | if a virtal function pointer member is declared as = 0 it means it has no pointer and therefore must be overridden |
| Napalm: | futher on we declare a static WndProc "inside" our class |
| Napalm: | but because its static it is global |
| Napalm: | this is a requirement since a member functions uses the 'thiscall' calling convention where ECX contains the class pointer |
| Napalm: | and windows WNDPROC type defines a windows procedure as a 'stdcall' calling convention function |
| Napalm: | so, we need to translate this fact so we can handle the messages inside our class |
| Napalm: | this is a simple trick |
| Napalm: | because we declare the WndProc inside our class definition we are essentially telling the compiler it has the 'friend' qualifier and therefore can access protected members |
| Napalm: | so when we receive a message it comes to our BaseWindow::WndProc function |
| Napalm: | as you can see we need to be able to get a pointer to the class |
| Napalm: | we do this with GetWindowLong to access our 'window extra byte' storage |
| Napalm: | the second parameter to the function is the offset in the byte storage |
| Napalm: | windows returns a dword from that address |
| Napalm: | GetWindowLong is only 32bit safe |
| Napalm: | we need to use GetWindowLongPtr if we want this code to compiler as 64bit |
| Napalm: | and/or 32bit |
| Napalm: | we then proceed to handle WM_NCCREATE because this is the first valid message we want |
| Napalm: | we then cast the lParam as a CREATESTRUCT which contains the LPVOID we passed in CreateWindowEx |
| Napalm: | if you noticed the last parameter to CreateWindowEx is the 'this' pointer for the current class |
| Napalm: | now we have the pointer we need to set the bytes for the first and only time |
| Napalm: | 'extra window bytes' i should say |
| Napalm: | we then update the LPVOID to our hWnd so we can store it later in m_hWnd |
| Napalm: | as i said before this is a static function so we cant access m_hWnd |
| Napalm: | if we have a pointer we can then execute member functions of our class because we (the WndProc function) qualifies as a 'friend' to the class |
| Napalm: | so we hand off the message to PreHandleMessage which keeps our m_hWnd and m_rcClient variables up to date |
| Napalm: | and then we forward that same message to the overridden HandleMessage function |
| Napalm: | this is defined in both TestWindow.h and GlassButton.h |
| Napalm: | the HandleMessage function can decided where it wants to handle any message and if it doesnt then the message gets post-processed by PostHandleMessage |
| Napalm: | this will hand off to our own default actions |
| Napalm: | in this case WM_PANT and WM_PRINTCLIENT |
| Napalm: | the importance of handling WM_PRINTCLIENT and not just WM_PAINT is that AnimateWindow and other windows api's might need to paint your window to a HDC they own |
| Napalm: | back on topic |
| Napalm: | if its any other message we dont want to handle in a specific case we pass it on to DefWindowProc |
| Napalm: | that concludes BaseWindow.h |
| Napalm: | so on to TestWindow.h our "Main Window" |
| Napalm: | here you see we override some of the default properties of the class definition |
| Napalm: | the style and exstyle are most important |
| Napalm: | this defines how our window is to be displayed |
| Napalm: | see GlassButton.h for an example of a child style definition |
| KOrUPt: | you know it sucks that i missed the start of this presentation, i'll read over the logs afterward |
| Napalm: | np |
| KOrUPt: | sorry for interrupting, continue... |
| Napalm: | ok, so im not going to explain all the code |
| Napalm: | i only wanted to explain the details of BaseWindow.h |
| Napalm: | but the comments should explain most of the code |
| Napalm: | on OnCreate we initalise our GlassButtons |
| Napalm: | we return -1 on failure and this forces CreateWindowEx to also fail and return NULL and again this would then display a MessageBox in WinMain |
| Napalm: | ive also created a small helper class called ResImage |
| Napalm: | this lets you load up a JPEG/GIF/ICON/BMP whatever from resource and use it as-if it was a HBITMAP in your GDI operations |
| Napalm: | we use WM_NCDESTORY aka OnNcDestroy instead of WM_DESTORY because WM_DESTORY is sent to use before our child windows and we dont really want to go and delete the class instances of our glassbuttons while the windows are still actibe |
| Napalm: | active |
| Napalm: | PostQuitMessage tells GetMessage to terminate our message pump loop |
| Napalm: | in WinMain |
| Napalm: | the value of its parameter is put in the wParam member of the MSG msgPump; structure in WinMain and this is what we return |
| Napalm: | so if we have a explicit failure we could make a function which uses PostQuitMesage(ERROR_CODE) so we known immediate what caused the problem |
| Napalm: | the rest of the message handlers in this class are quite simple |
| Napalm: | further down we reach OnPaint |
| Napalm: | this as you can see stretches the image we loaded via the HDC cast of the ResImage class |
| Napalm: | SelectFont and SelectBitmap and its friends are macros in windowsX.h |
| Napalm: | they simply fake SelectObject with casts |
| Napalm: | it makes things easier to read, thats all |
| Napalm: | as you can see, i draw the status text twices at a 1x1 offset so we get a slight black shadow |
| Napalm: | it makes the text easier to read in the center |
| Napalm: | and the OnClose handles the X button of the window |
| Napalm: | this animates and destroys our window we then receive a WM_DESTORY and we call PostQuitMessage to exit our application |
| Napalm: | ok... part 3 |
| Napalm: | lol |
| Napalm: | what you all like |
| Napalm: | GlassButton.h |
| Napalm: | this is not a sub-classed button we re-implement the mouse handling and keyboard input |
| Napalm: | this is so we can tab between buttons and use the space key to press the button |
| Napalm: | this is quite essential for accessiblity |
| Napalm: | same as before really |
| Napalm: | we initalise the vars |
| Napalm: | the 0xB28DBDFF is ARGB thats Alpha, Red, Green, Blue |
| Napalm: | standard 32bit color definition |
| Napalm: | this is our default blue coloured glow |
| Napalm: | further down we give the public option to change the glow |
| Napalm: | and handle the messages |
| Napalm: | we handle WM_GETDLGCODE because we might want to place our button in a dialog |
| Napalm: | im guessing you all have read the code by now |
| Napalm: | can anyone note some further changes? |
| Napalm: | well at the moment the control does not handle these things |
| Napalm: | + Enable state (to show the button as disabled / greyed out) so it cant be clicked |
| Napalm: | + perhaps the ability to set an icon to the button |
| Napalm: | + also it should pay attention to the window styles and react to the Default push button and default cancel button styles |
| Napalm: | this would let use be used for pressing Enter or Escape on a dialog |
| Napalm: | ive commented this alot |
| Napalm: | all of the mouse code maintains the state of the button and reacts to the mouse changes |
| Napalm: | the glow which i guess you all like |
| Napalm: | is achieved by the use of a Timer |
| Napalm: | windows maintains the timer and we call SetTimer and KillTimer to manage it |
| Napalm: | when the time for the timer elapses in this case 10ms (about the lowest pratical resolution) we get a WM_TIMER message |
| Napalm: | we handle that message to change the "glow level" which is a floating point percentage of how much to alpha the glow color |
| Napalm: | the OnTimer does the hard work here |
| Napalm: | it maintains the level of glow |
| Napalm: | so if the mouse moves out of the window (m_bMouseHover == FALSE) we decrement the glow |
| Napalm: | this way the glow doesnt jump around |
| Napalm: | now |
| Napalm: | on to OnSize |
| anubis: | wouldn't it be better to use WM_MOVE and TrackMouseEvent? |
| Napalm: | WM_MOVE is when the window moves and not the mouse moves |
| anubis: | or WM_MOUSEMOVE :| |
| Napalm: | i do use MouseMove |
| Napalm: | :P |
| Napalm: | as i said before our non-client area and client-area are rectangles |
| Napalm: | but we have rounded rectangles to our windows? |
| Napalm: | this is because we set the Window Region |
| Napalm: | a region can be any shape or size |
| Napalm: | its almost like a mask |
| Napalm: | the window is still rectangle |
| Napalm: | its just windows ignores things outside the region |
| Napalm: | we could even place a "hole" inside our region and we would have a "hole" in our window |
| Napalm: | this would not be deseriable because we wouldnt be able to paint there |
| Napalm: | we could still have the window underneath display though, but that would cause flicker |
| Napalm: | we trick the user into thinking our window is transparent |
| Napalm: | because no child window on a windows system can have a transparency |
| Napalm: | its all a shame |
| Napalm: | we copy what would have been behind our window from the parent onto our window |
| Napalm: | this is handled by the last function in our class |
| Napalm: | more on that later |
| Napalm: | the OnSize sets the region |
| Napalm: | so that we dont get WM_MOUSEMOVE and other messages when the mouse is on the corner areas |
| Napalm: | this is because essentially those corners our now part of the parent and not us |
| Napalm: | also note that after you call SetWindowRgn the HRGN you give it is OWNED by windows |
| Napalm: | you must not reuse it or destory it |
| Napalm: | destroy |
| Napalm: | this is different to the SelectClipRgn api in which a copy of your region is selected in and you still need to free your original |
| Napalm: | you also need to call SelectClipRgn with NULL so that the copy of the original is destroyed |
| Napalm: | now onward |
| Napalm: | nearly done |
| Napalm: | Redraw is a simple helper function to save code since we want to redraw the control a few times |
| Napalm: | like when the state changes |
| Napalm: | the m_bCacheDirty flag tells the WM_PAINT handler to destory the old image and use a new one |
| Napalm: | since WM_PAINT will not redraw the control but reuse the cached copy |
| Napalm: | this is used to save processor time |
| Napalm: | lets say a window is above a button |
| Napalm: | the window needs to be redrawn when it is uncovered |
| Napalm: | our app would use the cached copy and not repaint it |
| Napalm: | the RedrawWindow is passed with the UPDATENOW which means the WM_PAINT handler is called before the RedrawWindow function returns |
| Napalm: | this is important |
| Napalm: | this is so that actions like clicking the button are action immediatly |
| Napalm: | if we were to simply invalidate the window (this tells windows it needs to repaint us) |
| Napalm: | it might take a while to process |
| Napalm: | since lots of other messages might be in our message queue |
| Napalm: | and also because WM_PAINT is a low priorty message |
| Napalm: | priority |
| Napalm: | on the the big one |
| Napalm: | OnPaint |
| Napalm: | this does all the hard work |
| Napalm: | Here is where the GDI+ code starts |
| Napalm: | and yes, GDI+ has a C API which you can use instead of the GDI+ C++ classes |
| Napalm: | does anyone want any explainations on this code |
| Napalm: | or do the comments do it enough |
| Napalm: | ? |
| Napalm: | i guess not, or everyone have fallen asleep |
| Napalm: | either way i am continuing for the log |
| src: | i think the comments are suffecient |
| KOrUPt: | :) |
| Kenaneo: | sure on |
| Napalm: | in this function we take the State flags we previously set and paint the button correctly |
| Napalm: | remember we are drawing to a bitmap |
| Napalm: | so there is no-such thing as layers |
| Napalm: | we build up the image from back to front |
| Napalm: | so we start with the background and add things on top |
| Napalm: | all hex color codes are ARGB |
| Echo: | yeah i figured |
| Napalm: | the glow is calculated by taking the original color and shifting out the alpha and mutipling it by our alpha level |
| Napalm: | this means if our glow color has a 50% alpha level |
| Napalm: | our glow range is 0-50% |
| Napalm: | 100% glow leve is 50% color |
| Napalm: | *level |
| Napalm: | next we get the window text and draw it center of the button |
| Napalm: | we could modify this so that if a private class member of an icon is set we draw an icon to the left of the text |
| Napalm: | this icon could even be 32bit so it has a alpha channel |
| Napalm: | as you can see we clip all graphics to the inside of the button so we dont draw over our borders |
| Napalm: | the CreateRoundRectPath function after onPaint is needed because for some odd reason GDI+ has no AddRoundRect member on the GraphicsPath class |
| Napalm: | in standard GDI we can use the CreateRoundRectRgn API |
| Napalm: | so if your control/window is GDI only that is perfect since we can clip to regions in GDI |
| Napalm: | the DrawRoundRect is just to get a nice alignment in the anti-aliased borders |
| Napalm: | without that the borders are so close they might overlap and destory each other |
| Napalm: | the PaintParentBackground function does a neat trick |
| Napalm: | we pass it a HDC |
| Napalm: | we set the window origin |
| Napalm: | now this is not Window as in a screen object |
| Napalm: | this is 'window' used in the display context of the word |
| Napalm: | 'window' of an area |
| Napalm: | lets say the orgin was 10, 10 normally its 0, 0 |
| Napalm: | this means all drawing operations gets translated and 10 pixels are added to the x and y |
| Napalm: | so if i called the GDI operation LineTo(hdc, 10, 10); |
| Napalm: | it actually draws a line from 10, 10 to 20, 20 |
| Napalm: | we use this to our advantage |
| Napalm: | we set the origin to our location on the parent window |
| Napalm: | we use MapWindowPoints to do that |
| Napalm: | and then we Create a rectangle region for our client size (our width and height) and then clip all further painting to our HDC to that area |
| Napalm: | this means when we call the parent WM_PRINTCLIENT it only uses the portion of our width and height at our location on the parent |
| Napalm: | we then call WM_PRINTCLIENT and reset everything |
| Napalm: | wow |
| Napalm: | that was a lot of typing |
| Napalm: | im sure i have RSI |
| Napalm: | lol |
| Napalm: | so |
| Napalm: | lets get this long ass lecture over with |
| Napalm: | Q and A time |
| Napalm: | ask away and i will do my best to answer |
| anubis: | :D |
| anubis: | what's rsi |
| Napalm: | http://en.wikipedia.org/wiki/Repetitive_strain_injury |
| Napalm: | now how about some questions to do with the project? |
| Napalm: | anything? |
| KOrUPt: | i'll likely have some tomorrow morning once i finish reading over the (long) log |
| Napalm: | wow, i must have done a good job explaining if there are no questions |
| anubis: | yuz |
| Napalm: | my thoughts are just that everyone as got bored |
| src: | twas over 500 lines :O |
| Napalm: | and left |
| Napalm: | lol |
| Echo: | nope, we are here |
| src: | informative, thanks Napalm |
| Napalm: | i suppose i better stop the log bot |
| Kenaneo: | me still checking the code |
| Napalm: | ah |
| Napalm: | hmm, there is another topic i could cover |
| Echo: | hopefully everyone can understand that by using classes as a container the code stays extremly organized |
| Napalm: | before i close the log bot |
| Napalm: | if you are all want to listen to more? |
| KOrUPt: | go ahead |
| Echo: | what is the topic |
| src: | mhm |
| Napalm: | Thunking |
| Napalm: | http://en.wikipedia.org/wiki/Thunking |
| Napalm: | we can use a small piece of assembly code instead of our WndProc |
| Echo: | do it |
| Napalm: | and it would alot more efficent then using the 'extra window bytes' as storage |
| Napalm: | the idea goes like this |
| Napalm: | we great a small asm code like: |
| KOrUPt: | create* |
| Napalm: | lea ecx, this; mov eax, proc; jmp eax |
| Napalm: | args its late and im sluring my words |
| Napalm: | lol |
| Napalm: | ok |
| Napalm: | so the asm code above would move our class pointer into ecx |
| Napalm: | we then move the true method address for our private function in to eax and call it |
| Napalm: | this means we have simulated a 'thiscall' function call |
| Napalm: | so if we allocate a piece of executable memory using virtualalloc |
| Napalm: | insert our machine code and pointers and then set the classes WndProc to that |
| Napalm: | well, we would set it on a per-window basis with SetWindowLongPtr(hWnd, GWLP_WNDPROC) |
| Napalm: | this means, we essentially call our member procedure directly and handle the messages |
| Napalm: | this trick can be done with any other CALLBACK api function in windows |
| Napalm: | so for say a static timer from SetTimer |
| Napalm: | or even the result of a call to waveOutOpen |
| Napalm: | all sorts |
| Napalm: | quite a nifty idea |
| Napalm: | anyways, thats all |
| Napalm: | any questions before i stop the log bot? |
| falkman: | applauds. |
| Napalm: | bows |
| Napalm: | ok, bot stopping |