I have been attempting to write a GTKMM gui application in C++. In my earlier projects in Java I started by making so-called 'Screen' objects which would each contain the layout of, and objects in, different screens. So I tried that in C++ as well, I derived these different Screen objects from the Gtk::Box
so that I could easily append them to a Gtk::Notebook
.
However I found out that this approach results in circular dependencies, and after a lot of browsing I couldn't find people with other approaches. I currently have a screen to display data retrieved from a database, and wanted to add filters to that.
I managed to allow the swapping of screens by giving each screen a pointer to the Gtk::Notebook
they are in, but I hit a roadblock when I couldn't figure out how to make two screens interact with each other (eg. filter the data in another screen).
The general problem appears like this:
Gui.h:
class Gui {
protected:
// Child object pointers.
Gtk::Notebook *m_screens;
DbConnector *m_db;
// Screen object pointers.
MainScreen *m_mainScreen;
FilterScreen *m_filterScreen;
public:
// Constructors & destructor.
Gui();
virtual ~Gui();
};
Gui.cpp:
Gui::Gui() {
// Create application.
auto app = Gtk::Application::create();
// Db connector
m_db = new DbConnector();
// Create & configure window.
Gtk::Window m_window;
// Window configs.....
// Create notebook & screen objects.
m_screens = new Gtk::Notebook();
m_screens->set_show_tabs(false);
m_mainScreen = new MainScreen(*m_screens);
m_filterScreen = new FilterScreen(*m_screens);
// Add notebook to window.
m_window.add(*m_screens);
//Insert pages.
m_screens->append_page(*m_mainScreen);
m_screens->append_page(*m_filterScreen);
// Show all children & run app.
m_window.show_all_children();
app->run(m_window);
}
MainScreen.h:
class MainScreen : public Gtk::Box {
protected:
// Parent notebook pointer.
Gtk::Notebook* parent;
// Child widgets.
Gtk::Button m_toFilterScreenButton = Gtk::Button("To Filter Screen");
// Constructors & desctructor.
MainScreen(Gtk::Notebook& par);
virtual ~MainScreen();
// Methods.
void addFilter(std::string filterText);
void toFilterScreen();
};
MainScreen.cpp:
MainScreen::MainScreen(Gtk::Notebook& par) : parent(&par) {
// Build screen.
// Packing contents.....
// Configure widgets.
// Things like widget border width.....
// Signal handlers.
m_toFilterScreenButton.signal_clicked().connect(sigc::mem_fun(*this, &MainScreen::toFilterScreen));
}
void MainScreen::addFilter(std::string filterText) {
// Add filter
}
void MainScreen::toFilterScreen() {
notebook->set_current_screen(pagenum_of_filterscreen);
}
The problem I ran into now is when the FilterScreen
is up, a filter is selected, and that filter should be applied to the MainScreen
. The FilterScreen
can't reach the MainScreen
via the Gui
object because that would require the screens to include Gui.h
, which would result in a circular dependency. Trying to retrieve the MainScreen
from the Gtk::Notebook
returns a Widget&
, which will tell you a Gtk::Widget
has no function called addFilter(std::string filterText);
.
Is anybody aware of a pattern I could use that would allow this type of behavior? So far the only option I can think of is one giant class that sets the screens using functions instead of premade objects, which would be far from optimal...
With the advice given by Sam Varshavchik in the comments I made a tiny example app that can handle multiple screens and switch easily. For those interested: source can be found here: ExampleApp