PageRenderTime 52ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusArea.cs

http://github.com/mono/monodevelop
C# | 928 lines | 722 code | 148 blank | 58 comment | 99 complexity | 7ae829e7a849b822a2ebca8a4e411e2a MD5 | raw file
Possible License(s): LGPL-2.0, GPL-2.0, CC-BY-SA-3.0, MIT, LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. //
  2. // StatusArea.cs
  3. //
  4. // Author:
  5. // Mike Krüger <mkrueger@xamarin.com>
  6. //
  7. // Copyright (c) 2012 Xamarin Inc. (http://xamarin.com)
  8. //
  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:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  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
  25. // THE SOFTWARE.
  26. using System;
  27. using System.Diagnostics;
  28. using Gtk;
  29. using MonoDevelop.Components;
  30. using Cairo;
  31. using MonoDevelop.Ide;
  32. using MonoDevelop.Ide.Gui;
  33. using MonoDevelop.Ide.Tasks;
  34. using System.Collections.Generic;
  35. using MonoDevelop.Ide.CodeCompletion;
  36. using MonoDevelop.Core;
  37. using MonoDevelop.Ide.Gui.Components;
  38. using StockIcons = MonoDevelop.Ide.Gui.Stock;
  39. using Xwt.Motion;
  40. using MonoDevelop.Ide.Fonts;
  41. using System.Threading;
  42. using System.Linq;
  43. namespace MonoDevelop.Components.MainToolbar
  44. {
  45. class StatusArea : EventBox, ITestableStatusBar, Xwt.Motion.IAnimatable
  46. {
  47. struct Message
  48. {
  49. public string Text;
  50. public IconId Icon;
  51. public bool IsMarkup;
  52. public Message (IconId icon, string text, bool markup)
  53. {
  54. Text = text;
  55. Icon = icon;
  56. IsMarkup = markup;
  57. }
  58. }
  59. public struct RenderArg
  60. {
  61. public Gdk.Rectangle Allocation { get; set; }
  62. public double BuildAnimationProgress { get; set; }
  63. public double BuildAnimationOpacity { get; set; }
  64. public Gdk.Rectangle ChildAllocation { get; set; }
  65. public Xwt.Drawing.Image CurrentPixbuf { get; set; }
  66. public string CurrentText { get; set; }
  67. public bool CurrentTextIsMarkup { get; set; }
  68. public double ErrorAnimationProgress { get; set; }
  69. public double HoverProgress { get; set; }
  70. public string LastText { get; set; }
  71. public bool LastTextIsMarkup { get; set; }
  72. public Xwt.Drawing.Image LastPixbuf { get; set; }
  73. public Gdk.Point MousePosition { get; set; }
  74. public Pango.Context Pango { get; set; }
  75. public double ProgressBarAlpha { get; set; }
  76. public float ProgressBarFraction { get; set; }
  77. public bool ShowProgressBar { get; set; }
  78. public double TextAnimationProgress { get; set; }
  79. }
  80. StatusAreaTheme theme;
  81. RenderArg renderArg;
  82. HBox contentBox = new HBox (false, 8);
  83. StatusAreaSeparator statusIconSeparator;
  84. Gtk.Widget buildResultWidget;
  85. readonly HBox messageBox = new HBox ();
  86. internal readonly HBox statusIconBox = new HBox ();
  87. Alignment mainAlign;
  88. uint animPauseHandle;
  89. MouseTracker tracker;
  90. AnimatedIcon iconAnimation;
  91. IconId currentIcon;
  92. static Pad sourcePad;
  93. IDisposable currentIconAnimation;
  94. bool errorAnimPending;
  95. StatusBarContextHandler ctxHandler;
  96. bool progressBarVisible;
  97. string currentApplicationName = String.Empty;
  98. Queue<Message> messageQueue;
  99. public StatusBar MainContext {
  100. get { return ctxHandler.MainContext; }
  101. }
  102. public int MaxWidth { get; set; }
  103. void messageBoxToolTip (object o, QueryTooltipArgs e)
  104. {
  105. if (theme.IsEllipsized && (e.X < messageBox.Allocation.Width)) {
  106. var label = new Label ();
  107. if (renderArg.CurrentTextIsMarkup) {
  108. label.Markup = renderArg.CurrentText;
  109. } else {
  110. label.Text = renderArg.CurrentText;
  111. }
  112. label.Wrap = true;
  113. label.WidthRequest = messageBox.Allocation.Width;
  114. e.Tooltip.Custom = label;
  115. e.RetVal = true;
  116. } else {
  117. e.RetVal = false;
  118. }
  119. }
  120. public StatusArea ()
  121. {
  122. theme = new StatusAreaTheme ();
  123. renderArg = new RenderArg ();
  124. ctxHandler = new StatusBarContextHandler (this);
  125. VisibleWindow = false;
  126. NoShowAll = true;
  127. WidgetFlags |= Gtk.WidgetFlags.AppPaintable;
  128. statusIconBox.BorderWidth = 0;
  129. statusIconBox.Spacing = 3;
  130. Action<bool> animateProgressBar =
  131. showing => this.Animate ("ProgressBarFade",
  132. val => renderArg.ProgressBarAlpha = val,
  133. renderArg.ProgressBarAlpha,
  134. showing ? 1.0f : 0.0f,
  135. easing: Easing.CubicInOut);
  136. ProgressBegin += delegate {
  137. renderArg.ShowProgressBar = true;
  138. // StartBuildAnimation ();
  139. renderArg.ProgressBarFraction = 0;
  140. QueueDraw ();
  141. animateProgressBar (true);
  142. };
  143. ProgressEnd += delegate {
  144. renderArg.ShowProgressBar = false;
  145. // StopBuildAnimation ();
  146. QueueDraw ();
  147. animateProgressBar (false);
  148. };
  149. ProgressFraction += delegate(object sender, FractionEventArgs e) {
  150. renderArg.ProgressBarFraction = (float)e.Work;
  151. QueueDraw ();
  152. };
  153. contentBox.PackStart (messageBox, true, true, 0);
  154. contentBox.PackEnd (statusIconBox, false, false, 0);
  155. contentBox.PackEnd (statusIconSeparator = new StatusAreaSeparator (), false, false, 0);
  156. contentBox.PackEnd (buildResultWidget = CreateBuildResultsWidget (Orientation.Horizontal), false, false, 0);
  157. HasTooltip = true;
  158. QueryTooltip += messageBoxToolTip;
  159. mainAlign = new Alignment (0, 0.5f, 1, 0);
  160. mainAlign.LeftPadding = 12;
  161. mainAlign.RightPadding = 8;
  162. mainAlign.Add (contentBox);
  163. Add (mainAlign);
  164. mainAlign.ShowAll ();
  165. statusIconBox.Hide ();
  166. statusIconSeparator.Hide ();
  167. buildResultWidget.Hide ();
  168. Show ();
  169. this.ButtonPressEvent += delegate {
  170. if (sourcePad != null)
  171. sourcePad.BringToFront (true);
  172. };
  173. statusIconBox.Shown += delegate {
  174. UpdateSeparators ();
  175. };
  176. statusIconBox.Hidden += delegate {
  177. UpdateSeparators ();
  178. };
  179. messageQueue = new Queue<Message> ();
  180. tracker = new MouseTracker(this);
  181. tracker.MouseMoved += (sender, e) => QueueDraw ();
  182. tracker.HoveredChanged += (sender, e) => {
  183. this.Animate ("Hovered",
  184. x => renderArg.HoverProgress = x,
  185. renderArg.HoverProgress,
  186. tracker.Hovered ? 1.0f : 0.0f,
  187. easing: Easing.SinInOut);
  188. };
  189. IdeApp.FocusIn += delegate {
  190. // If there was an error while the application didn't have the focus,
  191. // trigger the error animation again when it gains the focus
  192. if (errorAnimPending) {
  193. errorAnimPending = false;
  194. TriggerErrorAnimation ();
  195. }
  196. };
  197. }
  198. protected override void OnDestroyed ()
  199. {
  200. if (theme != null)
  201. theme.Dispose ();
  202. base.OnDestroyed ();
  203. }
  204. void IAnimatable.BatchBegin () { }
  205. void IAnimatable.BatchCommit () { QueueDraw (); }
  206. void StartBuildAnimation ()
  207. {
  208. this.Animate ("Build",
  209. val => renderArg.BuildAnimationProgress = val,
  210. length: 5000,
  211. repeat: () => true);
  212. this.Animate ("BuildOpacity",
  213. start: renderArg.BuildAnimationOpacity,
  214. end: 1.0f,
  215. callback: x => renderArg.BuildAnimationOpacity = x);
  216. }
  217. void StopBuildAnimation ()
  218. {
  219. this.Animate ("BuildOpacity",
  220. x => renderArg.BuildAnimationOpacity = x,
  221. renderArg.BuildAnimationOpacity,
  222. 0.0f,
  223. finished: (val, aborted) => { if (!aborted) this.AbortAnimation ("Build"); });
  224. }
  225. protected override void OnSizeAllocated (Gdk.Rectangle allocation)
  226. {
  227. if (MaxWidth > 0 && allocation.Width > MaxWidth) {
  228. allocation = new Gdk.Rectangle (allocation.X + (allocation.Width - MaxWidth) / 2, allocation.Y, MaxWidth, allocation.Height);
  229. }
  230. base.OnSizeAllocated (allocation);
  231. }
  232. void TriggerErrorAnimation ()
  233. {
  234. /* Hack for a compiler error - csc crashes on this:
  235. this.Animate (name: "statusAreaError",
  236. length: 700,
  237. callback: val => renderArg.ErrorAnimationProgress = val);
  238. */
  239. this.Animate ("statusAreaError",
  240. val => renderArg.ErrorAnimationProgress = val,
  241. length: 900);
  242. }
  243. void UpdateSeparators ()
  244. {
  245. statusIconSeparator.Visible = statusIconBox.Visible && buildResultWidget.Visible;
  246. }
  247. public Widget CreateBuildResultsWidget (Orientation orientation)
  248. {
  249. EventBox ebox = new EventBox ();
  250. Gtk.Box box;
  251. if (orientation == Orientation.Horizontal)
  252. box = new HBox ();
  253. else
  254. box = new VBox ();
  255. box.Spacing = 3;
  256. var errorIcon = ImageService.GetIcon (StockIcons.Error).WithSize (Xwt.IconSize.Small);
  257. var warningIcon = ImageService.GetIcon (StockIcons.Warning).WithSize (Xwt.IconSize.Small);
  258. var errorImage = new Xwt.ImageView (errorIcon);
  259. var warningImage = new Xwt.ImageView (warningIcon);
  260. box.PackStart (errorImage.ToGtkWidget (), false, false, 0);
  261. Label errors = new Gtk.Label ();
  262. box.PackStart (errors, false, false, 0);
  263. box.PackStart (warningImage.ToGtkWidget (), false, false, 0);
  264. Label warnings = new Gtk.Label ();
  265. box.PackStart (warnings, false, false, 0);
  266. box.NoShowAll = true;
  267. box.Show ();
  268. TaskEventHandler updateHandler = delegate {
  269. int ec=0, wc=0;
  270. foreach (TaskListEntry t in IdeServices.TaskService.Errors) {
  271. if (t.Severity == TaskSeverity.Error)
  272. ec++;
  273. else if (t.Severity == TaskSeverity.Warning)
  274. wc++;
  275. }
  276. using (var font = IdeServices.FontService.SansFont.CopyModified (MonoDevelop.Ide.Gui.Styles.FontScale11)) {
  277. errors.Visible = ec > 0;
  278. errors.ModifyFont (font);
  279. errors.Text = ec.ToString ();
  280. errorImage.Visible = ec > 0;
  281. warnings.Visible = wc > 0;
  282. warnings.ModifyFont (font);
  283. warnings.Text = wc.ToString ();
  284. warningImage.Visible = wc > 0;
  285. }
  286. ebox.Visible = ec > 0 || wc > 0;
  287. UpdateSeparators ();
  288. };
  289. updateHandler (null, null);
  290. IdeServices.TaskService.Errors.TasksAdded += updateHandler;
  291. IdeServices.TaskService.Errors.TasksRemoved += updateHandler;
  292. currentApplicationName = BrandingService.ApplicationLongName;
  293. BrandingService.ApplicationNameChanged += ApplicationNameChanged;
  294. box.Destroyed += delegate {
  295. IdeServices.TaskService.Errors.TasksAdded -= updateHandler;
  296. IdeServices.TaskService.Errors.TasksRemoved -= updateHandler;
  297. BrandingService.ApplicationNameChanged -= ApplicationNameChanged;
  298. };
  299. ebox.VisibleWindow = false;
  300. ebox.Add (box);
  301. ebox.ShowAll ();
  302. ebox.ButtonReleaseEvent += delegate {
  303. var pad = IdeApp.Workbench.GetPad<MonoDevelop.Ide.Gui.Pads.ErrorListPad> ();
  304. pad.BringToFront ();
  305. };
  306. errors.Visible = false;
  307. errorImage.Visible = false;
  308. warnings.Visible = false;
  309. warningImage.Visible = false;
  310. return ebox;
  311. }
  312. void ApplicationNameChanged (object sender, EventArgs e)
  313. {
  314. if (renderArg.CurrentText == currentApplicationName) {
  315. LoadText (BrandingService.ApplicationLongName, false);
  316. LoadPixbuf (null);
  317. QueueDraw ();
  318. }
  319. currentApplicationName = BrandingService.ApplicationLongName;
  320. }
  321. protected override void OnRealized ()
  322. {
  323. base.OnRealized ();
  324. ModifyText (StateType.Normal, Styles.StatusBarTextColor.ToGdkColor ());
  325. ModifyFg (StateType.Normal, Styles.StatusBarTextColor.ToGdkColor ());
  326. }
  327. protected override void OnSizeRequested (ref Requisition requisition)
  328. {
  329. requisition.Height = 32;
  330. base.OnSizeRequested (ref requisition);
  331. }
  332. protected override bool OnExposeEvent (Gdk.EventExpose evnt)
  333. {
  334. using (var context = Gdk.CairoHelper.Create (evnt.Window)) {
  335. renderArg.Allocation = Allocation;
  336. renderArg.ChildAllocation = messageBox.Allocation;
  337. renderArg.MousePosition = tracker.MousePosition;
  338. renderArg.Pango = PangoContext;
  339. theme.Render (context, renderArg, this);
  340. }
  341. return base.OnExposeEvent (evnt);
  342. }
  343. #region StatusBar implementation
  344. List<StatusIcon> icons = new List<StatusIcon> ();
  345. public StatusBarIcon ShowStatusIcon (Xwt.Drawing.Image pixbuf)
  346. {
  347. Runtime.AssertMainThread ();
  348. StatusIcon icon = new StatusIcon (this, pixbuf);
  349. statusIconBox.PackEnd (icon.box);
  350. statusIconBox.ShowAll ();
  351. icons.Add (icon);
  352. return icon;
  353. }
  354. void HideStatusIcon (StatusIcon icon)
  355. {
  356. icons.Remove (icon);
  357. statusIconBox.Remove (icon.EventBox);
  358. if (statusIconBox.Children.Length == 0)
  359. statusIconBox.Hide ();
  360. icon.EventBox.Destroy ();
  361. }
  362. public StatusBarContext CreateContext ()
  363. {
  364. return ctxHandler.CreateContext ();
  365. }
  366. public void ShowReady ()
  367. {
  368. ShowMessage ("");
  369. SetMessageSourcePad (null);
  370. }
  371. public void SetMessageSourcePad (Pad pad)
  372. {
  373. sourcePad = pad;
  374. }
  375. public bool HasResizeGrip {
  376. get;
  377. set;
  378. }
  379. public class StatusIcon : StatusBarIcon
  380. {
  381. StatusArea statusBar;
  382. internal EventBox box;
  383. string tip;
  384. DateTime alertEnd;
  385. Xwt.Drawing.Image icon;
  386. uint animation;
  387. Xwt.ImageView image;
  388. int astep;
  389. Xwt.Drawing.Image[] images;
  390. TooltipPopoverWindow tooltipWindow;
  391. bool mouseOver;
  392. uint tipShowTimeoutId;
  393. DateTime scheduledTipTime;
  394. const int TooltipTimeout = 350;
  395. public StatusIcon (StatusArea statusBar, Xwt.Drawing.Image icon)
  396. {
  397. if (!icon.HasFixedSize)
  398. icon = icon.WithSize (IconSize.Menu);
  399. this.statusBar = statusBar;
  400. this.icon = icon;
  401. box = new EventBox ();
  402. box.VisibleWindow = false;
  403. image = new Xwt.ImageView (icon);
  404. box.Child = image.ToGtkWidget ();
  405. box.Events |= Gdk.EventMask.EnterNotifyMask | Gdk.EventMask.LeaveNotifyMask;
  406. box.EnterNotifyEvent += HandleEnterNotifyEvent;
  407. box.LeaveNotifyEvent += HandleLeaveNotifyEvent;
  408. box.ButtonPressEvent += (o, e) => {
  409. // TODO: Refactor this in Xwt as an extension method.
  410. var m = Xwt.ModifierKeys.None;
  411. if ((e.Event.State & Gdk.ModifierType.ShiftMask) != 0)
  412. m |= Xwt.ModifierKeys.Shift;
  413. if ((e.Event.State & Gdk.ModifierType.ControlMask) != 0)
  414. m |= Xwt.ModifierKeys.Control;
  415. if ((e.Event.State & Gdk.ModifierType.Mod1Mask) != 0)
  416. m |= Xwt.ModifierKeys.Alt;
  417. // TODO: Backport this one.
  418. if ((e.Event.State & Gdk.ModifierType.Mod2Mask) != 0)
  419. m |= Xwt.ModifierKeys.Command;
  420. Clicked?.Invoke (o, new StatusBarIconClickedEventArgs {
  421. Button = (Xwt.PointerButton)e.Event.Button,
  422. Modifiers = m,
  423. });
  424. };
  425. }
  426. [GLib.ConnectBefore]
  427. void HandleLeaveNotifyEvent (object o, LeaveNotifyEventArgs args)
  428. {
  429. mouseOver = false;
  430. HideTooltip ();
  431. }
  432. [GLib.ConnectBefore]
  433. void HandleEnterNotifyEvent (object o, EnterNotifyEventArgs args)
  434. {
  435. mouseOver = true;
  436. ShowTooltip ();
  437. }
  438. void ShowTooltip ()
  439. {
  440. scheduledTipTime = DateTime.Now + TimeSpan.FromMilliseconds (TooltipTimeout);
  441. if (tipShowTimeoutId == 0)
  442. tipShowTimeoutId = GLib.Timeout.Add (TooltipTimeout, ShowTooltipEvent);
  443. }
  444. bool ShowTooltipEvent ()
  445. {
  446. tipShowTimeoutId = 0;
  447. if (!mouseOver)
  448. return false;
  449. int remainingMs = (int) (scheduledTipTime - DateTime.Now).TotalMilliseconds;
  450. if (remainingMs > 50) {
  451. // Still some significant time left. Re-schedule the timer
  452. tipShowTimeoutId = GLib.Timeout.Add ((uint) remainingMs, ShowTooltipEvent);
  453. return false;
  454. }
  455. if (!string.IsNullOrEmpty (tip)) {
  456. HideTooltip ();
  457. tooltipWindow = TooltipPopoverWindow.Create ();
  458. tooltipWindow.ShowArrow = true;
  459. tooltipWindow.Text = tip;
  460. tooltipWindow.ShowPopup (box, PopupPosition.Top);
  461. }
  462. return false;
  463. }
  464. void HideTooltip ()
  465. {
  466. if (tooltipWindow != null) {
  467. tooltipWindow.Destroy ();
  468. tooltipWindow = null;
  469. }
  470. }
  471. public void Dispose ()
  472. {
  473. HideTooltip ();
  474. statusBar.HideStatusIcon (this);
  475. if (images != null) {
  476. foreach (Xwt.Drawing.Image img in images) {
  477. img.Dispose ();
  478. }
  479. }
  480. if (animation != 0) {
  481. GLib.Source.Remove (animation);
  482. animation = 0;
  483. }
  484. }
  485. public string ToolTip {
  486. get { return tip; }
  487. set {
  488. tip = value;
  489. if (tooltipWindow != null) {
  490. if (!string.IsNullOrEmpty (tip))
  491. tooltipWindow.Text = value;
  492. else
  493. HideTooltip ();
  494. } else if (!string.IsNullOrEmpty (tip) && mouseOver)
  495. ShowTooltip ();
  496. }
  497. }
  498. public string Title {
  499. get;
  500. set;
  501. }
  502. public string Help {
  503. get;
  504. set;
  505. }
  506. public EventBox EventBox {
  507. get { return box; }
  508. }
  509. public Xwt.Drawing.Image Image {
  510. get { return icon; }
  511. set {
  512. icon = value;
  513. if (!icon.HasFixedSize)
  514. icon = icon.WithSize (IconSize.Menu);
  515. image.Image = icon;
  516. }
  517. }
  518. public void SetAlertMode (int seconds)
  519. {
  520. astep = 0;
  521. alertEnd = DateTime.Now.AddSeconds (seconds);
  522. if (animation != 0)
  523. GLib.Source.Remove (animation);
  524. animation = GLib.Timeout.Add (60, new GLib.TimeoutHandler (AnimateIcon));
  525. if (images == null) {
  526. images = new Xwt.Drawing.Image [10];
  527. for (int n=0; n<10; n++)
  528. images [n] = icon.WithAlpha (((double)(9-n))/10.0);
  529. }
  530. }
  531. bool AnimateIcon ()
  532. {
  533. if (DateTime.Now >= alertEnd && astep == 0) {
  534. image.Image = icon;
  535. animation = 0;
  536. return false;
  537. }
  538. if (astep < 10)
  539. image.Image = images [astep];
  540. else
  541. image.Image = images [20 - astep - 1];
  542. astep = (astep + 1) % 20;
  543. return true;
  544. }
  545. public event EventHandler<StatusBarIconClickedEventArgs> Clicked;
  546. }
  547. #endregion
  548. #region StatusBarContextBase implementation
  549. public void ShowError (string error)
  550. {
  551. ShowMessage (StockIcons.StatusError, error);
  552. }
  553. public void ShowWarning (string warning)
  554. {
  555. ShowMessage (StockIcons.StatusWarning, warning);
  556. }
  557. public void ShowMessage (string message)
  558. {
  559. ShowMessage (null, message, false);
  560. }
  561. public void ShowMessage (string message, bool isMarkup)
  562. {
  563. ShowMessage (null, message, isMarkup);
  564. }
  565. public void ShowMessage (IconId image, string message)
  566. {
  567. ShowMessage (image, message, false);
  568. }
  569. public void ShowMessage (IconId image, string message, bool isMarkup)
  570. {
  571. if (this.AnimationIsRunning("Text") || animPauseHandle > 0) {
  572. messageQueue.Clear ();
  573. messageQueue.Enqueue (new Message (image, message, isMarkup));
  574. } else {
  575. ShowMessageInner (image, message, isMarkup);
  576. }
  577. }
  578. void ShowMessageInner (IconId image, string message, bool isMarkup)
  579. {
  580. Runtime.AssertMainThread ();
  581. if (image == StockIcons.StatusError) {
  582. // If the application doesn't have the focus, trigger the animation
  583. // again when it gains the focus
  584. if (!IdeApp.CommandService.ApplicationHasFocus)
  585. errorAnimPending = true;
  586. TriggerErrorAnimation ();
  587. }
  588. LoadText (message, isMarkup);
  589. LoadPixbuf (image);
  590. /* Hack for a compiler error - csc crashes on this:
  591. this.Animate ("Text", easing: Easing.SinInOut,
  592. callback: x => renderArg.TextAnimationProgress = x,
  593. finished: x => { animPauseHandle = GLib.Timeout.Add (1000, () => {
  594. if (messageQueue.Count > 0) {
  595. Message m = messageQueue.Dequeue();
  596. ShowMessageInner (m.Icon, m.Text, m.IsMarkup);
  597. }
  598. animPauseHandle = 0;
  599. return false;
  600. });
  601. });
  602. */
  603. this.Animate ("Text",
  604. x => renderArg.TextAnimationProgress = x,
  605. easing: Easing.SinInOut,
  606. finished: (x, b) => { animPauseHandle = GLib.Timeout.Add (1000, () => {
  607. if (messageQueue.Count > 0) {
  608. Message m = messageQueue.Dequeue();
  609. ShowMessageInner (m.Icon, m.Text, m.IsMarkup);
  610. }
  611. animPauseHandle = 0;
  612. return false;
  613. });
  614. });
  615. if (renderArg.CurrentText == renderArg.LastText)
  616. this.AbortAnimation ("Text");
  617. QueueDraw ();
  618. }
  619. void LoadText (string message, bool isMarkup)
  620. {
  621. if (string.IsNullOrEmpty(message))
  622. message = BrandingService.ApplicationLongName;
  623. message = message ?? "";
  624. renderArg.LastText = renderArg.CurrentText;
  625. renderArg.CurrentText = message.Replace (System.Environment.NewLine, " ").Replace ("\n", " ").Trim ();
  626. renderArg.LastTextIsMarkup = renderArg.CurrentTextIsMarkup;
  627. renderArg.CurrentTextIsMarkup = isMarkup;
  628. }
  629. static bool iconLoaded = false;
  630. void LoadPixbuf (IconId image)
  631. {
  632. // We dont need to load the same image twice
  633. if (currentIcon == image && iconLoaded)
  634. return;
  635. currentIcon = image;
  636. iconAnimation = null;
  637. // clean up previous running animation
  638. if (currentIconAnimation != null) {
  639. currentIconAnimation.Dispose ();
  640. currentIconAnimation = null;
  641. }
  642. // if we have nothing, use the default icon
  643. if (image == IconId.Null)
  644. image = "md-status-steady";
  645. // load image now
  646. if (ImageService.IsAnimation (image, Gtk.IconSize.Menu)) {
  647. iconAnimation = ImageService.GetAnimatedIcon (image, Gtk.IconSize.Menu);
  648. renderArg.CurrentPixbuf = iconAnimation.FirstFrame.WithSize (16,16);
  649. currentIconAnimation = iconAnimation.StartAnimation (delegate (Xwt.Drawing.Image p) {
  650. renderArg.CurrentPixbuf = p.WithSize (16,16);
  651. QueueDraw ();
  652. });
  653. } else {
  654. renderArg.CurrentPixbuf = ImageService.GetIcon (image).WithSize (16,16);
  655. }
  656. iconLoaded = true;
  657. }
  658. #endregion
  659. #region Progress Monitor implementation
  660. public static event EventHandler ProgressBegin, ProgressEnd, ProgressPulse;
  661. public static event EventHandler<FractionEventArgs> ProgressFraction;
  662. public sealed class FractionEventArgs : EventArgs
  663. {
  664. public double Work { get; private set; }
  665. public FractionEventArgs (double work)
  666. {
  667. this.Work = work;
  668. }
  669. }
  670. static void OnProgressBegin (EventArgs e)
  671. {
  672. var handler = ProgressBegin;
  673. if (handler != null)
  674. handler (null, e);
  675. }
  676. static void OnProgressEnd (EventArgs e)
  677. {
  678. var handler = ProgressEnd;
  679. if (handler != null)
  680. handler (null, e);
  681. }
  682. static void OnProgressPulse (EventArgs e)
  683. {
  684. var handler = ProgressPulse;
  685. if (handler != null)
  686. handler (null, e);
  687. }
  688. static void OnProgressFraction (FractionEventArgs e)
  689. {
  690. var handler = ProgressFraction;
  691. if (handler != null)
  692. handler (null, e);
  693. }
  694. public void BeginProgress (string name)
  695. {
  696. ShowMessage (name);
  697. if (!progressBarVisible) {
  698. progressBarVisible = true;
  699. OnProgressBegin (EventArgs.Empty);
  700. }
  701. }
  702. public void BeginProgress (IconId image, string name)
  703. {
  704. ShowMessage (image, name);
  705. if (!progressBarVisible) {
  706. progressBarVisible = true;
  707. OnProgressBegin (EventArgs.Empty);
  708. }
  709. }
  710. public void SetProgressFraction (double work)
  711. {
  712. Runtime.AssertMainThread ();
  713. OnProgressFraction (new FractionEventArgs (work));
  714. }
  715. public void EndProgress ()
  716. {
  717. if (!progressBarVisible)
  718. return;
  719. progressBarVisible = false;
  720. OnProgressEnd (EventArgs.Empty);
  721. AutoPulse = false;
  722. }
  723. public void Pulse ()
  724. {
  725. Runtime.AssertMainThread ();
  726. OnProgressPulse (EventArgs.Empty);
  727. }
  728. uint autoPulseTimeoutId;
  729. public bool AutoPulse {
  730. get { return autoPulseTimeoutId != 0; }
  731. set {
  732. Runtime.AssertMainThread ();
  733. if (value) {
  734. if (autoPulseTimeoutId == 0) {
  735. autoPulseTimeoutId = GLib.Timeout.Add (100, delegate {
  736. Pulse ();
  737. return true;
  738. });
  739. }
  740. } else {
  741. if (autoPulseTimeoutId != 0) {
  742. GLib.Source.Remove (autoPulseTimeoutId);
  743. autoPulseTimeoutId = 0;
  744. }
  745. }
  746. }
  747. }
  748. public void SetCancellationTokenSource (CancellationTokenSource source)
  749. {
  750. }
  751. #endregion
  752. string ITestableStatusBar.CurrentText => messageQueue.Count > 0 ? "" : renderArg.CurrentText;
  753. string [] ITestableStatusBar.CurrentIcons => icons.Select (x => x.ToolTip).ToArray ();
  754. }
  755. class StatusAreaSeparator: HBox
  756. {
  757. protected override bool OnExposeEvent (Gdk.EventExpose evnt)
  758. {
  759. using (var ctx = Gdk.CairoHelper.Create (this.GdkWindow)) {
  760. var alloc = Allocation;
  761. //alloc.Inflate (0, -2);
  762. ctx.Rectangle (alloc.X, alloc.Y, 1, alloc.Height);
  763. // FIXME: VV: Remove gradient features
  764. using (Cairo.LinearGradient gr = new LinearGradient (alloc.X, alloc.Y, alloc.X, alloc.Y + alloc.Height)) {
  765. gr.AddColorStop (0, new Cairo.Color (0, 0, 0, 0));
  766. gr.AddColorStop (0.5, new Cairo.Color (0, 0, 0, 0.2));
  767. gr.AddColorStop (1, new Cairo.Color (0, 0, 0, 0));
  768. ctx.SetSource (gr);
  769. ctx.Fill ();
  770. }
  771. }
  772. return true;
  773. }
  774. protected override void OnSizeRequested (ref Requisition requisition)
  775. {
  776. base.OnSizeRequested (ref requisition);
  777. requisition.Width = 1;
  778. }
  779. }
  780. }