Skip to content

Commit

Permalink
Merge commit 'ee4e36b5c703ad70241a38e055ec556ca4960092'
Browse files Browse the repository at this point in the history
  • Loading branch information
johnspackman committed Mar 11, 2024
2 parents 098f10d + ee4e36b commit 2138547
Show file tree
Hide file tree
Showing 10 changed files with 271 additions and 75 deletions.
27 changes: 27 additions & 0 deletions source/class/qxl/datagrid/column/Column.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,33 @@ qx.Class.define("qxl.datagrid.column.Column", {
check: "Function",
nullable: true,
event: "changeShouldFillWidth"
},

/**
* A callback used to determine how many columns a cell should fill. This
* also includes header cells at negative indexes.
*
* If the returned value is not a number, is zero or is negative, the
* colspan will default to 1. If a non-integer value is returned, it will
* always be rounded down.
*
* For grid cells, the behavior of colSpan will be overridden by
* {@link #shouldFillWidth} if that property's value function returns
* `true`.
*
* Providing a callback to this property will override the default call to
* {@link qxl.datagrid.ui.GridStyling#colSpan}'s value function. Columns
* may defer back to the GridStyling property value function by calling the
* first argument `stylingFn` (no parameters necessary).
*
*
* @type {(stylingFn: Function, model: any, child: qx.ui.core.Widget, relativePosition: qxl.datagrid.source.Position, absolutePosition: qxl.datagrid.source.Position) => Integer}
*/
colSpan: {
init: null,
check: "Function",
nullable: true,
event: "changecolSpan"
}
},

Expand Down
4 changes: 3 additions & 1 deletion source/class/qxl/datagrid/demo/biggrid/BigGridDemo.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ qx.Class.define("qxl.datagrid.demo.biggrid.BigGridDemo", {
rbComp.add(rbRow.set({ model: "row" }));
let rbCell = new qx.ui.form.RadioButton("Cell");
rbComp.add(rbCell.set({ model: "cell" }));
let rbArea = new qx.ui.form.RadioButton("Area");
rbComp.add(rbArea.set({ model: "area" }));
comp.add(rbComp, { row: 0, column: 1 });
let manager = new qx.ui.form.RadioGroup(rbRow, rbCell);
let manager = new qx.ui.form.RadioGroup(rbRow, rbCell, rbArea);
manager.addListener("changeSelection", evt => {
let rb = evt.getData()[0];
grid.getSelectionManager().setSelectionStyle(rb.getModel());
Expand Down
13 changes: 9 additions & 4 deletions source/class/qxl/datagrid/source/Position.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,15 @@ qx.Class.define("qxl.datagrid.source.Position", {
*/
__coerceValues(args) {
let row, column;
if (args.length == 1 && qx.lang.Type.isObject(args[0])) {
({ row, column } = args[0]);
} else if (args.length == 1 && qx.lang.Type.isArray(args[0])) {
[row, column] = args[0];
if (args.length == 1) {
if (args[0] instanceof qxl.datagrid.source.Position) {
row = args[0].getRow();
column = args[0].getColumn();
} else if (qx.lang.Type.isObject(args[0])) {
({ row, column } = args[0]);
} else if (qx.lang.Type.isArray(args[0])) {
[row, column] = args[0];
}
} else if (args.length > 0) {
[row, column] = args;
}
Expand Down
2 changes: 1 addition & 1 deletion source/class/qxl/datagrid/source/Range.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ qx.Class.define("qxl.datagrid.source.Range", {
/**
* Tests whether this range eclipses (ie completely overlaps) another range
*
* @param {qxl.datagrid.rource.Range} range
* @param {qxl.datagrid.source.Range} range
* @returns {Boolean}
*/
eclipses(range) {
Expand Down
13 changes: 9 additions & 4 deletions source/class/qxl/datagrid/test/source/Position.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,22 @@ qx.Class.define("qxl.datagrid.test.source.Position", {
this.assertTrue(p.getRow() == 1);
this.assertTrue(p.getColumn() == 2);

p = new qxl.datagrid.source.Position({ row: 3, column: 4 });
let sample = new qxl.datagrid.source.Position().set({ row: 3, column: 4 });
p = new qxl.datagrid.source.Position(sample);
this.assertTrue(p.getRow() == 3);
this.assertTrue(p.getColumn() == 4);

p = new qxl.datagrid.source.Position([5, 6]);
p = new qxl.datagrid.source.Position({ row: 5, column: 6 });
this.assertTrue(p.getRow() == 5);
this.assertTrue(p.getColumn() == 6);

p = new qxl.datagrid.source.Position([7, 7]);
this.assertTrue(p.getRow() == 7);
this.assertTrue(p.getColumn() == 8);

p.increment(1, 1);
this.assertTrue(p.getRow() == 6);
this.assertTrue(p.getColumn() == 7);
this.assertTrue(p.getRow() == 8);
this.assertTrue(p.getColumn() == 9);
}
}
});
27 changes: 27 additions & 0 deletions source/class/qxl/datagrid/ui/GridStyling.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,33 @@ qx.Class.define("qxl.datagrid.ui.GridStyling", {
check: "Integer",
apply: "__applyXxx",
event: "changeVerticalSpacing"
},

// todo: colspan here, use it in wpane/hrows/oeBgs, col-specific colspan takes priority over this.
/**
* A callback used to determine how many columns a cell should fill. This
* also includes header cells at negative indexes.
*
* If the returned value is not a number, is zero or is negative, the
* colspan will default to 1. If a non-integer value is returned, it will
* always be rounded down.
*
* For grid cells, the behavior of colSpan will be overridden by
* {@link qxl.datagrid.column.Column#shouldFillWidth} if that property's
* value function returns `true`.
*
* For a given column, if that column specifies
* {@link qxl.datagrid.column.Column#colSpan} then that value function will
* be used and this one will be ignored. Columns may defer back to this
* function by calling their first argument (no parameters necessary).
*
* @type {(model: any, child: qx.ui.core.Widget, relativePosition: qxl.datagrid.source.Position, absolutePosition: qxl.datagrid.source.Position) => Integer}
*/
colSpan: {
init: null,
check: "Function",
nullable: true,
event: "changecolSpan"
}
},

Expand Down
67 changes: 54 additions & 13 deletions source/class/qxl/datagrid/ui/HeaderRows.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ qx.Class.define("qxl.datagrid.ui.HeaderRows", {
if (!this.__widgetFactory.getColumns()) {
return;
}
let columns = this.__widgetFactory.getColumns();
let styling = this.__sizeCalculator.getStyling();
let sizesData = this.__sizeCalculator.getSizes();
if (!sizesData) {
Expand All @@ -66,35 +67,44 @@ qx.Class.define("qxl.datagrid.ui.HeaderRows", {
let children = {};
qx.lang.Array.clone(this._getChildren()).forEach(child => {
let cellData = child.getUserData("qxl.datagrid.cellData");
let id = cellData.row + ":" + cellData.column;
// prettier-ignore
if (cellData.row > numHeaderRows ||
cellData.column < minColumnIndex ||
cellData.column > maxColumnIndex) {
this.__widgetFactory.unbindWidget(child);
child.setUserData("qxl.datagrid.cellData", null);
this._remove(child);
this.__widgetFactory.disposeWidget(child);
if (cellData.row > numHeaderRows || cellData.column < minColumnIndex || cellData.column > maxColumnIndex) {
this.__fullDiscardWidget(child, id);
} else {
let id = cellData.row + ":" + cellData.column;
children[id] = child;
}
});

let horizontalSpacing = styling.getHorizontalSpacing();
let verticalSpacing = styling.getVerticalSpacing();
let top = 0;

const gridStyleColSpanFn = styling.getColSpan();

let currentRelativePosition = new qxl.datagrid.source.Position();
let currentAbsolutePosition = new qxl.datagrid.source.Position();
for (let rowSizeData of sizesData.rows) {
let left = 0;
if (rowSizeData.rowIndex >= 0) {
continue;
}
let rowIndex = -1 - rowSizeData.rowIndex;
let rowIndex = rowSizeData.rowIndex;
// exclusive endIndex
let lastColSpanEndIndex = -Infinity;

for (let visibleColumnIndex = 0; visibleColumnIndex < sizesData.columns.length; visibleColumnIndex++) {
let columnSizeData = sizesData.columns[visibleColumnIndex];
for (let relativeColumnIndex = 0; relativeColumnIndex < sizesData.columns.length; relativeColumnIndex++) {
let columnSizeData = sizesData.columns[relativeColumnIndex];
let id = rowIndex + ":" + columnSizeData.columnIndex;
let filledWidth = columnSizeData.width;

let child = children[id];

if (relativeColumnIndex < lastColSpanEndIndex) {
this.__fullDiscardWidget(child, id);
continue;
}

if (!child) {
child = this.__widgetFactory.getWidgetFor(rowIndex, columnSizeData.columnIndex);
children[id] = child;
Expand All @@ -106,17 +116,48 @@ qx.Class.define("qxl.datagrid.ui.HeaderRows", {
this.__widgetFactory.bindWidget(child);
}

const callbackArguments = [null, child, currentRelativePosition, currentAbsolutePosition];

currentRelativePosition.set({ row: rowIndex, column: relativeColumnIndex });
currentAbsolutePosition.set({ row: rowIndex, column: columnSizeData.columnIndex });

const columnColSpanFn = columns.getColumn(columnSizeData.columnIndex).getColSpan();
let colSpan = 1;
if (columnColSpanFn) {
colSpan = columnColSpanFn(() => gridStyleColSpanFn?.(...callbackArguments), ...callbackArguments);
} else if (gridStyleColSpanFn) {
colSpan = gridStyleColSpanFn(...callbackArguments);
}
colSpan = Math.floor(colSpan ?? 1);
child.setUserData("qxl.datagrid.cellData", {
...child.getUserData("qxl.datagrid.cellData"),
colSpan
});
lastColSpanEndIndex = relativeColumnIndex + colSpan;
for (let i = relativeColumnIndex + 1; i < lastColSpanEndIndex; i++) {
filledWidth += sizesData.columns[i].width + horizontalSpacing;
}

child.setLayoutProperties({
left: left,
width: columnSizeData.width,
width: filledWidth,
top: top,
height: rowSizeData.height
});
child.getSizeHint(true);
left += columnSizeData.width + horizontalSpacing;
left += filledWidth + horizontalSpacing;
}
top += rowSizeData.height + verticalSpacing;
}
},

__fullDiscardWidget(child, id) {
if (child) {
this.__widgetFactory.unbindWidget(child);
child.setUserData("qxl.datagrid.cellData", null);
this._remove(child);
this.__widgetFactory.disposeWidget(child);
}
}
}
});
2 changes: 1 addition & 1 deletion source/class/qxl/datagrid/ui/OddEvenRowBackgrounds.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ qx.Class.define("qxl.datagrid.ui.OddEvenRowBackgrounds", {
let maxRowIndex = null;
let minDataRowIndex = null;
sizesData.rows.forEach(row => {
if (row.rowIndex >= 0 && (minDataRowIndex === null || minDataRowIndex > row.rowIndex)) {
if (row.rowIndex >= styling.getNumFixedRows() && (minDataRowIndex === null || minDataRowIndex > row.rowIndex)) {
minDataRowIndex = row.rowIndex;
}
if (minRowIndex === null || minRowIndex > row.rowIndex) {
Expand Down
66 changes: 56 additions & 10 deletions source/class/qxl/datagrid/ui/SelectionManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ qx.Class.define("qxl.datagrid.ui.SelectionManager", {
/** Whether the user has to select entire rows, or just individual cells */
selectionStyle: {
init: "row",
check: ["row", "cell"],
check: ["row", "cell", "area"],
apply: "_applySelectionStyle",
event: "changeSelectionStyle"
},
Expand Down Expand Up @@ -89,8 +89,7 @@ qx.Class.define("qxl.datagrid.ui.SelectionManager", {
* Apply for `selectionStyle`
*/
_applySelectionStyle(value) {
if (value == "row") {
let dataSource = this.getDataSource();
if (value === "row") {
for (let i = 0; i < this.__selection.getLength(); i++) {
let model = this.__selection.getItem(i);
model = this.__forceRowModel(model);
Expand All @@ -110,16 +109,22 @@ qx.Class.define("qxl.datagrid.ui.SelectionManager", {
* Apply for `selectionMode`
*/
_applySelectionMode(value) {
if (value == "one" && this.__selection.getLength() > 1) {
if (value === "one" && this.__selection.getLength() > 1) {
this.__selection.replace([this.__selection.getItem(0)]);
}

if (qx.core.Environment.get("qx.debug")) {
if (this.getSelectionStyle() === "area") {
this.warn(`${this.classname}.selectionMode has no effect when the selectionStyle is 'area'`);
}
}
},

/**
* Transform for `focused`
*/
__transformFocused(value) {
if (this.getSelectionStyle() == "row") {
if (this.getSelectionStyle() === "row") {
value = this.__forceRowModel(value);
}
return value;
Expand Down Expand Up @@ -147,14 +152,55 @@ qx.Class.define("qxl.datagrid.ui.SelectionManager", {
return this.__selection;
},

getSelectionRange() {
return this.__selectionRange;
},

/**
* Replaces current selection with the given items.
*
* @param items {qx.ui.core.Widget[]} Items to select.
* @param {qx.ui.core.Widget[]|qx.data.Array<qx.ui.core.Widget>|qxl.datagrid.source.Range} selection Items to select.
* @throws {Error} if one of the items is not a child element and if
* items contains more than one elements.
*/
setSelection(items) {
setSelection(selection) {
this.__selectionRange = null;
if (this.getSelectionStyle() === "area") {
this.__setSelectionArea(selection);
} else {
this.__setSelectionStandard(selection);
}
},

__setSelectionArea(range) {
if (qx.core.Environment.get("qx.debug")) {
this.assertInstance(range, qxl.datagrid.source.Range, `Failed to set selection in area selection style. The selection ${range} is not an instance of qxl.datagrid.source.Range`);
}
this.__selectionRange = range;
this.__selection.replace(this.__cellsFromRange(range));
this.setFocused(this.__selection.getItem(0));
},

__cellsFromRange(range) {
const dataSource = this.getDataSource();
const x1 = Math.min(range.getStart().getColumn(), range.getEnd().getColumn());
const x2 = Math.max(range.getStart().getColumn(), range.getEnd().getColumn());
const y1 = Math.min(range.getStart().getRow(), range.getEnd().getRow());
const y2 = Math.max(range.getStart().getRow(), range.getEnd().getRow());
const items = new qx.data.Array();
for (let y = y1; y <= y2; y++) {
for (let x = x1; x <= x2; x++) {
const nextItem = dataSource.getModelForPosition(new qxl.datagrid.source.Position(y, x));
if (qx.core.Environment.get("qx.debug")) {
this.assertNotNull(nextItem, `Failed to set selection in area selection style. There is no item at position (${x},${y}) in the DataGrid.`);
}
items.push(nextItem);
}
}
return items;
},

__setSelectionStandard(items) {
if (qx.core.Environment.get("qx.debug")) {
const dataSource = this.getDataSource();
items.forEach(item => {
Expand All @@ -164,10 +210,10 @@ qx.Class.define("qxl.datagrid.ui.SelectionManager", {
if (items instanceof qx.data.Array) {
items = items.toArray();
}
if (this.getSelectionMode() == "one" && items.length > 1) {
if (this.getSelectionMode() === "one" && items.length > 1) {
items = [items[0]];
}
if (this.getSelectionStyle() == "row") {
if (this.getSelectionStyle() === "row") {
items = items.map(model => this.__forceRowModel(model));
}
this.__selection.replace(items);
Expand Down Expand Up @@ -197,7 +243,7 @@ qx.Class.define("qxl.datagrid.ui.SelectionManager", {
* @return {Boolean} Whether the selection is empty.
*/
isSelectionEmpty() {
return this.__selection.getLength() == 0;
return this.__selection.getLength() === 0;
},

/**
Expand Down
Loading

0 comments on commit 2138547

Please sign in to comment.