Skip to content

Commit

Permalink
add support for style HTML tag
Browse files Browse the repository at this point in the history
  • Loading branch information
DeDiamondPro committed Jun 1, 2024
1 parent 1bfd92c commit 282bb50
Show file tree
Hide file tree
Showing 13 changed files with 1,879 additions and 25 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ plugins {
}

group = "dev.dediamondpro"
version = "1.1.0+localtest6"
version = "1.1.0+localtest15"

repositories {
mavenCentral()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import dev.dediamondpro.minemark.style.*
import gg.essential.elementa.font.DefaultFonts
import gg.essential.elementa.font.FontProvider
import java.awt.Color
import java.util.function.Function

data class MarkdownStyle @JvmOverloads constructor(
private val textStyle: MarkdownTextStyle = MarkdownTextStyle(
Expand Down Expand Up @@ -64,5 +65,9 @@ data class MarkdownStyle @JvmOverloads constructor(
}

class MarkdownTextStyle(
defaultFontSize: Float, defaultTextColor: Color?, padding: Float, val font: FontProvider
) : TextStyleConfig(defaultFontSize, defaultTextColor, padding)
defaultFontSize: Float,
defaultTextColor: Color?,
padding: Float,
val font: FontProvider,
adaptFontSize: Function<Float, Float> = Function { it / 16f }
) : TextStyleConfig(defaultFontSize, defaultTextColor, padding, adaptFontSize)
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@
import dev.dediamondpro.minemark.style.TextStyleConfig;

import java.awt.*;
import java.util.function.Function;

public class MarkdownTextStyle extends TextStyleConfig {
private final boolean hasShadow;

public MarkdownTextStyle(float defaultFontSize, Color defaultTextColor, float padding, boolean hasShadow, Function<Float, Float> adaptFontSize) {
super(defaultFontSize, defaultTextColor, padding, adaptFontSize);
this.hasShadow = hasShadow;
}

public MarkdownTextStyle(float defaultFontSize, Color defaultTextColor, float padding, boolean hasShadow) {
super(defaultFontSize, defaultTextColor, padding);
super(defaultFontSize, defaultTextColor, padding, (size) -> size / 16f);
this.hasShadow = hasShadow;
}

Expand Down
65 changes: 56 additions & 9 deletions src/main/java/dev/dediamondpro/minemark/MineMarkCoreBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import dev.dediamondpro.minemark.elements.impl.LinkElement;
import dev.dediamondpro.minemark.elements.impl.ParagraphElement;
import dev.dediamondpro.minemark.elements.impl.formatting.AlignmentElement;
import dev.dediamondpro.minemark.elements.impl.formatting.CssStyleElement;
import dev.dediamondpro.minemark.elements.impl.formatting.FormattingElement;
import dev.dediamondpro.minemark.elements.impl.list.ListHolderElement;
import dev.dediamondpro.minemark.elements.impl.table.TableHolderElement;
Expand Down Expand Up @@ -78,8 +79,8 @@ public MineMarkCoreBuilder<S, R> addElement(@NotNull List<String> tags, @NotNull
/**
* Add a supported element to be used
*
* @param tag HTML tags the element will be applied to
* @param element An {@link TagBasedElementCreator.BasicElementCreator} of that element
* @param tag HTML tags the element will be applied to
* @param element An {@link TagBasedElementCreator.BasicElementCreator} of that element
*/
public MineMarkCoreBuilder<S, R> addElement(@NotNull String tag, @NotNull TagBasedElementCreator.BasicElementCreator<S, R> element) {
return addElement(Collections.singletonList(tag), element);
Expand All @@ -95,6 +96,50 @@ public MineMarkCoreBuilder<S, R> addElement(@NotNull Elements elementName, @NotN
return addElement(elementName.tags, element);
}

/**
* Add a supported element to be used
*
* @param position The position the element should be added at
* @param element An {@link ElementCreator} of that element
*/
public MineMarkCoreBuilder<S, R> addElement(int position, @NotNull ElementCreator<S, R> element) {
this.elements.add(position, element);
return this;
}

/**
* Add a supported element to be used
*
* @param position The position the element should be added at
* @param tags HTML tags the element will be applied to
* @param element An {@link ElementCreator} of that element
*/
public MineMarkCoreBuilder<S, R> addElement(int position, @NotNull List<String> tags, @NotNull TagBasedElementCreator.BasicElementCreator<S, R> element) {
return addElement(position, new TagBasedElementCreator<>(tags, element));
}

/**
* Add a supported element to be used
*
* @param position The position the element should be added at
* @param tag HTML tags the element will be applied to
* @param element An {@link TagBasedElementCreator.BasicElementCreator} of that element
*/
public MineMarkCoreBuilder<S, R> addElement(int position, @NotNull String tag, @NotNull TagBasedElementCreator.BasicElementCreator<S, R> element) {
return addElement(position, Collections.singletonList(tag), element);
}

/**
* Add a supported element to be used
*
* @param position The position the element should be added at
* @param elementName HTML tags the element will be applied to
* @param element An {@link TagBasedElementCreator.BasicElementCreator} of that element
*/
public MineMarkCoreBuilder<S, R> addElement(int position, @NotNull Elements elementName, @NotNull TagBasedElementCreator.BasicElementCreator<S, R> element) {
return addElement(position, elementName.tags, element);
}

/**
* Add supported elements to be used
*
Expand Down Expand Up @@ -143,13 +188,15 @@ public MineMarkCore<S, R> build() {
throw new IllegalArgumentException("A text element has to be provided by using \"setTextElement(textElement\"");
}
if (withDefaultElements) {
addElement(Elements.PARAGRAPH, ParagraphElement::new);
addElement(Elements.FORMATTING, FormattingElement::new);
addElement(Elements.LINK, LinkElement::new);
addElement(Elements.LIST_PARENT, ListHolderElement::new);
addElement(Elements.TABLE, TableHolderElement::new);
addElement(Elements.TABLE_ROW, TableRowElement::new);
addElement(new AlignmentElement.AlignmentElementCreator<>());
// Add default (formatting) elements, these are added first so they take priority and their formatting applies properly
addElement(0, Elements.PARAGRAPH, ParagraphElement::new);
addElement(0, Elements.FORMATTING, FormattingElement::new);
addElement(0, Elements.LINK, LinkElement::new);
addElement(0, Elements.LIST_PARENT, ListHolderElement::new);
addElement(0, Elements.TABLE, TableHolderElement::new);
addElement(0, Elements.TABLE_ROW, TableRowElement::new);
addElement(0, new AlignmentElement.AlignmentElementCreator<>());
addElement(0, new CssStyleElement.CssStyleElementCreator<>());
}
return new MineMarkCore<>(textElement, elements, extensions, urlSanitizer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void startElement(String uri, String localName, String qName, Attributes
}
addText();
Element<S, R> newElement = createElement(style, currentElement.getLayoutStyle(), currentElement, qName, attributes);
currentElement = newElement != null ? newElement : new EmptyElement<>(style, layoutStyle, currentElement, qName, attributes);
currentElement = newElement != null ? newElement : new EmptyElement<>(style, currentElement.getLayoutStyle(), currentElement, qName, attributes);
}

@Override
Expand Down Expand Up @@ -140,9 +140,9 @@ private Element<S, R> createElement(S style, LayoutStyle layoutStyle, @NotNull E

if (multipleElement == null) {
multipleElement = new SingleTagMultiElement<>(style, layoutStyle, parent, qName, attributes);
multipleElement.addElement(elementCreator, style, layoutStyle, qName, attributes);
multipleElement.addElement(elementCreator);
}
multipleElement.addElement(element, style, layoutStyle, qName, attributes);
multipleElement.addElement(element);

}
return multipleElement != null ? multipleElement : elementCreator != null ? elementCreator.createElement(style, layoutStyle, parent, qName, attributes) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@ public void generateLayout(LayoutData layoutData, R renderData) {
float width = getWidth(layoutData, renderData);
float height = getHeight(layoutData, renderData);
float padding = getPadding(layoutData, renderData);
if ((!(this instanceof Inline) && layoutData.isLineOccupied()) || layoutData.getX() + width > layoutData.getMaxWidth()) {
boolean inline = this instanceof Inline && (((Inline) this).isInline());
if ((!inline && layoutData.isLineOccupied()) || layoutData.getX() + width > layoutData.getMaxWidth()) {
layoutData.nextLine();
}
layoutData.updatePadding(padding);
position = layoutData.addElement(layoutStyle.getAlignment(), width, height);
if (!(this instanceof Inline) && layoutData.isLineOccupied()) {
if (!inline && layoutData.isLineOccupied()) {
layoutData.nextLine();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public ChildBasedElement(@NotNull S style, @NotNull LayoutStyle layoutStyle, @Nu
@Override
@ApiStatus.Internal
public void generateLayout(LayoutData layoutData, R renderData) {
if (!(this instanceof Inline) && layoutData.isLineOccupied()) {
boolean inline = this instanceof Inline && (((Inline) this).isInline());
if (!inline && layoutData.isLineOccupied()) {
layoutData.nextLine();
}
float padding = getPadding(layoutData, renderData);
Expand All @@ -42,7 +43,7 @@ public void generateLayout(LayoutData layoutData, R renderData) {
child.generateLayout(layoutData, renderData);
}
layoutData.updateBottomSpacing(padding);
if (!(this instanceof Inline)) {
if (!inline) {
layoutData.nextLine();
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/dev/dediamondpro/minemark/elements/Inline.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@


public interface Inline {
default boolean isInline() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,21 @@ public void generateLayout(LayoutData layoutData, R renderData) {
@Override
public @NotNull ArrayList<Element<S, R>> getChildren() {
// We return the children of the deepest child so all new elements are added to the deepest child,
return deepestChildElement.children;
if (deepestChildElement == this) return children;
return deepestChildElement.getChildren();
}

public void addElement(@NotNull ElementCreator<S, R> elementCreator, S style, LayoutStyle layoutStyle, @NotNull String qName, @Nullable Attributes attributes) {
deepestChildElement = elementCreator.createElement(style, layoutStyle, deepestChildElement, qName, attributes);
@Override
public LayoutStyle getLayoutStyle() {
if (deepestChildElement == this) return layoutStyle;
return deepestChildElement.getLayoutStyle();
}

public void addElement(@NotNull ElementCreator<S, R> elementCreator) {
assert parent != null;
Element<S, R> newElement = elementCreator.createElement(style, getLayoutStyle(), parent, qName, attributes);
parent.getChildren().remove(newElement);
deepestChildElement.getChildren().add(newElement);
deepestChildElement = newElement;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ public LinkElement(@NotNull S style, @NotNull LayoutStyle layoutStyle, @Nullable
this.link = attributes != null ? attributes.getValue("href") : null;
if (link != null) {
this.layoutStyle = this.layoutStyle.clone();
this.layoutStyle.setTextColor(style.getLinkStyle().getTextColor());
this.layoutStyle.setPartOfLink(true);
// Only change the text color if no custom color is active
if (style.getTextStyle().getDefaultTextColor().equals(this.layoutStyle.getTextColor())) {
this.layoutStyle.setTextColor(style.getLinkStyle().getTextColor());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* This file is part of MineMark
* Copyright (C) 2024 DeDiamondPro
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License Version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package dev.dediamondpro.minemark.elements.impl.formatting;

import dev.dediamondpro.minemark.LayoutStyle;
import dev.dediamondpro.minemark.elements.ChildBasedElement;
import dev.dediamondpro.minemark.elements.Element;
import dev.dediamondpro.minemark.elements.Inline;
import dev.dediamondpro.minemark.elements.creators.ElementCreator;
import dev.dediamondpro.minemark.style.Style;
import dev.dediamondpro.minemark.utils.ColorFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.xml.sax.Attributes;

public class CssStyleElement<S extends Style, R> extends ChildBasedElement<S, R> implements Inline {
private boolean isInline = true;

public CssStyleElement(@NotNull S style, @NotNull LayoutStyle layoutStyle, @Nullable Element<S, R> parent, @NotNull String qName, @Nullable Attributes attributes) {
super(style, layoutStyle, parent, qName, attributes);
assert attributes != null;
String inlineCss = attributes.getValue("style");
if (inlineCss == null) return;
this.layoutStyle = this.layoutStyle.clone();
String[] parts = inlineCss.split(";");
for (String part : parts) {
String[] propertyValue = part.split(":");
if (propertyValue.length != 2) continue;
String property = propertyValue[0].trim();
String value = propertyValue[1].trim();
switch (property) {
case "color":
this.layoutStyle.setTextColor(ColorFactory.web(value));
break;
case "text-align":
switch (value.toLowerCase()) {
case "left":
this.layoutStyle.setAlignment(LayoutStyle.Alignment.LEFT);
isInline = false;
break;
case "center":
this.layoutStyle.setAlignment(LayoutStyle.Alignment.CENTER);
isInline = false;
break;
case "right":
this.layoutStyle.setAlignment(LayoutStyle.Alignment.RIGHT);
isInline = false;
break;
}
break;
case "text-decoration":
for (String decoration : value.split(" ")) {
switch (decoration) {
case "underline":
this.layoutStyle.setUnderlined(true);
break;
case "line-through":
this.layoutStyle.setStrikethrough(true);
break;
}
}
break;
case "font-size":
Float fontSize = null;
if (value.endsWith("px")) {
fontSize = style.getTextStyle().adaptFontSize(Float.parseFloat(value.substring(0, value.length() - 2)));
} else if (value.endsWith("%")) {
fontSize = layoutStyle.getFontSize() * (Float.parseFloat(value.substring(0, value.length() - 1)) / 100);
}
if (fontSize != null) {
this.layoutStyle.setFontSize(fontSize);
}
break;
}
}
}

@Override
public boolean isInline() {
return isInline;
}

@Override
public String toString() {
return "CssStyleElement {" + attributes.getValue("style") + "}";
}

public static class CssStyleElementCreator<S extends Style, R> implements ElementCreator<S, R> {

@Override
public Element<S, R> createElement(S style, LayoutStyle layoutStyle, @NotNull Element<S, R> parent, @NotNull String qName, @NotNull Attributes attributes) {
return new CssStyleElement<>(style, layoutStyle, parent, qName, attributes);
}

@Override
public boolean appliesTo(S style, LayoutStyle layoutStyle, @NotNull Element<S, R> parent, @NotNull String qName, @NotNull Attributes attributes) {
return attributes.getValue("style") != null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,26 @@
package dev.dediamondpro.minemark.style;

import java.awt.*;
import java.util.function.Function;

public class TextStyleConfig {
private final float defaultFontSize;
private final Color defaultTextColor;
private final float padding;
/**
* Function used to adapt font size from px to the unit the rendering implementation uses
*/
private final Function<Float, Float> adaptFontSize;

public TextStyleConfig(float defaultFontSize, Color defaultTextColor, float padding) {
public TextStyleConfig(float defaultFontSize, Color defaultTextColor, float padding, Function<Float, Float> adaptFontSize) {
this.defaultFontSize = defaultFontSize;
this.defaultTextColor = defaultTextColor;
this.padding = padding;
this.adaptFontSize = adaptFontSize;
}

public TextStyleConfig(float defaultFontSize, Color defaultTextColor, float padding) {
this(defaultFontSize, defaultTextColor, padding, (size) -> size);
}

public float getDefaultFontSize() {
Expand All @@ -41,4 +51,8 @@ public Color getDefaultTextColor() {
public float getPadding() {
return padding;
}

public float adaptFontSize(float size) {
return adaptFontSize.apply(size);
}
}
Loading

0 comments on commit 282bb50

Please sign in to comment.