[Windows, Solved] Changing StatusBar field width

Just starting out? Need help? Post your questions and find answers here.
User avatar
Michael Vogel
Addict
Addict
Posts: 2860
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

[Windows, Solved] Changing StatusBar field width

Post by Michael Vogel »

I'd like to change the width of some statusbar fields but wondering why a progressbar won't be affected (or effected?) automatically.

The following example shows that text fields are updated as wanted (top button) but when changing the field to a progress bar (press bottom button once) changing the width seems to be ignored.

Code: Select all

; Define

	#PB_DpiBits=		16
	#PB_DpiScale=		1<<#PB_DpiBits

	Global DpiScale=GetDeviceCaps_(GetDC_(0),#LOGPIXELSX)<<#PB_DpiBits/96;	12 Bit (statt Fließkommaberechnung)

	Macro ScaleUp(value)
		(((value)*DpiScale)/#PB_DpiScale)
	EndMacro
	Macro ScaleDown(value)
		(((value)<<#PB_DpiBits)/DpiScale)
	EndMacro

; EndDefine
Procedure StatusBarFieldWidth(StatusBar,Field.i,Width.l)

	If IsStatusBar(StatusBar)

		Protected StatusBarID=StatusBarID(StatusBar)
		Protected nParts,lResult
		Protected lLeftField.l
		Protected *dwFields

		nParts=SendMessage_(StatusBarID,#SB_GETPARTS,0,0)
		*dwFields=AllocateMemory(nParts*4)

		If *dwFields
			SendMessage_(StatusBarID,#SB_GETPARTS,nParts,*dwFields)
			If Field>0
				lLeftField=PeekL(*dwFields+(Field-1)*4)
			EndIf
			PokeL(*dwFields+Field*4,lLeftField+ScaleUp(Width))
			lResult=SendMessage_(StatusBarID, #SB_SETPARTS,nParts,*dwFields)
			FreeMemory(*dwFields)
			ProcedureReturn lResult
		EndIf

		ProcedureReturn #False

	EndIf

EndProcedure

OpenWindow(0,0,0,600,400,"Progress Bar", #PB_Window_SystemMenu|#PB_Window_TitleBar|#PB_Window_ScreenCentered)

CreateStatusBar(0, WindowID(0))
AddStatusBarField(ScaleUp(20))
AddStatusBarField(ScaleUp(200))
AddStatusBarField(#PB_Ignore)
StatusBarText(0,0,">")
StatusBarText(0,1,"|---MOVE---|")
StatusBarText(0,2,"<")

ButtonGadget(1,200,120,200,50,"Toggle Width")
ButtonGadget(2,200,200,200,50,"Toggle Text/Progress Bar")

w=20
p=0

Repeat
	lEvent = WaitWindowEvent()

	Select lEvent

	Case #PB_Event_Gadget
		Select EventGadget()
		Case 1
			w=120-w
			StatusBarFieldWidth(0,0,w)
			StatusBarFieldWidth(0,1,220-w)
		Case 2
			p!1
			If p
				StatusBarProgress(0,1,50)
			Else
				StatusBarText(0,1,"|---MOVE---|")
			EndIf
		EndSelect

	Case #PB_Event_CloseWindow
		End

	EndSelect

ForEver

// Edited post title - Marked as solved //
Last edited by Michael Vogel on Thu Feb 19, 2026 8:46 am, edited 1 time in total.
Axolotl
Addict
Addict
Posts: 936
Joined: Wed Dec 31, 2008 3:36 pm

Re: [Windows] Changing StatusBar field width

Post by Axolotl »

I don't have a solution either, just a few thoughts.
I think this is an internal PB issue. The progress bar gadget is (probably) a child of the status bar.... (Classname = "msctls_progress32")
As I see it, the WinAPI only handles text in the status bar....

Maybe you have to send the new width also to the progress-control (client window)?

BTW: Pointer arithmetic is top notch. I prefer simple implementation.
That's why I took the liberty of showing an alternative here with a simple array.

Code: Select all

Procedure AlternateVersionOf_StatusBarFieldWidth(StatusBar,Field.i,Width.l)
	If IsStatusBar(StatusBar)
		Protected StatusBarID=StatusBarID(StatusBar)
		Protected nParts,lResult
		Protected lLeftField.l
		Protected Dim dwFields.l(0)   ; <= MSDN talks about int but this int is 4 bytes on 64-bit as well. 
		nParts=SendMessage_(StatusBarID,#SB_GETPARTS,0,0)
		If nParts > 0 
			Dim dwFields(nParts) 
			SendMessage_(StatusBarID,#SB_GETPARTS,nParts,@dwFields())
			If Field > 0 
			  lLeftField=dwFields(Field-1)
			EndIf
			dwFields(Field) = lLeftField + ScaleUp(Width) 
			lResult=SendMessage_(StatusBarID, #SB_SETPARTS,nParts,@dwFields())
			Dim dwFields(0) 
			ProcedureReturn lResult
		EndIf
		ProcedureReturn #False
	EndIf
EndProcedure
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 5038
Joined: Sun Apr 12, 2009 6:27 am

Re: [Windows] Changing StatusBar field width

Post by RASHAD »

SB_SETPARTS message

Sets the number of parts in a status window and the coordinate of the right edge of each part.
Egypt my love
User avatar
Michael Vogel
Addict
Addict
Posts: 2860
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: [Windows] Changing StatusBar field width

Post by Michael Vogel »

Correct, insn't this done in the procedure StatusBarFieldWidth in the initial post?

Field 0 starts at position 0, the first value in the array is the length of the field 0 (and also the left position of the field 1), the seond value in the array is the total length of field 0 and field 1 (which is the left position of field 2) and so on...

I've a workaround for the progressbar issue, but don't like it (because I can't get rid of the 3d border when coloring the progress bar)...

Code: Select all

; Define

	#PB_DpiBits=		16
	#PB_DpiScale=		1<<#PB_DpiBits

	Global DpiScale=GetDeviceCaps_(GetDC_(0),#LOGPIXELSX)<<#PB_DpiBits/96;	12 Bit (statt Fließkommaberechnung)

	#SB_SETBKCOLOR=					$2001
	#PBM_SETBARCOLOR=				1033
	#PBM_SETBKCOLOR=				8193

	Macro ScaleUp(value)
		(((value)*DpiScale)/#PB_DpiScale)
	EndMacro
	Macro ScaleDown(value)
		(((value)<<#PB_DpiBits)/DpiScale)
	EndMacro

; EndDefine
Procedure StatusBarFieldWidth(StatusBar,Field.i,Width.l)

	If IsStatusBar(StatusBar)

		Protected StatusBarID=StatusBarID(StatusBar)
		Protected nParts,lResult
		Protected lLeftField.l
		Protected *dwFields

		nParts=SendMessage_(StatusBarID,#SB_GETPARTS,0,0)
		*dwFields=AllocateMemory(nParts*4)

		If *dwFields
			SendMessage_(StatusBarID,#SB_GETPARTS,nParts,*dwFields)
			If Field>0
				lLeftField=PeekL(*dwFields+(Field-1)*4)
			EndIf
			PokeL(*dwFields+Field*4,lLeftField+ScaleUp(Width))
			lResult=SendMessage_(StatusBarID,#SB_SETPARTS,nParts,*dwFields)
			FreeMemory(*dwFields)
			ProcedureReturn lResult
		EndIf

		ProcedureReturn #False

	EndIf

EndProcedure
Procedure Main()

	OpenWindow(0,0,0,600,400,"Progress Bar", #PB_Window_SystemMenu|#PB_Window_TitleBar|#PB_Window_ScreenCentered)

	CreateStatusBar(0, WindowID(0))
	AddStatusBarField(ScaleUp(20))
	AddStatusBarField(ScaleUp(200))
	AddStatusBarField(#PB_Ignore)
	StatusBarText(0,0,"> > > > > > > >",#PB_StatusBar_BorderLess)
	StatusBarText(0,1,"|---MOVE---|",#PB_StatusBar_BorderLess)
	StatusBarText(0,2,"< < < < < < < <",#PB_StatusBar_BorderLess)

	ButtonGadget(1,200,100,200,50,"Toggle Width")
	ButtonGadget(2,200,170,200,50,"Toggle Text/Progress Bar")
	ButtonGadget(3,200,240,200,50,"Toggle Mode")

	ProgressBarGadget(0,ScaleUp(20),4,ScaleUp(200),ScaleUp(16),0,100)
	HideGadget(0,1)
	SetGadgetState(0,50)
	SetParent_(GadgetID(0),StatusBarID(0))
	SetWindowTheme_(GadgetID(0),#Null,"")
	SendMessage_(GadgetID(0),#PBM_SETBARCOLOR,0,$0A0AAA)
	SendMessage_(GadgetID(0),#PBM_SETBKCOLOR,0,$F8E7DC)
		
	SetWindowTheme_(StatusBarID(0),#Null,"")
	SendMessage_(StatusBarID(0),#SB_SETBKCOLOR,0,$EFD0BB)

	w=20
	p=0

	Repeat
		lEvent = WaitWindowEvent()

		Select lEvent

		Case #PB_Event_Gadget
			Select EventGadget()
			Case 1
				SetGadgetText(1,"Field is "+StringField("wide.short",Bool(w=20)+1,"."))
				w=120-w
				StatusBarFieldWidth(0,0,w)
				StatusBarFieldWidth(0,1,220-w)
				ResizeGadget(0,ScaleUp(w),#PB_Ignore,ScaleUp(220-w),#PB_Ignore)
			Case 2
				p!1
				SetGadgetText(2,"Field type is "+StringField("text.progress",p+1,"."))
				If workaround
					HideGadget(0,p!1)
					StatusBarText(0,1,StringField(".|---MOVE---|",2-p,"."),#PB_StatusBar_BorderLess)
				Else
					If p
						StatusBarProgress(0,1,50,#PB_StatusBar_BorderLess)
					Else
						StatusBarText(0,1,"|---MOVE---|",#PB_StatusBar_BorderLess)
					EndIf
				EndIf
			Case 3
				workaround!1
				SetGadgetText(3,"Bar mode is "+StringField("standard.workaround",workaround+1,"."))
				HideGadget(0,workaround!1 | p)
				PostEvent(#PB_Event_Gadget,0,2)
				PostEvent(#PB_Event_Gadget,0,2)
			EndSelect

		Case #PB_Event_CloseWindow
			End

		EndSelect

	ForEver

EndProcedure
Main()
User avatar
chi
Addict
Addict
Posts: 1092
Joined: Sat May 05, 2007 5:31 pm
Location: Austria

Re: [Windows] Changing StatusBar field width

Post by chi »

Resizing/Repositioning the progressbar is hardcoded in this example, but you get the idea...

Code: Select all

; Define

	#PB_DpiBits=		16
	#PB_DpiScale=		1<<#PB_DpiBits

	Global DpiScale=GetDeviceCaps_(GetDC_(0),#LOGPIXELSX)<<#PB_DpiBits/96;	12 Bit (statt Fließkommaberechnung)

	Macro ScaleUp(value)
		(((value)*DpiScale)/#PB_DpiScale)
	EndMacro
	Macro ScaleDown(value)
		(((value)<<#PB_DpiBits)/DpiScale)
	EndMacro

; EndDefine
Procedure StatusBarFieldWidth(StatusBar,Field.i,Width.l)

	If IsStatusBar(StatusBar)

		Protected StatusBarID=StatusBarID(StatusBar)
		Protected nParts,lResult
		Protected lLeftField.l
		Protected *dwFields
		Protected wRect.RECT, cRect.RECT

		nParts=SendMessage_(StatusBarID,#SB_GETPARTS,0,0)
		*dwFields=AllocateMemory(nParts*4)

		If *dwFields
			SendMessage_(StatusBarID,#SB_GETPARTS,nParts,*dwFields)
			If Field>0
				lLeftField=PeekL(*dwFields+(Field-1)*4)
			EndIf
			PokeL(*dwFields+Field*4,lLeftField+ScaleUp(Width))
			lResult=SendMessage_(StatusBarID, #SB_SETPARTS,nParts,*dwFields)
			
			;; get progress handle and set pos/size
			progHwnd = GetWindow_(StatusBarID, #GW_CHILD)
			GetClientRect_(progHwnd, @cRect)
			SetWindowPos_(progHwnd, #HWND_TOP, 220-ScaleUp(Width)+5, 5, ScaleUp(Width)-11, cRect\bottom, #SWP_NOZORDER|#SWP_NOACTIVATE)
			
			FreeMemory(*dwFields)			
			ProcedureReturn lResult
		EndIf		

		ProcedureReturn #False

	EndIf

EndProcedure

OpenWindow(0,0,0,600,400,"Progress Bar", #PB_Window_SystemMenu|#PB_Window_TitleBar|#PB_Window_ScreenCentered)

CreateStatusBar(0, WindowID(0))
AddStatusBarField(ScaleUp(20))
AddStatusBarField(ScaleUp(200))
AddStatusBarField(#PB_Ignore)
StatusBarText(0,0,">")
StatusBarText(0,1,"|---MOVE---|")
StatusBarText(0,2,"<")

ButtonGadget(1,200,120,200,50,"Toggle Width")
ButtonGadget(2,200,200,200,50,"Toggle Text/Progress Bar")

w=20
p=0

Repeat
	lEvent = WaitWindowEvent()

	Select lEvent

	Case #PB_Event_Gadget
		Select EventGadget()
		Case 1
			w=120-w
			StatusBarFieldWidth(0,0,w)
			StatusBarFieldWidth(0,1,220-w)
		Case 2
			p!1
			If p
				StatusBarProgress(0,1,50)
			Else
				StatusBarText(0,1,"|---MOVE---|")
			EndIf
		EndSelect

	Case #PB_Event_CloseWindow
		End

	EndSelect

ForEver
You probably have to use GetWindowRect_ and GetClientRect_ with MapWindowPoints_ (or something like that) to automatically calculate the size/pos, but I don't have time to fiddle around right now :)
Et cetera is my worst enemy
User avatar
Michael Vogel
Addict
Addict
Posts: 2860
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

[Windows, Solved] Changing StatusBar field width

Post by Michael Vogel »

Thanks to all of your responses, now we have three working alternatives to choose from:
- standard Purebasic with a brute force fix
- standard statusbar doing additional resizing
- using a gadget

The following code does show all three methods:

Code: Select all

; Define

	Enumeration
		#BarDefaultStatusbar
		#BarChiStatusbar
		#BarBirdGadget
	EndEnumeration

	#BarMode=#BarDefaultStatusbar
	;#BarMode=#BarChiStatusbar
	;#BarMode=#BarBirdGadget


	#PB_DpiBits=		16
	#PB_DpiScale=		1<<#PB_DpiBits

	Global DpiScale=GetDeviceCaps_(GetDC_(0),#LOGPIXELSX)<<#PB_DpiBits/96;	12 Bit (statt Fließkommaberechnung)

	#SB_SETBKCOLOR=					$2001
	#PBM_SETBARCOLOR=				1033
	#PBM_SETBKCOLOR=				8193

	Macro ScaleUp(value)
		(((value)*DpiScale)/#PB_DpiScale)
	EndMacro
	Macro ScaleDown(value)
		(((value)<<#PB_DpiBits)/DpiScale)
	EndMacro

; EndDefine
Procedure StatusBarFieldWidth(StatusBar,Field.i,Width.l)

	If IsStatusBar(StatusBar)

		Protected StatusBarID=StatusBarID(StatusBar)
		Protected nParts,lResult
		Protected lLeftField.l
		Protected *dwFields

		nParts=SendMessage_(StatusBarID,#SB_GETPARTS,0,0)
		*dwFields=AllocateMemory(nParts*4)

		If *dwFields
			SendMessage_(StatusBarID,#SB_GETPARTS,nParts,*dwFields)
			If Field>0
				lLeftField=PeekL(*dwFields+(Field-1)*4)
			EndIf
			PokeL(*dwFields+Field*4,lLeftField+ScaleUp(Width))
			lResult=SendMessage_(StatusBarID,#SB_SETPARTS,nParts,*dwFields)

			; Resize progress bar (by Chi)
			If #BarMode=#BarChiStatusbar And Field<2
				Protected ProgressId=GetWindow_(StatusBarID,#GW_CHILD)
				Protected Rect.Rect
				Protected PosLeft,PosRight

				PosLeft=PeekL(*dwFields)
				PosRight=PeekL(*dwFields+4)
				GetClientRect_(ProgressId,@Rect)
				SetWindowPos_(ProgressId,#HWND_TOP,PosLeft+5,5,PosRight-PosLeft-11,Rect\bottom,#SWP_NOZORDER|#SWP_NOACTIVATE)
			EndIf

			FreeMemory(*dwFields)
			ProcedureReturn lResult
		EndIf

		ProcedureReturn #False

	EndIf

EndProcedure
Procedure Main()
	
	LoadFont(0,"Segoe UI",14,#PB_Font_Bold)
	OpenWindow(0,0,0,600,400,"Progress Bar", #PB_Window_SystemMenu|#PB_Window_TitleBar|#PB_Window_ScreenCentered)

	CreateStatusBar(0, WindowID(0))
	AddStatusBarField(ScaleUp(20))
	AddStatusBarField(ScaleUp(200))
	AddStatusBarField(#PB_Ignore)
	StatusBarText(0,0,"> > > > > > > >",#PB_StatusBar_BorderLess)
	StatusBarText(0,1,"|---MOVE---|",#PB_StatusBar_BorderLess)
	StatusBarText(0,2,"< < < < < < < <",#PB_StatusBar_BorderLess)

	TextGadget(99,50,30,500,30,StringField("Default Purebasic StatusbarProgress.Statusbar Progress fix by Chi.Progress Gadget Workaround",#BarMode+1,"."),#PB_Text_Center)
	TextGadget(88,50,70,500,30,StringField("Resizing of the progressbar is forced by swapping between text and bar.Resizing is done by SetWindowPos in the StatusBarFieldWidth procedure.The progressbar gadget linked to the statusbar needs gets resized",#BarMode+1,"."),#PB_Text_Center)
	TextGadget(77,50,300,500,50,"The progressbar handling can be changed by setting the #BarMode constant in the program.",#PB_Text_Center)
	SetGadgetColor(77,#PB_Gadget_FrontColor,#Gray)
	
	ButtonGadget(1,200,140,200,50,"Toggle Field Width")
	ButtonGadget(2,200,200,200,50,"Toggle Text/Progress Bar")
	
	SetGadgetFont(99,FontID(0))
	
	If #BarMode=#BarBirdGadget
		ProgressBarGadget(0,ScaleUp(20)+5,4,ScaleUp(200)-11,ScaleUp(16),0,100)
		HideGadget(0,1)

		SetGadgetState(0,50)
		SetParent_(GadgetID(0),StatusBarID(0))
		SetWindowTheme_(GadgetID(0),#Null,"")
		SendMessage_(GadgetID(0),#PBM_SETBARCOLOR,0,$0A0AAA)
		SendMessage_(GadgetID(0),#PBM_SETBKCOLOR,0,$F8E7DC)

		SetWindowTheme_(StatusBarID(0),#Null,"")
		SendMessage_(StatusBarID(0),#SB_SETBKCOLOR,0,$EFD0BB)
		SetWindowLongPtr_(GadgetID(0),#GWL_EXSTYLE,GetWindowLongPtr_(GadgetID(0),#GWL_EXSTYLE)&~#WS_EX_STATICEDGE)
	EndIf

	w=20
	p=0

	Repeat
		lEvent = WaitWindowEvent()

		Select lEvent

		Case #PB_Event_Gadget
			Select EventGadget()

			Case 1
				SetGadgetText(1,"Field is "+StringField("wide.short",Bool(w=20)+1,"."))
				w=120-w
				StatusBarFieldWidth(0,0,w)
				StatusBarFieldWidth(0,1,220-w)
				Select #BarMode
				Case #BarDefaultStatusbar
					If p
						StatusBarText(0,1,"Brute force fix")
						StatusBarProgress(0,1,50)
					EndIf
				Case #BarBirdGadget
					ResizeGadget(0,ScaleUp(w)+5,#PB_Ignore,ScaleUp(220-w)-11,#PB_Ignore)
				EndSelect

			Case 2
				p!1
				SetGadgetText(2,"Field type is "+StringField("text.progress",p+1,"."))
				If #BarMode=#BarBirdGadget
					HideGadget(0,p!1)
					StatusBarText(0,1,StringField(".|---MOVE---|",2-p,"."),#PB_StatusBar_BorderLess)
				Else
					If p
						StatusBarProgress(0,1,50,#PB_StatusBar_BorderLess)
					Else
						StatusBarText(0,1,"|---MOVE---|",#PB_StatusBar_BorderLess)
					EndIf
				EndIf
			EndSelect

		Case #PB_Event_CloseWindow
			End

		EndSelect

	ForEver

EndProcedure
Main()
Post Reply