
com.threerings.chat.ChatGlyph Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nenya Show documentation
Show all versions of nenya Show documentation
Facilities for making networked multiplayer games.
The newest version!
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// https://github.com/threerings/nenya
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.chat;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import javax.swing.Icon;
import com.samskivert.swing.Label;
import com.samskivert.swing.util.SwingUtil;
import com.threerings.media.MediaPanel;
import com.threerings.media.animation.Animation;
import com.threerings.media.image.ColorUtil;
import static com.threerings.NenyaLog.log;
/**
* Responsible for rendering a "unit" of chat on a {@link MediaPanel}.
*/
public class ChatGlyph extends Animation
{
/** Can be used by the Overlay to mark our position in the history. */
public int histIndex;
/**
* Construct a chat glyph.
*
* @param owner the subtitle overlay that ownz us.
* @param bounds the bounds of the glyph
* @param shape the shape to draw/outline.
* @param icon the Icon to draw inside the bubble, or null for none
* @param iconpos the virtual coordinate at which to draw the icon (can be null if no icon)
* @param label the Label to draw inside the bubble.
* @param labelpos the virtual coordinate at which to draw the label
*/
public ChatGlyph (SubtitleChatOverlay owner, int type, long lifetime, Rectangle bounds,
Shape shape, Icon icon, Point iconpos, Label label, Point labelpos, Color outline)
{
super(bounds);
jiggleBounds();
_owner = owner;
_shape = shape;
_type = type;
_icon = icon;
_ipos = iconpos;
_label = label;
_lpos = labelpos;
_lifetime = lifetime;
_outline = outline;
if (Color.BLACK.equals(_outline)) {
_background = Color.WHITE;
} else {
_background = ColorUtil.blend(Color.WHITE, _outline, .8f);
}
}
/**
* Sets whether or not this glyph is in dimmed mode.
*/
public void setDim (boolean dim)
{
if (_dim != dim) {
_dim = dim;
invalidate();
}
}
/**
* Render the chat glyph with no thought to dirty rectangles or
* restoring composites.
*/
public void render (Graphics2D gfx)
{
Object oalias = SwingUtil.activateAntiAliasing(gfx);
gfx.setColor(getBackground());
gfx.fill(_shape);
gfx.setColor(_outline);
gfx.draw(_shape);
SwingUtil.restoreAntiAliasing(gfx, oalias);
if (_icon != null) {
_icon.paintIcon(_owner.getTarget(), gfx, _ipos.x, _ipos.y);
}
gfx.setColor(Color.BLACK);
_label.render(gfx, _lpos.x, _lpos.y);
}
/**
* Get the internal chat type of this bubble.
*/
public int getType ()
{
return _type;
}
/**
* Returns the shape of this chat bubble.
*/
public Shape getShape ()
{
return _shape;
}
@Override
public void setLocation (int x, int y)
{
// we'll need these so that we can translate our complex shapes
int dx = x - _bounds.x, dy = y - _bounds.y;
super.setLocation(x, y);
if (dx != 0 || dy != 0) {
// update our icon position, if any
if (_ipos != null) {
_ipos.translate(dx, dy);
}
// update our label position
_lpos.translate(dx, dy);
if (_shape instanceof Area) {
((Area) _shape).transform(
AffineTransform.getTranslateInstance(dx, dy));
} else if (_shape instanceof Polygon) {
((Polygon) _shape).translate(dx, dy);
} else {
log.warning("Unable to translate shape", "glyph", this, "shape", _shape + "]!");
}
}
}
/**
* Called when the view has scrolled. The default implementation adjusts the glyph to remain
* in the same position relative to the view rather than allowing it to scroll with the view.
*/
public void viewDidScroll (int dx, int dy)
{
translate(dx, dy);
}
/**
* Attempt to translate this glyph.
*/
public void translate (int dx, int dy)
{
setLocation(_bounds.x + dx, _bounds.y + dy);
}
@Override
public void tick (long tickStamp)
{
// set up our born stamp if we've got one
if (_bornStamp == 0L) {
_bornStamp = tickStamp;
invalidate(); // make sure we're painted the first time
}
// if we're not yet ready to die, do nothing
long deathTime = _bornStamp + _lifetime;
if (tickStamp < deathTime) {
/* TEMPORARILY disabled blinking until we sort it out
if (_type == SubtitleChatOverlay.ATTENTION) {
float alphaWas = _alpha;
long val = tickStamp - _bornStamp;
if ((val < 3000) && (val % 1000 < 500)) {
_alpha = 0f;
} else {
_alpha = ALPHA;
}
if (_alpha != alphaWas) {
invalidate();
}
}
*/
return;
}
long msecs = tickStamp - deathTime;
if (msecs >= ANIM_TIME) {
_alpha = 0f;
// stick a fork in ourselves
_finished = true;
_owner.glyphExpired(this);
} else {
_alpha = ALPHA * (ANIM_TIME - msecs) / ANIM_TIME;
}
invalidate();
}
@Override
public void fastForward (long timeDelta)
{
if (_bornStamp > 0L) {
_bornStamp += timeDelta;
}
}
@Override
public void paint (Graphics2D gfx)
{
float alpha = _dim ? _alpha / 3 : _alpha;
if (alpha == 0f) {
return;
}
if (alpha != 1f) {
Composite ocomp = gfx.getComposite();
gfx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
render(gfx);
gfx.setComposite(ocomp);
} else {
render(gfx);
}
}
protected Color getBackground ()
{
return _background;
}
/**
* The damn repaint manager expects 1 more pixel than the shape gives, so we manually resize.
*/
protected void jiggleBounds ()
{
_bounds.setSize(_bounds.width + 1, _bounds.height + 1);
}
/** The subtitle chat overlay that ownz us. */
protected SubtitleChatOverlay _owner;
/** The type of chat represented by this glyph. */
protected int _type;
/** The shape of this glyph. */
protected Shape _shape;
/** The visual icon, or null for none. */
protected Icon _icon;
/** The position at which we render our icon. */
protected Point _ipos;
/** The textual label. */
protected Label _label;
/** The position at which we render our text. */
protected Point _lpos;
/** The alpha level that we'll render at when fading out. */
protected float _alpha = ALPHA;
/** Whether we're in dimmed mode. */
protected boolean _dim = false;
/** The length of the fade animation. */
protected static final long ANIM_TIME = 800L;
/** The number of milliseconds to wait before this bubble will expire
* and should begin to fade out. */
protected long _lifetime;
/** The time at which we came into being on the screen. */
protected long _bornStamp;
/** Our outline color. */
protected Color _outline;
/** Our background color. */
protected Color _background;
/** The initial alpha of all chat glyphs. */
protected static final float ALPHA = .9f;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy