NetCore2K.net - Brought to you by Napalm and Typhoon
HOME PROJECTS ARTICLES FORUM LINKS
Username: Password:



"Debugging is anticipated with distaste, performed with reluctance, and bragged about forever."

 Projects

 Windows GUI Glass Buttons

Download Source  Download Binary  
> HelpPC 2.10 Quick Ref. Utility
An invaluable resource of real-mode programming related information.
> Windows GUI Glass Buttons
Small lecture on Windows GUI and GDI.
> FreeRAM
Memory boosters, optimizers, and washers.
> BlackCORE
x86 Operating System

I recently did a small lecture/talk on Windows GUI and GDI over on irc://irc.rohitab.com/tutorialtime. It went quite well and seemed to be a great success, spanning almost 3 hours. Hopefully the source is very educational. The IRC lecture log is below and the download links are next to the title of this page. Many thanks to unfunf and Limitz for the TutorialTime project which made this possible.



The window is resizable and the buttons Glow!


IRC Log

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

Link: IRC Logs on #tutorialtime


Many thanks to all who attended,
Napalm


Copyright © Netcore2K.net.
All rights reserved.
Contact Us