I'm having a problem with circular dependencies when trying to manage my windows with a 'WindowList' class. When I want to close a window with closeButtonPressed code below, I need to remove the object from the windowList file, however I am including WindowSetter in the WindowList file. Previous errors like this have been solvable through a forward declaration, however I am not sure how to solve this one. Anny suggestions? (full code can be viewed here: https://gist.github.com/anonymous/7d43c6d5b2cf1fef618be9f75077ad0c)
#pragma once
#include "../JuceLibraryCode/JuceHeader.h"
#include "WindowList.h"
class WindowList;
class WindowSetter : public DialogWindow
{
public:
WindowSetter (const String& title,
Component* content,
bool shouldBeResizeable,
int initWidth, int initHeight,
int minWidth, int minHeight,
int maxWidth, int maxHeight)
: DialogWindow (title, Colours::white, true, true),
owner (this)
{
setUsingNativeTitleBar (true);
setResizable (true, true);
setResizeLimits (minWidth, minHeight, maxWidth, maxHeight);
setContentOwned (content, false);
setVisible (true);
}
~WindowSetter()
{
}
void closeButtonPressed() override
{
WindowList::getWindowList(); // ERROR: Incomplete type 'WindowList' named in nested name specifier
owner = nullptr;
}
bool escapeKeyPressed() override
{
return true;
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowSetter)
ScopedPointer<Component> owner;
};
edit: Added the full code for the file causing the error and an error log screenshot
OMGtechy's response addresses the question that you asked, but I'd like to recommend a different design that:
The design as you have it here tightly couples things together. A more JUCE-y way to solve the problem is you use the ChangeBroadcaster
/ ChangeListener
classes to remove that tight coupling. When you add a WindowSetter
to your WindowList
, also subscribe to its change messages. When the user clicks on the close button, the WindowSetter
sets a boolean and alerts anyone listening to it that it's been updated.
In sketches, it looks like
class WindowSetter : public DialogWindow
, public ChangeBroadcaster
{
public:
WindowSetter( /*(etc...)*/)
: DialogWindow(...)
, owner(this)
, wantsToClose(false)
{
// etc
}
void closeButtonPressed() override
{
wantsToClose = true;
// notify observers that we've changed.
sendChangeMessage();
}
bool windowWantsToClose() const
{
return wantstoClose;
}
private:
bool wantsToClose;
};
class WindowList : public ChangeListener
{
void addWindowSetterToList(WindowSetter* wnd)
{
wnd->addChangeListener(this)
windows.addIfNotAlreadyThere(wnd);
}
void changeListenerCallback(ChangeBroadcaster* src) override
{
// cast from the ChangeBroadcaster base class to our WindowSetter class.
WindowSetter* wnd = dynamic_cast<WindowSetter*>(src);
if (nullptr != wnd)
{
// if we contain the object, and the object wants to be closed...
if (windows.contains(wnd) && wnd->windowWantsToClose())
{
// get rid of it.
windows.remove(wnd);
}
}
}
};
You'll see this kind of design used almost everywhere in JUCE codebases.