I'm trying to create a widget like in the video for scrolling in all directions with a snapping option
https://cdn.dribbble.com/users/5576/screenshots/7406355/media/30bf6f1d359b8b409cc25c612de9432c.mp4
what I'm tried till now
Widget build(BuildContext context) {
return InteractiveViewer(
constrained: false,
transformationController: _transformController,
onInteractionStart: _onPanStarted,
onInteractionUpdate: _onPanUpdated,
onInteractionEnd: _onPanEnd,
scaleEnabled: false,
panEnabled: true,
child: _buildGrid(),
);
}
and build function is
Widget _buildGrid() {
Table _table = Table(
defaultColumnWidth: FixedColumnWidth(_cardWidth),
children: List.generate(
_rows.length,
(x) => _rows[x],
),
);
return _table;
}
snapping function when dragging is ended
_snapTo({bool animate = true}) {
if (_animationController.isAnimating) {
_animationController.stop();
}
// moving to
var xDistance =
_currentOffset.getTranslation().x - _oldOffset.getTranslation().x;
var yDistance =
_currentOffset.getTranslation().y - _oldOffset.getTranslation().y;
if (xDistance > 20) {
_currentCol--;
_direction = Direction.LEFT;
} else if (xDistance < -20) {
_currentCol++;
_direction = Direction.RIGHT;
}
if (yDistance > 30) {
_currentRow--;
if (_direction == Direction.RIGHT) {
_direction = Direction.TOP_RIGHT;
} else if (_direction == Direction.LEFT) {
_direction = Direction.TOP_LEFT;
} else {
_direction = Direction.TOP;
}
} else if (yDistance < -30) {
_currentRow++;
if (_direction == Direction.RIGHT) {
_direction = Direction.DOWN_RIGHT;
} else if (_direction == Direction.LEFT) {
_direction = Direction.DOWN_LEFT;
} else {
_direction = Direction.DOWN;
}
}
print("$_currentCol,$_currentRow");
log("${xDistance.toInt()},${yDistance.toInt()}");
// Getting x,y to move to
var dx = (_currentCol * _cardWidth) - ((Get.width - _cardWidth) / 2);
var dy = (_currentRow * _cardHeight) - ((Get.height - _cardHeight) / 2);
var matrix = Matrix4.translationValues(-dx, -dy, 0);
_animation = Matrix4Tween(begin: _currentOffset, end: matrix).animate(
CurvedAnimation(
parent: _animationController,
curve: Curves.linear,
),
);
if (_currentOffset == matrix) return;
if (animate) {
_animationController.forward(from: 0);
} else {
setState(() {
_transformController.value = matrix;
});
}
// ..reset();
// ..animateTo(1);
_oldOffset = matrix;
// setState(() {});
// _animationController.reset();
}
any suggestion will be appreciated
Related
I want to make a sparkling animation in flutter
How to make this in flutter??
I would suggest a custom paint approche. My awswer is highly customisable. I only change the innerOuterRadiusRatio and the velocity. You can change the color or Opacity, the number of edges of the star, the rotation(angleOffsetToCenterStar),and the beamLength.
import 'package:flutter/material.dart';
import 'dart:math' as math;
class Sparkling extends StatefulWidget {
const Sparkling({Key? key}) : super(key: key);
#override
_SparklingState createState() => _SparklingState();
}
class _SparklingState extends State<Sparkling>
with SingleTickerProviderStateMixin {
late AnimationController animationController;
late Animation animation;
late List<MyStar> myStars;
#override
void initState() {
super.initState();
myStars = <MyStar>[];
animationController = AnimationController(
vsync: this,
duration: const Duration(
milliseconds: 250,
));
animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
animationController.reverse();
} else if (status == AnimationStatus.dismissed) {
for (final star in myStars) {
star.isEnable = math.Random().nextBool();
}
animationController.forward();
}
});
animation = Tween<double>(begin: 0, end: 8).animate(CurvedAnimation(
parent: animationController, curve: Curves.easeInOutSine));
animation.addListener(() {
setState(() {});
});
animationController.forward();
}
void postFrameCallback(_) {
if (!mounted) {
return;
}
final size = MediaQuery.of(context).size;
if (myStars.isEmpty) {
myStars = List.generate(60, (index) {
double velocityX = 2 * (math.Random().nextDouble());//max velocity 2
double velocityY = 2 * (math.Random().nextDouble());
velocityX = math.Random().nextBool() ? velocityX : -velocityX;
velocityY = math.Random().nextBool() ? velocityY : -velocityY;
return MyStar(
isEnable: math.Random().nextBool(),
innerCirclePoints: 4,
beamLength: math.Random().nextDouble() * (8 - 2) + 2,
innerOuterRadiusRatio: 0.0,
angleOffsetToCenterStar: 0,
center: Offset(size.width * (math.Random().nextDouble()),
size.height * (math.Random().nextDouble())),
velocity: Offset(velocityX, velocityY),
color: Colors.white);
});
} else {
for (final star in myStars) {
star.center = star.center + star.velocity;
if (star.isEnable) {
star.innerOuterRadiusRatio = animation.value;
if (star.center.dx >= size.width) {
if (star.velocity.dy > 0) {
star.velocity = const Offset(-1, 1);
} else {
star.velocity = const Offset(-1, -1);
}
star.center = Offset(size.width, star.center.dy);
} else if (star.center.dx <= 0) {
if (star.velocity.dy > 0) {
star.velocity = const Offset(1, 1);
} else {
star.velocity = const Offset(1, -1);
}
star.center = Offset(0, star.center.dy);
} else if (star.center.dy >= size.height) {
if (star.velocity.dx > 0) {
star.velocity = const Offset(1, -1);
} else {
star.velocity = const Offset(-1, -1);
}
star.center = Offset(star.center.dx, size.height);
} else if (star.center.dy <= 0) {
if (star.velocity.dx > 0) {
star.velocity = const Offset(1, 1);
} else {
star.velocity = const Offset(-1, 1);
}
star.center = Offset(star.center.dx, 0);
}
}
}
}
}
#override
Widget build(BuildContext context) {
WidgetsBinding.instance!.addPostFrameCallback(postFrameCallback);
return CustomPaint(
size: MediaQuery.of(context).size,
painter: StarPainter(
myStars: myStars,
));
}
}
The CustomPainter
class StarPainter extends CustomPainter {
List<MyStar> myStars;
StarPainter({required this.myStars});
List<Map> calcStarPoints(
{required double centerX,
required double centerY,
required int innerCirclePoints,
required double innerRadius,
required double outerRadius,
required double angleOffsetToCenterStar}) {
final angle = ((math.pi) / innerCirclePoints);
final totalPoints = innerCirclePoints * 2; // 10 in a 5-points star
List<Map> points = [];
for (int i = 0; i < totalPoints; i++) {
bool isEvenIndex = i % 2 == 0;
final r = isEvenIndex ? outerRadius : innerRadius;
var currY = centerY + math.cos(i * angle + angleOffsetToCenterStar) * r;
var currX = centerX + math.sin(i * angle + angleOffsetToCenterStar) * r;
points.add({'x': currX, 'y': currY});
}
return points;
}
#override
void paint(Canvas canvas, Size size) {
for (final myStar in myStars) {
final innerRadius = myStar.beamLength / myStar.innerCirclePoints;
final outerRadius = innerRadius * myStar.innerOuterRadiusRatio;
List<Map> points = calcStarPoints(
centerX: myStar.center.dx,
centerY: myStar.center.dy,
innerCirclePoints: myStar.innerCirclePoints,
innerRadius: innerRadius,
outerRadius: outerRadius,
angleOffsetToCenterStar: myStar.angleOffsetToCenterStar);
var star = Path()..moveTo(points[0]['x'], points[0]['y']);
for (var point in points) {
star.lineTo(point['x'], point['y']);
}
canvas.drawPath(
star,
Paint()..color = myStar.color,
);
}
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
And the class MyStar.
class MyStar {
bool isEnable;
int innerCirclePoints; //how many edges you need?
double beamLength;
double
innerOuterRadiusRatio; // outter circle is x2 the inner // set star sharpness/chubbiness
double angleOffsetToCenterStar;
Offset center;
Offset velocity;
Color color;
MyStar(
{required this.isEnable,
required this.innerCirclePoints,
required this.beamLength,
required this.innerOuterRadiusRatio,
required this.angleOffsetToCenterStar,
required this.center,
required this.velocity,
required this.color});
}
I would suggest using Lottie animations.
If you make a quick search you can find the one that matches your needs:
https://lottiefiles.com/search?q=star&category=animations
If you found the right now click on it and press download -> lottie.json and then install this package in flutter:
https://pub.dev/packages/lottie
Then you simply add the downloaded json animation in your asset folder and reference it like this:
Lottie.asset(
'assets/LottieLogo1.json',
width: 200,
height: 200,
fit: BoxFit.fill,
animate: true,
repeat: true
),
With this you have an beautiful repeating animation.
You can also use an controller to adapt everything even more.
Basically you can also make an animation in after effect and export it as an json animation with the bodymovin plugin
Use https://rive.app/ for creating animation for flutter applications. You can find many tutorials of creating animation in rive.app and integrating in flutter app.
I've an issue working with custom editor, my editing focus is changed without any action from my part.
Basically, this is a screen of my custom editor in a neutral state (just added empty translation entity):
And this is a screen when my focus got change without reason. As you can see, my editor detected that 2 keys was the same and notify it by a message at the top of the box, the issue is that I was actually editing the second box's "key" field, and my focus changed on the first box (highlighted blue). If I don't display the warning message, I don't have any issue so I guess it comes from there..
This is my custom editor script :
[CustomEditor(typeof(InternationalizationDatabaseSO))]
public class InternationalizationDatabaseSOEditor : Editor
{
#region Constantes
private const string ITEMS_PROPERTY_NAME = "_mItems";
private const string ITEM_CATEGORY_NAME = "_mCategory";
private const string ITEM_KEY_NAME = "_mKey";
private const string ITEM_VALUES_NAME = "_mValues";
private const string ITEM_VALUE_LANGUAGE = "_mLanguage";
private const string ITEM_VALUE_VALUE = "_mValue";
#endregion
#region Attributs
private InternationalizationDatabaseSO _mDatabase;
#endregion
#region Methods
private void OnEnable()
{
Init();
}
private void Init()
{
_mDatabase = target as InternationalizationDatabaseSO;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
SerializedProperty itemsProperty = serializedObject.FindProperty(ITEMS_PROPERTY_NAME);
int arraySize = itemsProperty.arraySize;
EditorGUI.BeginDisabledGroup(false);
{
EditorGUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
if (OnInspectorGUIButton("+", 40, 25, Color.white, Color.green))
{
itemsProperty.arraySize++;
itemsProperty.GetArrayElementAtIndex(itemsProperty.arraySize - 1).FindPropertyRelative(ITEM_CATEGORY_NAME).stringValue = "";
itemsProperty.GetArrayElementAtIndex(itemsProperty.arraySize - 1).FindPropertyRelative(ITEM_KEY_NAME).stringValue = "";
serializedObject.ApplyModifiedProperties();
Init();
return;
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
for(int i = 0; i < arraySize; i++)
{
if(OnInspectorGUIItem(i) == false)
{
serializedObject.ApplyModifiedProperties();
Init();
return;
}
}
}
EditorGUI.EndDisabledGroup();
serializedObject.ApplyModifiedProperties();
}
private bool OnInspectorGUIItem(int index)
{
SerializedProperty itemsProperty = serializedObject.FindProperty(ITEMS_PROPERTY_NAME);
SerializedProperty itemCategory = itemsProperty.GetArrayElementAtIndex(index).FindPropertyRelative(ITEM_CATEGORY_NAME);
SerializedProperty itemKey = itemsProperty.GetArrayElementAtIndex(index).FindPropertyRelative(ITEM_KEY_NAME);
EditorGUI.indentLevel += 1;
EditorGUILayout.BeginVertical(GUI.skin.box);
{
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.BeginVertical();
{
if(KeyAlreadyExist(index, itemKey.stringValue))
{
OnInspectorGUIText("Key already exists", 12, Color.red, FontStyle.Bold, false);
}
OnInspectorGUIText("Key : " + itemKey.stringValue, 12, FontStyle.Normal, false);
}
EditorGUILayout.EndVertical();
GUILayout.FlexibleSpace();
if(OnInspectorGUIButton("-", 40, 25, Color.white, Color.red))
{
itemCategory.stringValue = "";
itemKey.stringValue = "";
itemsProperty.DeleteArrayElementAtIndex(index);
return false;
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
EditorGUILayout.BeginVertical();
{
GUIStyle style = EditorStyles.foldout;
style.fontSize = 15;
style.fontStyle = FontStyle.Bold;
itemCategory.isExpanded = EditorGUILayout.Foldout(itemCategory.isExpanded, "General informations", style);
if(itemCategory.isExpanded)
{
EditorGUILayout.Space();
EditorGUILayout.PropertyField(itemCategory, new GUIContent("Category"));
EditorGUILayout.PropertyField(itemKey, new GUIContent("Key"));
}
if(OnInspectorGUIItemLanguage(index, itemsProperty.GetArrayElementAtIndex(index)) == false)
{
return false;
}
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
EditorGUI.indentLevel -= 1;
return true;
}
private bool OnInspectorGUIItemLanguage(int index, SerializedProperty propertyItem)
{
EditorGUILayout.BeginVertical();
{
GUIStyle style = EditorStyles.foldout;
style.fontSize = 15;
style.normal.textColor = Color.black;
style.fontStyle = FontStyle.Bold;
SerializedProperty itemValues = propertyItem.FindPropertyRelative(ITEM_VALUES_NAME);
itemValues.isExpanded = EditorGUILayout.Foldout(itemValues.isExpanded, "Languages informations", style);
if(itemValues.isExpanded)
{
EditorGUILayout.Space();
EditorGUILayout.BeginVertical();
{
EditorGUILayout.BeginHorizontal();
{
int nbTranslation = itemValues.arraySize;
EditorGUILayout.LabelField("Nb translation : " + nbTranslation);
GUILayout.FlexibleSpace();
if (OnInspectorGUIButton("+", 20, 20, Color.white, Color.green))
{
itemValues.arraySize++;
itemValues.GetArrayElementAtIndex(itemValues.arraySize - 1).FindPropertyRelative(ITEM_VALUE_LANGUAGE).intValue = 0;
itemValues.GetArrayElementAtIndex(itemValues.arraySize - 1).FindPropertyRelative(ITEM_VALUE_VALUE).stringValue = "";
return false;
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
if (itemValues.arraySize > 0)
{
string translatedLanguages = "{";
foreach (InternationalizationDatabaseItemLanguage item in _mDatabase.Items[index].Values)
{
translatedLanguages += item.Language.ToString() + ", ";
}
if (translatedLanguages.Length > 2)
{
translatedLanguages = translatedLanguages.Remove(translatedLanguages.Length - 2);
}
translatedLanguages += "}";
//EditorStyles.label.stretchHeight = true;
EditorStyles.label.wordWrap = true;
EditorGUILayout.LabelField("Translated : \n" + translatedLanguages);
EditorGUILayout.Space();
for (int i = 0; i < itemValues.arraySize; i++)
{
EditorGUILayout.BeginVertical(GUI.skin.box);
{
EditorGUILayout.Space();
InternationalizationDatabaseItemLanguage item = _mDatabase.Items[index].Values[i];
SerializedProperty propLanguage = itemValues.GetArrayElementAtIndex(i).FindPropertyRelative(ITEM_VALUE_LANGUAGE);
SerializedProperty propValue = itemValues.GetArrayElementAtIndex(i).FindPropertyRelative(ITEM_VALUE_VALUE);
EditorGUILayout.BeginHorizontal();
{
if (LanguageAlreadyExist(index, i, _mDatabase.Items[index].Values[i].Language))
{
OnInspectorGUIText("Warning language translation already exist", 12, Color.red, FontStyle.Bold, false);
}
GUILayout.FlexibleSpace();
if (OnInspectorGUIButton("-", 20, 20, Color.white, Color.red))
{
itemValues.DeleteArrayElementAtIndex(i);
return false;
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
EditorGUILayout.PropertyField(propLanguage, new GUIContent("Language"));
EditorGUILayout.PrefixLabel("Translation");
propValue.stringValue = EditorGUILayout.TextArea(propValue.stringValue, GUILayout.Height(80));
}
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
}
}
}
EditorGUILayout.EndVertical();
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
return true;
}
private bool KeyAlreadyExist(int currentIndex, string key)
{
for(int i = 0; i < _mDatabase.Items.Count; i++)
{
InternationalizationDatabaseItem item = _mDatabase.Items[i];
if(currentIndex != i && item.Key == key && string.IsNullOrEmpty(key) == false)
{
return true;
}
}
return false;
}
private bool LanguageAlreadyExist(int indexInDatabase, int currentIndex, SystemLanguage language)
{
for (int i = 0; i < _mDatabase.Items[indexInDatabase].Values.Count; i++)
{
InternationalizationDatabaseItemLanguage item = _mDatabase.Items[indexInDatabase].Values[i];
if (currentIndex != i && item.Language == language)
{
return true;
}
}
return false;
}
private void OnInspectorGUIText(string text, FontStyle fontStyle, bool prefixLabel)
{
GUIStyle style = new GUIStyle();
style.fontStyle = fontStyle;
style.wordWrap = true;
if (prefixLabel)
{
EditorGUILayout.PrefixLabel(text, GUIStyle.none, style);
}
else
{
EditorGUILayout.LabelField(text, style);
}
}
private void OnInspectorGUIText(string text, int fontSize, FontStyle fontStyle, bool prefixLabel)
{
GUIStyle style = new GUIStyle();
style.fontSize = fontSize;
style.fontStyle = fontStyle;
style.wordWrap = true;
if (prefixLabel)
{
EditorGUILayout.PrefixLabel(text, GUIStyle.none, style);
}
else
{
EditorGUILayout.LabelField(text, style);
}
}
private void OnInspectorGUIText(string text, int fontSize, Color textColor, FontStyle fontStyle, bool prefixLabel)
{
GUIStyle style = new GUIStyle();
style.fontSize = fontSize;
style.normal.textColor = textColor;
style.fontStyle = fontStyle;
style.wordWrap = true;
if(prefixLabel)
{
EditorGUILayout.PrefixLabel(text, GUIStyle.none, style);
}
else
{
EditorGUILayout.LabelField(text, style);
}
}
private bool OnInspectorGUIButton(string label, int width, int height, Color textColor, Color backgroundColor)
{
Color saveColor = GUI.backgroundColor;
GUILayoutOption[] options = { GUILayout.Width(width), GUILayout.Height(height) };
GUIStyle style = new GUIStyle(GUI.skin.button);
style.normal.textColor = textColor;
GUI.backgroundColor = backgroundColor;
bool pressed = GUILayout.Button(label, style, options);
GUI.backgroundColor = saveColor;
return pressed;
}
#endregion
}
Have you any suggestion ? Thanks
Have a nice day.
Are you sure you are not sharing reference to the same property in both boxes ?
Ok finally figured it out, I mean, I'm not still totally certain why it happens, but found a workaround thanks to : https://forum.unity.com/threads/editor-gui-inputfield-loses-focus-when-gui-updates.542147/
Basically, I'm just displaying always the LabelField, but if there is no error, I simply set the errorMessage to an empty string
I have 2 identical Items in the scene. Each one can be dragged and also accepts interaction with other dragged objects. To avoid interactions with itself I set drop.accept = false in appropriate drag functions. It works fine when I drag for example rect1 over rect2. rect2 responds to entering and exiting events. But if after that I try to move rect2 over rect1 the drag mechanism stops working and dragging events are not called at all, even if I again move rect1 over rect2.
My target is to know, at each moment of dragging, what object is dragged and on which objects it is moved over.
import QtQuick 2.4
import QtQuick.Window 2.0
Window {
id: win
width: 800
height: 600
Rectangle {
id: rect1
objectName: "rect1"
width: 100
height: 100
x: 200
y: 200
color: "orange"
property bool hold: false
z: hold ? 2 : 1
Text {
anchors.centerIn: parent
text: "rect1"
}
MouseArea {
id: area1
anchors.fill: parent
drag.target: rect1.hold ? rect1 : undefined
onPressed: rect1.hold = true;
onReleased: rect1.hold = false;
}
DropArea {
anchors.fill: parent
onEntered: {
if(drag.source == rect1) {
drag.accepted = false;
}
else{
drag.accepted = true;
console.log("rect1: entered");
}
}
onExited: {
if(drag.source == rect1) {
drag.accepted = false;
}
else {
drag.accepted = true;
console.log("rect1: exited");
}
}
}
Drag.active: rect1.hold
Drag.source: rect1.hold ? rect1 : undefined
Drag.hotSpot: Qt.point(width/2,height/2)
}
Rectangle {
id: rect2
objectName: "rect2"
width: 100
height: 100
x: 500
y: 300
color: "lightblue"
property bool hold: false
z: hold ? 2 : 1
Text {
anchors.centerIn: parent
text: "rect2"
}
MouseArea {
id: area2
anchors.fill: parent
drag.target: rect2.hold ? rect2 : undefined
onPressed: rect2.hold = true;
onReleased: rect2.hold = false;
}
DropArea {
anchors.fill: parent
onEntered: {
if(drag.source == rect2) {
drag.accepted = false;
}
else {
drag.accepted = true;
console.log("rect2: entered");
}
}
onExited: {
if(drag.source == rect2) {
drag.accepted = false;
}
else {
drag.accepted = true;
console.log("rect2: exited");
}
}
}
Drag.active: rect2.hold
Drag.source: rect2.hold ? rect2 : undefined
Drag.hotSpot: Qt.point(width/2,height/2)
}
}
i am making a game in which i am using is-clicked function when i click the object the letter written on it displayed now i want that when i clicked the same object again the word disappear... now how can i do that?
#pragma strict
static var nextPos = 200;
var word: String;
var sel: String;
var isClicked : boolean=false;
var xpos: float = 200;
function OnMouseDown()
{
if (!isClicked) {
isClicked = true;
xpos = nextPos;
nextPos += 8;
}
}
function OnGUI()
{
if (gameObject.name == "Sphere(Clone)" && isClicked )
{
GUI.Label(new Rect(xpos,260,400,100), "A");
}
else if (gameObject.name == "Sphere 1(Clone)" && isClicked )
{
GUI.Label(new Rect(xpos,260,400,100), "B");
}
else if (gameObject.name == "Sphere 2(Clone)" && isClicked )
{
GUI.Label(new Rect(xpos,260,400,100), "C");
}
else if (gameObject.name == "Sphere 3(Clone)" && isClicked )
{
GUI.Label(new Rect(xpos,260,400,100), "D");
}
}
write in OnMouseDown
else if(isClicked)
{
isClicked = false;
// do your xpos stuff here
}
I am trying to write a custom Label widget which supports animated GIF, but I found it's hard for me. Is there already such a widget available for use?
Edit---------------------------------------------------------------------------------
When I try to use GifCLabel class, it works fine with a gif picture(animated), but if I try to set a static png picture to it when the animation thread is running, the png picture will not be shown, but a frame of the animated gif is shown, here is my code :
public PageDemo(Shell parentShell) {
super(parentShell);
Composite topComp = new Composite(parentShell, SWT.NONE);
topComp.setLayout(new FormLayout());
final GifCLabel gl = new GifCLabel(topComp, SWT.CENTER);
gl.setText("some message");
gl.setGifImage("c://loading.gif");
Display.getCurrent().timerExec(5000, new Runnable(){
#Override
public void run() {
// gl.setGifImage("c:\\filter.png"); // also not work
gl.setImage(SWTResourceManager.getImage("c:\\filter.png"));
}
});
}
Bug fix--------------------------------------------------------------------------------
I think Sorceror's code is good, but there seems is a little bug:
public void run() {
while (run) {
int delayTime = loader.data[imageNumber].delayTime;
try {
Thread.sleep(delayTime * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!GifCLabel.this.isDisposed()) {
// if a asynchronous thread is running, this new runnable will be queued
GifCLabel.this.getDisplay().asyncExec(new Runnable() {
public void run() {
if(!run){
return;
}
if (!GifCLabel.this.isDisposed()) {
imageNumber = imageNumber == loader.data.length - 1 ? 0 : imageNumber + 1;
if (!GifCLabel.this.image.isDisposed())
GifCLabel.this.image.dispose();
ImageData nextFrameData = loader.data[imageNumber];
System.out.println("set to frame " + imageNumber);
GifCLabel.this.image = new Image(GifCLabel.this.getDisplay(), nextFrameData);
GifCLabel.this.redraw();
} else
stopRunning();
}
});
} else
stopRunning();
}
}
notice what I added in above :
if(!run){
return;
}
In the article Taking a look at SWT Images - Animation part is whole source code of gif animation for almost any purpose.. It didn't help?
EDIT
So I did the job, and here is a GifCLabel class for you which supports gif animation.. It's derived from SWT CLabel class, see setGifImage(String path), setGifImage(InputStream inputStream) methods and GifThread private class.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.accessibility.*;
/**
* <p><b>This class supports gif animation in label and is derived from {#link GifCLabel} SWT class (see {#link #setGifImage(InputStream)} or {#link #setGifImage(String)})</b><br />
* <b>Changes by Sorceror, (a)sync.exec(...) call fix by YAMaiDie</b></p>
*/
public class GifCLabel extends Canvas {
/** Gap between icon and text */
private static final int GAP = 5;
/** Left and right margins */
private static final int DEFAULT_MARGIN = 3;
/** a string inserted in the middle of text that has been shortened */
private static final String ELLIPSIS = "..."; //$NON-NLS-1$ // could use the ellipsis glyph on some platforms "\u2026"
/** the alignment. Either CENTER, RIGHT, LEFT. Default is LEFT*/
private int align = SWT.LEFT;
private int leftMargin = DEFAULT_MARGIN;
private int topMargin = DEFAULT_MARGIN;
private int rightMargin = DEFAULT_MARGIN;
private int bottomMargin = DEFAULT_MARGIN;
private String text;
private Image image;
private String appToolTipText;
private boolean ignoreDispose;
private Image backgroundImage;
private Color[] gradientColors;
private int[] gradientPercents;
private boolean gradientVertical;
private Color background;
private GifThread thread = null;
private static int DRAW_FLAGS = SWT.DRAW_MNEMONIC | SWT.DRAW_TAB | SWT.DRAW_TRANSPARENT | SWT.DRAW_DELIMITER;
public GifCLabel(Composite parent, int style) {
super(parent, checkStyle(style));
if ((style & (SWT.CENTER | SWT.RIGHT)) == 0) style |= SWT.LEFT;
if ((style & SWT.CENTER) != 0) align = SWT.CENTER;
if ((style & SWT.RIGHT) != 0) align = SWT.RIGHT;
if ((style & SWT.LEFT) != 0) align = SWT.LEFT;
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
onPaint(event);
}
});
addTraverseListener(new TraverseListener() {
public void keyTraversed(TraverseEvent event) {
if (event.detail == SWT.TRAVERSE_MNEMONIC) {
onMnemonic(event);
}
}
});
addListener(SWT.Dispose, new Listener() {
public void handleEvent(Event event) {
onDispose(event);
}
});
initAccessible();
}
private static int checkStyle (int style) {
if ((style & SWT.BORDER) != 0) style |= SWT.SHADOW_IN;
int mask = SWT.SHADOW_IN | SWT.SHADOW_OUT | SWT.SHADOW_NONE | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
style = style & mask;
return style |= SWT.NO_FOCUS | SWT.DOUBLE_BUFFERED;
}
public Point computeSize(int wHint, int hHint, boolean changed) {
checkWidget();
Point e = getTotalSize(image, text);
if (wHint == SWT.DEFAULT){
e.x += leftMargin + rightMargin;
} else {
e.x = wHint;
}
if (hHint == SWT.DEFAULT) {
e.y += topMargin + bottomMargin;
} else {
e.y = hHint;
}
return e;
}
private void drawBevelRect(GC gc, int x, int y, int w, int h, Color topleft, Color bottomright) {
gc.setForeground(bottomright);
gc.drawLine(x+w, y, x+w, y+h);
gc.drawLine(x, y+h, x+w, y+h);
gc.setForeground(topleft);
gc.drawLine(x, y, x+w-1, y);
gc.drawLine(x, y, x, y+h-1);
}
char _findMnemonic (String string) {
if (string == null) return '\0';
int index = 0;
int length = string.length ();
do {
while (index < length && string.charAt (index) != '&') index++;
if (++index >= length) return '\0';
if (string.charAt (index) != '&') return Character.toLowerCase (string.charAt (index));
index++;
} while (index < length);
return '\0';
}
public int getAlignment() {
//checkWidget();
return align;
}
public int getBottomMargin() {
//checkWidget();
return bottomMargin;
}
public Image getImage() {
//checkWidget();
return image;
}
public int getLeftMargin() {
//checkWidget();
return leftMargin;
}
public int getRightMargin() {
//checkWidget();
return rightMargin;
}
private Point getTotalSize(Image image, String text) {
Point size = new Point(0, 0);
if (image != null) {
Rectangle r = image.getBounds();
size.x += r.width;
size.y += r.height;
}
GC gc = new GC(this);
if (text != null && text.length() > 0) {
Point e = gc.textExtent(text, DRAW_FLAGS);
size.x += e.x;
size.y = Math.max(size.y, e.y);
if (image != null) size.x += GAP;
} else {
size.y = Math.max(size.y, gc.getFontMetrics().getHeight());
}
gc.dispose();
return size;
}
public int getStyle () {
int style = super.getStyle();
switch (align) {
case SWT.RIGHT: style |= SWT.RIGHT; break;
case SWT.CENTER: style |= SWT.CENTER; break;
case SWT.LEFT: style |= SWT.LEFT; break;
}
return style;
}
public String getText() {
//checkWidget();
return text;
}
public String getToolTipText () {
checkWidget();
return appToolTipText;
}
public int getTopMargin() {
//checkWidget();
return topMargin;
}
private void initAccessible() {
Accessible accessible = getAccessible();
accessible.addAccessibleListener(new AccessibleAdapter() {
public void getName(AccessibleEvent e) {
e.result = getText();
}
public void getHelp(AccessibleEvent e) {
e.result = getToolTipText();
}
public void getKeyboardShortcut(AccessibleEvent e) {
char mnemonic = _findMnemonic(GifCLabel.this.text);
if (mnemonic != '\0') {
e.result = "Alt+"+mnemonic; //$NON-NLS-1$
}
}
});
accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
public void getChildAtPoint(AccessibleControlEvent e) {
e.childID = ACC.CHILDID_SELF;
}
public void getLocation(AccessibleControlEvent e) {
Rectangle rect = getDisplay().map(getParent(), null, getBounds());
e.x = rect.x;
e.y = rect.y;
e.width = rect.width;
e.height = rect.height;
}
public void getChildCount(AccessibleControlEvent e) {
e.detail = 0;
}
public void getRole(AccessibleControlEvent e) {
e.detail = ACC.ROLE_LABEL;
}
public void getState(AccessibleControlEvent e) {
e.detail = ACC.STATE_READONLY;
}
});
}
void onDispose(Event event) {
/* make this handler run after other dispose listeners */
if (ignoreDispose) {
ignoreDispose = false;
return;
}
ignoreDispose = true;
notifyListeners (event.type, event);
event.type = SWT.NONE;
gradientColors = null;
gradientPercents = null;
backgroundImage = null;
text = null;
image = null;
appToolTipText = null;
}
void onMnemonic(TraverseEvent event) {
char mnemonic = _findMnemonic(text);
if (mnemonic == '\0') return;
if (Character.toLowerCase(event.character) != mnemonic) return;
Composite control = this.getParent();
while (control != null) {
Control [] children = control.getChildren();
int index = 0;
while (index < children.length) {
if (children [index] == this) break;
index++;
}
index++;
if (index < children.length) {
if (children [index].setFocus ()) {
event.doit = true;
event.detail = SWT.TRAVERSE_NONE;
}
}
control = control.getParent();
}
}
void onPaint(PaintEvent event) {
Rectangle rect = getClientArea();
if (rect.width == 0 || rect.height == 0) return;
boolean shortenText = false;
String t = text;
Image img = image;
int availableWidth = Math.max(0, rect.width - (leftMargin + rightMargin));
Point extent = getTotalSize(img, t);
if (extent.x > availableWidth) {
img = null;
extent = getTotalSize(img, t);
if (extent.x > availableWidth) {
shortenText = true;
}
}
GC gc = event.gc;
String[] lines = text == null ? null : splitString(text);
// shorten the text
if (shortenText) {
extent.x = 0;
for(int i = 0; i < lines.length; i++) {
Point e = gc.textExtent(lines[i], DRAW_FLAGS);
if (e.x > availableWidth) {
lines[i] = shortenText(gc, lines[i], availableWidth);
extent.x = Math.max(extent.x, getTotalSize(null, lines[i]).x);
} else {
extent.x = Math.max(extent.x, e.x);
}
}
if (appToolTipText == null) {
super.setToolTipText(text);
}
} else {
super.setToolTipText(appToolTipText);
}
// determine horizontal position
int x = rect.x + leftMargin;
if (align == SWT.CENTER) {
x = (rect.width - extent.x)/2;
}
if (align == SWT.RIGHT) {
x = rect.width - rightMargin - extent.x;
}
// draw a background image behind the text
try {
if (backgroundImage != null) {
// draw a background image behind the text
Rectangle imageRect = backgroundImage.getBounds();
// tile image to fill space
gc.setBackground(getBackground());
gc.fillRectangle(rect);
int xPos = 0;
while (xPos < rect.width) {
int yPos = 0;
while (yPos < rect.height) {
gc.drawImage(backgroundImage, xPos, yPos);
yPos += imageRect.height;
}
xPos += imageRect.width;
}
} else if (gradientColors != null) {
// draw a gradient behind the text
final Color oldBackground = gc.getBackground();
if (gradientColors.length == 1) {
if (gradientColors[0] != null) gc.setBackground(gradientColors[0]);
gc.fillRectangle(0, 0, rect.width, rect.height);
} else {
final Color oldForeground = gc.getForeground();
Color lastColor = gradientColors[0];
if (lastColor == null) lastColor = oldBackground;
int pos = 0;
for (int i = 0; i < gradientPercents.length; ++i) {
gc.setForeground(lastColor);
lastColor = gradientColors[i + 1];
if (lastColor == null) lastColor = oldBackground;
gc.setBackground(lastColor);
if (gradientVertical) {
final int gradientHeight = (gradientPercents[i] * rect.height / 100) - pos;
gc.fillGradientRectangle(0, pos, rect.width, gradientHeight, true);
pos += gradientHeight;
} else {
final int gradientWidth = (gradientPercents[i] * rect.width / 100) - pos;
gc.fillGradientRectangle(pos, 0, gradientWidth, rect.height, false);
pos += gradientWidth;
}
}
if (gradientVertical && pos < rect.height) {
gc.setBackground(getBackground());
gc.fillRectangle(0, pos, rect.width, rect.height - pos);
}
if (!gradientVertical && pos < rect.width) {
gc.setBackground(getBackground());
gc.fillRectangle(pos, 0, rect.width - pos, rect.height);
}
gc.setForeground(oldForeground);
}
gc.setBackground(oldBackground);
} else {
if (background != null || (getStyle() & SWT.DOUBLE_BUFFERED) == 0) {
gc.setBackground(getBackground());
gc.fillRectangle(rect);
}
}
} catch (SWTException e) {
if ((getStyle() & SWT.DOUBLE_BUFFERED) == 0) {
gc.setBackground(getBackground());
gc.fillRectangle(rect);
}
}
// draw border
int style = getStyle();
if ((style & SWT.SHADOW_IN) != 0 || (style & SWT.SHADOW_OUT) != 0) {
paintBorder(gc, rect);
}
Rectangle imageRect = null;
int lineHeight = 0, textHeight = 0, imageHeight = 0;
if (img != null) {
imageRect = img.getBounds();
imageHeight = imageRect.height;
}
if (lines != null) {
lineHeight = gc.getFontMetrics().getHeight();
textHeight = lines.length * lineHeight;
}
int imageY = 0, midPoint = 0, lineY = 0;
if (imageHeight > textHeight ) {
if (topMargin == DEFAULT_MARGIN && bottomMargin == DEFAULT_MARGIN) imageY = rect.y + (rect.height - imageHeight) / 2;
else imageY = topMargin;
midPoint = imageY + imageHeight/2;
lineY = midPoint - textHeight / 2;
}
else {
if (topMargin == DEFAULT_MARGIN && bottomMargin == DEFAULT_MARGIN) lineY = rect.y + (rect.height - textHeight) / 2;
else lineY = topMargin;
midPoint = lineY + textHeight/2;
imageY = midPoint - imageHeight / 2;
}
// draw the image
if (img != null) {
gc.drawImage(img, 0, 0, imageRect.width, imageHeight,
x, imageY, imageRect.width, imageHeight);
x += imageRect.width + GAP;
extent.x -= imageRect.width + GAP;
}
// draw the text
if (lines != null) {
gc.setForeground(getForeground());
for (int i = 0; i < lines.length; i++) {
int lineX = x;
if (lines.length > 1) {
if (align == SWT.CENTER) {
int lineWidth = gc.textExtent(lines[i], DRAW_FLAGS).x;
lineX = x + Math.max(0, (extent.x - lineWidth) / 2);
}
if (align == SWT.RIGHT) {
int lineWidth = gc.textExtent(lines[i], DRAW_FLAGS).x;
lineX = Math.max(x, rect.x + rect.width - rightMargin - lineWidth);
}
}
gc.drawText(lines[i], lineX, lineY, DRAW_FLAGS);
lineY += lineHeight;
}
}
}
private void paintBorder(GC gc, Rectangle r) {
Display disp= getDisplay();
Color c1 = null;
Color c2 = null;
int style = getStyle();
if ((style & SWT.SHADOW_IN) != 0) {
c1 = disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
c2 = disp.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
}
if ((style & SWT.SHADOW_OUT) != 0) {
c1 = disp.getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
c2 = disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
}
if (c1 != null && c2 != null) {
gc.setLineWidth(1);
drawBevelRect(gc, r.x, r.y, r.width-1, r.height-1, c1, c2);
}
}
public void setAlignment(int align) {
checkWidget();
if (align != SWT.LEFT && align != SWT.RIGHT && align != SWT.CENTER) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (this.align != align) {
this.align = align;
redraw();
}
}
public void setBackground (Color color) {
super.setBackground (color);
// Are these settings the same as before?
if (backgroundImage == null &&
gradientColors == null &&
gradientPercents == null) {
if (color == null) {
if (background == null) return;
} else {
if (color.equals(background)) return;
}
}
background = color;
backgroundImage = null;
gradientColors = null;
gradientPercents = null;
redraw ();
}
public void setBackground(Color[] colors, int[] percents) {
setBackground(colors, percents, false);
}
public void setBackground(Color[] colors, int[] percents, boolean vertical) {
checkWidget();
if (colors != null) {
if (percents == null || percents.length != colors.length - 1) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (getDisplay().getDepth() < 15) {
// Don't use gradients on low color displays
colors = new Color[] {colors[colors.length - 1]};
percents = new int[] { };
}
for (int i = 0; i < percents.length; i++) {
if (percents[i] < 0 || percents[i] > 100) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (i > 0 && percents[i] < percents[i-1]) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
}
}
// Are these settings the same as before?
final Color background = getBackground();
if (backgroundImage == null) {
if ((gradientColors != null) && (colors != null) &&
(gradientColors.length == colors.length)) {
boolean same = false;
for (int i = 0; i < gradientColors.length; i++) {
same = (gradientColors[i] == colors[i]) ||
((gradientColors[i] == null) && (colors[i] == background)) ||
((gradientColors[i] == background) && (colors[i] == null));
if (!same) break;
}
if (same) {
for (int i = 0; i < gradientPercents.length; i++) {
same = gradientPercents[i] == percents[i];
if (!same) break;
}
}
if (same && this.gradientVertical == vertical) return;
}
} else {
backgroundImage = null;
}
// Store the new settings
if (colors == null) {
gradientColors = null;
gradientPercents = null;
gradientVertical = false;
} else {
gradientColors = new Color[colors.length];
for (int i = 0; i < colors.length; ++i)
gradientColors[i] = (colors[i] != null) ? colors[i] : background;
gradientPercents = new int[percents.length];
for (int i = 0; i < percents.length; ++i)
gradientPercents[i] = percents[i];
gradientVertical = vertical;
}
// Refresh with the new settings
redraw();
}
public void setBackground(Image image) {
checkWidget();
if (image == backgroundImage) return;
if (image != null) {
gradientColors = null;
gradientPercents = null;
}
backgroundImage = image;
redraw();
}
public void setBottomMargin(int bottomMargin) {
checkWidget();
if (this.bottomMargin == bottomMargin || bottomMargin < 0) return;
this.bottomMargin = bottomMargin;
redraw();
}
public void setFont(Font font) {
super.setFont(font);
redraw();
}
public void setImage(Image image) {
checkWidget();
if(thread != null) {
thread.stopRunning();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (image != this.image) {
this.image = image;
redraw();
}
}
public void setGifImage(String path) {
try {
this.setGifImage(new FileInputStream(new File(path)));
} catch (FileNotFoundException e) {
this.image = null;
return;
}
}
public void setGifImage(InputStream inputStream) {
checkWidget();
if(thread != null) thread.stopRunning();
ImageLoader loader = new ImageLoader();
try {
loader.load(inputStream);
} catch (Exception e) {
this.image = null;
return;
}
if (loader.data[0] != null)
this.image = new Image(this.getDisplay(), loader.data[0]);
if (loader.data.length > 1) {
thread = new GifThread(loader);
thread.start();
}
redraw();
}
#Override
public void dispose() {
super.dispose();
if(thread != null) thread.stopRunning();
}
private class GifThread extends Thread {
private int imageNumber = 0;
private ImageLoader loader = null;
private boolean run = true;
public GifThread(ImageLoader loader) {
this.loader = loader;
}
public void run() {
while(run) {
int delayTime = loader.data[imageNumber].delayTime;
try {
Thread.sleep(delayTime * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(!GifCLabel.this.isDisposed()) {
GifCLabel.this.getDisplay().asyncExec(new Runnable() {
public void run() {
if(!run){
return;
}
if(!GifCLabel.this.isDisposed()) {
imageNumber = imageNumber == loader.data.length - 1 ? 0 : imageNumber + 1;
if (!GifCLabel.this.image.isDisposed()) GifCLabel.this.image.dispose();
ImageData nextFrameData = loader.data[imageNumber];
GifCLabel.this.image = new Image(GifCLabel.this.getDisplay(), nextFrameData);
GifCLabel.this.redraw();
} else stopRunning();
}
});
} else stopRunning();
}
}
public void stopRunning() {
run = false;
}
}
public void setLeftMargin(int leftMargin) {
checkWidget();
if (this.leftMargin == leftMargin || leftMargin < 0) return;
this.leftMargin = leftMargin;
redraw();
}
public void setMargins (int leftMargin, int topMargin, int rightMargin, int bottomMargin) {
checkWidget();
this.leftMargin = Math.max(0, leftMargin);
this.topMargin = Math.max(0, topMargin);
this.rightMargin = Math.max(0, rightMargin);
this.bottomMargin = Math.max(0, bottomMargin);
redraw();
}
public void setRightMargin(int rightMargin) {
checkWidget();
if (this.rightMargin == rightMargin || rightMargin < 0) return;
this.rightMargin = rightMargin;
redraw();
}
public void setText(String text) {
checkWidget();
if (text == null) text = ""; //$NON-NLS-1$
if (! text.equals(this.text)) {
this.text = text;
redraw();
}
}
public void setToolTipText (String string) {
super.setToolTipText (string);
appToolTipText = super.getToolTipText();
}
public void setTopMargin(int topMargin) {
checkWidget();
if (this.topMargin == topMargin || topMargin < 0) return;
this.topMargin = topMargin;
redraw();
}
protected String shortenText(GC gc, String t, int width) {
if (t == null) return null;
int w = gc.textExtent(ELLIPSIS, DRAW_FLAGS).x;
if (width<=w) return t;
int l = t.length();
int max = l/2;
int min = 0;
int mid = (max+min)/2 - 1;
if (mid <= 0) return t;
TextLayout layout = new TextLayout (getDisplay());
layout.setText(t);
mid = validateOffset(layout, mid);
while (min < mid && mid < max) {
String s1 = t.substring(0, mid);
String s2 = t.substring(validateOffset(layout, l-mid), l);
int l1 = gc.textExtent(s1, DRAW_FLAGS).x;
int l2 = gc.textExtent(s2, DRAW_FLAGS).x;
if (l1+w+l2 > width) {
max = mid;
mid = validateOffset(layout, (max+min)/2);
} else if (l1+w+l2 < width) {
min = mid;
mid = validateOffset(layout, (max+min)/2);
} else {
min = max;
}
}
String result = mid == 0 ? t : t.substring(0, mid) + ELLIPSIS + t.substring(validateOffset(layout, l-mid), l);
layout.dispose();
return result;
}
int validateOffset(TextLayout layout, int offset) {
int nextOffset = layout.getNextOffset(offset, SWT.MOVEMENT_CLUSTER);
if (nextOffset != offset) return layout.getPreviousOffset(nextOffset, SWT.MOVEMENT_CLUSTER);
return offset;
}
private String[] splitString(String text) {
String[] lines = new String[1];
int start = 0, pos;
do {
pos = text.indexOf('\n', start);
if (pos == -1) {
lines[lines.length - 1] = text.substring(start);
} else {
boolean crlf = (pos > 0) && (text.charAt(pos - 1) == '\r');
lines[lines.length - 1] = text.substring(start, pos - (crlf ? 1 : 0));
start = pos + 1;
String[] newLines = new String[lines.length+1];
System.arraycopy(lines, 0, newLines, 0, lines.length);
lines = newLines;
}
} while (pos != -1);
return lines;
}
}
and the possible usage is
final GifCLabel lbl = new GifCLabel(shell, SWT.CENTER);
lbl.setText("texxxxt");
lbl.setGifImage(this.getClass().getResourceAsStream("/8EWoM.gif"));
// lbl.setGifImage("src/8EWoM.gif");
Because of the limit of 30000 chars to answer, the pasted code is without comments, non-trim version could be found on http://pastebin.com/cJA682XD