5
// Lluis Sanchez <lluis@xamarin.com>
7
// Copyright (c) 2012 Xamarin Inc
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
10
// of this software and associated documentation files (the "Software"), to deal
11
// in the Software without restriction, including without limitation the rights
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
// copies of the Software, and to permit persons to whom the Software is
14
// furnished to do so, subject to the following conditions:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
using System.Collections.Generic;
31
using MonoDevelop.Ide.Extensions;
32
using MonoDevelop.Core;
33
using MonoDevelop.Components;
37
namespace MonoDevelop.Ide.Gui.Components
40
public class AnimatedIcon
42
/* An animation spec is a sequence of animation frames. Frames are separated using semicolons.
43
* Each frame can be an image (using a regular image spec), an effect, or a pause. For example:
45
* res:build1.png;morph;res:build2.png;morph
47
* Supported effects are:
48
* fade-out, fade-in, morph
56
const int defaultPause = 200;
57
List<Gdk.Pixbuf> images;
60
static Dictionary<string,Type> animationItems = new Dictionary<string, Type> ();
62
static AnimatedIcon ()
64
animationItems ["morph"] = typeof(MorphEffect);
65
animationItems ["fade-in"] = typeof(FadeInEffect);
66
animationItems ["fade-out"] = typeof(FadeOutEffect);
69
public AnimatedIcon (RuntimeAddin addin, string animationSpec, Gtk.IconSize size)
73
this.animationSpec = animationSpec;
74
Parse (animationSpec);
77
void Parse (string animationSpec)
79
List<AnimationItem> parsedItems = new List<AnimationItem> ();
80
string[] items = animationSpec.Split (';');
81
AnimationItem last = null;
83
foreach (var item in items) {
84
int i = item.IndexOf (':');
85
var tname = i != -1 ? item.Substring (0, i) : item;
88
AnimationItem aitem = null;
90
if (animationItems.TryGetValue (tname, out type)) {
91
aitem = (AnimationItem) Activator.CreateInstance (type);
94
else if (int.TryParse (item, out pause)) {
95
aitem = new PauseItem () { Pause = pause };
98
// It must be an image
99
var id = ImageService.GetStockId (addin, item, size);
100
var img = ImageService.GetPixbuf (id, size);
103
aitem = new ImageItem () { Image = img };
106
last.NextItem = aitem;
107
aitem.PreviousItem = last;
108
parsedItems.Add (aitem);
112
if (parsedItems.Count > 0) {
114
parsedItems[0].PreviousItem = parsedItems [parsedItems.Count - 1];
115
parsedItems [parsedItems.Count - 1].NextItem = parsedItems [0];
118
images = new List<Gdk.Pixbuf> ();
119
pauses = new List<int> ();
120
bool lastWasImage = false;
122
foreach (var aitem in parsedItems) {
123
foreach (var frame in aitem.GetFrames ()) {
124
if (frame is Gdk.Pixbuf) {
126
pauses.Add (defaultPause);
127
images.Add ((Gdk.Pixbuf)frame);
132
if (pauses.Count > 0)
133
pauses [pauses.Count - 1] = pauses [pauses.Count - 1] + (int) frame;
135
// Pause before any image. Add a dummy image
136
images.Add (ImageService.GetPixbuf ("md-empty"));
137
pauses.Add ((int) frame);
140
pauses.Add ((int)frame);
141
lastWasImage = false;
145
if (pauses.Count < images.Count)
146
pauses.Add (defaultPause);
149
public Gdk.Pixbuf FirstFrame {
151
return images.Count > 0 ? images [0] : ImageService.GetPixbuf ("md-empty");
155
public string AnimationSpec {
156
get { return animationSpec; }
159
public IDisposable StartAnimation (Action<Gdk.Pixbuf> renderer)
161
int currentFrame = 0;
162
return DispatchService.RunAnimation (delegate {
163
renderer (images [currentFrame]);
164
var res = pauses [currentFrame];
165
currentFrame = (currentFrame + 1) % images.Count;
170
abstract class AnimationItem
174
internal AnimationItem NextItem;
175
internal AnimationItem PreviousItem;
176
bool renderingFrames;
178
public virtual void Parse (string spec)
182
internal List<object> GetFrames ()
188
public Gdk.Pixbuf PreviousFrame {
190
PreviousItem.RenderFrames ();
191
var last = (Gdk.Pixbuf) PreviousItem.frames.LastOrDefault (f => f is Gdk.Pixbuf);
195
return PreviousItem.PreviousFrame;
199
public Gdk.Pixbuf NextFrame {
201
NextItem.RenderFrames ();
202
var first = (Gdk.Pixbuf) NextItem.frames.FirstOrDefault (f => f is Gdk.Pixbuf);
206
return NextItem.NextFrame;
213
throw new Exception ("Invalid animation sequence");
214
if (frames == null) {
215
renderingFrames = true;
216
frames = new List<object> ();
218
renderingFrames = false;
222
public abstract void OnRenderFrames ();
224
protected void AddImage (Gdk.Pixbuf image)
229
protected void AddPause (int ms)
235
class ImageItem: AnimationItem
237
public Gdk.Pixbuf Image { get; set; }
239
public override void OnRenderFrames ()
245
class PauseItem: AnimationItem
247
public int Pause { get; set; }
249
public override void OnRenderFrames ()
255
class FadeOutEffect: AnimationItem
257
public override void OnRenderFrames ()
259
var icon = PreviousFrame;
260
for (int n=0; n<10; n++) {
261
AddImage (ImageService.MakeTransparent (icon, ((double)(9-n))/10.0));
267
class FadeInEffect: AnimationItem
269
public override void OnRenderFrames ()
271
var icon = NextFrame;
272
for (int n=0; n<10; n++) {
273
AddImage (ImageService.MakeTransparent (icon, ((double)(n))/10.0));
279
class MorphEffect: AnimationItem
281
public override void OnRenderFrames ()
283
var prev = PreviousFrame;
284
var next = NextFrame;
285
for (int n=0; n<10; n++) {
286
var img1 = ImageService.MakeTransparent (next, ((double)(n))/10.0);
287
var img2 = ImageService.MakeTransparent (prev, ((double)(9-n))/10.0);
288
img1.Composite (img2,
290
img2.Width, img2.Width,
292
1, 1, Gdk.InterpType.Bilinear, 255);