Utiliser des curseurs personnalisés

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Avatar de l’utilisateur
Jacobus
Messages : 1559
Inscription : mar. 06/avr./2004 10:35
Contact :

Utiliser des curseurs personnalisés

Message par Jacobus »

Bonjour tout le monde
L'ancienne API LoadCursorFromFile_(name) utilisée pour charger un curseur est toujours fonctionnelle mais dépréciée, elle a été remplacée par LoadImage_(hInst,name,#type,cx,cy,#flags).
Si dans vos applications de dessin (ou autre d'ailleurs) vous souhaitez utiliser d'autres curseurs que ceux fournis par le système, c'est un moyen simple de le faire grâce aux fonctions de PureBasic.
Pour l'exemple d'utilisation j'ai repris celui fourni dans la documentation que j'ai adapté.
Pour ceux qui n'ont pas de curseurs personnels, je vous offre quelques uns des miens que vous pouvez télécharger ici : MyCursors.zip
Amusez-vous bien :wink:

Testé avec Windows11, PB 6.21B8, x64 et x86
Exemple d'utilisation:

Code : Tout sélectionner

 ;================================
; Loads an icon, cursor, animated cursor, or bitmap.
; https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadimagea#return-value

; HANDLE LoadImageA(
;   [in, optional] HINSTANCE hInst,  (Un handle vers le module d'une DLL ou d'un exécutable (.exe) contenant l'image à charger. Pour charger une image prédéfinie ou une ressource autonome (icône, curseur ou fichier bitmap), définissez ce paramètre sur NULL.)
;   [in]           LPCSTR    name,   (nom complet du fichier)
;   [in]           UINT      type,   (type de fichier défini par : #IMAGE_BITMAP ou #IMAGE_CURSOR ou #IMAGE_ICON)
;   [in]           int       cx,     (largeur curseur = 32 par défaut)
;   [in]           int       cy,     (hauteur curseur = 32 par défaut)
;   [in]           UINT      fuLoad  (#flags)
; );

;-FLAGS :
; #LR_CREATEDIBSECTION - Lorsque le paramètre uType spécifie IMAGE_BITMAP, la fonction renvoie une image bitmap de section DIB plutôt qu'une image bitmap compatible. Cet indicateur est utile pour charger une image bitmap sans la mapper aux couleurs du périphérique d'affichage.
; #LR_DEFAULTCOLOR     - L'indicateur par défaut ; il ne fait rien. Il signifie simplement « non LR_MONOCHROME ».
; #LR_DEFAULTSIZE      - Utilise la largeur ou la hauteur spécifiée par les valeurs métriques système pour les curseurs ou les icônes, si les valeurs cx ou cy sont définies sur zéro. Si cet indicateur n'est pas spécifié et que cx et cy sont définies sur zéro, la fonction utilise la taille réelle de la ressource. Si la ressource contient plusieurs images, la fonction utilise la taille de la première image. 
; #LR_LOADFROMFILE     - Charge l'image autonome à partir du fichier spécifié par son nom (icône, curseur ou fichier bitmap). 
; #LR_LOADMAP3DCOLORS  - Recherche l'image dans la table des couleurs et remplace les nuances de gris suivantes par la couleur 3D correspondante. N'utilisez pas cette option si vous chargez une image bitmap avec une profondeur de couleur supérieure à 8 bpp.
; #LR_LOADTRANSPARENT  - Récupère la valeur de couleur du premier pixel de l'image et remplace l'entrée correspondante de la table des couleurs par la couleur de fenêtre par défaut (COLOR_WINDOW). Tous les pixels de l'image utilisant cette entrée adoptent la couleur de fenêtre par défaut. Cette valeur s'applique uniquement aux images disposant de tables de couleurs correspondantes. N'utilisez pas cette option si vous chargez une image bitmap dont la profondeur de couleur est supérieure à 8 bpp.
; #LR_MONOCHROME       - Charge l'image en noir et blanc. 
; #LR_SHARED           - Partage le handle de l'image si celle-ci est chargée plusieurs fois. Si LR_SHARED n'est pas défini, un second appel à LoadImage pour la même ressource chargera à nouveau l'image et renverra un handle différent. N'utilisez pas LR_SHARED pour les images dont la taille n'est pas standard, qui peuvent changer après le chargement ou qui sont chargées depuis un fichier.
; #LR_VGACOLOR         - Utilise de vraies couleurs VGA.

; Valeur de retour : HANDLE
; - Si la fonction réussit, la valeur de retour est le handle de l'image nouvellement chargée. 
; - Si la fonction échoue, la valeur de retour est NULL.

; --> HANDLE = LoadImage_(hInst,name,#type,cx,cy,#flags)

;Pour libérer la ressource utilisée :
; Bitmap :	DeleteObject(HGDIOBJ)
; Cursor :	DestroyCursor(HCURSOR)
; Icon 	 :  DestroyIcon(HICON)

Enumeration
  #IMAGE_Content  ; stores the previous CanvasGadget content while the mouse is down
  #IMAGE_Color
  #IMAGE_LoadSave
EndEnumeration

Enumeration
  #GADGET_Canvas
  #GADGET_Color
  #GADGET_Brush
  #GADGET_Line
  #GADGET_Box
  #GADGET_Circle
  #GADGET_Fill
  #GADGET_Clear
  #GADGET_Load
  #GADGET_Save
EndEnumeration

;exemple avec nouvelle API
Global HCURSOR_F = LoadImage_(#Null,"MyCursors\Paint_fill.cur",#IMAGE_CURSOR,32,32,#LR_LOADFROMFILE|#LR_VGACOLOR)
; If HCURSOR_F = #Null
;   Debug "Erreur lors du chargement du curseur!"
; EndIf 
;exemple avec ancienne API
Global HCURSOR_B = LoadCursorFromFile_("MyCursors\Rectangle_full.cur")
Global HCURSOR_C = LoadCursorFromFile_("MyCursors\Pencil_1.cur")
;Global HCURSOR_F = LoadCursorFromFile_("MyCursors\Paint_fill.cur")
Global HCURSOR_E = LoadCursorFromFile_("MyCursors\Ellipse_empty.cur")
Global HCURSOR_L = LoadCursorFromFile_("MyCursors\Line.cur")
Global HCURSOR_P = LoadCursorFromFile_("MyCursors\Clic_on_plus.ani") ; curseur animé

Procedure Destroy_Curseurs()
  DestroyCursor_(HCURSOR_B) 
  DestroyCursor_(HCURSOR_C)
  DestroyCursor_(HCURSOR_F)
  DestroyCursor_(HCURSOR_E)
  DestroyCursor_(HCURSOR_L)
  DestroyCursor_(HCURSOR_P)
EndProcedure


Global CurrentColor, CurrentMode, StartX, StartY

; Draw the mouse action result depending on the currently selected mode and event type
;
Procedure DrawAction(x, y, EventType)

  If StartDrawing(CanvasOutput(#GADGET_Canvas))
    Select CurrentMode
    
      Case #GADGET_Brush
        If EventType = #PB_EventType_LeftButtonDown Or EventType = #PB_EventType_MouseMove
          Circle(x, y, 5, CurrentColor)
        EndIf
        
      Case #GADGET_Line
        DrawImage(ImageID(#IMAGE_Content), 0, 0)
          LineXY(StartX, StartY, x, y, CurrentColor)
       
      Case #GADGET_Box
        DrawImage(ImageID(#IMAGE_Content), 0, 0)
        Box(StartX, StartY, x-StartX, y-StartY, CurrentColor)
        
      Case #GADGET_Circle
        DrawImage(ImageID(#IMAGE_Content), 0, 0)
        
        If x > StartX
          rx = x - StartX
        Else
          rx = StartX - x
        EndIf
        
        If y > StartY
          ry = y - StartY
        Else
          ry = StartY - y
        EndIf
        
        If GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_Modifiers) & #PB_Canvas_Control
          ry = rx
        EndIf
        
        Ellipse(StartX, StartY, rx, ry, CurrentColor)
      
      Case #GADGET_Fill
        If EventType = #PB_EventType_LeftButtonDown
          FillArea(x, y, -1, CurrentColor)
        EndIf
  
    EndSelect
    
    StopDrawing()
  EndIf

EndProcedure

UseJPEGImageDecoder()
UseJPEGImageEncoder()

CurrentColor = $000000
CurrentMode  = #GADGET_Brush
CreateImage(#IMAGE_Color, DesktopScaledX(35), DesktopScaledY(35), 24)
CreateImage(#IMAGE_Content, DesktopScaledX(380), DesktopScaledY(380), 24)

If OpenWindow(0, 0, 0, 460, 400, "CanvasGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  CanvasGadget(#GADGET_Canvas, 10, 10, 380, 380, #PB_Canvas_ClipMouse)

  ButtonImageGadget(#GADGET_Color, 400, 10, 50, 50, ImageID(#IMAGE_Color))
  
  ButtonGadget(#GADGET_Brush,  400, 100, 50, 25, "Brush",  #PB_Button_Toggle)
  ButtonGadget(#GADGET_Line,   400, 130, 50, 25, "Line",   #PB_Button_Toggle)
  ButtonGadget(#GADGET_Box,    400, 160, 50, 25, "Box",    #PB_Button_Toggle)
  ButtonGadget(#GADGET_Circle, 400, 190, 50, 25, "Circle", #PB_Button_Toggle)
  ButtonGadget(#GADGET_Fill,   400, 220, 50, 25, "Fill",   #PB_Button_Toggle)
    
  ButtonGadget(#GADGET_Clear,  400, 280, 50, 25, "Clear")
  
  ButtonGadget(#GADGET_Load,   400, 335, 50, 25, "Load")
  ButtonGadget(#GADGET_Save,   400, 365, 50, 25, "Save")
  
  SetGadgetState(#GADGET_Brush, 1)
  ;SetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_Cursor, #PB_Cursor_Cross)
  SetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_CustomCursor, HCURSOR_P)
  

  Repeat
    Event = WaitWindowEvent()
    
    If Event = #PB_Event_Gadget
    
      Select EventGadget()
          
        Case #GADGET_Brush
          SetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_CustomCursor, HCURSOR_P)
          CurrentMode = EventGadget()
          SetGadgetState(#GADGET_Brush, 1) : SetGadgetState(#GADGET_Line, 0) : SetGadgetState(#GADGET_Box, 0) : SetGadgetState(#GADGET_Circle, 0) : SetGadgetState(#GADGET_Fill, 0) 
          
        Case #GADGET_Line
          SetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_CustomCursor, HCURSOR_L)
          CurrentMode = EventGadget()
          SetGadgetState(#GADGET_Brush, 0) : SetGadgetState(#GADGET_Line, 1) : SetGadgetState(#GADGET_Box, 0) : SetGadgetState(#GADGET_Circle, 0) : SetGadgetState(#GADGET_Fill, 0) 
          
        Case #GADGET_Box
          SetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_CustomCursor, HCURSOR_B)
          CurrentMode = EventGadget()
          SetGadgetState(#GADGET_Brush, 0) : SetGadgetState(#GADGET_Line, 0) : SetGadgetState(#GADGET_Box, 1) : SetGadgetState(#GADGET_Circle, 0) : SetGadgetState(#GADGET_Fill, 0) 
          
        Case #GADGET_Circle
          SetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_CustomCursor, HCURSOR_E)
          CurrentMode = EventGadget()
          SetGadgetState(#GADGET_Brush, 0) : SetGadgetState(#GADGET_Line, 0) : SetGadgetState(#GADGET_Box, 0) : SetGadgetState(#GADGET_Circle, 1) : SetGadgetState(#GADGET_Fill, 0) 
          
        Case #GADGET_Fill
          SetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_CustomCursor, HCURSOR_F)
          CurrentMode = EventGadget()
          SetGadgetState(#GADGET_Brush, 0) : SetGadgetState(#GADGET_Line, 0) : SetGadgetState(#GADGET_Box, 0) : SetGadgetState(#GADGET_Circle, 0) : SetGadgetState(#GADGET_Fill, 1) 
                   
        Case #GADGET_Canvas ;{
          X = GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_MouseX)
          Y = GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_MouseY)
          Type = EventType()
        
          Select EventType()
          
            Case #PB_EventType_LeftButtonDown
              ;
              ; This stores the current content of the CanvasGadget in #IMAGE_Content,
              ; so it can be re-drawn while the mouse moves
              ;
              If StartDrawing(ImageOutput(#IMAGE_Content))
                DrawImage(GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_Image), 0, 0)
                StopDrawing()
              EndIf
              
              StartX = X
              StartY = Y
              DrawAction(X, Y, EventType())

            
            Case #PB_EventType_LeftButtonUp
              DrawAction(X, Y, EventType())
            
            Case #PB_EventType_MouseMove
              If GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton
                DrawAction(X, Y, EventType())
              EndIf
                      
          EndSelect
          ;}
          
        Case #GADGET_Color ;{
          CurrentColor = ColorRequester(CurrentColor)
          If StartDrawing(ImageOutput(#IMAGE_Color))
            Box(0, 0, 35, 35, CurrentColor)
            StopDrawing()
            SetGadgetAttribute(#GADGET_Color, #PB_Button_Image, ImageID(#IMAGE_Color))
          EndIf
          ;}        
        Case #GADGET_Clear ;{
          If StartDrawing(CanvasOutput(#GADGET_Canvas))
            Box(0, 0, 380, 380, $FFFFFF)
            StopDrawing()
          EndIf
          ;}         
        Case #GADGET_Load ;{
          File$ = OpenFileRequester("Load Image...", "", "JPEG Images|*.jpeg|All Files|*.*", 0)
          If File$
            If LoadImage(#IMAGE_LoadSave, File$)
              If StartDrawing(CanvasOutput(#GADGET_Canvas))
                Box(0, 0, 380, 380, $FFFFFF)
                DrawImage(ImageID(#IMAGE_LoadSave), 0, 0)
                StopDrawing()
              EndIf
              FreeImage(#IMAGE_LoadSave)
            Else
              MessageRequester("CanvasGadget", "Cannot load image: " + File$)
            EndIf
          EndIf
          ;}         
        Case #GADGET_Save ;{
          File$ = SaveFileRequester("Save Image...", File$, "JPEG Images|*.jpeg|All Files|*.*", 0)
          If File$ And (FileSize(File$) = -1 Or MessageRequester("CanvasGadget", "Overwrite this file? " + File$, #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes)
            If CreateImage(#IMAGE_LoadSave, 380, 380, 24) And StartDrawing(ImageOutput(#IMAGE_LoadSave))
              DrawImage(GetGadgetAttribute(#GADGET_Canvas, #PB_Canvas_Image), 0, 0)
              StopDrawing()
              
              If SaveImage(#IMAGE_LoadSave, File$, #PB_ImagePlugin_JPEG) = 0
                MessageRequester("CanvasGadget", "Cannot save image: " + File$)
              EndIf
              
              FreeImage(#IMAGE_LoadSave)
            EndIf
          EndIf
          ;}
          
      EndSelect
    
    EndIf
    
  Until Event = #PB_Event_CloseWindow
 Destroy_Curseurs()
EndIf
Quand tous les glands seront tombés, les feuilles dispersées, la vigueur retombée... Dans la morne solitude, ancré au coeur de ses racines, c'est de sa force maturité qu'il renaîtra en pleine magnificence...Jacobus.
Avatar de l’utilisateur
falsam
Messages : 7317
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Utiliser des curseurs personnalisés

Message par falsam »

Merci pour ce code bien documenté et cette collection de curseurs.
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
Jacobus
Messages : 1559
Inscription : mar. 06/avr./2004 10:35
Contact :

Re: Utiliser des curseurs personnalisés

Message par Jacobus »

Bonjour falsam,
De rien, c'est toujours un plaisir de partager des petits trucs qui, parfois peuvent débloquer un utilisateur et son code. :)
Quand tous les glands seront tombés, les feuilles dispersées, la vigueur retombée... Dans la morne solitude, ancré au coeur de ses racines, c'est de sa force maturité qu'il renaîtra en pleine magnificence...Jacobus.
Shadow
Messages : 1413
Inscription : mer. 04/nov./2015 17:39

Re: Utiliser des curseurs personnalisés

Message par Shadow »

Merci beaucoup pour ce très bon exemple très utile :)
Processeur: Intel Core I7-4790 - 4 Cœurs - 8 Thread: 3.60 Ghz.
Ram: 32 GB.
Disque: C: SDD 250 GB, D: 3 TB.
Vidéo: NVIDIA GeForce GTX 960: 2 GB DDR5.
Écran: Asus VX248 24 Pouces: 1920 x 1080.
Système: Windows 7 64 Bits.

PureBasic: 5.60 x64 Bits.
Répondre