private static ImmutableDictionary <CellPos, Cell> CellsReducer(CellsModel parent, ImmutableDictionary <CellPos, Cell> oldCells, object action) { var newCells = oldCells.MutateEntries(action, (cell, _) => { return(ReduceCell(oldCells, cell, action)); }); // MyActions.SetCell can also be used to add new cells to the dictionary: if (action is MyActions.SetCell s && !newCells.ContainsKey(s.pos)) { newCells = newCells.Add(s.pos, NewCell(s, newCells, isSelected: false)); } // Update all cells that depend on the changed cell: int maxDependencySteps = 100; for (int i = 0; i < maxDependencySteps; i++) { if (oldCells == newCells) { return(oldCells); } // As long as there are cells changed because their dependent cells changed repeat updating var c = newCells; newCells = newCells.MutateEntries(action, (cell, _) => { return(UpdateDependentCell(cell, oldCells, newCells)); }); oldCells = c; } throw new StackOverflowException("Cells have to many transitive dependencies, might be a loop"); }
public static CellsModel MainReducer(CellsModel previousState, object action) { using (var mainReducerTiming = Log.MethodEnteredWith(action)) { var changed = false; var newCells = previousState.MutateField(previousState.cells, action, CellsReducer, ref changed); if (changed) { return(new CellsModel(newCells)); } return(previousState); } }
public static void TestDataStoreTransitiveChanges() { CellsModel model = new CellsModel(ImmutableDictionary <CellPos, Cell> .Empty); var store = new DataStore <CellsModel>(CellsReducers.MainReducer, model, Middlewares.NewLoggingMiddleware <CellsModel>()); store.Dispatch(new MyActions.SetCell("A", 1, "1 + 1")); var cells = store.SelectElement(s => s.cells); Cell a1 = cells()[new CellPos("A", 1)]; Assert.Equal(2, a1.value); // B1 will depend on A1: store.Dispatch(new MyActions.SetCell("B", 1, "3 * A1")); Cell b1 = cells()[new CellPos("B", 1)]; Assert.Equal(6, b1.value); // C1 will depend on A1 and B1: store.Dispatch(new MyActions.SetCell("C", 1, "B1 + 3 - A1")); Cell c1 = cells()[new CellPos("C", 1)]; Assert.Equal(7, c1.value); // D1 will depend on C1 (so transitivly also on A1 and B1): store.Dispatch(new MyActions.SetCell("D", 1, "2 * C1")); Cell d1 = cells()[new CellPos("D", 1)]; Assert.Equal(14, d1.value); // Now changing A1 must have affects to B1, C1, D1 as well: store.Dispatch(new MyActions.SetCell("A", 1, "4 + 1")); c1 = cells()[new CellPos("C", 1)]; Assert.Equal(13, c1.value); d1 = cells()[new CellPos("D", 1)]; Assert.Equal(26, d1.value); // Select cell C1: store.Dispatch(new MyActions.SelectCell(c1.pos)); Assert.True(cells()[new CellPos("C", 1)].isSelected); // Select cell D1: store.Dispatch(new MyActions.SelectCell(d1.pos)); Assert.True(cells()[new CellPos("D", 1)].isSelected); Assert.False(cells()[new CellPos("C", 1)].isSelected); store.Dispatch(new MyActions.SetCell("A", 3, "1")); store.Dispatch(new MyActions.SetCell("A", 2, "A3 + 1")); Assert.Throws <MyActions.SetCell.SelfRefException>(() => { store.Dispatch(new MyActions.SetCell("A", 3, "A2 + 1")); }); }
public static async Task ShowIn(ViewStack viewStack) { // Call model unit tests manually before the UI is shown: CellsModelTests.TestFromAndToRowName(); CellsModelTests.TestDataStoreTransitiveChanges(); CellsModel model = new CellsModel(ImmutableDictionary <CellPos, Cell> .Empty); Middleware <CellsModel> logging = Middlewares.NewLoggingMiddleware <CellsModel>(); UndoRedoReducer <CellsModel> undoLogic = new UndoRedoReducer <CellsModel>(); DataStore <CellsModel> store = new DataStore <CellsModel>(undoLogic.Wrap(CellsReducers.MainReducer), model, logging); MyPresenter presenter = new MyPresenter(); presenter.targetView = viewStack.ShowView("7GUIs_Task7_Cells"); await presenter.LoadModelIntoView(store); await TaskV2.Delay(2000); Toast.Show("Now simulating some table model changes.."); // Simulate changes in the model to check if the UI updates correctly: CellsModelTests.SimulateSomeChangesInModel(store); }
public static async Task TestLoggingOverhead() { StopwatchV2 t1, t2; { t1 = Log.MethodEntered("SimulateManyChangesInModel without logging"); CellsModel model = new CellsModel(ImmutableDictionary <CellPos, Cell> .Empty); var store = new DataStore <CellsModel>(CellsReducers.MainReducer, model); await SimulateManyChangesInModel(store); Log.MethodDone(t1); } { t2 = Log.MethodEntered("SimulateManyChangesInModel without logging"); CellsModel model = new CellsModel(ImmutableDictionary <CellPos, Cell> .Empty); var store = new DataStore <CellsModel>(CellsReducers.MainReducer, model, Middlewares.NewLoggingMiddleware <CellsModel>()); await SimulateManyChangesInModel(store); Log.MethodDone(t2); } // Logging makes mutating the model at least double as slow: Assert.True(t1.ElapsedMilliseconds * 2 < t2.ElapsedMilliseconds, $"t1={t1}, t2={t2}"); }