AutoHotkey Community

It is currently May 27th, 2012, 5:27 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 15 posts ] 
Author Message
PostPosted: February 15th, 2011, 6:02 pm 
Offline

Joined: December 30th, 2007, 5:42 pm
Posts: 31
Location: East Coast
Focused Control Colored Border Animation
GDI/Gdip, DllCall, And Onmessage

This example script draws a colored border around the currently focused control in an AHK GUI using onmessage, tics gdip library found here, and Lexiko's WinToClient function found here. I know that some controls have a border property that can be turned on and off. The example below can be used to specify any color border and any location for the border. You could also use this to draw a colored groupbox, to draw other shapes, and to draw text. There's many more examples that can be adopted from tics topic. I'm sure it's also possible to turn off the standard control outlines and borders and draw your own.

This example is using onmessage to draw a border around the focused control when the script receives the WM_COMMAND 0x111 message. The other onmessage, WM_SYSCOMMAND 0x112, is used to reset the border drawing when the GUI is restored from being minimized. The biggest lingering problem I'm having, is GUI flickering when the focus is switched quickly and repeatedly. I'm trying to find the best way to do this, I know it can be optimized further. I'm also open to and welcome suggestions for additional things to add to this.

The client area offset may be different for your system, it is going to be different for XP, Vista, and 7 and it may be different for different themes. You can fix it for now by changing the CX -= 1 an CY -= 1 offset as needed. I have tried to compensate for the difference in the client area position using Lexikos WinToClient function found here in the new version of this script. This should fix all of the border location issues from the first version. Please post a response if you have any issues with the location of the border drawing using the newest version.

Changes/Additions:
  • (2/15/11) Added Lexikos's WinToClient function to get the correct client area offset for different versions of Windows.

Image

Requires Gdip.ahk to be in your library, or in the same directory as this script.
You can download it here from this topic.


Code:
#NoEnv
#Include Gdip.ahk
SetWorkingDir %A_ScriptDir%
SetBatchLines, -1
OnExit, ExitProcess
If !pToken := Gdip_Startup()
{
   Msgbox, 64, Focused Control Border, Could not load gdipluss.dll. Make sure you have gdiplus.dll support on your system.
   ExitApp
}
Gui, +LastFound
WinId := WinExist()
OnMessage(0x112, "WindowState")
OnMessage(0x111, "DrawBorder")
Gui, Font, s9 cBlack, Trebuchet MS
Gui, Add, GroupBox, x7 y5 w312 h114 Section, Account Data
Gui, Font, s8 cBlack, Trebuchet MS
Gui, Add, Edit, xs+12 ys+21 w237 r1 vEmail Section
Gui, Add, Text, x+3 ys+4 w30, Email
Gui, Add, Edit, x18 y+9 w100 r1 vFirst Section
Gui, Add, Text, x+3 ys+4 w20, First
Gui, Add, Edit, x+11 ys w100 r1 vLast Section
Gui, Add, Text, x+3 ys+4 w30, Last
Gui, Add, Radio, x18 y+14 w15 h15 vMale Section Checked1
Gui, Add, Text, x+3 ys-2 w25, Male
Gui, Add, Radio, x+2 ys w15 h15 vFemale Section
Gui, Add, Text, x+2 ys-2 w30, Female
Gui, Add, DropDownList, x+16 ys-5 w126 r6 vQuestion Section, Option A||Option B|Option C|Option D
Gui, Add, Text, x+3 ys+4 w30, Option
Gui, Show,, Focused Control Border
Return


GuiClose:
;==============
ExitApp


ExitProcess:
;==============
If pToken !=
   Gdip_Shutdown(pToken)
ExitApp


DrawBorder(wParam, lParam, msg, hwnd)
{
   Global WinId
   Global Last
   If lParam = %Last%
      Return
   WinSet, Redraw,, ahk_id %WinId%
   Sleep, 20
   Last := lParam
   ControlGetPos, CX, CY, CW, CH,, ahk_id %lParam%
        WinToClient(WinId, CX, CY)
        CX -= 1
        CY -= 1
   CW += 1
   CH += 1
   Width = 1024
   Height = 768
   hbm := CreateDIBSection(Width, Height)
   hdc := getDC(WinId)
   obm := SelectObject(hdc, hbm)
   G := Gdip_GraphicsFromHDC(hdc)
   Gdip_SetSmoothingMode(G, 4)
   pPen := Gdip_CreatePen(0xff87ceff, 1)
   Gdip_DrawRectangle(G, pPen, CX, CY, CW, CH)
   Gdip_DeletePen(pPen)
   DllCall("UpdateWindow", UInt, WinId)
   SelectObject(hdc, obm)
   DeleteObject(hbm)
   DeleteDC(hdc)
   Gdip_DeleteGraphics(G)      
   Return
}


WindowState(wParam, lParam, msg, hwnd)
{
   Global Last
   If lparam = 0
      Last =
}


WinToClient(hwnd, ByRef CX, ByRef CY)
{
    WinGetPos, wx, wy,,, ahk_id %hwnd%
    VarSetCapacity(pt, 8)
    NumPut(CX + wx, pt, 0)
    NumPut(CY + wy, pt, 4)
    DllCall("ScreenToClient", "uint", hwnd, "uint", &pt)
    CX := NumGet(pt, 0, "int")
    CY := NumGet(pt, 4, "int")
}

_________________
Image


Last edited by Precise on February 16th, 2011, 6:07 am, edited 11 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 15th, 2011, 6:40 pm 
Offline
User avatar

Joined: May 18th, 2010, 3:10 pm
Posts: 1179
Location: Sweden
Wow dude, that's actually pretty sweet. I'm gonna have to check it out when I get home. You don't need GDIP.ahk in the same directory though, it's okay to have it in the AHK/Lib directory, amirite?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 15th, 2011, 6:45 pm 
Offline

Joined: December 30th, 2007, 5:42 pm
Posts: 31
Location: East Coast
sumon wrote:
Wow dude, that's actually pretty sweet. I'm gonna have to check it out when I get home. You don't need GDIP.ahk in the same directory though, it's okay to have it in the AHK/Lib directory, amirite?

Yeah, thanks, I'll add that to the main post. Let me know what you think when you check it out later. Like I said, I'm open for suggestions on how to improve this, more examples with different approaches, optimization, any feedback will be appreciated.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 15th, 2011, 8:09 pm 
Code:
DrawBorder(wParam,lParam,msg,hwnd) {
 Global WinId,Last

  If (lParam = Last)
    Return 0
  WinSet, Redraw,, % "ahk_id " WinId
  Sleep, 20
  ControlGetPos, CX, CY, CW, CH,, % "ahk_id " lParam
  CX -= 4,CY -= (InStr("WIN_NT4,WIN_95,WIN_98,WIN_ME,WIN_XP",A_OSVersion)) ? 27 : 24
  hbm := CreateDIBSection(A_ScreenWidth,A_ScreenHeight)
  hdc := GetDC(WinId),obm := SelectObject(hdc,hbm)
  G := Gdip_GraphicsFromHDC(hdc),Gdip_SetSmoothingMode(G,4)
  pPen := Gdip_CreatePen(0xff87ceff,1),Gdip_DrawRectangle(G,pPen,CX,CY,++CW,++CH),Gdip_DeletePen(pPen)
  DllCall("UpdateWindow",UInt,WinId)
  SelectObject(hdc,obm),DeleteObject(hbm),DeleteDC(hdc),Gdip_DeleteGraphics(G)     
  Return Last := lParam
}


Now uses the right offset on version before Vista / 7.


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: February 15th, 2011, 8:30 pm 
Guest_ wrote:
Now uses the right offset on version before Vista / 7.


Not quite... I'm running Windows XP (Royale theme) and it's 3 or 4 pixels off. First version was at least one pixel off. Tweaking this for personal use is perfect but otherwise still needs some work for the masses.


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: February 15th, 2011, 8:34 pm 
Offline

Joined: April 8th, 2009, 8:23 pm
Posts: 3036
Location: Rio de Janeiro - RJ - Brasil
Looks great, but nothing happens here. I'm on WinXP (SP3).
Nevertheless, here's my (untested) attempt to modify it:
Code:
#NoEnv
; #Include Gdip.ahk ; <---- uncomment if necessary
SetBatchLines, -1
OnExit, ExitProcess

OnMessage(0x111, "DrawBorder")
; OnMessage(0x112, "WindowState") ; <-----

Gui, Font, s9 cBlack, Trebuchet MS
Gui, Add, GroupBox, x7 y5 w312 h114 Section, Account Data
Gui, Font, s8 cBlack, Trebuchet MS
Gui, Add, Edit, xs+12 ys+21 w237 r1 vEmail Section
Gui, Add, Text, x+3 ys+4 w30, Email
Gui, Add, Edit, x18 y+9 w100 r1 vFirst Section
Gui, Add, Text, x+3 ys+4 w20, First
Gui, Add, Edit, x+11 ys w100 r1 vLast Section
Gui, Add, Text, x+3 ys+4 w30, Last
Gui, Add, Radio, x18 y+14 w15 h15 vMale Section Checked1
Gui, Add, Text, x+3 ys-2 w25, Male
Gui, Add, Radio, x+2 ys w15 h15 vFemale Section
Gui, Add, Text, x+2 ys-2 w30, Female
Gui, Add, DropDownList, x+16 ys-5 w126 r6 vQuestion Section, Option A||Option B|Option C|Option D
Gui, Add, Text, x+3 ys+4 w30, Option
Gui, Show,, Focused Control Border


Return

;-------------------------------------------------------

ExitProcess:
  Gdip_Shutdown(pToken)
GuiClose:
GuiEscape:
ExitApp


;-------------------------------------------------------

DrawBorder( p_WP , p_LP , p_MSG , p_HWND ) {
  Static st_WinID, st_Last
  If ( p_MSG = 0x111 ) && ( p_LP = st_Last )
    Return
  WinSet, Redraw,, % "ahk_id " (st_WinID := !st_WinID ? GetHwndGUI() : st_WinID)
  Sleep, %A_WinDelay%
  ControlGetPos, l_CX, l_CY, l_CW, l_CH,, ahk_id %p_LP%
  l_CX -= 4 , l_CY -= 24 , l_CW++ , l_CH++ , l_HDC := GetDC(st_WinId)
  l_HBM := CreateDIBSection(A_ScreenWidth,A_ScreenHeight)
  l_OBM := SelectObject(l_HDC,l_HBM) , l_Graph := Gdip_GraphicsFromHDC(l_HDC)
  Gdip_SetSmoothingMode(l_Graph,4) , l_Pen := Gdip_CreatePen( 0xFF87CEFF , 1 )
  Gdip_DrawRectangle(l_Graph,l_Pen,l_CX,l_CY,l_CW,l_CH) , Gdip_DeletePen(l_Pen)
  DllCall("UpdateWindow",UInt,st_WinId) , SelectObject(l_HDC,l_OBM) , DeleteObject(l_HBM)
  DeleteDC(l_HDC) , Gdip_DeleteGraphics(l_Graph)
  Return ( st_Last := p_LP )
}

;-------------------------------------------------------

; Specify N to choose a GUI or 0 to use the current default
GetHwndGUI(N=0) { ; by MasterFocus
  If N ;
    Gui, %N%: +LastFound
  Else
    Gui, +LastFound
  Return WinExist()
}

_________________
"Read the manual. Read it again. Search the forum.
Try something before asking. Show what you've tried.
"
Image
Antonio França
My stuff: Google Profile


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 15th, 2011, 8:57 pm 
Offline

Joined: December 30th, 2007, 5:42 pm
Posts: 31
Location: East Coast
Thanks for the great responses so far guys, I'll address them individualy tonight. I'm looking for a way to detect the client area of the window, instead of having to hardcode the offset. I'm not sure yet, but it looks like this might just work. There may already be examples of doing this in the forum. I'm going to check now, or attempt to write the dllcall(s) myself. More later.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 15th, 2011, 9:11 pm 
Offline
User avatar

Joined: May 18th, 2010, 3:10 pm
Posts: 1179
Location: Sweden
Didn't quite work, will be cool when it does.

Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 16th, 2011, 12:02 am 
Offline

Joined: October 13th, 2009, 10:09 pm
Posts: 1389
Check SysGet, I think you can query some variables that determine the client area position.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 16th, 2011, 12:31 am 
Offline

Joined: April 19th, 2010, 10:22 pm
Posts: 145
Location: Mobile, AL
Nice addition Precise, I've needed this a couple times and the closest thing to this I've seen was inside of the AHK Web Recorder project, but I couldn't get the code out and working on my projects... so thanks :-)

_________________
Image
Macro Everything
Lucid_Method Index


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 16th, 2011, 2:22 am 
Offline

Joined: April 8th, 2009, 8:23 pm
Posts: 3036
Location: Rio de Janeiro - RJ - Brasil
This has already been done. Look for Lexikos' GetClientRect code right here.

_________________
"Read the manual. Read it again. Search the forum.
Try something before asking. Show what you've tried.
"
Image
Antonio França
My stuff: Google Profile


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 16th, 2011, 3:02 am 
Offline

Joined: December 30th, 2007, 5:42 pm
Posts: 31
Location: East Coast
Guest_ wrote:
Now uses the right offset on version before Vista / 7.
Thanks for giving it a shot. I have updated the script to use Lexikos's WinToClient function, which gets the correct offset position automatically.

Anonymous wrote:
Not quite... I'm running Windows XP (Royale theme) and it's 3 or 4 pixels off. First version was at least one pixel off. Tweaking this for personal use is perfect but otherwise still needs some work for the masses.
Please try it again now that I have added Lexikos's WinToClient function, which gets the correct offset position automatically.

MasterFocus wrote:
Looks great, but nothing happens here. I'm on WinXP (SP3). Nevertheless, here's my (untested) attempt to modify it
Thanks for this. It would have been easier for me and other people reading this topic to follow had you not renamed everything :?. I'm not sure why this wouldn't work in XP SP3, you may not have gdiplus.dll in your system. You can download it and put it in the script directory.

sumon wrote:
Didn't quite work, will be cool when it does.
Try the updated version, it should fix border drawing location problems like the one in your pic :D.

fragman wrote:
Check SysGet, I think you can query some variables that determine the client area position.
I was not able to find what I needed with SysGet, but thank you anyway.

Lucid_Method wrote:
Nice addition Precise, I've needed this a couple times and the closest thing to this I've seen was inside of the AHK Web Recorder project, but I couldn't get the code out and working on my projects... so thanks :-)
Thanks! I'm glad I was able to clear up any confusion you had. I hope to develop this further and optimize it.

MasterFocus wrote:
This has already been done. Look for Lexikos' GetClientRect code right here.
Thank you for finding and posting this. I was very close to accomplishing a simple dllcall myself, however I was using "GetClientRect" and what I really wanted was "ScreenToClient". In the process I learned how varsetcapacity, numput, and numget work. Thanks Lexikos for another great contribution. I'll add this to the main script and respond to the other comments later on tonight.

OK I think that's everyone. Thanks for all of your great responses! I tried to address all of your responses in order, sorry if I skipped you, or went out of order. Check out the newest version, if you have not already. I'm still waiting for one of the resident pros to chime in with some sort of optimization for this :D. I'm going to try to implement double buffering at some point. I'm hoping there's a way to get rid of the GUI flickering all together. I imagine it is worse on older and slower systems. I'm open to suggestions to improve or develop this further. Let me know if you still have problems with the border position.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 17th, 2011, 3:58 pm 
Offline
User avatar

Joined: May 18th, 2010, 3:10 pm
Posts: 1179
Location: Sweden
It does work now, sweet! Might implement this into some scripts for neater GUIs. Thanks :)

_________________
~sumon Appifyer AHK Nova halted Recommended: AHK_L (Why?)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 18th, 2011, 3:16 am 
Offline

Joined: December 30th, 2007, 5:42 pm
Posts: 31
Location: East Coast
sumon wrote:
It does work now, sweet! Might implement this into some scripts for neater GUIs. Thanks :)
I'm glad this is working for you now! I'm still trying to understand how I will implement double buffering in this script to get rid of the flickering effect. I found this great ahk example when digging through post arranged search results for "double buffering" 8). I'm not sure if it is exactly what I need. Still waiting for one of the pros to chime in with some advice :D.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: February 18th, 2011, 4:05 pm 
Offline

Joined: April 22nd, 2007, 6:33 pm
Posts: 1833
Just taking a glance, I can see that you are using UpdateWindow in your code, which will raise a WM_PAINT event on the windows, causing its entire contents to redraw, which would be the reason for a flicker. One possibility is to bitblt your border onto the window with a timer which wouldn't cause any flickering.

Look at my post here to see how you can bitblt using the gdi+ library. You need to create a DC, select a bitmap into it, then get the graphics from the DC and perform all your drawing into the graphics as you would normally and bitblt the dc onto the window.

You will need to figure out how to cleanly "clean" up the border on a control when it loses focus


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 15 posts ] 

All times are UTC [ DST ]


Who is online

Users browsing this forum: Bing [Bot], Google Feedfetcher, sks, Stigg and 13 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group