Re: JTextPane: Workaround for invisible text with hanging indents?



Adam Warner <lists@xxxxxxxxxxxxxxxxx> wrote:

> import java.awt.*;
> import javax.swing.*;
> import javax.swing.text.*;
>
> public class InvisibleText extends JFrame {
>
> public InvisibleText() {
> JTextPane tp=new JTextPane();
> SimpleAttributeSet attrs=new SimpleAttributeSet();
> StyleConstants.setFirstLineIndent(attrs, -150.0f);
> StyleConstants.setLeftIndent(attrs, 150.0f);
> tp.setParagraphAttributes(attrs, true);
>
> JScrollPane sp=new JScrollPane(tp);
> sp.setPreferredSize(new Dimension(300, 200));
> getContentPane().add(sp);
> }
>
> public static void main(String[] args) {
> InvisibleText frame=new InvisibleText();
> frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
> frame.pack();
> frame.setVisible(true);
> }
>
>
> While typing text finally appears at the left indent position of
> subsequent lines.
>
> Assuming my code correctly invokes this API, can anyone come up with a
> workaround to make the text appear while it is typed?

See below, with NegativeFirstLineIndentParagraphView2.
ParagraphView2 allows settings first and other line indents independently
(but does not allow negative values)

Code just written, not really tested.



import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;



class StyledEditorKit2
extends StyledEditorKit
implements ViewFactory
{
public static final Object
OtherLinesIndent = "otherLinesIndent";

private ViewFactory factory = super.getViewFactory();


public View create(Element e)
{
if (e.getName().equals(AbstractDocument.ParagraphElementName))
return new ParagraphView2(e);
// return new NegativeFirstLineIndentParagraphView(e);

return factory.create(e);
}

public ViewFactory getViewFactory()
{
return this;
}
}



class ParagraphView2
extends ParagraphView
{
private int firstLineIndent;
private int otherLinesIndent;


public ParagraphView2(Element e)
{
super(e);
}



public void setOtherLinesIndent(float value)
{
otherLinesIndent = Math.max(0, (int)value);
}

public final int otherLinesIndent()
{
return otherLinesIndent;
}

public void setFirstLineIndent(float value)
{
firstLineIndent = Math.max(0, (int)value);
}

public final int firstLineIndent()
{
return firstLineIndent;
}

protected void setPropertiesFromAttributes()
{
super.setPropertiesFromAttributes();

super.firstLineIndent = 0;

AttributeSet a = getAttributes();

if (a != null)
{
Float f = (Float)a.getAttribute(StyledEditorKit2.OtherLinesIndent);

setOtherLinesIndent(f == null ? 0f : f.floatValue());

setFirstLineIndent(StyleConstants.getFirstLineIndent(a));
}
}


public int getFlowSpan(int index)
{
return layoutSpan - (index == 0 ? firstLineIndent : otherLinesIndent);
}

public int getFlowStart(int index)
{
return ((int)getTabBase()) + (index == 0 ? firstLineIndent : otherLinesIndent);
}


private boolean isLeftToRight()
{
// There must be some better way to do this.
return !flipEastAndWestAtEnds(-1, null);
}


// Unlike ParagraphView itself, the indent is not included in the Rows, but
// implemented by a modification of the BoxView layout of the ParagraphView
// itself.

protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans)
{
boolean rtl = !isLeftToRight();

int n = getViewCount();

int indent = firstLineIndent;

for (int i = 0; i < n; i++)
{
View v = getView(i);

int max = (int)Math.ceil(v.getMaximumSpan(axis));

if (max < targetSpan - indent)
{
float a = v.getAlignment(axis);

// This aligns in the span without the indent, unlike ParagraphView
offsets[i] = (int)((targetSpan - indent - max) * a) + indent;
spans[i] = max;
}
else
{
int min = (int)Math.ceil(v.getMinimumSpan(axis));
offsets[i] = indent;
spans[i] = Math.max(min, targetSpan - indent);
}

if (rtl)
offsets[i] = targetSpan - offsets[i] - spans[i];

indent = otherLinesIndent;
}
}


protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r)
{
if (r == null)
r = new SizeRequirements();

float pref = poolPreferredXSpan();
float min = poolMinimumXSpan();

r.minimum = (int)Math.ceil(min);
r.preferred = Math.max(r.minimum, (int)Math.ceil(pref));
r.maximum = Integer.MAX_VALUE;
r.alignment = 0.5f;

return r;
}

private float poolPreferredXSpan()
{
float result = 0;
float pref = 0;

int indent = firstLineIndent;

for (int n = layoutPool.getViewCount(), i = 0; i < n; i++)
{
View v = layoutPool.getView(i);

pref += indent + v.getPreferredSpan(X_AXIS);

if (v.getBreakWeight(X_AXIS, 0, Integer.MAX_VALUE) >= ForcedBreakWeight)
{
result = Math.max(result, pref);
pref = 0;
indent = otherLinesIndent;
}
}

result = Math.max(result, pref);

return result;
}


private float poolMinimumXSpan()
{
float result = 0;

float min = 0;
boolean lineHasView = false;

int indent = firstLineIndent;

for (int n = layoutPool.getViewCount(), i = 0; i < n; i++)
{
View v = layoutPool.getView(i);

if (v.getBreakWeight(X_AXIS, 0, Integer.MAX_VALUE) == BadBreakWeight)
{
min += v.getPreferredSpan(X_AXIS);
lineHasView = true;
}
else
{
if (lineHasView)
{
result = Math.max(min, result);

lineHasView = false;
min = 0;

indent = otherLinesIndent;
}
}
}

result = Math.max(result, min);
return result;
}
}



class NegativeFirstLineIndentParagraphView
extends ParagraphView
{
public NegativeFirstLineIndentParagraphView(Element e)
{
super(e);
}

public void paint(Graphics g, Shape a)
{
super.paint(g, a);


// do not optimize only if intersection, as super does
// this, again, could be optimized not to paint twice
if (firstLineIndent < 0)
{
Rectangle r = a.getBounds();

// may need to ensure clipping on the paragraph bounds,
// but is also not done by super

r.x += getLeftInset() + getOffset(X_AXIS, 0);
r.y += getTopInset() + getOffset(Y_AXIS, 0);
r.width = getSpan(X_AXIS, 0) - firstLineIndent;
r.height = getSpan(Y_AXIS, 0);

paintChild(g, r, 0);
}
}
}




public class InvisibleText extends JFrame
{
public InvisibleText()
{
JTextPane tp=new JTextPane();
tp.setEditorKit(new StyledEditorKit2());

SimpleAttributeSet attrs=new SimpleAttributeSet();

// for ParagraphView2
attrs.addAttribute(StyleConstants.FirstLineIndent, new Float(50f));
attrs.addAttribute(StyledEditorKit2.OtherLinesIndent, new Float(100f));

// for fun
StyleConstants.setAlignment(attrs, StyleConstants.ALIGN_CENTER);

// for NegativeFirstLineIndentParagraphView
// StyleConstants.setFirstLineIndent(attrs, -150.0f);
// StyleConstants.setLeftIndent(attrs, 150.0f);

tp.setParagraphAttributes(attrs, true);

JScrollPane sp=new JScrollPane(tp);
sp.setPreferredSize(new Dimension(300, 200));
getContentPane().add(sp);
}

public static void main(String[] args) {
InvisibleText frame=new InvisibleText();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}

}



Christian
.