wx layout
BlitzMax Forums/Brucey's Modules/wx layout
| ||
I am trying to set a canvas up so it is some fixed distance from the top-left corner, and so it goes down to the bottom of the screen, and to the right with some fixed spacing left. How do I handle the layouts in wxWidgets?:SuperStrict
Framework wx.wxApp
Import wx.wxFrame
Import wx.wxGLCanvas
Import wx.wxPanel
Const TOOLBARHEIGHT:Int=48
New MyApp.Run()
Type MyApp Extends wxAppMain
Field frame:MyFrame
Global shouldExit:Int=False
Method OnInit:Int()
frame=MyFrame(New MyFrame.Create(,,"test app",0,0,1024,768))
frame.show()
frame.Connect(, wxEVT_CLOSE,onQuit)
Return True
End Method
Method MainLoop:Int()
Repeat
While Not Pending() And ProcessIdle()
Wend
While Pending()
If Not Dispatch() Then
shouldExit = True
Exit
EndIf
Wend
If shouldExit Then
While pending()
dispatch()
Wend
Return 0
EndIf
Flip(0)
Forever
EndMethod
Function OnQuit(event:wxEvent)
MyApp.shouldExit=True
EndFunction
End Type
Type MyFrame Extends wxFrame
Field panel:MyPanel
Method OnInit()
panel=MyPanel(New MyPanel.Create(Self))
'Menu
Local fileMenu:wxMenu = wxMenu.CreateMenu()
' the "About" item should be in the help menu
Local helpMenu:wxMenu = wxMenu.CreateMenu()
helpMenu.Append(wxID_ABOUT, "&About...~tF1", "Show about dialog")
fileMenu.Append(wxID_EXIT, "&Quit~tAlt-Q", "Quit this program")
' now append the freshly created menu to the menu bar...
Local menuBar:wxMenuBar = wxMenuBar.CreateMenuBar()
menuBar.Append(fileMenu, "&File")
menuBar.Append(helpMenu, "&Help")
SetMenuBar(menuBar)
' create a status bar just for fun
CreateStatusBar(2)
SetStatusText("Welcome to wxWidgets!")
'Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, OnQuit)
'Connect(wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, OnAbout)
EndMethod
'Method OnQuit(event:wxEvent)
' End
'EndMethod
EndType
Type MyPanel Extends wxPanel
Field canvas:MyCanvas
Method onInit()
canvas = MyCanvas(New MyCanvas.Create(Self, -1, GRAPHICS_BACKBUFFER|GRAPHICS_DEPTHBUFFER,0,TOOLBARHEIGHT,400,300))
EndMethod
EndType
Type MyCanvas Extends wxGLCanvas
Method OnInit()
'connectany(wxEVT_SIZE, OnSize)
EndMethod
Method Draw()
SetGraphics CanvasGraphics(Self)
EndMethod
Method OnPaint(event:wxPaintEvent)
Draw()
Flip(0)
EndMethod
EndType |
| ||
| wxWidgets was built to be used with sizers. You can use wxFormBuilder (with or without wxCodeGen) to set up the layout you want and then check out the resulting sizer code. You'd probably need to use a spacer for the fixed margin. The benefit of this approach is it is cross platform and, if done right, should auto resize based on the parent window. I personally find wxSizers and wxFormBuilder hard going/frustrating for complicated GUI layouts and so most of my layouts are hard coded via an xml "skin" file. I can't say I'd recommend this approach though as it is time consuming and fiddly without an editor to create the xml for you. You also have to resize manually. So I guess I'm advising you wrestle wxFormBuilder into submission. I just had a crack and came up with this as the C code - the panel is a substitute as I didn't see a canvas icon. I'm sure those who actually use this tool could provide a much better example for you. wxBoxSizer* bSizer1; bSizer1 = new wxBoxSizer( wxHORIZONTAL ); bSizer1->Add( 32, 1, 0, 0, 5 ); m_panel1 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); bSizer1->Add( m_panel1, 1, wxEXPAND | wxALL, 5 ); this->SetSizer( bSizer1 ); this->Layout(); |
| ||
| Isn't there some kind of onsize callback you can use? |
| ||
Sure. You'd connect up the parent frame most likely. Loosely...Parent Frame's OnInit() Connect(GetId(),wxEVT_SIZE, _OnSize) Function _OnSize(_event:wxEvent) TYourType(_event.parent).OnSize(wxSizeEvent(_event)) End Function Method OnSize(_event:wxSizeEvent) 'resize the child panel and canvas code here End Method But with sizers you don't have to do that. |
| ||
I am trying some sizer code, but it doesn't have any effect:SuperStrict
Framework wx.wxApp
Import wx.wxFrame
Import wx.wxGLCanvas
Import wx.wxPanel
Const TOOLBARHEIGHT:Int=48
New MyApp.Run()
Type MyApp Extends wxAppMain
Field frame:MyFrame
Global shouldExit:Int=False
Method OnInit:Int()
frame=MyFrame(New MyFrame.Create(,,"test app",0,0,1024,768))
frame.show()
frame.Connect(, wxEVT_CLOSE,onQuit)
Return True
End Method
Method MainLoop:Int()
Repeat
While Not Pending() And ProcessIdle()
Wend
While Pending()
If Not Dispatch() Then
shouldExit = True
Exit
EndIf
Wend
If shouldExit Then
While pending()
dispatch()
Wend
Return 0
EndIf
Flip(0)
Forever
EndMethod
Function OnQuit(event:wxEvent)
MyApp.shouldExit=True
EndFunction
End Type
Type MyFrame Extends wxFrame
Field panel:MyPanel
Method OnInit()
panel=MyPanel(New MyPanel.Create(Self))
'Menu
Local fileMenu:wxMenu = wxMenu.CreateMenu()
' the "About" item should be in the help menu
Local helpMenu:wxMenu = wxMenu.CreateMenu()
helpMenu.Append(wxID_ABOUT, "&About...~tF1", "Show about dialog")
fileMenu.Append(wxID_EXIT, "&Quit~tAlt-Q", "Quit this program")
' now append the freshly created menu to the menu bar...
Local menuBar:wxMenuBar = wxMenuBar.CreateMenuBar()
menuBar.Append(fileMenu, "&File")
menuBar.Append(helpMenu, "&Help")
SetMenuBar(menuBar)
' create a status bar just for fun
CreateStatusBar(2)
SetStatusText("Welcome to wxWidgets!")
'Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, OnQuit)
'Connect(wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, OnAbout)
EndMethod
'Method OnQuit(event:wxEvent)
' End
'EndMethod
EndType
Type MyPanel Extends wxPanel
Field canvas:MyCanvas
Field topsizer:wxBoxSizer
Method onInit()
canvas = MyCanvas(New MyCanvas.Create(Self,-1,GRAPHICS_BACKBUFFER|GRAPHICS_DEPTHBUFFER,50,TOOLBARHEIGHT,400,300))
topsizer=New wxBoxSizer.Create(wxVERTICAL)
topsizer.Add(Self,1,wxEXPAND|wxALL,10)
Local button_sizer:wxBoxSizer = New wxBoxSizer.Create( wxHORIZONTAL )
button_sizer.Add(Self,0,wxALL,10)
EndMethod
Method OnSize()
EndMethod
EndType
Type MyCanvas Extends wxGLCanvas
Method OnInit()
EndMethod
Method Draw()
SetGraphics CanvasGraphics(Self)
EndMethod
Method OnPaint(event:wxPaintEvent)
Draw()
Flip(0)
EndMethod
EndType |
| ||
I also tried the callback approach. How do I get the client area of the window?:SuperStrict
Framework wx.wxApp
Import wx.wxFrame
Import wx.wxGLCanvas
Import wx.wxPanel
Const TOOLBARHEIGHT:Int=48
Const SIDEPANELWIDTH:Int=200
New MyApp.Run()
Type MyApp Extends wxAppMain
Field frame:MyFrame
Global shouldExit:Int=False
Method OnInit:Int()
frame=MyFrame(New MyFrame.Create(,,"test app",0,0,1024,768))
frame.show()
frame.Connect(, wxEVT_CLOSE,onQuit)
Return True
End Method
Method MainLoop:Int()
Repeat
While Not Pending() And ProcessIdle()
Wend
While Pending()
If Not Dispatch() Then
shouldExit = True
Exit
EndIf
Wend
If shouldExit Then
While pending()
dispatch()
Wend
Return 0
EndIf
Flip(0)
Forever
EndMethod
Function OnQuit(event:wxEvent)
MyApp.shouldExit=True
EndFunction
End Type
Type MyFrame Extends wxFrame
Field panel:MyPanel
Method OnInit()
panel=MyPanel(New MyPanel.Create(Self))
'Menu
Local fileMenu:wxMenu = wxMenu.CreateMenu()
' the "About" item should be in the help menu
Local helpMenu:wxMenu = wxMenu.CreateMenu()
helpMenu.Append(wxID_ABOUT, "&About...~tF1", "Show about dialog")
fileMenu.Append(wxID_EXIT, "&Quit~tAlt-Q", "Quit this program")
' now append the freshly created menu to the menu bar...
Local menuBar:wxMenuBar = wxMenuBar.CreateMenuBar()
menuBar.Append(fileMenu, "&File")
menuBar.Append(helpMenu, "&Help")
SetMenuBar(menuBar)
' create a status bar just for fun
CreateStatusBar(2)
SetStatusText("Welcome to wxWidgets!")
Connect(GetId(),wxEVT_SIZE, _OnSize)
EndMethod
Function _OnSize(_event:wxEvent)
MyFrame(_event.parent).OnSize(wxSizeEvent(_event))
EndFunction
Method OnSize(_event:wxSizeEvent)
Local w:Int,h:Int
_event.getsize(w,h)
panel.setsize(w-SIDEPANELWIDTH,h)
panel.canvas.setsize(w-SIDEPANELWIDTH,h)
EndMethod
EndType
Type MyPanel Extends wxPanel
Field canvas:MyCanvas
Field topsizer:wxBoxSizer
Method onInit()
canvas = MyCanvas(New MyCanvas.Create(Self,-1,GRAPHICS_BACKBUFFER|GRAPHICS_DEPTHBUFFER,0,0,640,480))
'topsizer=New wxBoxSizer.Create(wxVERTICAL)
'topsizer.Add(Self,1,wxEXPAND|wxALL,10)
'Local button_sizer:wxBoxSizer = New wxBoxSizer.Create( wxHORIZONTAL )
'button_sizer.Add(Self,0,wxALL,10)
EndMethod
Method OnSize()
EndMethod
EndType
Type MyCanvas Extends wxGLCanvas
Method OnInit()
EndMethod
Method Draw()
SetGraphics CanvasGraphics(Self)
EndMethod
Method OnPaint(event:wxPaintEvent)
Draw()
Flip(0)
EndMethod
EndType |
| ||
Method GetClientSize(width:Int Var, height:Int Var) Description This gets the size of the window 'client area' in pixels. Information The client area is the area which may be drawn on by the programmer, excluding title bar, border, scrollbars, etc. |
| ||
Here I am using the size event, but it flickers badly and is obviously wrong. I need to learn how to use sizers, but I can't find a good example anywhere:SuperStrict
Framework wx.wxApp
Import wx.wxFrame
Import wx.wxGLCanvas
Import wx.wxPanel
Import wx.wxToolBar
Import wx.wxButton
Import wx.wxPropGrid
Import wx.wxFrame
Import wx.wxPropGrid
Import wx.wxTextCtrl
Import wx.wxRadioBox
Import wx.wxSystemSettings
Import wx.wxArtProvider
Import wx.wxDatePickerCtrl
Import wx.wxButton
Const TOOLBARHEIGHT:Int=48
Const SIDEPANELWIDTH:Int=200
New MyApp.Run()
Type MyApp Extends wxAppMain
Field frame:MyFrame
Global shouldExit:Int=False
Method OnInit:Int()
frame=MyFrame(New MyFrame.Create(,,"test app",0,0,1024,768))
frame.show()
frame.Connect(, wxEVT_CLOSE,onQuit)
Return True
End Method
Method MainLoop:Int()
Repeat
While Not Pending() And ProcessIdle()
Wend
While Pending()
If Not Dispatch() Then
shouldExit = True
Exit
EndIf
Wend
If shouldExit Then
While pending()
dispatch()
Wend
Return 0
EndIf
Flip(0)
Forever
EndMethod
Function OnQuit(event:wxEvent)
MyApp.shouldExit=True
EndFunction
End Type
Type MyFrame Extends wxFrame
Field canvas:MyCanvas
Field panel:wxPanel
Field sidepanel:wxPanel
Field toolbar:wxToolBar
Field pg:wxPropertyGrid
Method OnInit()
panel=New wxPanel.Create(Self)
canvas=MyCanvas(New MyCanvas.Create(panel,-1,GRAPHICS_BACKBUFFER|GRAPHICS_DEPTHBUFFER,0,0,640,480))
sidepanel=New wxPanel.Create(Self)
' New wxButton.Create( sidepanel,0,"My Button",0,0,100,60 )
Local style:Int=wxPG_BOLD_MODIFIED | wxPG_SPLITTER_AUTO_CENTER | ..
wxPG_AUTO_SORT ..
| wxPG_SPLITTER_AUTO_CENTER ..
| wxPG_DEFAULT_STYLE', ..
' wxPG_EX_HELP_AS_TOOLTIPS | wxPG_EX_NATIVE_DOUBLE_BUFFERING
pg = New wxPropertyGrid.Create(sidepanel, 1, 0, 0, 180, 400, style )
pg.Append( New wxPropertyCategory.Create("Appearance", wxPG_LABEL) )
pg.Append( New wxStringProperty.Create("Label", wxPG_LABEL, GetTitle()) )
pg.Append( New wxFontProperty.Create("Font", wxPG_LABEL) )
pg.SetPropertyHelpString("Font", "Editing this will change font used in the property grid.")
pg.Append( New wxSystemColourProperty.Create("Margin Colour", wxPG_LABEL, pg.GetGrid().GetMarginColour()) )
pg.Append( New wxSystemColourProperty.Create("Cell Colour", wxPG_LABEL, pg.GetGrid().GetCellBackgroundColour()) )
pg.Append( New wxSystemColourProperty.Create("Cell Text Colour", wxPG_LABEL, pg.GetGrid().GetCellTextColour()) )
pg.Append( New wxSystemColourProperty.Create("Line Colour", wxPG_LABEL, pg.GetGrid().GetLineColour()) )
' Add bool property
pg.Append( New wxBoolProperty.Create( "BoolProperty", wxPG_LABEL, False ) )
' Add bool property with check box
pg.Append( New wxBoolProperty.Create( "BoolProperty with CheckBox", wxPG_LABEL, False ) )
pg.SetPropertyAttribute( "BoolProperty with CheckBox", wxPG_BOOL_USE_CHECKBOX, 1 )
'parent:wxWindow, id:Int, label:Object = Null, x:Int = -1, y:Int = -1, w:Int = -1, h:Int = -1, style:Int = 0)
pg.setposition(0,0)
'sidepanel.getClientsize(w,h)
'pg.setsize(w,h)
pg.setposition(0,0)
pg.setsize(200,400)
Local topSizer:wxBoxSizer = New wxBoxSizer.Create( wxVERTICAL )
topSizer.Add( pg, 0, wxEXPAND )
Local fileMenu:wxMenu=wxMenu.CreateMenu()
Local helpMenu:wxMenu=wxMenu.CreateMenu()
helpMenu.Append(wxID_ABOUT, "&About...~tF1", "Show about dialog")
fileMenu.Append(wxID_EXIT, "&Quit~tAlt-Q", "Quit this program")
' now append the freshly created menu to the menu bar...
Local menuBar:wxMenuBar = wxMenuBar.CreateMenuBar()
menuBar.Append(fileMenu, "&File")
menuBar.Append(helpMenu, "&Help")
SetMenuBar(menuBar)
' create a status bar just for fun
CreateStatusBar(2)
SetStatusText("Welcome to wxWidgets!")
Connect(GetId(),wxEVT_SIZE, _OnSize)
AdjustSize()
EndMethod
Function _OnSize(_event:wxEvent)
MyFrame(_event.parent).OnSize(wxSizeEvent(_event))
EndFunction
Method OnSize(_event:wxSizeEvent)
Local w:Int,h:Int
_event.getsize(w,h)
AdjustSize()
EndMethod
Method AdjustSize()
Local w:Int,h:Int
getclientsize(w,h)
panel.setsize(w-SIDEPANELWIDTH,h)
If canvas canvas.setsize(w-SIDEPANELWIDTH,h)
sidepanel.setposition(w-SIDEPANELWIDTH,0)
sidepanel.setsize(SIDEPANELWIDTH,h)
pg.setposition(0,0)
pg.setsize(SIDEPANELWIDTH,400)
EndMethod
EndType
Type TSidePanel Extends wxPanel
Method onInit()
EndMethod
Method OnSize()
EndMethod
EndType
Type MyCanvas Extends wxGLCanvas
Method OnInit()
EndMethod
Method Draw()
SetGraphics CanvasGraphics(Self)
EndMethod
Method OnPaint(event:wxPaintEvent)
Draw()
Flip(0)
EndMethod
EndType |
| ||
| I would really recommend the sizer approach if you can get your head around it. Some widgets won't display correctly without them (wxFlatNotebook comes to mind). I feel ignoring sizers and wxFormBuilder was the single biggest mistake I made in my current project. Here's a demo similar (I think!) to what you are after, except I have a second panel where you want a wxGLCanvas. SizeMain.bmx And the codegen generated SizerTest.bmx Here is the source project zipped for reference. You will find wxCodeGen in the Tools folder of wx.Mod. wxFormBuilder is here [edit: Ah right, didn't see your post immediately above re prop grid, but you get the idea I hope] |
| ||
Can you post an example that aligns a panel on a frame, with a constant for the edge border for left, right, top and bottom, so that when you edit the constant, the panel edges will be that distance from the edge of the frame? Here is my example that will set the left and right sides to any border distance:' BlitzMax code generated with wxCodeGen v1.16 : 24 May 2009 09:34:57 ' ' ' PLEASE DO "NOT" EDIT THIS FILE! ' SuperStrict Import wx.wxFrame Import wx.wxPanel Import wx.wxStatusBar Import wx.wxSystemSettings Import wx.wxWindow Type MyFrame1Base Extends wxFrame Field m_panel1:wxPanel Field PretendCanvas:wxPanel Field m_statusBar1:wxStatusBar Method Create:MyFrame1Base(parent:wxWindow = Null,id:Int = wxID_ANY, title:String = "", x:Int = -1, y:Int = -1, w:Int = 770, h:Int = 698, style:Int = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL) Return MyFrame1Base(Super.Create(parent, id, title, x, y, w, h, style)) End Method Method OnInit() Const frameborder:Int=10 Const panelleftborder:Int=10 Const panelrightborder:Int=64 Const paneltopborder:Int=64 Const panelbottomborder:Int=100 Local FrameBoxSizer:wxBoxSizer FrameBoxSizer = New wxBoxSizer.Create(wxVERTICAL) m_panel1 = New wxPanel.Create(Self, wxID_ANY,,,,, wxTAB_TRAVERSAL) PretendCanvas = New wxPanel.Create(m_panel1, wxID_ANY,,,,, wxTAB_TRAVERSAL) PretendCanvas.SetBackgroundColour(wxSystemSettings.GetColour(wxSYS_COLOUR_GRAYTEXT)) Local PanelBoxSizer:wxBoxSizer PanelBoxSizer = New wxBoxSizer.Create(wxHORIZONTAL) PanelBoxSizer.AddCustomSpacer(panelleftborder, 0, 0, wxEXPAND, 5) PanelBoxSizer.Add(PretendCanvas, 1, wxRIGHT|wxLEFT|wxEXPAND, 0) PanelBoxSizer.AddCustomSpacer(panelrightborder, 0, 0, wxEXPAND, 5) m_panel1.SetSizer(PanelBoxSizer) m_panel1.Layout() PanelBoxSizer.Fit(m_panel1) FrameBoxSizer.Add(m_panel1, 1, wxEXPAND | wxALL, frameborder) m_statusBar1 = Self.CreateStatusBar(1, 0|wxST_SIZEGRIP, wxID_ANY) SetSizer(FrameBoxSizer) Layout() End Method End Type |
| ||
| How's that? |
| ||
| Here is the working code: |
| ||
Just change the 4 custom spacer values as desired? Seems to work here.bSizer3.AddCustomSpacer(0, 64, 0, wxEXPAND, 5) bSizer4.AddCustomSpacer(10, 0, 0, wxEXPAND, 5) bSizer4.AddCustomSpacer(64, 0, 0, wxEXPAND, 5) bSizer3.AddCustomSpacer(0, 100, 0, wxEXPAND, 5) |
| ||
| You have to get rid of those border values of 5. Thanks for your help. |
| ||
| Ah right, I was just using the border defaults :-) Seriously, spend 2 hours with wxFormBuilder. The first hour will leave you a nervous wreck. But by the second things start to click - a little anyway! |
| ||
| I got two panels now, but how can I make them so the one on the right stays fixed to the edge?: |
| ||
| Pass a zero to the proportion argument of Add when you add one. That will prevent it from being resized, just make sure you pass a minimum size to the panel/button/window when creating it. |
| ||
| Yes, that works! I am seriously concerned that it is such an ordeal in this library just to position two controls next to each other. I can make the side panel a fixed width, but when I add buttons to the panel, the panel gets resized. How can I avoid this?: |
| ||
| Can't say I've ever needed to use a custom spacer for any of my apps. Setting the border size is usually enough. Mind you, I always use a GUI editor, which means I generally never code the UI part of the app anyway. But, I certainly wouldn't call it an ordeal :-) Saying that... I've found myself using spacers in Flex - but maybe that's because I don't know it well enough to not use them yet, to get the desired layout. |
| ||
| The problem with those GUI editors is my application interface has a lot of different panels that get hidden and shown depending on what the user is doing. I don't know if it would even be possible to make all those layers of controls in a GUI editor. |
| ||
| So make sure you code it all correctly? Not like you have to cram every single control into the same document. Load other controls as needed at runtime, keep it fairly modular such that you can add/remove components of the UI as needed. |
| ||
| keep it fairly modular That's the way :-) |
| ||
| I really advise that you use wxformbuilder. hiding and showing gui controls is possible in that editor too, so you can make all the controls you need in one form and hide/show them in your app. The sizers dictate how to build your form, yes, but once you 'get' how they work, it all falls into place. And the export from Brucey's converter is ace. I only found 2 bugs with it (which Brucey fixed pretty quickly) I used the form editor and its export for my particle editor currently in development... |