In accordance with Chen's request, I am now posting my more powerful version of the gadget resizing procedures together with a test routine.
The status bar resizing (righmost field only) has only been tested under Windows Me.
Code: Select all
; RS_ResizeGadgets.pbi AKJ 25-Mar-06
; Automatically Resize PureBasic Gadgets
; Author: USCode, modified by AKJ to enable scaled (proportionate) resizing
; February 19, 2006 - Updated for PureBasic 4.0
; April 15, 2004 - Original release
; http://forums.purebasic.com/english/viewtopic.php?t=10218&postdays=0&postorder=asc&start=0
;
; To use:
; Include this source file
; Create windows and gadgets
; Call RS_Register() ONCE for EACH Gadget to be resized, specifiying resize codes
; Call RS_Margins() once for each window if margin widths are to be fixed
; (A margin is the clear space usually present just inside a window's edge)
; Call RS_Resize() in the event loop wherever #PB_Event_SizeWindow is checked
; Optionally call RS_Unregister() to remove a gadget from the auto-resizing list
; Optionally call RS_GetWinSize() to estimate the window size that
; will result in a specified gadget having a specified target size
;- Declarations
Structure RS_gadget_struct
Window.l ; Window number
Gadget.l ; Gadget number
x.l ; Original gadget position
y.l
w.l ; Original gadget size
h.l
left.l ; Resizing codes (text characters, converted to numeric)
right.l
top.l
bottom.l
EndStructure
; Special case that defines a window (gadget number <0)
; All window definition elements precede gadget elements
; Window.l ; Window number
; Gadget.l ; -n if this window has/had n registered gadgets, else #PB_Ignore if 0
; x.l ; Flag: #True iff left/right window margins should remain fixed width
; y.l ; Flag: #True iff top/bottom window margins should remain fixed width
; w.l ; Original window size
; h.l
; left.l ; Dynamically calculated window margin widths
; right.l
; top.l
; bottom.l
Global NewList RS_Gadgets.RS_gadget_struct()
;}
Procedure RS_Margins(RS_window.l, fixed_lr.l=#True, fixed_tb.l=#True)
; Set whether a window's margins (borders) should remain fixed width
; By default (this procedure unused), they change proportionally with the window size
; The first parameter identifies the window whose margins are to be configured
; The second parameter specifies whether the left/right margins should be fixed
; The third parameter specifies whether the top/bottom margins should be fixed
; (This procedure is also called internally by other procedures to define a window)
; The margin width values are calculated automatically by RS_Register()
Protected found.l = #False
With RS_Gadgets()
; First see if the window is already defined
ResetList(RS_Gadgets())
While NextElement(RS_Gadgets())
If \Window=RS_window And \Gadget<0
found=#True: Break
EndIf
Wend
If Not found
; Define the window, using it's current size
ResetList(RS_Gadgets())
AddElement(RS_Gadgets()) ; !!! Bug: current element is set to -1 with InsertElement(RS_Gadgets())
\Window = RS_window
\Gadget = #PB_Ignore ; Flag: No gadgets yet registered for this window
\w = WindowWidth(RS_window) ; Original window size
\h = WindowHeight(RS_window)
EndIf
; Configure the margins
If fixed_lr<>#PB_Ignore: \x=fixed_lr: EndIf
If fixed_tb<>#PB_Ignore: \y=fixed_tb: EndIf
EndWith
EndProcedure
Procedure RS_Register(RS_window.l, RS_gadget.l, RS_resize_codes$="")
; Register gadget to be resized and how to resize
; Parameters: WindowID (long), GadgetID (long), RS_resize_codes$ (string) for left, right, top, bottom
; (original order was: left, top, right, bottom)
;
; Resize/lock codes (case insensitive) for each of the four sides:
; Unlocked: "0" or "u" or "f" (free, floating), with gadget size unchanged
; Locked : "1" or "l" or "a" (anchored)
; Scaled : "2" or "s" or "p" (proportionate) [The default]
; There is a special case:
; If the left/right (or top/bottom) codes are "ff" (or "00" or "uu") then the
; gadget's centre is moved proportionally, but the gadget's size is unchanged
; If a gadget is assigned no resize/lock codes, they will default to "ssss"
;
; These combinations resize the gadgets width/height: "aa" "as" "sa" "ss"
; These combinations do not resize the gadget: Any combination including an "f"
; These combinations do not seem to be particularly useful: "fs" "sf"
Protected index.l ; Used in calculating margin sizes
Protected marl.l, marr.l, mart.l, marb.l ; Margin widths
RS_resize_codes$=LCase(RS_resize_codes$)
With RS_Gadgets()
; First ensure an element for the window exists in the linked list
RS_Margins(RS_window, #PB_Ignore, #PB_Ignore)
index = ListIndex(RS_Gadgets()) ; Remember the window element's position
; Create an element for the gadget at the end of the linked list
LastElement(RS_Gadgets())
AddElement(RS_Gadgets())
\Window = RS_window
\Gadget = RS_gadget
\x = GadgetX(RS_gadget)
\y = GadgetY(RS_gadget)
\w = GadgetWidth(RS_gadget)
\h = GadgetHeight(RS_gadget)
; Convert codes from string to numeric
\left = (FindString("ulsfap012", Mid(RS_resize_codes$,1,1), 1)+2)%3
\right = (FindString("ulsfap012", Mid(RS_resize_codes$,2,1), 1)+2)%3
\top = (FindString("ulsfap012", Mid(RS_resize_codes$,3,1), 1)+2)%3
\bottom= (FindString("ulsfap012", Mid(RS_resize_codes$,4,1), 1)+2)%3
; Calculate relative margin widths based on the current gadget
marl=\x: marr=-\x-\w
mart=\y: marb=-\y-\h
; Calculate actual margin widths based on all gadgets found so far for the window
SelectElement(RS_Gadgets(), index) ; Revisit the window definition
marr+\w: marb+\h ; Add the window sizes
If marl<0: marl=0: EndIf ; Sanity tests
If marr<0: marr=0: EndIf
If mart<0: mart=0: EndIf
If marb<0: marb=0: EndIf
If \Gadget=#PB_Ignore ; If this is the first gadget for the window
\Gadget=-1
\left=marl: \right=marr: \top=mart: \bottom=marb
Else ; Remember any margin narrower than so far found
\Gadget-1
If \left>marl: \left=marl: EndIf
If \right>marr: \right=marr: EndIf
If \top>mart: \top=mart: EndIf
If \bottom>marb: \bottom=marb: EndIf
EndIf
EndWith
EndProcedure
Procedure RS_Unregister(RS_window.l, RS_gadget.l)
; Contributed by "FloHimself"
; Unregistering a gadget is equivalent to giving it resizing codes of "afaf"
Protected found=#False
If RS_gadget>=0 ; Not allowed to delete elements that define windows
ResetList(RS_Gadgets())
With RS_Gadgets()
While NextElement(RS_Gadgets())
If \Window=RS_window And \Gadget=RS_gadget
DeleteElement(RS_Gadgets())
found=#True: Break
EndIf
Wend
If found
; Update count in window definition element
RS_Margins(RS_window, #PB_Ignore, #PB_Ignore) ; Find the window element
\Gadget+1 ; E.g. -4 becomes -3
If \Gadget<-9999 Or \Gadget>=0: \Gadget=#PB_Ignore: EndIf
EndIf ; found
EndWith
EndIf
EndProcedure
Procedure RS_Resize(RS_window.l, RS_gadget.l=#PB_Ignore, *RS_Width.l=0, *RS_Height.l=0)
; Resize all registered gadgets (and any statusbar) for the resizing window RS_window
; This should normally be called from the event loop whenever #PB_Event_SizeWindow occurs
;
; Typically, only argument 1 is defined, with the other three arguments unused.
; If the second argument is a gadget number, the gadgets are not resized,
; instead the new width and height for that gadget are returned in arguments 3 and 4
; based upon window dimensions given on entry also in arguments 3 and 4
; This feature is used by RS_GetWinSize()
;
Protected RS_x.l, RS_y.l, RS_w.l, RS_h.l ; Original gadget size
Protected oldw.l, oldh.l ; Original window size
Protected winw.l, winh.l ; Current window size
Protected oldl.l, oldr.l, oldt.l, oldb.l ; Window's original margin widths
Protected marl.l, marr.l, mart.l, marb.l ; Window's new margin sizes
Protected x,y ; Relative to left/top margins
Protected sbh, cn$, parts, part ; Status bar handle, classname, parts, part#
; Get the window's current or target size
If RS_gadget=#PB_Ignore
winw = WindowWidth(RS_window): winh = WindowHeight(RS_window)
Else
winw = PeekL(*RS_Width): winh = PeekL(*RS_Height)
EndIf
With RS_Gadgets()
; Find the linked list element defining the window
RS_Margins(RS_window, #PB_Ignore, #PB_Ignore)
; Retrieve the window's original size
oldw=\w: oldh=\h
; Retrieve the old margin sizes and calculate the new sizes
; N.B. Do not precalculate 'winw/oldw' as this causes a loss of accuracy
oldl=\left: marl=oldl: If \x=0: marl*winw/oldw: EndIf
oldr=\right: marr=oldr: If \x=0: marr*winw/oldw: EndIf
oldt=\top: mart=oldt: If \y=0: mart*winh/oldh: EndIf
oldb=\bottom:marb=oldb: If \y=0: marb*winh/oldh: EndIf
; Calculate the old and new window sizes INSIDE the margins
oldw-oldl-oldr: winw-marl-marr
oldh-oldt-oldb: winh-mart-marb
; Resize the gadgets
While NextElement(RS_Gadgets()) ; Gadget elements always follow window elements
If \Window = RS_window
x=\x-oldl ; x is now relative to the original left margin
Select \left
Case 0: RS_w = #PB_Ignore ; Unlocked, free, floating
Select \right
Case 0: RS_x = ((x*2+\w)*winw/oldw-\w)/2
Case 1: RS_x = x+winw-oldw
Case 2: RS_x = (x+\w)*winw/oldw-\w
EndSelect
Case 1: RS_x = x ; Locked, anchored
Select \right
Case 0: RS_w = #PB_Ignore
Case 1: RS_w = \w+winw-oldw
Case 2: RS_w = (\w+x)*winw/oldw-x
EndSelect
Case 2: RS_x = x*winw/oldw ; Scaled, proportionate
Select \right
Case 0: RS_w = #PB_Ignore
Case 1: RS_w = \w+x-RS_x+winw-oldw
Case 2: RS_w = \w*winw/oldw
EndSelect
EndSelect ; \left
;
y=\y-oldt ; y is now relative to the original top margin
Select \top
Case 0: RS_h = #PB_Ignore ; Free
Select \bottom
Case 0: RS_y = ((y*2+\h)*winh/oldh-\h)/2
Case 1: RS_y = y+winh-oldh
Case 2: RS_y = (y+\h)*winh/oldh-\h
EndSelect
Case 1: RS_y = y ; Anchored
Select \bottom
Case 0: RS_h = #PB_Ignore
Case 1: RS_h = \h+winh-oldh
Case 2: RS_h = (\h+y)*winh/oldh-y
EndSelect
Case 2: RS_y = y*winh/oldh ; Scaled
Select \bottom
Case 0: RS_h = #PB_Ignore
Case 1: RS_h = \h+y-RS_y+winh-oldh
Case 2: RS_h = \h*winh/oldh
EndSelect
EndSelect ; \top
If RS_gadget=#PB_Ignore ; If resizing
ResizeGadget(\Gadget, RS_x+marl, RS_y+mart, RS_w, RS_h)
ElseIf RS_gadget=\Gadget ; If gadget size is wanted
PokeL(*RS_Width, RS_w): PokeL(*RS_Height, RS_h) ; Return the result
ProcedureReturn
EndIf
EndIf ; Window
Wend ; NextElement()
EndWith ; RS_Gadgets()
; Resize the last part (only) of any statusbar
sbh=GetWindow_(WindowID(RS_window), #GW_CHILD)
Repeat
cn$=Space(20) : GetClassName_(sbh,cn$,20)
If cn$="msctls_statusbar32": Break: EndIf
sbh=GetWindow_(sbh, #GW_HWNDNEXT)
Until sbh=0
If sbh ; If a status bar
parts=SendMessage_(sbh, #SB_GETPARTS, 0, 0) ; Parts in statusbar
If parts
Protected Dim rightedge(parts-1)
SendMessage_(sbh, #SB_GETPARTS, parts, @rightedge())
rightedge(parts-1)=-1 ; Resize the last part to reach the window edge
SendMessage_(sbh, #SB_SETPARTS, parts, @rightedge()) ; Redraw statusbar
EndIf ; parts
EndIf ; sbh
EndProcedure
Procedure RS_GetWinSize(RS_window.l, RS_gadget.l, *RS_width, *RS_height)
; Calculate the window size that will result in a gadget having a given target size
; On entry, arguments 3 and 4 contain the gadget size, on return they hold the window size
; If the gadget cannot be resized (an error), return the original window size
Protected targetw, targeth, oldw1, oldh1, oldw, oldh, gadw1, gadh1, gadw, gadh
; Get the target gadget size
targetw = PeekL(*RS_width): targeth = PeekL(*RS_height)
; Find the linked list element defining the window
RS_Margins(RS_window, #PB_Ignore, #PB_Ignore)
; Get the window's original width and height
oldw=RS_Gadgets()\w: oldh=RS_Gadgets()\h
oldw1=oldw: oldh1=oldh
gadw1=oldw: gadh1=oldh ; First start point for iteration
gadw=oldw*2: gadh=oldh*2 ; Second start point
; Get the gadget size for the original window size
RS_Resize(RS_window, RS_gadget, @gadw1, @gadh1)
With RS_Gadgets()
If \left*\right*\top*\bottom ; If the gadget can be resized
EndWith
; Get the gadget size for double that window size
RS_Resize(RS_window, RS_Gadget, @gadw, @gadh)
; Use 'Regula Falsi' method to iterate the window size for the target gadget size
; x3 = x1 - (x2-x1) * f(x1) / (f(x2)-f(x1)) where f() = gadgetsize - targetsize
; First iteration (result may differ from target by a few pixels)
oldw = oldw1-oldw*(gadw1-targetw)/(gadw-gadw1)
oldh = oldh1-oldh*(gadh1-targeth)/(gadh-gadh1)
; See what the gadget sizes are now
gadw=oldw: gadh=oldh
RS_Resize(RS_window, RS_gadget, @gadw, @gadh)
; Second iteration (result will probably be exactly equal to target)
If gadw<>gadw1: oldw = oldw1-(oldw-oldw1)*(gadw1-targetw)/(gadw-gadw1): EndIf
If gadh<>gadh1: oldh = oldh1-(oldh-oldh1)*(gadh1-targeth)/(gadh-gadh1): EndIf
EndIf
PokeL(*RS_width, oldw): PokeL(*RS_height, oldh) ; Return the desired window size
EndProcedure
;- Scaling logic
; Scaling logic in going from old window size to new window size:
; If either code is 0 (free), the gadget size remains unchanged
; 0,0 Free, Free (special case)
; Distance between the gadget centre and window centre is scaled
; 0,1 Free, Anchored
; 0,2 Free, Scaled
; The distance to the right/bottom of the gadget is scaled inside window margins
; 1,0 Anchored, Free
; 1,1 Anchored, Anchored
; 1,2 Anchored, Scaled
; The distance to the right/bottom of the gadget is scaled inside window margins
; 2,0 Scaled, Free
; The distance to the left/top of the gadget is scaled inside window margins
; 2,1 Scaled, Anchored
; The distance to the left/top of the gadget is scaled inside window margins
; 2,2 Scaled, Scaled [Default]
; All distances (left/top, gadget size, right/bottom) are scaled inside window margins
; With variable margins, this is simply a magnification/reduction of the entire window image
;}
Code: Select all
; RS_ResizeGadgets Test 3 AKJ 25-Mar-06
; PureBasic 4 Beta 7
; Experiment: Drag an edge or corner of the window to resize it
; Experiment: Clicking button 9 will reduce it to a square shape
; N.B. I have declared ;- as a Folding Start Keyword in File -> Preferences -> Folding
;- Declarations
EnableExplicit
IncludePath #PB_Compiler_Home+"AKJ Procedures\"
XIncludeFile "FN RESIZE GADGETS.pbi"
;}
;- GUI constants
Enumeration
#winMain
#staBar ; Status bar
#but0 ; Other button IDs follow this one
EndEnumeration
;}
;- GUI metrics
Define gap, stah, butw, buth, winw, winh
gap=30 ; Standard unit size of gap between gadgets
stah=24 ; Status bar height
butw=70: buth=40 ; Buttons
winw=butw*6+gap*9: winh=buth*3+gap*6+stah ; Window with 6 buttons across and 3 down
;}
;- GUI creation
Define flags, x, y, butx, buty, butnum
flags=#PB_Window_SizeGadget | #PB_Window_SystemMenu | #PB_Window_TitleBar | #PB_Window_ScreenCentered
If OpenWindow(#winMain, 0,0, winw,winh, "Resize Test", flags)
CreateStatusBar(#staBar, WindowID(#winMain))
AddStatusBarField(100): StatusBarText(#staBar, 0, "Part 0")
AddStatusBarField(100): StatusBarText(#staBar, 1, "Part 1")
AddStatusBarField(winw-200)
CreateGadgetList(WindowID(#winMain))
; Create 18 buttons
y=gap*2
For buty=0 To 2
x=gap*2
For butx=0 To 5
butnum=buty*6+butx
ButtonGadget(#but0+butnum, x, y, butw, buth, "Button "+Str(butnum))
x+butw+gap
Next butx
y+buth+gap
Next buty
SetGadgetText(#but0+9, "CLICK ME")
Else
MessageRequester("Error", "Failed to create main window"): End
EndIf
;}
;- Register gadgets for resizing
; Resize/lock codes (case insensitive) for each of the four sides l, r, t, b:
; Unlocked: "0" or "u" or "f" (free, floating), with gadget size unchanged
; Locked : "1" or "l" or "a" (anchored)
; Scaled : "2" or "s" or "p" (proportionate) [The default]
; Special case: "ff" moves a gadget moved proportionally, but with size unchanged
Define button
For button=#but0+0 To #but0+17
Select button
Case #but0+0 To #but0+5
RS_Register(#winMain, button, "afpf") ; Left anchored
Case #but0+6 To #but0+8
RS_Register(#winMain, button, "ffff") ; Move
Case #but0+9 To #but0+13
RS_Register(#winMain, button, "pppp") ; Proportionate [the default]
Case #but0+14
RS_Register(#winMain, button, "ffff") ; Move
Case #but0+15 To #but0+17
RS_Register(#winMain, button, "fafp") ; Right anchored
EndSelect
Next button
RS_Margins(#winMain) ; Omit this line and notice the top & left margins !!!
;}
;- Event loop that detects window resizing (and bug report)
Define event, but9w, but9h
; SmartWindowRefresh(#winMain, #True) ; Try un-commenting this line !!!
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_Gadget
If EventGadget()=#but0+9 ; If button 9
; Resize the entire window so that button 9 becomes square
but9w=GadgetWidth(EventGadget()): but9h=GadgetHeight(EventGadget())
If but9w>but9h: but9w=but9h: Else: but9h=but9w: EndIf ; Gadget will get smaller
RS_GetWinSize(EventWindow(), EventGadget(), @but9w, @but9h)
ResizeWindow(EventWindow(), #PB_Ignore, #PB_Ignore, but9w, but9h)
EndIf
Case #PB_Event_SizeWindow
RS_Resize(#winMain)
Debug EventWindow() ; Bug: This gives -1 the first time (if no status bar is present) and #winMain thereafter
EndSelect
Until Event=#PB_Event_CloseWindow
;}
End