Select a rectangle area on window using mouse

Share your advanced PureBasic knowledge/code with the community.
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Select a rectangle area on window using mouse

Post by BasicallyPure »

This could be useful for editing images when you need to select a portion of the image.

Code: Select all

; RectangleSelector.pb
; by BasicallyPure
; 8/28/2013
; PB 5.20 beta12 LTS(x86)
; windows only
; forum: http://www.purebasic.fr/english/viewtopic.php?f=40&t=56305
;
; Purpose: Use the mouse to select a rectangular area on a window.
; You can drag the selection rectangle with the mouse or resize the
; selection by draging the small square in the lower right corner.
; Clicking outside the selection will remove the selection rectangle.
; The structured variable 'selection.SelType' will contain the x y
; boundaries of the selected area.

EnableExplicit

Structure SelType
   left.i   ; left edge 'x' value of selected area
   right.i  ; right edge 'x' value of selected area
   top.i    ; top edge 'y' value of selected area
   bottom.i ; bottom edge 'y' value of selected area
   active.i ; set to #False when image is new or updated
   sFlag.i  ; if #True an area is selected
   limLeft.i   ; left boundary limit of selection area (window coordinates)
   limRight.i  ; right boundary limit of selection area (window coordinates)
   limTop.i    ; top boundary limit of selection area (window coordinates)
   limBottom.i ; bottom boundary limit of selection area (window coordinates)
EndStructure

Define selection.SelType

Procedure Update_Status()
   ; update the information displayed in the status bar
   Protected text.s, lenX.i, lenY.i
   Shared selection.SelType
   
   With selection
      lenX = \right - \left + 1
      lenY = \bottom - \top + 1
      
      If IsStatusBar(0)
         StatusBarText(0,0,"Size = " + Str(lenX) + " x " + Str(lenY))
         StatusBarText(0,1,"x/y ratio = " + StrF(lenX / lenY,5))
         StatusBarText(0,2,"xLeft = " + Str(\left - GadgetX(0)))
         StatusBarText(0,3,"yTop = " + Str(\top - GadgetY(0)))
         StatusBarText(0,4,"xRight = " + Str(\right - GadgetX(0)))
         StatusBarText(0,5,"yBottom = " + Str(\bottom - GadgetY(0)))
         If \sFlag : text = "True" : Else : text = "False" : EndIf
         StatusBarText(0,6,"Selection = " + text)
      EndIf
   EndWith
EndProcedure

Procedure SelectArea()
   ; Use the mouse to select an area on a window.
   
   Shared selection.SelType
   Static.i x, y, lenX, lenY, lastX, lastY, boxSet
   Static.i WIC = #PB_Window_InnerCoordinate
   Protected.i drag, stretch, msx, msy, ox, oy
   Protected Win = GetActiveWindow()
   
   With selection
      x = WindowMouseX(Win)
      y = WindowMouseY(Win)
   
      If x < \limLeft Or x > \limRight Or y < \limTop Or y > \limBottom
         ProcedureReturn
      EndIf
      
      If boxSet = #True And x > \right-9 And x < \right And y > \bottom-9 And y < \bottom
         stretch = #True ; resize selection rectangle is active
         x = lastX
         y = lastY
      ElseIf x > \left And x < \right And y > \top And y < \bottom
         drag = #True ; drage selection rectangle is active
         ox = lenX >> 1
         oy = lenY >> 1
         ; move cursor to center of selected area
         SetCursorPos_(\left+Abs(ox)+WindowX(Win, WIC), \top+Abs(oy)+WindowY(Win,WIC))
      Else
         drag = #False
      EndIf
      
      While WaitWindowEvent(100) <> #WM_LBUTTONUP
         StartDrawing(WindowOutput(Win))
            DrawingMode(#PB_2DDrawing_XOr)
            If \active = #True
               ; erase previous selection rectangle
               LineXY(lastX, lastY, lastX + lenX, lastY, 0)
               LineXY(lastX, lastY, lastX, lastY + lenY, 0)
               LineXY(lastX, lastY + lenY, lastX + lenX, lastY + lenY, 0)
               LineXY(lastX + lenX, lastY, lastX + lenX, lastY + lenY, 0)
               If boxSet = #True
                  boxSet = #False
                  Box(\right - 8, \bottom - 8, 8, 8, 0)
               EndIf
            Else
               ; activate for a new or freshly updated image
               \active = #True
               lastX = x
               lastY = y
               boxSet = #False
            EndIf
            
            msx = WindowMouseX(Win)
            msy = WindowMouseY(Win)
            
            ; check if mouse cursor is in bounds
            If msx >= \limLeft And msx < \limRight And msy >= \limTop And msy < \limBottom
               If drag = #False ; calculate size of next selection outline
                  lenX = msx - x
                  lenY = msy - y
               Else ; drag the selection outline
                  x = msx - lenX >> 1
                  y = msy - lenY >> 1
                  
                  ; prevent out of bounds dragging
                  If x < \limLeft : x = \limLeft : EndIf ; check left side
                  If y < \limTop  : y = \limTop  : EndIf ; check top side
                  If x > \limRight -1 - lenX ; check right side
                     x = \limRight -1 - lenX
                  EndIf
                  If y > \limBottom -1 - lenY ; check bottom side
                     y = \limBottom -1 - lenY
                  EndIf
               EndIf
            EndIf
            ; draw the selection rectangle
            LineXY(x, y, x + lenX, y, 0)
            LineXY(x, y, x, y + lenY, 0)
            LineXY(x, y + lenY, x + lenX, y + lenY, 0)
            LineXY(x + lenX, y, x + lenX, y + lenY, 0)
            
            lastX = x
            lastY = y
         StopDrawing()
         
         \left = x
         \top = y
         \right = x + lenX
         \bottom = y + lenY
         
         Update_Status()
      Wend
      
      If \left > \right ; fix left/right crossover
         Swap \left, \right : x + lenX : lastX = x : lenX * -1
      EndIf
      
      If \top > \bottom ; fix top/bottom crossover
         Swap \top, \bottom : y + lenY : lastY = y : lenY * -1
      EndIf
      
      ; mouse button was released
      If \left = \right And \top = \bottom ; single point so no selection
         \sFlag = #False
      Else ; a valid selection has been made
         \sFlag = #True
         ; draw the selection resize box
         StartDrawing(WindowOutput(Win))
            DrawingMode(#PB_2DDrawing_XOr)
            Box(\right - 8, \bottom - 8, 8, 8, 0)
            boxSet = #True
         StopDrawing()
      EndIf
      
   EndWith
   
   Update_Status()
   
EndProcedure


#WinNum = 0
If OpenWindow(#WinNum,0,0,750,500,"Rectangle Selection Demo",#PB_Window_ScreenCentered | #PB_Window_SystemMenu)
   CreateImage(0,640,480,24,$B8CE8C)
   CreateStatusBar(0,WindowID(#WinNum))
      AddStatusBarField(#PB_Ignore)
      AddStatusBarField(#PB_Ignore)
      AddStatusBarField(#PB_Ignore)
      AddStatusBarField(#PB_Ignore)
      AddStatusBarField(#PB_Ignore)
      AddStatusBarField(#PB_Ignore)
      AddStatusBarField(#PB_Ignore)
      
   ResizeWindow(#WinNum,#PB_Ignore,#PB_Ignore,#PB_Ignore,WindowHeight(#WinNum)+StatusBarHeight(0))
   ImageGadget(0,100,10,640,480,ImageID(0))
   
   ButtonGadget(1,10,10,80,30,"Unselect")
   
   ; set up selection area boundaries using image gadget 0
   With selection
      \limLeft = GadgetX(0)
      \limTop  = GadgetY(0)
      \limRight = \limLeft + GadgetWidth(0)
      \limBottom = \limTop + GadgetHeight(0)
   EndWith

   Repeat ; event loop
      Select WaitWindowEvent()
         Case #PB_Event_CloseWindow
            Break
         Case #PB_Event_Gadget
            Select EventGadget()
               Case 1 ; unselect programatically
                  If selection\sFlag
                     With selection
                        \active = #False
                        \sFlag  = #False
                        \top    = \bottom
                        \left   = \right
                     EndWith
                     SetGadgetState(0,ImageID(0))
                     Update_Status()
                  EndIf
            EndSelect
         Case #WM_LBUTTONDOWN
            SelectArea()
      EndSelect
   ForEver
EndIf
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
Fred
Administrator
Administrator
Posts: 18247
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Select a rectangle area on window using mouse

Post by Fred »

Nice, but you should really use a CanvasGadget() for that ;)
Post Reply