diff --git a/source/class/qxl/datagrid/DataGrid.js b/source/class/qxl/datagrid/DataGrid.js index 4febaee..453a8c2 100644 --- a/source/class/qxl/datagrid/DataGrid.js +++ b/source/class/qxl/datagrid/DataGrid.js @@ -161,6 +161,16 @@ qx.Class.define("qxl.datagrid.DataGrid", { */ scrollbar: { group: ["scrollbarX", "scrollbarY"] + }, + + /** + * Whether the grid is read only (even if the data grid does not yet support editing, this is + * still relevant because it is available to the columns and ) + */ + readOnly: { + init: false, + check: "Boolean", + event: "changeReadOnly" } }, @@ -190,6 +200,7 @@ qx.Class.define("qxl.datagrid.DataGrid", { headerWidgetFactory() { return new qxl.datagrid.ui.factory.HeaderWidgetFactory(this.getColumns()); }, + header() { return new qxl.datagrid.ui.HeaderRows(this.__sizeCalculator, this.getQxObject("headerWidgetFactory"), this.getDataSource()); } @@ -227,10 +238,16 @@ qx.Class.define("qxl.datagrid.DataGrid", { */ _applyColumns(value, oldValue) { if (oldValue) { - oldValue.removeListener("change", this.scheduleLayoutUpdate, this); + oldValue.removeListener("change", this.__onColumnsChange, this); + for (let column of oldValue) { + column.setDataGrid(null); + } } if (value) { - value.addListener("change", this.scheduleLayoutUpdate, this); + for (let column of value) { + column.setDataGrid(this); + } + value.addListener("change", this.__onColumnsChange, this); } this.__sizeCalculator.setColumns(value); this.getQxObject("headerWidgetFactory").setColumns(value); @@ -238,6 +255,22 @@ qx.Class.define("qxl.datagrid.DataGrid", { this.updateWidgets(); }, + /** + * Event handler for changes in the columns array + * + * @param {qx.event.type.Data} evt + */ + __onColumnsChange(evt) { + let data = evt.getData(); + if (data.type == "remove") { + data.column.setDataGrid(null); + } + if (data.type == "add") { + data.column.setDataGrid(this); + } + this.scheduleLayoutUpdate(); + }, + /** * Apply for `dataSource` */ diff --git a/source/class/qxl/datagrid/binding/Bindings.js b/source/class/qxl/datagrid/binding/Bindings.js index 635fc0c..857b161 100644 --- a/source/class/qxl/datagrid/binding/Bindings.js +++ b/source/class/qxl/datagrid/binding/Bindings.js @@ -65,14 +65,28 @@ qx.Class.define("qxl.datagrid.binding.Bindings", { __bindingData: null, /** + * Adds a binding or listener binding ID. + * + * Set `bindingType` to "binding" if this is a binding to a property, or "listener" if it's for a listener + * added with "addListener". If no value then it will be auto detected * * @param {qx.core.Object} model the object with a binding * @param {*} bindingId the binding ID to release - * @param {String?"binding"} bindingType The type of the binding. Either "binding" or "listener". Defaults to binding. Set to "binding " if this is a binding to a property, or "listener" if it's for a listener added with "addListener". + * @param {String?} bindingType The type of the binding. Either "binding" or "listener". Defaults to auto detect. */ add(model, bindingId, bindingType) { if (bindingType === undefined) { - bindingType = "binding"; + if (typeof bindingId == "string") { + bindingType = "listener"; + } else { + bindingType = "binding"; + } + } else if (qx.core.Environment.get("qx.debug")) { + if (bindingType === "listener") { + this.assertTrue(typeof bindingId == "string", "Invalid binding type " + bindingType + " for bindingId " + bindingId); + } else { + this.assertTrue(typeof bindingId != "string", "Invalid binding type " + bindingType + " for bindingId " + JSON.stringify(bindingId)); + } } this.__bindingData.push({ model, diff --git a/source/class/qxl/datagrid/column/Column.js b/source/class/qxl/datagrid/column/Column.js index 86db7ab..9b73e03 100644 --- a/source/class/qxl/datagrid/column/Column.js +++ b/source/class/qxl/datagrid/column/Column.js @@ -91,17 +91,103 @@ qx.Class.define("qxl.datagrid.column.Column", { event: "changeReadOnly" }, + /** Whether the column is enabled (this is controlled by the data grid) */ + enabled: { + init: true, + check: "Boolean", + apply: "_applyEnabled", + event: "changeEnabled" + }, + bindingOptions: { init: () => undefined } }, events: { + /** Fired when the column details change */ change: "qx.event.type.Data", - headerTap: "qx.event.type.Event" + + /** Fired when the user taps on a header cell */ + headerTap: "qx.event.type.Event", + + /** Fired when the effectivelyEnabled needs to be checked, data is a {Boolean} */ + changeEffectivelyEnabled: "qx.event.type.Data", + + /** Fired when the effectivelyReadOnly needs to be checked, data is a {Boolean} */ + changeEffectivelyReadOnly: "qx.event.type.Data" }, members: { + __datagrid: null, + + /** + * Called the the DataGrid when the column is added. Do NOT call this manually. + * + * @param {qxl.datagrid.DataGrid} datagrid + */ + setDataGrid(datagrid) { + if (this.__datagrid === datagrid) { + return; + } + if (qx.core.Environment.get("qx.debug")) { + this.assertInstance(datagrid, qxl.datagrid.DataGrid); + this.assertTrue(!this.__datagrid, "DataGrid already set"); + } + if (this.__datagrid) { + this.__datagrid.removeListener("changeEnabled", this.__onDataGridChangeEnabled, this); + this.__datagrid.removeListener("changeReadOnly", this.__onDataGridChangeReadOnly, this); + } + this.__datagrid = datagrid; + if (datagrid) { + datagrid.addListener("changeEnabled", this.__onDataGridChangeEnabled, this); + datagrid.addListener("changeReadOnly", this.__onDataGridChangeReadOnly, this); + } + this.fireDataEvent("changeEffectivelyEnabled", this.isEffectivelyEnabled()); + this.fireDataEvent("changeEffectivelyReadOnly", this.isEffectivelyEnabled()); + }, + + /** + * Returns the datagrid, if any + * + * @returns {qxl.datagrid.DataGrid} + */ + getDataGrid() { + return this.__datagrid; + }, + + /** + * Event handler for when the datagrid changes `enabled` + */ + __onDataGridChangeEnabled(evt) { + this.fireDataEvent("changeEffectivelyEnabled", this.isEffectivelyEnabled()); + }, + + /** + * Event handler for when the datagrid changes `readOnly` + */ + __onDataGridChangeReadOnly(evt) { + this.fireDataEvent("changeEffectivelyReadOnly", this.isEffectivelyEnabled()); + }, + + /** + * Detects whether the column is read only, taking into account the datagrid + * + * @returns {Boolean} + */ + isEffectivelyReadOnly() { + return this.getReadOnly() || !this.getEnabled() || this.__datagrid?.isReadOnly() || !this.__datagrid?.isEnabled(); + }, + + /** + * Detects whether the column is enabled, taking into account the datagrid + * + * @returns {Boolean} + */ + isEffectivelyEnabled() { + return !this.getEnabled() || this.__datagrid?.isEnabled(); + }, + /** * Called to implement the binding * @@ -112,13 +198,48 @@ qx.Class.define("qxl.datagrid.column.Column", { */ bindWidget(widget, model, factory) { let path = this.getPath(); + let bindings = new qxl.datagrid.binding.Bindings(model); if (path) { if (model) { let bindingId = model.bind(path, widget, "value", this.getBindingOptions()(widget, model)); - return new qxl.datagrid.binding.Bindings(model, bindingId); + bindings.add(model, bindingId); } } - return new qxl.datagrid.binding.Bindings(model); + if (typeof widget.setReadOnly == "function") { + const update = () => { + widget.setReadOnly(this.isEffectivelyReadOnly()); + widget.setEnabled(this.isEffectivelyEnabled()); + }; + + bindings.add( + this, + this.addListener("changeEffectivelyReadOnly", () => update()), + "listener" + ); + bindings.add( + this, + this.addListener("changeEffectivelyEnabled", () => update()), + "listener" + ); + update(); + } else { + const update = () => { + widget.setEnabled(this.isEffectivelyEnabled() && !this.isEffectivelyReadOnly()); + }; + + bindings.add( + this, + this.addListener("changeEffectivelyReadOnly", () => update()), + "listener" + ); + bindings.add( + this, + this.addListener("changeEffectivelyEnabled", () => update()), + "listener" + ); + update(); + } + return bindings; }, /** @@ -152,8 +273,13 @@ qx.Class.define("qxl.datagrid.column.Column", { _applyFlex(value) {}, /** - * Apply for `readOnly` property` + * Apply for `readOnly` property + */ + _applyReadOnly(value) {}, + + /** + * Apply for `enabled` property */ - _applyReadOnly(value) {} + _applyEnabled(value) {} } }); diff --git a/source/class/qxl/datagrid/column/toolbar/CellWidget.js b/source/class/qxl/datagrid/column/toolbar/CellWidget.js index bb7a9bc..3ab1c6b 100644 --- a/source/class/qxl/datagrid/column/toolbar/CellWidget.js +++ b/source/class/qxl/datagrid/column/toolbar/CellWidget.js @@ -62,6 +62,11 @@ qx.Class.define("qxl.datagrid.column.toolbar.CellWidget", { } }, + /** + * Adds a button factory + * + * @param {qxl.datagrid.column.toolbar.Factory} factory + */ _addButton(factory) { let button = factory.createButton(); button.addListener("execute", () => { @@ -71,6 +76,11 @@ qx.Class.define("qxl.datagrid.column.toolbar.CellWidget", { this._add(button); }, + /** + * Removes a button factory + * + * @param {qxl.datagrid.column.toolbar.Factory} factory + */ _removeButton(factory) { let current = this.__buttonsByFactoryHash[factory.toHashCode()]; if (current) { @@ -80,6 +90,16 @@ qx.Class.define("qxl.datagrid.column.toolbar.CellWidget", { } }, + /** + * @Override + */ + _applyEnabled(value, oldValue) { + for (let btn of Object.values(this.__buttonsByFactoryHash)) { + btn.setEnabled(value); + } + return super._applyEnabled(value, oldValue); + }, + /** * Event handler for changes to the buttons array *