SyntaxBomb - Indie Coders

Languages & Coding => Blitz Code Archives => BlitzPlus Gui => Topic started by: BlitzBot on June 29, 2017, 00:28:41

Title: [bb] Splitter Gadget by Jim Teeuwen [ 1+ years ago ]
Post by: BlitzBot on June 29, 2017, 00:28:41
Title : Splitter Gadget
Author : Jim Teeuwen
Posted : 1+ years ago

Description : A gadget that allows realtime resizing of gadets with a simple mousedrag. Demo code at the bottom

[FIX 1]
Added an optional Size parameter to the CreateSplitter function. this is the Splitter Size (eg Width) it defaults to 3 pixels, but it was pointed out to me that this is a bit hard to click.

[Fix 2]
KeithJ was kind enough to provide us with a userlib extension, wich enables us to actually change the cursor to some nifty resize arrows. many thanks for this Keith!

To use it, first create a file in the blitzplus Userlibs directory, and name it 'user32.decls'
Open it in notepad and add the following text to the file:

=============================================
.lib "user32.dll"
LoadCursor%( ID, Cursor ):"LoadCursorA"
SetCursor%( ID ):"SetCursor"
=============================================

This creates 2 methods for use to use in blitz. namely LoadCursor() and SetCursor.. the ID values represent constants wich are preset by the windows API. I puzzled a little and found the following constants for different cursors:
=================================
;// Load with SetCursor(LoadCursor(0, IDC_SIZEXX))
Const IDC_SIZENONE            = 32641      ;// No Cursor
Const IDC_SIZENWSE            = 32642      ;// Top left to bottom right
Const IDC_SIZENESW            = 32643      ;// Top right to bottom left
Const IDC_SIZEWE            = 32644      ;// Left to right
Const IDC_SIZENS            = 32645      ;// Top to Bottom
Const IDC_SIZEMOVE            = 32646      ;// Cross (4 directions)
Const IDC_SIZEDENIED         = 32648      ;// Circle with diagonal Line
Const IDC_SIZEHAND            = 32649      ;// Hand cursor
Const IDC_SIZEHOURGLASS         = 32650      ;// Hourglass
Const IDC_SIZEHELP            = 32651      ;// QuestionMark
Const IDC_SIZEMOVENS         = 32652      ;// Move Top to Bottom
Const IDC_SIZEMOVEWE         = 32653      ;// Move Left to right
Const IDC_SIZEMOVENSEW         = 32654      ;// Move 4 directions
Const IDC_SIZEMOVEN            = 32655      ;// Move Up
Const IDC_SIZEMOVES            = 32656      ;// Move Down
Const IDC_SIZEMOVEW            = 32657      ;// Move Left
Const IDC_SIZEMOVEE            = 32658      ;// Move Right
Const IDC_SIZEMOVENW         = 32659      ;// Move Top Left
Const IDC_SIZEMOVENE         = 32660      ;// Move Top Right
Const IDC_SIZEMOVESW         = 32661      ;// Move Bottom Left
Const IDC_SIZEMOVESE         = 32662      ;// Move Bottom Right
Const IDC_SIZECD            = 32663      ;// CD loading Icon

=================================

With the '.decls' file set and the constants ready, we can change the code to incorporate the cursor change. Below is the modified code. in the SplitterEventLoop() method there have been some aditions. namely the calls to SetCursor(LoadCursor(0, IDC_SIZEWE)) and SetCursor(LoadCursor(0, IDC_SIZENS))  for left-to-right and top-to-bottom arrows respectivly


Code :
Code (blitzbasic) Select
;//
;// Name : Splitter Control
;// Desc : BlitzPlus extension,
;//  A Splitter can simultaniously scale 2 controls,
;//  By simply dragging the splitter back and forth.
;//  It serves for resizing purposes, and can be applied for
;//  Both horizontal and vertical resizing.
;// Author : Jim Teeuwen / Defiance
;//  http://www.transmuter.nl :: blitz@transmuter.nl
;//
;//  (c) 2002-2004, Jim Teeuwen. All rights reserved.
;//

Type Splitter
Field Gadget
Field FirstControl, SecondControl
Field Alignment
Field Group
Field Size
Field Canvas
End Type

Const SPLITTER_ALIGN_VERTICAL = 0
Const SPLITTER_ALIGN_HORIZONTAL = 1


Function CreateSplitter.Splitter(Group, PrimaryGadget, SecundaryGadget, Alignment = SPLITTER_ALIGN_VERTICAL, Size = 3)

spl.Splitter = New Splitter
splFirstControl = PrimaryGadget
splSecondControl = SecundaryGadget
splAlignment = Alignment
splGroup = Group
splSize = Size
splGadget = CreatePanel(0, 0, splSize, ClientHeight(Group), Group, 0)

If(splAlignment = SPLITTER_ALIGN_VERTICAL) Then
SetGadgetLayout(splgadget, 1, 1, 1, 1)
Else
SetGadgetLayout(splgadget, 1, 1, 0, 0)
End If

splCanvas = CreateCanvas(0, 0, GadgetWidth(splGadget), GadgetHeight(splGadget), splGadget)
SetGadgetLayout(splCanvas, 1, 1, 1, 1)
SetBuffer(CanvasBuffer(splCanvas))
Color(100, 100, 112)
Rect(0, 0, GadgetWidth(splGadget), GadgetHeight(splGadget))

UpdateSplitter(spl)

If(splSecondControl <> 0) Then
If(splAlignment = SPLITTER_ALIGN_VERTICAL) Then
SetGadgetShape(splSecondControl, GadgetX(splSecondControl) + splSize, GadgetY(splSecondControl), GadgetWidth(splSecondControl) - splSize, GadgetHeight(splSecondControl))
Else
SetGadgetShape(splSecondControl, GadgetX(splSecondControl), GadgetY(splSecondControl) + splSize, GadgetWidth(splSecondControl), GadgetHeight(splSecondControl) - splSize)
End If
End If

Return spl

End Function

Function UpdateSplitter(spl.Splitter)

If(splFirstControl = 0) Then
If(splAlignment = SPLITTER_ALIGN_VERTICAL) Then
SetGadgetShape(splGadget, 0, 0, splSize, ClientHeight(splGroup))
Else
SetGadgetShape(splGadget, 0, 0, ClientWidth(splGroup), 3)
End If
Else
If(splAlignment = SPLITTER_ALIGN_VERTICAL) Then
SetGadgetShape(splGadget, GadgetWidth(splFirstControl), GadgetY(splFirstControl), splSize, GadgetHeight(splFirstControl))
Else
SetGadgetShape(splGadget, GadgetX(splFirstControl), GadgetHeight(splFirstControl), GadgetWidth(splFirstControl), splSize)
End If
End If

End Function

Function SplitterEventLoop(spl.Splitter)

If(MouseDown(1)) Then
offsetX = EventX()
offsetY = EventY()

If(splAlignment = SPLITTER_ALIGN_VERTICAL) Then
If((GadgetWidth(splFirstControl) + offsetX) < (ClientWidth(splGroup) - splSize) And (GadgetWidth(splFirstControl) + offsetX) > 0) Then
SetGadgetShape(splFirstControl, GadgetX(splFirstControl), GadgetY(splFirstControl), (GadgetWidth(splFirstControl) + offsetX), GadgetHeight(splFirstControl))
SetGadgetShape(splGadget, GadgetWidth(splFirstControl), GadgetY(splFirstControl), splSize, GadgetHeight(splFirstControl))
SetGadgetShape(splSecondControl, GadgetX(splGadget) + splSize, GadgetY(splSecondControl), GadgetWidth(splSecondControl) - offsetX, GadgetHeight(splSecondControl))
End If
Else
If((GadgetHeight(splFirstControl) + offsetY) < (GadgetHeight(splGroup) - splSize) And (GadgetHeight(splFirstControl) + offsetY) > 0) Then
SetGadgetShape(splFirstControl, GadgetX(splFirstControl), GadgetY(splFirstControl), GadgetWidth(splFirstControl), (GadgetHeight(splFirstControl) + offsetX))
SetGadgetShape(splGadget, GadgetX(splFirstControl), GadgetHeight(splFirstControl), GadgetWidth(splFirstControl), splSize)
SetGadgetShape(splSecondControl, GadgetX(splSecondControl), (GadgetY(splGadget) + splSize), GadgetWidth(splSecondControl), GadgetHeight(splSecondControl) - offsetX)
End If
End If
End If

If(splAlignment = SPLITTER_ALIGN_VERTICAL) Then
SetCursor(LoadCursor(0, IDC_SIZEWE))
Else
SetCursor(LoadCursor(0, IDC_SIZENS))
End If

UpdateSplitter()

End Function


;// ### DEMO #####################################################################################

SplitterDemo()
Global mySplitter.Splitter

;/* Window Messages */
Const WM_MOUSEMOVE = $203
Const WM_RESIZE = $802
Const WM_CLOSE = $803

Const IDC_SIZENONE = 32641 ;// No Cursor
Const IDC_SIZENWSE = 32642 ;// Top left to bottom right
Const IDC_SIZENESW = 32643 ;// Top right to bottom left
Const IDC_SIZEWE = 32644 ;// Left to right
Const IDC_SIZENS = 32645 ;// Top to Bottom
Const IDC_SIZEMOVE = 32646 ;// Cross (4 directions)
Const IDC_SIZEDENIED = 32648 ;// Circle with diagonal Line
Const IDC_SIZEHAND = 32649 ;// Hand cursor
Const IDC_SIZEHOURGLASS = 32650 ;// Hourglass
Const IDC_SIZEHELP = 32651 ;// QuestionMark
Const IDC_SIZEMOVENS = 32652 ;// Move Top to Bottom
Const IDC_SIZEMOVEWE = 32653 ;// Move Left to right
Const IDC_SIZEMOVENSEW = 32654 ;// Move 4 directions
Const IDC_SIZEMOVEN = 32655 ;// Move Up
Const IDC_SIZEMOVES = 32656 ;// Move Down
Const IDC_SIZEMOVEW = 32657 ;// Move Left
Const IDC_SIZEMOVEE = 32658 ;// Move Right
Const IDC_SIZEMOVENW = 32659 ;// Move Top Left
Const IDC_SIZEMOVENE = 32660 ;// Move Top Right
Const IDC_SIZEMOVESW = 32661 ;// Move Bottom Left
Const IDC_SIZEMOVESE = 32662 ;// Move Bottom Right
Const IDC_SIZECD = 32663 ;// CD loading Icon

Function SplitterDemo()

;// Create a new form with some panels on it.
;// We will use the splitter control to be able to resize these panels
;// at runtime. The splitter will appear as a darkgray 3-pixel wide line between 2 panels
;// To use it., simply click on the splitter and drag it left/right(vertically aligned splitter)
;// or up/down (horizontally aligned Splitter)

;/* frmMain */
frmMain = CreateWindow("UI Test", 20, 20, ClientWidth(Desktop()) - 40, ClientHeight(Desktop()) - 40, 0)
SetMinWindowSize(frmMain, 320, 240)
SetGadgetText(frmMain, "Splitter Control Demo")

;/* Workspace */
pnlWorkSpace = CreatePanel(0, 0, ClientWidth(frmMain) - 193, ClientHeight(frmMain) , frmMain, 0)
SetGadgetLayout(pnlWorkSpace, 1, 1, 1, 1)
SetGadgetText(pnlWorkSpace, "WorkSpace")
SetPanelColor(pnlWorkSpace, 136, 147, 158)

;/* pnlProperties *.
pnlProperties = CreatePanel(GadgetWidth(frmMain) - 200, 0, 200, GadgetHeight(frmMain), frmMain, 0)
SetGadgetLayout(pnlProperties, 0, 1, 1, 1)
SetGadgetText(pnlProperties, "Properties")

trvProject = CreateTreeView(0, 0, GadgetWidth(pnlProperties) - 8, GadgetHeight(pnlProperties) / 3, pnlProperties)
SetGadgetLayout(trvProject, 1, 1, 1, 0)

;/* Splitter */
;// Here we create the splitter.
;// This splitter will 'split' the 2 panels 'pnlWorkSpace' and 'pnlProperties'
;// Normally we would add this splitter gadget after the first panel, but we need a valid
;// reference to the second Properties panel for this to work. Thus, we create the splitters
;// AFTER ALL THE PANELS ARE CREATED!
;// This splitter will be Vertically aligned
;// Calling the create method takes a few parameters:
;//
;// spl = CreateSplitter(group, firstControl, secondControl, alignment)
;//
;// group : This is the group to wich the splitter is added. works the same as
;//  adding a normal gadget.
;// firstControl : This is the first gadget to wich the splitter will apply.
;//  In our case this is 'pnlWorkSpace'
;// secondControl : Naturally, this is the second gadget to wich the splitter applies
;//  You see now why we need to wait with splitter creation till ALL
;//  the regular controls have been initialised.
;// Alignment : This is the splitter alignment. Can be either SPLITTER_ALIGN_VERTICAL (default),
;//  or SPLITTER_ALIGN_HORIZONTAL. WIth the Vertical splitter, your 2 gadgets
;//  to wich it applies should be left/right of eachother
;//  With the Horizontal splitter, your 2 gadgets to wich the splitter applies
;//  should be above/underneath eachother.
;//
;// You do not have to care about the positioning of the 2 gadgets, this will all be handled
;// by the Splitter code.
mySplitter.Splitter = CreateSplitter(frmMain, pnlWorkSpace, pnlProperties, SPLITTER_ALIGN_VERTICAL)


;// now we enter the main eventloop for the form and check for messages
;// The messages we want to handle for the splitter are:
;// WM_RESIZE and WM_MOUSEMOVE
Repeat

Select(WaitEvent())
Case WM_CLOSE ;// $803 Form close event
End

Case WM_RESIZE ;// $802
;// Update the splitter
UpdateSplitter(mySplitter)

Case WM_MOUSEMOVE ;// $203
;// Handle the movement
;// Note that only the Canvas gadget will detect MouseMove events, so I
;// added a special Canvas that streches the entire surface of the splitter
;// This will detect the Mouseevents for us. we have to determine here of the
;// mousemove event was fired by our splitter
Select(EventSource())
Case mySplitterCanvas
SplitterEventLoop(mySplitter)

End Select

End Select

Forever

;// Ready! Now start the app and notice the small darkgray line between the 2 panels
;// just click on it and drag the line Left/right while holding the mousebutton to see
;// the Splitter in action!

End Function


Comments : none...