/// <summary> /// Map many Behaviours to a single Behaviour with a function. /// </summary> public Behaviour <R> Combine <B, R>(Func <A, B, R> combine, Behaviour <B> b) { var r = new Behaviour <R>(combine(Value, b.Value)); Subject.CombineLatest(b.Subject, combine).Subscribe(r.Subject); return(r); }
public static void Create(IFuse fuse, IObservable <Unit> doneLoadingMainWindow, BehaviorSubject <bool> showSetupGuide) { var showSublimeNotification = new BehaviorSubject <bool>(false); var dontCheckForSublimePlugin = UserSettings.Bool("DontCheckForSublimePlugin").AutoInvalidate(); Application.Desktop.CreateSingletonWindow( isVisible: showSublimeNotification.CombineLatest(doneLoadingMainWindow, (show, _) => show), window: dialog => new Window { Title = Observable.Return("No editor plugin detected"), Size = Optional.Some(Property.Constant(Optional.Some(new Size <Points>(600, 270)))), Content = Control.Lazy(() => CreateContent(showSublimeNotification, showSetupGuide, dontCheckForSublimePlugin)) .WithBackground(Theme.PanelBackground), Background = Theme.PanelBackground, Foreground = Theme.DefaultText, Border = Separator.MediumStroke, }); dontCheckForSublimePlugin .Select(v => v.Or(false)) .Where(v => !v) .Take(1) .SubscribeOn(TaskPoolScheduler.Default) .Subscribe(_ => Task.Run(() => { if (new SublimePlugin(fuse.ModulesDir).Status == ComponentStatus.NotInstalled) { showSublimeNotification.OnNext(true); } })); }
public static IControl CreateDebugControl() { var debugControls = DebugControls .Throttle(TimeSpan.FromMilliseconds(10)); var controls = debugControls .CachePerElement(getKey: ctrl => ctrl.Value, getValue: ctrl => ctrl.Value.Name) .SelectPerElement(name => CreateDebugItem(name, Command.Enabled(action: () => SelectedControl.OnNext(name)))); var infoPanel = SelectedControl .CombineLatest(debugControls, (sel, ctrls) => sel.HasValue ? CreateInfoPanel(ctrls[sel.Value]) : CreateNothingSelected()) .Switch(); return(Layout.Dock() .Left( Layout.Dock().Top(Header("Debug Controls")) .Fill(controls.StackFromTop().MakeScrollable()) .WithWidth(200) .WithPadding(new Thickness <Points>(10)) ) .Left( Layout.StackFromTop( Header("Mount Info"), infoPanel ).WithPadding(new Thickness <Points>(10)) ) .Fill() .WithPadding(new Thickness <Points>(10))); }
public SetupGuide(IFuse fuse, IObservable <Unit> doneLoadingMainWindow) { var output = new Subject <string>(); LogMessages = output; var report = fuse.Report; var softwares = new SoftwareCollectionStatus(fuse); var showSetupGuide = new BehaviorSubject <bool>(false); MissingPluginNotification.Create(fuse, doneLoadingMainWindow, showSetupGuide); Application.Desktop.CreateSingletonWindow( isVisible: showSetupGuide.CombineLatest(doneLoadingMainWindow, (s, _) => s), window: dialog => new Window { Title = Observable.Return("Setup guide"), Size = Optional.Some(Property.Constant(Optional.Some(new Size <Points>(500, 300)))), Content = Control.Lazy(() => Create(softwares, report, dialog).ShowWhen(showSetupGuide)) .WithBackground(Theme.PanelBackground), Background = Theme.PanelBackground, Foreground = Theme.DefaultText, Border = Separator.MediumStroke, Style = WindowStyle.Fat, }); Menu = Menu.Item("Install Android SDKs", () => fuse.RunFuse("install", new [] { "android" }, Observer.Create <string>(output.OnNext))) + Menu.Item("Setup guide", () => showSetupGuide.OnNext(true)); }
/// <summary> /// Initializes a new instance of the <see cref="FeatureActivator"/> class. /// </summary> /// <param name="activate">A function to be called when the first observer subscribes and all dependencies (if any) are available.</param> /// <param name="deactivate">A function to be called when any dependency becomes unavailable.</param> /// <param name="dependsOn">A set of dependencies, all of which must produce a true result in order for activation to be triggered.</param> public FeatureActivator(Func <T> activate, Func <T> deactivate, params IObservable <bool>[] dependsOn) { if (activate == null) { throw new ArgumentNullException("activate"); } this.activate = activate; this.deactivate = deactivate ?? (() => default(T)); IObservable <bool> dependenciesAvailable = new BehaviorSubject <bool>(true); foreach (var dependency in dependsOn) { dependenciesAvailable = dependenciesAvailable.CombineLatest(dependency, (x, y) => x && y); } available = dependenciesAvailable .DistinctUntilChanged() .Select(availableNow => { if (!availableNow) { if (hasBeenActivated) { return(Deactivate()); } return(default(T)); } return(Activate()); }) .Replay(1); }
public Submission() { children = new BehaviorSubject <IEnumerable <Submission> >(Enumerable.Empty <Submission>()); answer = new BehaviorSubject <string>(""); manualGrade = new BehaviorSubject <float?>(null); childGrade = children.Select(xs => xs.Select(x => x.grade).CombineLatestSafe()).Merge().Select(x => x.Flatten().AverageSafe()); childPass = children.Select(xs => xs.Select(x => x.pass).CombineLatestSafe()).Merge().Select(xs => xs.Conjunction()); grade = manualGrade.CombineLatest(childGrade, childPass, (manualGrade1, childGrade1, childPass1) => { if (manualGrade1.HasValue) { return(manualGrade1); } else { if (childPass1) { return(childGrade1); } else { return(null); } } }); pass = grade.Select(g => g.HasValue ? g >= 5.5f : false); }
public void RxPrecise() { var lefts = new BehaviorSubject <bool>(false); var rights = new BehaviorSubject <bool>(false); var leftOrRight = lefts.CombineLatest(rights, (left, right) => left || right); leftOrRight.Subscribe(value => Console.WriteLine("leftOrRight = " + value)); // Prints 'leftOrRight = False' rights.OnNext(true); // Prints 'leftOrRight = True' lefts.OnNext(true); // Prints 'leftOrRight = True' rights.OnNext(false); // Prints 'leftOrRight = True' (while SmartReactives prints nothing here) }
private IControl CreateContent() { return (Layout.StackFromTop( Label.Create( _mainFile .Select(x => x.Select(path => "Start/stop random mutation of " + path.NativePath)) .Or("No file with <App> tag found.") .AsText()) .WithHeight(70) .Center(), Button.Create(clicked: _state.CombineLatest(_mainFile, CreateCommand).Switch()) .Center(), Label.Create("DANGER: This will overwrite stuff in your file!") .WithHeight(70) .Center())); }
public async Task SyncBufferTest() { var i1 = new BehaviorSubject <int>(1); var i2 = new BehaviorSubject <int>(4); var sum = i1.CombineLatest(i2, (i1Value, i2Value) => i1Value + i2Value); var listAsync = sum.SynchronousBuffer().Select(buf => buf.Last()).ToList().RunAsync(new CancellationToken()); Action syncChange1 = () => { i1.OnNext(2); i2.OnNext(5); i1.OnNext(7); }; Action syncChange2 = () => { i1.OnNext(1); i2.OnNext(1); }; Action syncChange3 = () => { i1.OnNext(3); i1.OnCompleted(); i2.OnCompleted(); }; var changeTask = Task.Run(syncChange1) .ContinueWith(t => syncChange2()) .ContinueWith(t => syncChange3()); var list = await listAsync; await changeTask; Assert.Equal(new List <int> { 5, 12, 2, 4 }, list.ToList()); }
public static void Create() { var lightTheme = new OriginalLightTheme(); var darkTheme = new OriginalDarkTheme(); var path = new BehaviorSubject <string>(string.Empty); var refresh = new BehaviorSubject <Unit>(Unit.Default); var image = path .CombineLatest(refresh, ((p, _) => p)) .Select(p => AbsoluteFilePath.TryParse(p) .Where(f => File.Exists(f.NativePath)) .Select( absPath => absPath.NativePath.EndsWith(".svg") ? (IImage) new SvgImage(() => File.OpenRead(absPath.NativePath)) : new MultiResolutionImage( new[] { new ImageStream(new Ratio <Pixels, Points>(1), () => File.OpenRead(absPath.NativePath)) })) .Or(() => (IImage) new SvgImage(() => new MemoryStream(FallbackImage)))); var content = Layout.Dock().Top( Layout.Dock() .Left(Label.Create("Path: ", font: Theme.DefaultFont, color: Theme.DefaultText).CenterVertically()) .Right(Buttons.DefaultButton("Refresh", Command.Enabled(() => refresh.OnNext(Unit.Default)))) .Fill(ThemedTextBox.Create(path.AsProperty()))) .Fill(Layout.SubdivideHorizontally(ImageVersionsRowForTheme(image, darkTheme), ImageVersionsRowForTheme(image, lightTheme))) .WithBackground(Theme.PanelBackground); Application.Desktop.CreateSingletonWindow( Observable.Return(true), dialog => new Window { Title = Observable.Return("Icon preview"), Size = Property.Create <Optional <Size <Points> > >(new Size <Points>(600, 600)).ToOptional(), Content = content, Background = Theme.PanelBackground, Foreground = Theme.DefaultText, Border = Separator.MediumStroke }); }
public void AsyncObservableThreadsWithBetterThrottleOnComputeAndIsCalculating() { Console.WriteLine("Starting Thread " + Thread.CurrentThread.ManagedThreadId); BehaviorSubject<int> s1 = new BehaviorSubject<int>(2); BehaviorSubject<int> s2 = new BehaviorSubject<int>(3); BehaviorSubject<int> sum = new BehaviorSubject<int>(5); List<int> computeThreads = new List<int>(); List<int> receiveThreads = new List<int>(); IScheduler throttleScheduler = new EventLoopScheduler(); Func<IScheduler> getComputeScheduler = () => new EventLoopScheduler(); IScheduler receiveScheduler = new EventLoopScheduler(); IObservable<Tuple<int, int>> sumObservable = s1.CombineLatest(s2, Tuple.Create).Throttle(TimeSpan.FromMilliseconds(100), throttleScheduler); IDisposable sumObservableSubscription = null; using (sumObservable.Subscribe(v => { if (sumObservableSubscription != null) { Console.WriteLine("Canceling previous."); sumObservableSubscription.Dispose(); } sumObservableSubscription = Observable.Create<int>((o, token) => Task.Factory.StartNew(() => { Thread.Sleep(200); if (!token.IsCancellationRequested) { Console.WriteLine("Computing value " + v.Item1 + " + " + v.Item2 + " = " + (v.Item1 + v.Item2) + " on Thread " + Thread.CurrentThread.ManagedThreadId + "."); computeThreads.Add(Thread.CurrentThread.ManagedThreadId); o.OnNext(v.Item1 + v.Item2); } o.OnCompleted(); return Disposable.Empty; })).ObserveOn(receiveScheduler).Subscribe(v2 => { Console.WriteLine("Received value " + v2 + " on Thread " + Thread.CurrentThread.ManagedThreadId + "."); receiveThreads.Add(Thread.CurrentThread.ManagedThreadId); }); })) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); Thread.Sleep(150); s2.OnNext(1); Thread.Sleep(50); s1.OnNext(4); Thread.Sleep(250); s2.OnNext(4); Thread.Sleep(150); s1.OnNext(1); Thread.Sleep(350); stopwatch.Stop(); Console.WriteLine("Total Time: " + stopwatch.ElapsedMilliseconds + " ms"); foreach (KeyValuePair<int, int> p in computeThreads.GroupBy(v => v).Select(g => new KeyValuePair<int, int>(g.Key, g.Count()))) { Console.WriteLine(p.Value + " computes on Thread " + p.Key); } foreach (KeyValuePair<int, int> p in receiveThreads.GroupBy(v => v).Select(g => new KeyValuePair<int, int>(g.Key, g.Count()))) { Console.WriteLine(p.Value + " receives on Thread " + p.Key); } } }
public void RxOnlyAsyncObservableThreadsWithBetterThrottleOnComputeAndIsCalculating() { Console.WriteLine("Starting Thread " + Thread.CurrentThread.ManagedThreadId); BehaviorSubject<int> s1 = new BehaviorSubject<int>(2); BehaviorSubject<int> s2 = new BehaviorSubject<int>(3); List<int> computeThreads = new List<int>(); List<int> receiveThreads = new List<int>(); IScheduler throttleScheduler = new EventLoopScheduler(); IScheduler computeScheduler = NewThreadScheduler.Default; IScheduler receiveScheduler = new EventLoopScheduler(); IObservable<Tuple<int, int>> sumObservable = s1.CombineLatest(s2, Tuple.Create).Throttle(TimeSpan.FromMilliseconds(100), throttleScheduler); Func<CalculatedPropertyHelper, int, int, Task<int>> calculate = async (helper, v1, v2) => { Thread.Sleep(200); helper.CheckCancellationToken(); Console.WriteLine("Computing value " + v1 + " + " + v2 + " = " + (v1 + v2) + " on Thread " + Thread.CurrentThread.ManagedThreadId + "."); computeThreads.Add(Thread.CurrentThread.ManagedThreadId); return await Task.FromResult(v1 + v2); }; BehaviorSubject<int> sum = new BehaviorSubject<int>(0); IDisposable scheduledTask = computeScheduler.ScheduleAsync(async (scheduler, token) => { await scheduler.Yield(); sum.OnNext(await calculate(new CalculatedPropertyHelper(scheduler, token), s1.Value, s2.Value)); }); using (sumObservable.Subscribe(v => { if (scheduledTask != null) { Console.WriteLine("Canceling previous."); scheduledTask.Dispose(); } scheduledTask = computeScheduler.ScheduleAsync(async (scheduler, token) => { await scheduler.Yield(); sum.OnNext(await calculate(new CalculatedPropertyHelper(scheduler, token), v.Item1, v.Item2)); }); })) { using (sum.ObserveOn(receiveScheduler).Subscribe(v2 => { Console.WriteLine("Received value " + v2 + " on Thread " + Thread.CurrentThread.ManagedThreadId + "."); receiveThreads.Add(Thread.CurrentThread.ManagedThreadId); })) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); Thread.Sleep(150); s2.OnNext(1); Thread.Sleep(50); s1.OnNext(4); Thread.Sleep(250); s2.OnNext(4); Thread.Sleep(150); s1.OnNext(1); Thread.Sleep(350); stopwatch.Stop(); Console.WriteLine("Total Time: " + stopwatch.ElapsedMilliseconds + " ms"); foreach (KeyValuePair<int, int> p in computeThreads.GroupBy(v => v).Select(g => new KeyValuePair<int, int>(g.Key, g.Count()))) { Console.WriteLine(p.Value + " computes on Thread " + p.Key); } foreach (KeyValuePair<int, int> p in receiveThreads.GroupBy(v => v).Select(g => new KeyValuePair<int, int>(g.Key, g.Count()))) { Console.WriteLine(p.Value + " receives on Thread " + p.Key); } } } }
public static IControl Create(ITreeViewModel model) { var rowHeight = TreeRowView.Height; var hoverIndicator = model.PendingDrop.SelectPerElement( x => Point.Create( Observable.Return(TreeRowView.GetIndentation(x.Depth + (x.DropPosition == DropPosition.Inside ? 1 : 0))), Observable.Return <Points>((x.RowOffset + (x.DropPosition != DropPosition.Before ? 1 : 0)) * rowHeight))); var rowCount = model.TotalRowCount.Replay(1).RefCount(); var visibleHeight = new BehaviorSubject <Points>(0); var contentHeight = visibleHeight.CombineLatest(rowCount, (vheight, rcount) => (Points)Math.Max(vheight, rcount * rowHeight)).Replay(1).RefCount(); var flipy = ShouldFlip ? ((Func <IObservable <Points>, IObservable <Points> >)(logicalY => logicalY.CombineLatest(contentHeight, (ly, ch) => ch - ly))) : (y => y); var rowOffsetToTop = (Func <IObservable <int>, IObservable <Points> >) (rowOffset => flipy( rowOffset.Select(offset => new Points(rowHeight.Value * offset)))); var top = RectangleEdge.Top.FlipVerticallyOnMac(); //var down = ShouldFlip ? -1.0 : 1.0; //var scrollTargetRect = // rowOffsetToTop(model.ScrollTarget) // .Select(rowTop => // Rectangle.FromPositionSize<Points>(0, 0, 50, 0) // .WithEdge(top, rowTop) // .WithEdge(top.Opposite(), rowTop + rowHeight * down)); var rowViews = model.VisibleRows .CachePerElement(rowModel => CreateRow(rowModel, rowOffsetToTop)) .Replay(1) .RefCount(); var background = rowViews.SelectPerElement(x => x.Background).Layer(); var foreground = rowViews.SelectPerElement(x => x.Foreground).Layer(); var overlay = rowViews.SelectPerElement(x => x.Overlay).Layer(); var width = rowViews.Select( rows => rows.Select(row => row.Foreground.DesiredSize.Width) .CombineLatest() .Select(widths => widths.ConcatOne(0).Max())) .Switch() .DistinctUntilChanged(); var treeView = Layout.Layer(background, foreground, overlay) .WithBackground(Theme.PanelBackground) .WithOverlay(InsertionRod.Create(hoverIndicator)) .WithHeight(contentHeight) .WithWidth(width) .MakeScrollable( darkTheme: Theme.IsDark, // scrollToRectangle: scrollTargetRect, onBoundsChanged: bounds => { var rowTop = bounds.Visible.GetEdge(top); rowTop = ShouldFlip ? bounds.Content.Height - rowTop : rowTop; var rowOffset = (int)Math.Floor(rowTop / rowHeight); model.VisibleRowOffset = rowOffset; var maxVisibleRowCount = (int)Math.Ceiling(bounds.Visible.VerticalInterval.Length / rowHeight.Value) + 1; model.VisibleRowCount = maxVisibleRowCount; visibleHeight.OnNext(bounds.Visible.Height); }); // TODO: CAN'T GET LOCAL KEYS TO WORK PROPERLY //.OnKeyPressed(ModifierKeys.None, Key.Up, model.NavigateUpCommand, isGlobal: true) //.OnKeyPressed(ModifierKeys.None, Key.Down, model.NavigateDownCommand, isGlobal: true); return(CreatePopScopeButton(model).DockTop(fill: treeView)); }
protected override IObservable <float> CreateObservable(IAttributeHolder holder) { Ensure.That(holder, nameof(holder)).IsNotNull(); return(_value.CombineLatest(OnModifierChange, OnRangeChange, (v, m, r) => r.Clamp(v * m))); }