進捗インジケーター
デフォルトでは、アクションは実行中は UI をブロックします。すぐに終了しないアクションの場合は、アクションがアクティブであることをユーザーに示し、進行状況と時間の見積もりに関するフィードバックをユーザーに提供する進行状況バーを表示することをお勧めします。このセクションでは、進行状況インジケーターを適切に表示および更新する方法、ユーザーがアクションを手動でキャンセルできるようにする方法、およびアクションをバックグラウンドに送信する方法について説明します。
非同期タスク
UI がフリーズしないように、すべてのアクションを迅速に行うことを目指す必要があります。長期的なアクティビティは、アクションから抽出して、1 つ以上の非同期で実行されるタスクに配置する必要があります。タスクは、com.intellij.openapi.progress @ java_stub からタスククラスを拡張し、モーダルダイアログを表示するか(Task.Modal クラス)、バックグラウンドに送信する(Task.Backgroundable クラス)ことができます。タスクは、ApplicationManager.getApplication()。invokeLater() メソッドを介して EDT で呼び出す必要があります。
action ModalProgressAction {
mnemonic: <no mnemonic>
execute outside command: false
also available in: << ... >>
caption: BackgroundableProgressAction
description: <no description>
icon: <no icon>
construction parameters
<< ... >>
action context parameters ( always visible = false )
Project project key: PROJECT required
MPSProject mpsProject key: MPS_PROJECT required
<update block>
execute(event)->void {
boolean canBeCanceled = true;
Task.Modal modalTask = new Task.Modal(ModalProgressAction.this.project, "Modal cancelable task", canBeCanceled) {
public void run(@NotNull() final ProgressIndicator indicator) {
doWorkChunk1();
if (checkForCancellation()) return;
doWorkChunk2();
if (checkForCancellation()) return;
...
}
@Override
public void onCancel() {
super.onCancel();
}
};
ApplicationManager.getApplication().invokeLater({ => ProgressManager.getInstance().run(modalTask); });
}
}
タスクは通常、タスクを実行するための run() メソッドと、ユーザーによって呼び出されたキャンセルを処理するための onCancel() メソッドを提供します。実際のキャンセルロジックは、タスク実装者が実装する必要があります。run() での作業は、保留中のキャンセルをチェックするチャンクに編成する必要があります。onCancel() メソッドは、run() がキャンセルによって終了した後にのみ起動されるため、主にタスクによって実行された部分的な作業をクリーンアップするために使用されます。
キャンセル不可能なモーダルタスク(canBeCancelled パラメーターを false に設定)は、アクションが完全に終了するまでユーザーを待機させるため、キャンセルを適切に処理することが難しい重要なアクションに対してのみ、注意して使用する必要があります。
進捗状況の監視
タスクの run() メソッドは、パラメーターとして IntelliJ のプログレスインジケータのインスタンスを取得します。このインスタンスは、jetbrains.mps.progress@ java_stub から取得した ProgressMonitorAdapter でラップすることをお勧めします。ProgressMonitorAdapter は、視覚的なプログレスダイアログを表し、実際のプログレスバーの値やユーザーに表示されるラベルを設定するメソッドを提供します。また、キャンセルに関する情報も保持します。
Task.Modal modalTask = new Task.Modal(ModalProgressAction.this.project, "Modal cancelable task", canBeCanceled) {
public void run(@NotNull() final ProgressIndicator indicator) {
final ProgressMonitorAdapter adapter = new ProgressMonitorAdapter(indicator);
adapter.start("Progress in progress...", 8);
int stepValue = 1;
adapter.step("Do work 1 ...");
do work chunk 1
adapter.advance(stepValue);
if (adapter.isCanceled()) { return; }
adapter.step("Do work 2 ...");
do work chunk 2
adapter.advance(stepValue);
if (adapter.isCanceled()) { return; }
...
adapter.done();
}
いくつかのメモ:
タスクのコンストラクターには、進行状況のタイトルとして使用されるテキストが含まれていますダイアログ
start() メソッドは、進行状況バーの上に表示するテキストと、タスクを完了するためのいくつかのステップ / ポイントを提供します
step() メソッドは、進行中の進行状況バーに表示されるテキストラベルを変更しますダイアログ
advance() メソッドは、指定されたステップ / ポイント数だけプログレスバーを新しい値に移動します
ユーザーエクスペリエンスを向上させるために、進行手順をできる限り小さくします。ステップを小さくすることで、ユーザーはよりスムーズに操作できます
バックグラウンドで実行
Task.Backgroundable クラスは、バックグラウンドで処理できるタスクに使用する必要があります。
action BackgroundableProgressAction {
mnemonic: <no mnemonic>
execute outside command: false
also available in: << ... >>
caption: BackgroundableProgressAction
description: <no description>
icon: <no icon>
construction parameters
<< ... >>
action context parameters ( always visible = false )
Project project key: PROJECT required
MPSProject mpsProject key: MPS_PROJECT required
<update block>
execute(event)->void {
boolean canBeCanceled = true;
PerformInBackgroundOption showProgress = PerformInBackgroundOption.DEAF;
Task.Backgroundable backgroundable = new Task.Backgroundable(BackgroundableProgressAction.this.project, "Backgroundable cancelable task", canBeCanceled, showProgress) {
public void run(@NotNull() final ProgressIndicator indicator) {
...
}
@Override
public void onCancel() {
super.onCancel();
}
};
ApplicationManager.getApplication().invokeLater({ => ProgressManager.getInstance().run(backgroundable); });
}
additional methods
private void doWork() {
...
}
}
いくつかのメモ:
バックグラウンド可能なタスクはキャンセルを許可する場合と許可しない場合があります
PerformInBackgroundOption インターフェースを使用すると、フォアグラウンドとバックグラウンドで開始するタスクを作成できます。
ユーザーは、バックグラウンド可能なタスクをバックグラウンドだけでなくフォアグラウンドにも移動できます
PerformInBackgroundOption インターフェースの事前定義定数は、DEAF (フォアグラウンドで開始) と ALWAYS_BACKGROUND (バックグラウンドで開始。ダイアログが表示されず、UI が完全に使用可能な状態を維持するため、ユーザーが注意を払う必要のない重要でないアクションに便利です) です。
リソースにアクセスするときの適切なロック
タスク内で MPS リポジトリへの読み取りおよび書き込みロックを取得し、コマンドを実行しても問題ありません。
// ReadAction in step is ok for display state
ModalProgressAction.this.mpsProject.getRepository().getModelAccess().runReadAction(new Runnable() {
public void run() {
adapter.step("Do some work with Read Lock...");
ModalProgressAction.this.doWork();
}
});
adapter.advance(stepValue);
if (adapter.isCanceled()) { return; }
// WriteAction in step is ok for display state
ModalProgressAction.this.mpsProject.getRepository().getModelAccess().runWriteAction(new Runnable() {
public void run() {
adapter.step("Do some work with Write Lock...");
ModalProgressAction.this.doWork();
}
});
adapter.advance(stepValue);
if (adapter.isCanceled()) { return; }
// Command in step is ok for display state
ModalProgressAction.this.mpsProject.getRepository().getModelAccess().executeCommand(new Runnable() {
public void run() {
adapter.step("Do some work in command...");
ModalProgressAction.this.doWork();
}
});
adapter.advance(stepValue);
if (adapter.isCanceled()) { return; }
いくつかのメモ:
アクション内でロックする必要がある場合は、すべての変更を単一のロックブロックにグループ化することをお勧めします
他の潜在的なユーザーアクションのブロックを回避するために、不要になったらすぐにロックを解除します
EDT スレッドで R/W アクションまたはコマンドを使用しないでください - これにより、進行状況の予測不可能な更新が発生し、UI がフリーズする可能性があります
取り消し不可能なアクション
モデルの変更には、executeCommandInEDT() メソッドで実行できる取り消し可能な操作が必要です。ただし、コマンド自体が EDT で実行されているため、プログレスバーの変更要求はすべてコマンドが終了するまで遅延するため、コマンド内から ProgressIndicator のメソッドを呼び出さないでください。推奨される方法は、executeCommandInEDT() を使用してコマンドを呼び出す前に、メインアクションのスレッドからプログレスバーを操作し、CyclicBarrier などの同期プリミティブを使用して、コマンドが終了するまでメインアクションのスレッドをブロックすることです。
adapter.step("Do some work in command ...");
final CyclicBarrier barrier = new CyclicBarrier(2);
ModalProgressAction.this.mpsProject.getRepository().getModelAccess().executeCommandInEDT(new Runnable() {
public void run() {
try {
model m = model/myModel/;
m.new root node(MyRootConceptDeclaration);
} finally {
try {
barrier.await();
} catch (BrokenBarrierException e) {
<no statements>
} catch (InterruptedException e) {
<no statements>
}
}
}
});
try {
barrier.await();
} catch (InterruptedException e) {
<no statements>
} catch (BrokenBarrierException e) {
<no statements>
}
adapter.advance(stepValue);
if (adapter.isCanceled()) { return; }
追加の方法セクションを活用して、アクションの本体からロック実装コードを抽出できます。
private void block(CyclicBarrier barrier) {
try {
barrier.await();
} catch (BrokenBarrierException e) {
<no statements>
} catch (InterruptedException e) {
<no statements>
}
}
2026 年 5 月 06 日