見出し画像

JUCEがモーダルウインドウ禁止になったので仕方なくリファクタリングしてみた

SHOULD NEVER USE THIS METHOD!

さて、少し前から、「プログラム的に危険である」「Androidが非対応」ということで非推奨(というか、"you SHOULD NEVER USE THIS METHOD!")になっていたモーダルウインドウ(入力待ちダイアログ)。

ついに6.1.0からデフォルトで使えなくなり、Async(非同期)系の、プロセスはすぐ次に移動してコールバックで処理を行う方法のみに変わりました。


具体的には、

AlertWindow w("Modal Window.", "", AlertWindow::InfoIcon);
				w.addButton("OK", 1, juce::KeyPress(KeyPress::returnKey, 0, 0));
				w.addButton("Clear PW", 2, KeyPress(KeyPress::deleteKey, 0, 0));
				if (w.runModalLoop() == 2) // "Clear PW"
				{

みたいな、runModalLoop() 使って表示&入力待ちする書き方とか、showMessageBox() とかですね。


まだ、juce_PlatformDefs.h で
 JUCE_MODAL_LOOPS_PERMITTED 1
にすれば一応動きますが、今後動かなくなるのでしょう。


リファクタリング(OK/Cancel)

では、どう直せば良いのでしょう?

プロセスを止めることができない以上、全く同じ動作を再現することはできません。

例えば showMessageBox() であれば showMessageBoxAsync() に書き換えれば動きはするのですが、メインの処理とダイアログの処理はそれぞれ非同期で別々に行うことになります。

AlertWindowを表示させるには、runModalLoop() の代わりに、enterModalState() を使います。

・ PluginProcessor.h

private:
   std::unique_ptr<AlertWindow> w1, w2;


・PluginProcessor.cpp prepareToPlay()

	w1 = std::make_unique<AlertWindow>("AlertWindow Test", "", AlertWindow::InfoIcon);
	w1->addButton("OK", 1, KeyPress(KeyPress::returnKey, 0, 0));
	w1->addButton("Cancel", 0, KeyPress(KeyPress::deleteKey, 0, 0));
	w1->enterModalState(true, ModalCallbackFunction::create([this](int r)
		{
			DBG("retuen : " << r);
			if (r) {
				// 実際の処理を書く
				AlertWindow::showMessageBoxAsync(AlertWindow::InfoIcon, "Pass", "");
				DBG("pass");
			}
			else{
				// 実際の処理を書く
				AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Canceled", "");
				DBG("Cancel");
			}
			w1->exitModalState(r);
			w1->setVisible(false);
		}));
	w1->grabKeyboardFocus();

・実行結果

画像3

画像2

画像3

enterModalState() 最後の引数である deleteWhenDismissed を trueしておくと入力があった後に自動でダイアログが閉じるのですが、これで閉じるとプログラム終了にも消しに行く(?)ようで、すでに消えているとエラーが出ます。

そのため、deleteWhenDismissed は省略しておいて、コールバックの中で
 w1->exitModalState(r);
 w1->setVisible(false);
を使ってウインドウの破棄を行っています。

リファクタリング(ComboBox、EditText付き)

・ PluginProcessor.h

#include <thread>
#include <chrono>
using std::this_thread::sleep_for;
using namespace std::chrono_literals;

入力テキストが ”AudiiSion” 以外だったときは fail として2秒間メッセージを表示させてからアプリケーションを終了させているので、その sleep_for() 用の宣言です。

・ PluginProcessor.cpp prepareToPlay()

	std::string ptext = "AudiiSion";
	// set dialog
	w2 = std::make_unique<AlertWindow>("AlertWindow Test2", "", MessageBoxIconType::NoIcon);
	w2->addTextEditor("text", "Enter Text", "Edit Text Example");
	// set ComboBox
	StringArray options;
	options.add("Option1");
	options.add("Option2");
	w2->addComboBox("option", options, "Option select");
	// add button
	w2->addButton("OK", 1, KeyPress(KeyPress::returnKey, 0, 0));
	w2->addButton("Cancel", 0, KeyPress(KeyPress::escapeKey, 0, 0));
	w2->enterModalState(true, ModalCallbackFunction::create([this, ptext](int r)
		{
			DBG("return " << r);
			if (r)
			{
				auto inputText = w2->getTextEditorContents("text");  // get input text
				auto selected = w2->getComboBoxComponent("option")->getSelectedItemIndex();
				DBG("text " << inputText);
				DBG("selected " << selected);
				if (inputText.compare(ptext) == 0) {
					AlertWindow::showMessageBoxAsync(AlertWindow::InfoIcon, "Pass", "");
					DBG("pass");
				}
				else {
					AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Fail", "");
					DBG("fail");
					sleep_for(2000ms);
					JUCEApplicationBase::getInstance()->systemRequestedQuit();  // exit application
				}
			}
			else {
				AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Cancel", "");
				DBG("Cancel");
			}
			w2->exitModalState(r);
			w2->setVisible(false);
		}));
	w2->grabKeyboardFocus();

・実行結果

画像4

画像5

画像8

画像6

画像7

C++をマスターしていない私にはなぜだか分からないのですが、コールバック内では例えば string型の演算子が使えないようなので、compare() とかの関数を使っています。

う~ん、めんどくさいけど、やっとかないとしょうがないですね。(゚~゚)


この記事が気に入ったらサポートをしてみませんか?