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... |