Page 1 of 1
Auto-positioning of gadgets
Posted: Tue May 17, 2022 7:12 pm
by Quin
Hi,
I'm a totally blind PureBasic programmer. PB's IDE, tooling, and gadgets are all at least mostly accessible to a screen reader, and I love it. However, there's one thing that's always gotten me, positioning/resizing gadgets. I don't know what sizes look good. Currently, I just let each gadget fill up an equal part of the screen. Like, if I had 3 buttons, each one would take up WindowHeight(0) and WindowWIdth(0)/3. I imagine this doesn't look pretty, though. Plus, it can get kind of messy and confusing. Is there a way to have PureBasic handle all this kind of stuff for me? As well as handle resizing gadgets when the user resizes the window?
Thanks a ton!
Re: Auto-positioning of gadgets
Posted: Tue May 17, 2022 7:44 pm
by Danilo
Using the Dialog library gives you an automatic layout system.
Simple example:
Code: Select all
;
; http://www.purebasic.fr/english/viewtopic.php?f=13&t=56528
;
CompilerIf #PB_Compiler_Unicode
#XmlEncoding = #PB_UTF8
CompilerElse
#XmlEncoding = #PB_Ascii
CompilerEndIf
#Dialog = 0
#Xml = 0
;#NSRoundedBezelStyle = 1
XML$ = "<?xml version='1.0'?>"+
"<dialogs>"+
" <window id='0' name='Window_Main' text='app' width='455' height='200' flags='#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget'>"+
" <gridbox columns='6'>" +
" <button text='Button 1' />" +
" <button text='Button 2' />" +
" <button text='Button 3' colspan='3' />" +
" <button text='Button 4' />" +
" <button text='Button 5' rowspan='2' />" +
" <button text='Button 6' />" +
" <button text='Button 7' />" +
" <button text='Button 8' />" +
" <button text='Button 9' />" +
" <button text='Button 10' />" +
" <button text='Button 11' />" +
" <button text='Button 12' />" +
" </gridbox>" +
" </window>"+
"</dialogs>"
If CatchXML(#Xml, @XML$, StringByteLength(XML$), 0, #XmlEncoding) And XMLStatus(#Xml) = #PB_XML_Success
If CreateDialog(#Dialog) And OpenXMLDialog(#Dialog, #Xml, "Window_Main")
Repeat
Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
Else
Debug "Dialog error: " + DialogError(#Dialog)
EndIf
Else
Debug "XML error: " + XMLError(#Xml) + " (Line: " + XMLErrorLine(#Xml) + ")"
EndIf
Re: Auto-positioning of gadgets
Posted: Tue May 17, 2022 7:46 pm
by Danilo
More advanced example:
Code: Select all
CompilerIf #PB_Compiler_Unicode
#XmlEncoding = #PB_UTF8
CompilerElse
#XmlEncoding = #PB_Ascii
CompilerEndIf
#Dialog = 0
#Xml = 0
XML$ = "<window id='#PB_Any' name='test' text='test' minwidth='600' minheight='auto' flags='#PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget'>" +
"<vbox expand='item:1'>" +
"" + ; gridbox 1
" <gridbox columns='5' colexpand='item:2' rowexpand='item:1'>" +
"" + ; column 1
"" + ; left side spin gadgets
" <singlebox expand='no'>" +
" <vbox expand='no'>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly' width='60'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" </vbox>" +
" </singlebox>" +
"" + ; column 2-4
" <editor text='content' height='150' colspan='3' rowspan='2'/>" +
"" + ; column 5
"" + ; right side spin gadgets
" <singlebox expand='no'>" +
" <vbox expand='no'>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly' width='60'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" </vbox>" +
" </singlebox>" +
" <singlebox/>" +
" <singlebox/>" +
" </gridbox>" +
"" + ; gridbox 2
" <gridbox columns='3' colexpand='item:2' rowexpand='item:1' align='bottom'>" +
"" + ; left side spin gadgets
" <singlebox expand='no'>" +
" <vbox expand='no'>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly' width='60'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" </vbox>" +
" </singlebox>" +
"" + ; inner/middle gridbox for buttons
" <gridbox columns='5'>" +
" <button text='Btn 1'/>" +
" <button text='Btn 2'/>" +
" <button text='Btn 3'/>" +
" <button text='Btn 4'/>" +
" <button text='Btn 5'/>" +
" <vbox>" +
" <button text='Btn 6'/>" +
" <singlebox/>" +
" <button text='Btn 7'/>" +
" <singlebox/>" +
" </vbox>" +
" <singlebox expand='no' colspan='3' align='center'>" +
" <vbox expand='yes'>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly' width='60'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" <checkbox text='Checkbox 1'/>" +
" <checkbox text='Checkbox 2'/>" +
" </vbox>" +
" </singlebox>" +
" <vbox>" +
" <button text='Btn 8'/>" +
" <singlebox/>" +
" <button text='Btn 9'/>" +
" <singlebox/>" +
" </vbox>" +
" </gridbox>" +
"" + ; right side spin gadgets
" <singlebox expand='no'>" +
" <vbox expand='no'>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly' width='60'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" <spin min='1' max='100' value='1' flags='#PB_Spin_Numeric|#PB_Spin_ReadOnly'/>" +
" </vbox>" +
" </singlebox>" +
" </gridbox>" +
"</vbox>" +
"</window>"
If CatchXML(#Xml, @XML$, StringByteLength(XML$), 0, #XmlEncoding) And XMLStatus(#Xml) = #PB_XML_Success
If CreateDialog(#Dialog) And OpenXMLDialog(#Dialog, #Xml, "test", 0,0,800,600)
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
Else
Debug "Dialog error: " + DialogError(#Dialog)
EndIf
Else
Debug "XML error: " + XMLError(#Xml) + " (Line: " + XMLErrorLine(#Xml) + ")"
EndIf
Re: Auto-positioning of gadgets
Posted: Tue May 17, 2022 9:29 pm
by Quin
Wow, this looks like exactly what I need! Thank you!
Last I heard though, the Dialog library can only do basic controls. You can't do, for example, multi-column listviews. Is this still the case? If so, how do you work around it?
Re: Auto-positioning of gadgets
Posted: Tue May 17, 2022 9:53 pm
by Danilo
You probably need to add a ContainerGadget inside the dialog and put custom controls inside the container by code, right after dialog creation.
Re: Auto-positioning of gadgets
Posted: Wed May 18, 2022 7:10 am
by Danilo
Small example shows how to add a custom gadget/control to an empty Dialog container:
Code: Select all
;
; Use Custom Gadget with Dialog Container
;
; by Danilo, 2022/05/18
;
CompilerIf #PB_Compiler_Unicode
#XmlEncoding = #PB_UTF8
CompilerElse
#XmlEncoding = #PB_Ascii
CompilerEndIf
#Dialog = 0
#Xml = 0
XML$ = "<?xml version='1.0'?>"+
"<dialogs>"+
" <window id='#PB_Any' name='mainWindow' text='Custom Gadgets in Dialogs' width='455' height='200' flags='#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget'>"+
" <gridbox columns='6'>" +
" <button text='Button 1' />" +
" <button text='Button 2' />" +
" <container name='CustomGadget1' colspan='3' height='80'/>" +
" <button text='Button 4' />" +
" <button text='Button 5' rowspan='2' />" +
" <container name='CustomGadget2' rowspan='2' />" +
" <button text='Button 6' />" +
" <button text='Button 7' />" +
" <button text='Button 8' />" +
" <button text='Button 9' />" +
" <button text='Button 10' />" +
" <button text='Button 11' />" +
" <button text='Button 12' />" +
" </gridbox>" +
" </window>"+
"</dialogs>"
;
; Procedures for adding Custom Gadget to Dialog Container
; and control child resizing
;
Procedure __ResizeCustomGadget()
Protected gadget = EventGadget()
Protected child = GetGadgetData(gadget)
ResizeGadget(child,0,0,GadgetWidth(gadget),GadgetHeight(gadget))
EndProcedure
Procedure.i OpenDialogContainer(Dialog.i, ContainerName.s)
Protected Container = DialogGadget(Dialog, ContainerName)
If Container
OpenGadgetList(Container)
EndIf
ProcedureReturn Container
EndProcedure
Procedure CloseDialogContainer(Container, Gadget)
If Container
SetGadgetData(Container, Gadget)
CloseGadgetList()
BindGadgetEvent(Container,@__ResizeCustomGadget(),#PB_EventType_Resize)
ResizeGadget(Gadget,0,0,GadgetWidth(Container),GadgetHeight(Container))
EndIf
EndProcedure
;--------------------------------------------------------------
;
; Minimal Custom Gadget (just as an example)
;
Procedure Draw()
If StartDrawing(CanvasOutput(EventGadget()))
Protected width = OutputWidth(), height = OutputHeight()
Box(0,0,width,height,RGB(7, 136, 184))
FrontColor(RGB($FF,$FF,$FF))
LineXY(0,0, width-1, height-1)
LineXY(0,height-1, width-1, 0)
DrawingMode(#PB_2DDrawing_Outlined)
Box(0,0,width,height)
StopDrawing()
EndIf
EndProcedure
Procedure.i MyCustomGadget()
Protected gadget = CanvasGadget(#PB_Any, 0,0,0,0)
BindGadgetEvent(gadget,@Draw(),#PB_EventType_Resize)
ProcedureReturn gadget
EndProcedure
;
;--------------------------------------------------------------
If CatchXML(#Xml, @XML$, StringByteLength(XML$), 0, #XmlEncoding) And XMLStatus(#Xml) = #PB_XML_Success
If CreateDialog(#Dialog) And OpenXMLDialog(#Dialog, #Xml, "mainWindow")
; add a custom gadget to an empty dialog container
container = OpenDialogContainer(#Dialog,"CustomGadget1")
If container
customGadget = MyCustomGadget()
CloseDialogContainer(container, customGadget)
EndIf
container = OpenDialogContainer(#Dialog,"CustomGadget2")
If container
customGadget = MyCustomGadget()
CloseDialogContainer(container, customGadget)
EndIf
Repeat:Until WaitWindowEvent() = #PB_Event_CloseWindow
Else
Debug "Dialog error: " + DialogError(#Dialog)
EndIf
Else
Debug "XML error: " + XMLError(#Xml) + " (Line: " + XMLErrorLine(#Xml) + ")"
EndIf
Re: Auto-positioning of gadgets
Posted: Wed May 18, 2022 10:09 am
by AZJIO
It's not hard to write calculation rules using
flags like in AutoIt3, but you still have to take the time to set the expected behavior.
Code: Select all
w = WindowWidth(#Window)
h = WindowHeight(#Window)
btnW = (w - 10)/3 -10
btnP1 = 10
btnP1 = btnW + 20
btnP2 = (btnW + 10) *2 + 10
You can define all GUI elements as a percentage and calculate using a scaling factor.
Re: Auto-positioning of gadgets
Posted: Wed May 18, 2022 7:54 pm
by Jagermeister
I use a pad with factor of 8 pixels between gadgets for breathability. Desktop resolution is a factor of 8, so this works out well when maximized.
Code: Select all
; 1. Add button to enumeration
; 2. In window_main() - Define button gadget(s)
; 3. In resize() - Adjust buttonCount to total enumerated buttons
; 4. In resize() - Insert resize line(s) for each button
;ENUMERATIONS
Enumeration windows
#window_main
EndEnumeration
Enumeration gadgets
#button_1
#button_2
#button_3
EndEnumeration
;GLOBALS
Global shutdown.b
Procedure resize()
winW = WindowWidth(#window_main)
winH = WindowHeight(#window_main)
pad = 8
buttonCount = 3
; For example: Three buttons will have a total of four pads - one in the beginning, one between 1 and 2, one between 2 and 3, and one after 3. So four pads (or buttonCount + 1).
buttonW = (winW - (pad * (buttonCount + 1))) / buttonCount
ResizeGadget(#button_1 , pad , pad , buttonW , winH - (pad * 2))
ResizeGadget(#button_2 , GadgetX(#button_1) + GadgetWidth(#button_1) + pad , GadgetY(#button_1) , buttonW , GadgetHeight(#button_1))
ResizeGadget(#button_3 , GadgetX(#button_2) + GadgetWidth(#button_2) + pad , GadgetY(#button_1) , buttonW , GadgetHeight(#button_1))
EndProcedure
Procedure window_main()
winX = 0
winY = 0
winW = 640
winH = 80
title$ = "Test"
flags = #PB_Window_MaximizeGadget |
#PB_Window_MinimizeGadget |
#PB_Window_ScreenCentered |
#PB_Window_SizeGadget |
#PB_Window_SystemMenu
OpenWindow(#window_main , winX , winY , winW , winH , title$ , flags)
ButtonGadget(#button_1 , 0 , 0 , 0 , 0 , "One")
ButtonGadget(#button_2 , 0 , 0 , 0 , 0 , "Two")
ButtonGadget(#button_3 , 0 , 0 , 0 , 0 , "Three")
BindEvent(#PB_Event_SizeWindow , @resize() , #window_main)
EndProcedure
window_main()
resize()
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_CloseWindow
Select EventWindow()
Case #window_main
shutdown = #True
Default
CloseWindow(EventWindow())
EndSelect
Case #PB_Event_Gadget
EndSelect
Until shutdown = #True
End
Re: Auto-positioning of gadgets
Posted: Tue Jul 16, 2024 1:03 pm
by Quin
Been coding in PB for over a quarter of my life and I'm still stuck on this problem. I've tried using various forms of all the solutions suggested here, and admittedly the one I liked the most was DynamicDialogs (a wrapper over the dialogs library). However, I can't use it, because I very often need to supply API flags to my gadgets, which the dialog library still doesn't support. Are there similar options? I did a search, but maybe I had bad keywords
Re: Auto-positioning of gadgets
Posted: Tue Jul 16, 2024 1:29 pm
by infratec
Have you ever tried this:
https://www.purebasic.fr/english/viewtopic.php?t=70386
It is good enough that Fred pinned it.

Re: Auto-positioning of gadgets
Posted: Tue Jul 16, 2024 1:42 pm
by Quin
Infratec,
I had not tried it, thanks for the suggestion. However, as I feared, it's not accessible

why so many unlabeled ButtonImageGadgets?
Time to go submit some feedback
Does it require drag-and-drop? If so, I'm afraid that'll be another issue.