diff --git a/Makefile.dryice.js b/Makefile.dryice.js index 7a060aea269..1b84f0c0b32 100755 --- a/Makefile.dryice.js +++ b/Makefile.dryice.js @@ -606,7 +606,11 @@ function extractCss(callback) { } var buffer = Buffer.from(data.slice(i + 1), "base64"); imageCounter++; - var imageName = name + "-" + imageCounter + ".png"; + var imageName; + if (/^image\/svg\+xml/.test(data)) + imageName = name + "-" + imageCounter + ".svg"; + else + imageName = name + "-" + imageCounter + ".png"; images[imageName] = buffer; console.log("url(\"" + directory + "/" + imageName + "\")"); return "url(\"" + directory + "/" + imageName + "\")"; diff --git a/ace.d.ts b/ace.d.ts index a3641c6f80d..e816e36e72b 100644 --- a/ace.d.ts +++ b/ace.d.ts @@ -194,6 +194,7 @@ export namespace Ace { theme: string; hasCssTransforms: boolean; maxPixelHeight: number; + useSvgGutterIcons: boolean; } export interface MouseHandlerOptions { diff --git a/src/css/editor.css.js b/src/css/editor.css.js index 052b9214af1..c62630005c2 100644 --- a/src/css/editor.css.js +++ b/src/css/editor.css.js @@ -105,7 +105,7 @@ module.exports = ` pointer-events: none; } -.ace_gutter-cell { +.ace_gutter-cell, .ace_gutter-cell_svg-icons { position: absolute; top: 0; left: 0; @@ -115,6 +115,11 @@ module.exports = ` background-repeat: no-repeat; } +.ace_gutter-cell_svg-icons .ace_icon_svg{ + margin-left: -14px; + float: left; +} + .ace_gutter-cell.ace_error, .ace_icon.ace_error { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg=="); background-repeat: no-repeat; @@ -136,6 +141,19 @@ module.exports = ` background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC"); } +.ace_icon_svg.ace_error { + -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJyZWQiIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIj4KPGNpcmNsZSBmaWxsPSJub25lIiBjeD0iOCIgY3k9IjgiIHI9IjciIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPGxpbmUgeDE9IjExIiB5MT0iNSIgeDI9IjUiIHkyPSIxMSIvPgo8bGluZSB4MT0iMTEiIHkxPSIxMSIgeDI9IjUiIHkyPSI1Ii8+CjwvZz4KPC9zdmc+"); + background-color: crimson; +} +.ace_icon_svg.ace_warning { + -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJkYXJrb3JhbmdlIiBzaGFwZS1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiI+Cjxwb2x5Z29uIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiIHBvaW50cz0iOCAxIDE1IDE1IDEgMTUgOCAxIi8+CjxyZWN0IHg9IjgiIHk9IjEyIiB3aWR0aD0iMC4wMSIgaGVpZ2h0PSIwLjAxIi8+CjxsaW5lIHgxPSI4IiB5MT0iNiIgeDI9IjgiIHkyPSIxMCIvPgo8L2c+Cjwvc3ZnPg=="); + background-color: darkorange; +} +.ace_icon_svg.ace_info { + -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJibHVlIiBzaGFwZS1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiI+CjxjaXJjbGUgZmlsbD0ibm9uZSIgY3g9IjgiIGN5PSI4IiByPSI3IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjggMTEgOCA4Ii8+Cjxwb2x5bGluZSBwb2ludHM9IjkgOCA2IDgiLz4KPGxpbmUgeDE9IjEwIiB5MT0iMTEiIHgyPSI2IiB5Mj0iMTEiLz4KPHJlY3QgeD0iOCIgeT0iNSIgd2lkdGg9IjAuMDEiIGhlaWdodD0iMC4wMSIvPgo8L2c+Cjwvc3ZnPg=="); + background-color: royalblue; +} + .ace_scrollbar { contain: strict; position: absolute; @@ -437,7 +455,16 @@ module.exports = ` width: 18px; } -.ace_folding-enabled > .ace_gutter-cell { +.ace_icon_svg { + display: inline-block; + width: 12px; + vertical-align: top; + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: 12px; + -webkit-mask-position: center; +} + +.ace_folding-enabled > .ace_gutter-cell, .ace_folding-enabled > .ace_gutter-cell_svg-icons { padding-right: 13px; } diff --git a/src/editor.js b/src/editor.js index db3a648ac3f..b9f928f055c 100644 --- a/src/editor.js +++ b/src/editor.js @@ -2883,6 +2883,7 @@ config.defineOptions(Editor.prototype, "editor", { maxPixelHeight: "renderer", useTextareaForIME: "renderer", useResizeObserver: "renderer", + useSvgGutterIcons: "renderer", scrollSpeed: "$mouseHandler", dragDelay: "$mouseHandler", diff --git a/src/ext/options.js b/src/ext/options.js index a80010444af..00f86bd36ff 100644 --- a/src/ext/options.js +++ b/src/ext/options.js @@ -194,6 +194,9 @@ var optionGroups = { }, "Custom scrollbar": { path: "customScrollbar" + }, + "Use SVG gutter icons": { + path: "useSvgGutterIcons" } } }; diff --git a/src/layer/gutter.js b/src/layer/gutter.js index 579bb98b00e..2f1bdbc73ae 100644 --- a/src/layer/gutter.js +++ b/src/layer/gutter.js @@ -278,6 +278,7 @@ class Gutter{ var textNode = element.childNodes[0]; var foldWidget = element.childNodes[1]; + var annotationNode = element.childNodes[2]; var firstLineNumber = session.$firstLineNumber; @@ -287,7 +288,27 @@ class Gutter{ var foldWidgets = this.$showFoldWidgets && session.foldWidgets; var foldStart = fold ? fold.start.row : Number.MAX_VALUE; - var className = "ace_gutter-cell "; + var lineHeight = config.lineHeight + "px"; + + var className; + if (this.$useSvgGutterIcons){ + className = "ace_gutter-cell_svg-icons "; + + if (this.$annotations[row]){ + annotationNode.className = "ace_icon_svg" + this.$annotations[row].className; + + dom.setStyle(annotationNode.style, "height", lineHeight); + dom.setStyle(annotationNode.style, "display", "block"); + } + else { + dom.setStyle(annotationNode.style, "display", "none"); + } + } + else { + className = "ace_gutter-cell "; + dom.setStyle(annotationNode.style, "display", "none"); + } + if (this.$highlightGutterLine) { if (row == this.$cursorRow || (fold && row < this.$cursorRow && row >= foldStart && this.$cursorRow <= fold.end.row)) { className += "ace_gutter-active-line "; @@ -324,8 +345,7 @@ class Gutter{ if (foldWidget.className != className) foldWidget.className = className; - var foldHeight = config.lineHeight + "px"; - dom.setStyle(foldWidget.style, "height", foldHeight); + dom.setStyle(foldWidget.style, "height", lineHeight); dom.setStyle(foldWidget.style, "display", "inline-block"); } else { if (foldWidget) { @@ -415,6 +435,9 @@ function onCreateCell(element) { var foldWidget = dom.createElement("span"); element.appendChild(foldWidget); + + var annotationNode = dom.createElement("span"); + element.appendChild(annotationNode); return element; } diff --git a/src/mouse/default_gutter_handler.js b/src/mouse/default_gutter_handler.js index 28fddd78901..d53d5543aab 100644 --- a/src/mouse/default_gutter_handler.js +++ b/src/mouse/default_gutter_handler.js @@ -57,9 +57,11 @@ function GutterHandler(mouseHandler) { info: {singular: "information message", plural: "information messages"} }; + var iconClassName = gutter.$useSvgGutterIcons ? "ace_icon_svg" : "ace_icon"; + // Construct the body of the tooltip. for (var i = 0; i < annotation.text.length; i++) { - var line = ` ${annotation.text[i]}`; + var line = ` ${annotation.text[i]}`; annotationMessages[annotation.type[i]].push(line); } var tooltipBody = "
"; diff --git a/src/mouse/default_gutter_handler_test.js b/src/mouse/default_gutter_handler_test.js index d089c9024be..59e2f823b90 100644 --- a/src/mouse/default_gutter_handler_test.js +++ b/src/mouse/default_gutter_handler_test.js @@ -112,6 +112,24 @@ module.exports = { assert.ok(/info test/.test(tooltipBody.textContent)); }, 100); }, + "test: gutter svg icons" : function() { + var editor = this.editor; + var value = ""; + + editor.session.setMode(new Mode()); + editor.setOption("useSvgGutterIcons", true); + editor.setValue(value, -1); + editor.session.setAnnotations([{row: 0, column: 0, type: "error", text: "error test"}]); + editor.renderer.$loop._flush(); + + var lines = editor.renderer.$gutterLayer.$lines; + var line = lines.cells[0].element; + assert.ok(/ace_gutter-cell_svg-icons/.test(line.className)); + + var annotation = line.children[2]; + assert.ok(/ace_icon_svg/.test(annotation.className)); + }, + tearDown : function() { this.editor.destroy(); diff --git a/src/mouse/mouse_handler_test.js b/src/mouse/mouse_handler_test.js index 64a4c105fd5..3d057312fd7 100644 --- a/src/mouse/mouse_handler_test.js +++ b/src/mouse/mouse_handler_test.js @@ -126,7 +126,7 @@ module.exports = { editor.setValue(value, -1); editor.renderer.$loop._flush(); var lines = editor.renderer.$gutterLayer.$lines; - var toggler = lines.cells[0].element.lastChild; + var toggler = lines.cells[0].element.children[1]; var rect = toggler.getBoundingClientRect(); if (!rect.left) rect.left = 100; // for mockdom toggler.dispatchEvent(MouseEvent("down", {x: rect.left, y: rect.top})); diff --git a/src/virtual_renderer.js b/src/virtual_renderer.js index d8b76288043..7dfa7cf47a9 100644 --- a/src/virtual_renderer.js +++ b/src/virtual_renderer.js @@ -1902,6 +1902,12 @@ config.defineOptions(VirtualRenderer.prototype, "renderer", { }, initialValue: true }, + useSvgGutterIcons: { + set: function(value){ + this.$gutterLayer.$useSvgGutterIcons = value; + }, + initialValue: false + }, fadeFoldWidgets: { set: function(show) { dom.setCssClass(this.$gutter, "ace_fade-fold-widgets", show);