org.cobraparser.html.renderer.RUIControl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Cobra Show documentation
Show all versions of Cobra Show documentation
Cobra is the rendering engine designed for LoboBrowser
/*
GNU LESSER GENERAL PUBLIC LICENSE
Copyright (C) 2006 The Lobo Project
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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 library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Contact info: [email protected]
*/
/*
* Created on Apr 17, 2005
*/
package org.cobraparser.html.renderer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNull;
import org.cobraparser.html.domimpl.ModelNode;
import org.cobraparser.html.domimpl.NodeImpl;
import org.cobraparser.html.domimpl.UINode;
import org.cobraparser.html.style.RenderState;
import org.cobraparser.ua.UserAgentContext;
import cz.vutbr.web.css.CSSProperty.VerticalAlign;
/**
* @author J. H. S.
*/
class RUIControl extends BaseElementRenderable {
private static final int MAX_CACHE_SIZE = 10;
public final UIControl widget;
private final FrameContext frameContext;
public RUIControl(final ModelNode me, final UIControl widget, final RenderableContainer container, final FrameContext frameContext,
final UserAgentContext ucontext) {
super(container, me, ucontext);
this.widget = widget;
this.frameContext = frameContext;
widget.setRUIControl(this);
}
@Override
public void focus() {
super.focus();
final java.awt.Component c = this.widget.getComponent();
c.requestFocus();
}
@Override
public final void invalidateLayoutLocal() {
// Invalidate widget (some redundancy)
super.invalidateLayoutLocal();
this.widget.invalidate();
// Invalidate cached values
this.cachedLayout.clear();
this.lastLayoutKey = null;
this.lastLayoutValue = null;
}
public VerticalAlign getVAlign() {
return this.widget.getVAlign();
}
public boolean hasBackground() {
return (this.backgroundColor != null) || (this.backgroundImage != null) || (this.lastBackgroundImageUri != null);
}
@Override
public final void paintShifted(final Graphics g) {
final RenderState rs = this.modelNode.getRenderState();
if ((rs != null) && (rs.getVisibility() != RenderState.VISIBILITY_VISIBLE)) {
// Just don't paint it.
return;
}
// Prepaint borders, background images, etc.
this.prePaint(g);
// We need to paint the GUI component.
// For various reasons, we need to do that
// instead of letting AWT do it.
final Insets insets = this.getBorderInsets();
g.translate(insets.left, insets.top);
try {
this.widget.paint(g);
} finally {
g.translate(-insets.left, -insets.top);
}
}
public boolean onMouseClick(final java.awt.event.MouseEvent event, final int x, final int y) {
final ModelNode me = this.modelNode;
if (me != null) {
return HtmlController.getInstance().onMouseClick(me, event, x, y);
} else {
return true;
}
}
public boolean onDoubleClick(final java.awt.event.MouseEvent event, final int x, final int y) {
final ModelNode me = this.modelNode;
if (me != null) {
return HtmlController.getInstance().onDoubleClick(me, event, x, y);
} else {
return true;
}
}
public boolean onMousePressed(final java.awt.event.MouseEvent event, final int x, final int y) {
final ModelNode me = this.modelNode;
if (me != null) {
return HtmlController.getInstance().onMouseDown(me, event, x, y);
} else {
return true;
}
}
public boolean onMouseReleased(final java.awt.event.MouseEvent event, final int x, final int y) {
final ModelNode me = this.modelNode;
if (me != null) {
return HtmlController.getInstance().onMouseUp(me, event, x, y);
} else {
return true;
}
}
public boolean onMouseDisarmed(final java.awt.event.MouseEvent event) {
final ModelNode me = this.modelNode;
if (me != null) {
return HtmlController.getInstance().onMouseDisarmed(me, event);
} else {
return true;
}
}
/*
* (non-Javadoc)
*
* @see
* org.xamjwg.html.renderer.BoundableRenderable#invalidateState(org.xamjwg
* .html.renderer.RenderableContext)
*/
public void invalidateRenderStyle() {
// NOP - No RenderStyle below this node.
}
/*
* (non-Javadoc)
*
* @see
* org.xamjwg.html.domimpl.ContainingBlockContext#repaint(org.xamjwg.html.
* renderer.RenderableContext)
*/
public void repaint(final ModelNode modelNode) {
final Object widget = this.widget;
if (widget instanceof UINode) {
((UINode) widget).repaint(modelNode);
} else {
this.repaint();
}
}
@Override
public void updateWidgetBounds(final int guiX, final int guiY) {
// Overrides
super.updateWidgetBounds(guiX, guiY);
final Insets insets = this.getBorderInsets();
this.widget.setBounds(guiX + insets.left, guiY + insets.top, this.width - insets.left - insets.right, this.height - insets.top
- insets.bottom);
}
@Override
public Color getBlockBackgroundColor() {
return this.widget.getBackgroundColor();
}
/*
* (non-Javadoc)
*
* @see
* org.xamjwg.html.renderer.BoundableRenderable#paintSelection(java.awt.Graphics
* , boolean, org.xamjwg.html.renderer.RenderablePoint,
* org.xamjwg.html.renderer.RenderablePoint)
*/
@Override
public boolean paintSelection(final Graphics g, boolean inSelection, final RenderableSpot startPoint, final RenderableSpot endPoint) {
inSelection = super.paintSelection(g, inSelection, startPoint, endPoint);
if (inSelection) {
final Color over = new Color(0, 0, 255, 50);
final Color oldColor = g.getColor();
try {
g.setColor(over);
g.fillRect(0, 0, this.width, this.height);
} finally {
g.setColor(oldColor);
}
}
return inSelection;
}
@Override
public boolean extractSelectionText(final StringBuffer buffer, final boolean inSelection, final RenderableSpot startPoint,
final RenderableSpot endPoint) {
// No text here
return inSelection;
}
public RenderableSpot getLowestRenderableSpot(final int x, final int y) {
// Nothing draggable - return self
return new RenderableSpot(this, x, y);
}
private int declaredWidth = -1;
private int declaredHeight = -1;
private LayoutKey lastLayoutKey = null;
private LayoutValue lastLayoutValue = null;
private final Map cachedLayout = new HashMap<>(5);
@Override
public void doLayout(final int availWidth, final int availHeight, final boolean sizeOnly) {
final Map cachedLayout = this.cachedLayout;
final RenderState rs = this.modelNode.getRenderState();
final int whitespace = rs == null ? RenderState.WS_NORMAL : rs.getWhiteSpace();
final Font font = rs == null ? null : rs.getFont();
final LayoutKey layoutKey = new LayoutKey(availWidth, availHeight, whitespace, font);
LayoutValue layoutValue;
if (sizeOnly) {
layoutValue = cachedLayout.get(layoutKey);
} else {
if (java.util.Objects.equals(this.lastLayoutKey, layoutKey)) {
layoutValue = this.lastLayoutValue;
} else {
layoutValue = null;
}
}
if (layoutValue == null) {
this.applyStyle(availWidth, availHeight);
final UIControl widget = this.widget;
widget.reset(availWidth, availHeight);
final RenderState renderState = this.modelNode.getRenderState();
Insets paddingInsets = this.paddingInsets;
if (paddingInsets == null) {
paddingInsets = RBlockViewport.ZERO_INSETS;
}
Insets borderInsets = this.borderInsets;
if (borderInsets == null) {
borderInsets = RBlockViewport.ZERO_INSETS;
}
Insets marginInsets = this.marginInsets;
if (marginInsets == null) {
marginInsets = RBlockViewport.ZERO_INSETS;
}
final int actualAvailWidth = availWidth - paddingInsets.left - paddingInsets.right - borderInsets.left - borderInsets.right
- marginInsets.left - marginInsets.right;
final int actualAvailHeight = availHeight - paddingInsets.top - paddingInsets.bottom - borderInsets.top - borderInsets.bottom
- marginInsets.top - marginInsets.bottom;
final Integer dw = this.getDeclaredWidth(renderState, actualAvailWidth);
final Integer dh = this.getDeclaredHeight(renderState, actualAvailHeight);
final int declaredWidth = dw == null ? -1 : dw.intValue();
final int declaredHeight = dh == null ? -1 : dh.intValue();
this.declaredWidth = declaredWidth;
this.declaredHeight = declaredHeight;
this.widthConstrained = declaredWidth != -1;
this.heightConstrained = declaredHeight != -1;
final Insets insets = this.getInsets(false, false);
int finalWidth = declaredWidth == -1 ? -1 : declaredWidth + insets.left + insets.right;
int finalHeight = declaredHeight == -1 ? -1 : declaredHeight + insets.top + insets.bottom;
if ((finalWidth == -1) || (finalHeight == -1)) {
final Dimension size = widget.getPreferredSize();
if (finalWidth == -1) {
finalWidth = size.width + insets.left + insets.right;
}
if (finalHeight == -1) {
finalHeight = size.height + insets.top + insets.bottom;
}
}
{
final Integer maxWidth = getDeclaredMaxWidth(renderState, actualAvailWidth);
if (maxWidth != null) {
if (finalWidth > maxWidth) {
finalWidth = maxWidth;
widthConstrained = true;
}
}
}
{
final Integer minWidth = getDeclaredMinWidth(renderState, actualAvailWidth);
if (minWidth != null) {
if (finalWidth < minWidth) {
finalWidth = minWidth;
widthConstrained = true;
}
}
}
{
final Integer maxHeight = getDeclaredMaxHeight(renderState, actualAvailHeight);
if (maxHeight != null) {
if (finalHeight > maxHeight) {
finalHeight = maxHeight;
heightConstrained = true;
}
}
}
{
final Integer minHeight = getDeclaredMinHeight(renderState, actualAvailHeight);
if (minHeight != null) {
if (finalHeight < minHeight) {
finalHeight = minHeight;
heightConstrained = true;
}
}
}
layoutValue = new LayoutValue(finalWidth, finalHeight);
if (sizeOnly) {
if (cachedLayout.size() > MAX_CACHE_SIZE) {
// Unlikely, but we should ensure it's bounded.
cachedLayout.clear();
}
cachedLayout.put(layoutKey, layoutValue);
this.lastLayoutKey = null;
this.lastLayoutValue = null;
} else {
this.lastLayoutKey = layoutKey;
this.lastLayoutValue = layoutValue;
}
}
this.width = layoutValue.width;
this.height = layoutValue.height;
}
/**
* May be called by controls when they wish to modifiy their preferred size
* (e.g. an image after it's loaded). This method must be called in the GUI
* thread.
*/
public final void preferredSizeInvalidated() {
final int dw = RUIControl.this.declaredWidth;
final int dh = RUIControl.this.declaredHeight;
if ((dw == -1) || (dh == -1)) {
this.frameContext.delayedRelayout((NodeImpl) this.modelNode);
} else {
RUIControl.this.repaint();
}
}
public Iterator<@NonNull Renderable> getRenderables(final boolean topFirst) {
// No children for GUI controls
return null;
}
public Color getPaintedBackgroundColor() {
return this.container.getPaintedBackgroundColor();
}
public Color getForegroundColor() {
final RenderState rs = this.modelNode.getRenderState();
return rs == null ? null : rs.getColor();
}
private static class LayoutKey {
public final int availWidth;
public final int availHeight;
public final int whitespace;
public final Font font;
public LayoutKey(final int availWidth, final int availHeight, final int whitespace, final Font font) {
this.availWidth = availWidth;
this.availHeight = availHeight;
this.whitespace = whitespace;
this.font = font;
}
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof LayoutKey)) {
return false;
}
final LayoutKey other = (LayoutKey) obj;
return (other.availWidth == this.availWidth) && (other.availHeight == this.availHeight) && (other.whitespace == this.whitespace)
&& java.util.Objects.equals(other.font, this.font);
}
@Override
public int hashCode() {
final Font font = this.font;
return ((this.availWidth * 1000) + this.availHeight) ^ (font == null ? 0 : font.hashCode());
}
}
private static class LayoutValue {
public final int width;
public final int height;
public LayoutValue(final int width, final int height) {
this.width = width;
this.height = height;
}
}
private boolean widthConstrained = false;
private boolean heightConstrained = false;
protected boolean isWidthConstrained() {
return widthConstrained;
}
protected boolean isHeightConstrained() {
return heightConstrained;
}
@Override
public void setInnerWidth(Integer newWidth) {
super.setInnerWidth(newWidth);
widthConstrained = true;
}
@Override
public void setInnerHeight(Integer newHeight) {
super.setInnerHeight(newHeight);
heightConstrained = true;
}
}