diff --git a/app/app.xhtml b/app/app.xhtml index 407d2873..be2288a9 100644 --- a/app/app.xhtml +++ b/app/app.xhtml @@ -146,11 +146,11 @@ - + @@ -225,7 +225,6 @@ - diff --git a/app/package.json b/app/package.json index a0555afb..5bda0a7e 100644 --- a/app/package.json +++ b/app/package.json @@ -2,7 +2,7 @@ "name": "Pencil", "productName": "Pencil", "description": "An open-source GUI prototyping tool that is available for ALL platforms.", - "version": "3.1.1", + "version": "3.1.2", "author": { "name": "Evolus", "url": "http://evolus.vn", diff --git a/app/pencil-core/behavior/commonFunctions.js b/app/pencil-core/behavior/commonFunctions.js index ff563297..f8a3a76f 100644 --- a/app/pencil-core/behavior/commonFunctions.js +++ b/app/pencil-core/behavior/commonFunctions.js @@ -84,8 +84,9 @@ F.newDOMElement = function (spec) { if (name.match(/^_/)) continue; e.setAttribute(name, spec[name]); } - - if (spec._text) { + if (spec._html) { + e.innerHTML = spec._html; + } else if (spec._text) { e.appendChild(e.ownerDocument.createTextNode(spec._text)); } if (spec._children && spec._children.length > 0) { diff --git a/app/pencil-core/canvasHelper/GestureHelper.js b/app/pencil-core/canvasHelper/GestureHelper.js index 8768593f..fdf06b3f 100644 --- a/app/pencil-core/canvasHelper/GestureHelper.js +++ b/app/pencil-core/canvasHelper/GestureHelper.js @@ -67,7 +67,7 @@ GestureHelper.prototype.updateKeyCodes = function () { if (this.activeGestureDef) break; } - GestureHelper._output.innerHTML = this.heldKeyCodes.join(", ") + ":" + this.heldKeyCodes.map(c => {return String.fromCharCode(c)}).join(", "); + //GestureHelper._output.innerHTML = this.heldKeyCodes.join(", ") + ":" + this.heldKeyCodes.map(c => {return String.fromCharCode(c)}).join(", "); }; GestureHelper.fromCanvas = function (canvas) { diff --git a/app/pencil-core/common/Canvas.js b/app/pencil-core/common/Canvas.js index 8d700345..eed1d28e 100644 --- a/app/pencil-core/common/Canvas.js +++ b/app/pencil-core/common/Canvas.js @@ -1945,6 +1945,24 @@ Canvas.prototype.doCopy = function () { var transferableData = this.currentController.createTransferableData(); + var target = null; + + if (transferableData.dataNode.getAttributeNS(PencilNamespaces.p, "type")) { + target = this.createControllerFor(transferableData.dataNode); + } else { + var targets = []; + for (var i = 0; i < transferableData.dataNode.childNodes.length; i ++) { + var node = transferableData.dataNode.childNodes[i]; + var subTarget = this.createControllerFor(node); + + targets.push(subTarget); + } + target = new TargetSet(this, targets); + } + + if (target.processExportingTransferableProperties) target.processExportingTransferableProperties(); + + transferableData.dataNode.removeAttribute("p:parentRef"); var metaNode = Dom.getSingle(".//p:metadata", transferableData.dataNode); var childTargetsNode = Dom.getSingle("./p:childTargets", metaNode); diff --git a/app/pencil-core/common/controller.js b/app/pencil-core/common/controller.js index 1c96ecc5..9641b157 100644 --- a/app/pencil-core/common/controller.js +++ b/app/pencil-core/common/controller.js @@ -1425,7 +1425,7 @@ Controller.prototype.invalidateBitmapFilePath = function (page, invalidatedIds) for (var key in page.bitmapCache) { var filePath = page.bitmapCache[key]; try { - fs.unlinkSync(page.bitmapFilePath); + fs.unlinkSync(filePath); } catch (e) { } } diff --git a/app/pencil-core/common/svgRasterizer.js b/app/pencil-core/common/svgRasterizer.js index bbeda177..24648b44 100644 --- a/app/pencil-core/common/svgRasterizer.js +++ b/app/pencil-core/common/svgRasterizer.js @@ -359,6 +359,8 @@ Rasterizer.getExportScale = function (inputScale) { } Rasterizer.prototype.rasterizeSelectionToFile = function (target, filePath, callback, scale, options) { var geo = target.getGeometry(); + var rect = target.svg.getBoundingClientRect(); + if (!geo) { //Util.showStatusBarWarning(Util.getMessage("the.selected.objects.cannot.be.exported"), true); alert(Util.getMessage("the.selected.objects.cannot.be.exported")); @@ -373,8 +375,8 @@ Rasterizer.prototype.rasterizeSelectionToFile = function (target, filePath, call padding += strokeStyle.w; } - var w = geo.dim.w + padding; - var h = geo.dim.h + padding; + var w = rect.width + padding; + var h = rect.height + padding; debug("w: " + w); @@ -382,18 +384,15 @@ Rasterizer.prototype.rasterizeSelectionToFile = function (target, filePath, call svg.setAttribute("width", "" + w + "px"); svg.setAttribute("height", "" + h + "px"); + var wrapper = document.createElementNS(PencilNamespaces.svg, "g"); + var prect = target.svg.ownerSVGElement.getBoundingClientRect(); + wrapper.setAttribute("transform", "translate(" + (prect.x - rect.x) + ", " + (prect.y - rect.y) + ")"); + var content = target.svg.cloneNode(true); - content.removeAttribute("transform"); content.removeAttribute("id"); - try { - var dx = Math.round((w - geo.dim.w) / 2); - var dy = Math.round((h - geo.dim.h) / 2); - content.setAttribute("transform", "translate(" + dx + ", " + dy + ")"); - } catch (e) { - Console.dumpError(e); - } - svg.appendChild(content); + svg.appendChild(wrapper); + wrapper.appendChild(content); var thiz = this; var s = Rasterizer.getExportScale(scale); @@ -411,7 +410,6 @@ Rasterizer.prototype.rasterizeSelectionToFile = function (target, filePath, call }); }, false, options); }; - Rasterizer.prototype._prepareWindowForRasterization = function(backgroundColor) { var h = 0; var w = 0; diff --git a/app/pencil-core/common/util.js b/app/pencil-core/common/util.js index 1d7ede19..6a0f5abf 100644 --- a/app/pencil-core/common/util.js +++ b/app/pencil-core/common/util.js @@ -469,9 +469,9 @@ Object.defineProperty(Event.prototype, "originalTarget", { var domParser = new DOMParser(); -/* public static XmlDocument */ Dom.loadSystemXml = function (relPath) { +/* public static XmlDocument */ Dom.loadSystemXml = function (relPath, preProcessFileContent) { var absPath = getStaticFilePath(relPath); - return Dom.parseFile(absPath); + return Dom.parseFile(absPath, preProcessFileContent); }; Dom.isElementExistedInDocument = function(element) { @@ -917,8 +917,9 @@ Dom.swapNode = function (node1, node2) { parentNode.removeChild(node1); parentNode.insertBefore(node1, ref); }; -Dom.parseFile = function (file) { +Dom.parseFile = function (file, preProcessFileContent) { var fileContents = fs.readFileSync(file, "utf8"); + if (preProcessFileContent) fileContents = preProcessFileContent(fileContents); var dom = Dom.parser.parseFromString(fileContents, "text/xml"); return dom; }; diff --git a/app/pencil-core/editor/handleEditor.config.xml b/app/pencil-core/editor/handleEditor.config.xml index fd9525da..b6802dad 100644 --- a/app/pencil-core/editor/handleEditor.config.xml +++ b/app/pencil-core/editor/handleEditor.config.xml @@ -12,8 +12,8 @@ @namespace xul url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul); g.HandleEditor rect[p|name='Handle'] { - fill: #ff0; - stroke: #0a0; + fill: $handle_fill_color; + stroke: $handle_stroke_color; stroke-width: 3; stroke-opacity: 0; fill-opacity: .7; @@ -21,7 +21,7 @@ g.HandleEditor rect[p|name='Handle'][p|focused='true'] { stroke-width: 2; stroke-opacity: 1; - stroke: #f00; + stroke: $handle_focused_stroke_color; } g.HandleEditor rect[p|name='Handle']:hover { stroke-width: 6 !important; @@ -30,7 +30,7 @@ } g.HandleEditor rect[p|name='Handle'][p|connected='true'] { stroke-width: 15 !important; - stroke: #f00 !important; + stroke: $handle_connected_stroke_color !important; stroke-opacity: .5 !important; } ]]> diff --git a/app/pencil-core/editor/handleEditor.js b/app/pencil-core/editor/handleEditor.js index 3ce29e3c..55ce2e75 100644 --- a/app/pencil-core/editor/handleEditor.js +++ b/app/pencil-core/editor/handleEditor.js @@ -3,7 +3,15 @@ function HandleEditor() { this.canvas = null; } HandleEditor.ANCHOR_SIZE = 6; -HandleEditor.configDoc = Dom.loadSystemXml("pencil-core/editor/handleEditor.config.xml"); +HandleEditor.configDoc = Dom.loadSystemXml("pencil-core/editor/handleEditor.config.xml", function (fileContent) { + if (!fileContent) return ""; + var newContent = fileContent + .replace("$handle_fill_color", Config.get("handle.fill_color", "#ff0")) + .replace("$handle_stroke_color",Config.get("handle.stroke_color", "#0a0")) + .replace("$handle_focused_stroke_color", Config.get("handle.focused_stroke_color", "#f00")) + .replace("$handle_connected_stroke_color", Config.get("handle.connected_stroke_color", "#f00")); + return newContent; +}); HandleEditor.prototype.install = function (canvas) { this.canvas = canvas; this.canvas.onScreenEditors.push(this); diff --git a/app/pencil-core/propertyType/handle.js b/app/pencil-core/propertyType/handle.js index 2f9e29f8..a2c13079 100644 --- a/app/pencil-core/propertyType/handle.js +++ b/app/pencil-core/propertyType/handle.js @@ -6,8 +6,8 @@ Handle.REG_EX = /^([\-0-9\.]+)\,([\-0-9\.]+)$/; Handle.fromString = function(literal) { var handle = new Handle(); if (literal.match(Handle.REG_EX)) { - handle.x = parseFloat(RegExp.$1); - handle.y = parseFloat(RegExp.$2); + handle.x = Math.round(RegExp.$1); + handle.y = Math.round(RegExp.$2); } return handle; diff --git a/app/pencil-core/propertyType/imageData.js b/app/pencil-core/propertyType/imageData.js index d325783b..5b6dbc13 100644 --- a/app/pencil-core/propertyType/imageData.js +++ b/app/pencil-core/propertyType/imageData.js @@ -87,10 +87,40 @@ ImageData.invalidateValue = function (oldData, callback) { callback(null, error); } }); + } else if (oldData.data.match(/^(ref:\/\/([^@]+))@(.+)$/)) { + var ref = RegExp.$1; + var fp = RegExp.$3; + var id = ImageData.refStringToId(ref); + var refFilePath = Pencil.controller.refIdToFilePath(id); + + const fs = require("fs"); + // assume that the ref is local to this document, generate the local file path and check it + if (fsExistSync(refFilePath)) { + callback(new ImageData(oldData.w, oldData.h, ref, null)); + } else { + Pencil.controller.copyAsRef(fp, function (id, error) { + if (id) { + callback(new ImageData(oldData.w, oldData.h, ImageData.idToRefString(id)), null); + } else { + callback(null, error); + } + }); + } } else { callback(null); } }; +ImageData.prepareForTransferable = function (imageData, propDef) { + if (imageData.data.match(/^ref:\/\//)) { + var data = imageData.data; + if (imageData.data.match(/^(ref:\/\/[^@]+)(@.+)$/)) data = RegExp.$1; + var id = ImageData.refStringToId(data); + if (!id) return; + var filePath = Pencil.controller.refIdToFilePath(id); + data = data + "@" + filePath; + imageData.data = data; + } +}; ImageData.prepareForEmbedding = function (oldData, callback) { if (oldData.data.match(/^ref:\/\//)) { var id = ImageData.refStringToId(oldData.data); diff --git a/app/pencil-core/propertyType/richText.js b/app/pencil-core/propertyType/richText.js index 7b4abdd6..9f7bd994 100644 --- a/app/pencil-core/propertyType/richText.js +++ b/app/pencil-core/propertyType/richText.js @@ -1,6 +1,9 @@ function RichText(html) { this.html = html; this.value = html; + if (this.html instanceof PlainText && this.html.value) { + this.html.value = Dom.htmlEncode(this.html.value); + } } RichText.fromString = function (html) { return new RichText(html); diff --git a/app/pencil-core/target/group.js b/app/pencil-core/target/group.js index 1f411f69..3acb2295 100644 --- a/app/pencil-core/target/group.js +++ b/app/pencil-core/target/group.js @@ -440,6 +440,16 @@ Group.prototype.createTransferableData = function () { dataNode: this.svg.cloneNode(true) }; }; +Group.prototype.processExportingTransferableProperties = function () { + for (t in this.targets) { + this.targets[t].processExportingTransferableProperties(); + } +}; +Group.prototype.processImportedTransferableProperties = function () { + for (t in this.targets) { + this.targets[t].processImportedTransferableProperties(); + } +}; Group.prototype.lock = function () { this.svg.setAttributeNS(PencilNamespaces.p, "p:locked", "true"); }; diff --git a/app/pencil-core/target/shape.js b/app/pencil-core/target/shape.js index fa7a2b25..67319b77 100644 --- a/app/pencil-core/target/shape.js +++ b/app/pencil-core/target/shape.js @@ -878,16 +878,7 @@ Shape.prototype.getTextEditingInfo = function (editingEvent) { } } var targetObject = Dom.getSingle(".//*[@p:name='" + target + "']", this.svg); - //checking if the target is ok for use to base the location calculation - var ok = true; - try { - var clientRect = targetObject.getBoundingClientRect(); - if (clientRect.width == 0 || clientRect.height == 0) { - ok = false; - } - } catch (e) {} - - if (ok) { + if (targetObject) { info = {prop: prop, value: this.getProperty(name), targetName: target, @@ -1061,12 +1052,56 @@ Shape.prototype.getTextEditingInfo = function (editingEvent) { }; Shape.prototype.createTransferableData = function () { + var dataNode = this.svg.cloneNode(true); + return { type: ShapeXferHelper.MIME_TYPE, isSVG: true, - dataNode: this.svg.cloneNode(true) + dataNode: dataNode }; }; +Shape.prototype.processExportingTransferableProperties = function () { + for (var name in this.def.propertyMap) { + var prop = this.def.propertyMap[name]; + var value = this.getProperty(name); + + if (prop.type.prepareForTransferable) { + prop.type.prepareForTransferable(value, prop); + this.storeProperty(name, value); + } + } +}; + +Shape.prototype.processImportedTransferableProperties = function () { + var names = []; + for (var name in this.def.propertyMap) { + names.push(name); + } + var index = -1; + var thiz = this; + (function next() { + index ++; + if (index >= names.length) return; + var name = names[index]; + + var prop = thiz.def.propertyMap[name]; + var value = thiz.getProperty(name); + + if (prop.type.invalidateValue) { + prop.type.invalidateValue(value, function (newValue) { + if (newValue) { + thiz.storeProperty(name, newValue); + thiz.applyBehaviorForProperty(name); + } + next(); + }); + } else { + next(); + } + })(); +} + + Shape.prototype.lock = function () { this.svg.setAttributeNS(PencilNamespaces.p, "p:locked", "true"); }; diff --git a/app/pencil-core/target/targetSet.js b/app/pencil-core/target/targetSet.js index 48cdaa32..d606faaa 100644 --- a/app/pencil-core/target/targetSet.js +++ b/app/pencil-core/target/targetSet.js @@ -2,7 +2,7 @@ function TargetSet(canvas, targets) { this.canvas = canvas; this.targets = targets; this.targetIds = []; - + this.id = "sys_currentTargetSet"; for (var target of this.targets) this.targetIds.push(target.id); @@ -121,7 +121,7 @@ TargetSet.prototype.getBoundingRect = function () { }; rect2.width = Math.max(0, Math.max(rect.x + rect.width, r.x + r.width) - rect2.x); rect2.height = Math.max(0, Math.max(rect.y + rect.height, r.y + r.height) - rect2.y); - + rect = rect2; } return rect; @@ -536,6 +536,7 @@ TargetSet.prototype.sendToBack = function () { }; TargetSet.prototype.createTransferableData = function () { var node = this.canvas.ownerDocument.createElementNS(PencilNamespaces.svg, "g"); + for (i in this.targets) node.appendChild(this.targets[i].createTransferableData().dataNode); return {type: TargetSetXferHelper.MIME_TYPE, @@ -543,6 +544,16 @@ TargetSet.prototype.createTransferableData = function () { dataNode: node }; }; +TargetSet.prototype.processExportingTransferableProperties = function () { + for (t in this.targets) { + this.targets[t].processExportingTransferableProperties(); + } +}; +TargetSet.prototype.processImportedTransferableProperties = function () { + for (t in this.targets) { + this.targets[t].processImportedTransferableProperties(); + } +}; TargetSet.prototype.lock = function () { for (i in this.targets) if (this.targets[i].lock) this.targets[i].lock(); }; @@ -614,12 +625,12 @@ TargetSet.prototype.invalidateOutboundConnections = function () { TargetSet.prototype.getSnappingGuide = function () { var vertical = []; var horizontal = []; - + for (target of this.targets) { if (!target.getSnappingGuide) continue; var guide = target.getSnappingGuide(); if (!guide) continue; - + if (guide.horizontal && guide.horizontal.length > 0) horizontal = horizontal.concat(guide.horizontal); if (guide.vertical && guide.vertical.length > 0) vertical = vertical.concat(guide.vertical); } @@ -628,4 +639,3 @@ TargetSet.prototype.getSnappingGuide = function () { vertical: vertical, horizontal: horizontal }; }; - diff --git a/app/pencil-core/templates/HTML/prototype.HTML/Resources/script.js b/app/pencil-core/templates/HTML/prototype.HTML/Resources/script.js index 5040fa04..0f0af3ea 100644 --- a/app/pencil-core/templates/HTML/prototype.HTML/Resources/script.js +++ b/app/pencil-core/templates/HTML/prototype.HTML/Resources/script.js @@ -142,26 +142,6 @@ function handleMouseMove() { var THUMB_WIDTH = 250; var THUMB_HEIGHT = 160; -var THUMB_DISPLAY_SIZE = 160; - -function buildThumbnail(url, callback) { - var image = new Image(); - image.onload = function () { - var canvas = document.createElement('canvas'); - var ctx = canvas.getContext('2d'); - - var r = Math.max(image.width / THUMB_WIDTH, image.height / THUMB_HEIGHT); - var w = image.width / r, h = image.height / r; - canvas.width = w; - canvas.height = h; - - ctx.drawImage(image, 0, 0, w, h); - - callback(canvas.toDataURL('image/png'), w, h); - }; - - image.src = url; -} function generateTOC() { var toc = document.createElement("div"); @@ -173,7 +153,7 @@ function generateTOC() { var item = document.createElement("div"); var imageWrapper = document.createElement("a"); - var itemImage = document.createElement("img"); + item.classList.add("Page_" + page.id); item.setAttribute("tabindex", 0); @@ -182,22 +162,19 @@ function generateTOC() { imageWrapper.setAttribute("href", "#" + page.id); item.appendChild(imageWrapper); - var name = document.createElement("strong"); - name.innerHTML = title.innerHTML; toc.appendChild(item); - buildThumbnail(img.src, function (dataUrl, w, h) { - var r = Math.max(w / THUMB_DISPLAY_SIZE, h / THUMB_DISPLAY_SIZE); - var w = w / r, h = h / r; - - imageWrapper.appendChild(itemImage); - itemImage.style.width = w + "px"; - itemImage.style.height = h + "px"; - itemImage.src = dataUrl; - - imageWrapper.appendChild(name); - }); + var itemImage = document.createElement("img"); + itemImage.style.maxWidth = THUMB_WIDTH + "px"; + itemImage.style.maxHeight = THUMB_HEIGHT + "px"; + itemImage.src = img.src; + imageWrapper.appendChild(itemImage); + + var name = document.createElement("strong"); + name.innerHTML = title.innerHTML; + imageWrapper.appendChild(name); + }); document.body.appendChild(toc); diff --git a/app/pencil-core/xferHelper/shapeXferHelper.js b/app/pencil-core/xferHelper/shapeXferHelper.js index 25edf6cf..bd82999e 100644 --- a/app/pencil-core/xferHelper/shapeXferHelper.js +++ b/app/pencil-core/xferHelper/shapeXferHelper.js @@ -48,21 +48,33 @@ ShapeXferHelper.prototype.handleData = function (dom) { } } + if (this.canvas.currentController.processImportedTransferableProperties) { + try { + var processed = this.canvas.currentController.processImportedTransferableProperties(); + if (processed) { + this.canvas.selectNone(); + this.canvas.selectShape(shape); + } + } catch (e) { + console.error(e); + } + } + var rect = this.canvas.currentController.getBoundingRect(); var mx = dx; var my = dy; - + var padding = this.canvas.element.getBoundingClientRect().left - this.canvas._wrapper.getBoundingClientRect().left; var x0 = this.canvas._scrollPane.scrollLeft - padding; var y0 = this.canvas._scrollPane.scrollTop - padding; - + console.log(this.canvas.getSize(), this.canvas._scrollPane.scrollWidth, this.canvas._scrollPane.scrollHeight); - + var x1 = x0 + Math.min(this.canvas.getSize().width, this.canvas._scrollPane.clientWidth - padding); var y1 = y0 + Math.min(this.canvas.getSize().height, this.canvas._scrollPane.clientHeight - padding); - + console.log(x0, y0, x1, y1, padding); - + if (rect.x + rect.width > x1 || rect.x < x0) { mx = Math.round((x1 + x0) / 2 - (rect.x + rect.width / 2)); } diff --git a/app/pencil-core/xferHelper/targetSetXferHelper.js b/app/pencil-core/xferHelper/targetSetXferHelper.js index 6e6446a5..27bf9d3c 100644 --- a/app/pencil-core/xferHelper/targetSetXferHelper.js +++ b/app/pencil-core/xferHelper/targetSetXferHelper.js @@ -33,23 +33,48 @@ TargetSetXferHelper.prototype.handleData = function (dom) { this.canvas.drawingLayer.appendChild(shape); importedShapes.push(shape); } - this.canvas.selectMultiple(importedShapes) - + + this.canvas.selectMultiple(importedShapes); + + if (this.canvas.currentController.renewTargetProperties) { + try { + var renewed = this.canvas.currentController.renewTargetProperties(); + if (renewed) { + this.canvas.selectNone(); + this.canvas.selectMultiple(importedShapes); + } + } catch (e) { + console.error(e); + } + } + + if (this.canvas.currentController.processImportedTransferableProperties) { + try { + var processed = this.canvas.currentController.processImportedTransferableProperties(); + if (processed) { + this.canvas.selectNone(); + this.canvas.selectMultiple(importedShapes); + } + } catch (e) { + console.error(e); + } + } + var rect = this.canvas.currentController.getBoundingRect(); var mx = dx; var my = dy; - + var padding = this.canvas.element.getBoundingClientRect().left - this.canvas._wrapper.getBoundingClientRect().left; var x0 = this.canvas._scrollPane.scrollLeft - padding; var y0 = this.canvas._scrollPane.scrollTop - padding; - + console.log(this.canvas.getSize(), this.canvas._scrollPane.scrollWidth, this.canvas._scrollPane.scrollHeight); - + var x1 = x0 + Math.min(this.canvas.getSize().width, this.canvas._scrollPane.clientWidth - padding); var y1 = y0 + Math.min(this.canvas.getSize().height, this.canvas._scrollPane.clientHeight - padding); - + console.log(x0, y0, x1, y1, padding); - + if (rect.x + rect.width > x1 || rect.x < x0) { mx = Math.round((x1 + x0) / 2 - (rect.x + rect.width / 2)); } diff --git a/app/stencils/BasicWebElements/Layout.xhtml b/app/stencils/BasicWebElements/Layout.xhtml new file mode 100644 index 00000000..60af3d25 --- /dev/null +++ b/app/stencils/BasicWebElements/Layout.xhtml @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/app/stencils/BasicWebElements/layout_image.png b/app/stencils/BasicWebElements/layout_image.png new file mode 100644 index 00000000..4f1dc061 Binary files /dev/null and b/app/stencils/BasicWebElements/layout_image.png differ diff --git a/app/stencils/Common/Definition.xml b/app/stencils/Common/Definition.xml index 4f618fe6..c342bef0 100644 --- a/app/stencils/Common/Definition.xml +++ b/app/stencils/Common/Definition.xml @@ -1661,6 +1661,7 @@ 30,30 + false false @@ -1696,6 +1697,15 @@ $fillColor $strokeColor $strokeStyle + new Bool(!$isSquare.value) + + + $box.narrowed($strokeStyle.w) + [translate(($strokeStyle.w / 2), ($strokeStyle.w / 2))] + $fillColor + $strokeColor + $strokeStyle + $isSquare $textContent @@ -1715,6 +1725,7 @@ var box = this.getProperty("box"); var size = Math.max(box.w, box.h); this.setProperty("box", new Dimension(size, size)); + this.setProperty("isSquare", true); ]]> @@ -1727,6 +1738,7 @@ + diff --git a/app/stencils/CommonShapes_Flowchart/Definition.xml b/app/stencils/CommonShapes_Flowchart/Definition.xml index 6a2d0502..53333240 100644 --- a/app/stencils/CommonShapes_Flowchart/Definition.xml +++ b/app/stencils/CommonShapes_Flowchart/Definition.xml @@ -297,7 +297,7 @@ F.buildTextWrapDomContent(F._target, $textContent.value, $box.w - $textFont.getPixelHeight(), $textAlign) Bound.fromBox($box, $textFont.getPixelHeight() / 2) - new Alignment(1,1) + $textAlign diff --git a/app/stencils/SketchyGUI/Definition.xml b/app/stencils/SketchyGUI/Definition.xml index f9edbd9c..a7f30f76 100644 --- a/app/stencils/SketchyGUI/Definition.xml +++ b/app/stencils/SketchyGUI/Definition.xml @@ -984,6 +984,8 @@ MenuItem]]> css.set("height", height+"px" ); css.set("line-height", height+"px" ); css.set("padding", "0px 5px"); + css.set("text-align", $textAlign.h == 0 ? "left" : ($textAlign.h == 1 ? "center" : "right")); + css.set("vertical-align", $textAlign.v == 0 ? "top" : ($textAlign.v == 1 ? "middle" : "bottom")); if (title.match(/^(.+)\*[ ]*$/)) { title = RegExp.$1; css.set("background", $fillColor.shaded(0.1).toRGBString()); @@ -1488,7 +1490,7 @@ MenuItem]]> - 'Comic Sans MS'|Bold|normal|12px + $$textFont $$textColor 0,1 diff --git a/app/views/ApplicationPane.xhtml b/app/views/ApplicationPane.xhtml index 93e8997c..7b04c765 100644 --- a/app/views/ApplicationPane.xhtml +++ b/app/views/ApplicationPane.xhtml @@ -307,7 +307,6 @@ - diff --git a/app/views/PageDetailDialog.js b/app/views/PageDetailDialog.js index 26382e4d..99dbbaff 100644 --- a/app/views/PageDetailDialog.js +++ b/app/views/PageDetailDialog.js @@ -20,7 +20,7 @@ function PageDetailDialog() { this.pageSizeCombo.comparer = function (a, b) { return false; } - + this.backgroundCombo.renderer = function (item) { return item.name; @@ -139,7 +139,6 @@ PageDetailDialog.prototype.onShown = function () { }; PageDetailDialog.prototype.handlePageSizeSelect = function () { var pageSize = this.pageSizeCombo.getSelectedItem(); - console.log("on page size selected: ", pageSize); var value = pageSize.value; if (!value) return; if (value.match(SIZE_RE)) { @@ -231,7 +230,7 @@ PageDetailDialog.prototype.setup = function (options) { if (this.options && this.options.defaultParentPage) { this.pageCombo.selectItem(this.options.defaultParentPage); } - + this.backgroundCombo.setItems(backgroundItems); var pageSize = this.pageSizeCombo.getSelectedItem(); @@ -243,9 +242,9 @@ PageDetailDialog.prototype.setup = function (options) { if (this.originalPage) { this.updateUIWith(this.originalPage); } - + this.populatePageSizeSelector(); - + if (!this.originalPage && this._defaultSize) { this.widthInput.value = this._defaultSize.w; this.heightInput.value = this._defaultSize.h; @@ -256,9 +255,8 @@ PageDetailDialog.prototype.setup = function (options) { PageDetailDialog.prototype.invalidateBackgroundElements = function () { var background = this.backgroundCombo.getSelectedItem(); - console.log(background); this.colorButton.disabled = background.value ? true : false; - + var usingBackgroundPage = background.value && background.value != "transparent"; this.copyLinksCheckbox.disabled = !usingBackgroundPage; this.copyLinksLabel.style.opacity = usingBackgroundPage ? "1" : "0.5"; @@ -274,16 +272,16 @@ PageDetailDialog.prototype.populatePageSizeSelector = function () { if (lastSizeConfig && lastSizeConfig.match(SIZE_RE)) { w = Math.max(24, parseInt(RegExp.$1, 10)); h = Math.max(24, parseInt(RegExp.$2, 10)); - + this._defaultSize = {w: w, h: h}; } - + var lastSize = w + "x" + h; pageSizes.push({ displayName: "Last used", value: lastSize }); - + var bestFitSizeText = Pencil.controller.getBestFitSize(); if (bestFitSizeText && bestFitSizeText.match(SIZE_RE)) { w = Math.max(24, parseInt(RegExp.$1, 10)); @@ -297,9 +295,9 @@ PageDetailDialog.prototype.populatePageSizeSelector = function () { value: bestFitSize }); } - + if (!this._defaultSize) this._defaultSize = {w: w, h: h}; - + var backgroundPage = this.backgroundCombo.getSelectedItem(); if (backgroundPage && backgroundPage.width && backgroundPage.height) { pageSizes.push({ @@ -320,8 +318,7 @@ PageDetailDialog.prototype.updateUIWith = function (page) { this.widthInput.value = page.width; this.heightInput.value = page.height; - - console.log("after setting size to ", page.width, page.height); + if (page.backgroundColor) { this.backgroundCombo.selectItem({ @@ -346,7 +343,7 @@ PageDetailDialog.prototype.updateUIWith = function (page) { }); this.invalidateBackgroundElements(); } - + this.copyLinksCheckbox.checked = (page.backgroundPageId && page.copyBackgroundLinks) || false; } @@ -367,7 +364,7 @@ PageDetailDialog.prototype.createPage = function () { backgroundPageId = background.value; } } - + var options = { name: name, width: width, @@ -412,7 +409,7 @@ PageDetailDialog.prototype.updatePage = function() { } var parentPageId = this.pageCombo.getSelectedItem().id; - + var options = { name: name, width: width, @@ -423,7 +420,7 @@ PageDetailDialog.prototype.updatePage = function() { copyBackgroundLinks: background.value && background.value != "transparent" && this.copyLinksCheckbox.checked }; Pencil.controller.updatePageProperties(page, options); - + return page; } PageDetailDialog.prototype.isPageInfoValid = function () { diff --git a/app/views/collections/BaseCollectionPane.xhtml b/app/views/collections/BaseCollectionPane.xhtml index 673e6577..baf337f0 100644 --- a/app/views/collections/BaseCollectionPane.xhtml +++ b/app/views/collections/BaseCollectionPane.xhtml @@ -332,11 +332,12 @@ } @clearTextButton { position: absolute; - right: 1.5em; - top: 1.3em; + right: 0.7em; + top: 0.7em; line-height: 1em; - height: 1.8em; + height: 1.5em; width: auto; + background: #c6e4ff; } @clearTextButton:not(:hover) { border-color: transparent; diff --git a/app/views/common/Popup.js b/app/views/common/Popup.js index 987aa2ec..021065c9 100644 --- a/app/views/common/Popup.js +++ b/app/views/common/Popup.js @@ -11,6 +11,10 @@ function Popup(node) { if (popupClass) Dom.addClass(this.popupContainer, popupClass); } + this.bind("scroll", function () { + this.invalidateOverflowIndicators(); + }, this.popupContainer); + Popup.registerGlobalListeners(); } __extend(BaseTemplatedWidget, Popup); @@ -134,11 +138,13 @@ Popup.prototype.show = function (anchor, hAlign, vAlign, hPadding, vPadding, aut Popup.prototype.isVisible = function () { return this.visible; }; +Popup.prototype.invalidateOverflowIndicators = function () { + Dom.toggleClass(this.popupContainer, "ReachedTop", this.popupContainer.scrollTop <= 0); + Dom.toggleClass(this.popupContainer, "ReachedBottom", this.popupContainer.scrollTop >= this.popupContainer.scrollHeight - this.popupContainer.offsetHeight); +} Popup.prototype.showAt = function (x, y, skipEvent, autoFlip) { this.reparent(); - console.log("Showing at: ", [x, y]); - if (this.mode) { this.popupContainer.setAttribute("mode", this.mode); } @@ -151,13 +157,35 @@ Popup.prototype.showAt = function (x, y, skipEvent, autoFlip) { var h = this.popupContainer.offsetHeight; - var screenW = document.body.offsetWidth - 10; - var screenH = window.innerHeight - 10; + const EDGE_MARGIN = 10; + var screenW = document.body.offsetWidth - 2 * EDGE_MARGIN; + var screenH = window.innerHeight; - if (y + h > screenH) { - y = y - h; - if (y < 0) { - y += h/2; + if (y + h > screenH - EDGE_MARGIN) { + if (y - h > EDGE_MARGIN) { + y = y - h; + } else { + if (h < screenH - 2 * EDGE_MARGIN) { + y = screenH - EDGE_MARGIN - h; + } else { + y = EDGE_MARGIN; + const INDICATOR_HEIGHT = 8; + var height = screenH - 2 * EDGE_MARGIN; + + this.popupContainer.style.height = height + "px"; + Dom.addClass(this.popupContainer, "Overflowed"); + this._topOverflowIndicator = Dom.newDOMElement({_name: "div", "class": "Top OverflowIndicator"}); + this.popupContainer.insertBefore(this._topOverflowIndicator, this.popupContainer.firstChild); + this._topOverflowIndicator.style.top = `0%`; + this._topOverflowIndicator.style.height = INDICATOR_HEIGHT + "px" + + this._bottomOverflowIndicator = Dom.newDOMElement({_name: "div", "class": "Bottom OverflowIndicator"}); + this.popupContainer.appendChild(this._bottomOverflowIndicator); + this._bottomOverflowIndicator.style.top = `calc(100% - ${INDICATOR_HEIGHT}px)`; + this._bottomOverflowIndicator.style.height = INDICATOR_HEIGHT + "px" + + this.invalidateOverflowIndicators(); + } } } @@ -313,6 +341,17 @@ Popup.prototype.hidePopupContainer = function () { this.popupContainer.style.opacity = 0; this.popupContainer.style.visibility = "hidden"; this.visible = false; + + this.popupContainer.style.removeProperty("height"); + Dom.removeClass(this.popupContainer, "Overflowed"); + if (this._topOverflowIndicator) { + this.popupContainer.remove(this._topOverflowIndicator); + this._topOverflowIndicator = null; + } + if (this._bottomOverflowIndicator) { + this.popupContainer.remove(this._bottomOverflowIndicator); + this._bottomOverflowIndicator = null; + } } Popup.prototype.hide = function (silent, reason, event) { this.hidePopupContainer(); diff --git a/app/views/common/Popup.xhtml b/app/views/common/Popup.xhtml index fa6db09c..7e21bf05 100644 --- a/app/views/common/Popup.xhtml +++ b/app/views/common/Popup.xhtml @@ -10,6 +10,9 @@ box-sizing: border-box; visibility: hidden; } + body @popupContainer.Overflowed { + overflow-y: auto; + }
diff --git a/app/views/editors/ColorSelector.js b/app/views/editors/ColorSelector.js index 2c6ff41a..5a631352 100644 --- a/app/views/editors/ColorSelector.js +++ b/app/views/editors/ColorSelector.js @@ -423,7 +423,7 @@ ColorSelector.prototype.updateRecentlyUsedColors = function () { } } this.recentlyUsedColors.push(aColor); - if (this.recentlyUsedColors.length > 10) { + if (this.recentlyUsedColors.length > 16) { this.recentlyUsedColors.shift(); } var colors = this.recentlyUsedColors.join(","); diff --git a/app/views/editors/FontEditor.xhtml b/app/views/editors/FontEditor.xhtml index 1a7ca85a..223c6ed0 100644 --- a/app/views/editors/FontEditor.xhtml +++ b/app/views/editors/FontEditor.xhtml @@ -17,7 +17,7 @@ @number_input(); } @lineHeight { - width: 2.5em; + width: 3em; @number_input(); } @fontCombo { @@ -47,7 +47,7 @@ - +
diff --git a/app/views/editors/OnMenuEditor.js b/app/views/editors/OnMenuEditor.js index 19fde9eb..b60fba5c 100644 --- a/app/views/editors/OnMenuEditor.js +++ b/app/views/editors/OnMenuEditor.js @@ -106,7 +106,8 @@ OnMenuEditor.prototype.generateMenuItems = function () { if (!checked) return; thiz.targetObject.setProperty(this.property, new Enum(this.value)); var editors = Pencil.controller.applicationPane.sharedPropertyEditor.propertyEditor; - editors[this.property].setValue(this.value); + var editor = editors[this.property]; + if (editor) editor.setValue(this.value); Pencil.controller.applicationPane.sharedPropertyEditor.validationEditorUI(); } }); diff --git a/app/views/editors/SharedPropertyEditor.js b/app/views/editors/SharedPropertyEditor.js index 41157968..2f48b2e7 100644 --- a/app/views/editors/SharedPropertyEditor.js +++ b/app/views/editors/SharedPropertyEditor.js @@ -74,6 +74,20 @@ SharedPropertyEditor.prototype.validationEditorUI = function() { } }; +SharedPropertyEditor.prototype.quickInvalidate = function (target) { + var definedGroups = this.target.getPropertyGroups(); + + for (var i in definedGroups) { + var group = definedGroups[i]; + for (var j in group.properties) { + var property = group.properties[j]; + var editor = this.propertyEditor[property.name]; + if (editor) { + editor.setValue(this.target.getProperty(property.name)); + } + } + } +}; SharedPropertyEditor.prototype.attach = function (target) { if (!target) return; if (target && target.getAttributeNS && target.getAttributeNS(PencilNamespaces.p, "locked") == "true") { return; } @@ -85,6 +99,7 @@ SharedPropertyEditor.prototype.attach = function (target) { if (this.target && this.target.id == target.id) { this.target = target; + this.quickInvalidate(); return; } diff --git a/app/views/menus/Menu.js b/app/views/menus/Menu.js index 821884b9..c91ee7c1 100644 --- a/app/views/menus/Menu.js +++ b/app/views/menus/Menu.js @@ -13,7 +13,6 @@ function Menu() { checkbox.checked = !checkbox.checked; } if (item.handleAction) { - console.log("checkbox.checked: " + checkbox.checked); item.handleAction(checkbox.checked); } else if (item.run) { item.run(checkbox.checked, event); @@ -257,7 +256,9 @@ Menu.prototype.showMenu = function (anchor, hAlign, vAlign, hPadding, vPadding, }; Menu.prototype.showMenuAt = function (x, y) { this.render(); - this.showAt(x, y, false, "autoFlip"); + setTimeout(() => { + this.showAt(x, y, false, "autoFlip"); + }, 10); }; Menu.prototype.hideMenu = function () { this.hide(); diff --git a/app/views/menus/Menu.xhtml b/app/views/menus/Menu.xhtml index 66c84127..e3ce29f2 100644 --- a/app/views/menus/Menu.xhtml +++ b/app/views/menus/Menu.xhtml @@ -25,6 +25,18 @@ background: @selected_bg; color: @selected_fg; } + + body .MenuPopupContainer .MenuItem.Active:not([disabled='true']) > i, + body .MenuPopupContainer .MenuItem:not([disabled='true']):hover > i, + body .MenuPopupContainer .MenuItem[selected='true']:not([disabled='true']) > i, + body .MenuPopupContainer .MenuItem.Active:not([disabled='true']) > input[type="checkbox"]::after, + body .MenuPopupContainer .MenuItem:not([disabled='true']):hover > input[type="checkbox"]::after, + body .MenuPopupContainer .MenuItem[selected='true']:not([disabled='true']) > input[type="checkbox"]::after { + color: #000; + filter: invert(1); + opacity: 0.7; + } + body .MenuPopupContainer .MenuItem[disabled='true'] { opacity: 0.3; } @@ -61,6 +73,38 @@ body .MenuPopupContainer .MenuItem > *:first-child + label + * { margin-left: 2ex; } + + body .MenuPopupContainer.Overflowed { + overflow-y: auto; + box-sizing: border-box; + position: relative; + padding-top: 0px; + padding-bottom: 0px; + } + + body .MenuPopupContainer.Overflowed > .OverflowIndicator { + position: sticky; + left: 0px; + right: 0px; + background-color: #FF000077; + margin: 0ex -0.5ex; + pointer-events: none; + opacity: 1; + transition: opacity 0.2s ease; + } + body .MenuPopupContainer.Overflowed > .OverflowIndicator.Top { + top: 0px; + background: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0)); + } + body .MenuPopupContainer.Overflowed > .OverflowIndicator.Bottom { + bottom: 0px; + background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.2)); + } + + body .MenuPopupContainer.Overflowed.ReachedTop > .OverflowIndicator.Top, + body .MenuPopupContainer.Overflowed.ReachedBottom > .OverflowIndicator.Bottom { + opacity: 0; + } diff --git a/app/views/tools/SettingDialog.js b/app/views/tools/SettingDialog.js index 763f409a..9377167d 100644 --- a/app/views/tools/SettingDialog.js +++ b/app/views/tools/SettingDialog.js @@ -68,11 +68,21 @@ function SettingDialog() { // if (configName == "external.editor.vector.path") { // if (node.value == "" ) node.value = "/usr/bin/inkscape"; // } + var configValue = Config.get(configName, node.value); Config.set(configName, node.value); this.setPreferenceItems(); if (configName == "view.uiTextScale") { - widget.reloadDesktopFont().then(function () {}); + const delta = Math.abs(node.value - configValue); + widget.reloadDesktopFont().then(function () { + if (event.restoreAction || delta == 1) return; + Dialog.confirm("Would you like to keep this configuration?", "", + "Restore the previous configuration", function () { + this.textScaleInput.value = configValue; + Dom.emitEvent("change", node, {restoreAction: true}); + return true; + }.bind(this), "Keep this configuration"); + }.bind(this)); } } diff --git a/app/views/tools/StencilGeneratorDialog.js b/app/views/tools/StencilGeneratorDialog.js index aff5834d..bce34c43 100644 --- a/app/views/tools/StencilGeneratorDialog.js +++ b/app/views/tools/StencilGeneratorDialog.js @@ -92,7 +92,7 @@ function StencilGeneratorDialog() { __extend(WizardDialog, StencilGeneratorDialog); StencilGeneratorDialog.prototype.invalidateFinish = function () { - for (var i in thiz.imagePaths) { + for (var i in this.imagePaths) { if (this.imagePaths[i].checked) this.stencils.push(this.imagePaths[i]._stencil); } @@ -153,6 +153,7 @@ StencilGeneratorDialog.prototype.onSelectionChanged = function () { StencilGeneratorDialog.prototype.initStencils = function () { var thiz = this; this.preloadStencils(function (stencils, listener) { + thiz.stencils = stencils; for (i in thiz.imagePaths) { thiz.imagePaths[i]._stencil = stencils[i]; if (thiz.imagePaths[i].checked != null) { @@ -223,7 +224,7 @@ StencilGeneratorDialog.prototype.loadStencil = function (result, stencils, index thiz = this; try { if (index < stencils.length) { - if ("png|jpg|gif|bmp".indexOf(stencils[index]._ext) != -1) { + if ("png|jpg|gif|bmp".indexOf(("" + stencils[index]._ext).toLowerCase()) != -1) { var img = new Image(); img._stencils = stencils; @@ -282,15 +283,15 @@ StencilGeneratorDialog.prototype.generateId = function (s) { }; StencilGeneratorDialog.prototype.createCollection = function () { var thiz = this; - dialog.showSaveDialog({ title: "Save as", defaultPath: os.homedir(), filters: [ { name: "Stencil file", extensions: ["zip"] } ] - }, function (filePath) { - if (!filePath) return; + }).then(function (fileSelection) { + if (!fileSelection || fileSelection.canceled || !fileSelection.filePath) return; + var filePath = fileSelection.filePath; var starter = function (listener) { try { var s = "= thiz.stencils.length) { s += ""; - try { var tempDir = tmp.dirSync({ keep: false, unsafeCleanup: true }); var file = fs.createWriteStream(tempDir.name + "/" + "Definition.xml"); - - fs.writeFile(file.path, s, (err) => { - if (err) throw err; - var iconFolder = fs.mkdirSync(tempDir.name + "/" + "icon"); - - var archiver = require("archiver"); - var archive = archiver("zip"); - var output = fs.createWriteStream(filePath); - archive.pipe(output); - archive.directory(tempDir.name, "/", {}); - archive.finalize(); + file.on('open', function() { + fs.writeFile(file.path, s, (err) => { + if (err) throw err; + var iconFolder = fs.mkdirSync(tempDir.name + "/" + "icon"); + + var archiver = require("archiver"); + var archive = archiver("zip"); + var output = fs.createWriteStream(filePath); + + archive.pipe(output); + archive.directory(tempDir.name, "/", {}); + archive.finalize(); + }); }); } catch (e5) { console.log(e5);