package org.intellij.plugins.markdown.ui.preview.lobo; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.ui.JBColor; import com.intellij.util.Range; import com.intellij.util.ui.JBUI; import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lobobrowser.html.HtmlRendererContext; import org.lobobrowser.html.UserAgentContext; import org.lobobrowser.html.dombl.NodeVisitor; import org.lobobrowser.html.domimpl.DOMNodeImpl; import org.lobobrowser.html.gui.HtmlBlockPanel; import org.lobobrowser.html.gui.HtmlPanel; import org.lobobrowser.html.renderer.RBlock; import org.lobobrowser.html.renderer.RBlockViewport; import org.w3c.dom.Node; import javax.swing.*; import java.awt.*; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.io.IOException; import java.net.URI; import java.net.URL; final class LoboHtmlPanel extends MarkdownHtmlPanel { private static final int FOCUS_ELEMENT_DY = 100; @NotNull private final HtmlPanel myPanel; @NotNull private final MarkdownHtmlRendererContext myHtmlRendererContext; @NotNull private String myLastRenderedHtml = ""; @Nullable private String myCssInlineText = null; public LoboHtmlPanel() { myPanel = new HtmlPanel() { @Override protected HtmlBlockPanel createHtmlBlockPanel(UserAgentContext ucontext, HtmlRendererContext rcontext) { return new ScrollPreservingHtmlBlockPanel(JBColor.WHITE, true, ucontext, rcontext, this); } }; myHtmlRendererContext = new MarkdownHtmlRendererContext(myPanel); myPanel.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { adjustBrowserSize(); } }); } @Override @NotNull public JComponent getComponent() { return myPanel; } @Override public void setCSS(@Nullable String inlineCss, @NotNull String... fileUris) { myCssInlineText = StringUtil.notNullize(inlineCss); if (fileUris.length > 0 && fileUris[0] != null) { try { final URL url = URI.create(fileUris[0]).toURL(); final String cssText = StreamUtil.readText(url.openStream(), CharsetToolkit.UTF8); myCssInlineText = cssText + "\n" + myCssInlineText; myCssInlineText += "body {\n font-size: " + JBUI.scale(100) + "%;\n}"; } catch (IOException ignore) { } } } @Override public void setHtml(@NotNull String html) { myLastRenderedHtml = html; final String htmlToRender = html.replace("<head>", "<head>" + getCssLines(myCssInlineText)); myPanel.setHtml(htmlToRender, "file://a.html", myHtmlRendererContext); } @Override public void render() { setHtml(myLastRenderedHtml); } private void adjustBrowserSize() { } @Override public void scrollToMarkdownSrcOffset(final int offset) { ApplicationManager.getApplication().invokeAndWait(() -> { final DOMNodeImpl root = myPanel.getRootNode(); final Ref<Pair<Node, Integer>> resultY = new Ref<>(); root.visit(new NodeVisitor() { @Override public void visit(Node node) { Node child = node.getFirstChild(); while (child != null) { final Range<Integer> range = nodeToSrcRange(child); if (range != null && child instanceof DOMNodeImpl) { int currentDist = Math.min(Math.abs(range.getFrom() - offset), Math.abs(range.getTo() - 1 - offset)); if (resultY.get() == null || resultY.get().getSecond() > currentDist) { resultY.set(Pair.create(child, currentDist)); } } if (range == null || range.getTo() <= offset) { child = child.getNextSibling(); continue; } if (range.getFrom() > offset) { break; } if (range.getTo() > offset) { visit(child); break; } } } }); if (resultY.get() != null) { myPanel.scrollTo(resultY.get().getFirst()); final RBlockViewport viewport = ((RBlock)myPanel.getBlockRenderable()).getRBlockViewport(); final Rectangle renderBounds = myPanel.getBlockRenderable().getBounds(); if (viewport.getY() + viewport.getHeight() - renderBounds.getHeight() > 0) { myPanel.scrollBy(0, -FOCUS_ELEMENT_DY); } myPanel.repaint(); } }, ModalityState.NON_MODAL); } @Override public void dispose() { } }