Auto-positioning of gadgets

Just starting out? Need help? Post your questions and find answers here.
Quin
Enthusiast
Enthusiast
Posts: 282
Joined: Thu Mar 31, 2022 7:03 pm
Location: United States
Contact:

Auto-positioning of gadgets

Post 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!
PB v5.40/6.10, Windows 10 64-bit.
16-core AMD Ryzen 9 5950X, 128 GB DDR5.
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: Auto-positioning of gadgets

Post 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
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: Auto-positioning of gadgets

Post 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
Quin
Enthusiast
Enthusiast
Posts: 282
Joined: Thu Mar 31, 2022 7:03 pm
Location: United States
Contact:

Re: Auto-positioning of gadgets

Post 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?
PB v5.40/6.10, Windows 10 64-bit.
16-core AMD Ryzen 9 5950X, 128 GB DDR5.
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: Auto-positioning of gadgets

Post 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.
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: Auto-positioning of gadgets

Post 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
AZJIO
Addict
Addict
Posts: 1318
Joined: Sun May 14, 2017 1:48 am

Re: Auto-positioning of gadgets

Post 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.
Jagermeister
Enthusiast
Enthusiast
Posts: 136
Joined: Thu Nov 15, 2012 11:38 pm
Location: Los Angeles

Re: Auto-positioning of gadgets

Post 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
Post Reply