Sharing objects between notebook tabs. Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Hubble
In need of some credit
In need of some credit
Posts: 6
Joined: Sun Jan 13, 2019 5:52 pm

Sharing objects between notebook tabs.

Post by Hubble » Sun Jan 13, 2019 6:59 pm

I'm creating an IDE program that allows the user to write, compile and debug code. There are two modes - edit mode and debug mode - which can can selected using tabs from the wxNotebook class. There are features that both modes share, like the compile button.
I thought that one way to implement this is to duplicate the features for both panels. This is plausible for things like buttons. But for things like a text editor, this is not the best solution. The program would have to copy the buffers when switching modes.

What would be the best way share resources between panels? When I say "share resources" I mean instantiate one object that both panels use. I know it's possible to switch parents but what about sizers?
An example of what I'm looking for is to able to use the same text editor in both modes and be able to resize and move that text editor when switching between modes(using sizers).

Bellow is a code snippet of the class constructor creating all the wxWidget objects.

Code: Select all

	wxSize window_size = UIPanel::panel() -> GetSize(); // UIPanel is a wrapper class for wxPanel.
	uint32_t notebook_id = 1000;
	
	// creation and window relationships.
	wxNotebook * notebook = new wxNotebook(UIPanel::panel() , notebook_id , wxDefaultPosition , wxSize(window_size.GetWidth() , window_size.GetWidth()) , 0 , "");
	wxPanel * m_edit_mode_panel = new wxPanel(notebook , wxID_ANY , wxDefaultPosition , wxDefaultSize , 0 , "");
	wxPanel * m_debug_mode_panel = new wxPanel(notebook , wxID_ANY , wxDefaultPosition , wxDefaultSize , 0 , "");
	
	m_text_editor = new TextEditor(m_edit_mode_panel); // Wrapper class for wxStyledTextCtrl.
	
	notebook -> AddPage(m_edit_mode_panel , "edit");
	notebook -> AddPage(m_debug_mode_panel , "debug");
	
	// button creation and binding.
	wxButton * compile_button = new wxButton(m_edit_mode_panel , COMPILE_ID , "Compile" , wxDefaultPosition); // default mode is edit mode.
	wxButton * run_button = new wxButton(m_edit_mode_panel , RUN_ID , "Run" , wxDefaultPosition);
	
	// I have removed binding.
	
	// sizing
	wxBoxSizer * button_sizer = new wxBoxSizer(wxHORIZONTAL);
	button_sizer -> Add(compile_button);
	button_sizer -> Add(run_button);
	
	wxBoxSizer * edit_mode_sizer = new wxBoxSizer(wxHORIZONTAL);
	edit_mode_sizer -> Add(m_text_editor , wxALIGN_RIGHT);
	edit_mode_sizer -> Add(button_sizer , wxALIGN_LEFT);
	
	wxBoxSizer * debug_mode_sizer = new wxBoxSizer(wxHORIZONTAL);
	m_debug_mode_panel -> SetSizer(debug_mode_sizer);			// Adding text editor to debug_mode_sizer creates sizing conflicts.
	
	this -> panel() -> GetSizer() -> Add(notebook , 1 , wxEXPAND); // "this" inherits from UIPanel hence "this -> panel() -> ..." .
Thanks in advance.

User avatar
doublemax
Moderator
Moderator
Posts: 13127
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Sharing objects between notebook tabs.

Post by doublemax » Mon Jan 14, 2019 6:13 am

You can detach controls from sizers and insert it into another one, but usually that gets messy in the end.

If you keep using a notebook, you could try to keep the shared controls outside the notebook, on a different level of the GUI.
Or, instead of using different notebook pages, keep everything in one panel and when switching between modes, show/hide the individual controls.
Use the source, Luke!

Hubble
In need of some credit
In need of some credit
Posts: 6
Joined: Sun Jan 13, 2019 5:52 pm

Re: Sharing objects between notebook tabs.

Post by Hubble » Mon Jan 14, 2019 12:58 pm

Keeping everything on one panel is good idea and it works well for my design. I have implemented my application as a state machine that allows me to easily add widgets that hide/show themselves upon changing state.
But is this still possible? The wx documentations states that a panel cannot be added more than once to a notebook, at least that's true for wxBookCtrlBase::AddPage.
The page must have the book control itself as the parent and must not have been added to this control previously.
I'm not sure if this is true for wxNotebook::InsertPage. I'm going to have to test this idea.

I'm not sure how keeping the controls outside the notebook works. I'm no expert, but I assume that the panel must child of the notebook. This is how, if my assumption is correct, the notebook controls the pages. Eg if the notebook changes size, so does the attached panels.

Thanks for the suggestions. I will make some changes and let you know how well it works.

User avatar
doublemax
Moderator
Moderator
Posts: 13127
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Sharing objects between notebook tabs.

Post by doublemax » Mon Jan 14, 2019 1:50 pm

You can't add the same page multiple times as a notebook page. With the show/hide approach, you wouldn't be able to use a wxNoteBook. You'd need another way of switching between the modes.
Use the source, Luke!

Hubble
In need of some credit
In need of some credit
Posts: 6
Joined: Sun Jan 13, 2019 5:52 pm

Re: Sharing objects between notebook tabs.

Post by Hubble » Mon Jan 14, 2019 3:56 pm

Ok, I was going to post a reply but something went wrong so I'll make this short.
After searching around I found a set off classes relating to tabs - wxTabbedPanel , wxPanelTabView , wxTabControl... . However, I can't find any documentation on these classes on the official website. I found a small tutorial on how to make a tabbed dialog analogous to wxNotebook.

http://manual.freeshell.org/wx/wxwin447.htm

I think this is what I need.

User avatar
doublemax
Moderator
Moderator
Posts: 13127
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Sharing objects between notebook tabs.

Post by doublemax » Mon Jan 14, 2019 6:02 pm

wxTabbedPanel , wxPanelTabView , wxTabControl
Nice I had no idea they were still in wx3.x.

But i have a bad feeling they might look ugly :)
Use the source, Luke!

ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 3036
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Sharing objects between notebook tabs.

Post by ONEEYEMAN » Mon Jan 14, 2019 6:37 pm

Hi,
Those classes are not part of the main wx distribution anymore.
You can try to search wxCode, but my guess would be that you will need some effort in order to make them compile and run on the latest wx release.

Also, by looking at the link you mentioned, it looks like something very MSW-specific...

Thank you.

Hubble
In need of some credit
In need of some credit
Posts: 6
Joined: Sun Jan 13, 2019 5:52 pm

Re: Sharing objects between notebook tabs.

Post by Hubble » Mon Jan 14, 2019 7:43 pm

ONEEYEMAN wrote:Hi,
Those classes are not part of the main wx distribution anymore.
You're right. Looking through wx source I can't find a tabs.h file which should contain all the relevant classes. I had a quick look through notebook.h and other related files. I can't figure out how notebook implements tabs. As doublemax said, I could have shared controls as separate entities part of a different level of the GUI, but I don't understand how to go about this.

At the moment, I can only think of to reparent and resizer these shared resources. This is, as doublemax stated before, a messy technique and in my opinion long winded.
Are there classes that allow for custom tabs?

Thanks.

User avatar
doublemax
Moderator
Moderator
Posts: 13127
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Sharing objects between notebook tabs.

Post by doublemax » Mon Jan 14, 2019 8:43 pm

The classes are still there, defined in /include/generic/tabg.h

I haven't tested them though.
Use the source, Luke!

ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 3036
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Sharing objects between notebook tabs.

Post by ONEEYEMAN » Mon Jan 14, 2019 8:53 pm

doublemax,
They are not documented though, which means they are most likely a generic classes for the wxNotebook.

Thank you.

rando
Earned a small fee
Earned a small fee
Posts: 24
Joined: Fri Nov 09, 2018 9:11 pm

Re: Sharing objects between notebook tabs.

Post by rando » Tue Jan 15, 2019 9:07 pm

I do this sort of thing all the time using the following steps:
  • Create all needed panels.
    Create page sets which are arrays of pointers to the panels you created.
    Make functions such as HideAll() and ShowSetl() which take the arrays as arguments and use a for loop to iterate through the elements calling Show() or Hide().
    Make a function such as AddThesePagesl() with wxNotebook::AddPage() in a loop, use a pointer to the correct array of pages and pointer arithmetic.
    Make a function such as RemoveAllPages() which contains the code

    Code: Select all

     while (GetPageCount() > 0) RemovePage(0);
    (those are wxNotebook functions).
To use this setup:
Call Freeze()
Call RemoveAllPages()
Call HideAll() (I generally make this function call Hide() on all of the created panels, rather than passing a specific array)
Call ShowSet(array) with the desired array of page pointers
Call AddThesePages(array) with the desired array of page pointers.
On the notebook, call Layout();
Sometimes I have to write an event handler for the main window to call Layout() and Refresh() when pages are updated, but not always, so you will need to experiment with that.
Call Thaw()

Hoepfully that is clear enough :) I don't have time right now to put together an example and the code I have access to copy/paste is proprietary.

Hubble
In need of some credit
In need of some credit
Posts: 6
Joined: Sun Jan 13, 2019 5:52 pm

Re: Sharing objects between notebook tabs.

Post by Hubble » Wed Jan 16, 2019 6:32 pm

Let's say there are two wxPanel derived classes, one called EditModePanel and the Other DebugModePanel each having a pointer to the same text editor. They would have to display the text editor when active. If I understand correctly, in your method are a number of sets containing windows each corresponding to a set of pages for the notebook. swapping sets in and out of the notebook change the pages. With this method, all the features (buttons, text editor etc...) can be moved into one panel and for every page have a set of windows containing that one panel with the remaining spaces filled in with dummy windows.

Page 0: {panel , X , X}
page 1: {X , panel , X}
page 2: {X , X , panel} , where X is a dummy window.

Note that the page number maps to the position of the panel in the corresponding set. When the user clicks on a tab, the respective set set is swapped in (tab 2 goes with page set 2). In all cases the panel will be active and is effectively the same as having all pages assigned to one panel which wxNotebook forbids. With this there's no need to reparent the text editor between EditModePanel and DebugModePanel. Changing modes can be simple functions like ModesPanel::to_edit() and ModesPanel::to_debug().
The only problem now is to manage the sizers. One solution I can think of would be to have a "root" sizer for each page. A root sizer contains all the windows (or sizers) that make up the page. Then, swap out the correct root sizer for each page change(edit mode sizer for edit mode). I'm not sure how well wxWidgets handles multiple sizers containing the same window, but with my experience there seems to be a sizing conflict.
Another way would be to unbind all windows from sizers and rebind them to fit the layout of the next page.

What's your opinion on this approach?
Can two different sizers contain the same window?

Thanks.

User avatar
doublemax
Moderator
Moderator
Posts: 13127
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Sharing objects between notebook tabs.

Post by doublemax » Wed Jan 16, 2019 6:45 pm

Can two different sizers contain the same window?
No.

While the approach to reparent controls and detach them from one sizer and insert into another may sound promising, i would highly advise against it. Blood, sweat, tears and crashes will follow. If you can't or don't want to use the approach i suggested earlier (put everything in one panel and just show/hide the controls), i would just duplicate the common controls. I think it's the cleaner solution.
Use the source, Luke!

rando
Earned a small fee
Earned a small fee
Posts: 24
Joined: Fri Nov 09, 2018 9:11 pm

Re: Sharing objects between notebook tabs.

Post by rando » Wed Jan 16, 2019 10:05 pm

I agree, do not try to re-parent controls or reassign containing sizers. Just transfer data between the two, or more, distinct controls to give the illusion that they are the same object.
The approach I outlined is used to swap out full page sets in a notebook. I don't create any dummy pages or anything like that. The notebook automatically handles tab count by how many pages you add.

User avatar
shawnhcorey
Knows some wx things
Knows some wx things
Posts: 34
Joined: Mon Nov 19, 2012 3:29 pm
Location: The Great White North

Re: Sharing objects between notebook tabs.

Post by shawnhcorey » Thu Jan 17, 2019 2:40 pm

Wouldn't DocView work here?

Post Reply