diff --git a/flutter-idea/src/icons/FlutterIcons.java b/flutter-idea/src/icons/FlutterIcons.java index c6294887f8..704063151c 100644 --- a/flutter-idea/src/icons/FlutterIcons.java +++ b/flutter-idea/src/icons/FlutterIcons.java @@ -14,6 +14,7 @@ private static Icon load(String path) { public static final Icon DevTools = load("/icons/expui/devTools.svg"); public static final Icon DevToolsExtensions = load("/icons/expui/extensions.svg"); public static final Icon DevToolsInspector = load("icons/expui/inspector.svg"); + public static final Icon PropertyEditor = load("/icons/expui/propertyEditor.svg"); public static final Icon Flutter_13_2x = load("/icons/flutter_13@2x.png"); public static final Icon Flutter_64 = load("/icons/flutter_64.png"); diff --git a/flutter-idea/src/io/flutter/propertyeditor/PropertyEditorViewFactory.java b/flutter-idea/src/io/flutter/propertyeditor/PropertyEditorViewFactory.java new file mode 100644 index 0000000000..a78d4235d1 --- /dev/null +++ b/flutter-idea/src/io/flutter/propertyeditor/PropertyEditorViewFactory.java @@ -0,0 +1,78 @@ +/* + * Copyright 2025 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +package io.flutter.propertyeditor; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowFactory; +import io.flutter.FlutterUtils; +import io.flutter.actions.RefreshToolWindowAction; +import io.flutter.bazel.WorkspaceCache; +import io.flutter.devtools.DevToolsIdeFeature; +import io.flutter.devtools.DevToolsUrl; +import io.flutter.run.daemon.DevToolsService; +import io.flutter.sdk.FlutterSdk; +import io.flutter.sdk.FlutterSdkVersion; +import io.flutter.utils.AsyncUtils; +import kotlin.coroutines.Continuation; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Optional; + +public class PropertyEditorViewFactory implements ToolWindowFactory { + @NotNull private static String TOOL_WINDOW_ID = "Flutter Property Editor"; + + @Override + public Object isApplicableAsync(@NotNull Project project, @NotNull Continuation $completion) { + FlutterSdk sdk = FlutterSdk.getFlutterSdk(project); + FlutterSdkVersion sdkVersion = sdk == null ? null : sdk.getVersion(); + return sdkVersion != null && sdkVersion.canUsePropertyEditor(); + } + + @Override + public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { + FlutterSdk sdk = FlutterSdk.getFlutterSdk(project); + FlutterSdkVersion sdkVersion = sdk == null ? null : sdk.getVersion(); + + AsyncUtils.whenCompleteUiThread( + DevToolsService.getInstance(project).getDevToolsInstance(), + (instance, error) -> { + // Skip displaying if the project has been closed. + if (!project.isOpen()) { + return; + } + + if (error != null) { + return; + } + + if (instance == null) { + return; + } + + final DevToolsUrl devToolsUrl = new DevToolsUrl.Builder() + .setDevToolsHost(instance.host()) + .setDevToolsPort(instance.port()) + .setPage("propertyEditor") + .setEmbed(true) + .setFlutterSdkVersion(sdkVersion) + .setWorkspaceCache(WorkspaceCache.getInstance(project)) + .setIdeFeature(DevToolsIdeFeature.TOOL_WINDOW) + .build(); + + ApplicationManager.getApplication().invokeLater(() -> { + Optional.ofNullable( + FlutterUtils.embeddedBrowser(project)) + .ifPresent(embeddedBrowser -> embeddedBrowser.openPanel(toolWindow, "Property Editor", devToolsUrl, System.out::println)); + }); + } + ); + + toolWindow.setTitleActions(List.of(new RefreshToolWindowAction(TOOL_WINDOW_ID))); + } +} diff --git a/flutter-idea/src/io/flutter/sdk/FlutterSdkVersion.java b/flutter-idea/src/io/flutter/sdk/FlutterSdkVersion.java index 7754f88da4..b865d3bade 100644 --- a/flutter-idea/src/io/flutter/sdk/FlutterSdkVersion.java +++ b/flutter-idea/src/io/flutter/sdk/FlutterSdkVersion.java @@ -46,6 +46,9 @@ public final class FlutterSdkVersion implements Comparable { @NotNull private static final FlutterSdkVersion MIN_SUPPORTS_DTD = new FlutterSdkVersion("3.22.0"); + @NotNull + private static final FlutterSdkVersion MIN_SUPPORTS_PROPERTY_EDITOR = new FlutterSdkVersion("3.29.0"); + @Nullable private final Version version; @Nullable @@ -127,6 +130,10 @@ public boolean canUseDeepLinksTool() { return supportsVersion(MIN_SUPPORTS_DEEP_LINKS_TOOL); } + public boolean canUsePropertyEditor() { + return supportsVersion(MIN_SUPPORTS_PROPERTY_EDITOR); + } + public boolean canUseDevToolsMultiEmbed() { return supportsVersion(MIN_SUPPORTS_DEVTOOLS_MULTI_EMBED); } diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 9e93c4cab2..52a5b473a9 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -387,6 +387,8 @@ + + diff --git a/resources/META-INF/plugin_template.xml b/resources/META-INF/plugin_template.xml index efd50598b0..6b3469598d 100644 --- a/resources/META-INF/plugin_template.xml +++ b/resources/META-INF/plugin_template.xml @@ -315,6 +315,8 @@ + + diff --git a/resources/icons/expui/propertyEditor.svg b/resources/icons/expui/propertyEditor.svg new file mode 100644 index 0000000000..1541855f61 --- /dev/null +++ b/resources/icons/expui/propertyEditor.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/expui/propertyEditor@20x20.svg b/resources/icons/expui/propertyEditor@20x20.svg new file mode 100644 index 0000000000..668bd3d6fa --- /dev/null +++ b/resources/icons/expui/propertyEditor@20x20.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/resources/icons/expui/propertyEditor@20x20_dark.svg b/resources/icons/expui/propertyEditor@20x20_dark.svg new file mode 100644 index 0000000000..348db038eb --- /dev/null +++ b/resources/icons/expui/propertyEditor@20x20_dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/icons/expui/propertyEditor_dark.svg b/resources/icons/expui/propertyEditor_dark.svg new file mode 100644 index 0000000000..6174df18b5 --- /dev/null +++ b/resources/icons/expui/propertyEditor_dark.svg @@ -0,0 +1,6 @@ + + + + + +