Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a6358e7
table models [WIP]
parro-it Sep 7, 2018
ed610a7
Moved function type check to napi_utils
parro-it Sep 8, 2018
ce9738a
model handler binding struct
parro-it Sep 8, 2018
6f0d8a7
model create function
parro-it Sep 9, 2018
706787d
numcolumns fn
parro-it Sep 9, 2018
5d9a714
todo
parro-it Sep 9, 2018
9e7a3e7
refactor: extracted run_handler_fn
parro-it Sep 9, 2018
47fc4dc
table & example [WIP]
parro-it Sep 12, 2018
960235f
model handler methods
parro-it Sep 12, 2018
c8f5c7f
table method
parro-it Sep 14, 2018
e08071a
add columns method in js table
parro-it Sep 18, 2018
9f9a6a1
handle & option colorModelCOlumn
parro-it Sep 18, 2018
3d5280c
table model js
parro-it Sep 18, 2018
ca1b7b2
tweaks
parro-it Sep 18, 2018
7c52cdf
setCellValue
parro-it Sep 19, 2018
b869741
tweaks
parro-it Sep 22, 2018
390c631
merged.us
parro-it Sep 22, 2018
91cf6d4
Fixed some warnings
parro-it Sep 22, 2018
df07f4d
button column
parro-it Sep 22, 2018
5ffce08
checkbox column
parro-it Sep 22, 2018
1b67455
progressbar column
parro-it Sep 22, 2018
555ec48
UiTableModel.ValueTypes enumeration
parro-it Sep 25, 2018
3bcab20
images [WIP]
parro-it Sep 25, 2018
862e7a2
uiimage
parro-it Sep 25, 2018
511836d
tweaks
parro-it Sep 25, 2018
d0e8d95
advanced model [WIP]
parro-it Sep 28, 2018
1646a1b
extracted fromMetadata method to own file
parro-it Sep 28, 2018
b312018
Boolean [WIP]
parro-it Sep 28, 2018
4d99821
Merge branch 'master' into tables
parro-it Sep 28, 2018
e3848cf
lint
parro-it Oct 6, 2018
f732140
Bool -> Checkbox
parro-it Oct 25, 2018
7b56ae0
back-merged master
parro-it Nov 7, 2018
74eabe0
tweak
parro-it Nov 7, 2018
6319174
LabeledCheckbox & ProgressBar column types
parro-it Nov 8, 2018
14fb292
WIP - remaining column types
parro-it Nov 8, 2018
6b82ec5
Fix label code on Button column
parro-it Nov 8, 2018
fcd1523
removed old table example
parro-it Nov 8, 2018
f1bca07
metadata model completed...
parro-it Dec 4, 2018
776f4a6
Fix image index calculation
parro-it Dec 10, 2018
8d87b16
fix ;
parro-it Dec 10, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added example/lightning-orb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions example/tables.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const libui = require('..');
const {Text, Image, Checkbox, ProgressBar, Button, LabeledCheckbox, LabeledImage} =
libui.UiTableModel.Fields;

const win = new libui.UiWindow('Tables example', 800, 600, true);
win.margined = true;

const model = libui.UiTableModel.fromMetadata({
name: {type: Text, editable: true, header: 'Name'},
surname: {type: Text},
male: {type: Checkbox, editable: true, header: 'Is a male?'},
picture: {type: Image, value: () => img},
completed: {type: ProgressBar},
someProp: {type: LabeledCheckbox, editable: true, label: 'some props'},
otherProp: {type: LabeledImage, value: () => img, label: 'an Image'},
sayhi: {
type: Button,
editable: () => 1,
label: 'Say Hi',
click: obj => libui.UiDialogs.msgBox(win, 'HI', obj.name + ' ' + obj.surname)
}
});

const data = [
{name: 'Andrea', surname: 'Parodi', male: true, completed: 30, someProp: true},
{name: 'Giorgia', surname: 'Parodi', male: false, completed: 75, someProp: false}
];

const tb = new libui.UiTable(model.bind(data));
model.addColumns(tb, 'name', 'surname', 'picture', 'male', 'completed', 'sayhi',
'someProp', 'otherProp');

const vbox = new libui.UiVerticalBox();
vbox.append(tb, true);
win.setChild(vbox);

win.onClosing(() => {
win.close();
libui.stopLoop();
});

let img = null;
async function run() {
img = await libui.UiImage.loadFromPng(__dirname + '/lightning-orb.png');
win.show();
libui.startLoop();
}

run().catch(err => console.error(err));
30 changes: 20 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const {
const {UiBox} = require('./js/box');
const {SeparatorBase} = require('./js/separator-base');
const {UiControl} = require('./js/ui-control');

const {UiTable} = require('./js/table');
const {UiButton} = require('./js/button');
const {UiWindow} = require('./js/window');
const {UiSlider} = require('./js/slider');
Expand Down Expand Up @@ -81,6 +81,13 @@ const {FontDescriptor} = require('./js/font-descriptor');
const {FontAttribute} = require('./js/font-attribute');
const {AttributedString} = require('./js/font-string');
const {OpenTypeFeatures} = require('./js/font-opentype');
const {UiTableModel} = require('./js/table-model');
const {UiImage} = require('./js/image');
const fromMetadata = require('./js/table-model-from-metadata');

UiTableModel.fromMetadata = fromMetadata;
UiTableModel.ValueTypes = fromMetadata.ValueTypes;
UiTableModel.Fields = fromMetadata.Fields;

function applySetterGetter(...classConstructors) {
for (const classConstructor of classConstructors) {
Expand Down Expand Up @@ -146,19 +153,22 @@ function applySetterGetterAll(doSetter, ...classConstructors) {
}

// Takes about 3.5ms:
applySetterGetter(UiEntryBase, UiBox, SeparatorBase, UiControl, UiGrid, UiMenuItem,
UiMenu, UiSpinbox, UiHorizontalSeparator, UiVerticalSeparator,
UiRadioButtons, UiProgressBar, UiGroup, UiEntry, UiPasswordEntry,
UiSearchEntry, UiEditableCombobox, UiTimePicker, UiDatePicker,
UiDateTimePicker, UiCombobox, UiColorButton, UiCheckbox, UiWindow,
UiButton, UiLabel, UiForm, UiSlider, UiMultilineEntry, UiHorizontalBox,
UiVerticalBox, UiTab, UiArea, DrawBrush, BrushGradientStop, UiDrawPath,
DrawStrokeParams, UiDrawMatrix, UiAreaKeyEvent, UiAreaMouseEvent,
UiFontButton, FontDescriptor, FontAttribute, DrawTextLayout);
applySetterGetter(
UiImage, UiTableModel, UiTable, UiEntryBase, UiBox, SeparatorBase, UiControl, UiGrid,
UiMenuItem, UiMenu, UiSpinbox, UiHorizontalSeparator, UiVerticalSeparator,
UiRadioButtons, UiProgressBar, UiGroup, UiEntry, UiPasswordEntry, UiSearchEntry,
UiEditableCombobox, UiTimePicker, UiDatePicker, UiDateTimePicker, UiCombobox,
UiColorButton, UiCheckbox, UiWindow, UiButton, UiLabel, UiForm, UiSlider,
UiMultilineEntry, UiHorizontalBox, UiVerticalBox, UiTab, UiArea, DrawBrush,
BrushGradientStop, UiDrawPath, DrawStrokeParams, UiDrawMatrix, UiAreaKeyEvent,
UiAreaMouseEvent, UiFontButton, FontDescriptor, FontAttribute, DrawTextLayout);
applySetterGetterAll(true, Point, Color, Size);
applySetterGetterAll(false, AreaDrawParams, UiAreaMouseEvent, UiAreaKeyEvent);

Object.assign(libui, {
UiImage,
UiTableModel,
UiTable,
UiFontButton,
FontDescriptor,
FontAttribute,
Expand Down
40 changes: 40 additions & 0 deletions js/image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const {Image} = require('..');
const {readFile} = require('fs');
const parsePng = require('parse-png');

/**
* An image.
*/
class UiImage {
/**
* Create a new UiImage object.
* @return {UiImage}
*/
constructor(width, height) {
this._img = Image.create(width, height);
}

append(pixels, pixelWidth, pixelHeight, byteStride) {
Image.append(this._img, pixels, pixelWidth, pixelHeight, byteStride);
}

static loadFromPng(pngPath) {
return new Promise((resolve, reject) => {
readFile(pngPath, (err, data) => {
if (err) {
return reject(err);
}

parsePng(data)
.then(png => {
const img = new UiImage(png.width, png.height);
img.append(png.data, png.width, png.height, png.width * 4);
resolve(img);
})
.catch(reject);
});
});
}
}

module.exports = {UiImage};
199 changes: 199 additions & 0 deletions js/table-model-from-metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
const {UiImage} = require('./image');
const {UiTableModel} = require('./table-model');

const always = val => () => val;
const noop = () => undefined;

const ValueTypes = {
String: 0,
Image: 1,
Int: 2,
Color: 3
};

const Text = {
getter: ({key}) => (data, row) => data[row][key],
setter: ({key}) => (data, row, value) => (data[row][key] = value),
cellType: () => ValueTypes.String,
adder: column => tb =>
tb.appendTextColumn(column.header, column.idx, column.idx + 1, null)
};

const Image = {
getter: ({value}) => {
if (value instanceof UiImage) {
return always(column.value);
}

if (typeof value === 'function') {
return value;
}

return (data, row) => data[row][key];
},
setter: () => noop,
cellType: () => ValueTypes.Image,
adder: column => tb => tb.appendImageColumn(column.header, column.idx)
};

const Checkbox = {
getter: ({key}) => (data, row) => Number(data[row][key]),
setter: ({key}) => (data, row, value) => (data[row][key] = Boolean(value)),
cellType: () => ValueTypes.Int,
adder: column => tb =>
tb.appendCheckboxColumn(column.header, column.idx, column.idx + 1)
};

const LabeledCheckbox = {
getter: ({key}) => (data, row) => Number(data[row][key]),
setter: ({key}) => (data, row, value) => (data[row][key] = Boolean(value)),
cellType: () => ValueTypes.Int,
addFurtherColumns: ({column, columnTypes, cellGetters, cellSetters}) => {
const labelIdx = columnTypes.length;
console.log('LabeledCheckbox labelIdx', column.idx, labelIdx);
columnTypes[labelIdx] = ValueTypes.String;
cellGetters[labelIdx] = () => column.label;
cellSetters[labelIdx] = noop;

columnTypes[labelIdx + 1] = ValueTypes.Int;
cellGetters[labelIdx + 1] = () => 0;
cellSetters[labelIdx + 1] = noop;

column.labelIdx = labelIdx;
},
adder: column => tb => {
const textModelColumn = column.labelIdx;
const textEditableModelColumn = column.labelIdx + 1;

tb.appendCheckboxTextColumn(column.header, column.idx, column.idx + 1,
textModelColumn, textEditableModelColumn, null);
}
};

const LabeledImage = {
getter: ({value}) => {
if (value instanceof UiImage) {
return always(column.value);
}

if (typeof value === 'function') {
return value;
}

return (data, row) => data[row][key];
},
setter: ({key}) => () => 0,
cellType: () => ValueTypes.Image,
addFurtherColumns: ({column, columnTypes, cellGetters, cellSetters}) => {
const labelIdx = columnTypes.length;
console.log('LabeledImage labelIdx', column.idx, labelIdx);
columnTypes[labelIdx] = ValueTypes.String;
cellGetters[labelIdx] = () => {
console.log('column.label', column.label);
return column.label;
};
cellSetters[labelIdx] = noop;

columnTypes[labelIdx + 1] = ValueTypes.Int;
cellGetters[labelIdx + 1] = () => 0;
cellSetters[labelIdx + 1] = noop;

column.labelIdx = labelIdx;
},
adder: column => tb => {
console.log('adder', column.labelIdx)
const textModelColumn = column.labelIdx;
const textEditableModelColumn = column.labelIdx + 1;

tb.appendImageTextColumn(column.header, column.idx, textModelColumn,
textEditableModelColumn, null);
}
};

const ProgressBar = {
getter: ({key}) => (data, row) => Number(data[row][key]),
setter: ({key}) => (data, row, value) => (data[row][key] = Number(value)),
cellType: () => ValueTypes.Int,
adder: column => tb => tb.appendProgressBarColumn(column.header, column.idx)
};

const Button = {
getter: ({label}) => () => String(label),
setter: ({key, click}) => (data, row) => click(data[row]),
cellType: () => ValueTypes.String,
adder: column => tb =>
tb.appendButtonColumn(column.header, column.idx, column.idx + 1)
};

function fromMetadata(model) {
const numColumns = Object.keys(model).length;
const columnTypes = [];
const cellGetters = [];
const cellSetters = [];

for (const [key, column] of Object.entries(model)) {
const idx = columnTypes.length;
column.key = key;
column.idx = idx;

columnTypes[idx] = column.type.cellType(column);
cellGetters[idx] = column.type.getter(column);
cellSetters[idx] = column.type.setter(column);
column.adder = column.type.adder(column);

if (typeof column.editable === 'function') {
columnTypes[idx + 1] = ValueTypes.Int;
cellGetters[idx + 1] = (data, row) => column.editable(data[row]);
} else {
columnTypes[idx + 1] = ValueTypes.Int;
cellGetters[idx + 1] = always(Number(Boolean(column.editable)));
}
cellSetters[idx + 1] = noop;

if (typeof column.type.addFurtherColumns === 'function') {

column.type.addFurtherColumns(
{columnTypes, cellGetters, cellSetters, column});
}

if (column.header === undefined) {
column.header = key;
}
}

return {
fields: model,
addColumns(tb, ...columnNames) {
for (const columnName of columnNames) {
debugger;
this.fields[columnName].adder(tb);
}
},
bind(data) {
return new UiTableModel({
numColumns: () => numColumns,
columnType: column => columnTypes[column],
numRows: () => data.length,
cellValue: (row, column) => {
const value = cellGetters[column](data, row);
return value;
},
setCellValue: (row, column, value) =>
cellSetters[column](data, row, value)
});
}
};
}

fromMetadata.ValueTypes = ValueTypes;
fromMetadata.Fields = {
Text,
Image,
Checkbox,
ProgressBar,
LabeledCheckbox,
LabeledImage,
Button
};

module.exports = fromMetadata;
28 changes: 28 additions & 0 deletions js/table-model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const {TableModel} = require('..');

class UiTableModel {
/**
* Create a new UiTableModel object.
* @param {object} modelHanlder - handler for the new table model
* @return {UiTable}
*/
constructor(modelHanlder) {
this.model = TableModel.create(modelHanlder.numColumns, modelHanlder.numRows,
modelHanlder.columnType, modelHanlder.cellValue,
modelHanlder.setCellValue);
}

rowInserted(index) {
return TableModel.rowInserted(this.model, index);
}

rowChanged(index) {
return TableModel.rowChanged(this.model, index);
}

rowDeleted(index) {
return TableModel.rowDeleted(this.model, index);
}
}

module.exports = {UiTableModel};
Loading