Skip to content

Commit 6a7750d

Browse files
authored
Add warning about "docked unpinned" state (#8247)
This threads a potential warning message from an individual tool window (specifically property editor) to an embedded browser tab. There's probably a better way to structure this code, but I've already spent an embarrassing amount of time on shuffling around the UI pieces. 🙃 Suggestions welcome though. Fixes #8181 <img width="519" alt="Screenshot 2025-06-05 at 3 47 54 PM" src="https://github.com/user-attachments/assets/53809ba7-9e15-46b5-90a6-222e070b976e" />
1 parent 1f849ee commit 6a7750d

File tree

5 files changed

+90
-13
lines changed

5 files changed

+90
-13
lines changed

flutter-idea/src/io/flutter/devtools/AbstractDevToolsViewFactory.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.flutter.view.ViewUtils;
2424
import kotlin.coroutines.Continuation;
2525
import org.jetbrains.annotations.NotNull;
26+
import org.jetbrains.annotations.Nullable;
2627

2728
import java.util.List;
2829
import java.util.Optional;
@@ -63,6 +64,10 @@ public Object isApplicableAsync(@NotNull Project project, @NotNull Continuation<
6364

6465
@Override
6566
public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
67+
createToolWindowContent(project, toolWindow, null);
68+
}
69+
70+
public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow, @Nullable String warningMessage) {
6671
final FlutterSdk flutterSdk = FlutterSdk.getFlutterSdk(project);
6772
final FlutterSdkVersion flutterSdkVersion = flutterSdk == null ? null : flutterSdk.getVersion();
6873

@@ -104,15 +109,16 @@ public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindo
104109
}
105110

106111
// Final case:
107-
loadDevToolsInEmbeddedBrowser(project, toolWindow, flutterSdkVersion);
112+
loadDevToolsInEmbeddedBrowser(project, toolWindow, flutterSdkVersion, warningMessage);
108113

109114
// Finally, listen for the panel to be reopened and potentially reload DevTools.
110-
maybeReloadDevToolsWhenVisible(project, toolWindow, flutterSdkVersion);
115+
maybeReloadDevToolsWhenVisible(project, toolWindow, flutterSdkVersion, warningMessage);
111116
}
112117

113118
private void loadDevToolsInEmbeddedBrowser(@NotNull Project project,
114119
@NotNull ToolWindow toolWindow,
115-
@NotNull FlutterSdkVersion flutterSdkVersion) {
120+
@NotNull FlutterSdkVersion flutterSdkVersion,
121+
@Nullable String warningMessage) {
116122
viewUtils.presentLabel(toolWindow, "Loading " + getToolWindowTitle() + "...");
117123

118124
AsyncUtils.whenCompleteUiThread(
@@ -144,7 +150,7 @@ private void loadDevToolsInEmbeddedBrowser(@NotNull Project project,
144150
FlutterUtils.embeddedBrowser(project))
145151
.ifPresent(embeddedBrowser ->
146152
{
147-
embeddedBrowser.openPanel(toolWindow, getToolWindowTitle(), devToolsUrl, System.out::println);
153+
embeddedBrowser.openPanel(toolWindow, getToolWindowTitle(), devToolsUrl, System.out::println, warningMessage);
148154
devToolsLoadedInBrowser = true;
149155
doAfterBrowserOpened(project, embeddedBrowser);
150156
// The "refresh" action refreshes the embedded browser, not the panel.
@@ -157,14 +163,15 @@ private void loadDevToolsInEmbeddedBrowser(@NotNull Project project,
157163
}
158164

159165
private void maybeReloadDevToolsWhenVisible(@NotNull Project project,
160-
@NotNull ToolWindow toolWindow, @NotNull FlutterSdkVersion flutterSdkVersion) {
166+
@NotNull ToolWindow toolWindow, @NotNull FlutterSdkVersion flutterSdkVersion,
167+
@Nullable String warningMessage) {
161168
MessageBusConnection connection = project.getMessageBus().connect();
162169
connection.subscribe(ToolWindowManagerListener.TOPIC, new ToolWindowManagerListener() {
163170
@Override
164171
public void toolWindowShown(@NotNull ToolWindow activatedToolWindow) {
165172
if (activatedToolWindow.getId().equals(getToolWindowId())) {
166173
if (!devToolsLoadedInBrowser) {
167-
loadDevToolsInEmbeddedBrowser(project, toolWindow, flutterSdkVersion);
174+
loadDevToolsInEmbeddedBrowser(project, toolWindow, flutterSdkVersion, warningMessage);
168175
}
169176
}
170177
}

flutter-idea/src/io/flutter/propertyeditor/PropertyEditorViewFactory.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
package io.flutter.propertyeditor;
77

88
import com.intellij.openapi.project.Project;
9+
import com.intellij.openapi.util.Disposer;
910
import com.intellij.openapi.wm.ToolWindow;
11+
import com.intellij.openapi.wm.ToolWindowManager;
12+
import com.intellij.openapi.wm.ToolWindowType;
13+
import com.intellij.openapi.wm.ex.ToolWindowManagerListener;
14+
import com.intellij.util.messages.MessageBusConnection;
1015
import io.flutter.bazel.WorkspaceCache;
1116
import io.flutter.dart.DartPlugin;
1217
import io.flutter.dart.DartPluginVersion;
@@ -16,13 +21,15 @@
1621
import io.flutter.run.daemon.DevToolsInstance;
1722
import io.flutter.sdk.FlutterSdkVersion;
1823
import org.jetbrains.annotations.NotNull;
24+
import org.jetbrains.annotations.Nullable;
1925

2026
import java.util.List;
2127

2228
public class PropertyEditorViewFactory extends AbstractDevToolsViewFactory {
23-
@NotNull public static String TOOL_WINDOW_ID = "Flutter Property Editor";
29+
@NotNull public final static String TOOL_WINDOW_ID = "Flutter Property Editor";
2430

25-
@NotNull public static String DEVTOOLS_PAGE_ID = "propertyEditor";
31+
@NotNull public final static String DEVTOOLS_PAGE_ID = "propertyEditor";
32+
@Nullable private static Boolean previousDockedUnpinned = null;
2633

2734
@Override
2835
public boolean versionSupportsThisTool(@NotNull final FlutterSdkVersion flutterSdkVersion) {
@@ -65,6 +72,39 @@ public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindo
6572
"newer version of the Dart plugin."));
6673
return;
6774
}
68-
super.createToolWindowContent(project, toolWindow);
75+
76+
checkDockedUnpinnedAndCreateContent(project, toolWindow);
77+
78+
final PropertyEditorViewFactory self = this;
79+
MessageBusConnection connection = project.getMessageBus().connect();
80+
connection.subscribe(ToolWindowManagerListener.TOPIC, new ToolWindowManagerListener() {
81+
@Override
82+
public void toolWindowShown(@NotNull ToolWindow activatedToolWindow) {
83+
if (activatedToolWindow.getId().equals(getToolWindowId())) {
84+
checkDockedUnpinnedAndCreateContent(project, toolWindow);
85+
}
86+
}
87+
88+
@Override
89+
public void stateChanged(@NotNull ToolWindowManager toolWindowManager, @NotNull ToolWindowManagerEventType changeType) {
90+
if (changeType.equals(ToolWindowManagerEventType.SetToolWindowAutoHide) ||
91+
changeType.equals(ToolWindowManagerEventType.SetToolWindowType)) {
92+
checkDockedUnpinnedAndCreateContent(project, toolWindow);
93+
}
94+
}
95+
});
96+
Disposer.register(toolWindow.getDisposable(), connection);
97+
}
98+
99+
private void checkDockedUnpinnedAndCreateContent(@NotNull Project project, ToolWindow toolWindow) {
100+
final Boolean isDockedUnpinned = toolWindow.getType().equals(ToolWindowType.DOCKED) && toolWindow.isAutoHide();
101+
if (!isDockedUnpinned.equals(previousDockedUnpinned)) {
102+
previousDockedUnpinned = isDockedUnpinned;
103+
super.createToolWindowContent(project, toolWindow, isDockedUnpinned
104+
? "This tool window is in \"Docked Unpinned\" mode, which means it will disappear "
105+
+ "during normal use of the property editor. Select Options (three dots) > View "
106+
+ "Mode > Docked Pinned instead."
107+
: null);
108+
}
69109
}
70110
}

flutter-idea/src/io/flutter/run/daemon/DevToolsServerTask.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public void run(@NotNull ProgressIndicator progressIndicator) {
8080
progressIndicator.setFraction(30);
8181
progressIndicator.setText2("Init");
8282

83-
// If DevTools is not supported, start the daemon instead.
83+
// If the `dart devtools` command is not supported, start the daemon instead.
8484
final boolean dartDevToolsSupported = dartSdkSupportsDartDevTools();
8585
if (!dartDevToolsSupported) {
8686
LOG.info("Starting the DevTools daemon.");

flutter-idea/src/io/flutter/view/EmbeddedBrowser.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.flutter.utils.LabelInput;
2323
import io.flutter.utils.OpenApiUtils;
2424
import org.jetbrains.annotations.NotNull;
25+
import org.jetbrains.annotations.Nullable;
2526

2627
import javax.swing.*;
2728
import java.awt.*;
@@ -35,7 +36,7 @@
3536

3637
/**
3738
* There is one instance of embedded browser across the project, but it manages tabs across multiple tool
38-
* windows. Each tab can display contents of an independent URL.
39+
* windows. Each tab can display the contents of an independent URL.
3940
*/
4041
public abstract class EmbeddedBrowser {
4142
static public class BrowserTab {
@@ -77,7 +78,18 @@ public void projectClosing(@NotNull Project project) {
7778
});
7879
}
7980

80-
public void openPanel(ToolWindow toolWindow, String tabName, DevToolsUrl devToolsUrl, Consumer<String> onBrowserUnavailable) {
81+
public void openPanel(@NotNull ToolWindow toolWindow,
82+
@NotNull String tabName,
83+
@NotNull DevToolsUrl devToolsUrl,
84+
@NotNull Consumer<String> onBrowserUnavailable) {
85+
openPanel(toolWindow, tabName, devToolsUrl, onBrowserUnavailable, null);
86+
}
87+
88+
public void openPanel(@NotNull ToolWindow toolWindow,
89+
@NotNull String tabName,
90+
@NotNull DevToolsUrl devToolsUrl,
91+
@NotNull Consumer<String> onBrowserUnavailable,
92+
@Nullable String warningMessage) {
8193
this.url = devToolsUrl;
8294
Map<String, BrowserTab> tabs = windows.computeIfAbsent(toolWindow.getId(), k -> new HashMap<>());
8395

@@ -137,7 +149,17 @@ public void openPanel(ToolWindow toolWindow, String tabName, DevToolsUrl devTool
137149
tab.content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
138150
// TODO(helin24): Use differentiated icons for each tab and copy from devtools toolbar.
139151
tab.content.setIcon(FlutterIcons.Phone);
140-
tab.contentManager.addContent(tab.content);
152+
153+
final JPanel panel = new JPanel(new BorderLayout());
154+
155+
if (warningMessage != null) {
156+
panel.add(new ViewUtils().warningLabel(warningMessage), BorderLayout.NORTH);
157+
}
158+
159+
panel.add(tab.content.getComponent(), BorderLayout.CENTER);
160+
final Content panelContent = tab.contentManager.getFactory().createContent(panel, null, false);
161+
162+
tab.contentManager.addContent(panelContent);
141163
tab.contentManager.setSelectedContent(tab.content, true);
142164
tab.embeddedTab.matchIdeZoom();
143165
});

flutter-idea/src/io/flutter/view/ViewUtils.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@
2222
import java.util.List;
2323

2424
public class ViewUtils {
25+
public @NotNull JBLabel warningLabel(@NotNull String warning) {
26+
final JBLabel descriptionLabel = new JBLabel(wrapWithHtml(warning));
27+
descriptionLabel.setBorder(JBUI.Borders.empty(5));
28+
descriptionLabel.setHorizontalAlignment(SwingConstants.CENTER);
29+
descriptionLabel.setForeground(Color.RED);
30+
return descriptionLabel;
31+
}
32+
2533
public void presentLabel(ToolWindow toolWindow, String text) {
2634
final JBLabel label = new JBLabel(wrapWithHtml(text), SwingConstants.CENTER);
2735
label.setForeground(UIUtil.getLabelDisabledForeground());

0 commit comments

Comments
 (0)