mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[markdown] restore ability to use JavaFX WebView to preview markdown files (IDEA-243740)
This commit reverts 26e1b1296d. GitOrigin-RevId: c78b65d09f45ab17e7a6554458c799931e6f7198
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9012b5af3f
commit
4fb4d7bb64
@@ -122,6 +122,7 @@ Includes the following features:</p>
|
||||
</applicationListeners>
|
||||
|
||||
<extensions defaultExtensionNs="org.intellij.markdown">
|
||||
<html.panel.provider implementation="org.intellij.plugins.markdown.ui.preview.javafx.JavaFxHtmlPanelProvider" />
|
||||
<html.panel.provider implementation="org.intellij.plugins.markdown.ui.preview.jcef.JCEFHtmlPanelProvider" />
|
||||
<fenceLanguageProvider implementation="org.intellij.plugins.markdown.extensions.plantuml.PlantUMLCodeFenceLanguageProvider"/>
|
||||
<codeFencePluginGeneratingProvider implementation="org.intellij.plugins.markdown.extensions.plantuml.PlantUMLCodeGeneratingProvider"/>
|
||||
|
||||
@@ -72,6 +72,7 @@ markdown.cannot.resolve.anchor.in.file.error.message=Cannot resolve anchor in fi
|
||||
|
||||
markdown.navigate.to.header=Select Header To Navigate To
|
||||
markdown.navigate.to.header.no.headers=Cannot find header to navigate to
|
||||
markdown.navigate.to.header.group=Markdown headers group
|
||||
markdown.settings.preview.plantUML.download=Install
|
||||
markdown.settings.preview.plantUML.installed=PlantUML framework is installed and ready to use.
|
||||
markdown.settings.preview.plantUML.download.success=PlantUML has been successfully installed!
|
||||
@@ -122,4 +123,5 @@ action.org.intellij.plugins.markdown.ui.actions.styling.ToggleBoldAction.text=To
|
||||
action.org.intellij.plugins.markdown.ui.actions.styling.ToggleBoldAction.description=Toggles bold mode on caret/selection
|
||||
group.Markdown.Toolbar.Left.text=Markdown Editor Actions
|
||||
dialog.message.uri.parsing.reports.error=URI ''{0}'' parsing reports the error: {1}
|
||||
dialog.message.cannot.set.preview.panel.provider=Cannot set preview panel provider ({0}):\n{1}
|
||||
dialog.message.tried.to.use.preview.panel.provider=Tried to use preview panel provider ({0}), but it is unavailable. Reverting to default.
|
||||
+2
-1
@@ -5,6 +5,7 @@ import com.intellij.util.xmlb.annotations.Attribute;
|
||||
import com.intellij.util.xmlb.annotations.Property;
|
||||
import com.intellij.util.xmlb.annotations.Tag;
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanelProvider;
|
||||
import org.intellij.plugins.markdown.ui.preview.javafx.JavaFxHtmlPanelProvider;
|
||||
import org.intellij.plugins.markdown.ui.preview.jcef.JCEFHtmlPanelProvider;
|
||||
import org.intellij.plugins.markdown.ui.split.SplitFileEditor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -20,7 +21,7 @@ public final class MarkdownPreviewSettings {
|
||||
@Property(surroundWithTag = false)
|
||||
@NotNull
|
||||
private MarkdownHtmlPanelProvider.ProviderInfo myHtmlPanelProviderInfo =
|
||||
JBCefApp.isSupported() ? new JCEFHtmlPanelProvider().getProviderInfo() : new MarkdownHtmlPanelProvider.ProviderInfo("Unavailable", "Unavailable");
|
||||
JBCefApp.isSupported() ? new JCEFHtmlPanelProvider().getProviderInfo() : new JavaFxHtmlPanelProvider().getProviderInfo();
|
||||
|
||||
@Attribute("UseGrayscaleRendering")
|
||||
private boolean myUseGrayscaleRendering = true;
|
||||
|
||||
+7
-5
@@ -351,10 +351,12 @@ public class MarkdownSettingsForm implements MarkdownCssSettings.Holder, Markdow
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static MarkdownHtmlPanelProvider getDefaultProvider() {
|
||||
MarkdownHtmlPanelProvider[] providers = MarkdownHtmlPanelProvider.getProviders();
|
||||
if (providers.length > 0) return providers[0];
|
||||
throw new RuntimeException("No providers are defined");
|
||||
private static MarkdownHtmlPanelProvider getProvider(@SuppressWarnings("SameParameterValue") @NotNull String providerClass) {
|
||||
for (MarkdownHtmlPanelProvider provider : MarkdownHtmlPanelProvider.getProviders()) {
|
||||
if (isProviderOf(provider.getProviderInfo(), providerClass)) return provider;
|
||||
}
|
||||
|
||||
throw new RuntimeException("Cannot find " + providerClass);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -363,7 +365,7 @@ public class MarkdownSettingsForm implements MarkdownCssSettings.Holder, Markdow
|
||||
return Objects.requireNonNull(myPreviewPanelModel.getSelected());
|
||||
}
|
||||
else {
|
||||
return getDefaultProvider().getProviderInfo();
|
||||
return getProvider(JAVA_FX_HTML_PANEL_PROVIDER).getProviderInfo();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-5
@@ -13,8 +13,12 @@
|
||||
// limitations under the License.
|
||||
package org.intellij.plugins.markdown.ui.preview;
|
||||
|
||||
import com.intellij.CommonBundle;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.util.xmlb.annotations.Attribute;
|
||||
import org.intellij.plugins.markdown.MarkdownBundle;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -40,12 +44,18 @@ public abstract class MarkdownHtmlPanelProvider {
|
||||
|
||||
@NotNull
|
||||
public static MarkdownHtmlPanelProvider createFromInfo(@NotNull ProviderInfo providerInfo) {
|
||||
for (MarkdownHtmlPanelProvider provider : getProviders()) {
|
||||
if (provider.getProviderInfo().getClassName().equals(providerInfo.getClassName())) {
|
||||
return provider;
|
||||
}
|
||||
try {
|
||||
return ((MarkdownHtmlPanelProvider)Class.forName(providerInfo.getClassName()).newInstance());
|
||||
}
|
||||
catch (Exception e) {
|
||||
Messages.showMessageDialog(
|
||||
MarkdownBundle.message("dialog.message.cannot.set.preview.panel.provider", providerInfo.getName(), e.getMessage()),
|
||||
CommonBundle.getErrorTitle(),
|
||||
Messages.getErrorIcon()
|
||||
);
|
||||
Logger.getInstance(MarkdownHtmlPanelProvider.class).error(e);
|
||||
return getProviders()[0];
|
||||
}
|
||||
return getProviders()[0];
|
||||
}
|
||||
|
||||
public static boolean hasAvailableProviders() {
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import org.intellij.plugins.markdown.settings.MarkdownCssSettings;
|
||||
import org.intellij.plugins.markdown.ui.preview.jcef.MarkdownJCEFHtmlPanel;
|
||||
import org.intellij.plugins.markdown.ui.preview.javafx.MarkdownJavaFxHtmlPanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.ide.BuiltInServerManager;
|
||||
@@ -94,7 +94,7 @@ public class PreviewStaticServer extends HttpRequestHandler {
|
||||
if ("scripts".equals(contentType) && MarkdownHtmlPanel.SCRIPTS.contains(fileName)) {
|
||||
sendResource(request,
|
||||
context.channel(),
|
||||
MarkdownJCEFHtmlPanel.class,
|
||||
MarkdownJavaFxHtmlPanel.class,
|
||||
fileName);
|
||||
}
|
||||
else if ("styles".equals(contentType) && MarkdownHtmlPanel.STYLES.contains(fileName)) {
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.intellij.plugins.markdown.ui.preview.javafx;
|
||||
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanel;
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanelProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class JavaFxHtmlPanelProvider extends MarkdownHtmlPanelProvider {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MarkdownHtmlPanel createHtmlPanel() {
|
||||
return new MarkdownJavaFxHtmlPanel();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public AvailabilityInfo isAvailable() {
|
||||
try {
|
||||
if (Class.forName("javafx.scene.web.WebView", false, getClass().getClassLoader()) != null) {
|
||||
return AvailabilityInfo.AVAILABLE;
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
|
||||
return AvailabilityInfo.UNAVAILABLE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ProviderInfo getProviderInfo() {
|
||||
return new ProviderInfo("JavaFX WebView", JavaFxHtmlPanelProvider.class.getName());
|
||||
}
|
||||
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.intellij.plugins.markdown.ui.preview.javafx;
|
||||
|
||||
import com.intellij.notification.NotificationGroup;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.NotNullLazyValue;
|
||||
import com.intellij.openapi.wm.ToolWindowId;
|
||||
import com.intellij.ui.javafx.JavaFxHtmlPanel;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.concurrent.Worker.State;
|
||||
import javafx.scene.text.FontSmoothingType;
|
||||
import javafx.scene.web.WebEngine;
|
||||
import javafx.scene.web.WebView;
|
||||
import netscape.javascript.JSObject;
|
||||
import org.intellij.markdown.html.HtmlGenerator;
|
||||
import org.intellij.plugins.markdown.MarkdownBundle;
|
||||
import org.intellij.plugins.markdown.settings.MarkdownApplicationSettings;
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanel;
|
||||
import org.intellij.plugins.markdown.ui.preview.PreviewStaticServer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class MarkdownJavaFxHtmlPanel extends JavaFxHtmlPanel implements MarkdownHtmlPanel {
|
||||
|
||||
private static final NotNullLazyValue<String> MY_SCRIPTING_LINES = new NotNullLazyValue<String>() {
|
||||
@NotNull
|
||||
@Override
|
||||
protected String compute() {
|
||||
return SCRIPTS.stream()
|
||||
.map(s -> "<script src=\"" + PreviewStaticServer.getScriptUrl(s) + "\"></script>")
|
||||
.reduce((s, s2) -> s + "\n" + s2)
|
||||
.orElseGet(String::new);
|
||||
}
|
||||
};
|
||||
|
||||
private String @NotNull [] myCssUris = ArrayUtilRt.EMPTY_STRING_ARRAY;
|
||||
@NotNull
|
||||
private String myCSP = "";
|
||||
@NotNull
|
||||
private String myLastRawHtml = "";
|
||||
@NotNull
|
||||
private final ScrollPreservingListener myScrollPreservingListener = new ScrollPreservingListener();
|
||||
@NotNull
|
||||
private final BridgeSettingListener myBridgeSettingListener = new BridgeSettingListener();
|
||||
|
||||
public MarkdownJavaFxHtmlPanel() {
|
||||
super();
|
||||
runInPlatformWhenAvailable(() -> {
|
||||
if (myWebView != null) {
|
||||
updateFontSmoothingType(myWebView, MarkdownApplicationSettings.getInstance().getMarkdownPreviewSettings().isUseGrayscaleRendering());
|
||||
}
|
||||
});
|
||||
|
||||
subscribeForGrayscaleSetting();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerListeners(@NotNull WebEngine engine) {
|
||||
engine.getLoadWorker().stateProperty().addListener(myBridgeSettingListener);
|
||||
engine.getLoadWorker().stateProperty().addListener(myScrollPreservingListener);
|
||||
}
|
||||
|
||||
private void subscribeForGrayscaleSetting() {
|
||||
MessageBusConnection settingsConnection = ApplicationManager.getApplication().getMessageBus().connect(this);
|
||||
MarkdownApplicationSettings.SettingsChangedListener settingsChangedListener =
|
||||
new MarkdownApplicationSettings.SettingsChangedListener() {
|
||||
@Override
|
||||
public void beforeSettingsChanged(@NotNull final MarkdownApplicationSettings settings) {
|
||||
runInPlatformWhenAvailable(() -> {
|
||||
if (myWebView != null) {
|
||||
updateFontSmoothingType(myWebView, settings.getMarkdownPreviewSettings().isUseGrayscaleRendering());
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
settingsConnection.subscribe(MarkdownApplicationSettings.SettingsChangedListener.TOPIC, settingsChangedListener);
|
||||
}
|
||||
|
||||
private static void updateFontSmoothingType(@NotNull WebView view, boolean isGrayscale) {
|
||||
final FontSmoothingType typeToSet;
|
||||
if (isGrayscale) {
|
||||
typeToSet = FontSmoothingType.GRAY;
|
||||
}
|
||||
else {
|
||||
typeToSet = FontSmoothingType.LCD;
|
||||
}
|
||||
view.fontSmoothingTypeProperty().setValue(typeToSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHtml(@NotNull String html) {
|
||||
myLastRawHtml = html;
|
||||
super.setHtml(html);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String prepareHtml(@NotNull String html) {
|
||||
return ImageRefreshFix.setStamps(html
|
||||
.replace("<head>", "<head>"
|
||||
+ "<meta http-equiv=\"Content-Security-Policy\" content=\"" + myCSP + "\"/>"
|
||||
+ MarkdownHtmlPanel.getCssLines(null, myCssUris) + "\n" + getScriptingLines()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCSS(@Nullable String inlineCss, String @NotNull ... fileUris) {
|
||||
PreviewStaticServer.getInstance().setInlineStyle(inlineCss);
|
||||
myCssUris = inlineCss == null ? fileUris
|
||||
: ArrayUtil
|
||||
.mergeArrays(fileUris, PreviewStaticServer.getStyleUrl(PreviewStaticServer.INLINE_CSS_FILENAME));
|
||||
myCSP = PreviewStaticServer.createCSP(ContainerUtil.map(SCRIPTS, s -> PreviewStaticServer.getScriptUrl(s)),
|
||||
ContainerUtil.concat(
|
||||
ContainerUtil.map(STYLES, s -> PreviewStaticServer.getStyleUrl(s)),
|
||||
ContainerUtil.filter(fileUris, s -> s.startsWith("http://") || s.startsWith("https://"))
|
||||
));
|
||||
setHtml(myLastRawHtml);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollToMarkdownSrcOffset(final int offset) {
|
||||
runInPlatformWhenAvailable(() -> {
|
||||
getWebViewGuaranteed().getEngine().executeScript(
|
||||
"if ('__IntelliJTools' in window) " +
|
||||
"__IntelliJTools.scrollToOffset(" + offset + ", '" + HtmlGenerator.Companion.getSRC_ATTRIBUTE_NAME() + "');"
|
||||
);
|
||||
final Object result = getWebViewGuaranteed().getEngine().executeScript(
|
||||
"document.documentElement.scrollTop || (document.body && document.body.scrollTop)");
|
||||
if (result instanceof Number) {
|
||||
myScrollPreservingListener.myScrollY = ((Number)result).intValue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
runInPlatformWhenAvailable(() -> {
|
||||
getWebViewGuaranteed().getEngine().getLoadWorker().stateProperty().removeListener(myScrollPreservingListener);
|
||||
getWebViewGuaranteed().getEngine().getLoadWorker().stateProperty().removeListener(myBridgeSettingListener);
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getScriptingLines() {
|
||||
return MY_SCRIPTING_LINES.getValue();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class JavaPanelBridge {
|
||||
static final JavaPanelBridge INSTANCE = new JavaPanelBridge();
|
||||
private static final NotificationGroup MARKDOWN_NOTIFICATION_GROUP = NotificationGroup
|
||||
.toolWindowGroup("Markdown headers group", ToolWindowId.MESSAGES_WINDOW, true,
|
||||
MarkdownBundle.message("markdown.navigate.to.header.group"));
|
||||
|
||||
public void openInExternalBrowser(@NotNull String link) {
|
||||
SafeOpener.openLink(link);
|
||||
}
|
||||
|
||||
public void log(@Nullable String text) {
|
||||
Logger.getInstance(JavaPanelBridge.class).warn(text);
|
||||
}
|
||||
}
|
||||
|
||||
private class BridgeSettingListener implements ChangeListener<State> {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
|
||||
JSObject win
|
||||
= (JSObject)getWebViewGuaranteed().getEngine().executeScript("window");
|
||||
win.setMember("JavaPanelBridge", JavaPanelBridge.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
private class ScrollPreservingListener implements ChangeListener<State> {
|
||||
volatile int myScrollY = 0;
|
||||
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
|
||||
if (newValue == State.RUNNING) {
|
||||
final Object result =
|
||||
getWebViewGuaranteed().getEngine().executeScript("document.documentElement.scrollTop || document.body.scrollTop");
|
||||
if (result instanceof Number) {
|
||||
myScrollY = ((Number)result).intValue();
|
||||
}
|
||||
}
|
||||
else if (newValue == State.SUCCEEDED) {
|
||||
getWebViewGuaranteed().getEngine()
|
||||
.executeScript("document.documentElement.scrollTop = ({} || document.body).scrollTop = " + myScrollY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user