Icons between text in a jlabel using graphics - java

How to paint multiple icons (ie., say icons between text) in JLabel using graphics? Please do assist me in this effort

One option I would use is to have a sequence of JLabels. Ones with icon and ones with text only.
The other option would be to leverage the mini HTML support of the JLabel: Have
<html><img src='theimage.png'>The text<img src='theimage2.png'>
as the text of the JLabel. This approach works for text formatting but I'm not sure if the image tags work there too.
Or you did override the JLabel.paint() to do custom rendering btw?
Then I would use the following approach:
List<Object> textAndImage = new ArrayList<Object>() {{
add("This ");
add(new ImageIcon("image1.png"));
add(" is ");
add(new ImageIcon("image2.png"));
add(" an ");
add(" imaged text sample ");
}};
FontMetrics fm = g.getFontMetrics();
int x = 0;
for (Object o : textAndImage) {
if (o instanceof String) {
g.drawString((String)o, x, fm.getHeight());
x += fm.stringWidth((String)o);
} else
if (o instanceof ImageIcon) {
((ImageIcon)o).paintIcon(null, g, x, 0);
x += ((ImageIcon)o).getIconWidth();
}
}
Of course this is not a fully fledged solution but might give you some hints how to proceed.

One way is to use a custom border that paints an icon (and text if you want), then you can nest indefinitely.
Here's one such border:
/**
* Show a leading or trailing icon in border.
*/
public static class IconBorder implements Border
{
/**
* #param icon - icon to paint for this border
* #param top outer insets for this border
* #param left
* #param bottom
* #param right
*/
public IconBorder(Icon icon, int top, int left, int bottom, int right)
{
setIcon(icon);
top_ = top;
left_ = left;
bottom_ = bottom;
right_ = right;
rect_ = new Rectangle(0, 0, icon_.getIconWidth(), icon_.getIconHeight());
}
public Insets getBorderInsets(Component c)
{
if( icon_ == null )
return new Insets(0, 0, 0, 0);
return isTrailing_ ? new Insets(top_, left_, bottom_, icon_.getIconWidth() + right_) :
new Insets(top_, icon_.getIconWidth() + left_, bottom_, right_);
}
public boolean isBorderOpaque()
{
return false;
}
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
{
if( icon_ != null )
{
if( isTrailing_ )
x = width - icon_.getIconWidth() + 4;
else
x += left_ - 1;
icon_.paintIcon(c, g, x, y + top_);
rect_.x = x;
rect_.y = y;
}
}
public void setIcon(Icon icon)
{
if( icon_ != icon )
icon_ = icon;
}
public Icon getIcon()
{
return icon_;
}
public void setPosition(boolean isTrailing)
{
isTrailing_ = isTrailing;
}
public Rectangle getIconRect()
{
return rect_;
}
private final int top_;
private final int left_;
private final int bottom_;
private final int right_;
private final Rectangle rect_;
private Icon icon_;
private boolean isTrailing_ = true;
}
I use this to add a search icon to a JTextField (a la browser search box). getIconRect can be used to check for mouse hover. For example I change cursor to HAND_CURSOR when mouse is over the search icon.

Related

Aligning an ArrayList of Strings into a (custom)Box won't get in proper position

I've got a little problem with aligning a group of Strings to the bottom of a box in Java.
NOTE: the Box class I'm using is not a default > javax.Swing box! It's a simple costum box with a x, y position, and with a width and height!
What do I currently have?
A Message class which can be individually aligned by my Allign class.
A MessageList object containing an ArrayList of Message objects which can be aligned together with the Allign class.
A Box object that contains the position and dimension of the box. The Allign class uses this box to align the objects in.
The Allign class that can align different types of Objects, and use a Box object to align in.
How does my code should work:
(code snippets further down the page)
The Message object can use different Font settings. The Allign class can align these Messages objects. The Message class contains a method called obtainFontDimension(), which gets the bounds of the object's String in the preferred Font settings. When we want to apply an alignment to a Message object, I create a Box object which contains the x,y position and the width and height. By calling Allign.applyAllignment(params) the calculations will be applied to align the Message object in the Box, and the Message's dx, dy (drawing x, y positions) will be set.
Till here it works fine.
Now I create a MessageList object and add some (Message objects to it. When applying an alignment to this, it will run through the Message objects it contains, and will call the obtainFontDimensions() on them. The height of these Strings will be summed, and results into a total height (int listHeight) of the Strings together. To get the drawing position of each Message object, we take the y-position of the Box where we want to align in. First we remove the listHeight of the Box's y position:
Now we got the offset of the first String. When the bottom alignment is being applied, it just adds the height of the Box to the offset. Finally, the offset is set for the next Message object by adding the current Message object height to te offset. Time for the next iteration, till the ArrayList has been fully calculated. This should result in the following:
What is going wrong?
When applying an alignment to a MessageList object, some Strings touch eachother perfectly (see circle B on image), and some keep some pixels more distance then others (see circle A1, A2 on image). And next to that, there remains an unexpected padding on the bottom (see circle C on image).
What have I attempted so far?
First I've been checking the height of the Strings, which all seem to be correct. So the `obtainFontDimensions()` method seems to be working fine.
Drawing the concept on paper, and attempt to recalculate the procedure, which should get me the correct position of the Strings. For example:
- `Box`: x=80, y=80, width=100, height=100
- `MessageList` with 3 `Messages`, which have a height of 0=10, 1=10, 2=20. Total height of these Strings is 40 pixels.
- Before actually alligning, the position of the first String becomes box.y-listHeight, which is 40.
- When actually alligning to the bottom, the the offset becomes 40+100=140.
- The offset for the second String will be calculated: 140+20(current message's height)=160.
- This repeats for the third string, which is is 160+10 = 170.
- That means, that the bottom line of the final string is on 170+10 = 180, which equals the bottom of the `Box`'s bottom.
Your suggestion...?
Code snippets
(only important parts)
public class Window()
{
public void draw(Graphics2D g2d)
{
Box contentBox = new Box(100, 100, 300, 300);
Message loadTitle = new Message("This is a testing TITLE", Colors.ORANGE, Fonts.LOADING_TITLE, false);
Message loadDescription = new Message(loadString, Colors.RED, Fonts.LOADING_DESCRIPTION, false);
Message loadTip = new Message("This is a random TIP!", Colors.RED, Fonts.LOADING_DESCRIPTION, false);
Message loadRelease = new Message("Planned game release 2939!", Colors.RED, Fonts.LOADING_DESCRIPTION, false);
Message loadSingle = new Message("THIS IS A SINGLE MESSAGE! 2o15", Colors.RED, Fonts.LOADING_DESCRIPTION, false);
MessageList list = new MessageList();
list.add(loadTitle);
list.add(loadDescription);
list.add(loadTip);
list.add(loadRelease);
list.add(loadSingle);
Allign.applyAllignment(g2d, Allignment.BOTTOM_RIGHT, list, loadBox);
loadBox.testDraw(g2d);
loadTitle.draw(g2d);
loadDescription.draw(g2d);
loadTip.draw(g2d);
loadRelease.draw(g2d);
loadSingle.draw(g2d);
}
}
public class Message
{
private String text;
private Color color;
private Font font;
private int dx, dy;
private int width, height;
private Rectancle2D vb; // temp
public Message(String text, int x, int y, Color color, Font font)
{
// set text, color, font, x, y..
}
public Rectangle2D obtainFontDimension(Graphics2D g2d)
{
if(font == null){ font = Fonts.DEFAULT; }
g2d.setFont(font);
FontRenderContext frc = g2d.getFontRenderContext();
GlyphVector gv = g2d.getFont().createGlyphVector(frc, text);
Rectangle2D vb = gv.getPixelBounds(null, 0, 0);
this.width = (int)vb.getWidth();
this.height = (int)vb.getHeight();
this.gv = gv; // TEMP for bound drawing
return vb;
}
public void draw(Graphics2D g2d)
{
g2d.setFont(font);
g2d.setColor(color);
g2d.drawString(text, dx, dy);
// TEMP draw bounds
g2d.setColor(new Color(0, 0, 0, 100));
g2d.draw(gv.getPixelBounds(null, dx, dy));
}
}
public class Allign
{
public static enum Allignment
{
BOTTOM_RIGHT
//, etc
}
public static void applyAllignment(Graphics2D g2d, Allignment allignment, Object object, Box box)
{
Point position = null;
Point dimension = null;
if(obj instanceof Message){ // Single Message object }
else if(obj instanceof Message)
{
MessageList messageList = (MessageList) obj;
int listHeight = 0;
for(Message message : messageList.getList())
{
listHeight += message.obtainFontDimension(g2d).getHeight();
}
position = new Point(box.x, box.y-listHeight); // offset Y
for(Message message : messageList.getList())
{
message.setDrawPosition(allign(allignment, position, new Dimension(message.getWidth(), 0), box, true));
position.y += message.getHeight();
}
}
}
private static Point allign(Allignment allignment, Point position, Dimension dimension, Box box, boolean verticalAllign)
{
switch(allignment)
{
case BOTTOM_RIGHT:
position = allignRight(position, dimension, box);
if(!verticalAllign) break;
position = allignBottom(position, dimension, box);
break;
// Rest
}
}
private static Point allignBottom(Point position, Dimension dimension, Box box)
{
return new Point(position.x, position.y+box.height-dimension.height);
}
}
Can I suggest a utility class like this?
public class TextPrinter {
public enum VerticalAlign {
TOP,
MIDDLE,
BOTTOM
}
public enum HorizontalAlign {
LEFT,
CENTER,
RIGHT
}
private Font font;
private Color color;
private int width;
private int height;
private VerticalAlign vAlign = VerticalAlign.TOP;
private HorizontalAlign hAlign = HorizontalAlign.LEFT;
public Font getFont() {
return font;
}
public void setFont(Font font) {
this.font = font;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public VerticalAlign getVerticalAlign() {
return vAlign;
}
public void setVerticalAlign(VerticalAlign vAlign) {
this.vAlign = vAlign;
}
public HorizontalAlign getHorizontalAlign() {
return hAlign;
}
public void setHorizontalAlign(HorizontalAlign hAlign) {
this.hAlign = hAlign;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
private int getOffSetX(int widthText){
int result = 0;
if (hAlign == HorizontalAlign.CENTER){
result = (width - widthText)/2;
} else if (hAlign == HorizontalAlign.RIGHT){
result = width - widthText;
}
return result;
}
private int getOffSetY(int ascent, int descent){
int result = ascent;
if (vAlign == VerticalAlign.MIDDLE){
result = (height + ascent - descent)/2;
} else if (vAlign == VerticalAlign.BOTTOM){
result = height - descent;
}
return result;
}
public void print(Graphics g, String text, int x, int y) {
g.setColor(color);
g.setFont(font);
FontMetrics fm = g.getFontMetrics(font);
int widthText = fm.stringWidth(text);
g.drawString(text, x + getOffSetX(widthText), y + getOffSetY(fm.getAscent(), fm.getDescent()));
}
}
I use it in my poker game:
https://github.com/dperezcabrera/jpoker
Thanks David Perez and VGR for the inputs!
I have switched back to the Font metrics, grabbed the height of the string's bounds. However, this only uses the height of the baseline to the highest ascending point. For a proper bottom spacing (like on top) I added the descent integer aswell.
public int obtainFontDimension(Graphics2D g2d)
{
if(font == null){ font = Fonts.DEFAULT; }
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics(font);
Rectangle2D sb = fm.getStringBounds(text, g2d);
this.width = (int)sb.getWidth();
this.height = (int)sb.getHeight();
this.descent = (int)fm.getDescent(); // added
tempShape = new Rectangle(width, height+descent); // Temp for drawing bounds
return height;
}
Before the allignment begins, I first calculate the total height of all of the Strings in the MessageList. The total height is the height of the String including the descending height.
Then I need to get the offset height for each vertical allignment possibility, which I've added. (TOP, MIDDLE, BOTTOM)
After that, we allign every Message under eachother by adding the height and descent to the offset on every itteration, with vertical allignment disabled this time, or it will reallign every Message horizontally as a single Message object instead as a group of Messages, but it does allow it to allign vertically.
if (obj instanceof MessageList)
{
MessageList messageList = (MessageList) obj;
int listHeight = 0;
for(Message message : messageList.getList())
{
message.obtainFontDimension(g2d);
listHeight += message.getHeight()+message.getDescent();
}
position = new Point(box.x, box.y);
Dimension listDimension = new Dimension(0, listHeight);
if( allignment == Allignment.MIDDLE || allignment == Allignment.MIDDLE_LEFT
|| allignment == Allignment.ABSOLUTE_MIDDLE || allignment == Allignment.MIDDLE_RIGHT)
{
position = allign(Allignment.MIDDLE, position, listDimension, box, true);
}
else if(allignment == Allignment.BOTTOM || allignment == Allignment.BOTTOM_LEFT
|| allignment == Allignment.BOTTOM_CENTER || allignment == Allignment.BOTTOM_RIGHT)
{
position = allign(Allignment.BOTTOM, position, listDimension, box, true);
}
else
{
position = allign(Allignment.TOP, position, listDimension, box, true);
}
for(Message message : messageList.getList())
{
position.y += message.getHeight(); // prepare the offset
message.setDrawPosition(allign(allignment, position, new Dimension(message.getWidth(), 0), box, true));
position.y += message.getDescent(); // add descending offset for next itteration
}
}
To draw the Messages with the new bounds:
public void draw(Graphics2D g2d)
{
g2d.setFont(font);
g2d.setColor(color);
g2d.drawString(text, dx, dy);
// Drawing bounds for testing
g2d.setColor(new Color(0, 0, 0, 100));
shape.setLocation(dx, dy-height);
g2d.draw(tempShape);
}
Final result:
Thanks again!

Android setEllipsize not working on class that extends EditText

I have a class that extends EditText, on this class i'm trying to make the text to set setEllipsize when the text is to long. For some reason all my tryies didn't work.
It seems that that i can scroll the text horizontal inside the text view...
Can anybody advice how to make it work.
(So far i've tryed many combination of those functions and
that's my part in the code:
public class LMEditTextAutoSize extends EditText {
private boolean autoSize;
public LMEditTextAutoSize(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mTextSize = getTextSize();
mMaxTextSize = getTextSize();
}
public LMEditTextAutoSize(Context context, AttributeSet attrs) {
super(context, attrs);
mTextSize = getTextSize();
mMaxTextSize = getTextSize();
}
public LMEditTextAutoSize(Context context) {
super(context);
mTextSize = getTextSize();
mMaxTextSize = getTextSize();
}
public void setAutoSize(boolean autoSize) {
this.autoSize = autoSize;
resetTextSize();
}
// Minimum text size for this text view
public static final float MIN_TEXT_SIZE = 50;
// Interface for resize notifications
public interface OnTextResizeListener {
public void onTextResize(TextView textView, float oldSize, float newSize);
}
// Our ellipse string
private static final String mEllipsis = "...";
// Registered resize listener
private OnTextResizeListener mTextResizeListener;
// Flag for text and/or size changes to force a resize
private boolean mNeedsResize = false;
// Text size that is set from code. This acts as a starting point for resizing
private float mTextSize;
// Temporary upper bounds on the starting text size
private float mMaxTextSize = 0;
// Lower bounds for text size
private float mMinTextSize = MIN_TEXT_SIZE;
// Text view line spacing multiplier
private float mSpacingMult = 1.0f;
// Text view additional line spacing
private float mSpacingAdd = 0.0f;
// Add ellipsis to text that overflows at the smallest text size
private boolean mAddEllipsis = true;
private int widthLimit;
private int heightLimit;
/**
* When text changes, set the force resize flag to true and reset the text size.
*/
#Override
protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
mNeedsResize = true;
// Since this view may be reused, it is good to reset the text size
resetTextSize();
}
/**
* If the text view size changed, set the force resize flag to true
*/
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != oldw || h != oldh) {
mNeedsResize = true;
}
}
/**
* Register listener to receive resize notifications
*
* #param listener
*/
public void setOnResizeListener(OnTextResizeListener listener) {
mTextResizeListener = listener;
}
/**
* Override the set text size to update our internal reference values
*/
#Override
public void setTextSize(float size) {
super.setTextSize(size);
mTextSize = getTextSize();
}
/**
* Override the set text size to update our internal reference values
*/
#Override
public void setTextSize(int unit, float size) {
super.setTextSize(unit, size);
mTextSize = getTextSize();
}
/**
* Override the set line spacing to update our internal reference values
*/
#Override
public void setLineSpacing(float add, float mult) {
super.setLineSpacing(add, mult);
mSpacingMult = mult;
mSpacingAdd = add;
}
/**
* Set the upper text size limit and invalidate the view
*
* #param maxTextSize
*/
public void setMaxTextSize(float maxTextSize) {
mMaxTextSize = maxTextSize;
requestLayout();
invalidate();
}
/**
* Return upper text size limit
*
* #return
*/
public float getMaxTextSize() {
return mMaxTextSize;
}
/**
* Set the lower text size limit and invalidate the view
*
* #param minTextSize
*/
public void setMinTextSize(float minTextSize) {
mMinTextSize = minTextSize;
requestLayout();
invalidate();
}
/**
* Return lower text size limit
*
* #return
*/
public float getMinTextSize() {
return mMinTextSize;
}
/**
* Set flag to add ellipsis to text that overflows at the smallest text size
*
* #param addEllipsis
*/
public void setAddEllipsis(boolean addEllipsis) {
mAddEllipsis = addEllipsis;
}
/**
* Return flag to add ellipsis to text that overflows at the smallest text size
*
* #return
*/
public boolean getAddEllipsis() {
return mAddEllipsis;
}
/**
* Reset the text to the original size
*/
public void resetTextSize() {
if (autoSize) {
if (mTextSize > 0) {
super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mMaxTextSize);
mTextSize = mMaxTextSize;
}
}
}
/**
* Resize text after measuring
*/
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (changed || mNeedsResize) {
widthLimit = (right - left) - getCompoundPaddingLeft() - getCompoundPaddingRight();
heightLimit = (bottom - top) - getCompoundPaddingBottom() - getCompoundPaddingTop();
resizeText(widthLimit, heightLimit);
}
super.onLayout(changed, left, top, right, bottom);
}
/**
* Resize the text size with specified width and height
*
* #param width
* #param height
*/
public void resizeText(int width, int height) {
if (autoSize) {
CharSequence text = getText();
int oneLineWidth = width;
int lineCount = getLineCount();
int minTextHeight = getTextHeight("oo", getPaint(), width, mMinTextSize);
int maxLineInHeight = height / minTextHeight;
lineCount = lineCount > maxLineInHeight ? maxLineInHeight : lineCount;
if (lineCount > 1) {
width = width * lineCount;
}
// Do not resize if the view does not have dimensions or there is no text
if (text == null || text.length() == 0 || height <= 0 || width <= 0 || mTextSize == 0) {
return;
}
// Get the text view's paint object
TextPaint textPaint = getPaint();
// Store the current text size
float oldTextSize = textPaint.getTextSize();
// If there is a max text size set, use the lesser of that and the default text size
float targetTextSize = mMaxTextSize > 0 ? Math.min(mTextSize, mMaxTextSize) : mTextSize;
// Get the required text height
int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
// Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
while (textHeight > height && targetTextSize > mMinTextSize) {
targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
textHeight = getTextHeight(text, textPaint, width, targetTextSize);
}
TextPaint paintCopy = new TextPaint(textPaint);
paintCopy.setTextSize(targetTextSize);
float textWidte = paintCopy.measureText(text, 0, text.length());
while (textWidte > width && targetTextSize > mMinTextSize) {
targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
paintCopy.setTextSize(targetTextSize);
textWidte = paintCopy.measureText(text, 0, text.length());
}
if (lineCount > 1) {
int textHeightforLine = getTextHeight(text, textPaint, oneLineWidth, targetTextSize);
while (textHeightforLine > height && targetTextSize > mMinTextSize) {
targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
textHeightforLine = getTextHeight(text, textPaint, oneLineWidth, targetTextSize);
}
}
// If we had reached our minimum text size and still don't fit, append an ellipsis
if (mAddEllipsis && targetTextSize == mMinTextSize && textHeight > height) {
// Draw using a static layout
// modified: use a copy of TextPaint for measuring
TextPaint paint = new TextPaint(textPaint);
paint.setTextSize(mMinTextSize);
// Draw using a static layout
StaticLayout layout = new StaticLayout(text, paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false);
// Check that we have a least one line of rendered text
if (layout.getLineCount() > 0) {
// Since the line at the specific vertical position would be cut off,
// we must trim up to the previous line
int lastLine = layout.getLineForVertical(height) - 1;
// If the text would not even fit on a single line, clear it
if (lastLine < 0) {
setText("");
}
// Otherwise, trim to the previous line and add an ellipsis
else {
int start = layout.getLineStart(lastLine);
int end = layout.getLineEnd(lastLine);
float lineWidth = layout.getLineWidth(lastLine);
float ellipseWidth = textPaint.measureText(mEllipsis);
// Trim characters off until we have enough room to draw the ellipsis
while (width < lineWidth + ellipseWidth) {
lineWidth = textPaint.measureText(text.subSequence(start, --end + 1).toString());
}
if (end != 0) {
setInputType(InputType.TYPE_CLASS_TEXT);
setSingleLine(true);
setLines(1);
setMaxLines(1);
setEllipsize(TextUtils.TruncateAt.END);
setSelected(true);
setText(text);
}
}
}
}
// Some devices try to auto adjust line spacing, so force default line spacing
// and invalidate the layout as a side effect
setTextSize(TypedValue.COMPLEX_UNIT_PX, targetTextSize);
setLineSpacing(mSpacingAdd, mSpacingMult);
// Notify the listener if registered
if (mTextResizeListener != null) {
mTextResizeListener.onTextResize(this, oldTextSize, targetTextSize);
}
// Reset force resize flag
mNeedsResize = false;
}
}
// Set the text size of the text paint object and use a static layout to render text off screen before measuring
private int getTextHeight(CharSequence source, TextPaint paint, int width, float textSize) {
TextPaint paintCopy = new TextPaint(paint);
paintCopy.setTextSize(textSize);
StaticLayout layout = new StaticLayout(source, paintCopy, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
return layout.getHeight();
}
}
remove setHorizontallyScrolling(true) it is contradicting with setEllipsize(TextUtils.TruncateAt.END); Refer to the android doc for each ones function
https://developer.android.com/reference/android/widget/TextView.html

Error displaying bufferedImage repainting with mouse Listening

Im having difficulty drawing a Sub Image of a Buffered Image everytime the Mouse Pointer Location equals that of the each border of the JPanel. The problem is that the BufferedImage that is equals the SubImage wont display
Here is the JPanel the initialization might not be correct Im still learning the components of Java and 2D graphics.
public class Map extends JPanel implements MouseListener, MouseMotionListener {
private final int SCR_W = 800;
private final int SCR_H = 600;
private int x;
private int y;
private int dx;
private int dy;
String dir = "C:\\imgs\\war\\";
private BufferedImage map_buffer;
public BufferedImage scr_buffer;
public void initScreen(int x, int y, int stage){
if(stage == 0){
try{ map_buffer = ImageIO.read(new File(dir + "map" + stage + ".jpg" ));
}catch(Exception error) { System.out.println("Error: cannot read tileset image.");
}
}
scr_buffer = map_buffer.getSubimage(x, y, SCR_W, SCR_H);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
if(scr_buffer == null)
initScreen(x, y, 0);
g.drawImage(scr_buffer, 0, 0, this);
}
boolean isLeftBorder = false;
boolean isRightBorder = false;
boolean isTopBorder = false;
boolean isBottomBorder = false;
public Map(){
addMouseListener(new MouseAdapter() {
public void mouseMoved(MouseEvent e) {
/**
* Check location of mouse pointer if(specified_edge)move(scr_buffer)
*
*/
System.out.println("MouseMove: " + e.getPoint().getX() + " , " + e.getPoint().getY());
if(e.getPoint().getX() == SCR_W)isRightBorder = true;
if(e.getPoint().getY() == SCR_H)isBottomBorder = true;
if(e.getPoint().getX() == 0 && e.getPoint().getY() == SCR_H)isLeftBorder = true;
if(e.getPoint().getY() == 0 && e.getPoint().getX() == SCR_W)isTopBorder = true;
if(e.getPoint().getX() != 0 && e.getPoint().getX() != SCR_W
&& e.getPoint().getY() != 0 && e.getPoint().getY() != SCR_H){
isLeftBorder = false;
isRightBorder = false;
isTopBorder = false;
isBottomBorder = false;
}
if(isRightBorder){ x += 2; repaint(); }
if(isBottomBorder){ y -= 2; repaint(); }
if(isLeftBorder){ x -= 2; repaint();}
if(isTopBorder){ y += 2; repaint(); }
}
});
}
}
In the main I init a JFrame to contain the Panel all im getting is a error
public static void main(String[] args) {
JFrame f = new JFrame("War");
f.setSize(800, 600);
f.setLayout(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Map m = new Map();
f.getContentPane().add(f);
f.setVisible(true);
}
In order to detect mouse movement you should use a MosuseMotionListener, while technically MouseAdapter implements this, you need to register it with the JPanel correctly
Instead of using addMouseListener, you'll want to use addMouseMotionListener instead
I'd also be worried about the use of SRC_W and SRC_H, as you can't guarantee the size of the panel. Instead, you should be using getWidth and getHeight, which will tell you the actual size of the component
You can improve the chances of obtaining the size you want by overriding the getPreferredSize and return the size you would like. You'd then use pack on the frame to wrap the frame about it
f.getContentPane().add(f); is adding the frame to itself, it should probably be more like f.getContentPane().add(m);
f.setLayout(null); will prevent any of the child components from been sized and positioned and is best avoid, just get rid of it.
Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
This scr_buffer = map_buffer.getSubimage(x, y, SCR_W, SCR_H); is also a little dangerous, as it could be asking for more of the image then is available, you should be testing to see if x + SCR_W < image width (and same goes for the height)
I don't know if this deliberate or not, but you never reset the "border" flags, so once set, they will always be true...
addMouseMotionListener(new MouseAdapter() {
public void mouseMoved(MouseEvent e) {
/**
* Check location of mouse pointer if(specified_edge)move(scr_buffer)
*
*/
isRightBorder = false;
isBottomBorder = false;
isTopBorder = false;
isLeftBorder = false;
You may also want to have a "space" around the edge, which when the mouse enters it, it will set the border flags, for example...
if (e.getPoint().getX() >= getWidth() - 4) {
isRightBorder = true;
}
if (e.getPoint().getY() >= getHeight() - 4) {
isBottomBorder = true;
}
if (e.getPoint().getX() <= 4) {
isLeftBorder = true;
}
if (e.getPoint().getY() <= 4) {
isTopBorder = true;
}
Your logic for the vertical movement is wrong, when the mouse is within the bottom border, it should add to the y position and subtract when it's within the top border...
if (isBottomBorder) {
y += 2;
}
if (isTopBorder) {
y -= 2;
}
You need to perform some range checking after you've modified the x/y positions to make sure you're not request for a portion of the image which is not available...
if (x < 0) {
x = 0;
} else if (x + getWidth() > map_buffer.getWidth()) {
x = map_buffer.getWidth() - getWidth();
}
if (y < 0) {
y = 0;
} else if (y + getHeight() > map_buffer.getHeight()) {
y = map_buffer.getHeight() - getHeight();
}
There is a logic error within the initScreen method, src_buffer is never set to null, meaning that once it has a "sub image", it never tries to obtain a new one (also, you shouldn't be loading the map_buffer in there either).
scr_buffer = null;
repaint();
Thank you for you time and understanding.
Inside mouse moved
if (e.getPoint().getX() >= getWidth() - 4) {
isRightBorder = true; // unnecessary
scr_buffer = null;
x = x + 2;
repaint();
}

Text sizing in android with java and libgdx

I'm creating text in libgx, but am having a problem where the text can be different sizes depending on the phone. for the images, I resize them based on the screen size, but I can't do this with the text,b ecuase it needs wrapping and fitting to the phone. Can anyone advise on a better way of drawing text that looks the same on any mobile screen.
The text class:
public class TextActor extends Actor {
BitmapFont font;
String text;
float x = 0;
float y = 0;
float w = 10;
float h = 10;
public TextActor(String text){
font = new BitmapFont(Gdx.files.internal("font.fnt"));
this.text = text;
}
#Override
public void draw(SpriteBatch batch, float parentAlpha) {
font.draw(batch, text, x, y);
}
public void setPosition(float x, float y) {
this.x =x;
this.y=y;
}
public void setText(String text){
this.text = text;
}
}
in the screen class:
#Override
public void show() {
batch = new SpriteBatch();
stage = new Stage(Gdx.graphics.getWidth(),Gdx.graphics.getHeight(),true);
scoreText = new TextActor("How to play: \n");
scoreText.setPosition(10, ((Gdx.graphics.getHeight() /7) * 7));
stage.addActor(scoreText);
}
In one of my project I used different font sizes for different phones like this:
// create fonts
float density = Gdx.graphics.getDensity();
String path = "hdpi";
if (density <= 1) {
path = "mdpi";
} else if (density > 1 && density < 1.5f) {
path = "hdpi";
} else if (density >= 1.5 && density < 2) {
path = "xdpi";
} else if (density >= 2) {
path = "xxdpi";
}
normalFont = new BitmapFont(Gdx.files.internal("font/" + path + "/font.fnt"));
As you can see I used bitmapfont, you can find a bitmapfont creator here with you can create different size of bitmapfonts: http://www.badlogicgames.com/wordpress/?p=1247 . This way you can work with the fonts like you worked with your resized bitmaps, and also you can resize the font like:
normalFont.setScale(2.0f);
I hope this helps.
Use TextBounds like:
textBounds = yourfont.getBounds("How to play: \n");
then get the width of textbounds like: textbounds.width.
then position where you want.

RSyntaxTextArea not showing line numbers

I'm using RSyntaxTextArea for a minimized IDE I'm working on, Everything seems to be really working smoothly except for the line numbering, which I couldn't really make it show:
RSyntaxTextArea textArea = new RSyntaxTextArea(20, 60);
textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_C);
textArea.setCodeFoldingEnabled(true);
textArea.setAntiAliasingEnabled(true);
RTextScrollPane sp = new RTextScrollPane(textArea);
sp.setLineNumbersEnabled(true);
sp.setFoldIndicatorEnabled(true);
if ( sp.getLineNumbersEnabled() )
{
System.out.println("Enabled"); // it prints the line but it's not showing
}
contentPane.add(/*textEditorScrollPane*/ textArea, BorderLayout.CENTER);
I can't figure out why it's not showing the line numbers..
It's not showing the scrollbars either, right? Assuming that contentPane is where you want your components, you need to add the RTextScrollPane instance to the contentPane, not the RSyntaxTextArea instance. The Gutter, which displays line numbers, is a part of the RTextScrollPane - an extended JScrollPane.
If you don't add a scroll pane to your GUI, it will not be shown, nor will you be able to scroll around. :P
So try the following:
contentPane.add(sp, BorderLayout.CENTER);
Alternatively, you can use the following class:
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.*;
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
/**
* This class will display line numbers for a related text component. The text
* component must use the same line height for each line. TextLineNumber
* supports wrapped lines and will highlight the line number of the current
* line in the text component.
* <p>
* This class was designed to be used as a component added to the row header
* of a JScrollPane.
*/
public class TextLineNumber extends JPanel
implements CaretListener, DocumentListener, PropertyChangeListener
{
public final static float LEFT = 0.0f;
public final static float CENTER = 0.5f;
public final static float RIGHT = 1.0f;
private final static Border OUTER = new MatteBorder(0, 0, 0, 2, Color.GRAY);
private final static int HEIGHT = Integer.MAX_VALUE - 1000000;
// Text component this TextTextLineNumber component is in sync with
private JTextComponent component;
// Properties that can be changed
private boolean updateFont;
private int borderGap;
private Color currentLineForeground;
private float digitAlignment;
private int minimumDisplayDigits;
// Keep history information to reduce the number of times the component
// needs to be repainted
private int lastDigits;
private int lastHeight;
private int lastLine;
private HashMap<String, FontMetrics> fonts;
/**
* Create a line number component for a text component. This minimum
* display width will be based on 3 digits.
*
* #param component the related text component
*/
public TextLineNumber(JTextComponent component)
{
this(component, 3);
}
/**
* Create a line number component for a text component.
*
* #param component the related text component
* #param minimumDisplayDigits the number of digits used to calculate
* the minimum width of the component
*/
public TextLineNumber(JTextComponent component, int minimumDisplayDigits)
{
this.component = component;
setFont(component.getFont());
setBorderGap(5);
setCurrentLineForeground(Color.RED);
setDigitAlignment(RIGHT);
setMinimumDisplayDigits(minimumDisplayDigits);
component.getDocument().addDocumentListener(this);
component.addCaretListener(this);
component.addPropertyChangeListener("font", this);
}
/**
* Gets the update font property
*
* #return the update font property
*/
public boolean getUpdateFont()
{
return updateFont;
}
/**
* Set the update font property. Indicates whether this Font should be
* updated automatically when the Font of the related text component
* is changed.
*
* #param updateFont when true update the Font and repaint the line
* numbers, otherwise just repaint the line numbers.
*/
public void setUpdateFont(boolean updateFont)
{
this.updateFont = updateFont;
}
/**
* Gets the border gap
*
* #return the border gap in pixels
*/
public int getBorderGap()
{
return borderGap;
}
/**
* The border gap is used in calculating the left and right insets of the
* border. Default value is 5.
*
* #param borderGap the gap in pixels
*/
public void setBorderGap(int borderGap)
{
this.borderGap = borderGap;
Border inner = new EmptyBorder(0, borderGap, 0, borderGap);
setBorder(new CompoundBorder(OUTER, inner));
lastDigits = 0;
setPreferredWidth();
}
/**
* Gets the current line rendering Color
*
* #return the Color used to render the current line number
*/
public Color getCurrentLineForeground()
{
return currentLineForeground == null ? getForeground() : currentLineForeground;
}
/**
* The Color used to render the current line digits. Default is Coolor.RED.
*
* #param currentLineForeground the Color used to render the current line
*/
public void setCurrentLineForeground(Color currentLineForeground)
{
this.currentLineForeground = currentLineForeground;
}
/**
* Gets the digit alignment
*
* #return the alignment of the painted digits
*/
public float getDigitAlignment()
{
return digitAlignment;
}
/**
* Specify the horizontal alignment of the digits within the component.
* Common values would be:
* <ul>
* <li>TextLineNumber.LEFT
* <li>TextLineNumber.CENTER
* <li>TextLineNumber.RIGHT (default)
* </ul>
*
* #param currentLineForeground the Color used to render the current line
*/
public void setDigitAlignment(float digitAlignment)
{
this.digitAlignment =
digitAlignment > 1.0f ? 1.0f : digitAlignment < 0.0f ? -1.0f : digitAlignment;
}
/**
* Gets the minimum display digits
*
* #return the minimum display digits
*/
public int getMinimumDisplayDigits()
{
return minimumDisplayDigits;
}
/**
* Specify the mimimum number of digits used to calculate the preferred
* width of the component. Default is 3.
*
* #param minimumDisplayDigits the number digits used in the preferred
* width calculation
*/
public void setMinimumDisplayDigits(int minimumDisplayDigits)
{
this.minimumDisplayDigits = minimumDisplayDigits;
setPreferredWidth();
}
/**
* Calculate the width needed to display the maximum line number
*/
private void setPreferredWidth()
{
Element root = component.getDocument().getDefaultRootElement();
int lines = root.getElementCount();
int digits = Math.max(String.valueOf(lines).length(), minimumDisplayDigits);
// Update sizes when number of digits in the line number changes
if (lastDigits != digits)
{
lastDigits = digits;
FontMetrics fontMetrics = getFontMetrics(getFont());
int width = fontMetrics.charWidth('0') * digits;
Insets insets = getInsets();
int preferredWidth = insets.left + insets.right + width;
Dimension d = getPreferredSize();
d.setSize(preferredWidth, HEIGHT);
setPreferredSize(d);
setSize(d);
}
}
/**
* Draw the line numbers
*/
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
// Determine the width of the space available to draw the line number
FontMetrics fontMetrics = component.getFontMetrics(component.getFont());
Insets insets = getInsets();
int availableWidth = getSize().width - insets.left - insets.right;
// Determine the rows to draw within the clipped bounds.
Rectangle clip = g.getClipBounds();
int rowStartOffset = component.viewToModel(new Point(0, clip.y));
int endOffset = component.viewToModel(new Point(0, clip.y + clip.height));
while (rowStartOffset <= endOffset)
{
try
{
if (isCurrentLine(rowStartOffset))
{
g.setColor(getCurrentLineForeground());
} else
{
g.setColor(getForeground());
}
// Get the line number as a string and then determine the
// "X" and "Y" offsets for drawing the string.
String lineNumber = getTextLineNumber(rowStartOffset);
int stringWidth = fontMetrics.stringWidth(lineNumber);
int x = getOffsetX(availableWidth, stringWidth) + insets.left;
int y = getOffsetY(rowStartOffset, fontMetrics);
g.drawString(lineNumber, x, y);
// Move to the next row
rowStartOffset = Utilities.getRowEnd(component, rowStartOffset) + 1;
} catch (Exception e)
{
break;
}
}
}
/*
* We need to know if the caret is currently positioned on the line we
* are about to paint so the line number can be highlighted.
*/
private boolean isCurrentLine(int rowStartOffset)
{
int caretPosition = component.getCaretPosition();
Element root = component.getDocument().getDefaultRootElement();
if (root.getElementIndex(rowStartOffset) == root.getElementIndex(caretPosition))
{
return true;
} else
{
return false;
}
}
/*
* Get the line number to be drawn. The empty string will be returned
* when a line of text has wrapped.
*/
protected String getTextLineNumber(int rowStartOffset)
{
Element root = component.getDocument().getDefaultRootElement();
int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index);
if (line.getStartOffset() == rowStartOffset)
{
return String.valueOf(index + 1);
} else
{
return "";
}
}
/*
* Determine the X offset to properly align the line number when drawn
*/
private int getOffsetX(int availableWidth, int stringWidth)
{
return (int) ((availableWidth - stringWidth) * digitAlignment);
}
/*
* Determine the Y offset for the current row
*/
private int getOffsetY(int rowStartOffset, FontMetrics fontMetrics)
throws BadLocationException
{
// Get the bounding rectangle of the row
Rectangle r = component.modelToView(rowStartOffset);
int lineHeight = fontMetrics.getHeight();
int y = r.y + r.height;
int descent = 0;
// The text needs to be positioned above the bottom of the bounding
// rectangle based on the descent of the font(s) contained on the row.
if (r.height == lineHeight) // default font is being used
{
descent = fontMetrics.getDescent();
} else // We need to check all the attributes for font changes
{
if (fonts == null)
{
fonts = new HashMap<>();
}
Element root = component.getDocument().getDefaultRootElement();
int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index);
for (int i = 0; i < line.getElementCount(); i++)
{
Element child = line.getElement(i);
AttributeSet as = child.getAttributes();
String fontFamily = (String) as.getAttribute(StyleConstants.FontFamily);
Integer fontSize = (Integer) as.getAttribute(StyleConstants.FontSize);
String key = fontFamily + fontSize;
FontMetrics fm = fonts.get(key);
if (fm == null)
{
Font font = new Font(fontFamily, Font.PLAIN, fontSize);
fm = component.getFontMetrics(font);
fonts.put(key, fm);
}
descent = Math.max(descent, fm.getDescent());
}
}
return y - descent;
}
//
// Implement CaretListener interface
//
#Override
public void caretUpdate(CaretEvent e)
{
// Get the line the caret is positioned on
int caretPosition = component.getCaretPosition();
Element root = component.getDocument().getDefaultRootElement();
int currentLine = root.getElementIndex(caretPosition);
// Need to repaint so the correct line number can be highlighted
if (lastLine != currentLine)
{
repaint();
lastLine = currentLine;
}
}
//
// Implement DocumentListener interface
//
#Override
public void changedUpdate(DocumentEvent e)
{
documentChanged();
}
#Override
public void insertUpdate(DocumentEvent e)
{
documentChanged();
}
#Override
public void removeUpdate(DocumentEvent e)
{
documentChanged();
}
/*
* A document change may affect the number of displayed lines of text.
* Therefore the lines numbers will also change.
*/
private void documentChanged()
{
// View of the component has not been updated at the time
// the DocumentEvent is fired
SwingUtilities.invokeLater(() ->
{
try
{
int endPos = component.getDocument().getLength();
Rectangle rect = component.modelToView(endPos);
if (rect != null && rect.y != lastHeight)
{
setPreferredWidth();
repaint();
lastHeight = rect.y;
}
} catch (BadLocationException ex)
{ /* nothing to do */ }
});
}
//
// Implement PropertyChangeListener interface
//
#Override
public void propertyChange(PropertyChangeEvent evt)
{
if (evt.getNewValue() instanceof Font)
{
if (updateFont)
{
Font newFont = (Font) evt.getNewValue();
setFont(newFont);
lastDigits = 0;
setPreferredWidth();
} else
{
repaint();
}
}
}
}
Then line numbers can be added like this:
TextLineNumber textLineNumber = new TextLineNumber(sourceCodeArea);
sourceCodeAreaScrollPane.setRowHeaderView(textLineNumber);

Categories