diff --git a/.gitignore b/.gitignore --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .*.swp .*.swo +.arcconfig diff --git a/lib/qt.js b/lib/qt.js --- a/lib/qt.js +++ b/lib/qt.js @@ -1607,6 +1607,8 @@ }; +function parse($TEXT, options) { + /* -----[ Parser (constants) ]----- */ var UNARY_PREFIX = makePredicate([ @@ -1655,8 +1657,6 @@ /* -----[ Parser ]----- */ -function parse($TEXT, options) { - options = defaults(options, { strict : false, filename : null, @@ -4509,6 +4509,12 @@ }, "name": function(src) { return bindout(tree, src); + }, + "string": function(src) { + return src; + }, + "num": function(src) { + return src; } }; @@ -7030,7 +7036,34 @@ Key_Play: 250, Key_Sleep: 95, Key_Zoom: 251, - Key_Cancel: 3 + Key_Cancel: 3, + // CursorShape + ArrowCursor: 0, + UpArrowCursor: 1, + CrossCursor: 2, + WaitCursor: 3, + IBeamCursor: 4, + SizeVerCursor: 5, + SizeHorCursor: 6, + SizeBDiagCursor: 7, + SizeFDiagCursor: 8, + SizeAllCursor: 9, + BlankCursor: 10, + SplitVCursor: 11, + SplitHCursor: 12, + PointingHandCursor: 13, + ForbiddenCursor: 14, + WhatsThisCursor: 15, + BusyCursor: 16, + OpenHandCursor: 17, + ClosedHandCursor: 18, + DragCopyCursor: 19, + DragMoveCursor: 20, + DragLinkCursor: 21, + LastCursor: 21, //DragLinkCursor, + BitmapCursor: 24, + CustomCursor: 25 + } /** @@ -7304,21 +7337,32 @@ if (meta.object.$class in constructors) { item = new constructors[meta.object.$class](meta); } else if (cTree = engine.loadComponent(meta.object.$class)) { - if (cTree.$children.length !== 1) + if (cTree.$children.length !== 1) { console.error("A QML component must only contain one root element!"); - var item = (new QMLComponent({ object: cTree, context: meta.context })).createObject(meta.parent); - + } + + var component = new QMLComponent({ object: cTree, context: meta.context }); + component.finalizeImports(); + + var item = component.createObject(meta.parent); + // Recall QMLBaseObject with the meta of the instance in order to get property // definitions, etc. from the instance QMLBaseObject.call(item, meta); + if (typeof item.dom != 'undefined') item.dom.className += " " + meta.object.$class + (meta.object.id ? " " + meta.object.id : ""); + var dProp; // Handle default properties } else { console.log("No constructor found for " + meta.object.$class); return; } + if (!global.qmlEngine.doc) { + global.qmlEngine.doc = item; + } + // id if (meta.object.id) meta.context[meta.object.id] = item; @@ -7557,7 +7601,7 @@ if (source != null) { global.qmlEngine = new QMLEngine(); - qmlEngine.loadFile(source); + qmlEngine.loadFile(source, null); qmlEngine.start(); break ; } @@ -7625,6 +7669,12 @@ this.getAttributes = function() { return (attributes); } } +registerQmlType({ + module: 'QtQuick', + name: 'QtObject', + versions: /.*/, + constructor: QMLBaseObject +}); // TODO function QMLColor(val) { @@ -7711,36 +7761,40 @@ if (!qrc.includesFile(file)) { var src = getUrlContents(file); - console.log('loading file', file); - qrc[file] = qmlparse(src); + if (src) { + console.log('Loading file [', file,']'); + qrc[file] = qmlparse(src); + }else { + console.log('Can nor load file [', file,']'); + } } } - + // Load file, parse and construct (.qml or .qml.js) - this.loadFile = function(file) { + this.loadFile = function(file, parentComponent) { var tree; basePath = this.pathFromFilepath(file); this.basePath = basePath; this.ensureFileIsLoadedInQrc(file); tree = convertToEngine(qrc[file]); - this.loadQMLTree(tree); + return this.loadQMLTree(tree, parentComponent); } // parse and construct qml this.loadQML = function(src) { - this.loadQMLTree(parseQML(src)); + this.loadQMLTree(parseQML(src), null); } - this.loadQMLTree = function(tree) { + this.loadQMLTree = function(tree, parentComponent) { engine = this; if (options.debugTree) { options.debugTree(tree); } // Create and initialize objects - var component = new QMLComponent({ object: tree, parent: null }); - doc = component.createObject(null); + var component = new QMLComponent({ object: tree, parent: parentComponent }); + doc = component.createObject(parentComponent); component.finalizeImports(); this.$initializePropertyBindings(); @@ -7750,10 +7804,12 @@ for (var i in this.completedSignals) { this.completedSignals[i](); } + + return component; } this.rootContext = function() { - return doc.$context; + return global.qmlEngine.doc.$context; } this.focusedElement = (function() { @@ -7901,7 +7957,9 @@ // Initialize property bindings for (var i = 0; i < this.bindedProperties.length; i++) { var property = this.bindedProperties[i]; - property.binding.compile(); + if (property.binding!=null) { + property.binding.compile(); + } property.update(); } this.bindedProperties = []; @@ -8090,7 +8148,8 @@ } QMLPositioner.slotChildrenChanged = function() { - for (var i = 0; i < this.children.length; i++) { + var c = this.children.length; + for (var i = 0; i < c; i++) { var child = this.children[i]; if (!child.widthChanged.isConnected(this, this.layoutChildren)) child.widthChanged.connect(this, this.layoutChildren); @@ -8349,6 +8408,8 @@ this.width = width; this.$updatingGeometry = false; + + if (this.parent != undefined) updateChildrenRect(this.parent); } function updateVGeometry(newVal, oldVal, propName) { @@ -8448,38 +8509,43 @@ this.height = height; this.$updatingGeometry = false; + + if (this.parent != undefined) updateChildrenRect(this.parent); } - - -function QMLButton(meta) { - this.dom = document.createElement("button"); - QMLItem.call(this, meta); - var self = this; - - this.dom.style.pointerEvents = "auto"; - this.dom.innerHTML = ""; - - createSimpleProperty("string", this, "text"); - this.clicked = Signal(); - - this.Component.completed.connect(this, function() { - this.implicitWidth = this.dom.firstChild.offsetWidth + 20; - this.implicitHeight = this.dom.firstChild.offsetHeight + 5; - }); - this.textChanged.connect(this, function(newVal) { - this.dom.firstChild.innerHTML = newVal; - //TODO: Replace those statically sized borders - this.implicitWidth = this.dom.firstChild.offsetWidth + 20; - this.implicitHeight = this.dom.firstChild.offsetHeight + 5; - }); - - this.dom.onclick = function(e) { - self.clicked(); - } +function updateChildrenRect(component){ + + var children = component !== undefined ? component.children : undefined + if ( children == undefined || children.length == 0 ) + return; + + var maxWidth = 0; + var maxHeight = 0; + var minX = children.length>0 ? children[0].x : 0; + var minY = children.length>0 ? children[0].y : 0; + var h=0; + var w=0; + + for(var i in children){ + var child = children[i]; + + h = child.$isUsingImplicitHeight !== 0 ? child.implicitHeight : child.height; + w = child.$isUsingImplicitWidth !== 0 ? child.implicitWidth : child.width; + + maxWidth = Math.max(maxWidth, child.x + w); + maxHeight = Math.max(maxHeight, child.y + h); + minX = Math.min(minX, child.x); + minY = Math.min(minX, child.y); + } + + component.childrenRect.x = minX; + component.childrenRect.y = minY; + component.childrenRect.width = maxWidth; + component.childrenRect.height = maxHeight; + } -registerQmlType('Button', QMLButton); + QMLComponent.prototype.createObject = function(parent, properties) { var oldState = engine.operationState; @@ -8517,11 +8583,15 @@ if (typeof qmlEngine.basePath != 'undefined') src = qmlEngine.basePath + src; - if (typeof qrc[src] != 'undefined') + + if (typeof qrc[src] != 'undefined') { js = qrc[src]; - else + } + else { js = jsparse(getUrlContents(src)); - var $context = qmlEngine.rootContext(); + } + + var $context = this.$context; $context[importDesc.alias] = {}; importJavascriptInContext(js, $context[importDesc.alias]); } @@ -8683,6 +8753,7 @@ this.$isUsingImplicitWidth = true; this.$isUsingImplicitHeight = true; + // anchors property this.anchors = new QObject(this); createSimpleProperty("real", this.anchors, "left"); createSimpleProperty("real", this.anchors, "right"); @@ -8714,6 +8785,14 @@ this.anchors.marginsChanged.connect(this, updateHGeometry); this.anchors.marginsChanged.connect(this, updateVGeometry); + // childrenRect property + this.childrenRect = new QObject(this); + createSimpleProperty("real", this.childrenRect, "x","rw"); //todo ro + createSimpleProperty("real", this.childrenRect, "y","rw"); // todo ro + createSimpleProperty("real", this.childrenRect, "width","rw"); // todo ro + createSimpleProperty("real", this.childrenRect, "height","rw"); // todo ro + + createSimpleProperty("list", this, "states"); createSimpleProperty("string", this, "state"); createSimpleProperty("list", this, "transitions"); @@ -8913,7 +8992,11 @@ this.transform = []; this.rotation = 0; this.scale = 1; - + this.childrenRect.x = 0; + this.childrenRect.y = 0; + this.childrenRect.width = 0; + this.childrenRect.height = 0; + // Init size of root element if (this.$parent === null && engine.rootElement == undefined) { window.onresize(); @@ -9294,6 +9377,35 @@ } }); +function QMLButton(meta) { + this.dom = document.createElement("button"); + QMLItem.call(this, meta); + var self = this; + + this.dom.style.pointerEvents = "auto"; + this.dom.innerHTML = ""; + + createSimpleProperty("string", this, "text"); + this.clicked = Signal(); + + this.Component.completed.connect(this, function() { + this.implicitWidth = this.dom.firstChild.offsetWidth + 20; + this.implicitHeight = this.dom.firstChild.offsetHeight + 5; + }); + this.textChanged.connect(this, function(newVal) { + this.dom.firstChild.innerHTML = newVal; + //TODO: Replace those statically sized borders + this.implicitWidth = this.dom.firstChild.offsetWidth + 20; + this.implicitHeight = this.dom.firstChild.offsetHeight + 5; + }); + + this.dom.onclick = function(e) { + self.clicked(); + } +} + +registerQmlType('Button', QMLButton); + registerQmlType({ module: 'QtQuick.Controls', name: 'CheckBox', @@ -9331,7 +9443,85 @@ }; } }); +registerQmlType({ + module: 'QtQuick', + name: 'ComboBox', + versions: /.*/, + constructor: QMLComboBox +}); +function QMLComboBox(meta) { + QMLItem.call(this, meta); + var self = this; + + this.dom.style.pointerEvents = "auto"; + this.name = "QMLComboBox"; + + createSimpleProperty("int", this, "count"); + createSimpleProperty("int", this, "currentIndex"); + createSimpleProperty("string", this, "currentText"); + createSimpleProperty("array", this, "menu"); + createSimpleProperty("array", this, "model"); + createSimpleProperty("bool", this, "pressed"); + + this.count = 0; + this.currentIndex = 0; + this.currentText = ""; + this.menu = []; + this.model = []; + this.pressed = false; + + var updateCB = function(){ + var head = ""; + var html = head; + + var model = self.model; + var count = model.length; + self.count = count; + + for (var i = 0; i < count; i++) { + var elt = model[i]; + //if (elt instanceof Array) { // TODO - optgroups? update model ! + // var count_i = elt.length; + // for (var j = 0; j < count_i; j++) + // html += ""; + //} + //else + html += ""; + } + html += tail; + return html; + }; + + this.accepted = Signal(); + this.activated = Signal([{type: "int", name: "index"}]); + + this.find = function(text) { + return self.model.indexOf(text) + }; + this.selectAll = function () {}; // TODO + this.textAt = function(index) { + return this.model[index]; + }; + + this.Component.completed.connect(this, function () { + this.dom.innerHTML = updateCB(); + var child = this.dom.firstChild; + this.implicitWidth = child.offsetWidth; + this.implicitHeight = child.offsetHeight; + }); + + this.modelChanged.connect(updateCB); + + this.dom.onclick = function (e) { + var index = self.dom.firstChild.selectedIndex; + self.currentIndex = index ; + self.currentText = self.model[index]; + self.accepted(); + self.activated(index); + }; +} registerQmlType({ module: 'QtQuick.Controls', name: 'TextArea', @@ -9557,15 +9747,35 @@ QMLColumn.prototype.layoutChildren = function() { var curPos = 0, maxWidth = 0; - for (var i = 0; i < this.children.length; i++) { - var child = this.children[i]; - if (!(child.visible && child.opacity && child.width && child.height)) + var children = []; + var child = undefined; + var QMLRepeater = getConstructor('QtQuick', '2.0', 'Repeater'); + var QMLListView = getConstructor('QtQuick', '2.0', 'ListView'); + + for (var key in this.children) { + child = this.children[key]; + + if ( child instanceof QMLRepeater || child instanceof QMLListView) { + children = children.concat(child.children); + } + else { + children.push(child); + } + } + + if (children.length == 0) return; + + for (var i = 0; i < children.length; i++) { + child = children[i]; + if (!(child.visible && child.opacity && child.width && child.height)) { continue; + } + maxWidth = child.width > maxWidth ? child.width : maxWidth; - child.y = curPos; curPos += child.height + this.spacing; } + this.implicitWidth = maxWidth; this.implicitHeight = curPos - this.spacing; // We want no spacing at the bottom side } @@ -9651,48 +9861,70 @@ this.layoutDirectionChanged.connect(this, this.layoutChildren); this.widthChanged.connect(this, this.layoutChildren); - this.flow = 0; - this.layoutDirection = 0; + this.flow = this.Flow.LeftToRight; + this.layoutDirection = 0 ; } QMLFlow.prototype.layoutChildren = function() { var curHPos = 0, curVPos = 0, rowSize = 0; - for (var i = 0; i < this.children.length; i++) { - var child = this.children[i]; + var children = []; + var child = undefined; + var QMLRepeater = getConstructor('QtQuick', '2.0', 'Repeater'); + + for (var key in this.children){ + child = this.children[key]; + + if ( child instanceof QMLRepeater) { + children = children.concat(child.children); + } + else{ + children.push(child); + } + } + + if (children.length == 0) return; + + var flowWidth = this.$isUsingImplicitWidth ? this.implicitWidth : this.width; + var flowHeight = this.$isUsingImplicitHeight ? this.implicitHeight : this.height; + + for (var i = 0; i < children.length; i++) { + child = children[i]; if (!(child.visible && child.opacity && child.width && child.height)) continue; - if (this.flow == 0) { - if (curHPos + child.width > this.width) { - curHPos = 0; + if (this.flow == this.Flow.LeftToRight) { + if (curHPos + child.width > flowWidth) { + if (this.$isUsingImplicitWidth == false ) curHPos = 0; curVPos += rowSize + this.spacing; rowSize = 0; } rowSize = child.height > rowSize ? child.height : rowSize; child.x = this.layoutDirection == 1 - ? this.width - curHPos - child.width : curHPos; + ? flowWidth - curHPos - child.width : curHPos; child.y = curVPos; curHPos += child.width + this.spacing; } else { - if (curVPos + child.height > this.height) { - curVPos = 0; + if (curVPos + child.height > flowHeight) { + if (this.$isUsingImplicitHeight == false ) curVPos = 0; curHPos += rowSize + this.spacing; rowSize = 0; } rowSize = child.width > rowSize ? child.width : rowSize; child.x = this.layoutDirection == 1 - ? this.width - curHPos - child.width : curHPos; + ? flowWidth - curHPos - child.width : curHPos; child.y = curVPos; curVPos += child.height + this.spacing; } } - if (this.flow == 0) + + if (this.$isUsingImplicitHeight) this.implicitHeight = curVPos + rowSize; - else + + if (this.$isUsingImplicitWidth) this.implicitWidth = curHPos + rowSize; } @@ -10189,31 +10421,28 @@ }); -registerQmlType({ - module: 'QtQuick', - name: 'ListElement', - versions: /.*/, - constructor: function QMLListElement(meta) { +function QMLListElement(meta) { QMLBaseObject.call(this, meta); for (var i in meta.object) { if (i[0] != "$") { createSimpleProperty("variant", this, i); } } applyProperties(meta.object, this, this, this.$context); - } -}); +} registerQmlType({ module: 'QtQuick', - name: 'ListModel', + name: 'ListElement', versions: /.*/, - constructor: function QMLListModel(meta) { + constructor: QMLListElement +}); + +function QMLListModel(meta) { QMLBaseObject.call(this, meta); var self = this, firstItem = true; - var QMLListElement = getConstructor('QtQuick', '2.0', 'ListElement'); createSimpleProperty("int", this, "count"); createSimpleProperty("list", this, "$items"); @@ -10223,17 +10452,8 @@ this.count = 0; this.$itemsChanged.connect(this, function(newVal) { - if (firstItem) { - firstItem = false; - var roleNames = []; - var dict = newVal[0]; - for (var i in (dict instanceof QMLListElement) ? dict.$properties : dict) { - if (i != "index") - roleNames.push(i); - } - this.$model.setRoleNames(roleNames); - } this.count = this.$items.length; + updateRoleNames(newVal); }); this.$model.data = function(index, role) { @@ -10244,7 +10464,21 @@ } this.append = function(dict) { - this.insert(this.$items.length, dict); + var index = this.$items.length; + var c = 0; + + if (dict instanceof Array){ + for (var key in dict) { + this.$items.push(dict[key]); + c++; + } + }else { + this.$items.push(dict); + c=1; + } + + this.$itemsChanged(this.$items); + this.$model.rowsInserted(index, index + c); } this.clear = function() { this.$items = []; @@ -10257,7 +10491,7 @@ this.insert = function(index, dict) { this.$items.splice(index, 0, dict); this.$itemsChanged(this.$items); - this.$model.rowsInserted(index, index+1); + this.$model.rowsInserted(index, index + 1); } this.move = function(from, to, n) { var vals = this.$items.splice(from, n); @@ -10273,11 +10507,34 @@ } this.set = function(index, dict) { this.$items[index] = dict; + engine.$requestDraw(); } this.setProperty = function(index, property, value) { this.$items[index][property] = value; + engine.$requestDraw(); } - } + + function updateRoleNames(newVal){ + if (firstItem && newVal.length > 0 ) { + firstItem = false; + var roleNames = []; + var dict = newVal[0]; + + for (var i in (dict instanceof QMLListElement) ? dict.$properties : dict) { + if (i != "index") + roleNames.push(i); + } + + self.$model.setRoleNames(roleNames); + } + } +} + +registerQmlType({ + module: 'QtQuick', + name: 'ListModel', + versions: /.*/, + constructor: QMLListModel }); registerQmlType({ @@ -10322,6 +10579,144 @@ } }); +/** + * + * Loader is used to dynamically load QML components. + * + * Loader can load a QML file (using the source property) + * or a Component object (using the sourceComponent property). + * It is useful for delaying the creation of a component until + * it is required: for example, when a component should be created + * on demand, or when a component should not be created unnecessarily + * for performance reasons. + * + */ + +registerQmlType({ + module: 'QtQuick', + name: 'Loader', + versions: /.*/, + constructor: function(meta) { + QMLItem.call(this, meta); + + var self = this; + + createSimpleProperty('bool', this, 'active'); //totest + createSimpleProperty('bool', this, 'asynchronous'); //todo + createSimpleProperty('var', this, 'item'); + createSimpleProperty('real', this, 'progress'); //todo + createSimpleProperty('url', this, 'source'); + createSimpleProperty('Component', this, 'sourceComponent'); //totest + createSimpleProperty('enum', this, 'status'); + + + this.active = true; + this.asynchronous = false; + this.item = undefined; + this.progress = 0.0; + this.source = undefined; + this.sourceComponent = undefined; + this.status = 1; + this.sourceUrl = ''; + + this.loaded = Signal(); + + implicitWidth = this.item ? this.item.width : 0; + implicitHeight = this.item ? this.item.height : 0; + + this.activeChanged.connect( function(newVal) { + + if (self.active){ + if (self.source) + sourceChanged(); + else if (self.sourceComponent) + sourceComponentChanged(); + }else{ + unload(); + } + }); + + this.sourceChanged.connect( function(newVal) { + if (self.active == false )//|| (newVal == self.sourceUrl && self.item !== undefined) ) //todo + { + console.log( " Loader isn't active."); + return; + } + + unload(); + + if (self.source.length>0) { + var fileName = newVal.lastIndexOf(".qml") == newVal.length-4 ? newVal.substring(0, newVal.length-4) : ""; + + if ( fileName !== "" ) { + var tree = engine.loadComponent(fileName); + + var component = new QMLComponent({ object: tree, context: self , parent: self}); + var loadedComponent = component.createObject(self); + + loadedComponent.parent = self; + component.finalizeImports(); + + self.childrenChanged(); + + if (engine.operationState !== QMLOperationState.Init) { + // We don't call those on first creation, as they will be called + // by the regular creation-procedures at the right time. + engine.$initializePropertyBindings(); + callOnCompleted(loadedComponent); + } + + self.sourceComponent = loadedComponent; + self.sourceUrl = newVal; + } + } + + } ); + + this.sourceComponentChanged.connect(function(newVal) { + + if ( self.active == false ) { + return; + } + + self.item = newVal; + + if (self.item){ + if (!self.$isUsingImplicitWidth) { + self.item.width = self.width; + } + if (!self.$isUsingImplicitHeight) { + self.item.height = self.height; + } + + self.loaded(); + } + } ); + + function unload(){ + if (self.item){ + + self.item.$delete(); + self.item.parent = undefined; + self.item = undefined; + } + } + + function callOnCompleted(child) { + child.Component.completed(); + for (var i = 0; i < child.children.length; i++) + callOnCompleted(child.children[i]); + } + + this.setSource = function(url, options) { + this.sourceUrl = url; + this.props = options; + this.source = url; + } + + } +}); + registerQmlType({ module: 'QtQuick', name: 'MouseArea', @@ -10345,6 +10740,9 @@ createSimpleProperty("real", this, "mouseY"); createSimpleProperty("bool", this, "pressed"); createSimpleProperty("bool", this, "containsMouse"); + createSimpleProperty("enum", this, "cursorShape"); + + this.clicked = Signal([{type: "variant", name: "mouse"}]); this.entered = Signal(); this.exited = Signal(); @@ -10354,7 +10752,8 @@ this.enabled = true; this.hoverEnabled = false; this.containsMouse = false; - + this.cursorShape = Qt.ArrowCursor; + function eventToMouse(e) { return { accepted: true, @@ -10400,14 +10799,56 @@ self.containsMouse = false; self.exited(); } + this.dom.onmouseover = function(e) { + self.containsMouse = true; + self.dom.style.cursor = cursorShapeToCSS(); + self.entered(); + } + this.dom.onmouseout = function(e) { + self.containsMouse = false; + self.dom.style.cursor = "auto"; + self.exited(); + } this.dom.onmousemove = function(e) { if (self.enabled && (self.hoverEnabled || self.pressed)) { var mouse = eventToMouse(e); self.positionChanged(mouse); self.mouseX = mouse.x; self.mouseY = mouse.y; } } + + function cursorShapeToCSS(){ + var cursor = "auto"; + switch (self.cursorShape) { + case Qt.ArrowCursor: cursor = "default"; break; + case Qt.UpArrowCursor: cursor = "auto";break; + case Qt.CrossCursor: cursor = "crosshair";break; + case Qt.WaitCursor: cursor = "wait";break; + case Qt.IBeamCursor: cursor = "auto";break; + case Qt.SizeVerCursor: cursor = "auto";break; + case Qt.SizeHorCursor: cursor = "auto";break; + case Qt.SizeBDiagCursor: cursor = "auto";break; + case Qt.SizeFDiagCursor: cursor = "auto";break; + case Qt.SizeAllCursor: cursor = "auto";break; + case Qt.BlankCursor: cursor = "auto";break; + case Qt.SplitVCursor: cursor = "auto";break; + case Qt.SplitHCursor: cursor = "auto";break; + case Qt.PointingHandCursor: cursor = "pointer";break; + case Qt.ForbiddenCursor: cursor = "not-allowed";break; + case Qt.WhatsThisCursor: cursor = "auto";break; + case Qt.BusyCursor: cursor = "progress";break; + case Qt.OpenHandCursor: cursor = "auto";break; + case Qt.ClosedHandCursor: cursor = "move";break; + case Qt.DragCopyCursor: cursor = "auto";break; + case Qt.DragMoveCursor: cursor = "auto";break; + case Qt.DragLinkCursor: cursor = "auto";break; + case Qt.LastCursor: cursor = "auto";break; + case Qt.BitmapCursor: cursor = "auto";break; + case Qt.CustomCursor: cursor = "auto"; break; + } + return cursor; + } } }); @@ -10856,8 +11297,11 @@ constructor: function QMLRepeater(meta) { QMLItem.call(this, meta); var self = this; + var QMLListModel = getConstructor('QtQuick', '2.0', 'ListModel'); + this.parent = meta.parent; // TODO: some (all ?) of the components including Repeater needs to know own parent at creation time. Please consider this major change. + createSimpleProperty("Component", this, "delegate"); this.container = function() { return this.parent; } this.$defaultProperty = "delegate"; @@ -10869,8 +11313,8 @@ this.modelChanged.connect(applyModel); this.delegateChanged.connect(applyModel); + this.childrenChanged.connect - this.model = 0; this.count = 0; this.itemAt = function(index) { @@ -10883,20 +11327,31 @@ callOnCompleted(child.children[i]); } function insertChildren(startIndex, endIndex) { - for (var index = startIndex; index < endIndex; index++) { + var index = 0; + var model = self.model instanceof QMLListModel ? self.model.$model : self.model; + + for ( index = startIndex; index < endIndex; index++) { var newItem = self.delegate.createObject(self); + newItem.parent = self; // Repeater children + self.delegate.finalizeImports(); // To properly import JavaScript in the context of a component createSimpleProperty("int", newItem, "index"); - var model = self.model instanceof QMLListModel ? self.model.$model : self.model; - for (var i in model.roleNames) { - if (typeof newItem.$properties[model.roleNames[i]] == 'undefined') - createSimpleProperty("variant", newItem, model.roleNames[i]); - newItem.$properties[model.roleNames[i]].set(model.data(index, model.roleNames[i]), true, newItem, self.model.$context); + + if ( typeof model == "number" || model instanceof Array ) { + if (typeof newItem.$properties["modelData"] == 'undefined'){ + createSimpleProperty("variant", newItem, "modelData"); + } + + var value = model instanceof Array ? model[index] : typeof model == "number" ? index : "undefined"; + newItem.$properties["modelData"].set(value, true, newItem, model.$context); + } else { + for (var i in model.roleNames) { + if (typeof newItem.$properties[model.roleNames[i]] == 'undefined') + createSimpleProperty("variant", newItem, model.roleNames[i]); + newItem.$properties[model.roleNames[i]].set(model.data(index, model.roleNames[i]), true, newItem, self.model.$context); + } } - - self.container().children.splice(self.parent.children.indexOf(self) - self.$items.length + index, 0, newItem); - newItem.parent = self.container(); - self.container().childrenChanged(); + self.$items.splice(index, 0, newItem); newItem.index = index; @@ -10908,6 +11363,11 @@ callOnCompleted(newItem); } } + + if (index > 0) { + self.container().childrenChanged(); + } + for (var i = endIndex; i < self.$items.length; i++) self.$items[i].index = i; @@ -10917,7 +11377,9 @@ function applyModel() { if (!self.delegate) return; + var model = self.model instanceof QMLListModel ? self.model.$model : self.model; + if (model instanceof JSItemModel) { model.dataChanged.connect(function(startIndex, endIndex) { //TODO @@ -10950,9 +11412,14 @@ } else if (typeof model == "number") { removeChildren(0, self.$items.length); insertChildren(0, model); - } + } else if (model instanceof Array) { + removeChildren(0, self.$items.length); + insertChildren(0, model.length); + } + } + function removeChildren(startIndex, endIndex) { var removed = self.$items.splice(startIndex, endIndex - startIndex); for (var index in removed) { @@ -10968,7 +11435,6 @@ } } }); - registerQmlType({ module: 'QtQuick', name: 'Rotation', @@ -11024,25 +11490,48 @@ } QMLRow.prototype.layoutChildren = function() { - var curPos = 0, - maxHeight = 0, - // When layoutDirection is RightToLeft we need oposite order - i = this.layoutDirection == 1 ? this.children.length - 1 : 0, - endPoint = this.layoutDirection == 1 ? -1 : this.children.length, + var curPos = 0, maxHeight = 0; + var children = []; + var child = undefined; + var QMLRepeater = getConstructor('QtQuick', '2.0', 'Repeater'); + + for (var key in this.children){ + child = this.children[key]; + + if ( child instanceof QMLRepeater) { + children = children.concat(child.children); + } + else{ + children.push(child); + } + } + + if (children.length == 0) return; + + // When layoutDirection is RightToLeft we need oposite order + var i = this.layoutDirection == 1 ? children.length - 1 : 0, + endPoint = this.layoutDirection == 1 ? -1 : children.length, step = this.layoutDirection == 1 ? -1 : 1; + + var rowWidth = this.$isUsingImplicitWidth ? this.implicitWidth : this.width; + var rowHeight = this.$isUsingImplicitHeight ? this.implicitHeight : this.height; + for (; i !== endPoint; i += step) { - var child = this.children[i]; + child = children[i]; + if (!(child.visible && child.opacity && child.width && child.height)) continue; + maxHeight = child.height > maxHeight ? child.height : maxHeight; child.x = curPos; curPos += child.width + this.spacing; } + this.implicitHeight = maxHeight; this.implicitWidth = curPos - this.spacing; // We want no spacing at the right side + } - registerQmlType({ module: 'QtQuick', name: 'Scale', @@ -11498,22 +11987,26 @@ var self = this; - this.font = new getConstructor('QtQuick', '2.0', 'Font')(this); + var QMLFont = new getConstructor('QtQuick', '2.0', 'Font'); + this.font = new QMLFont(this); this.dom.innerHTML = "" this.dom.firstChild.style.pointerEvents = "auto"; // In some browsers text-inputs have a margin by default, which distorts // the positioning, so we need to manually set it to 0. this.dom.firstChild.style.margin = "0"; + this.dom.firstChild.style.padding = "0"; this.dom.firstChild.style.width = "100%"; + this.dom.firstChild.style.height = "100%"; this.setupFocusOnDom(this.dom.firstChild); createSimpleProperty("string", this, "text"); createSimpleProperty("int", this, "maximumLength"); createSimpleProperty("bool", this, "readOnly"); createSimpleProperty("var", this, "validator"); createSimpleProperty("enum", this, "echoMode"); + this.accepted = Signal(); this.readOnly = false; this.maximumLength = -1; @@ -11711,6 +12204,43 @@ }); registerQmlType({ + module: 'QtQuick', + name: 'Screen', + versions: /.*/, + constructor: QMLScreen +}); + +function QMLScreen(meta) { + QMLItem.call(this, meta); + var self = this; + + createSimpleProperty("int", this, "desktopAvailableHeight"); + createSimpleProperty("int", this, "desktopAvailableWidth"); + createSimpleProperty("real", this, "devicePixelRatio"); + createSimpleProperty("int", this, "height"); + createSimpleProperty("string", this, "name"); + createSimpleProperty("enum", this, "orientation"); + createSimpleProperty("enum", this, "orientationUpdateMask"); + createSimpleProperty("real", this, "pixelDensity"); + createSimpleProperty("enum", this, "primaryOrientation"); + createSimpleProperty("int", this, "width"); + + this.Component.completed.connect(this, updateSC); + + function updateSC() { + self.desktopAvailableHeight = window.outerHeight; + self.desktopAvailableWidth = window.outerWidth; + self.devicePixelRatio = window.devicePixelRatio; + self.height = window.innerHeight; + self.name = this.name; + self.orientation = Qt.PrimaryOrientation; + self.orientationUpdateMask = 0; + self.pixelDensity = 100.0; // TODO + self.primaryOrientation = Qt.PrimaryOrientation; + self.width = window.innerWidth; + } +} +registerQmlType({ module: 'QmlWeb', name: 'RestModel', versions: /.*/, diff --git a/readme_gulp b/readme_gulp new file mode 100644 --- /dev/null +++ b/readme_gulp @@ -0,0 +1,8 @@ + +To rebuild library run + +./node_modules/.bin/gulp qt + +or + +./node_modules/.bin/gulp qt.min diff --git a/src/qtcore/qml/AutoLoader.js b/src/qtcore/qml/AutoLoader.js --- a/src/qtcore/qml/AutoLoader.js +++ b/src/qtcore/qml/AutoLoader.js @@ -7,7 +7,7 @@ if (source != null) { global.qmlEngine = new QMLEngine(); - qmlEngine.loadFile(source); + qmlEngine.loadFile(source, null); qmlEngine.start(); break ; } diff --git a/src/qtcore/qml/QMLBaseObject.js b/src/qtcore/qml/QMLBaseObject.js --- a/src/qtcore/qml/QMLBaseObject.js +++ b/src/qtcore/qml/QMLBaseObject.js @@ -59,3 +59,9 @@ this.getAttributes = function() { return (attributes); } } +registerQmlType({ + module: 'QtQuick', + name: 'QtObject', + versions: /.*/, + constructor: QMLBaseObject +}); diff --git a/src/qtcore/qml/QMLEngine.js b/src/qtcore/qml/QMLEngine.js --- a/src/qtcore/qml/QMLEngine.js +++ b/src/qtcore/qml/QMLEngine.js @@ -44,7 +44,7 @@ var i; if (this.operationState !== QMLOperationState.Running) { this.operationState = QMLOperationState.Running; - tickerId = setInterval(tick, this.$interval); + tickerId = setInterval(tick, this.$interval); // TODO: considering performance: shouldn't it we start only when we have active animation for (i = 0; i < whenStart.length; i++) { whenStart[i](); } @@ -77,36 +77,40 @@ if (!qrc.includesFile(file)) { var src = getUrlContents(file); - console.log('loading file', file); - qrc[file] = qmlparse(src); + if (src) { + console.log('Loading file [', file,']'); + qrc[file] = qmlparse(src); + }else { + console.log('Can nor load file [', file,']'); + } } } - + // Load file, parse and construct (.qml or .qml.js) - this.loadFile = function(file) { + this.loadFile = function(file, parentComponent) { var tree; basePath = this.pathFromFilepath(file); this.basePath = basePath; this.ensureFileIsLoadedInQrc(file); tree = convertToEngine(qrc[file]); - this.loadQMLTree(tree); + return this.loadQMLTree(tree, parentComponent); } // parse and construct qml this.loadQML = function(src) { - this.loadQMLTree(parseQML(src)); + this.loadQMLTree(parseQML(src), null); } - this.loadQMLTree = function(tree) { + this.loadQMLTree = function(tree, parentComponent) { engine = this; if (options.debugTree) { options.debugTree(tree); } // Create and initialize objects - var component = new QMLComponent({ object: tree, parent: null }); - doc = component.createObject(null); + var component = new QMLComponent({ object: tree, parent: parentComponent }); + doc = component.createObject(parentComponent); component.finalizeImports(); this.$initializePropertyBindings(); @@ -116,10 +120,12 @@ for (var i in this.completedSignals) { this.completedSignals[i](); } + + return component; } this.rootContext = function() { - return doc.$context; + return global.qmlEngine.doc.$context; } this.focusedElement = (function() { @@ -264,13 +270,19 @@ } this.$initializePropertyBindings = function() { + var property; + // Initialize property bindings for (var i = 0; i < this.bindedProperties.length; i++) { - var property = this.bindedProperties[i]; - property.binding.compile(); + property = this.bindedProperties[i]; + if (!property) continue; + + if (property.binding != null) { + property.binding.compile(); + } property.update(); } - this.bindedProperties = []; + this.bindedProperties.length = 0; } this.$getTextMetrics = function(text, fontCss) diff --git a/src/qtcore/qml/QMLList.js b/src/qtcore/qml/QMLList.js --- a/src/qtcore/qml/QMLList.js +++ b/src/qtcore/qml/QMLList.js @@ -1,10 +1,12 @@ + function QMLList(meta) { var list = []; - if (meta.object instanceof Array) - for (var i in meta.object) + if (meta.object instanceof Array) { + for (var i=0;i 0) { - var item = this.$tidyupList[0]; - if (item.$delete) // It's a QObject + item = this.$tidyupList[0]; + if (item.$delete) {// It's a QObject item.$delete(); + item.parent = undefined; + } else // It must be a signal item.disconnect(this); } + var prop; for (var i in this.$properties) { - var prop = this.$properties[i]; + prop = this.$properties[i]; while (prop.$tidyupList.length > 0) prop.$tidyupList[0].disconnect(prop); } diff --git a/src/qtcore/qml/UpdateGeometry.js b/src/qtcore/qml/UpdateGeometry.js --- a/src/qtcore/qml/UpdateGeometry.js +++ b/src/qtcore/qml/UpdateGeometry.js @@ -95,6 +95,8 @@ this.width = width; this.$updatingGeometry = false; + + if (this.parent != undefined) updateChildrenRect(this.parent); } function updateVGeometry(newVal, oldVal, propName) { @@ -194,6 +196,39 @@ this.height = height; this.$updatingGeometry = false; + + if (this.parent != undefined) updateChildrenRect(this.parent); } +function updateChildrenRect(component){ + + var children = component !== undefined ? component.children : undefined + if ( children == undefined || children.length == 0 ) + return; + + var maxWidth = 0; + var maxHeight = 0; + var minX = children.length>0 ? children[0].x : 0; + var minY = children.length>0 ? children[0].y : 0; + var h=0; + var w=0; + var child; + + for (var i=0;i"; + //} + //else + html += ""; + } + html += tail; + return html; + }; + + this.accepted = Signal(); + this.activated = Signal([{type: "int", name: "index"}]); + + this.find = function(text) { + return self.model.indexOf(text) + }; + this.selectAll = function () {}; // TODO + this.textAt = function(index) { + return this.model[index]; + }; + + this.Component.completed.connect(this, function () { + this.dom.innerHTML = updateCB(); + var child = this.dom.firstChild; + this.implicitWidth = child.offsetWidth; + this.implicitHeight = child.offsetHeight; + }); + + this.modelChanged.connect(updateCB); + + this.dom.onclick = function (e) { + var index = self.dom.firstChild.selectedIndex; + self.currentIndex = index ; + self.currentText = self.model[index]; + self.accepted(); + self.activated(index); + }; +} \ No newline at end of file diff --git a/src/qtcore/qml/elements/QtQuick.Controls/ScrollView.js b/src/qtcore/qml/elements/QtQuick.Controls/ScrollView.js new file mode 100644 --- /dev/null +++ b/src/qtcore/qml/elements/QtQuick.Controls/ScrollView.js @@ -0,0 +1,108 @@ +registerQmlType({ + module: 'QtQuick.Controls', + name: 'ScrollView', + versions: /.*/, + constructor: function QMLScrollView(meta) { + QMLItem.call(this, meta); + + var self = this; + + + this.dom.style.pointerEvents = "auto"; + this.setupFocusOnDom(this.dom); + + createSimpleProperty("Item", this, "contentItem"); + this.$defaultProperty = "contentItem"; + createSimpleProperty("Item", this, "flickableItem"); //TODO 0) implement it 1) make it read-only + createSimpleProperty("Item", this, "viewport"); //TODO + createSimpleProperty("bool", this, "frameVisible"); + createSimpleProperty("bool", this, "highlightOnFocus"); //TODO test + createSimpleProperty("enum", this, "verticalScrollBarPolicy"); + createSimpleProperty("enum", this, "horizontalScrollBarPolicy"); + createSimpleProperty("Component", this, "style"); //TODO + + + this.contentItemChanged.connect(this, function(newItem){ + if (typeof newItem !== undefined) + { + newItem.parent = self; + } + }); + this.flickableItemChanged.connect(this, function(newItem){}); + + this.viewportChanged.connect(this, function(newViewport){}); + + this.frameVisibleChanged.connect(this, function(visible){ + this.dom.style.border= visible ? "1px solid gray" : "hidden"; + }); + this.highlightOnFocusChanged.connect(this, function(highlight){ + + }); + + this.horizontalScrollBarPolicyChanged.connect(this, function(newPolicy){ + var newVal = "auto"; + switch (newPolicy){ + case Qt.ScrollBarAsNeeded:{ + newVal = "auto"; + break; + } + case Qt.ScrollBarAlwaysOff:{ + newVal = "hidden"; + break; + } + case Qt.ScrollBarAlwaysOn:{ + newVal = "scroll"; + break; + } + } + + this.dom.style.overflowX = newVal; + }); + this.verticalScrollBarPolicyChanged.connect(this, function(newPolicy){ + var newVal = "auto"; + switch (newPolicy){ + case Qt.ScrollBarAsNeeded:{ + newVal = "auto"; + break; + } + case Qt.ScrollBarAlwaysOff:{ + newVal = "hidden"; + break; + } + case Qt.ScrollBarAlwaysOn:{ + newVal= "scroll"; + break; + } + } + + this.dom.style.overflowY = newVal; + }); + + this.styleChanged.connect(this, function(newStyle){}); + + //// + this.childrenChanged.connect(this, function(){ + if (typeof self.contentItem == undefined && self.children.length == 1){ + self.contentItem = self.children[0]; + } + }); + this.focusChanged.connect(this, function(focus){ + this.dom.style.outline = self.highlight && focus ? "outline: lightblue solid 2px;" : ""; + }); + + this.width = this.implicitWidth = 240; // default QML ScrollView width + this.height = this.implicitHeight = 150; // default QML ScrollView height + this.width = this.implicitWidth; + this.height = this.implicitHeight; + + this.contentItem = undefined; + this.flickableItem = undefined; + this.viewport = undefined; + this.frameVisible = false; + this.highlightOnFocus = false; + this.verticalScrollBarPolicy = Qt.ScrollBarAsNeeded; + this.horizontalScrollBarPolicy = Qt.ScrollBarAsNeeded; + this.style = undefined; + + } +}); diff --git a/src/qtcore/qml/elements/QtQuick.Window/Screen.js b/src/qtcore/qml/elements/QtQuick.Window/Screen.js new file mode 100644 --- /dev/null +++ b/src/qtcore/qml/elements/QtQuick.Window/Screen.js @@ -0,0 +1,37 @@ +registerQmlType({ + module: 'QtQuick', + name: 'Screen', + versions: /.*/, + constructor: QMLScreen +}); + +function QMLScreen(meta) { + QMLItem.call(this, meta); + var self = this; + + createSimpleProperty("int", this, "desktopAvailableHeight"); + createSimpleProperty("int", this, "desktopAvailableWidth"); + createSimpleProperty("real", this, "devicePixelRatio"); + createSimpleProperty("int", this, "height"); + createSimpleProperty("string", this, "name"); + createSimpleProperty("enum", this, "orientation"); + createSimpleProperty("enum", this, "orientationUpdateMask"); + createSimpleProperty("real", this, "pixelDensity"); + createSimpleProperty("enum", this, "primaryOrientation"); + createSimpleProperty("int", this, "width"); + + this.Component.completed.connect(this, updateSC); + + function updateSC() { + self.desktopAvailableHeight = window.outerHeight; + self.desktopAvailableWidth = window.outerWidth; + self.devicePixelRatio = window.devicePixelRatio; + self.height = window.innerHeight; + self.name = this.name; + self.orientation = Qt.PrimaryOrientation; + self.orientationUpdateMask = 0; + self.pixelDensity = 100.0; // TODO + self.primaryOrientation = Qt.PrimaryOrientation; + self.width = window.innerWidth; + } +} \ No newline at end of file diff --git a/src/qtcore/qml/elements/QtQuick/Column.js b/src/qtcore/qml/elements/QtQuick/Column.js --- a/src/qtcore/qml/elements/QtQuick/Column.js +++ b/src/qtcore/qml/elements/QtQuick/Column.js @@ -3,19 +3,29 @@ } QMLColumn.prototype.layoutChildren = function() { - var curPos = 0, - maxWidth = 0; - for (var i = 0; i < this.children.length; i++) { - var child = this.children[i]; - if (!(child.visible && child.opacity && child.width && child.height)) + var curPos = 0, maxWidth = 0; + var children = this.children; + var child = undefined; + var childWidth =0, childHeight = 0; + var i,l = children.length; + if ( l == 0) return; + + for (i = 0; i < l; i++) { + child = children[i]; + childHeight = child.$isUsingImplicitHeight ? child.implicitHeight : child.height; + childWidth = child.$isUsingImplicitWidth ? child.implicitWidth : child.width; + + if (!(child.visible && childWidth && childHeight)) { continue; - maxWidth = child.width > maxWidth ? child.width : maxWidth; - + } + + maxWidth = childWidth > maxWidth ? childWidth : maxWidth; child.y = curPos; - curPos += child.height + this.spacing; + curPos += childHeight + this.spacing; } - this.implicitWidth = maxWidth; - this.implicitHeight = curPos - this.spacing; // We want no spacing at the bottom side + + if (this.$isUsingImplicitWidth) this.implicitWidth = maxWidth; + if (this.$isUsingImplicitHeight) this.implicitHeight = curPos - this.spacing; // We want no spacing at the bottom side } registerQmlType({ diff --git a/src/qtcore/qml/elements/QtQuick/Flow.js b/src/qtcore/qml/elements/QtQuick/Flow.js --- a/src/qtcore/qml/elements/QtQuick/Flow.js +++ b/src/qtcore/qml/elements/QtQuick/Flow.js @@ -11,49 +11,75 @@ this.flowChanged.connect(this, this.layoutChildren); this.layoutDirectionChanged.connect(this, this.layoutChildren); this.widthChanged.connect(this, this.layoutChildren); - - this.flow = 0; - this.layoutDirection = 0; + this.implicitWidthChanged.connect(this, this.layoutChildren); + + this.flow = this.Flow.LeftToRight; + this.layoutDirection = 0 ; } QMLFlow.prototype.layoutChildren = function() { var curHPos = 0, curVPos = 0, rowSize = 0; - for (var i = 0; i < this.children.length; i++) { - var child = this.children[i]; - if (!(child.visible && child.opacity && child.width && child.height)) + var children = []; + var child = undefined; + var QMLRepeater = getConstructor('QtQuick', '2.0', 'Repeater'); + + for (var key in this.children){ + child = this.children[key]; + + if ( child instanceof QMLRepeater) { + children = children.concat(child.children); + } + else{ + children.push(child); + } + } + + if (children.length == 0) return; + + var flowWidth = this.$isUsingImplicitWidth ? this.implicitWidth : this.width; + var flowHeight = this.$isUsingImplicitHeight ? this.implicitHeight : this.height; + + for (var i=0;i < children.length;i++) { + child = children[i]; + childHeight = child.$isUsingImplicitHeight ? child.implicitHeight : child.height; + childWidth = child.$isUsingImplicitWidth ? child.implicitWidth : child.width; + + if (!(child.visible && childWidth && childHeight)) continue; - if (this.flow == 0) { - if (curHPos + child.width > this.width) { - curHPos = 0; + if (this.flow == this.Flow.LeftToRight) { + if (curHPos + childWidth > flowWidth) { + if (this.$isUsingImplicitWidth == false ) curHPos = 0; curVPos += rowSize + this.spacing; rowSize = 0; } - rowSize = child.height > rowSize ? child.height : rowSize; + rowSize = childHeight > rowSize ? childHeight : rowSize; child.x = this.layoutDirection == 1 - ? this.width - curHPos - child.width : curHPos; + ? flowWidth - curHPos - childWidth : curHPos; child.y = curVPos; - curHPos += child.width + this.spacing; + curHPos += childWidth + this.spacing; } else { - if (curVPos + child.height > this.height) { - curVPos = 0; + if (curVPos + childHeight > flowHeight) { + if (this.$isUsingImplicitHeight == false ) curVPos = 0; curHPos += rowSize + this.spacing; rowSize = 0; } - rowSize = child.width > rowSize ? child.width : rowSize; + rowSize = childWidth > rowSize ? childWidth : rowSize; child.x = this.layoutDirection == 1 - ? this.width - curHPos - child.width : curHPos; + ? flowWidth - curHPos - childWidth : curHPos; child.y = curVPos; - curVPos += child.height + this.spacing; + curVPos += childHeight + this.spacing; } } - if (this.flow == 0) + + if (this.$isUsingImplicitHeight) this.implicitHeight = curVPos + rowSize; - else + + if (this.$isUsingImplicitWidth) this.implicitWidth = curHPos + rowSize; } diff --git a/src/qtcore/qml/elements/QtQuick/Grid.js b/src/qtcore/qml/elements/QtQuick/Grid.js --- a/src/qtcore/qml/elements/QtQuick/Grid.js +++ b/src/qtcore/qml/elements/QtQuick/Grid.js @@ -35,11 +35,18 @@ gridHeight = -this.spacing, curHPos = 0, curVPos = 0; - + var child =0, childWidth=0,childHeight=0; + var item =0, itemWidth=0, itemHeight=0; + var i = 0, j = 0; + var l = this.children.length; + // How many items are actually visible? - for (var i = 0; i < this.children.length; i++) { - var child = this.children[i]; - if (child.visible && child.opacity && child.width && child.height) + for (i = 0; i < l; i++) { + child = this.children[i]; + childHeight = child.$isUsingImplicitHeight ? child.implicitHeight : child.height; + childWidth = child.$isUsingImplicitWidth ? child.implicitWidth : child.width; + + if (child.visible && childWidth && childHeight) visibleItems.push(this.children[i]); } @@ -57,27 +64,29 @@ // How big are the colums/rows? if (this.flow == 0) - for (var i = 0; i < r; i++) { - for (var j = 0; j < c; j++) { - var item = visibleItems[i*c+j]; - if (!item) - break; - if (!colWidth[j] || item.width > colWidth[j]) - colWidth[j] = item.width; - if (!rowHeight[i] || item.height > rowHeight[i]) - rowHeight[i] = item.height; + for (i = 0; i < r; i++) { + for (j = 0; j < c; j++) { + item = visibleItems[i*c+j]; + if (!item) break; + + itemHeight = item.$isUsingImplicitHeight ? item.implicitHeight : item.height; + itemWidth = item.$isUsingImplicitWidth ? item.implicitWidth : item.width; + + if (!colWidth[j] || itemWidth > colWidth[j]) colWidth[j] = itemWidth; + if (!rowHeight[i] || itemHeight > rowHeight[i]) rowHeight[i] = itemHeight; } } else - for (var i = 0; i < c; i++) { - for (var j = 0; j < r; j++) { - var item = visibleItems[i*r+j]; - if (!item) - break; - if (!rowHeight[j] || item.height > rowHeight[j]) - rowHeight[j] = item.height; - if (!colWidth[i] || item.width > colWidth[i]) - colWidth[i] = item.width; + for (i = 0; i < c; i++) { + for (j = 0; j < r; j++) { + item = visibleItems[i*r+j]; + if (!item) break; + + itemHeight = item.$isUsingImplicitHeight ? item.implicitHeight : item.height; + itemWidth = item.$isUsingImplicitWidth ? item.implicitWidth : item.width; + + if (!rowHeight[j] || itemHeight > rowHeight[j]) rowHeight[j] = itemHeight; + if (!colWidth[i] || itemWidth > colWidth[i]) colWidth[i] = itemWidth; } } @@ -91,10 +100,11 @@ var step = this.layoutDirection == 1 ? -1 : 1, startingPoint = this.layoutDirection == 1 ? c - 1 : 0, endPoint = this.layoutDirection == 1 ? -1 : c; - if (this.flow == 0) - for (var i = 0; i < r; i++) { - for (var j = startingPoint; j !== endPoint; j += step) { - var item = visibleItems[i*c+j]; + + if (this.flow == 0) { + for (i = 0; i < r; i++) { + for (j = startingPoint; j !== endPoint; j += step) { + item = visibleItems[i*c+j]; if (!item) break; item.x = curHPos; @@ -105,10 +115,11 @@ curVPos += rowHeight[i] + this.spacing; curHPos = 0; } - else - for (var i = startingPoint; i !== endPoint; i += step) { - for (var j = 0; j < r; j++) { - var item = visibleItems[i*r+j]; + } + else { + for (i = startingPoint; i !== endPoint; i += step) { + for (j = 0; j < r; j++) { + item = visibleItems[i*r+j]; if (!item) break; item.x = curHPos; @@ -119,7 +130,8 @@ curHPos += colWidth[i] + this.spacing; curVPos = 0; } - - this.implicitWidth = gridWidth; - this.implicitHeight = gridHeight; + } + + if (this.$isUsingImplicitWidth) this.implicitWidth = gridWidth; + if (this.$isUsingImplicitHeight) this.implicitHeight = gridHeight; } diff --git a/src/qtcore/qml/elements/QtQuick/ListElement.js b/src/qtcore/qml/elements/QtQuick/ListElement.js --- a/src/qtcore/qml/elements/QtQuick/ListElement.js +++ b/src/qtcore/qml/elements/QtQuick/ListElement.js @@ -1,15 +1,17 @@ -registerQmlType({ - module: 'QtQuick', - name: 'ListElement', - versions: /.*/, - constructor: function QMLListElement(meta) { +function QMLListElement(meta) { QMLBaseObject.call(this, meta); for (var i in meta.object) { if (i[0] != "$") { createSimpleProperty("variant", this, i); } } applyProperties(meta.object, this, this, this.$context); - } +} + +registerQmlType({ + module: 'QtQuick', + name: 'ListElement', + versions: /.*/, + constructor: QMLListElement }); diff --git a/src/qtcore/qml/elements/QtQuick/ListModel.js b/src/qtcore/qml/elements/QtQuick/ListModel.js --- a/src/qtcore/qml/elements/QtQuick/ListModel.js +++ b/src/qtcore/qml/elements/QtQuick/ListModel.js @@ -1,32 +1,20 @@ -registerQmlType({ - module: 'QtQuick', - name: 'ListModel', - versions: /.*/, - constructor: function QMLListModel(meta) { +function QMLListModel(meta) { QMLBaseObject.call(this, meta); var self = this, firstItem = true; - var QMLListElement = getConstructor('QtQuick', '2.0', 'ListElement'); + var QMLListElement = getConstructor('QtQuick', '2.0', 'ListElement'); + createSimpleProperty("int", this, "count"); createSimpleProperty("list", this, "$items"); this.$defaultProperty = "$items"; this.$items = []; this.$model = new JSItemModel(); this.count = 0; this.$itemsChanged.connect(this, function(newVal) { - if (firstItem) { - firstItem = false; - var roleNames = []; - var dict = newVal[0]; - for (var i in (dict instanceof QMLListElement) ? dict.$properties : dict) { - if (i != "index") - roleNames.push(i); - } - this.$model.setRoleNames(roleNames); - } this.count = this.$items.length; + updateRoleNames(newVal); }); this.$model.data = function(index, role) { @@ -37,20 +25,34 @@ } this.append = function(dict) { - this.insert(this.$items.length, dict); + var index = this.$items.length; + var c = 0; + + if (dict instanceof Array){ + for (var key in dict) { + this.$items.push(dict[key]); + c++; + } + }else { + this.$items.push(dict); + c=1; + } + + this.$itemsChanged(this.$items); + this.$model.rowsInserted(index, index + c); } this.clear = function() { - this.$items = []; this.$model.modelReset(); + this.$items.length = 0; this.count = 0; } this.get = function(index) { return this.$items[index]; } this.insert = function(index, dict) { this.$items.splice(index, 0, dict); this.$itemsChanged(this.$items); - this.$model.rowsInserted(index, index+1); + this.$model.rowsInserted(index, index + 1); } this.move = function(from, to, n) { var vals = this.$items.splice(from, n); @@ -66,9 +68,32 @@ } this.set = function(index, dict) { this.$items[index] = dict; + engine.$requestDraw(); } this.setProperty = function(index, property, value) { this.$items[index][property] = value; + engine.$requestDraw(); + } + + function updateRoleNames(newVal){ + if (firstItem && newVal.length > 0 ) { + firstItem = false; + var roleNames = []; + var dict = newVal[0]; + + for (var i in (dict instanceof QMLListElement) ? dict.$properties : dict) { + if (i != "index") + roleNames.push(i); + } + + self.$model.setRoleNames(roleNames); + } } - } +} + +registerQmlType({ + module: 'QtQuick', + name: 'ListModel', + versions: /.*/, + constructor: QMLListModel }); diff --git a/src/qtcore/qml/elements/QtQuick/Loader.js b/src/qtcore/qml/elements/QtQuick/Loader.js new file mode 100644 --- /dev/null +++ b/src/qtcore/qml/elements/QtQuick/Loader.js @@ -0,0 +1,169 @@ +/** + * + * Loader is used to dynamically load QML components. + * + * Loader can load a QML file (using the source property) + * or a Component object (using the sourceComponent property). + * It is useful for delaying the creation of a component until + * it is required: for example, when a component should be created + * on demand, or when a component should not be created unnecessarily + * for performance reasons. + * + */ + +registerQmlType({ + module: 'QtQuick', + name: 'Loader', + versions: /.*/, + constructor: function QMLLoader(meta) { + QMLItem.call(this, meta); + + var self = this; + + createSimpleProperty('bool', this, 'active'); //totest + createSimpleProperty('bool', this, 'asynchronous'); //todo + createSimpleProperty('var', this, 'item'); + createSimpleProperty('real', this, 'progress'); //todo + createSimpleProperty('url', this, 'source'); + createSimpleProperty('Component', this, 'sourceComponent'); //totest + createSimpleProperty('enum', this, 'status'); + + + this.active = true; + this.asynchronous = false; + this.item = undefined; + this.progress = 0.0; + this.source = undefined; + this.sourceComponent = undefined; + this.status = 1; + this.sourceUrl = ''; + + this.loaded = Signal(); + + this.activeChanged.connect( function(newVal) { + + if (self.active){ + if (self.source) + sourceChanged(); + else if (self.sourceComponent) + sourceComponentChanged(); + }else{ + unload(); + } + }); + + this.sourceChanged.connect( function(newVal) { + if (self.active == false )//|| (newVal == self.sourceUrl && self.item !== undefined) ) //todo + { + console.log( " Loader isn't active."); + return; + } + + unload(); + + if (self.source.length>0) { + var fileName = newVal.lastIndexOf(".qml") == newVal.length-4 ? newVal.substring(0, newVal.length-4) : ""; + + if ( fileName !== "" ) { + + var tree = engine.loadComponent(fileName); + var meta = { object: tree, context: self , parent: self}; + + var qmlComponent = new QMLComponent(meta); + var loadedComponent = createComponentObject(qmlComponent, self); + + self.sourceComponent = loadedComponent; + self.sourceUrl = newVal; + } + } + + } ); + + this.sourceComponentChanged.connect(function(newItem) { + + if ( self.active == false ) { + return; + } + + unload(); + + var qmlComponent = newItem; + + if (newItem instanceof QMLComponent) { + var meta = { object: newItem.$metaObject, context: self , parent: self}; + qmlComponent = construct(meta); + } + + qmlComponent.parent = self; + self.item = qmlComponent; + + updateGeometry(); + + if (self.item){ + // setTimeout(self.loaded(), 5000) + self.loaded(); + } + } ); + + + this.widthChanged.connect(function(newWidth) {updateGeometry();} ); + this.heightChanged.connect(function(newHeight) {updateGeometry();} ); + + function createComponentObject(qmlComponent, parent){ + var newComponent = qmlComponent.createObject(parent); + + newComponent.parent = parent; + qmlComponent.finalizeImports(); + + if (engine.operationState !== QMLOperationState.Init) { + + // We don't call those on first creation, as they will be called + // by the regular creation-procedures at the right time. + engine.$initializePropertyBindings(); + callOnCompleted(newComponent); + } + + return newComponent; + } + + function updateGeometry(){ + // Loader size doesn't exist + if (!self.width) { + self.width = self.item ? self.item.width : 0; + } + else{ + // Loader size exists + if (self.item) self.item.width = self.width; + } + + if (!self.height) { + self.height = self.item ? self.item.height : 0; + } else { + // Loader size exists + if (self.item) self.item.height = self.height; + } + } + + function unload(){ + if (self.item){ + self.item.$delete(); + self.item.parent = undefined; + self.item = undefined; + } + } + + function callOnCompleted(child) { + child.Component.completed(); + for (var i = 0; i < child.children.length; i++) + callOnCompleted(child.children[i]); + } + + this.setSource = function(url, options) { + this.sourceUrl = url; + this.props = options; + this.source = url; + } + + } +}); + diff --git a/src/qtcore/qml/elements/QtQuick/MouseArea.js b/src/qtcore/qml/elements/QtQuick/MouseArea.js --- a/src/qtcore/qml/elements/QtQuick/MouseArea.js +++ b/src/qtcore/qml/elements/QtQuick/MouseArea.js @@ -21,6 +21,9 @@ createSimpleProperty("real", this, "mouseY"); createSimpleProperty("bool", this, "pressed"); createSimpleProperty("bool", this, "containsMouse"); + createSimpleProperty("enum", this, "cursorShape"); + + this.clicked = Signal([{type: "variant", name: "mouse"}]); this.entered = Signal(); this.exited = Signal(); @@ -30,7 +33,8 @@ this.enabled = true; this.hoverEnabled = false; this.containsMouse = false; - + this.cursorShape = Qt.ArrowCursor; + function eventToMouse(e) { return { accepted: true, @@ -76,13 +80,55 @@ self.containsMouse = false; self.exited(); } + this.dom.onmouseover = function(e) { + self.containsMouse = true; + self.dom.style.cursor = cursorShapeToCSS(); + self.entered(); + } + this.dom.onmouseout = function(e) { + self.containsMouse = false; + self.dom.style.cursor = "auto"; + self.exited(); + } this.dom.onmousemove = function(e) { if (self.enabled && (self.hoverEnabled || self.pressed)) { var mouse = eventToMouse(e); self.positionChanged(mouse); self.mouseX = mouse.x; self.mouseY = mouse.y; } } + + function cursorShapeToCSS(){ + var cursor = "auto"; + switch (self.cursorShape) { + case Qt.ArrowCursor: cursor = "default"; break; + case Qt.UpArrowCursor: cursor = "auto";break; + case Qt.CrossCursor: cursor = "crosshair";break; + case Qt.WaitCursor: cursor = "wait";break; + case Qt.IBeamCursor: cursor = "auto";break; + case Qt.SizeVerCursor: cursor = "auto";break; + case Qt.SizeHorCursor: cursor = "auto";break; + case Qt.SizeBDiagCursor: cursor = "auto";break; + case Qt.SizeFDiagCursor: cursor = "auto";break; + case Qt.SizeAllCursor: cursor = "auto";break; + case Qt.BlankCursor: cursor = "auto";break; + case Qt.SplitVCursor: cursor = "auto";break; + case Qt.SplitHCursor: cursor = "auto";break; + case Qt.PointingHandCursor: cursor = "pointer";break; + case Qt.ForbiddenCursor: cursor = "not-allowed";break; + case Qt.WhatsThisCursor: cursor = "auto";break; + case Qt.BusyCursor: cursor = "progress";break; + case Qt.OpenHandCursor: cursor = "auto";break; + case Qt.ClosedHandCursor: cursor = "move";break; + case Qt.DragCopyCursor: cursor = "auto";break; + case Qt.DragMoveCursor: cursor = "auto";break; + case Qt.DragLinkCursor: cursor = "auto";break; + case Qt.LastCursor: cursor = "auto";break; + case Qt.BitmapCursor: cursor = "auto";break; + case Qt.CustomCursor: cursor = "auto"; break; + } + return cursor; + } } }); diff --git a/src/qtcore/qml/elements/QtQuick/Repeater.js b/src/qtcore/qml/elements/QtQuick/Repeater.js --- a/src/qtcore/qml/elements/QtQuick/Repeater.js +++ b/src/qtcore/qml/elements/QtQuick/Repeater.js @@ -5,8 +5,11 @@ constructor: function QMLRepeater(meta) { QMLItem.call(this, meta); var self = this; + var QMLListModel = getConstructor('QtQuick', '2.0', 'ListModel'); + this.parent = meta.parent; // TODO: some (all ?) of the components including Repeater needs to know own parent at creation time. Please consider this major change. + createSimpleProperty("Component", this, "delegate"); this.container = function() { return this.parent; } this.$defaultProperty = "delegate"; @@ -18,8 +21,8 @@ this.modelChanged.connect(applyModel); this.delegateChanged.connect(applyModel); - - this.model = 0; + this.parentChanged.connect(applyModel); + this.count = 0; this.itemAt = function(index) { @@ -32,87 +35,138 @@ callOnCompleted(child.children[i]); } function insertChildren(startIndex, endIndex) { - for (var index = startIndex; index < endIndex; index++) { - var newItem = self.delegate.createObject(self); + + if (endIndex <= 0) return; + + var index = 0; + var model = self.model instanceof QMLListModel ? self.model.$model : self.model; + var newItem; + var l=0; + var roleName; + var isEngineInit = engine.operationState == QMLOperationState.Init; + + for ( index = startIndex; index < endIndex; index++) { + newItem = self.delegate.createObject(); + newItem.parent = self.parent; + self.delegate.finalizeImports(); // To properly import JavaScript in the context of a component + createSimpleProperty("int", newItem, "index"); - var model = self.model instanceof QMLListModel ? self.model.$model : self.model; - for (var i in model.roleNames) { - if (typeof newItem.$properties[model.roleNames[i]] == 'undefined') - createSimpleProperty("variant", newItem, model.roleNames[i]); - newItem.$properties[model.roleNames[i]].set(model.data(index, model.roleNames[i]), true, newItem, self.model.$context); + newItem.index = index; + + if ( typeof model == "number" || model instanceof Array ) { + if (typeof newItem.$properties["modelData"] == 'undefined'){ + createSimpleProperty("variant", newItem, "modelData"); + } + + var value = model instanceof Array ? model[index] : typeof model == "number" ? index : "undefined"; + newItem.$properties["modelData"].set(value, true, newItem, model.$context); + } else { + for (var i=0;i 0) { + self.container().childrenChanged(); + } + + l = self.$items.length; + for (var i = endIndex; i < l; i++) self.$items[i].index = i; - self.count = self.$items.length; + self.count = l; } + function onModelDataChanged(startIndex, endIndex) { //TODO + } + function onRowsMoved(sourceStartIndex, sourceEndIndex, destinationIndex){ + var i, l; + var vals = self.$items.splice(sourceStartIndex, sourceEndIndex-sourceStartIndex); + + for (i = 0; i < vals.length; i++) { + self.$items.splice(destinationIndex + i, 0, vals[i]); + } + var smallestChangedIndex = sourceStartIndex < destinationIndex + ? sourceStartIndex : destinationIndex; + for (i = smallestChangedIndex; i < self.$items.length; i++) { + self.$items[i].index = i; + } + } + function onRowsRemoved(startIndex, endIndex){ + removeChildren(startIndex, endIndex); + + var l = self.$items.length; + for (var i = startIndex; i < l; i++) { + self.$items[i].index = i; + } + self.count = l; + } + function onModelReset(){ + var model = self.model instanceof QMLListModel ? self.model.$model : self.model; + removeChildren(0, self.$items.length); + } function applyModel() { - if (!self.delegate) + + if (!self.delegate || !self.parent) return; + + removeChildren(0, self.$items.length); + var model = self.model instanceof QMLListModel ? self.model.$model : self.model; + if (model instanceof JSItemModel) { - model.dataChanged.connect(function(startIndex, endIndex) { - //TODO - }); - model.rowsInserted.connect(insertChildren); - model.rowsMoved.connect(function(sourceStartIndex, sourceEndIndex, destinationIndex) { - var vals = self.$items.splice(sourceStartIndex, sourceEndIndex-sourceStartIndex); - for (var i = 0; i < vals.length; i++) { - self.$items.splice(destinationIndex + i, 0, vals[i]); - } - var smallestChangedIndex = sourceStartIndex < destinationIndex - ? sourceStartIndex : destinationIndex; - for (var i = smallestChangedIndex; i < self.$items.length; i++) { - self.$items[i].index = i; - } - }); - model.rowsRemoved.connect(function(startIndex, endIndex) { - removeChildren(startIndex, endIndex); - for (var i = startIndex; i < self.$items.length; i++) { - self.$items[i].index = i; - } - self.count = self.$items.length; - }); - model.modelReset.connect(function() { - removeChildren(0, self.$items.length); - insertChildren(0, model.rowCount()); - }); - + + if ( model.dataChanged.isConnected(onModelDataChanged) == false ) model.dataChanged.connect(onModelDataChanged); + if ( model.rowsInserted.isConnected(insertChildren) == false ) model.rowsInserted.connect(insertChildren); + if ( model.rowsMoved.isConnected(onRowsMoved) == false ) model.rowsMoved.connect(onRowsMoved); + if ( model.rowsRemoved.isConnected(onRowsRemoved) == false ) model.rowsRemoved.connect(onRowsRemoved); + if ( model.modelReset.isConnected(onModelReset) == false ) model.modelReset.connect(onModelReset); + insertChildren(0, model.rowCount()); } else if (typeof model == "number") { - removeChildren(0, self.$items.length); insertChildren(0, model); - } + } else if (model instanceof Array) { + insertChildren(0, model.length); + } + } - + function removeChildren(startIndex, endIndex) { var removed = self.$items.splice(startIndex, endIndex - startIndex); - for (var index in removed) { - removed[index].$delete(); - removed[index].parent = undefined; - removeChildProperties(removed[index]); + var l = removed.length; + var item; + + for (var i=0;i maxHeight ? child.height : maxHeight; + + maxHeight = childHeight > maxHeight ? childHeight : maxHeight; child.x = curPos; - curPos += child.width + this.spacing; + curPos += childWidth + this.spacing; } - this.implicitHeight = maxHeight; - this.implicitWidth = curPos - this.spacing; // We want no spacing at the right side -} + + if (this.$isUsingImplicitHeight) this.implicitHeight = maxHeight; + if (this.$isUsingImplicitWidth) this.implicitWidth = curPos - this.spacing; // We want no spacing at the right side +} \ No newline at end of file diff --git a/src/qtcore/qml/elements/QtQuick/Text.js b/src/qtcore/qml/elements/QtQuick/Text.js --- a/src/qtcore/qml/elements/QtQuick/Text.js +++ b/src/qtcore/qml/elements/QtQuick/Text.js @@ -8,7 +8,7 @@ // We create another span inside the text to distinguish the actual // (possibly html-formatted) text from child elements this.dom.innerHTML = ""; - this.dom.style.pointerEvents = "auto"; + this.dom.firstChild.style.width = "100%"; this.dom.firstChild.style.height = "100%"; @@ -76,6 +76,7 @@ break; case 1: this.dom.firstChild.style.whiteSpace = "pre-wrap"; + this.dom.firstChild.style.wordWrap = "normal"; break; case 2: this.dom.firstChild.style.whiteSpace = "pre-wrap"; @@ -140,44 +141,25 @@ this.wrapMode = this.Text.NoWrap; this.color = "black"; this.text = ""; - - this.textChanged.connect(this, updateImplicitHeight); - this.textChanged.connect(this, updateImplicitWidth); - this.font.boldChanged.connect(this, updateImplicitHeight); - this.font.boldChanged.connect(this, updateImplicitWidth); - this.font.pixelSizeChanged.connect(this, updateImplicitHeight); - this.font.pixelSizeChanged.connect(this, updateImplicitWidth); - this.font.pointSizeChanged.connect(this, updateImplicitHeight); - this.font.pointSizeChanged.connect(this, updateImplicitWidth); - this.font.familyChanged.connect(this, updateImplicitHeight); - this.font.familyChanged.connect(this, updateImplicitWidth); - this.font.letterSpacingChanged.connect(this, updateImplicitHeight); - this.font.wordSpacingChanged.connect(this, updateImplicitWidth); - - this.Component.completed.connect(this, updateImplicitHeight); - this.Component.completed.connect(this, updateImplicitWidth); - - function updateImplicitHeight() { - var height; - - if (this.text === Undefined || this.text === "") { - height = 0; + + this.textChanged.connect(this, updateImplicit); + this.font.boldChanged.connect(this, updateImplicit); + this.font.pixelSizeChanged.connect(this, updateImplicit); + this.font.pointSizeChanged.connect(this, updateImplicit); + this.font.familyChanged.connect(this, updateImplicit); + this.font.letterSpacingChanged.connect(this, updateImplicit); + this.font.wordSpacingChanged.connect(this, updateImplicit); + + this.Component.completed.connect(this, updateImplicit); + + function updateImplicit() { + if (typeof this.text == undefined || this.text === "" || !this.dom) { + this.implicitHeigh = this.implicitWidth = 0; } else { - height = this.dom ? this.dom.firstChild.offsetHeight : 0; + var fc = this.dom.firstChild; + this.implicitHeight = fc.offsetHeight; + this.implicitWidth = fc.offsetWidth; } - - this.implicitHeight = height; - } - - function updateImplicitWidth() { - var width; - - if (this.text === Undefined || this.text === "") - width = 0; - else - width = this.dom ? this.dom.firstChild.offsetWidth : 0; - - this.implicitWidth = width; } this.$drawItem = function(c) { diff --git a/src/qtcore/qml/elements/QtQuick/TextInput.js b/src/qtcore/qml/elements/QtQuick/TextInput.js --- a/src/qtcore/qml/elements/QtQuick/TextInput.js +++ b/src/qtcore/qml/elements/QtQuick/TextInput.js @@ -11,22 +11,26 @@ var self = this; - this.font = new getConstructor('QtQuick', '2.0', 'Font')(this); + var QMLFont = new getConstructor('QtQuick', '2.0', 'Font'); + this.font = new QMLFont(this); this.dom.innerHTML = "" this.dom.firstChild.style.pointerEvents = "auto"; // In some browsers text-inputs have a margin by default, which distorts // the positioning, so we need to manually set it to 0. this.dom.firstChild.style.margin = "0"; + this.dom.firstChild.style.padding = "0"; this.dom.firstChild.style.width = "100%"; + this.dom.firstChild.style.height = "100%"; this.setupFocusOnDom(this.dom.firstChild); createSimpleProperty("string", this, "text"); createSimpleProperty("int", this, "maximumLength"); createSimpleProperty("bool", this, "readOnly"); createSimpleProperty("var", this, "validator"); createSimpleProperty("enum", this, "echoMode"); + this.accepted = Signal(); this.readOnly = false; this.maximumLength = -1; diff --git a/src/qtcore/qml/lib/import.js b/src/qtcore/qml/lib/import.js --- a/src/qtcore/qml/lib/import.js +++ b/src/qtcore/qml/lib/import.js @@ -52,7 +52,7 @@ var contents = getUrlContents(file + ".js"); if (contents) { console.log("Using pre-processed content for " + file); - return eval("(function(){return "+contents+"})();"); + return new Function("(function(){return ",contents,"})();"); } else { contents = getUrlContents(file); if (contents) { @@ -181,7 +181,7 @@ src += "}})()"; // evaluate source to get the object. - return eval(src); + return new Function(src); } /** diff --git a/src/qtcore/qml/lib/parser.js b/src/qtcore/qml/lib/parser.js --- a/src/qtcore/qml/lib/parser.js +++ b/src/qtcore/qml/lib/parser.js @@ -1680,6 +1680,12 @@ }, "name": function(src) { return bindout(tree, src); + }, + "string": function(src) { + return src; + }, + "num": function(src) { + return src; } }; diff --git a/src/qtcore/qml/qml.js b/src/qtcore/qml/qml.js --- a/src/qtcore/qml/qml.js +++ b/src/qtcore/qml/qml.js @@ -168,34 +168,57 @@ * @return {Object} New qml object */ function construct(meta) { - var item, - cTree; - + var item = 0, + cTree = 0; + if (meta.object.$class in constructors) { item = new constructors[meta.object.$class](meta); + } else if (cTree = engine.loadComponent(meta.object.$class)) { - if (cTree.$children.length !== 1) + if (cTree.$children.length !== 1) { console.error("A QML component must only contain one root element!"); - var item = (new QMLComponent({ object: cTree, context: meta.context })).createObject(meta.parent); + } + var component = new QMLComponent( {object: cTree, context: meta.context }); + //component.finalizeImports(); + + item = component.createObject(meta.parent); + component.finalizeImports(); + // Recall QMLBaseObject with the meta of the instance in order to get property // definitions, etc. from the instance QMLBaseObject.call(item, meta); + + if (typeof item.dom != 'undefined') item.dom.className += " " + meta.object.$class + (meta.object.id ? " " + meta.object.id : ""); + + +// var metaObject = component.$metaObject; +// // id +// if (metaObject && metaObject.id) { +// meta.context[metaObject.id] = item; +// } + var dProp; // Handle default properties } else { console.log("No constructor found for " + meta.object.$class); return; } - // id - if (meta.object.id) - meta.context[meta.object.id] = item; - - // Apply properties (Bindings won't get evaluated, yet) - applyProperties(meta.object, item, item, meta.context); - + if (!global.qmlEngine.doc) { + global.qmlEngine.doc = item; + } + + + // id + if (meta.object.id){ + meta.context[meta.object.id] = item; + } + + // Apply properties (Bindings won't get evaluated, yet) + applyProperties( meta.object, item, item, meta.context); + return item; } @@ -215,12 +238,12 @@ obj.$properties[propName] = prop; getter = function() { return obj.$properties[propName].get(); }; if (access == 'rw') - setter = function(newVal) { return obj.$properties[propName].set(newVal); }; + setter = function(newVal) { obj.$properties[propName].set(newVal); }; else { setter = function(newVal) { if (obj.$canEditReadOnlyProperties != true) throw "property '" + propName + "' has read only access"; - return obj.$properties[propName].set(newVal); + obj.$properties[propName].set(newVal); } } setupGetterSetter(obj, propName, getter, setter); @@ -287,17 +310,30 @@ * @param {Object} componentScope Component scope in which properties should be evaluated */ function applyProperties(metaObject, item, objectScope, componentScope) { - var i; + var i, value, signalName; + objectScope = objectScope || item; + + if (metaObject.$children && metaObject.$children.length !== 0) { + if (item.$defaultProperty) + item.$properties[item.$defaultProperty].set(metaObject.$children, true, objectScope, componentScope); + else + throw "Cannot assign to unexistant default property"; + } + // We purposefully set the default property AFTER using it, in order to only have it applied for + // instanciations of this component, but not for its internal children + if (metaObject.$defaultProperty) + item.$defaultProperty = metaObject.$defaultProperty; + for (i in metaObject) { - var value = metaObject[i]; + value = metaObject[i]; // skip global id's and internal values if (i == "id" || i[0] == "$") { continue; } // slots if (i.indexOf("on") == 0 && i[2].toUpperCase() == i[2]) { - var signalName = i[2].toLowerCase() + i.slice(3); + signalName = i[2].toLowerCase() + i.slice(3); if (!item[signalName]) { console.warn("No signal called " + signalName + " found!"); continue; @@ -356,25 +392,18 @@ continue; } } - if (item.$properties && i in item.$properties) + + if (item.$properties && i in item.$properties){ item.$properties[i].set(value, true, objectScope, componentScope); + } else if (i in item) item[i] = value; else if (item.$setCustomData) item.$setCustomData(i, value); else console.warn("Cannot assign to non-existent property \"" + i + "\". Ignoring assignment."); } - if (metaObject.$children && metaObject.$children.length !== 0) { - if (item.$defaultProperty) - item.$properties[item.$defaultProperty].set(metaObject.$children, true, objectScope, componentScope); - else - throw "Cannot assign to unexistant default property"; - } - // We purposefully set the default property AFTER using it, in order to only have it applied for - // instanciations of this component, but not for its internal children - if (metaObject.$defaultProperty) - item.$defaultProperty = metaObject.$defaultProperty; + if (typeof item.completed != 'undefined' && item.completedAlreadyCalled == false) { item.completedAlreadyCalled = true; item.completed(); diff --git a/src/qtcore/qt.js b/src/qtcore/qt.js --- a/src/qtcore/qt.js +++ b/src/qtcore/qt.js @@ -181,5 +181,35 @@ Key_Play: 250, Key_Sleep: 95, Key_Zoom: 251, - Key_Cancel: 3 + Key_Cancel: 3, + // CursorShape + ArrowCursor: 0, + UpArrowCursor: 1, + CrossCursor: 2, + WaitCursor: 3, + IBeamCursor: 4, + SizeVerCursor: 5, + SizeHorCursor: 6, + SizeBDiagCursor: 7, + SizeFDiagCursor: 8, + SizeAllCursor: 9, + BlankCursor: 10, + SplitVCursor: 11, + SplitHCursor: 12, + PointingHandCursor: 13, + ForbiddenCursor: 14, + WhatsThisCursor: 15, + BusyCursor: 16, + OpenHandCursor: 17, + ClosedHandCursor: 18, + DragCopyCursor: 19, + DragMoveCursor: 20, + DragLinkCursor: 21, + LastCursor: 21, //DragLinkCursor, + BitmapCursor: 24, + CustomCursor: 25, + // ScrollBar Policy + ScrollBarAsNeeded: 0, + ScrollBarAlwaysOff: 1, + ScrollBarAlwaysOn: 2 } diff --git a/src/qtcore/signal.js b/src/qtcore/signal.js --- a/src/qtcore/signal.js +++ b/src/qtcore/signal.js @@ -13,9 +13,13 @@ var obj = options.obj var signal = function() { - for (var i in connectedSlots) - connectedSlots[i].slot.apply(connectedSlots[i].thisObj, arguments); + for (var i=0;i