diff --git a/Samples/WaitTasksSamples/Program.cs b/Samples/WaitTasksSamples/Program.cs index 0c55bef7..7c6619a9 100644 --- a/Samples/WaitTasksSamples/Program.cs +++ b/Samples/WaitTasksSamples/Program.cs @@ -23,23 +23,23 @@ static void Main(string[] args) .Run(); PromptPlus.DoubleDash($"Control:WaitProcess - normal usage sequencial mode"); - var wt1 = PromptPlus.WaitProcess("wait process", "main desc") + var wt1 = PromptPlus.WaitProcess("wait process", "main desc") .Finish($"end wait all process") .Interaction(steps1, (ctrl, item) => { ctrl.AddStep(StepMode.Sequential, $"id{item}",null, - (cts) => + (eventw, cts) => { cts.WaitHandle.WaitOne(TimeSpan.FromSeconds(item)); }); }) .ShowElapsedTime() .AddStep(StepMode.Sequential, "id5-10", "Desc 5 and 10", - (cts) => + (eventw, cts) => { cts.WaitHandle.WaitOne(TimeSpan.FromSeconds(5)); }, - (cts) => + (eventw, cts) => { cts.WaitHandle.WaitOne(TimeSpan.FromSeconds(10)); }) @@ -47,7 +47,7 @@ static void Main(string[] args) if (!wt1.IsAborted) { - foreach (var item in wt1.Value) + foreach (var item in wt1.Value.States) { PromptPlus.WriteLine($"You task {item.Id} - {item.Description}, {item.Status}, {item.ElapsedTime}, {item.StepMode}"); } @@ -63,24 +63,24 @@ static void Main(string[] args) PromptPlus.DoubleDash($"Control:WaitProcess - normal usage Parallel mode"); - wt1 = PromptPlus.WaitProcess("wait process", "main desc") + wt1 = PromptPlus.WaitProcess("wait process", "main desc") .Finish($"end wait all process") .TaskTitle("MyProcess") .Interaction(steps2, (ctrl, item) => { ctrl.AddStep(StepMode.Parallel, $"id{item}",null, - (cts) => + (eventw, cts) => { cts.WaitHandle.WaitOne(TimeSpan.FromSeconds(item)); }); }) .ShowElapsedTime() .AddStep(StepMode.Parallel, "id5-10", "Desc 5 and 10", - (cts) => + (eventw, cts) => { cts.WaitHandle.WaitOne(TimeSpan.FromSeconds(5)); }, - (cts) => + (eventw, cts) => { cts.WaitHandle.WaitOne(TimeSpan.FromSeconds(10)); }) @@ -88,7 +88,7 @@ static void Main(string[] args) if (!wt1.IsAborted) { - foreach (var item in wt1.Value) + foreach (var item in wt1.Value.States) { PromptPlus.WriteLine($"You task {item.Id} - {item.Description}, {item.Status}, {item.ElapsedTime}, {item.StepMode}"); } diff --git a/Src/Controls/Pipeline/EventPipe.cs b/Src/Controls/Pipeline/EventPipe.cs index 8fefa317..aa55d06c 100644 --- a/Src/Controls/Pipeline/EventPipe.cs +++ b/Src/Controls/Pipeline/EventPipe.cs @@ -9,7 +9,12 @@ namespace PPlus.Controls /// Typeof Input public class EventPipe { - internal EventPipe(T value,string? from, string? current, string? to, ReadOnlyCollection listpipes) + private EventPipe() + { + throw new PromptPlusException("EventPipe CTOR NotImplemented"); + } + + internal EventPipe(ref T value,string? from, string? current, string? to, ReadOnlyCollection listpipes) { FromPipe = from; CurrentPipe = current; diff --git a/Src/Controls/Pipeline/PipelineControl.cs b/Src/Controls/Pipeline/PipelineControl.cs index c622ab8d..286883e1 100644 --- a/Src/Controls/Pipeline/PipelineControl.cs +++ b/Src/Controls/Pipeline/PipelineControl.cs @@ -14,7 +14,7 @@ internal class PipelineControl : BaseControl>, IControlPipe private EventPipe _currentevent; private ReadOnlyCollection _pipes; private List _runpipes; - + private T _context; public PipelineControl(IConsoleControl console, PipelineOptions options) : base(console, options) { _options = options; @@ -65,14 +65,14 @@ public override ResultPrompt> TryResult(CancellationToken canc !_currentevent.CancelPipeLine ? PipeStatus.Executed : PipeStatus.Canceled, _runpipes[index].Elapsedtime); _currentevent = NextPipe(_currentevent, cancellationToken); - return new ResultPrompt>(new ResultPipeline(_currentevent.Input,_runpipes.ToArray()), _currentevent.CancelPipeLine, _currentevent.CurrentPipe != null, false, false); + return new ResultPrompt>(new ResultPipeline(_currentevent.Input, _runpipes.ToArray()), _currentevent.CancelPipeLine, _currentevent.CurrentPipe != null, false, false); } private EventPipe NextPipe(EventPipe curevent, CancellationToken cancellationToken) { if (curevent.ToPipe == null || curevent.CurrentPipe == null) { - return new EventPipe(curevent.Input, curevent.CurrentPipe, null, null, _pipes); + return new EventPipe(ref _context, curevent.CurrentPipe, null, null, _pipes); } var from = curevent.CurrentPipe; var cur = curevent.ToPipe; @@ -81,7 +81,7 @@ private EventPipe NextPipe(EventPipe curevent, CancellationToken cancellat { to = _pipes[_pipes.IndexOf(cur) + 1]; } - var newevent = new EventPipe(curevent.Input, from, cur, to, _pipes); + var newevent = new EventPipe(ref _context, from, cur, to, _pipes); while (cur != null && _options.Conditions.TryGetValue(cur, out var condition)) { var sw = new Stopwatch(); @@ -105,9 +105,10 @@ private EventPipe NextPipe(EventPipe curevent, CancellationToken cancellat public override string InitControl(CancellationToken cancellationToken) { + _context = _options.CurrentValue; if (_options.Pipes.Count == 0) { - _currentevent = new EventPipe(_options.CurrentValue, null, null, null, _pipes); + _currentevent = new EventPipe(ref _context, null, null, null, _pipes); return string.Empty; } _runpipes = new List(); @@ -119,7 +120,7 @@ public override string InitControl(CancellationToken cancellationToken) { next = _pipes[1]; } - _currentevent = new EventPipe(_options.CurrentValue, null, first, next, _pipes); + _currentevent = new EventPipe(ref _context, null, first, next, _pipes); while (first != null && _options.Conditions.TryGetValue(first, out var condition)) { var sw = new Stopwatch(); diff --git a/Src/Controls/ResultWaitProcess.cs b/Src/Controls/ResultWaitProcess.cs new file mode 100644 index 00000000..b9bc602a --- /dev/null +++ b/Src/Controls/ResultWaitProcess.cs @@ -0,0 +1,43 @@ +// *************************************************************************************** +// MIT LICENCE +// The maintenance and evolution is maintained by the PromptPlus project under MIT license +// *************************************************************************************** + +using System.Collections.Generic; + +namespace PPlus.Controls +{ + /// + /// Represents The Result to WaitProcess Controls + /// + /// Typeof return + public readonly struct ResultWaitProcess + { + /// + /// Create a ResultPipeline + /// + /// + /// Do not use this constructor! + /// + public ResultWaitProcess() + { + throw new PromptPlusException("ResultWaitProcess CTOR NotImplemented"); + } + + internal ResultWaitProcess(T conext, StateProcess[] stateprocess) + { + Context = conext; + States = stateprocess; + } + + /// + /// Get conext value + /// + public T Context { get; } + + /// + /// Get State of process + /// + public StateProcess[] States { get; } + } +} diff --git a/Src/Controls/TasksProcess/EventWaitProcess.cs b/Src/Controls/TasksProcess/EventWaitProcess.cs new file mode 100644 index 00000000..679f8471 --- /dev/null +++ b/Src/Controls/TasksProcess/EventWaitProcess.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace PPlus.Controls +{ + /// + /// Represents the event to task process with with conex value + /// + /// Typeof Input + public class EventWaitProcess + { + private object _lock = new object(); + private T _value = default; + private bool _cancelnext; + + private EventWaitProcess() + { + throw new PromptPlusException("EventWaitProcess CTOR NotImplemented"); + } + + internal EventWaitProcess(ref T value, bool cancelnextalltasks) + { + _value = value; + _cancelnext = cancelnextalltasks; + } + + + /// + /// Get/set Context value + /// + public T Context + { + get + { + lock (_lock) + { + return _value; + } + } + set + { + lock (_lock) + { + _value = value; + } + } + } + + /// + /// Get/Set Cancel all next tasks. + /// + public bool CancelAllNextTasks + { + get + { + lock (_lock) + { + return _cancelnext; + } + } + set + { + lock (_lock) + { + _cancelnext = value; + } + } + } + } +} + diff --git a/Src/Controls/TasksProcess/IControlWait.cs b/Src/Controls/TasksProcess/IControlWait.cs index 4e464d04..f2160c5a 100644 --- a/Src/Controls/TasksProcess/IControlWait.cs +++ b/Src/Controls/TasksProcess/IControlWait.cs @@ -13,52 +13,58 @@ namespace PPlus.Controls /// /// Represents the interface with all Methods of the WaitTimer/WaitProcess control /// - public interface IControlWait : IPromptControls> + /// typeof return + public interface IControlWait : IPromptControls> { /// /// Execute a action foreach item of colletion passed as a parameter /// - /// typeof item /// Colletion for interaction /// Action to execute - /// - IControlWait Interaction(IEnumerable values, Action action); + /// + IControlWait Interaction(IEnumerable values, Action,T1> action); /// /// Maximum number of concurrent tasks enable. Default vaue is number of processors. /// /// Number of concurrent tasks - /// - IControlWait MaxDegreeProcess(int value); + /// + IControlWait MaxDegreeProcess(int value); + /// + /// Set Contex value for all tasks + /// + /// Context value + /// + IControlWait Context(T value); /// /// Overwrite Task Title . Default task title comes from the embedded resource. /// /// TaskTitle Task - /// - IControlWait TaskTitle(string value); + /// + IControlWait TaskTitle(string value); /// /// Define if show Elapsed Time for each task. /// - /// - IControlWait ShowElapsedTime(); + /// + IControlWait ShowElapsedTime(); /// /// Custom config the control. /// /// Action to apply changes. - /// - IControlWait Config(Action context); + /// + IControlWait Config(Action context); /// /// Finish answer to show when Wait process is completed. /// /// Text Finish answer - /// - IControlWait Finish(string text); + /// + IControlWait Finish(string text); /// /// Overwrite . Default value is SpinnersType.Ascii @@ -68,16 +74,16 @@ public interface IControlWait : IPromptControls> /// Style of spinner. /// Number of mileseconds foreach interation of spinner. Valid only to SpinnersType.custom, otherwise will be ignored /// IEnumerable values for custom spinner. Valid only to SpinnersType.custom, otherwise will be ignored - /// - IControlWait Spinner(SpinnersType spinnersType, Style? SpinnerStyle = null, int? speedAnimation = null, IEnumerable? customspinner = null); + /// + IControlWait Spinner(SpinnersType spinnersType, Style? SpinnerStyle = null, int? speedAnimation = null, IEnumerable? customspinner = null); /// /// Add list of tasks to execute. /// /// Sequential or parallel execution /// list of tasks - /// - IControlWait AddStep(StepMode stepMode, params Action[] process); + /// + IControlWait AddStep(StepMode stepMode, params Action, CancellationToken>[] process); /// /// Add list of tasks to execute with title and description @@ -86,7 +92,7 @@ public interface IControlWait : IPromptControls> /// Id of tasks /// Label of tasks /// list of tasks - /// - IControlWait AddStep(StepMode stepMode, string? id, string? label, params Action[] process); + /// + IControlWait AddStep(StepMode stepMode, string? id, string? label, params Action,CancellationToken>[] process); } } diff --git a/Src/Controls/TasksProcess/ProgressBarControl.cs b/Src/Controls/TasksProcess/ProgressBarControl.cs index a600e426..144a2527 100644 --- a/Src/Controls/TasksProcess/ProgressBarControl.cs +++ b/Src/Controls/TasksProcess/ProgressBarControl.cs @@ -28,6 +28,7 @@ internal class ProgressBarControl : BaseControl>,IControl private (int CursorLeft, int CursorTop) _spinnerCursor; private (int CursorLeft, int CursorTop) _progressbarCursor; private int lastline = -1; + private T _context; public ProgressBarControl(IConsoleControl console, ProgressBarOptions options) : base(console, options) { @@ -42,10 +43,12 @@ public override string InitControl(CancellationToken cancellationToken) { throw new PromptPlusException("Not have UpdateHandler to run"); } + + _context = _options.ValueResult; _ticketStep = double.Parse(_options.Witdth.ToString()) / (int.Parse(_options.Maxvalue.ToString()) - int.Parse(_options.Minvalue.ToString())); _ctsesc = new CancellationTokenSource(); _lnkcts = CancellationTokenSource.CreateLinkedTokenSource(_ctsesc.Token, cancellationToken); - _handler = new UpdateProgressBar(_options.ValueResult, _options.StartWith, _options.Minvalue, _options.Maxvalue, ""); + _handler = new UpdateProgressBar(ref _context, _options.StartWith, _options.Minvalue, _options.Maxvalue, ""); return _handler.Value.ToString(); } diff --git a/Src/Controls/TasksProcess/UpdateProgressBar.cs b/Src/Controls/TasksProcess/UpdateProgressBar.cs index ea4f9cda..ece0713b 100644 --- a/Src/Controls/TasksProcess/UpdateProgressBar.cs +++ b/Src/Controls/TasksProcess/UpdateProgressBar.cs @@ -20,7 +20,7 @@ private UpdateProgressBar() throw new PromptPlusException("UpdateProgressBar CTOR NotImplemented"); } - internal UpdateProgressBar(T context, double value,double min, double max, string desc) + internal UpdateProgressBar(ref T context, double value,double min, double max, string desc) { _lastvalue = null; _lastdescription = desc; diff --git a/Src/Controls/TasksProcess/WaitControl.cs b/Src/Controls/TasksProcess/WaitControl.cs index a18e9abf..52cd6536 100644 --- a/Src/Controls/TasksProcess/WaitControl.cs +++ b/Src/Controls/TasksProcess/WaitControl.cs @@ -14,9 +14,9 @@ namespace PPlus.Controls { - internal class WaitControl : BaseControl>, IControlWait, IDisposable + internal class WaitControl : BaseControl>, IControlWait, IDisposable { - private readonly WaitOptions _options; + private readonly WaitOptions _options; private CancellationTokenSource _lnkcts; private CancellationTokenSource _ctsesc; private Task _process; @@ -24,8 +24,11 @@ internal class WaitControl : BaseControl>, IControlWai private (int CursorLeft, int CursorTop) _initialCursor; private (int CursorLeft, int CursorTop) _spinnerCursor; private int _promptlines; + private EventWaitProcess _event; + private T _context; - public WaitControl(IConsoleControl console, WaitOptions options) : base(console, options) + + public WaitControl(IConsoleControl console, WaitOptions options) : base(console, options) { _options = options; } @@ -36,6 +39,8 @@ public override string InitControl(CancellationToken cancellationToken) { throw new PromptPlusException("Not have process to run"); } + _context = _options.Context; + _event = new EventWaitProcess(ref _context, false); _ctsesc = new CancellationTokenSource(); _lnkcts = CancellationTokenSource.CreateLinkedTokenSource(_ctsesc.Token, cancellationToken); return string.Empty; @@ -78,7 +83,7 @@ protected virtual void Dispose(bool disposing) #region IControlWait - public IControlWait Interaction(IEnumerable values, Action action) + public IControlWait Interaction(IEnumerable values, Action, T1> action) { foreach (var item in values) { @@ -86,30 +91,37 @@ public IControlWait Interaction(IEnumerable values, Action ShowElapsedTime() { _options.ShowElapsedTime = true; return this; } - public IControlWait Config(Action context) + public IControlWait Context(T value) + { + _options.Context = value; + return this; + } + + + public IControlWait Config(Action context) { context?.Invoke(_options); return this; } - public IControlWait Finish(string value) + public IControlWait Finish(string value) { _options.Finish = value; return this; } - public IControlWait TaskTitle(string value) + public IControlWait TaskTitle(string value) { _options.OverWriteTitlekName = value; return this; } - public IControlWait Spinner(SpinnersType spinnersType, Style? spinnerStyle = null, int? speedAnimation = null, IEnumerable? customspinner = null) + public IControlWait Spinner(SpinnersType spinnersType, Style? spinnerStyle = null, int? speedAnimation = null, IEnumerable? customspinner = null) { if (spinnersType == SpinnersType.Custom && customspinner.Any()) { @@ -128,7 +140,7 @@ public IControlWait Spinner(SpinnersType spinnersType, Style? spinnerStyle = nul } - public IControlWait MaxDegreeProcess(int value) + public IControlWait MaxDegreeProcess(int value) { if (value < 1) { @@ -138,12 +150,12 @@ public IControlWait MaxDegreeProcess(int value) return this; } - public IControlWait AddStep(StepMode stepMode, params Action[] process) + public IControlWait AddStep(StepMode stepMode, params Action, CancellationToken>[] process) { return AddStep(stepMode, string.Empty, string.Empty, process); } - public IControlWait AddStep(StepMode stepMode, string id, string description, params Action[] process) + public IControlWait AddStep(StepMode stepMode, string id, string description, params Action,CancellationToken>[] process) { foreach (var item in process) { @@ -167,7 +179,7 @@ public override void InputTemplate(ScreenBuffer screenBuffer) _process ??= Task.Run(() => RunAllTasks(_lnkcts.Token), CancellationToken.None); } - public override ResultPrompt> TryResult(CancellationToken cancellationToken) + public override ResultPrompt> TryResult(CancellationToken cancellationToken) { var abort = false; while (!_lnkcts.IsCancellationRequested) @@ -203,10 +215,10 @@ public override ResultPrompt> TryResult(CancellationTo abort = true; _ctsesc.Cancel(); } - return new ResultPrompt>(_options.States, abort); + return new ResultPrompt>(new ResultWaitProcess(_event.Context,_options.States.ToArray()), abort); } - public override void FinishTemplate(ScreenBuffer screenBuffer, IEnumerable result, bool aborted) + public override void FinishTemplate(ScreenBuffer screenBuffer, ResultWaitProcess result, bool aborted) { _ctsesc.Cancel(); if (aborted) @@ -282,7 +294,7 @@ private void RunAllTasks(CancellationToken cancelationtoken) { samedesc = (firstdesc == _options.States[i].Description); } - if (!cancelationtoken.IsCancellationRequested) + if (!cancelationtoken.IsCancellationRequested && !_event.CancelAllNextTasks) { var act = _options.Steps[i]; using var waitHandle = new AutoResetEvent(false); @@ -297,7 +309,7 @@ private void RunAllTasks(CancellationToken cancelationtoken) tm.Start(); try { - act.Invoke(cancelationtoken); + act.Invoke(_event,cancelationtoken); actsta = cancelationtoken.IsCancellationRequested? TaskStatus.Canceled: TaskStatus.RanToCompletion; } catch (Exception ex) diff --git a/Src/Controls/TasksProcess/WaitExtensions.cs b/Src/Controls/TasksProcess/WaitExtensions.cs index c0e0c721..bc817585 100644 --- a/Src/Controls/TasksProcess/WaitExtensions.cs +++ b/Src/Controls/TasksProcess/WaitExtensions.cs @@ -16,10 +16,21 @@ public static partial class PromptPlus /// /// The prompt text to write /// The description text to write - /// IEnumerable after Run method. - public static IControlWait WaitProcess(string prompt, string description=null) + /// + public static IControlWait WaitProcess(string prompt, string description = null) { - return WaitProcess(prompt,description, null); + return WaitProcess(prompt, description, null); + } + + /// + /// Create Wait Control + /// + /// The prompt text to write + /// The description text to write + /// + public static IControlWait WaitProcess(string prompt, string description = null) + { + return WaitProcess(prompt, description, null); } /// @@ -28,17 +39,37 @@ public static IControlWait WaitProcess(string prompt, string description=null) /// The prompt text to write /// The description text to write /// The config action - /// IEnumerable after Run method. - public static IControlWait WaitProcess(string prompt, string description, Action config = null) + /// + public static IControlWait WaitProcess(string prompt, string description, Action config = null) { - var opt = new WaitOptions(false) + var opt = new WaitOptions(false) { WaitTime = false, OptPrompt = prompt, OptDescription = description, }; config?.Invoke(opt); - return new WaitControl(_consoledrive, opt); + return new WaitControl(_consoledrive, opt); + } + + + /// + /// Create Wait Control + /// + /// The prompt text to write + /// The description text to write + /// The config action + /// + public static IControlWait WaitProcess(string prompt, string description, Action config = null) + { + var opt = new WaitOptions(false) + { + WaitTime = false, + OptPrompt = prompt, + OptDescription = description, + }; + config?.Invoke(opt); + return new WaitControl(_consoledrive, opt); } /// @@ -50,11 +81,10 @@ public static IControlWait WaitProcess(string prompt, string description, Action /// True show Countdown, otherwise 'no' /// The config action /// for control - public static void WaitTimer(string prompt, TimeSpan delay, SpinnersType spinnersType = SpinnersType.Ascii, bool showCountdown = false, Action config = null, CancellationToken? cancellationToken = null) { var cts = cancellationToken ?? CancellationToken.None; - var opt = new WaitOptions(false) + var opt = new WaitOptions(false) { WaitTime = true, TimeDelay = delay, @@ -64,9 +94,10 @@ public static void WaitTimer(string prompt, TimeSpan delay, SpinnersType spinner opt.HideAfterFinish(true); opt.EnabledAbortKey(false); config?.Invoke(opt); - var aux = new WaitControl(_consoledrive, opt); + var aux = new WaitControl(_consoledrive, opt); + aux.Context(null); aux.Spinner(spinnersType); - aux.AddStep(StepMode.Sequential, (cts) => + aux.AddStep(StepMode.Sequential, (eventw, cts) => { cts.WaitHandle.WaitOne(delay); }); diff --git a/Src/Controls/TasksProcess/WaitOptions.cs b/Src/Controls/TasksProcess/WaitOptions.cs index 4cb857ef..cff981fa 100644 --- a/Src/Controls/TasksProcess/WaitOptions.cs +++ b/Src/Controls/TasksProcess/WaitOptions.cs @@ -10,7 +10,7 @@ namespace PPlus.Controls { - internal class WaitOptions : BaseOptions + internal class WaitOptions : BaseOptions { private WaitOptions() { @@ -22,11 +22,12 @@ internal WaitOptions(bool showcursor) : base(showcursor) Steps = new(); States = new(); } + public T Context { get; set; } = default; public TimeSpan TimeDelay { get; set; } public string OverWriteTitlekName { get; set; } public bool WaitTime { get; set; } public bool ShowCountdown { get; set; } - public List> Steps { get; set; } + public List, CancellationToken>> Steps { get; set; } public List States { get; set; } public Style SpinnerStyle { get; set; } = PromptPlus.StyleSchema.Prompt(); public string? Finish { get; set; } diff --git a/Src/Drivers/ConsoleDriveMemory.cs b/Src/Drivers/ConsoleDriveMemory.cs index 8cca357b..c7681086 100644 --- a/Src/Drivers/ConsoleDriveMemory.cs +++ b/Src/Drivers/ConsoleDriveMemory.cs @@ -304,17 +304,17 @@ private int CountLines(Segment[] segments, int left, int width, int padleft) { return 1; } + var pos = left; foreach (var segment in segments.Where(x => !x.IsAnsiControl)) { var overflow = segment.Style.OverflowStrategy; var parts = segment.Text.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); + if (pos < padleft) + { + pos = padleft; + } foreach (var (_, first, last, part) in parts.Enumerate()) { - var pos = left; - if (pos < padleft) - { - pos = padleft; - } if (part != null) { pos += part.GetWidth(); @@ -355,6 +355,7 @@ private int CountLines(Segment[] segments, int left, int width, int padleft) if (!first) { qtd++; + pos = padleft; } } } diff --git a/Src/Drivers/ConsoleDriveWindows.cs b/Src/Drivers/ConsoleDriveWindows.cs index 315469aa..20a79f35 100644 --- a/Src/Drivers/ConsoleDriveWindows.cs +++ b/Src/Drivers/ConsoleDriveWindows.cs @@ -8,6 +8,7 @@ using System; using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Text; using System.Threading; @@ -252,16 +253,19 @@ private int CountLines(Segment[] segments, int left, int width, int padleft) { return 1; } + int pos = left; foreach (var segment in segments.Where(x => !x.IsAnsiControl)) { var overflow = segment.Style.OverflowStrategy; var parts = segment.Text.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); foreach (var (_, first, last, part) in parts.Enumerate()) { - var pos = left; - if (pos < padleft) + if (first) { - pos = padleft; + if (pos < padleft) + { + pos = padleft; + } } if (part != null) { @@ -302,6 +306,7 @@ private int CountLines(Segment[] segments, int left, int width, int padleft) if (!first) { qtd++; + pos = padleft; } } } diff --git a/UnitTests/Controls/TaskWaitControl/WaitControlTests.cs b/UnitTests/Controls/TaskWaitControl/WaitControlTests.cs index c6b39ea0..155260a9 100644 --- a/UnitTests/Controls/TaskWaitControl/WaitControlTests.cs +++ b/UnitTests/Controls/TaskWaitControl/WaitControlTests.cs @@ -9,10 +9,10 @@ public class WaitControlTests : BaseTest [Fact] public void Should_ValidInitControlPrompt1() { - var ctrl = (WaitControl)PromptPlus + var ctrl = (WaitControl)PromptPlus .WaitProcess("P") - .AddStep(StepMode.Sequential,(cts) => - { + .AddStep(StepMode.Sequential, (_, cts) => + { }); ActionOnDispose = () => ctrl.FinalizeControl(CancellationToken.None); @@ -24,9 +24,9 @@ public void Should_ValidInitControlPrompt1() [Fact] public void Should_ValidInitControlPrompt2() { - var ctrl = (WaitControl)PromptPlus - .WaitProcess("P","D") - .AddStep(StepMode.Sequential, (cts) => + var ctrl = (WaitControl)PromptPlus + .WaitProcess("P", "D") + .AddStep(StepMode.Sequential, (_, cts) => { }); @@ -39,10 +39,10 @@ public void Should_ValidInitControlPrompt2() [Fact] public void Should_OverwriteCultureTaskName() { - var ctrl = (WaitControl)PromptPlus + var ctrl = (WaitControl)PromptPlus .WaitProcess("P", "D") .TaskTitle("XXXXXXXXXXXX") - .AddStep(StepMode.Sequential, "task1", "desc task1", (cts) => + .AddStep(StepMode.Sequential, "task1", "desc task1", (_, cts) => { Thread.Sleep(500); }); @@ -58,7 +58,7 @@ public void Should_OverwriteCultureTaskName() ctrl.InputTemplate(new ScreenBuffer()); var result = ctrl.TryResult(CancellationToken.None); Assert.False(result.IsRunning); - Assert.True(result.Value.Count() == 1); + Assert.True(result.Value.States.Count() == 1); }); }); Assert.Contains("XXXXXXXXXXXX", output); @@ -67,11 +67,11 @@ public void Should_OverwriteCultureTaskName() [Fact] public void Should_Interaction() { - var ctrl = (WaitControl)PromptPlus + var ctrl = (WaitControl)PromptPlus .WaitProcess("P", "D") .Interaction(new string[2] { "", "" }, (ctrl, _) => { - ctrl.AddStep(StepMode.Sequential, (cts) => + ctrl.AddStep(StepMode.Sequential, (_, cts) => { }); }); @@ -85,8 +85,8 @@ public void Should_Interaction() ctrl.InputTemplate(new ScreenBuffer()); var result = ctrl.TryResult(CancellationToken.None); Assert.True(!result.IsRunning); - Assert.True(result.Value.Count() == 2); - Assert.True(result.Value.First().Status == TaskStatus.RanToCompletion); + Assert.True(result.Value.States.Count() == 2); + Assert.True(result.Value.States.First().Status == TaskStatus.RanToCompletion); }); } @@ -94,9 +94,9 @@ public void Should_Interaction() [Fact] public void Should_Runtask1() { - var ctrl = (WaitControl)PromptPlus + var ctrl = (WaitControl)PromptPlus .WaitProcess("P", "D") - .AddStep(StepMode.Sequential,"task1","desc task1" ,(cts) => + .AddStep(StepMode.Sequential, "task1", "desc task1", (_, cts) => { Thread.Sleep(250); }); @@ -112,9 +112,9 @@ public void Should_Runtask1() ctrl.InputTemplate(new ScreenBuffer()); var result = ctrl.TryResult(CancellationToken.None); Assert.True(!result.IsRunning); - Assert.True(result.Value.Count() == 1); - Assert.True(result.Value.First().Status == TaskStatus.RanToCompletion); - Assert.True(result.Value.First().ElapsedTime > TimeSpan.FromMilliseconds(250)); + Assert.True(result.Value.States.Count() == 1); + Assert.True(result.Value.States.First().Status == TaskStatus.RanToCompletion); + Assert.True(result.Value.States.First().ElapsedTime > TimeSpan.FromMilliseconds(250)); }); }); Assert.Contains("desc task1", output); @@ -124,9 +124,9 @@ public void Should_Runtask1() [Fact] public void Should_RuntaskWithException() { - var ctrl = (WaitControl)PromptPlus + var ctrl = (WaitControl)PromptPlus .WaitProcess("P", "D") - .AddStep(StepMode.Sequential, "task1", "desc task1", (cts) => + .AddStep(StepMode.Sequential, "task1", "desc task1", (_, cts) => { cts.WaitHandle.WaitOne(400); throw new Exception(); @@ -139,14 +139,14 @@ public void Should_RuntaskWithException() var output = PromptPlus.RecordOutput(() => { ctrl.InputTemplate(new ScreenBuffer()); - ResultPrompt>? result = null; + ResultPrompt>? result = null; CompletesIn(3000, () => { result = ctrl.TryResult(CancellationToken.None); - },true); + }, true); Assert.False(result!.Value.IsRunning); - Assert.True(result!.Value.Value.Count() == 1); - Assert.True(result!.Value.Value.First().Status == TaskStatus.Faulted); + Assert.True(result!.Value.Value.States.Count() == 1); + Assert.True(result!.Value.Value.States.First().Status == TaskStatus.Faulted); }); Assert.Contains("desc task1", output); } @@ -154,8 +154,9 @@ public void Should_RuntaskWithException() [Fact] public void Should_showAcceptInputEsc() { - var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") - .AddStep(StepMode.Parallel, (_) => { }); + var ctrl = (WaitControl)PromptPlus + .WaitProcess("P", "D") + .AddStep(StepMode.Parallel, (_, _) => { }); ActionOnDispose = () => ctrl.FinalizeControl(CancellationToken.None); ctrl.InitControl(CancellationToken.None); @@ -174,11 +175,11 @@ public void Should_showAcceptInputEsc() [Fact] public void Should_notshowAcceptInputEsc() { - var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D", (cfg) => + var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D", (cfg) => { cfg.EnabledAbortKey(false); }) - .AddStep(StepMode.Parallel, (_) => { }); + .AddStep(StepMode.Parallel, (_, _) => { }); ActionOnDispose = () => ctrl.FinalizeControl(CancellationToken.None); ctrl.InitControl(CancellationToken.None); @@ -197,11 +198,11 @@ public void Should_notshowAcceptInputEsc() [Fact] public void Should_AcceptInputTemplateCustomTooltip() { - var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D", (cfg) => + var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D", (cfg) => { cfg.Tooltips("CustomTooltip"); }) - .AddStep(StepMode.Parallel, (_) => { }); + .AddStep(StepMode.Parallel, (_, _) => { }); ActionOnDispose = () => ctrl.FinalizeControl(CancellationToken.None); ctrl.InitControl(CancellationToken.None); @@ -214,14 +215,14 @@ public void Should_AcceptInputTemplateCustomTooltip() var result = ctrl.TryResult(CancellationToken.None); }); }); - Assert.Contains("CustomTooltip",output); + Assert.Contains("CustomTooltip", output); } [Fact] public void Should_TryResulAcceptEsc() { - var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") - .AddStep(StepMode.Sequential, (cts) => + var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") + .AddStep(StepMode.Sequential, (_, cts) => { cts.WaitHandle.WaitOne(5000); }); @@ -243,12 +244,12 @@ public void Should_TryResulAcceptEsc() [Fact] public void Should_TryResulNotAcceptEsc() { - var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") - .AddStep(StepMode.Sequential, (cts) => + var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") + .AddStep(StepMode.Sequential, (_, cts) => { cts.WaitHandle.WaitOne(2000); }) - .Config((cfg) => + .Config((cfg) => { cfg.EnabledAbortKey(false); }); @@ -264,17 +265,17 @@ public void Should_TryResulNotAcceptEsc() var result = ctrl.TryResult(CancellationToken.None); Assert.True(!result.IsAborted); Assert.False(result.IsRunning); - Assert.True(result.Value.First().ElapsedTime > TimeSpan.FromSeconds(2)); + Assert.True(result.Value.States.First().ElapsedTime > TimeSpan.FromSeconds(2)); }); } [Fact] public void Should_TryResultAbort() { - var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") - .AddStep(StepMode.Sequential, (cts) => + var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") + .AddStep(StepMode.Sequential, (_, cts) => { - cts.WaitHandle.WaitOne(5000); + cts.WaitHandle.WaitOne(5000); }); ActionOnDispose = () => ctrl.FinalizeControl(CancellationToken.None); @@ -297,13 +298,135 @@ public void Should_TryResultAbort() }); } + [Fact] + public void Should_EventAbortSequential() + { + var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") + .MaxDegreeProcess(2) + .AddStep(StepMode.Sequential, (_, cts) => + { + cts.WaitHandle.WaitOne(1000); + }) + .AddStep(StepMode.Sequential, (evt, cts) => + { + evt.CancelAllNextTasks = true; + cts.WaitHandle.WaitOne(2000); + + }) + .AddStep(StepMode.Sequential, (_, cts) => + { + cts.WaitHandle.WaitOne(5000); + }) + .AddStep(StepMode.Sequential, (_, cts) => + { + cts.WaitHandle.WaitOne(5000); + }); + + ActionOnDispose = () => ctrl.FinalizeControl(CancellationToken.None); + + + using var cts = new CancellationTokenSource(); + + ctrl.InitControl(cts.Token); + + ctrl.InputTemplate(new ScreenBuffer()); + + CompletesIn(8000, () => + { + var result = ctrl.TryResult(cts.Token); + Assert.False(result.IsAborted); + Assert.Equal(2,result.Value.States.Count(x => x.Status == TaskStatus.Canceled)); + }); + } + + [Fact] + public void Should_EventChangeConext() + { + var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") + .MaxDegreeProcess(2) + .Context(0) + .AddStep(StepMode.Parallel, (evt, cts) => + { + evt.Context++; + }) + .AddStep(StepMode.Parallel, (evt, cts) => + { + evt.Context++; + }) + .AddStep(StepMode.Parallel, (evt, cts) => + { + evt.Context++; + }) + .AddStep(StepMode.Parallel, (evt, cts) => + { + evt.Context++; + }); + + ActionOnDispose = () => ctrl.FinalizeControl(CancellationToken.None); + + + using var cts = new CancellationTokenSource(); + + ctrl.InitControl(cts.Token); + + ctrl.InputTemplate(new ScreenBuffer()); + + CompletesIn(1000, () => + { + var result = ctrl.TryResult(cts.Token); + Assert.False(result.IsAborted); + Assert.Equal(4, result.Value.Context); + }); + } + + [Fact] + public void Should_EventAbortParallel() + { + var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") + .MaxDegreeProcess(2) + .AddStep(StepMode.Parallel, (evt, cts) => + { + evt.CancelAllNextTasks = true; + cts.WaitHandle.WaitOne(1000); + }) + .AddStep(StepMode.Parallel, (_, cts) => + { + cts.WaitHandle.WaitOne(2000); + + }) + .AddStep(StepMode.Parallel, (_, cts) => + { + cts.WaitHandle.WaitOne(5000); + }) + .AddStep(StepMode.Parallel, (_, cts) => + { + cts.WaitHandle.WaitOne(5000); + }); + + ActionOnDispose = () => ctrl.FinalizeControl(CancellationToken.None); + + + using var cts = new CancellationTokenSource(); + + ctrl.InitControl(cts.Token); + + ctrl.InputTemplate(new ScreenBuffer()); + + CompletesIn(8000, () => + { + var result = ctrl.TryResult(cts.Token); + Assert.False(result.IsAborted); + Assert.Equal(2, result.Value.States.Count(x => x.Status == TaskStatus.Canceled)); + }); + } + [Fact] public void Should_AcceptCustomFinish() { - var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") + var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") .Finish("FinishTest") - .AddStep(StepMode.Parallel, (_) => { }); + .AddStep(StepMode.Parallel, (_, _) => { }); ActionOnDispose = () => ctrl.FinalizeControl(CancellationToken.None); ctrl.InitControl(CancellationToken.None); @@ -314,7 +437,7 @@ public void Should_AcceptCustomFinish() { ctrl.InputTemplate(new ScreenBuffer()); ctrl.TryResult(CancellationToken.None); - ctrl.FinishTemplate(new ScreenBuffer(),new StateProcess[] { },false); + ctrl.FinishTemplate(new ScreenBuffer(), new ResultWaitProcess(new object(), Array.Empty()), false); }); }); Assert.Contains("FinishTest", output); @@ -323,8 +446,8 @@ public void Should_AcceptCustomFinish() [Fact] public void Should_AcceptFinishAbort() { - var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") - .AddStep(StepMode.Parallel, (_) => { }); + var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") + .AddStep(StepMode.Parallel, (_, _) => { }); ActionOnDispose = () => ctrl.FinalizeControl(CancellationToken.None); ctrl.InitControl(CancellationToken.None); @@ -335,7 +458,7 @@ public void Should_AcceptFinishAbort() { ctrl.InputTemplate(new ScreenBuffer()); ctrl.TryResult(CancellationToken.None); - ctrl.FinishTemplate(new ScreenBuffer(), new StateProcess[] { }, true); + ctrl.FinishTemplate(new ScreenBuffer(), new ResultWaitProcess(new object(), Array.Empty()), true); }); }); var partmsg = Messages.CanceledKey.Split()[0]; @@ -345,16 +468,16 @@ public void Should_AcceptFinishAbort() [Fact] public void Should_WaitTime() { - var opt = new WaitOptions(false) + var opt = new WaitOptions(false) { WaitTime = true, TimeDelay = TimeSpan.FromSeconds(1), ShowCountdown = true, OptPrompt = "P" }; - var ctrl = new WaitControl((IConsoleControl)PromptPlus.Console, opt); + var ctrl = new WaitControl((IConsoleControl)PromptPlus.Console, opt); ctrl.Spinner(SpinnersType.Ascii); - ctrl.AddStep(StepMode.Sequential, (cts) => + ctrl.AddStep(StepMode.Sequential, (_, cts) => { cts.WaitHandle.WaitOne(TimeSpan.FromSeconds(1)); }); @@ -368,7 +491,7 @@ public void Should_WaitTime() var result = ctrl.TryResult(CancellationToken.None); Assert.False(result.IsAborted); Assert.False(result.IsRunning); - Assert.True(result.Value.First().ElapsedTime >= TimeSpan.FromSeconds(1)); + Assert.True(result.Value.States.First().ElapsedTime >= TimeSpan.FromSeconds(1)); }); } @@ -376,11 +499,11 @@ public void Should_WaitTime() [Fact] public void Should_MaxDegreeProcess() { - var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") + var ctrl = (WaitControl)PromptPlus.WaitProcess("P", "D") .MaxDegreeProcess(1) - .AddStep(StepMode.Parallel, (_) => { }) - .AddStep(StepMode.Parallel, (_) => { }) - .AddStep(StepMode.Parallel, (_) => { }); + .AddStep(StepMode.Parallel, (_, _) => { }) + .AddStep(StepMode.Parallel, (_, _) => { }) + .AddStep(StepMode.Parallel, (_, _) => { }); ActionOnDispose = () => ctrl.FinalizeControl(CancellationToken.None); @@ -392,7 +515,7 @@ public void Should_MaxDegreeProcess() { ctrl.InputTemplate(new ScreenBuffer()); var result = ctrl.TryResult(CancellationToken.None); - Assert.True(result.Value.Count() == 3); + Assert.True(result.Value.States.Count() == 3); Assert.True(!result.IsRunning); }); }); diff --git a/docs/apis/apis.md b/docs/apis/apis.md index d9b0a08a..9a0d891c 100644 --- a/docs/apis/apis.md +++ b/docs/apis/apis.md @@ -74,6 +74,8 @@ - [EventPipe<T>](./pplus.controls.eventpipe-1.md) +- [EventWaitProcess<T>](./pplus.controls.eventwaitprocess-1.md) + - [FilterMode](./pplus.controls.filtermode.md) - [FormatTime](./pplus.controls.formattime.md) @@ -124,7 +126,7 @@ - [IControlTreeViewSelect<T>](./pplus.controls.icontroltreeviewselect-1.md) -- [IControlWait](./pplus.controls.icontrolwait.md) +- [IControlWait<T>](./pplus.controls.icontrolwait-1.md) - [IPromptConfig](./pplus.controls.ipromptconfig.md) @@ -160,6 +162,8 @@ - [ResultPrompt<T>](./pplus.controls.resultprompt-1.md) +- [ResultWaitProcess<T>](./pplus.controls.resultwaitprocess-1.md) + - [SliderBarType](./pplus.controls.sliderbartype.md) - [SpinnersType](./pplus.controls.spinnerstype.md) diff --git a/docs/apis/pplus.controls.eventwaitprocess-1.md b/docs/apis/pplus.controls.eventwaitprocess-1.md new file mode 100644 index 00000000..e1f9af28 --- /dev/null +++ b/docs/apis/pplus.controls.eventwaitprocess-1.md @@ -0,0 +1,66 @@ +# PromptPlus API:EventWaitProcess + +[![Build](https://github.com/FRACerqueira/PromptPlus/workflows/Build/badge.svg)](https://github.com/FRACerqueira/PromptPlus/actions/workflows/build.yml) +[![Publish](https://github.com/FRACerqueira/PromptPlus/actions/workflows/publish.yml/badge.svg)](https://github.com/FRACerqueira/PromptPlus/actions/workflows/publish.yml) +[![License](https://img.shields.io/github/license/FRACerqueira/PromptPlus)](https://github.com/FRACerqueira/PromptPlus/blob/master/LICENSE) +[![NuGet](https://img.shields.io/nuget/v/PromptPlus)](https://www.nuget.org/packages/PromptPlus/) +[![Downloads](https://img.shields.io/nuget/dt/PromptPlus)](https://www.nuget.org/packages/PromptPlus/) + +[**Back to List Api**](./apis.md) + +# EventWaitProcess<T> + +Namespace: PPlus.Controls + +Represents the event to task process with with conex value + +```csharp +public class EventWaitProcess +``` + +#### Type Parameters + +`T`
+Typeof Input + +Inheritance [Object](https://docs.microsoft.com/en-us/dotnet/api/system.object) → [EventWaitProcess<T>](./pplus.controls.eventwaitprocess-1.md) + +## Properties + +### **CancelNextAll** + +Context value + +```csharp +public bool CancelNextAll { get; private set; } +``` + +#### Property Value + +[Boolean](https://docs.microsoft.com/en-us/dotnet/api/system.boolean)
+ +###
**Context** + +Get/set Context value + +```csharp +public T Context { get; set; } +``` + +#### Property Value + +T
+ +## Methods + +###
**CancelAllNextTasks()** + +Set Cancel all next tasks. + +```csharp +public void CancelAllNextTasks() +``` + + +- - - +[**Back to List Api**](./apis.md) diff --git a/docs/apis/pplus.controls.icontrolwait-1.md b/docs/apis/pplus.controls.icontrolwait-1.md new file mode 100644 index 00000000..2d5a51a1 --- /dev/null +++ b/docs/apis/pplus.controls.icontrolwait-1.md @@ -0,0 +1,226 @@ +# PromptPlus API:IControlWait + +[![Build](https://github.com/FRACerqueira/PromptPlus/workflows/Build/badge.svg)](https://github.com/FRACerqueira/PromptPlus/actions/workflows/build.yml) +[![Publish](https://github.com/FRACerqueira/PromptPlus/actions/workflows/publish.yml/badge.svg)](https://github.com/FRACerqueira/PromptPlus/actions/workflows/publish.yml) +[![License](https://img.shields.io/github/license/FRACerqueira/PromptPlus)](https://github.com/FRACerqueira/PromptPlus/blob/master/LICENSE) +[![NuGet](https://img.shields.io/nuget/v/PromptPlus)](https://www.nuget.org/packages/PromptPlus/) +[![Downloads](https://img.shields.io/nuget/dt/PromptPlus)](https://www.nuget.org/packages/PromptPlus/) + +[**Back to List Api**](./apis.md) + +# IControlWait<T> + +Namespace: PPlus.Controls + +Represents the interface with all Methods of the WaitTimer/WaitProcess control + +```csharp +public interface IControlWait : IPromptControls> +``` + +#### Type Parameters + +`T`
+typeof return + +Implements IPromptControls<ResultWaitProcess<T>> + +## Methods + +###
**AddStep(StepMode, params Action<EventWaitProcess<T>, CancellationToken>[])** + +Add list of tasks to execute. + +```csharp +IControlWait AddStep(StepMode stepMode, params Action, CancellationToken>[] process) +``` + +#### Parameters + +`stepMode` [StepMode](./pplus.controls.stepmode.md)
+Sequential or parallel execution + +`process` Action<EventWaitProcess<T>, CancellationToken>[]
+list of tasks + +#### Returns + +[IControlWait<T>](./pplus.controls.icontrolwait-1.md) + +###
**AddStep(StepMode, String, String, params Action<EventWaitProcess<T>, CancellationToken>[])** + +Add list of tasks to execute with title and description + +```csharp +IControlWait AddStep(StepMode stepMode, string id, string label, params Action, CancellationToken>[] process) +``` + +#### Parameters + +`stepMode` [StepMode](./pplus.controls.stepmode.md)
+Sequential or parallel execution + +`id` [String](https://docs.microsoft.com/en-us/dotnet/api/system.string)
+Id of tasks + +`label` [String](https://docs.microsoft.com/en-us/dotnet/api/system.string)
+Label of tasks + +`process` Action<EventWaitProcess<T>, CancellationToken>[]
+list of tasks + +#### Returns + +[IControlWait<T>](./pplus.controls.icontrolwait-1.md) + +###
**Config(Action<IPromptConfig>)** + +Custom config the control. + +```csharp +IControlWait Config(Action context) +``` + +#### Parameters + +`context` [Action<IPromptConfig>](https://docs.microsoft.com/en-us/dotnet/api/system.action-1)
+Action to apply changes. [IPromptConfig](./pplus.controls.ipromptconfig.md) + +#### Returns + +[IControlWait<T>](./pplus.controls.icontrolwait-1.md) + +###
**Context(T)** + +Set Contex value for all tasks + +```csharp +IControlWait Context(T value) +``` + +#### Parameters + +`value` T
+Context value + +#### Returns + +[IControlWait<T>](./pplus.controls.icontrolwait-1.md) + +###
**Finish(String)** + +Finish answer to show when Wait process is completed. + +```csharp +IControlWait Finish(string text) +``` + +#### Parameters + +`text` [String](https://docs.microsoft.com/en-us/dotnet/api/system.string)
+Text Finish answer + +#### Returns + +[IControlWait<T>](./pplus.controls.icontrolwait-1.md) + +###
**Interaction<T1>(IEnumerable<T1>, Action<IControlWait<T>, T1>)** + +Execute a action foreach item of colletion passed as a parameter + +```csharp +IControlWait Interaction(IEnumerable values, Action, T1> action) +``` + +#### Type Parameters + +`T1`
+ +#### Parameters + +`values` IEnumerable<T1>
+Colletion for interaction + +`action` Action<IControlWait<T>, T1>
+Action to execute + +#### Returns + +[IControlWait<T>](./pplus.controls.icontrolwait-1.md) + +###
**MaxDegreeProcess(Int32)** + +Maximum number of concurrent tasks enable. Default vaue is number of processors. + +```csharp +IControlWait MaxDegreeProcess(int value) +``` + +#### Parameters + +`value` [Int32](https://docs.microsoft.com/en-us/dotnet/api/system.int32)
+Number of concurrent tasks + +#### Returns + +[IControlWait<T>](./pplus.controls.icontrolwait-1.md) + +###
**ShowElapsedTime()** + +Define if show Elapsed Time for each task. + +```csharp +IControlWait ShowElapsedTime() +``` + +#### Returns + +[IControlWait<T>](./pplus.controls.icontrolwait-1.md) + +### **Spinner(SpinnersType, Nullable<Style>, Nullable<Int32>, IEnumerable<String>)** + +Overwrite [SpinnersType](./pplus.controls.spinnerstype.md). Default value is SpinnersType.Ascii +
When use custom spinner, if has unicode values console does not support it, the rendering may not be as expected + +```csharp +IControlWait Spinner(SpinnersType spinnersType, Nullable