Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

[fun] SetCursor 1.1 - Set cursor shape for control or window


  • Please log in to reply
15 replies to this topic
majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
OUTDATED: Use Cursorfrom Formsframework.

SetCursor

Set cursor shape for control or window


Download           Documentation


ToDo
- Specify control using hwnd or AHK control name.


Posted Image

Zoulou
  • Guests
  • Last active:
  • Joined: --
Very usefull and long time asked. Thanks a lot.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
A little bug fixed.
Posted Image

jaco0646
  • Moderators
  • 3165 posts
  • Last active: Apr 01 2014 01:46 AM
  • Joined: 07 Oct 2006
First of all, thank you very much. I'm one of those who has been asking for this for quite some time. The function works fine on my computer.

But I'm trying to understand how it works, and unfortunately I'm not well-educated in DLLCalls. I've gone through the function and added comments explaining what I understand and what I don't, so if someone could help educate me, I would appreciate it. :D

SetCursor(pShape, pCtrl="") {
	return SetCursor_(pShape, pCtrl, 0, 0) 		;Why have two functions instead of one?
}

SetCursor_(wparam, lparam, msg, hwnd) {
    static WM_SETCURSOR := 0x20, WM_MOUSEMOVE := 0x200
	static APPSTARTING := 32650,HAND := 32649 ,ARROW := 32512,CROSS := 32515 ,IBEAM := 32513 ,NO := 32648 ,SIZE := 32640 ,SIZEALL := 32646 ,SIZENESW := 32643 ,SIZENS := 32645 ,SIZENWSE := 32642 ,SIZEWE := 32644 ,UPARROW := 32516 ,WAIT := 32514 
	static hover, curOld=32512, cursor, ctrls="`n", init

	if !init {
		init := 1
		OnMessage(WM_SETCURSOR, "SetCursor_")	;Couldn't the OnMessage functions be in the Auto-Execute section?
		OnMessage(WM_MOUSEMOVE, "SetCursor_") 

	}

	if A_Gui = 
	{
		if wparam is not Integer		;What would wparam be besides an integer?
			cursor := DllCall("LoadCursor", "Uint", 0, "Int", %WPARAM%, "Uint") 
		if lparam =
			  curOld := cursor		;If wparam IS an integer AND lparam is false, curOld becomes blank?
		else  ctrls .= lparam "=" cursor "`n"	;Create list of controls with corresponding cursors.
	}

	If (msg = WM_SETCURSOR)
		ifEqual, hover, 1,	return 1	;This stops the OS from resetting its own cursor?

	if (msg = WM_MOUSEMOVE)
	{
		MouseGetPos, ,,,c,ahk_id %hwnd%		;The Help file says the last parameter should be 1|2|3	...???
		If j := InStr(ctrls, "`n" c "=")	;Search for the control name under mouse? This is confusing shorthand.
		{
			hover := true, 
			j += 2+StrLen(c)		;Locate first character after "=" sign.
			j := SubStr(ctrls, j, InStr(ctrls, "`n", 0, j)-j+1)	;Retrieve cursor name without line break.
			DllCall("SetCursor", "uint",j) 				;Set prescribed cursor.
		} 
		else DllCall("SetCursor", "uint", curOld), hover := ""	;If control has no prescribed cursor, reset old.
	} 
    
}

And finally, the static variables are just for usability, right? The numbers could be substituted to shorten the script? Thanks in advance.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Jaco, I don't have time to explain you the function nor principle. All in all, it is black box, so most of the things are done to acomplish that.

There is no need for two functions, its just syntax suggar so you don't have to add 2 trailing zeros, wich are NOT and can't be optional.

Shortening the script by removing statics is contraproductive and will not speed up script. It will just make it harder to read and change, so if I did that, you wouldn't understant single word. Don't do that.
Posted Image

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Version 1.1 by freakkk
- Addeded new types of system cursors
- You can now load cursors from file

See docs.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006

MouseGetPos, ,,,c,ahk_id %hwnd% ;The Help file says the last parameter should be 1|2|3 ...???

This is actually a harmless bug (thank you) as it doesn't reproduce.... It will be taken as 0 wich I actually wanted.

;What would wparam be besides an integer?

Well, thats the thing :D 8)
The same function is used as an interface and as OnMessage handler. Thats the reason for another function wich is syntax suggar as you can't have optinal parameters with OnMessage, and I need them all. For the point of user they are miningless so I creted wrapping function.

This is encapsulation. You just use the function and don't think about it. Your suggestion about using OnMessage on the script start is far from encapsulation, as user step is needed to control internal SetCursor details, and that is never good. Leave to the user as low choices as possible to fuk up something....
Posted Image

Andreone
  • Members
  • 257 posts
  • Last active: Mar 19 2008 05:30 PM
  • Joined: 20 Jul 2007
Thank you for this very useful function.
Do you think it'd be possible to make it work with any window (meaning, not only one owned the script)?

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Yes, its possible, but not in this language :)

Basicly, anything that deals with 3th party processes need either global hooks or Remote Buffer. Hooks can inject the code into remote process so you can subclass third party controls to listen and affect their messages. Using Remote Buffer only, I doubt it can be done - one of the ways would be inserting machine code functions into remote buffers and executing them (hm... just an idea, who knows if it works, never tried that mumbo jumbo). Remote Buffer is mostly useful when only data is expected to be in the remote process while process requesting info based on that data can be outside.

Perhaps WinEvents can do it, something similar to what Dock does, but its too complicated for such uninspirative thing like cursor changing.

You can always use Timers and constant checking if mouse is over some control, and you can set this to be as granular as timer allows - keep in mind that timer period is reversly proprortional with CPU used (thats serious drawback of pulling techniques).

AH, I know now, there is easy way without Timer - You can instantiate WinEvents hook on mouse move (it luckily exists, keyboard monitoring is for instance not there). So, when user moves the mouse, your hook will be called and you can change the cursor depending on the control in the back. When cursor is not moved, hook will be iddle. Sounds like idea that can work....

So, I think LOCATION CHANGE event is reporting mouse, the same thing used in Dock to track windows. In dock, I just return on mouse imediately. You will have to instantiate only that hook, and to check in it if mouse is over your control , then change system cursor back and forth. Easy as that...

Thinking about that, I will probably see to utilize mosue tracking in the system. It can be very useful. Currently you can't track mouse movement if your app isn't active.

jaco0646
  • Moderators
  • 3165 posts
  • Last active: Apr 01 2014 01:46 AM
  • Joined: 07 Oct 2006

This is encapsulation. You just use the function and don't think about it. Your suggestion about using OnMessage on the script start is far from encapsulation, as user step is needed to control internal SetCursor details, and that is never good. Leave to the user as low choices as possible to fuk up something....


Yes, I agree with you. I wasn't suggesting that the OnMessage functions be moved, nor that the static variables be eliminated; I was just wondering if it was possible, so I could better understand what the code was doing... sorry if I'm being too nosy. :oops:

Thank you for your responses, and again for writing the function.

Petru
  • Members
  • 236 posts
  • Last active: Jan 19 2012 06:47 PM
  • Joined: 17 Dec 2007
I found a bug. When I want to use SetCursor for more GUIs, it will doesn't work properly.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Post a sample
Posted Image

kiropes
  • Members
  • 78 posts
  • Last active: Oct 16 2014 05:35 AM
  • Joined: 21 Mar 2007

I found a bug. When I want to use SetCursor for more GUIs, it will doesn't work properly.


me too

I assigned the "HAND" cursor to 14 static controls in GUI 1
When I launch gui 2 the change of cursor moves from gui1 to those gui2
Instead I need to remain in gui1
How can I eliminate this effect?

:oops:

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
This function is outdated although it works most cases.

Try similar one instead:

/* Function: Cursor
			Set cursor shape for control or window.
 
 Dependencies: 
			<Win> 1.22

 Parameters: 
			HCtrl	- Control handle.
            Shape   - Name of the system cursor to set or cursor handle or full cursor path (must have .ani or .cur extension).

 System Cursors: 
      appstarting  - Standard arrow and small hourglass.
      arrow        - Standard arrow.
      cross        - Crosshair.
      hand         - Hand.
      help         - Arrow and question mark.
      ibeam        - I-beam.
      icon         - Obsolete for applications marked version 4.0 or later. 
      no           - Slashed circle.
      size         - Obsolete for applications marked version 4.0 or later. Use SIZEALL.
      sizeall      - Four-pointed arrow pointing north, south, east, and west.
      sizenesw     - Double-pointed arrow pointing northeast and southwest.
      sizens       - Double-pointed arrow pointing north and south.
      sizenwse     - Double-pointed arrow pointing northwest and southeast.
      sizewe       - Double-pointed arrow pointing west and east.
      uparrow      - Vertical arrow.
      wait         - Hourglass.
      sizewe_big   - Big double-pointed arrow pointing west and east.
      sizeall_big  - Big four-pointed arrow pointing north, south, east, and west.
      sizen_big    - Big arrow pointing north.
      sizes_big    - Big arrow pointing south.
      sizew_big    - Big arrow pointing west.
      sizee_big    - Big arrow pointing east.
      sizenw_big   - Big double-pointed arrow pointing north and west.
      sizene_big   - Big double-pointed arrow pointing north and east.
      sizesw_big   - Big double-pointed arrow pointing south and west.
      sizese_big   - Big double-pointed arrow pointing south and east.

 Remarks:
	 Setting the same cursor type on several controls uses the same cursor resource.
     Some controls may host child windows. In that case you should pass handle of the
	 topmost child window it contains instead handle of the control itself (for instance RaGrid, SpreedSheat ...)

 About:
	o 1.2 by majkinetor
	o Licensed under BSD <http://creativecommons.org/licenses/BSD/> 
 */
Ext_Cursor(HCtrl, Shape) { 
;	static adrWndProc = "Ext_Cursor_wndProc"
;	Form_SubClass(HCtrl, adrWndProc, "", adrWndProc)	;subclassing with the same function all the time makes problem when instantiated bunch of times...
	Win_SubClass(HCtrl, "Ext_Cursor_wndProc")
	return Ext_Cursor_WndProc(0, 0, Shape, HCtrl)
} 

Ext_Cursor_wndProc(Hwnd, UMsg, WParam, LParam) { 
	static 
	static WM_SETCURSOR := 0x20, WM_MOUSEMOVE := 0x200
	static APPSTARTING := 32650, HAND := 32649 ,ARROW := 32512,CROSS := 32515 ,IBEAM := 32513 ,NO := 32648,SIZE := 32646 ,SIZENESW := 32643 ,SIZENS := 32645 ,SIZENWSE := 32642 ,SIZEWE := 32644 ,UPARROW := 32516, WAIT := 32514, SIZEWE_BIG := 32653, SIZEALL_BIG := 32654, SIZEN_BIG := 32655, SIZES_BIG := 32656, SIZEW_BIG := 32657, SIZEE_BIG := 32658, SIZENW_BIG := 32659, SIZENE_BIG := 32660, SIZESW_BIG := 32661, SIZESE_BIG := 32662
	
	if !Hwnd  {
		if WParam is not Integer
		{
			ext := SubStr(WParam, -2, 3)
			if ext in cur,ani
			 	 %LParam% := DllCall("LoadCursorFromFile", "Str", WParam) 
			else %LParam% := DllCall("LoadCursor", "Uint", 0, "Int", %WParam%, "Uint")
		} else %LParam% := %WParam%
		
		curArrow .= curArrow ? "" : DllCall("LoadCursor", "Uint", 0, "Int", 32512, "Uint")
		return (%LParam%)
	}

   If (UMsg = WM_SETCURSOR) 
      return 1 

   if (UMsg = WM_MOUSEMOVE) 
      If (%Hwnd% != "")
			DllCall("SetCursor", "uint", %Hwnd%)
	  else  DllCall("SetCursor", "uint", curArrow)

   return DllCall("CallWindowProcA", "UInt", A_EventInfo, "UInt", hwnd, "UInt", uMsg, "UInt", wParam, "UInt", lParam)
} 

#include *i Win.ahk

It requires Win.ahk.
Posted Image

soggos
  • Members
  • 129 posts
  • Last active: Nov 30 2012 10:35 AM
  • Joined: 27 Mar 2008
Hello All,
AnyBody, Know the icon number of the + or link (the arrow-link)
, like drag a file and press control in xp ?

@ majkinetor ; once again a good function for us...
with ahk, all is different!...