mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
IDEA-328256 Replace New UI Onboarding dialog banner with animation
WebM video is used here instead of suitable vector Lottie animation, because animation content is 3D, and it is unable to be rendered by vector graphics. GitOrigin-RevId: 72dddbdfcd7f332f59faf9f9121e28b4dd631a05
This commit is contained in:
committed by
intellij-monorepo-bot
parent
19e37ec9f7
commit
f2823a7d20
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 171 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 544 KiB |
@@ -4,16 +4,19 @@ package com.intellij.platform.ide.newUiOnboarding
|
||||
import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.openapi.util.IconLoader
|
||||
import com.intellij.openapi.util.text.HtmlChunk
|
||||
import com.intellij.ui.*
|
||||
import com.intellij.ui.dsl.builder.panel
|
||||
import com.intellij.ui.dsl.gridLayout.UnscaledGaps
|
||||
import com.intellij.ui.dsl.gridLayout.UnscaledGapsY
|
||||
import com.intellij.ui.jcef.JBCefBrowser
|
||||
import com.intellij.ui.scale.JBUIScale
|
||||
import com.intellij.util.ui.JBDimension
|
||||
import com.intellij.util.ui.JBFont
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import java.awt.Font
|
||||
import java.util.*
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JRootPane
|
||||
import javax.swing.border.Border
|
||||
@@ -29,15 +32,21 @@ class NewUiOnboardingDialog(project: Project)
|
||||
}
|
||||
|
||||
override fun createCenterPanel(): JComponent {
|
||||
// todo: replace with separate banner when it will be ready
|
||||
val banner = IconLoader.getIcon("newUiOnboarding/banner.png", NewUiOnboardingDialog::class.java.classLoader)
|
||||
val videoSize = JBDimension(384, 242)
|
||||
val contentGaps = UnscaledGaps(28, 32, 22, 32)
|
||||
|
||||
val panel = panel {
|
||||
row {
|
||||
icon(banner)
|
||||
val videoBase64 = readVideoAsBase64()
|
||||
val browser = JBCefBrowser.createBuilder().setMouseWheelEventEnable(false).build()
|
||||
val pageHtml = createVideoHtmlPage(videoBase64)
|
||||
browser.loadHTML(pageHtml)
|
||||
cell(browser.component)
|
||||
.customize(UnscaledGaps.EMPTY)
|
||||
.applyToComponent { WindowMoveListener(this).installTo(this) }
|
||||
.applyToComponent {
|
||||
WindowMoveListener(this).installTo(components?.firstOrNull() ?: this)
|
||||
preferredSize = videoSize
|
||||
}
|
||||
}
|
||||
panel {
|
||||
row {
|
||||
@@ -48,7 +57,7 @@ class NewUiOnboardingDialog(project: Project)
|
||||
}
|
||||
}
|
||||
row {
|
||||
val maxWidth = banner.iconWidth - JBUI.scale(contentGaps.width)
|
||||
val maxWidth = videoSize.width - JBUI.scale(contentGaps.width)
|
||||
val charWidth = window.getFontMetrics(JBFont.label()).charWidth('0')
|
||||
val maxLineLength = maxWidth / charWidth
|
||||
text(NewUiOnboardingBundle.message("dialog.text"), maxLineLength)
|
||||
@@ -78,4 +87,35 @@ class NewUiOnboardingDialog(project: Project)
|
||||
}
|
||||
|
||||
override fun createContentPaneBorder(): Border? = null
|
||||
|
||||
private fun createVideoHtmlPage(videoBase64: String): String {
|
||||
val componentId = "video"
|
||||
val head = HtmlChunk.head().child(LottieUtils.getSingleContentCssStyles(Gray._32, componentId))
|
||||
|
||||
val videoTag = HtmlChunk.tag("video")
|
||||
.attr("id", componentId)
|
||||
.attr("autoplay")
|
||||
.attr("loop")
|
||||
.attr("muted")
|
||||
.child(HtmlChunk.tag("source")
|
||||
.attr("type", "video/webm")
|
||||
.attr("src", "data:video/webm;base64,$videoBase64"))
|
||||
val body = HtmlChunk.body().child(videoTag)
|
||||
|
||||
return HtmlChunk.html()
|
||||
.child(head)
|
||||
.child(body)
|
||||
.toString()
|
||||
}
|
||||
|
||||
private fun readVideoAsBase64(): String {
|
||||
val url = NewUiOnboardingDialog::class.java.classLoader.getResource(VIDEO_PATH)
|
||||
?: error("Failed to find file by path: $VIDEO_PATH")
|
||||
val videoBytes = url.readBytes()
|
||||
return Base64.getEncoder().encodeToString(videoBytes)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val VIDEO_PATH: String = "newUiOnboarding/DialogVideo.webm"
|
||||
}
|
||||
}
|
||||
@@ -16,26 +16,10 @@ import java.awt.Dimension
|
||||
object LottieUtils {
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
fun createLottieAnimationPage(lottieJson: String, lottieScript: String? = null, background: Color): String {
|
||||
val head = HtmlBuilder().append(
|
||||
HtmlChunk.tag("style").addRaw("""
|
||||
body {
|
||||
background-color: #${ColorUtil.toHex(background)};
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
#lottie {
|
||||
background-color: #${ColorUtil.toHex(background)};
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
transform: translate3d(0,0,0);
|
||||
text-align: center;
|
||||
opacity: 1;
|
||||
}
|
||||
""".trimIndent())
|
||||
).wrapWith(HtmlChunk.head())
|
||||
val componentId = "lottie"
|
||||
val head = HtmlBuilder()
|
||||
.append(getSingleContentCssStyles(background, componentId))
|
||||
.wrapWith(HtmlChunk.head())
|
||||
|
||||
val script = if (lottieScript != null) {
|
||||
HtmlChunk.tag("script").addRaw(lottieScript)
|
||||
@@ -44,7 +28,7 @@ object LottieUtils {
|
||||
|
||||
val body = HtmlBuilder()
|
||||
.append(script)
|
||||
.append(HtmlChunk.div().attr("id", "lottie").addRaw(""))
|
||||
.append(HtmlChunk.div().attr("id", componentId).addRaw(""))
|
||||
.append(HtmlChunk.tag("script").addRaw("""
|
||||
const animationData = $lottieJson;
|
||||
const params = {
|
||||
@@ -65,6 +49,28 @@ object LottieUtils {
|
||||
.toString()
|
||||
}
|
||||
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
fun getSingleContentCssStyles(background: Color, componentId: String): HtmlChunk {
|
||||
return HtmlChunk.tag("style").addRaw("""
|
||||
body {
|
||||
background-color: #${ColorUtil.toHex(background)};
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
#${componentId} {
|
||||
background-color: #${ColorUtil.toHex(background)};
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
transform: translate3d(0,0,0);
|
||||
text-align: center;
|
||||
opacity: 1;
|
||||
}
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Throws(SerializationException::class)
|
||||
fun getLottieImageSize(lottieJson: String): Dimension {
|
||||
val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
@@ -125,7 +125,10 @@ public abstract class HtmlChunk {
|
||||
public void appendTo(@NotNull StringBuilder builder) {
|
||||
builder.append('<').append(myTagName);
|
||||
myAttributes.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> {
|
||||
builder.append(' ').append(entry.getKey()).append("=\"").append(StringUtil.escapeXmlEntities(entry.getValue())).append('"');
|
||||
builder.append(' ').append(entry.getKey());
|
||||
if (entry.getValue() != null) {
|
||||
builder.append("=\"").append(StringUtil.escapeXmlEntities(entry.getValue())).append('"');
|
||||
}
|
||||
});
|
||||
if (myChildren.isEmpty()) {
|
||||
builder.append("/>");
|
||||
@@ -158,6 +161,17 @@ public abstract class HtmlChunk {
|
||||
return new Element(myTagName, myAttributes.with(name, Integer.toString(value)), myChildren);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute without '=' sign and a value
|
||||
*
|
||||
* @param name attribute name
|
||||
* @return a new element that is like this element but has the specified attribute added or replaced
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public @NotNull Element attr(@NonNls String name) {
|
||||
return new Element(myTagName, myAttributes.with(name, null), myChildren);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param style CSS style specification
|
||||
* @return a new element that is like this element but has the specified style added or replaced
|
||||
|
||||
Reference in New Issue
Block a user