Return Integer from DialogWindow

OK there will be a simple answer to this but I cannot find it!

I have a DialogWindow that has a Component on it. The Component only has 3 Buttons and depending on which one is clicked we exitModalState(0), exitModalState(1) or exitModalState(2)

So how do I get that integer 0, 1 or 2 back to the DialogWindow and also returned from the openSwapDialog function?

Current code below, missing an integer to return:

	int MemoryBankComponent::openSwapDialog(int startSlot, int endSlot)
	{
		juce::DialogWindow::LaunchOptions dialog;
		dialog.content.setNonOwned(new DialogComponent());
		dialog.dialogTitle = "Swap Memory Slots!";
		dialog.componentToCentreAround = this;
		dialog.dialogBackgroundColour = getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId);
		dialog.escapeKeyTriggersCloseButton = true;
		dialog.useNativeTitleBar = false;
		dialog.resizable = false;
		dialog.useBottomRightCornerResizer = false;

		dialog.launchAsync();
	}

	class DialogComponent : public juce::Component
	{
	public:
		DialogComponent()
		{
			addAndMakeVisible(cancelButton);
			cancelButton.onClick = [this] { getParentComponent()->exitModalState(0);  };
			addAndMakeVisible(swapInLibrarianButton);
			swapInLibrarianButton.onClick = [this] { getParentComponent()->exitModalState(1); };
			addAndMakeVisible(swapBothButton);
			swapBothButton.onClick = [this] { getParentComponent()->exitModalState(2); };

			setSize(400, 300);
		}
		~DialogComponent() override
		{
		}
		void resized()
		{
			swapInLibrarianButton.setBounds(getWidth() * 0.25, getHeight() * 0.2, getWidth()*0.5, getHeight()*0.1);
			swapBothButton.setBounds(getWidth() * 0.25, getHeight() * 0.5, getWidth() * 0.5, getHeight() * 0.1);
			cancelButton.setBounds(getWidth() * 0.25, getHeight() * 0.8, getWidth() * 0.5, getHeight() * 0.1);
		}
	private:
		// Buttons
		// -------
		juce::TextButton swapInLibrarianButton{ "Swap only in Librarian?" };
		juce::TextButton swapBothButton{ "Also swap in Hardware" };
		juce::TextButton cancelButton{ "Cancel" };
	};

Most of the solutions I found on the Forum refer to runModalLoop with an equal number of other articles saying DO NOT run ModalLoops! So I’m using launchAsync instead but I can’t find much reference to returning integers for that. I also looked at the DialogsDemo but tbh I cannot make head nor tail of it.

One method I use is to pass in callbacks that handle the button presses externally. In looking at my code I forget why I didn’t called exitModalState within the dialog. I will have to give that another look.

Contained component:

    LocateFileComponent (std::vector<juce::File> theMissingFiles, juce::File startingFolder,
                         std::function<void (std::vector<std::tuple <juce::File, juce::File>>)> theLocatedFilesCallback, std::function<void ()> theCancelCallback);

Creating dialog:

    juce::DialogWindow::LaunchOptions options;
    auto locateComponent { std::make_unique<LocateFileComponent> (filesToLocate, locateFilesInitialDirectory, [this] (std::vector<std::tuple <juce::File, juce::File>> locatedFiles)
    {
        // do things with the files
        locateDialog->exitModalState (0);
    },
    [this] ()
    {
        // handle the cancel (usually just doing nothing)
        locateDialog->exitModalState (0);
    }) };
    options.content.setOwned (locateComponent.release ());
    // etc
    locateDialog = options.launchAsync ();

My first approach was to use the buttons to call functions externally like this:

findParentComponentOfClass<MemoryBankComponent>()->swapLibrarianMemorySlots();
getParentComponent()->exitModalState(1);

But the problem I ran into there was that although the swapLibrarianMemorySlots() function is called from the MemoryBankComponent Class, it does NOT call the object / instance of that Class!

So the variables that I was using inside that function are not available and I end up with:

So if someone could show me how to call the Object rather than just the Class, that would be another solution.

My approach should not have this issue, as it captures the this pointer from the calling context.

@cpr2323 you put me on the right track and I have a solution of sorts:

When you call the DialogWindow, pass in a pointer to the current Object, like:

openSwapDialog(this);

In the DialogWindow you call this pointer to the original Class & Object ‘m’, and pass it into the Component you place on the DialogWindow, like:

	void MemoryBankComponent::openSwapDialog(MemoryBankComponent* m)
	{
		juce::DialogWindow::LaunchOptions dialog;
		dialog.content.setOwned(new DialogComponent(m));
		dialog.dialogTitle = "Swap Memory Slots!";
		dialog.componentToCentreAround = this;
		dialog.dialogBackgroundColour = getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId);
		dialog.escapeKeyTriggersCloseButton = true;
		dialog.useNativeTitleBar = false;
		dialog.resizable = false;
		dialog.useBottomRightCornerResizer = false;

		dialog.launchAsync();
	}

Now in the DialogComponent that is placed on the DialogWindow, below:

	class DialogComponent : public juce::Component
	{
	public:
		DialogComponent(MemoryBankComponent* m)
		{
			addAndMakeVisible(cancelButton);
			cancelButton.onClick = [this, m] { getParentComponent()->exitModalState(0); m->cancelPressed(); };
			addAndMakeVisible(swapInLibrarianButton);
			swapInLibrarianButton.onClick = [this, m] { getParentComponent()->exitModalState(1);  m->swapInLibrarianPressed(); };
			addAndMakeVisible(swapBothButton);
			swapBothButton.onClick = [this, m] { getParentComponent()->exitModalState(2); m->swapBothPressed(); };

			setSize(400, 300);
		}
		~DialogComponent() override
		{
		}
		void resized()
		{
			swapInLibrarianButton.setBounds(getWidth() * 0.25, getHeight() * 0.2, getWidth()*0.5, getHeight()*0.1);
			swapBothButton.setBounds(getWidth() * 0.25, getHeight() * 0.5, getWidth() * 0.5, getHeight() * 0.1);
			cancelButton.setBounds(getWidth() * 0.25, getHeight() * 0.8, getWidth() * 0.5, getHeight() * 0.1);
		}

	private:
		// Buttons
		// -------
		juce::TextButton swapInLibrarianButton{ "Swap only in Librarian?" };
		juce::TextButton swapBothButton{ "Also swap in Hardware" };
		juce::TextButton cancelButton{ "Cancel" };
	};

In the onClick Lambdas you not only have this, referring to the DialogComponent, you also have m, which is a pointer to the original Class & Object that called all this in the first place!

Now you can use that pointer m to call the functions you want on the original Object, hooray!

I currently have a love / hate relationship with C++, mostly the latter at present :slight_smile:

Awesome that you have a solution! :slight_smile:

Either I don’t understand this part, or it is redundant:

void MemoryBankComponent::openSwapDialog(MemoryBankComponent* m)

Is the caller of openSwapDialog a different instantiation of the class? ie. The this pointer inside of openSwapDialog is the pointer to the current instantiation of the class. Is it possible you don’t need it as a parameter to openSwapDialog, and could just do dialog.content.setOwned(new DialogComponent(this));

Nope, you understand perfectly, you’re right, it isn’t necessary, the following works just fine:

	void MemoryBankComponent::openSwapDialog()
	{
		juce::DialogWindow::LaunchOptions dialog;
		dialog.content.setOwned(new DialogComponent(this));
		dialog.dialogTitle = "Swap Memory Slots!";
		dialog.componentToCentreAround = this;
		dialog.dialogBackgroundColour = getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId);
		dialog.escapeKeyTriggersCloseButton = true;
		dialog.useNativeTitleBar = false;
		dialog.resizable = false;
		dialog.useBottomRightCornerResizer = false;

		dialog.launchAsync();
	}

	class DialogComponent : public juce::Component
	{
	public:
		DialogComponent(MemoryBankComponent* m)
		{
			addAndMakeVisible(cancelButton);
			cancelButton.onClick = [this, m] { getParentComponent()->exitModalState(0); m->cancelPressed(); };
			addAndMakeVisible(swapInLibrarianButton);
			swapInLibrarianButton.onClick = [this, m] { getParentComponent()->exitModalState(1);  m->swapInLibrarianPressed(); };
			addAndMakeVisible(swapBothButton);
			swapBothButton.onClick = [this, m] { getParentComponent()->exitModalState(2); m->swapBothPressed(); };

			setSize(400, 300);
		}
		~DialogComponent() override
		{
		}
		void resized()
		{
			swapInLibrarianButton.setBounds(getWidth() * 0.25, getHeight() * 0.2, getWidth()*0.5, getHeight()*0.1);
			swapBothButton.setBounds(getWidth() * 0.25, getHeight() * 0.5, getWidth() * 0.5, getHeight() * 0.1);
			cancelButton.setBounds(getWidth() * 0.25, getHeight() * 0.8, getWidth() * 0.5, getHeight() * 0.1);
		}

	private:
		// Buttons
		// -------
		juce::TextButton swapInLibrarianButton{ "Swap only in Librarian?" };
		juce::TextButton swapBothButton{ "Also swap in Hardware" };
		juce::TextButton cancelButton{ "Cancel" };
	};

I’m still not sure how you would use the exitModalState integers though? But at this stage I don’t need to as long as I can call different functions from the Object / Instance of MemoryBankComponent.

re “I’m still not sure how you would use the exitModalState integers though”, there is no need to. If you really wanted to, you can get an instance of the ModalComponentManager and register a callback with it, but, that is a rather round-about way of doing what you are already doing.

Not that it is an issue here, but if you ever want to have a Dialog that is reusable, ie. the interface is the same, but the functions executed when the buttons are pressed, or just to reduce coupling, your solution won’t allow for that. Using the callback solution I suggested is how you would go. And, if you wanted to use a ‘return value’ type approach, the callback could be generic like std::function<void onDialogButtonPress (int buttonIndex)>, which the caller would pass in their lambda and handle the buttonIndex value there. I find the callback per button approach more explicit when using dialogs, so that is how I go. As well, as in my example, different buttons could produce different data.