SmartColumn.js: update event triggers too early

The requiresSave state of a table field is not correctly updated
if the value of a smart column is edited.
This bug was introduced by the commits trying to prevent flickering
(added about a month ago).

The update event must not be fired before the value and the status
are correct.

288929
diff --git a/eclipse-scout-core/src/table/columns/Column.js b/eclipse-scout-core/src/table/columns/Column.js
index 7e34e8e..4cea3e3 100644
--- a/eclipse-scout-core/src/table/columns/Column.js
+++ b/eclipse-scout-core/src/table/columns/Column.js
@@ -529,7 +529,11 @@
 
   setCellValue(row, value) {
     let cell = this.cell(row);
+    this._setCellValue(row, value, cell);
+    this._updateCellText(row, cell);
+  }
 
+  _setCellValue(row, value, cell) {
     // value may have the wrong type (e.g. text instead of date) -> ensure type
     value = this._parseValue(value);
 
@@ -541,7 +545,6 @@
     }
 
     cell.setValue(value);
-    this._updateCellText(row, cell);
   }
 
   setCellTextDeferred(promise, row, cell) {
diff --git a/eclipse-scout-core/src/table/columns/SmartColumn.js b/eclipse-scout-core/src/table/columns/SmartColumn.js
index a92f203..a5f14e1 100644
--- a/eclipse-scout-core/src/table/columns/SmartColumn.js
+++ b/eclipse-scout-core/src/table/columns/SmartColumn.js
@@ -216,12 +216,32 @@
   }
 
   _updateCellFromValidEditor(row, field) {
+    // The following code is only necessary to prevent flickering because the text is updated async.
+    // Instead of only calling setCellValue which itself would update the display text, we set the text manually before calling setCellValue.
+    // This works because in most of the cases the text computed by the column will be the same as the one computed by the editor field.
+
+    // Clear error status first (regular behavior)
     this.setCellErrorStatus(row, null);
-    // Always set the text even if the value will be set
-    // This prevents flickering when display text is updated async.
-    // In most of the cases the text computed by the column will be the same as the one from the field.
-    this.setCellText(row, field.displayText);
-    this.setCellValue(row, field.value);
+
+    // Update cell text
+    // We cannot use setCellText to not trigger updateRows yet -> it has to be done after the value and row.status are updated correctly.
+    let cell = this.cell(row);
+    let oldText = cell.text;
+    let newText = field.displayText;
+    cell.setText(newText);
+
+    // Update cell value
+    // We cannot use setCellValue since it would add the update event to the updateBuffer but we need the row update to be sync to prevent the flickering
+    this._setCellValue(row, field.value, cell);
+
+    // Update row -> Render row, trigger update event
+    // Only trigger update row event if text has changed (same as setCellText would do)
+    if (row.initialized && oldText !== newText && cell.text === newText) {
+      this.table.updateRow(row);
+    }
+
+    // Ensure display text is correct (for the rare case that the column computes a different text than the editor field).
+    this._updateCellText(row, cell);
   }
 
   /**
@@ -235,9 +255,8 @@
     return !!value;
   }
 
-  setCellValue(row, value) {
-    super.setCellValue(row, value);
-    let cell = this.cell(row);
+  _setCellValue(row, value, cell) {
+    super._setCellValue(row, value, cell);
     cell.setSortCode(this._calculateCellSortCode(cell));
   }
 }
diff --git a/eclipse-scout-core/test/table/editor/CellEditorSpec.js b/eclipse-scout-core/test/table/editor/CellEditorSpec.js
index 194b52f..318d8d6 100644
--- a/eclipse-scout-core/test/table/editor/CellEditorSpec.js
+++ b/eclipse-scout-core/test/table/editor/CellEditorSpec.js
@@ -8,7 +8,7 @@
  * Contributors:
  *     BSI Business Systems Integration AG - initial API and implementation
  */
-import {Cell, keys, scout, StaticLookupCall, Status, Widget} from '../../../src/index';
+import {Cell, keys, scout, StaticLookupCall, Status, TableRow, Widget} from '../../../src/index';
 import {FormSpecHelper, TableSpecHelper} from '../../../src/testing/index';
 
 describe('CellEditor', () => {
@@ -535,6 +535,25 @@
         });
       });
     });
+
+    it('triggers update row event containing row with correct state', () => {
+      table.columns[0].setEditable(true);
+      table.markRowsAsNonChanged();
+      table.prepareCellEdit(table.columns[0], table.rows[0], true);
+      jasmine.clock().tick(300);
+      table.cellEditorPopup.cell.field.setValue('key1');
+      jasmine.clock().tick(300);
+      let updateRowCount = 0;
+      table.on('rowsUpdated', event => {
+        expect(event.rows[0].cells[0].value).toBe('key1');
+        expect(event.rows[0].cells[0].text).toBe('Key 1');
+        expect(event.rows[0].status).toBe(TableRow.Status.UPDATED);
+        updateRowCount++;
+      });
+      table.completeCellEdit();
+      jasmine.clock().tick(300);
+      expect(updateRowCount).toBe(1);
+    });
   });
 
   describe('cancelCellEdit', () => {