Page 1 of 1

[Module] Cursor Control (WIP)

Posted: Wed Oct 01, 2014 1:11 pm
by IndigoFuzz
Hey there folks,

I'm in need of a solution whereby I can control the cursor icon/design during runtime. A solution that will be cross-compatible between Linux and Windows without having to make adjustments to the code...

So I started writing a module specifically for that purpose and would like to share the progress with you:

So far it works well on the Windows environment (hopefully I can get confirmation of this with other versions of Windows as I'm testing on Win 7), and over time I hope to include more cursor styles and of course the all-important GTK implementation. If you folks have any suggestions or ideas then please do share :)

(Current Revision: 1)

Code: Select all

; ====================================================================================================
; Title:        UI Cursor Module
; Description:  Enable the changing of the mouse cursor style.
; Target OS:    Windows, Linux [GTK] (TODO)
; Author:       IndigoFuzz
; Contributors: You name here?
; Revision:     1
; Notes:        When cursor leaves bounding of window/gadget then cursor will be reset.
; ====================================================================================================

; TODO LIST 
; Implement more cursor styles.
; Begin implementation of GTK code.


DeclareModule UICursor
  
  Enumeration CursorID
    #Cursor_Arrow
    ;#Cursor_RightArrow
    ;#Cursor_Bullseye
    ;#Cursor_Char
    #Cursor_Cross
    #Cursor_Hand
    #Cursor_IBeam
    ;#Cursor_LeftButton
    ;#Cursor_Magnifier
    ;#Cursor_MiddleButton
    #Cursor_NoEntry
    ;#Cursor_PaintBrush
    ;#Cursor_Pencil
    ;#Cursor_PointLeft
    ;#Cursor_PointRight
    #Cursor_QuestionArrow
    ;#Cursor_RightButton
    #Cursor_SizeNESW
    #Cursor_SizeNS
    #Cursor_SizeNWSE
    #Cursor_SizeWE
    #Cursor_Sizing
    ;#Cursor_SprayCan
    #Cursor_Wait
    ;#Cursor_Watch
    ;#Cursor_Blank
    #Cursor_ArrowWait
    #Cursor_Count
  EndEnumeration
  
  Declare SetCursor(*WindowID, Cursor = #Cursor_Arrow)
  Declare ReleaseAll()
  
EndDeclareModule

Module UICursor 
  
  EnableExplicit
  
  ; Private:
  
  Structure modData
    *pCursor[#Cursor_Count]
  EndStructure
  
  Global _inst.modData  
  
  Procedure _loadCursor(ID)
    Protected stockID.i = 0
    With _inst
      If \pCursor[ID] = #Null
        CompilerSelect #PB_Compiler_OS
          CompilerCase #PB_OS_Windows
            Select ID
              Case #Cursor_Arrow
                \pCursor[ID] = LoadCursor_(0, #IDC_ARROW)
              ;Case #Cursor_RightArrow                
              ;Case #Cursor_Bullseye                
              ;Case #Cursor_Char                
              Case #Cursor_Cross
                \pCursor[ID] = LoadCursor_(0, #IDC_CROSS)
              Case #Cursor_Hand
                \pCursor[ID] = LoadCursor_(0, #IDC_HAND)
              Case #Cursor_IBeam
                \pCursor[ID] = LoadCursor_(0, #IDC_IBEAM)
              ;Case #Cursor_LeftButton                
              ;Case #Cursor_Magnifier                
              ;Case #Cursor_MiddleButton                
              Case #Cursor_NoEntry
                \pCursor[ID] = LoadCursor_(0, #IDC_NO)
              ;Case #Cursor_PaintBrush                
              ;Case #Cursor_Pencil                
              ;Case #Cursor_PointLeft                
              ;Case #Cursor_PointRight                
              Case #Cursor_QuestionArrow
                \pCursor[ID] = LoadCursor_(0, #IDC_HELP)
              ;Case #Cursor_RightButton
              Case #Cursor_SizeNESW
                \pCursor[ID] = LoadCursor_(0, #IDC_SIZENESW)
              Case #Cursor_SizeNS
                \pCursor[ID] = LoadCursor_(0, #IDC_SIZENS)
              Case #Cursor_SizeNWSE
                \pCursor[ID] = LoadCursor_(0, #IDC_SIZENWSE)
              Case #Cursor_SizeWE
                \pCursor[ID] = LoadCursor_(0, #IDC_SIZEWE)
              Case #Cursor_Sizing
                \pCursor[ID] = LoadCursor_(0, #IDC_SIZEALL)
              ;Case #Cursor_SprayCan                
              Case #Cursor_Wait
                \pCursor[ID] = LoadCursor_(0, #IDC_WAIT)
              ;Case #Cursor_Watch
              ;Case #Cursor_Blank                
              Case #Cursor_ArrowWait
                \pCursor[ID] = LoadCursor_(0, #IDC_APPSTARTING)
            EndSelect
          CompilerCase #PB_OS_Linux
            Select ID
              Case #Cursor_Arrow                
              ;Case #Cursor_RightArrow                
              ;Case #Cursor_Bullseye                
              ;Case #Cursor_Char                
              Case #Cursor_Cross                
              Case #Cursor_Hand                
              Case #Cursor_IBeam                
              ;Case #Cursor_LeftButton                
              ;Case #Cursor_Magnifier                
              ;Case #Cursor_MiddleButton                
              Case #Cursor_NoEntry                
              ;Case #Cursor_PaintBrush                
              ;Case #Cursor_Pencil                
              ;Case #Cursor_PointLeft                
              ;Case #Cursor_PointRight                
              Case #Cursor_QuestionArrow                
              ;Case #Cursor_RightButton
              Case #Cursor_SizeNESW                
              Case #Cursor_SizeNS                
              Case #Cursor_SizeNWSE                
              Case #Cursor_SizeWE                
              Case #Cursor_Sizing                
              ;Case #Cursor_SprayCan                
              Case #Cursor_Wait                
              ;Case #Cursor_Watch
              ;Case #Cursor_Blank                
              Case #Cursor_ArrowWait                
            EndSelect
        CompilerEndSelect
      EndIf
    EndWith
  EndProcedure
  
  Procedure _applyCursor(*WindowID, Cursor)
    With _inst      
      CompilerSelect #PB_Compiler_OS
        CompilerCase #PB_OS_Windows
          If \pCursor[Cursor]
            SetClassLong_(*WindowID, #GCL_HCURSOR, 0)
            SetCursor_(\pCursor[Cursor])            
          EndIf
        CompilerCase #PB_OS_Linux
      CompilerEndSelect
    EndWith
  EndProcedure
  
  ; Public:
  
  Procedure SetCursor(*WindowID, Cursor = #Cursor_Arrow)
    If Cursor > -1 And Cursor < #Cursor_Count
      If *WindowID <> 0
        _loadCursor(Cursor)
        _applyCursor(*WindowID, Cursor)
      EndIf
    EndIf
  EndProcedure
  
  Procedure ReleaseAll()
    With _inst
      Protected ix.i
      CompilerSelect #PB_Compiler_OS
        CompilerCase #PB_OS_Windows
          For ix = 0 To (#Cursor_Count - 1)
            If \pCursor[ix]
              DestroyCursor_(\pCursor[ix])
            EndIf          
          Next
      CompilerEndSelect
    EndWith
  EndProcedure
  
EndModule


; =============
; == EXAMPLE ==
; =============

; - Create Window -
OpenWindow(0, #PB_Ignore, #PB_Ignore, 500, 300, "Cursor Test", #PB_Window_TitleBar | #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ListIconGadget(0, 10, 10, 480, 280, "Cursors", 475)

; - Populate List -
AddGadgetItem(0, -1, "#Cursor_Arrow")
;AddGadgetItem(0, -1, "#Cursor_RightArrow")
;AddGadgetItem(0, -1, "#Cursor_Bullseye")
;AddGadgetItem(0, -1, "#Cursor_Char")
AddGadgetItem(0, -1, "#Cursor_Cross")
AddGadgetItem(0, -1, "#Cursor_Hand")
AddGadgetItem(0, -1, "#Cursor_IBeam")
;AddGadgetItem(0, -1, "#Cursor_LeftButton")
;AddGadgetItem(0, -1, "#Cursor_Magnifier")
;AddGadgetItem(0, -1, "#Cursor_MiddleButton")
AddGadgetItem(0, -1, "#Cursor_NoEntry")
;AddGadgetItem(0, -1, "#Cursor_PaintBrush")
;AddGadgetItem(0, -1, "#Cursor_Pencil")
;AddGadgetItem(0, -1, "#Cursor_PointLeft")
;AddGadgetItem(0, -1, "#Cursor_PointRight")
AddGadgetItem(0, -1, "#Cursor_QuestionArrow")
;AddGadgetItem(0, -1, "#Cursor_RightButton")
AddGadgetItem(0, -1, "#Cursor_SizeNESW")
AddGadgetItem(0, -1, "#Cursor_SizeNS")
AddGadgetItem(0, -1, "#Cursor_SizeNWSE")
AddGadgetItem(0, -1, "#Cursor_SizeWE")
AddGadgetItem(0, -1, "#Cursor_Sizing")
;AddGadgetItem(0, -1, "#Cursor_SprayCan")
AddGadgetItem(0, -1, "#Cursor_Wait")
;AddGadgetItem(0, -1, "#Cursor_Watch")
;AddGadgetItem(0, -1, "#Cursor_Blank")
AddGadgetItem(0, -1, "#Cursor_ArrowWait")

SetGadgetState(0, 0)

; - Loop Entry Point -

Repeat
  Select WindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventGadget() = 0 And GetGadgetState(0) > -1
        UICursor::SetCursor(GadgetID(0), GetGadgetState(0))
      EndIf
  EndSelect
ForEver

CloseWindow(0)
UICursor::ReleaseAll()

Re: [Module] Cursor Control (WIP)

Posted: Wed Oct 01, 2014 3:35 pm
by IdeasVacuum
Very nice idea and being cross-platform should perhaps be included with PB. 8)
Can't quite follow your code. For Windows, use a copy of the cursor image, else the original will be lost:

Code: Select all

iHandCursor = CopyImage_(\pCursor[Cursor],#IMAGE_CURSOR,0,0,#LR_COPYRETURNORG)
SetCursor_(iHandCursor) 
Again for Windows, on App exit, restore the default cursor set:

Code: Select all

SystemParametersInfo_(#SPI_SETCURSORS,0,#Null,#Null)

Re: [Module] Cursor Control (WIP)

Posted: Thu Oct 02, 2014 5:37 pm
by mestnyi

Code: Select all

;==========================================================================================
Procedure NewCursor(Cursor)
 CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    Select Cursor
      Case #PB_Cursor_Default         :ProcedureReturn #IDC_ARROW 
      Case #PB_Cursor_Cross           :ProcedureReturn #IDC_CROSS
      Case #PB_Cursor_IBeam           :ProcedureReturn #IDC_IBEAM
      Case #PB_Cursor_Hand            :ProcedureReturn #IDC_HAND 
      Case #PB_Cursor_Arrows          :ProcedureReturn #IDC_SIZEALL 
      Case #PB_Cursor_Busy            :ProcedureReturn #IDC_WAIT 
      Case #PB_Cursor_Denied          :ProcedureReturn #IDC_NO 
      Case #PB_Cursor_LeftRight       :ProcedureReturn #IDC_SIZEWE 
      Case #PB_Cursor_LeftUpRightDown :ProcedureReturn #IDC_SIZENWSE
      Case #PB_Cursor_UpDown          :ProcedureReturn #IDC_SIZENS
      Case #PB_Cursor_LeftDownRightUp :ProcedureReturn #IDC_SIZENESW 
    EndSelect  
  CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
   If Cursor=#PB_Cursor_Default
    ProcedureReturn #GDK_ARROW 
   ElseIf Cursor=1
    ProcedureReturn #GDK_LEFT_SIDE ;#GDK_SB_H_DOUBLE_ARROW
   ElseIf Cursor=2
    ProcedureReturn #GDK_TOP_LEFT_CORNER
   ElseIf Cursor=3
    ProcedureReturn #GDK_TOP_SIDE ;#GDK_SB_V_DOUBLE_ARROW
   ElseIf Cursor=4
    ProcedureReturn #GDK_TOP_RIGHT_CORNER 
   ElseIf Cursor=5
    ProcedureReturn #GDK_RIGHT_SIDE 
   ElseIf Cursor=6
    ProcedureReturn #GDK_BOTTOM_RIGHT_CORNER
   ElseIf Cursor=7
    ProcedureReturn #GDK_BOTTOM_SIDE 
   ElseIf Cursor=8
    ProcedureReturn #GDK_BOTTOM_LEFT_CORNER 
   ElseIf Cursor=#PB_Cursor_Arrows
     ProcedureReturn 52 
   EndIf  
 CompilerEndIf
EndProcedure 

ProcedureDLL LoadCursor(NewCursor) ;Load cursor
 CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  ProcedureReturn LoadCursor_(0,NewCursor)
 CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
  ProcedureReturn gdk_cursor_new_(NewCursor)
 CompilerEndIf
EndProcedure 

ProcedureDLL LoadCursorRes(Cursor$) ;Load cursor from resident file
 CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  ProcedureReturn LoadCursor_(GetModuleHandle_(0), Cursor$)
 CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
   
 CompilerEndIf
EndProcedure 

ProcedureDLL SetCursorLoad(LoadCursor) ;Set load cursor (valid only until the next event window)
 CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  SetCursor_(LoadCursor)
 CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
  Protected *Widget.GtkWidget = WindowID(GetActiveWindow())
   gdk_window_set_cursor_(*Widget\window,LoadCursor)
 CompilerEndIf
EndProcedure  

ProcedureDLL SetCursor(Handle,Cursor) ;Set cursor (valid only until the next event window)  
  Protected NewCursor = NewCursor(Cursor)
  SetCursorLoad(LoadCursor(NewCursor))
  SetClassLong_(Handle,#GCL_HCURSOR,LoadCursor(NewCursor))
  ProcedureReturn NewCursor
EndProcedure  


Procedure SetCursorClass(ClasshWnd, LoadCursor)
  If ClasshWnd
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
     SetClassLong_(ClasshWnd,#GCL_HCURSOR,LoadCursor)
      CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
     Protected *Widget.GtkWidget = ClasshWnd
    gdk_window_set_cursor_(*Widget\window,LoadCursor)
  CompilerEndIf
 EndIf
EndProcedure  

ProcedureDLL WindowCursor(Window, Cursor) ;Set pb cursor on the window class
  If IsWindow(Window)
   SetCursorClass(WindowID(Window), LoadCursor(NewCursor(Cursor)))
  EndIf
EndProcedure  

ProcedureDLL GadgetCursor(Gadget, Cursor) ;Set pb cursor on the gadget class
  If IsGadget(Gadget)
   SetCursorClass(GadgetID(Gadget), LoadCursor(NewCursor(Cursor)))
  EndIf
EndProcedure  

ProcedureDLL WindowLoadCursor(Window, LoadCursor) ;Set new load cursor on the window
  If IsWindow(Window)
     SetCursorClass(WindowID(Window), LoadCursor)
  EndIf
EndProcedure  

ProcedureDLL GadgetLoadCursor(Gadget, LoadCursor) ;Set new load cursor on the gadget
 If IsGadget(Gadget)
    SetCursorClass(GadgetID(Gadget), LoadCursor)
 EndIf
EndProcedure  


; =============
; == EXAMPLE ==
; =============

; - Create Window -
OpenWindow(0, #PB_Ignore, #PB_Ignore, 500, 300, "Cursor Test", #PB_Window_TitleBar | #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ListIconGadget(0, 10, 10, 480, 280, "Cursors", 475)

; - Populate List -
AddGadgetItem(0, -1, "#Cursor_Arrow")
;AddGadgetItem(0, -1, "#Cursor_RightArrow")
;AddGadgetItem(0, -1, "#Cursor_Bullseye")
;AddGadgetItem(0, -1, "#Cursor_Char")
AddGadgetItem(0, -1, "#Cursor_Cross")
AddGadgetItem(0, -1, "#Cursor_Hand")
AddGadgetItem(0, -1, "#Cursor_IBeam")
;AddGadgetItem(0, -1, "#Cursor_LeftButton")
;AddGadgetItem(0, -1, "#Cursor_Magnifier")
;AddGadgetItem(0, -1, "#Cursor_MiddleButton")
AddGadgetItem(0, -1, "#Cursor_NoEntry")
;AddGadgetItem(0, -1, "#Cursor_PaintBrush")
;AddGadgetItem(0, -1, "#Cursor_Pencil")
;AddGadgetItem(0, -1, "#Cursor_PointLeft")
;AddGadgetItem(0, -1, "#Cursor_PointRight")
AddGadgetItem(0, -1, "#Cursor_QuestionArrow")
;AddGadgetItem(0, -1, "#Cursor_RightButton")
AddGadgetItem(0, -1, "#Cursor_SizeNESW")
AddGadgetItem(0, -1, "#Cursor_SizeNS")
AddGadgetItem(0, -1, "#Cursor_SizeNWSE")
AddGadgetItem(0, -1, "#Cursor_SizeWE")
AddGadgetItem(0, -1, "#Cursor_Sizing")
;AddGadgetItem(0, -1, "#Cursor_SprayCan")
AddGadgetItem(0, -1, "#Cursor_Wait")
;AddGadgetItem(0, -1, "#Cursor_Watch")
;AddGadgetItem(0, -1, "#Cursor_Blank")
AddGadgetItem(0, -1, "#Cursor_ArrowWait")

SetGadgetState(0, 0)

; - Loop Entry Point -

Repeat
  Select WindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventGadget() = 0 And GetGadgetState(0) > -1
        SetCursor( GadgetID(0),GetGadgetState(0))
      EndIf
  EndSelect
ForEver

Re: [Module] Cursor Control (WIP)

Posted: Sun Oct 05, 2014 9:25 pm
by IndigoFuzz
Very nice code, mestnyi :) Would you mind if I were to adapt my original code with yours?

@IdeasVacuum: I'm quite surprised it's not already included! The code (in terms of process in the first revision) is fairly simply.. Two main internal routines, one to load the handle for a cursor (if not already loaded) and the second to apply the cursor using API calls. Still rough and in need of polishing however, but we'll get there :)

I do hope to add further functions in to the module which surround control and use of the mouse pointer on the relevant OS, but will post more as I go along.

Re: [Module] Cursor Control (WIP)

Posted: Thu Oct 16, 2014 11:22 am
by mestnyi
Would you mind if I were to adapt my original code with yours?
no problem :D