Skip to content

Commit

Permalink
Improved JavaFX -> HTML background & border mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
salmonb committed Nov 13, 2023
1 parent c4ceeeb commit 429cc8e
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@ public final class HtmlButtonPeer
implements ButtonPeerMixin<N, NB, NM>, NoWrapWhiteSpacePeer {

public HtmlButtonPeer() {
this((NB) new ButtonPeerBase(), HtmlUtil.createButtonElement());
this((NB) new ButtonPeerBase(), HtmlUtil.createElement("fx-button"));
}

public HtmlButtonPeer(NB base, HTMLElement element) {
super(base, element);
prepareDomForAdditionalSkinChildren("fx-button");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,5 @@ public HtmlLabelPeer(NB base) {
// to have the text vertically centered, we need to set line-height: 100%
// Commented because we want space between lines when the text is wrapped. TODO: check if there is an effect on text vertically centered
// setElementStyleAttribute("line-height", "100%"); // 100% means node height = font height with no extra on top & bottom
subtractCssPaddingBorderWhenUpdatingSize = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,6 @@ public HtmlTextInputControlPeer(NB base, HTMLElement textInputElement) {
getNode().fireEvent(new ActionEvent());
return null;
};
/*
The behavior when setting the style width/height on a text input seems different from on other html elements.
On other html elements (ex: a button) this will size the outer visual box (including padding and border) to the
specified width/height. On a text input, this will size the inner visual box (excluding the padding and border).
*/
subtractCssPaddingBorderWhenUpdatingSize = true;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public final class HtmlLayoutPeer

public HtmlLayoutPeer(String tag) {
super((NB) new RegionPeerBase<N, NB, NM>(), HtmlUtil.createElement(tag));
subtractCssPaddingBorderWhenUpdatingSize = true;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,15 @@ public void updateAllNodeTransforms(List<Transform> allNodeTransforms) {
@Override
public void updateEffect(Effect effect) {
String boxShadow = toBoxShadow(effect);
CSSStyleDeclaration style = getElement().style;
CSSStyleDeclaration style = getEffectElement().style;
style.boxShadow = boxShadow;
super.updateEffect(boxShadow != null ? null : effect); // other effects managed by filter function
}

protected HTMLElement getEffectElement() {
return getElement();
}

private String toBoxShadow(Effect effect) {
String boxShadow = null;
if (effect instanceof InnerShadow) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,123 +25,51 @@ public abstract class HtmlRegionPeer
extends HtmlNodePeer<N, NB, NM>
implements RegionPeerMixin<N, NB, NM> {

protected boolean subtractCssPaddingBorderWhenUpdatingSize;
protected boolean subtractNodePaddingWhenUpdatingSize;
protected boolean subtractNodeBorderWhenUpdatingSize;
private final HTMLElement fxBackground = createBehindElement("fx-background");
private final HTMLElement fxBorder = createBehindElement("fx-border");

protected HtmlRegionPeer(NB base, HTMLElement element) {
super(base, element);
fxBorder.style.boxSizing = "border-box";
HTMLElement fxChildren = createBehindElement("fx-children");
setChildrenContainer(fxChildren);
HtmlUtil.setChildren(element, fxBackground, fxBorder, fxChildren);
}

private static HTMLElement createBehindElement(String tag) {
HTMLElement element = HtmlUtil.createElement(tag);
CSSStyleDeclaration style = element.style;
style.display = "block";
style.width = CSSProperties.WidthUnionType.of("100%");
style.height = CSSProperties.HeightUnionType.of("100%");
style.position = "absolute";
style.left = "0";
style.top = "0";
return element;
}

@Override
public void updateWidth(Number width) {
double w = width.doubleValue();
if (w >= 0) {
if (subtractCssPaddingBorderWhenUpdatingSize)
w -= computeCssPaddingBorderWidth();
else {
if (subtractNodePaddingWhenUpdatingSize)
w -= getNodePaddingWidth();
if (subtractNodeBorderWhenUpdatingSize)
w -= getNodeBorderWidth();
}
getElement().style.width = CSSProperties.WidthUnionType.of(toPx(w));
clearLayoutCache();
}
}

private double computeCssPaddingBorderWidth() {
CSSStyleDeclaration cs = HtmlUtil.getComputedStyle(getElement());
return sumPx(cs.paddingLeft, cs.paddingRight, cs.borderLeftWidth, cs.borderRightWidth);
}

private static double sumPx(Object... values) {
double result = 0;
for (Object value : values) {
String s = value.toString();
int i = s.indexOf("px");
if (i > 0)
result += Double.parseDouble(s.substring(0, i));
}
return result;
}

private double getNodePaddingWidth() {
N node = getNode();
Insets padding = node.getPadding();
return padding == null ? 0 : padding.getLeft() + padding.getRight();
}

private double getNodeBorderWidth() {
N node = getNode();
Border border = node.getBorder();
if (border != null) {
Insets insets = border.getInsets();
if (insets != null)
return insets.getLeft() + insets.getRight();
}
return 0;
}

@Override
public void updateHeight(Number height) {
double h = height.doubleValue();
if (h >= 0) {
if (subtractCssPaddingBorderWhenUpdatingSize)
h -= computeCssPaddingBorderHeight();
else {
if (subtractNodePaddingWhenUpdatingSize)
h -= getNodePaddingHeight();
if (subtractNodeBorderWhenUpdatingSize)
h -= getNodeBorderHeight();
}
getElement().style.height = CSSProperties.HeightUnionType.of(toPx(h));
clearLayoutCache();
}
}

private double computeCssPaddingBorderHeight() {
CSSStyleDeclaration cs = HtmlUtil.getComputedStyle(getElement());
return sumPx(cs.paddingTop, cs.paddingBottom, cs.borderTopWidth, cs.borderBottomWidth);
}

private double getNodePaddingHeight() {
N node = getNode();
Insets padding = node.getPadding();
return padding == null ? 0 : padding.getTop() + padding.getBottom();
}

private double getNodeBorderHeight() {
N node = getNode();
Border border = node.getBorder();
if (border != null) {
Insets insets = border.getInsets();
if (insets != null)
return insets.getTop() + insets.getBottom();
}
return 0;
}

private void updateWidthAndHeight() {
updateWidth(getNode().getWidth());
updateHeight(getNode().getHeight());
}

@Override
public void updateBackground(Background background) {
CSSStyleDeclaration style = getBackgroundBorderElement().style;
CSSStyleDeclaration style = getBackgroundElement().style;
style.background = toCssBackground(background);
// If a border is also set on the node, we take it as higher priority
if (getNode().getBorder() == null) { // so we apply the background border only if no other border is set on the node
applyBackgroundBorder(background, style);
}
}

protected HTMLElement getBackgroundBorderElement() {
return (HTMLElement) getContainer();
}

private void applyBackgroundBorder(Background background, CSSStyleDeclaration style) {
CornerRadii radii = null;
if (background != null) {
// Note: for now, we support only one border that we take from the first background fill
Expand All @@ -154,33 +82,41 @@ private void applyBackgroundBorder(Background background, CSSStyleDeclaration st
applyBorderRadii(radii, style);
}

protected HTMLElement getBackgroundElement() {
return fxBackground;
}

protected HTMLElement getBorderElement() {
return fxBorder;
}

@Override
protected HTMLElement getEffectElement() {
return fxBackground;
}

@Override
public void updateBorder(Border border) {
CSSStyleDeclaration style = getBackgroundBorderElement().style;
CSSStyleDeclaration style = getBorderElement().style;
// Note: for now, we support only one border that we take from the first border stroke
BorderStroke firstStroke = border == null ? null : Collections.get(border.getStrokes(), 0);
if (firstStroke != null) {
if (firstStroke == null) {
style.borderLeft = style.borderTop = style.borderRight = style.borderBottom = null;
} else {
BorderWidths widths = firstStroke.getWidths();
style.borderLeft = toCssBorder(firstStroke.getLeftStroke(), firstStroke.getLeftStyle(), widths.getLeft(), widths.isLeftAsPercentage());
style.borderTop = toCssBorder(firstStroke.getTopStroke(), firstStroke.getTopStyle(), widths.getTop(), widths.isTopAsPercentage());
style.borderRight = toCssBorder(firstStroke.getRightStroke(), firstStroke.getRightStyle(), widths.getRight(), widths.isRightAsPercentage());
style.borderLeft = toCssBorder(firstStroke.getLeftStroke(), firstStroke.getLeftStyle(), widths.getLeft(), widths.isLeftAsPercentage());
style.borderTop = toCssBorder(firstStroke.getTopStroke(), firstStroke.getTopStyle(), widths.getTop(), widths.isTopAsPercentage());
style.borderRight = toCssBorder(firstStroke.getRightStroke(), firstStroke.getRightStyle(), widths.getRight(), widths.isRightAsPercentage());
style.borderBottom = toCssBorder(firstStroke.getBottomStroke(), firstStroke.getBottomStyle(), widths.getBottom(), widths.isBottomAsPercentage());
applyBorderRadii(firstStroke.getRadii(), style);
} else {
style.borderLeft = style.borderTop = style.borderRight = style.borderBottom = null;
applyBackgroundBorder(getNode().getBackground(), style);
}
if (subtractCssPaddingBorderWhenUpdatingSize || subtractNodeBorderWhenUpdatingSize)
updateWidthAndHeight();
}

@Override
public void updatePadding(Insets padding) {
// Note: this code should be executed only if the html content is managed by the peer, otherwise it should be
// skipped (ex: css padding not needed for layout peers or controls with content provided by skin)
getElement().style.padding = toCssPadding(padding);
if (subtractCssPaddingBorderWhenUpdatingSize || subtractNodePaddingWhenUpdatingSize)
updateWidthAndHeight();
}

protected CSSProperties.PaddingUnionType toCssPadding(Insets padding) {
Expand All @@ -191,13 +127,14 @@ protected CSSProperties.PaddingUnionType toCssPadding(Insets padding) {
}

private void applyBorderRadii(CornerRadii radii, CSSStyleDeclaration style) {
if (radii != null) {
style.borderTopLeftRadius = CSSProperties.BorderTopLeftRadiusUnionType.of(toPx(Math.max(radii.getTopLeftHorizontalRadius(), radii.getTopLeftVerticalRadius())));
style.borderTopRightRadius = CSSProperties.BorderTopRightRadiusUnionType.of(toPx(Math.max(radii.getTopRightHorizontalRadius(), radii.getTopRightVerticalRadius())));
style.borderBottomRightRadius = CSSProperties.BorderBottomRightRadiusUnionType.of(toPx(Math.max(radii.getBottomRightHorizontalRadius(), radii.getBottomRightVerticalRadius())));
style.borderBottomLeftRadius = CSSProperties.BorderBottomLeftRadiusUnionType.of(toPx(Math.max(radii.getBottomLeftHorizontalRadius(), radii.getBottomLeftVerticalRadius())));
} else
if (radii == null)
style.borderRadius = null;
else {
style.borderTopLeftRadius = CSSProperties.BorderTopLeftRadiusUnionType.of( toPx(Math.max(radii.getTopLeftHorizontalRadius(), radii.getTopLeftVerticalRadius())));
style.borderTopRightRadius = CSSProperties.BorderTopRightRadiusUnionType.of( toPx(Math.max(radii.getTopRightHorizontalRadius(), radii.getTopRightVerticalRadius())));
style.borderBottomRightRadius = CSSProperties.BorderBottomRightRadiusUnionType.of(toPx(Math.max(radii.getBottomRightHorizontalRadius(), radii.getBottomRightVerticalRadius())));
style.borderBottomLeftRadius = CSSProperties.BorderBottomLeftRadiusUnionType.of( toPx(Math.max(radii.getBottomLeftHorizontalRadius(), radii.getBottomLeftVerticalRadius())));
}
}

private static String toCssBorder(Paint stroke, BorderStrokeStyle style, double width, boolean isPercentage) {
Expand Down

0 comments on commit 429cc8e

Please sign in to comment.